Skip to content

fix(runtime): decouple tool dispatch from permission to prevent duplicate tool_result#96

Merged
yishuiliunian merged 1 commit intomainfrom
fix/tool-dispatch-duplicate-result
Apr 11, 2026
Merged

fix(runtime): decouple tool dispatch from permission to prevent duplicate tool_result#96
yishuiliunian merged 1 commit intomainfrom
fix/tool-dispatch-duplicate-result

Conversation

@yishuiliunian
Copy link
Copy Markdown
Contributor

Summary

  • Introduce ToolDispatch enum (Pipeline | RunnerDirect) on the Tool trait to separate execution strategy from permission level
  • Runner-direct tools (AskUser, EnterPlanMode, ExitPlanMode) were both early-started by feed_tool AND intercepted by the runner, producing duplicate ToolResult blocks with the same tool_use_id — causing Anthropic API 400 errors
  • feed_tool now checks dispatch() before early-starting, with a defensive filter in Phase 3

Changes

  • crates/loopal-tool-api/src/tool.rs — new ToolDispatch enum + dispatch() trait method (default: Pipeline)
  • crates/tools/agent/ask-user/src/lib.rs, plan-mode/src/lib.rs — override dispatch()RunnerDirect
  • crates/loopal-runtime/src/agent_loop/streaming_tool_exec.rsfeed_tool skips RunnerDirect tools
  • crates/loopal-runtime/src/agent_loop/tools.rs — Phase 3 defensive filter on intercepted_indices
  • New tests: dispatch_test.rs (unit), turn_test.rs (E2E), registry_test.rs (contract)

Test plan

  • bazel test //... — 51 tests pass
  • bazel build //... --config=clippy — zero warnings
  • CI passes

…cate tool_result (#96)

ReadOnly permission was conflated with "safe to early-start" execution
strategy. Runner-direct tools (AskUser, EnterPlanMode, ExitPlanMode)
were both early-started by feed_tool AND intercepted by the runner,
producing duplicate ToolResult blocks with the same tool_use_id.
This caused Anthropic API 400 errors.

Introduce ToolDispatch enum (Pipeline | RunnerDirect) on the Tool trait
to separate execution strategy from permission level. feed_tool now
checks dispatch() before early-starting, and Phase 3 adds a defensive
filter for intercepted indices.
@yishuiliunian yishuiliunian merged commit a646856 into main Apr 11, 2026
3 checks passed
@yishuiliunian yishuiliunian deleted the fix/tool-dispatch-duplicate-result branch April 11, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant