package runmgr // EXT-14 红测(提案 2026-10-09-ext-g1-b1-b3 §2):带 exposeAsTool 的命令投影为 // 回合内工具;模型调用经命令总分发(Entrance="agent"、同审计信封);不带字段的 // 命令不出现;高危命令在对话内弹内联确认卡(HITL 收口不变)。 import ( "context" "testing" "agent-system-backend/internal/tools" core "agent-system-core" ) // fakeBusSource EXT-14 测试桩:命令清单 + 分发记录(说话用 runmgr 视图类型, // 与生产代码同口径——runmgr 不依赖 cmdbus)。 type fakeBusSource struct { cmds []CommandTool lastReq CommandRequest resp CommandResponse } func (f *fakeBusSource) CommandTools() []CommandTool { return f.cmds } func (f *fakeBusSource) CommandToolsFor(owner string) []CommandTool { return f.cmds } func (f *fakeBusSource) DispatchCommand(ctx context.Context, req CommandRequest) CommandResponse { f.lastReq = req return f.resp } func TestCommandToolsProjected(t *testing.T) { rc0, _ := newExecEnv(t) m := rc0.mgr src := &fakeBusSource{cmds: []CommandTool{ {ID: "rdm.trace", Description: "查追溯网", Params: []byte(`{"type":"object"}`), Risk: "low"}, }} m.SetCommandSource(src) rc := rc0 tools := rc.Tools() found := map[string]bool{} for _, d := range tools { found[d.Name] = true } if !found["rdm.trace"] { t.Fatalf("exposeAsTool 命令未投影为工具: %+v", tools) } if found["rdm.hidden"] { t.Fatal("命令源之外的工具不得出现") } } func TestCommandToolDispatchesThroughBus(t *testing.T) { rc0, _ := newExecEnv(t) m := rc0.mgr src := &fakeBusSource{ cmds: []CommandTool{{ID: "rdm.trace", Params: []byte(`{"type":"object"}`), Risk: "low"}}, resp: CommandResponse{State: "committed"}, } m.SetCommandSource(src) rc := rc0 res := rc.ExecuteTool(core.ToolCallRequest{CallID: "c1", Name: "rdm.trace", Arguments: []byte(`{"q":"x"}`)}) if res.Status != "ok" { t.Fatalf("dispatch result = %s %s", res.Status, res.Output) } req := src.lastReq if req.ID != "rdm.trace" || req.Entrance != "agent" || req.Actor != rc.run.Owner || req.Owner != rc.run.Owner || req.ConversationID != rc.run.ConversationID { t.Fatalf("bus request = %+v(须 Entrance=agent、Actor/Owner=会话 owner、带会话)", req) } if req.Params["q"] != "x" { t.Fatalf("params = %+v", req.Params) } // 未注册命令源时:调用按注册表口径报错(现状),不 panic。 reg2 := tools.NewRegistry() if err := reg2.RegisterBuiltin(); err != nil { t.Fatal(err) } m2 := New(nil, nil, reg2, nil, nil, nil, nil, nil, nil) rc2 := &runCtx{mgr: m2, run: core.Run{Owner: "bob"}, prof: core.AgentProfile{Tools: core.ToolPolicy{Allow: []string{"*"}}}} if res := rc2.ExecuteTool(core.ToolCallRequest{CallID: "c2", Name: "rdm.trace", Arguments: []byte(`{}`)}); res.Status != "error" { t.Fatalf("no source: %s(须 registry miss 报错)", res.Status) } }