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.
91 lines
3.2 KiB
Go
91 lines
3.2 KiB
Go
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)
|
||
}
|
||
}
|