Repository navigation
fix(agent): reject duplicate client tool names - #32
Conversation
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.
|
I'll fix CI failures and address comments from users with write access that start with 'Devin'.
|
There was a problem hiding this comment.
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.
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]andapprove_tool_calls, andotherruns with the approved arguments.ModelResult.__init__now callsassert_unique_client_tool_names(options["tools"]), which raisesValueError('Duplicate tool name "<name>" ...')before any request or tool execution. Server tools are skipped. This ports upstream@openrouter/agent1.2.1 (assertUniqueClientToolNames).Tests:
tests/unit/test_duplicate_tool_names.py(the resume case fails onmainand passes here);pytest tests/unit,ruff check,ruff format --check,mypy src testsclean.