feat(models): add claude-opus-5 + fix interactive /effort on Anthropic#746
Merged
Conversation
Register `claude-opus-5` and make `--effort` / `/effort` actually work on the interactive path, so `clawcodex --model claude-opus-5 --effort xhigh` runs at xhigh on a Claude subscription. ## claude-opus-5 registration `_model_supports_extended_thinking` already matched opus-5 by regex, so without a registration the loop fell through to the `budget_tokens` branch — REMOVED on Opus 5, i.e. a 400 on every request. Two quieter consequences: `--effort` was dropped (opus-5 was off the effort allowlist) and the context window resolved to the 200K default instead of 1M, the early-compaction handicap #730 removed for opus-4-8. * MODEL_CONFIGS row (1M context, 5/25 per MTok, 32K first-attempt wire max_tokens per the existing convention) + pricing tier and family prefix + both provider available-model lists. * The three query.py gates: adaptive thinking, effort, xhigh. Wire-probed 2026-07-25 over subscription OAuth: `claude-opus-5` with adaptive thinking accepts `effort: xhigh` AND `max` (200, end_turn), so the xhigh allowlist entry is measured, not documentation-derived. The comments that said "unprobed" now say so only of fable-5. ## Interactive /effort was broken on Anthropic Two independent defects, both invisible at runtime: 1. `_do_set_effort` validated against a hardcoded `(minimal|low|medium|high)`, so `/effort xhigh` and `/effort max` were REJECTED in the TUI while the same values worked via `--effort`, `/effort` on the other surfaces, and settings.effort. `minimal` was accepted despite being a GPT-5 level absent from every other surface — and actively harmful here: it is not in VALID_THINKING_EFFORT_LEVELS, so resolve_thinking_effort treats it as "nothing requested" and substitutes settings.effort, i.e. `/effort minimal` could emit `max` while the TUI echoed "minimal". The ladder is now VALID_EFFORT_VALUES. 2. Every provider got `extra_body={"reasoning_effort": …}`. That is the OpenAI-compat shape; the Anthropic wire rejects it outright (probed: `400 — reasoning_effort: Extra inputs are not permitted`), so a single `/effort` broke every following request in an interactive Anthropic session. New `_turn_effort_routing` sends Anthropic down `thinking_effort` → `output_config.effort` (with the per-model gating, so xhigh still clamps to high where unsupported) and keeps `_EffortProvider` for OpenAI-compat. Not wrapping Anthropic providers also un-breaks the `isinstance(provider, AnthropicProvider)` checks that agent_loop_compat worked around and advisor.py did not. ## --effort now applies interactively The flag was plumbed only into HeadlessOptions, so the interactive path parsed and discarded it. It now rides cli/launcher → backend argv → AgentServerConfig → the session's /effort level, on all four entry paths (`clawcodex`, `clawcodex tui`, `--print-connect`, `agent-server --stdio`). Validated and lowercased at the seed as well as via argparse `choices=`, because --stdio callers reach the config without a parser. ## Also * `is_anthropic_wire()` in src/providers replaces a predicate that was copy-pasted at three call sites and had to agree at all of them. * `_EffortProvider.__getattr__` guards `_inner`: `copy.copy` built an uninitialized instance and recursed to RecursionError, which two live callers swallowed as `except Exception` — silently running subagents on the session model instead of their override. * `/effort` and `/thinking off` now each warn that the other discards effort (it rides inside the thinking block on this wire). * The TUI advertised `[minimal|low|medium|high|auto|ultracode]`; it now advertises the real ladder, and the effort badge next to the model name has a producer for the first time (backend emits `reasoning_effort`). Tests: 8827 passed. Every hop of the flag chain and both halves of the routing split are individually revert-sensitive (verified by deleting each line and confirming exactly the intended test fails). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes
clawcodex --model claude-opus-5 --effort xhighwork on a Claude subscription — interactively, not just headless. Three separate defects stood in the way.1.
claude-opus-5was unregistered_model_supports_extended_thinkingalready matches opus-5 by regex, so the loop fell through to thebudget_tokensbranch — removed on Opus 5, so every request 400'd. Quieter side effects:--effortwas dropped (opus-5 was off the effort allowlist) and the context window resolved to the 200K default instead of 1M, the early-compaction handicap #730 removed for opus-4-8.Registered in
MODEL_CONFIGS(1M context, 5/25 per MTok),pricing.py, both provider model lists, and the threequery.pygates.Wire-probed 2026-07-25 over subscription OAuth:
claude-opus-5+ adaptive thinking acceptseffort: xhighandmax(200,end_turn). The xhigh entry is measured, not inferred; the "unprobed" comments now scope that to fable-5 only.2. Interactive
/effortwas broken on Anthropic_do_set_effortvalidated against a hardcoded(minimal|low|medium|high)— so/effort xhighand/effort maxwere rejected in the TUI while the same values worked via--effortand every other surface. Meanwhileminimal(a GPT-5 level) was accepted, and because it isn't inVALID_THINKING_EFFORT_LEVELS,resolve_thinking_efforttreated it as "nothing requested" and substitutedsettings.effort—/effort minimalcould emitmaxwhile the TUI echoed "minimal". The ladder is nowVALID_EFFORT_VALUES.extra_body={"reasoning_effort": …}, which is the OpenAI-compat shape. The Anthropic wire rejects it:400 — reasoning_effort: Extra inputs are not permitted(probed). One/effortbroke every following request in an interactive Anthropic session. New_turn_effort_routingroutes Anthropic throughthinking_effort→output_config.effort(per-model gating intact, so xhigh still clamps to high where unsupported) and keeps_EffortProviderfor OpenAI-compat.Not wrapping Anthropic providers also un-breaks the
isinstance(provider, AnthropicProvider)checks thatagent_loop_compatworked around andadvisor.pydid not.3.
--effortdidn't apply interactively at allPlumbed only into
HeadlessOptions, so the interactive path parsed and discarded it. Now: cli/launcher → backend argv →AgentServerConfig→ the session's/effortlevel, across all four entry paths (clawcodex,clawcodex tui,--print-connect,agent-server --stdio). Validated and lowercased at the seed as well as via argparsechoices=, because--stdiocallers reach the config without a parser.Also
is_anthropic_wire()insrc/providersreplaces a predicate copy-pasted at three sites that all had to agree._EffortProvider.__getattr__guards_inner:copy.copybuilt an uninitialized instance and recursed toRecursionError, which two live callers swallowed asexcept Exception— silently running subagents on the session model instead of their override./effortand/thinking offeach now warn that the other discards effort.[minimal|low|medium|high|auto|ultracode]; it now advertises the real ladder, and the effort badge beside the model name has a producer for the first time.Verification
test_connection_error_re_raises, a pre-existing hang confirmed on cleanmain).tsc --noEmitclean; ui-tui tests at their 8-failed pre-existing baseline, no new failures.--model claude-opus-5 --effort xhighon subscription returns a real answer; the interactive control path accepts/effort xhighand routes it asthinking_effort='xhigh'with the provider unwrapped.Known follow-ups (not in scope here)
claude-sonnet-5has the identicalbudget_tokens400 today — same defect class, unregistered.src/utils/advisor.py's advisor allowlist is opus-4-6/sonnet-4-6 only; the TS reference is two models ahead and carries@[MODEL LAUNCH]markers the port lacks.src/models/aliases.py'sopusalias still resolves to Opus 4, soAgent(model="opus")subagents don't follow the session model./effortdoesn't persist tosettings.effort, so subagents don't inherit it — now consistent with--efforton both paths, but a product decision worth making.🤖 Generated with Claude Code