ci / vet-and-test (pull_request) Successful in 48s
Why: Real-run before/after evidence (phase01 card) showed later turns never re-read the uploaded source (0 original reads in plan-followup) because the attachment only existed in the current turn's injection and the context window hard default was 32000 chars - a 25k-char transcript tripped the clear-on-first request, replacing old tool results with a pointer the model has no tool to follow. MiniMax is an interleaved-thinking model whose docs require <think> blocks in assistant history; dropping them degrades later turns. Evidence: SELF - new tests TestThinkReplayWrapsAssistantHistory, TestContextWindowScalesFromTokens, TestThinkReplayEnabled, TestPersistInputTextsAndListWorkspaceInputs; go test ./internal/... passes; live before/after comparison on the golden task set recorded on the phase02 task card per the new policy clause. Impact: models.chat.contextWindow/outputReserve/contextBuffer (tokens, defaults 128k/16k/4k) drive the in-run clear threshold, converted to chars; cleared tool results spill to <sandbox>/.agent/results with readable paths; attachment parse text persists as inputs/<name>.md and the env section lists all workspace inputs every turn; context.thinkReplay (auto|on|off, auto = MiniMax) replays thinking blocks into request-side assistant history only.
83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
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
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
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
|
|
}
|
|
_, err := m.st.PutMemory(m.owner, 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,
|
|
})
|
|
return err
|
|
}
|