Files
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

94 lines
3.4 KiB
Go

package repository
import (
"context"
"errors"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
"gorm.io/gorm"
)
// MemoryAllowedMessageIDs uses per-turn Agent identity; a session can switch
// Agents and its last-request settings cannot authorize older messages.
func (r *messageRepository) MemoryAllowedMessageIDs(ctx context.Context, scope interfaces.MemoryScope, sessionID string, messages []*types.Message) (map[string]bool, error) {
allowed := map[string]bool{}
owned, err := r.MemoryOwnedSessionIDs(ctx, scope, []string{sessionID})
if err != nil || !owned[sessionID] {
return allowed, err
}
for _, input := range messages {
if input == nil || input.Role != "user" || input.SessionID != sessionID {
continue
}
var answer types.Message
query := r.db.WithContext(ctx).Where("session_id = ? AND role = ?", sessionID, "assistant")
if input.RequestID != "" {
query = query.Where("request_id = ?", input.RequestID)
} else {
// Without a request identity, only an adjacent assistant can safely
// attribute a historical input. Never borrow another turn's Agent.
query = r.db.WithContext(ctx).Where("session_id = ? AND created_at > ?", sessionID, input.CreatedAt)
}
if err := query.Order("created_at ASC, id ASC").First(&answer).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
continue
}
return nil, err
}
if answer.Role != "assistant" {
continue
}
if !answer.IsCompleted {
// Keep the checkpoint behind a turn still streaming in this session.
return nil, errors.New("memory extraction is waiting for a completed turn")
}
if answer.AgentID == "" {
// Historical ordinary-assistant turns have no custom Agent binding.
allowed[input.ID] = true
continue
}
agentTenant := answer.AgentTenantID
if agentTenant == 0 {
agentTenant = scope.TenantID
}
var agent types.CustomAgent
err := r.db.WithContext(ctx).Where("tenant_id = ? AND id = ?", agentTenant, answer.AgentID).First(&agent).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
// Built-in defaults may have no tenant override. A soft-deleted
// override must not be recreated through the built-in registry.
var deleted int64
if err := r.db.WithContext(ctx).Unscoped().Model(&types.CustomAgent{}).
Where("tenant_id = ? AND id = ?", agentTenant, answer.AgentID).Count(&deleted).Error; err != nil {
return nil, err
}
builtin := types.GetBuiltinAgent(answer.AgentID, agentTenant)
if deleted != 0 || builtin == nil || agentTenant != scope.TenantID {
continue
}
agent = *builtin
} else if err != nil {
return nil, err
}
if !agent.Enabled || (agent.Config.MemoryEnabled != nil && !*agent.Config.MemoryEnabled) {
continue
}
if agentTenant != scope.TenantID {
var shares int64
err := r.db.WithContext(ctx).Table("agent_shares").
Joins("JOIN organization_tenant_members otm ON otm.organization_id = agent_shares.organization_id").
Joins("JOIN organizations ON organizations.id = agent_shares.organization_id AND organizations.deleted_at IS NULL").
Where("agent_shares.agent_id = ? AND agent_shares.source_tenant_id = ? AND agent_shares.deleted_at IS NULL AND otm.tenant_id = ?", answer.AgentID, agentTenant, scope.TenantID).
Count(&shares).Error
if err != nil {
return nil, err
}
if shares == 0 {
continue
}
}
allowed[input.ID] = true
}
return allowed, nil
}