package runmgr import ( "context" "time" "agent-native/runtime/memory" "agent-system-backend/internal/store" core "agent-system-core" ) // storeMemory 把 agent-native 的记忆端口接到 memory_entries 表(N3 WP5)。 // owner/项目态由适配器持有(端口本身不传租户态)。 type storeMemory struct { st *store.Store owner string projectID string checked bool allowImplicitDefault bool } // NewStoreMemory 构建端口实现(owner 为空时用默认租户)。 func NewStoreMemory(st *store.Store, owner, projectID string) memory.Store { if owner == "" { owner = store.OwnerDefault } return &storeMemory{st: st, owner: owner, projectID: projectID} } func newAccountCheckedStoreMemory(st *store.Store, owner, projectID string, allowImplicitDefault bool) memory.Store { return &storeMemory{st: st, owner: owner, projectID: projectID, checked: true, allowImplicitDefault: allowImplicitDefault} } // Recall 按 scope 过滤(空 = 不限,保持迁移前 Memories() 的全量语义), // 过期与空内容条目的剔除与文案渲染在 memory.Block(同一形态)。 func (m *storeMemory) Recall(ctx context.Context, q memory.RecallQuery) ([]memory.Entry, error) { projectID := "" if q.Scope == "project" { projectID = m.projectID } list, err := m.st.ListMemory(m.owner, q.Scope, projectID) if err != nil { return nil, err } limit := q.Limit if limit <= 0 { limit = memory.MaxRecallEntries } out := make([]memory.Entry, 0, len(list)) for _, e := range list { out = append(out, memory.Entry{ ID: e.ID, Scope: e.Scope, ProjectID: string(e.ProjectID), Type: e.Type, Content: e.Content, Source: e.Source, Confidence: e.Confidence, CreatedAt: e.CreatedAt, ExpiresAt: e.ExpiresAt, }) if len(out) >= limit { break } } return out, nil } // StoreMemory 是 runCtx 的端口出口(llm_direct 经接口断言取用)。 func (c *runCtx) StoreMemory() memory.Store { if c.mgr == nil || c.mgr.st == nil { return nil } if c.mgr.accountEffectiveAuthorization != nil { return newAccountCheckedStoreMemory(c.mgr.st, c.run.Owner, c.run.ProjectID, c.mgr.accountAllowImplicitLocal) } return NewStoreMemory(c.mgr.st, c.run.Owner, c.run.ProjectID) } // Append 写回一条(project scope 用适配器的项目上下文)。 func (m *storeMemory) Append(ctx context.Context, e memory.Entry) error { projectID := e.ProjectID if e.Scope == "project" && projectID == "" { projectID = m.projectID } now := time.Now().UTC() created := e.CreatedAt if created.IsZero() { created = now } entry := core.MemoryEntry{ ID: e.ID, Scope: e.Scope, ProjectID: core.ID(projectID), Type: e.Type, Content: e.Content, Source: e.Source, Confidence: e.Confidence, CreatedAt: created, ExpiresAt: e.ExpiresAt, } var err error if m.checked { _, err = m.st.PutMemoryAccountChecked(m.owner, entry, m.allowImplicitDefault) } else { _, err = m.st.PutMemory(m.owner, entry) } return err }