ci / vet-and-test (pull_request) Successful in 41s
Why: User rulings 1 and 3. The direct conversation path exposed only run_shell/run_python, so any file edit meant rewriting and re-running whole generation scripts (diagnosis: 24 calls with 9 failures in one turn, 6KB scripts regenerated 8 times). The kernel already ships line-numbered reads, exact edit/multi_edit with read-before-write, grep and glob; they were wired only into the squad path. Deliverables had no editable source: docx output came from one-shot python scripts, so "make section 3 more detailed" rebuilt everything. Now: WorkspaceToolsCheckpointed (sandbox = conversation workspace, checkpoint hooks wired) is registered into the direct run and its decls join the tool surface (plan mode keeps the read-only subset). Word deliverables get a markdown-source pipeline - python3 -m housestyle.build renders .md deterministically via docx_kit and runs lint + outline on every build (write-time diagnostics, exit 1 on error so the model fixes the source and rebuilds). run_python kits remain for xlsx/pptx/images (ruling 3). base-tools/base-deliverables prompts and the house-style SKILL document the new loop. Evidence: SELF - TestDirectRunExposesKernelWorkspaceTools (read->edit->grep against the real sandbox FS), TestDirectRunWithoutSandboxHidesKernelTools, three new BuildTests in recipes (renders+lint passes, heading-skip errors exit 1, CLI entry); go vet + go test ./... pass; live before/after on the golden task set recorded on the phase03 task card per the policy clause. Impact: Direct conversations can now read and edit workspace files surgically; docx deliverables become edit-source + rebuild instead of regenerate. No contract or storage changes.
72 lines
2.2 KiB
Go
72 lines
2.2 KiB
Go
package runtime
|
||
|
||
import (
|
||
"context"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
agentllm "agent-native/runtime/llm"
|
||
core "agent-system-core"
|
||
)
|
||
|
||
// 裁定 1:会话沙箱配置时,内核工作区工具(read/edit/multi_edit/grep/glob/list)
|
||
// 进入直连工具面;计划模式只留只读子集。
|
||
func TestDirectRunExposesKernelWorkspaceTools(t *testing.T) {
|
||
sandbox := t.TempDir()
|
||
if err := os.WriteFile(filepath.Join(sandbox, "note.txt"), []byte("alpha\nbeta\n"), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rc := &fakeRunCtx{
|
||
ctx: context.Background(), sandbox: sandbox,
|
||
prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}},
|
||
}
|
||
// 第一步 read_file(行号分页),第二步 edit_file(须先读过),第三步 grep。
|
||
turns := padTurns(
|
||
textDeltaTurn("", toolCall("read_file", `{"path":"note.txt"}`)),
|
||
textDeltaTurn("", toolCall("edit_file", `{"path":"note.txt","old":"alpha","new":"gamma"}`)),
|
||
textDeltaTurn("", toolCall("grep", `{"pattern":"gamma"}`)),
|
||
textDeltaTurn("改完了。"),
|
||
)
|
||
r := &LLMRunner{Stream: scriptStream(turns, nil)}
|
||
if err := r.Execute(rc); err != nil {
|
||
t.Fatalf("Execute: %v", err)
|
||
}
|
||
raw, err := os.ReadFile(filepath.Join(sandbox, "note.txt"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if !strings.Contains(string(raw), "gamma") || strings.Contains(string(raw), "alpha") {
|
||
t.Fatalf("edit_file did not apply: %q", string(raw))
|
||
}
|
||
found := 0
|
||
for _, p := range rc.assistant {
|
||
if p.ToolResult != nil && p.ToolResult.Summary != "" {
|
||
found++
|
||
}
|
||
}
|
||
if found < 3 {
|
||
t.Fatalf("workspace tool results missing from assistant parts: %+v", rc.assistant)
|
||
}
|
||
}
|
||
|
||
// 无沙箱(未配置工作区)时内核工具不注册,模型看不到。
|
||
func TestDirectRunWithoutSandboxHidesKernelTools(t *testing.T) {
|
||
rc := &fakeRunCtx{
|
||
ctx: context.Background(),
|
||
prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}},
|
||
}
|
||
r := &LLMRunner{Stream: scriptStream(textTurns("你好。"), nil)}
|
||
if err := r.Execute(rc); err != nil {
|
||
t.Fatalf("Execute: %v", err)
|
||
}
|
||
for _, p := range rc.assistant {
|
||
if p.ToolCall != nil && p.ToolCall.Name == "read_file" {
|
||
t.Fatalf("read_file must not be visible without a sandbox")
|
||
}
|
||
}
|
||
}
|
||
|
||
var _ = agentllm.RoleUser
|