fix(computer-use): stop dropping tool name when action is present#217
fix(computer-use): stop dropping tool name when action is present#217gzak wants to merge 1 commit into
Conversation
d923bc9 to
fae0a85
Compare
_derive_connector_type(tool_name, action) discarded tool_name whenever
action was present, returning only "computer_use.{action}" — so every
actioned Computer Use tool call collapsed to the same connector_type
shape regardless of which tool (computer vs. any other actioned tool)
triggered it.
MCPCheckInputRequest/MCPCheckOutputRequest now carry a genuine two-field
(server, tool) identity contract (epic #2905, platform sub-issue #2904).
Client.mcp_check_input/mcp_check_output gain a `tool` parameter sent as
its own wire field.
_derive_connector_type is removed; ComputerUseGovernor now passes
connector_type=tool_name and tool=action directly to mcp_check_input,
so neither value is discarded. tool_name is chosen as the
connector_type-equivalent (rather than a fixed "computer_use" literal)
because it matches the rest of the SDK's convention of bare category
names for connector_type (e.g. "postgres", "snowflake") with no
namespace prefix, and because action already varies meaningfully within
a single tool (e.g. "left_click" vs "screenshot" for "computer") while
tool_name is the more stable partition key. check_result() has no
action available at its call site, so tool is left unset there,
matching its pre-existing behavior of not being able to distinguish
sub-actions on output checks.
Signed-off-by: Gregory Zak <gzak@getaxonflow.com>
fae0a85 to
92fb7cf
Compare
|
R3 review (master + independent hostile pass). The core fix is verified correct — previously BLOCKER — CI is red
MEDIUM — semantic drift needs an epic-level ruling before mergeThis PR sends MEDIUM — stale contract claims + #216 collision
MEDIUM — hard policy break with no escape hatch
|
Summary
_derive_connector_type(tool_name, action)inaxonflow/adapters/computer_use.pydiscardedtool_namewheneveractionwas present, returning onlyf"computer_use.{action}". Every actioned Computer Use tool call (e.g.computerwithaction="left_click") collapsed to the sameconnector_typeshape regardless of which actual tool triggered it —connector_typecouldn't distinguish tool identity for any actioned tool. The non-actioned branch correctly preservedtool_name; only the actioned branch had the bug.Fixes #2910 (epic #2905). Depends on the same platform two-field
(server, tool)identity contract as #2906 (platform sub-issue #2904, R3-approved/CI-green, not yet merged to axonflow-enterprise).Design decision (as requested in the sub-issue)
The two-field contract has exactly two slots (
connector_type,tool), but there were three meaningful pieces of information floating around: the"computer_use"domain marker (previously just a hardcoded string prefix),tool_name(e.g."computer","bash","text_editor"), andaction(e.g."left_click","screenshot", only present for some tools).I mapped:
connector_type=tool_name(bare, e.g."computer") — matches the rest of the SDK's existing convention forconnector_typevalues (bare category names like"postgres","snowflake", no namespace prefix — seelanggraph.py'sMCPInterceptorOptionsdefault ofrequest.server_name). The"computer_use."prefix is dropped since it added no distinguishing information oncetool_nameis preserved in full.tool=action(when present, else unset) — this is the piece that genuinely varies within a single tool_name (e.g."computer"can be"left_click"or"screenshot"), so it fits the "tool" slot's role as the finer-grained identity component.This means
tool_nameis never dropped (previously it was, wheneveractionwas set), andactionis preserved as its own field instead of being folded into a single derived string._derive_connector_typeis removed entirely;ComputerUseGovernor.check_tool_usenow passesconnector_type=name, tool=tooldirectly tomcp_check_input.check_result()has noactionavailable at its call site (its signature only takestool_name), sotoolis simply left unset there — no regression, since that call site never had action info to begin with.Also (shared with #2906, since both fixes need the wire-level
toolfield and are independent/unordered PRs):axonflow.client.Client.mcp_check_input/mcp_check_output(async and sync) gain atool: str | None = Noneparameter, sent as its owntoolfield only when non-empty.MCPCheckInputRequest/MCPCheckOutputRequestgain a matching optionaltoolfield.tests/fixtures/wire_shape_baseline.jsonacknowledgestoolas an SDK-superset field on both request models (mirrors the existingcontent_typeentry) until #2904 merges to axonflow-enterprise. Justified per CONTRIBUTING.md's baseline burndown policy — blocked on the platform-side PR landing, not something this PR can burn down.Module docstring at the top of
computer_use.pywas reviewed for staleness — it doesn't referenceconnector_typederivation at all, so no update was needed there.Test plan
TestDeriveConnectorType(tested the removed helper directly) withTestConnectorTypeToolSplit, assertingconnector_typeandtoolare sent as two separate, correct values for actioned and non-actioned tools, including a test that two calls to the same tool with different actions produce the sameconnector_typebut differenttoolvalues (exactly what the old bug broke).TestCheckToolUse/TestCheckResultassertions that referenced the old"computer_use.*"concatenated shape.pytest tests/test_computer_use.py— 29 passedpytest— 1047 passed, 29 skippedruff check/ruff format --checkon changed files — cleanmypy axonflow— no new errors (one pre-existing unrelated error inheartbeat.py)