Symptom
A root session running claude-opus-5 fails an entire turn with an Anthropic 400:
'claude-opus-5' does not support tool types: computer_20250124
Two consecutive attempts, both fail, turn aborts, the user's message is never answered. Observed 2026-08-10 16:42 EDT on WSL2 (Ubuntu on Windows). Installed tree confirmed at cache dir amplifier-bundle-computer-use.git-ca4d97610e35db4c, HEAD eb26715, with upstream fix 9b9d034 confirmed present as an ancestor. This is NOT a recurrence of issue #1 — it survives that fix.
Root Cause (traced end-to-end, file:line)
A background, out-of-band provider call made by hooks-session-naming mutates the SHARED ComputerTool instance's resolved tool version between turns:
hooks-session-naming/__init__.py:37 — session naming defaults to model_role: "fast".
- Same file,
_call_provider() lines 477–503 — matches the provider by NAME ("anthropic"), so it grabs the very same anthropic provider OBJECT the main session uses. (Verified: active routing matrix is balanced; routing/balanced.yaml:52-56 maps fast → provider anthropic, model claude-haiku-4-5-20251001.)
- Lines 533–538 — builds
ChatRequest(..., model=model_override, ...) where model_override is the resolved FAST model (a Haiku variant), not the session's claude-opus-5.
- Line 549 — awaits
provider.complete(). hook-computer-use has already monkey-patched THAT SAME provider object's .complete (hook-computer-use/init.py:829), so this unrelated background call is silently routed through the wrapper too.
hook-computer-use/__init__.py:798–801 — _effective_model = request.model or provider.default_model. Here request.model IS set (to Haiku), so _effective_model becomes Haiku.
_note_model_on_computer_tool() lines 651–714 — does coordinator.get("tools", "computer") — the SAME shared ComputerTool instance as the main session — and calls tool.note_model(haiku_model).
ComputerTool.note_model() calls resolve_tool_version(): "a known model always wins" unconditionally overwrites _tool_version from computer_20251124 to computer_20250124 (providers.py:269,277 map claude-haiku-4-5 → computer_20250124).
native_tool_spec is read ONCE PER TURN, BEFORE provider.complete() runs. The corruption lands between turns, so the root session's very next turn builds tools[] with the stale computer_20250124 alongside model claude-opus-5 → 400.
Notably, hooks-session-naming's request declares ZERO tools. It has no business influencing the computer tool's resolved version at all.
Why issue #1 fix (9b9d034) doesn't cover it
9b9d034 closed two specific cases:
- Blind reliance on
ChatRequest.model when it's None in sub-agents
- One-turn-late priming (reading
native_tool_spec before complete() runs)
It did not anticipate a same-coordinator, same-provider-object background call from an unrelated hook that explicitly sets request.model to a different model while declaring no tools. The tool_versions.py module docstring already flags this general hazard class ("provider-anthropic's own model-fallback... silently switches models mid-session with no signal to this module"). This is that hazard realized in a different way.
Severity / Blast Radius
It's a race, not a one-off — hooks-session-naming fires on an interval (every 5 turns by default), so it can recur any time that interval lands in the window between turns. Blast radius is any session composing this bundle together with hooks-session-naming and a routing matrix whose fast role resolves to a model in a different computer-tool dialect than the session model. The failure kills the whole turn, not just the tool call.
Suggested Minimal Fix (proposed, not implemented)
In hook-computer-use's wrapped complete(), only call _note_model_on_computer_tool() when the outgoing request actually declares a computer-shaped tool (e.g. request.tools non-empty and containing a computer_* type). A request carrying no tools should never influence the resolved tool version. One-line guard; leaves tool_versions.py's resolution policy untouched.
Recovery
The session self-heals: the wrapper calls _note_model_on_computer_tool() with the correct effective model BEFORE delegating to the real complete(), so the failed attempt itself restores computer_20251124. The turn is lost but the next turn succeeds. Confirmed empirically — the affected session answered normally on the next message with no restart required.
Environment
- WSL2 (Ubuntu) on Windows
- Amplifier cache: amplifier-bundle-computer-use.git-ca4d97610e35db4c, HEAD eb26715
- Routing matrix:
balanced (default)
- Session model:
claude-opus-5
fast role model: claude-haiku-4-5-20251001
- Session id: 66b33f1c-41e3-4a62-b619-9a2f599894bf
Reported-by: External WSL2 user (same reporter as issue #1)
Related: Distinct from #1 (fixed by 9b9d034); same module, different corruption path
Symptom
A root session running
claude-opus-5fails an entire turn with an Anthropic 400:Two consecutive attempts, both fail, turn aborts, the user's message is never answered. Observed 2026-08-10 16:42 EDT on WSL2 (Ubuntu on Windows). Installed tree confirmed at cache dir amplifier-bundle-computer-use.git-ca4d97610e35db4c, HEAD eb26715, with upstream fix 9b9d034 confirmed present as an ancestor. This is NOT a recurrence of issue #1 — it survives that fix.
Root Cause (traced end-to-end, file:line)
A background, out-of-band provider call made by
hooks-session-namingmutates the SHAREDComputerToolinstance's resolved tool version between turns:hooks-session-naming/__init__.py:37— session naming defaults tomodel_role: "fast"._call_provider()lines 477–503 — matches the provider by NAME ("anthropic"), so it grabs the very same anthropic provider OBJECT the main session uses. (Verified: active routing matrix isbalanced; routing/balanced.yaml:52-56 maps fast → provider anthropic, model claude-haiku-4-5-20251001.)ChatRequest(..., model=model_override, ...)wheremodel_overrideis the resolved FAST model (a Haiku variant), not the session's claude-opus-5.provider.complete().hook-computer-usehas already monkey-patched THAT SAME provider object's .complete (hook-computer-use/init.py:829), so this unrelated background call is silently routed through the wrapper too.hook-computer-use/__init__.py:798–801—_effective_model = request.model or provider.default_model. Here request.model IS set (to Haiku), so _effective_model becomes Haiku._note_model_on_computer_tool()lines 651–714 — doescoordinator.get("tools", "computer")— the SAME shared ComputerTool instance as the main session — and callstool.note_model(haiku_model).ComputerTool.note_model()callsresolve_tool_version(): "a known model always wins" unconditionally overwrites _tool_version from computer_20251124 to computer_20250124 (providers.py:269,277 map claude-haiku-4-5 → computer_20250124).native_tool_specis read ONCE PER TURN, BEFOREprovider.complete()runs. The corruption lands between turns, so the root session's very next turn builds tools[] with the stale computer_20250124 alongside model claude-opus-5 → 400.Notably,
hooks-session-naming's request declares ZERO tools. It has no business influencing the computer tool's resolved version at all.Why issue #1 fix (9b9d034) doesn't cover it
9b9d034 closed two specific cases:
ChatRequest.modelwhen it'sNonein sub-agentsnative_tool_specbeforecomplete()runs)It did not anticipate a same-coordinator, same-provider-object background call from an unrelated hook that explicitly sets
request.modelto a different model while declaring no tools. The tool_versions.py module docstring already flags this general hazard class ("provider-anthropic's own model-fallback... silently switches models mid-session with no signal to this module"). This is that hazard realized in a different way.Severity / Blast Radius
It's a race, not a one-off —
hooks-session-namingfires on an interval (every 5 turns by default), so it can recur any time that interval lands in the window between turns. Blast radius is any session composing this bundle together withhooks-session-namingand a routing matrix whosefastrole resolves to a model in a different computer-tool dialect than the session model. The failure kills the whole turn, not just the tool call.Suggested Minimal Fix (proposed, not implemented)
In
hook-computer-use's wrappedcomplete(), only call_note_model_on_computer_tool()when the outgoing request actually declares a computer-shaped tool (e.g.request.toolsnon-empty and containing a computer_* type). A request carrying no tools should never influence the resolved tool version. One-line guard; leavestool_versions.py's resolution policy untouched.Recovery
The session self-heals: the wrapper calls
_note_model_on_computer_tool()with the correct effective model BEFORE delegating to the realcomplete(), so the failed attempt itself restores computer_20251124. The turn is lost but the next turn succeeds. Confirmed empirically — the affected session answered normally on the next message with no restart required.Environment
balanced(default)claude-opus-5fastrole model:claude-haiku-4-5-20251001Reported-by: External WSL2 user (same reporter as issue #1)
Related: Distinct from #1 (fixed by 9b9d034); same module, different corruption path