Why: N1 increment three (WP1/WP4) - agents need a canonical spec format with plugin restrictions, hosts need typed input/output/tool guardrails, delegation needs lifecycle events, and squad adapters need to pass the permission ask port through. Evidence: go vet clean; go test ./... 26 packages ok, including new agentspec (12 tests), guardrails (5), loop_guardrails (5), and subagent hook tests (3). Impact: new runtime/agentspec and runtime/guardrails packages; loop consumes UserPromptSubmit outcomes (deny skips the loop, modify appends the rewrite); subagent gains RegisterWithHooks emitting SubagentStart/Stop around delegation; GateOptions gains PermissionAsk forwarded into the kernel config. All additions are opt-in: nil ports preserve prior behavior exactly. Refs: Documents proposal 2026-10-07-agent-stack-n1q1i1-plans.md WP1/WP4
61 lines
1.8 KiB
Go
61 lines
1.8 KiB
Go
package subagent
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"agent-native/runtime/hooks"
|
|
"agent-native/runtime/tools"
|
|
)
|
|
|
|
func TestDelegateEmitsStartAndStop(t *testing.T) {
|
|
var seen []hooks.Event
|
|
bus := hooks.New()
|
|
bus.On(func(c hooks.Call) hooks.Outcome {
|
|
seen = append(seen, c.Event)
|
|
return hooks.Outcome{}
|
|
})
|
|
reg := tools.NewRegistry(t.TempDir())
|
|
RegisterWithHooks(reg, func(ctx context.Context, req Request) (Result, error) {
|
|
return Result{Success: true, Text: "done"}, nil
|
|
}, bus)
|
|
res := reg.Execute(context.Background(), "delegate_task", json.RawMessage(`{"goal":"核对第三章来源","done_criteria":"逐条标注"}`))
|
|
if res.IsError {
|
|
t.Fatalf("delegate result: %s", res.Content)
|
|
}
|
|
if len(seen) != 2 || seen[0] != hooks.SubagentStart || seen[1] != hooks.SubagentStop {
|
|
t.Fatalf("events = %v", seen)
|
|
}
|
|
}
|
|
|
|
func TestDelegateStopMarksFailure(t *testing.T) {
|
|
failed := false
|
|
bus := hooks.New()
|
|
bus.On(func(c hooks.Call) hooks.Outcome {
|
|
if c.Event == hooks.SubagentStop {
|
|
failed = c.IsError
|
|
}
|
|
return hooks.Outcome{}
|
|
})
|
|
reg := tools.NewRegistry(t.TempDir())
|
|
RegisterWithHooks(reg, func(ctx context.Context, req Request) (Result, error) {
|
|
return Result{Success: false, Text: "nope"}, nil
|
|
}, bus)
|
|
reg.Execute(context.Background(), "delegate_task", json.RawMessage(`{"goal":"x","done_criteria":"y"}`))
|
|
if !failed {
|
|
t.Fatal("stop event did not mark the failure")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWithoutBusKeepsSilent(t *testing.T) {
|
|
reg := tools.NewRegistry(t.TempDir())
|
|
Register(reg, func(ctx context.Context, req Request) (Result, error) {
|
|
return Result{Success: true, Text: "ok"}, nil
|
|
})
|
|
res := reg.Execute(context.Background(), "delegate_task", json.RawMessage(`{"goal":"x","done_criteria":"y"}`))
|
|
if res.IsError {
|
|
t.Fatalf("result: %s", res.Content)
|
|
}
|
|
}
|