Skip to content

fix(computer-use): stop dropping tool name when action is present#217

Open
gzak wants to merge 1 commit into
mainfrom
feat/2910-computer-use-tool-name-fix
Open

fix(computer-use): stop dropping tool name when action is present#217
gzak wants to merge 1 commit into
mainfrom
feat/2910-computer-use-tool-name-fix

Conversation

@gzak

@gzak gzak commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

_derive_connector_type(tool_name, action) in axonflow/adapters/computer_use.py discarded tool_name whenever action was present, returning only f"computer_use.{action}". Every actioned Computer Use tool call (e.g. computer with action="left_click") collapsed to the same connector_type shape regardless of which actual tool triggered it — connector_type couldn't distinguish tool identity for any actioned tool. The non-actioned branch correctly preserved tool_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"), and action (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 for connector_type values (bare category names like "postgres", "snowflake", no namespace prefix — see langgraph.py's MCPInterceptorOptions default of request.server_name). The "computer_use." prefix is dropped since it added no distinguishing information once tool_name is 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_name is never dropped (previously it was, whenever action was set), and action is preserved as its own field instead of being folded into a single derived string. _derive_connector_type is removed entirely; ComputerUseGovernor.check_tool_use now passes connector_type=name, tool=tool directly to mcp_check_input. check_result() has no action available at its call site (its signature only takes tool_name), so tool is 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 tool field and are independent/unordered PRs):

  • axonflow.client.Client.mcp_check_input / mcp_check_output (async and sync) gain a tool: str | None = None parameter, sent as its own tool field only when non-empty.
  • MCPCheckInputRequest / MCPCheckOutputRequest gain a matching optional tool field.
  • tests/fixtures/wire_shape_baseline.json acknowledges tool as an SDK-superset field on both request models (mirrors the existing content_type entry) 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.py was reviewed for staleness — it doesn't reference connector_type derivation at all, so no update was needed there.

Test plan

  • Replaced TestDeriveConnectorType (tested the removed helper directly) with TestConnectorTypeToolSplit, asserting connector_type and tool are 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 same connector_type but different tool values (exactly what the old bug broke).
  • Updated existing TestCheckToolUse / TestCheckResult assertions that referenced the old "computer_use.*" concatenated shape.
  • pytest tests/test_computer_use.py — 29 passed
  • Full suite: pytest — 1047 passed, 29 skipped
  • ruff check / ruff format --check on changed files — clean
  • mypy axonflow — no new errors (one pre-existing unrelated error in heartbeat.py)

@gzak
gzak force-pushed the feat/2910-computer-use-tool-name-fix branch from d923bc9 to fae0a85 Compare July 16, 2026 22:45
_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>
@gzak
gzak force-pushed the feat/2910-computer-use-tool-name-fix branch from fae0a85 to 92fb7cf Compare July 16, 2026 22:51
@saurabhjain1592

Copy link
Copy Markdown
Member

R3 review (master + independent hostile pass). The core fix is verified correct — previously _derive_connector_type discarded the tool name whenever an action existed; now both survive, with good unit + live-wire coverage (actioned/non-actioned/same-tool-different-action/check_result). Must-fix:

BLOCKER — CI is red

lint fails (run 29540706653) with 12 NEW falsey-clobber findings, mostly in files this PR doesn't touch (client.py:1682/1723/1781/2299, computer_use.py:175/209) — line-number baseline drift. Regenerate .lint_baselines/falsey_clobber.json in this PR.

MEDIUM — semantic drift needs an epic-level ruling before merge

This PR sends tool = ACTION (left_click) with connector_type = tool name (computer, bash), while the four langgraph PRs send tool = tool name. On a new platform, audit rows here read tool_server="bash" / tool_name="left_click"bash is not a server, and the meaning of the tool_name audit column now depends on which adapter emitted it. If intended, record the decision on epic #2905; if not, tool should carry the tool name and the action belongs elsewhere (e.g. operation/statement).

MEDIUM — stale contract claims + #216 collision

wire_shape_baseline.json note + types.py comment say #2904 is "not yet merged" — it merged 2026-07-16 (enterprise c8df2006b). Update, and reconcile with #216's edits to the same lines (guaranteed conflict; #216's text is the correct one).

MEDIUM — hard policy break with no escape hatch

connector_type loses the computer_use. namespace entirely (computer_use.bashbash) on EVERY platform version, so existing policies/per-connector settings scoped to computer_use.* stop applying — and this adapter has no connector_type_fn escape hatch. The changelog migration note is the only mitigation; it's currently absent. Add it (Go #189 has the model text).

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