From 9aee6e769a19fcf21493f9f76c967bc3ff5557de Mon Sep 17 00:00:00 2001 From: Claudomator Agent Date: Wed, 8 Jul 2026 09:31:15 +0000 Subject: feat(agentloop,executor): expose report_verdict as a tool on both transports --- internal/agentloop/tools.go | 23 +++++++++++++++++++++++ internal/executor/agentmcp.go | 15 +++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/internal/agentloop/tools.go b/internal/agentloop/tools.go index 32dd284..2bdf0e0 100644 --- a/internal/agentloop/tools.go +++ b/internal/agentloop/tools.go @@ -122,6 +122,18 @@ func agentToolSpecs() []provider.ToolSpec { "required": []string{"role"}, }, }, + { + Name: "report_verdict", + Description: "Report a structured approve/reject decision after evaluating another task's work (e.g. as an arbitration-role task). Call this before report_summary when your job is to decide whether the work is acceptable — this is read by the orchestrator to decide the outcome, not just logged for a human to read later.", + ParametersJSONSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "approved": map[string]any{"type": "boolean", "description": "true if the work meets its acceptance criteria and should proceed; false if it needs to be sent back for a fix"}, + "reasoning": strProp("a concise explanation of the decision"), + }, + "required": []string{"approved", "reasoning"}, + }, + }, { Name: "read_file", Description: "Read the contents of a file in the sandbox working directory.", @@ -263,6 +275,17 @@ func (l *Loop) dispatchTool(ctx context.Context, name, argsJSON string) (result } return fmt.Sprintf("Proposed role config %s v%d (draft)", a.Role, version), false, nil + case "report_verdict": + var a struct { + Approved bool `json:"approved"` + Reasoning string `json:"reasoning"` + } + _ = json.Unmarshal([]byte(argsJSON), &a) + if rvErr := l.Channel.ReportVerdict(ctx, a.Approved, a.Reasoning); rvErr != nil { + return "", false, rvErr + } + return "Verdict recorded.", false, nil + case "read_file": var a struct { Path string `json:"path"` diff --git a/internal/executor/agentmcp.go b/internal/executor/agentmcp.go index 0b36c14..35781e2 100644 --- a/internal/executor/agentmcp.go +++ b/internal/executor/agentmcp.go @@ -106,6 +106,11 @@ func (in proposeRoleConfigInput) toRoleConfig() role.RoleConfig { } } +type reportVerdictInput struct { + Approved bool `json:"approved" jsonschema:"true if the work meets its acceptance criteria and should proceed; false if it needs to be sent back for a fix"` + Reasoning string `json:"reasoning" jsonschema:"a concise explanation of the decision"` +} + func textResult(text string) *mcp.CallToolResult { return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: text}}} } @@ -197,6 +202,16 @@ func newAgentServer(ch AgentChannel) *mcp.Server { return textResult(fmt.Sprintf("Proposed role config %s v%d (draft)", in.Role, version)), nil, nil }) + mcp.AddTool(s, &mcp.Tool{ + Name: "report_verdict", + Description: "Report a structured approve/reject decision after evaluating another task's work (e.g. as an arbitration-role task). Call this before report_summary when your job is to decide whether the work is acceptable -- this is read by the orchestrator to decide the outcome, not just logged for a human to read later.", + }, func(ctx context.Context, _ *mcp.CallToolRequest, in reportVerdictInput) (*mcp.CallToolResult, any, error) { + if err := ch.ReportVerdict(ctx, in.Approved, in.Reasoning); err != nil { + return nil, nil, err + } + return textResult("Verdict recorded."), nil, nil + }) + return s } -- cgit v1.2.3