Skip to content

fix(agent): reject duplicate client tool names - #32

Merged
alexb-openrouter merged 2 commits into
mainfrom
devin/1794512287-reject-duplicate-tool-names
Nov 12, 2026
Merged

alexb-openrouter merged 2 commits into
mainfrom
devin/1794512287-reject-duplicate-tool-names

Conversation

@alexb-openrouter

@alexb-openrouter alexb-openrouter commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Tool calls are dispatched by name to the first matching client tool (model_result.py, tool_executor.py, conversation_state.py). With two client tools sharing a name, the first one listed receives arguments meant for the other. That includes an approved call: pause with [trusted], resume with [other, trusted] and approve_tool_calls, and other runs with the approved arguments.

ModelResult.__init__ now calls assert_unique_client_tool_names(options["tools"]), which raises ValueError('Duplicate tool name "<name>" ...') before any request or tool execution. Server tools are skipped. This ports upstream @openrouter/agent 1.2.1 (assertUniqueClientToolNames).

Tests: tests/unit/test_duplicate_tool_names.py (the resume case fails on main and passes here); pytest tests/unit, ruff check, ruff format --check, mypy src tests clean.

Tool calls are dispatched by name to the first matching client tool, so two
tools with the same name let the first one receive arguments the model or an
approver meant for the other, including an approved call resumed with a
different tool list. call_model now raises when client tool names collide.
@devin-ai-integration

devin-ai-integration Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access that start with 'Devin'.

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: no blockers. Posting as COMMENT, not APPROVE, because the code was written by the same automation that is reviewing it; a human reviewer should approve.
Estimated impact: low. Apps that pass two client tools with the same name now get a ValueError from call_model instead of silently dispatching to the first one; that is a breaking change only for configurations that were already ambiguous.
Risk: implementation low, premise low, impact low.

Checked: ModelResult.options["tools"] is set once in __init__ and never reassigned (follow-up requests only rewrite tool_choice), so a check at construction covers every name lookup (_find_tool, tool_requires_approval, execute_tool, next_turn_params). Server tools are skipped, matching upstream.

Action items:

  • suggestion: no changelog entry. If this ships in the same patch release as the approval fix, add a line under that release in upstreamer-changelog.md (not in this diff, so not inline).
  • suggestion: the check rejects duplicates but does not bind a pending approval to the original tool, so resuming with a different tool under the same name (original removed) still runs the replacement. Upstream has the same limitation; consider a follow-up that stores a tool fingerprint with the pending call and re-checks it on resume.

@alexb-openrouter
alexb-openrouter merged commit 5dca174 into main Nov 12, 2026
7 checks passed
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.

2 participants