fix(code): route orchestrated tool calls through the confirmation gate - #2853
Conversation
CodeAgent's orchestrator ran tools by pulling the callable straight out of _TOOL_REGISTRY, bypassing Agent._execute_tool entirely. That skipped the user-confirmation guardrail for every gated tool the orchestrator touched, including run_shell_command and write_file, and any MCP tool registered by a co-resident agent in the same process. The executor now delegates to _execute_tool, so orchestrated calls take the same path as the agent loop's: the gate, name resolution, bounded execution and error formatting. A denial is a user decision, not a transient fault, so it is threaded through as its own outcome rather than a generic failure. ItemExecutionResult carries a denied flag, the error handler never retries a denied item, and the orchestrator stops replanning instead of queuing another prompt for the same work. Result parsing now reads the base agent's status field alongside the legacy success key, so a denied or errored call is no longer mistaken for success by tools that return a bare payload dict. Also fixes warnings being dropped when a checklist exits early — they are now collected before the exit checks, so a stopped run still reports what the completed items produced.
|
Verdict: Approve with suggestions — safe to merge; one gap worth closing in this PR. This PR fixes a real guardrail bypass: CodeAgent's orchestrator was calling the tool registry directly, so shell/file-mutating tools ( The one thing to fix: the new regression test that pins this guardrail ( Real-world evidence
The evidence supports the verdict: the guardrail demonstrably holds and the timeout budgets are real. It also independently surfaced the CI-wiring gap below. 🔍 Technical details🟡 ImportantNew guardrail regression test never runs in CI ( The whole point of this PR is a durable security guardrail, and Notes (non-blocking)
Strengths
|
… lane The test pinning the orchestrator to the confirmation gate was not in any workflow, so it guarded nothing — the bypass could return without CI noticing. It is fast and needs no LLM, so it joins the existing gated validator/guardrail set.
|
Fixed in 9b579b4 — good catch. A regression test no workflow runs guards nothing, which defeats the point of adding it.
|
CodeAgent's orchestrator ran tools by pulling the callable straight out of
_TOOL_REGISTRY, skippingAgent._execute_tooland with it the user-confirmation guardrail — sorun_shell_command,write_fileand any MCP tool registered by a co-resident agent in the same process all executed with no prompt during orchestrated runs. Orchestrated calls now take the same path as the agent loop's, so the same tools prompt whether they are invoked directly or through a checklist.Same vulnerability class as #2846, which fixes it for MCP tools in the base agent; this is the CodeAgent half. Reported via responsible disclosure.
A denial is treated as a user decision rather than a transient fault: it is never retried, it stops the checklist, and the orchestrator stops replanning instead of queuing another prompt for the same work. Result parsing now reads the base agent's
statusfield alongside the legacysuccesskey, so a denied or errored call can no longer be mistaken for success by tools that return a bare payload dict.Also fixes warnings being dropped when a checklist exits early — a stopped run now still reports what the completed items produced.
Test plan
python -m pytest hub/agents/code/python/tests/test_tool_executor_confirmation.py -q— 16 tests, 19 subtestsgaia-codegeneration task through the orchestrator and confirm non-gated tools are unaffectedpython util/lint.py --all