Files
weichen 43297795f5
ci / vet-and-test (pull_request) Successful in 48s
Keep original materials and cleared results reachable across turns
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.
2026-10-09 14:28:39 +00:00

91 lines
3.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package runmgr
import (
"context"
"testing"
agentllm "agent-native/runtime/llm"
agentpolicy "agent-native/runtime/policy"
core "agent-system-core"
)
func TestPlanModeRejectsWriteAndKeepsRead(t *testing.T) {
c, st := newExecEnv(t)
if err := st.SetPermissionMode("alice", c.run.ConversationID, "plan"); err != nil {
t.Fatal(err)
}
c.permissionMode = "plan"
res := c.ExecuteTool(core.ToolCallRequest{CallID: "c1", Name: "write_artifact", Arguments: []byte(`{"name":"a.md","content":"x"}`)})
if res.Status != "error" || res.Output == "" {
t.Fatalf("write in plan mode: %+v", res)
}
ok := c.ExecuteTool(core.ToolCallRequest{CallID: "c2", Name: "time_now", Arguments: []byte(`{}`)})
if ok.Status != "ok" {
t.Fatalf("read in plan mode: %+v", ok)
}
names := map[string]bool{}
for _, tool := range c.Tools() {
names[tool.Name] = true
}
if names["write_artifact"] || !names["time_now"] || !names["ask_user"] || !names["exit_plan_mode"] {
t.Fatalf("plan tool list: %v", names)
}
}
func TestDefaultModeStillRunsWrite(t *testing.T) {
c, _ := newExecEnv(t)
res := c.ExecuteTool(core.ToolCallRequest{CallID: "c1", Name: "write_artifact", Arguments: []byte(`{"name":"a.md","content":"x"}`)})
if res.Status != "ok" {
t.Fatalf("default write: %+v", res)
}
}
// TestConvAllowStoreKeysByName:SessionAllows 端口的键控口径保持「会话 × 工具名」,
// 不区分参数——与旧 sessionAllows 布尔参数的行为一致。
func TestConvAllowStoreKeysByName(t *testing.T) {
c, _ := newExecEnv(t)
conv := c.run.ConversationID
store := convAllowStore{mgr: c.mgr, conv: conv}
pol := agentpolicy.Policy{
Rules: []agentpolicy.Rule{{Effect: agentpolicy.EffectAsk, Tool: "run_shell"}},
SessionAllows: store,
}
if got := pol.Decide("run_shell", `{"command":"ls"}`, agentpolicy.Traits{}); got != agentpolicy.EffectAsk {
t.Fatalf("before remember: %q, want ask", got)
}
store.Remember("run_shell", `{"command":"ls"}`)
if got := pol.Decide("run_shell", `{"command":"rm -rf /tmp/x"}`, agentpolicy.Traits{}); got != agentpolicy.EffectAllow {
t.Fatalf("after remember, other args: %q, want allow(按名放行)", got)
}
if !c.mgr.sessionAllows(conv, "run_shell") {
t.Fatal("remember did not reach the manager table")
}
}
// TestPermissionAskRoutesToHITL:内核 ask 决策经 HITL 确认卡裁决;
// session 选项落会话放行,取消给出可读理由。
func TestPermissionAskRoutesToHITL(t *testing.T) {
c, _ := newExecEnv(t)
c.h = &runHandle{hitlCh: make(chan hitlAnswer, 1)}
call := agentllm.ToolCall{ID: "c1", Name: "run_shell", Arguments: `{"command":"ls"}`}
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "yes"}}
if v := c.PermissionAsk(context.Background(), call); !v.Allow {
t.Fatalf("yes verdict = %+v", v)
}
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "session"}}
if v := c.PermissionAsk(context.Background(), call); !v.Allow {
t.Fatalf("session verdict = %+v", v)
}
if !c.mgr.sessionAllows(c.run.ConversationID, "run_shell") {
t.Fatal("session choice did not remember the allow")
}
c.h.hitlCh <- hitlAnswer{resp: core.HITLResponse{ChoiceID: "no"}}
if v := c.PermissionAsk(context.Background(), call); v.Allow || v.Reason == "" {
t.Fatalf("no verdict = %+v", v)
}
}