Summary
The interactive Shift+Tab thinking-level cycle (TUI model picker) hides the max effort tier for several Claude models that actually support it — most visibly Claude Opus 4.7 / 4.8. The cycle offers off / low / medium / high / xhigh and never reaches max.
This is a gating bug only: authored thinking_budget values (e.g. adaptive/max) are still sent correctly to the API. It affects only the user-selectable Shift+Tab cycle.
Reproduction
- Configure an agent:
models:
anthropic-large:
provider: anthropic
model: claude-opus-4-8
thinking_budget: adaptive/max
- Run the TUI; the header correctly shows
thinking auto adaptive.
- Press Shift+Tab to cycle thinking levels.
- Observed: cycle is
off → low → medium → high → xhigh → (wrap). max is never offered.
- Expected:
max is offered, since Anthropic lists max as supported on Opus 4.8.
Root cause
anthropicTopEffort() in pkg/modelinfo/thinking_levels.go returns a single highest effort Level, implicitly assuming the selectable tiers form a linear ladder. For Opus 4.7+ it returns XHigh:
switch {
case minor >= 7:
return effort.XHigh
case minor == 6:
return effort.Max
default:
return ""
}
thinkingLevelMap() then declares only that one tier as supported:
m := effort.LevelMap{effort.Minimal: false}
if top := anthropicTopEffort(modelID); top != "" {
m[top] = true
}
Because Max is an explicit-only tier (explicitOnlyLevels = {XHigh, Max} in pkg/effort/effort.go) and the map only declares XHigh: true, SupportedLevels() drops Max. Net result for Opus 4.7/4.8: off / low / medium / high / xhigh.
The deeper problem: xhigh and max are independent capabilities, not a single ladder. A single returned "top tier" cannot represent a model that supports both xhigh and max (Opus 4.7/4.8, Fable 5), nor distinguish it from a model that supports max without xhigh (Opus 4.6, Sonnet 4.6).
Authoritative reference
Anthropic — Effort: https://platform.claude.com/docs/en/build-with-claude/effort
Per-level model availability, quoted from that page:
max — Claude Fable 5, Claude Mythos 5, Claude Opus 4.8, Claude Mythos Preview, Claude Opus 4.7, Claude Opus 4.6, Claude Sonnet 4.6
xhigh — Claude Fable 5, Claude Mythos 5, Claude Opus 4.8, Claude Opus 4.7
Resulting capability matrix:
| Model |
low |
medium |
high |
xhigh |
max |
| Opus 4.5 |
✓ |
✓ |
✓ |
— |
— |
| Sonnet 4.6 |
✓ |
✓ |
✓ |
— |
✓ |
| Opus 4.6 |
✓ |
✓ |
✓ |
— |
✓ |
| Opus 4.7 |
✓ |
✓ |
✓ |
✓ |
✓ |
| Opus 4.8 |
✓ |
✓ |
✓ |
✓ |
✓ |
| Fable 5 |
✓ |
✓ |
✓ |
✓ |
✓ |
Affected models (cycle gating incorrect)
- Opus 4.7, Opus 4.8 —
max missing (cycle caps at xhigh)
- Sonnet 4.6 —
max missing (cycle caps at high)
- Fable 5 —
max missing (cycle caps at xhigh)
- Mythos 5 / Mythos Preview — unrecognized (cycle caps at
high)
Correct today: Opus 4.6 (max, no xhigh) and Opus 4.5 (high).
Not affected (scope clarification)
- The request path in
pkg/model/provider/anthropic/thinking.go sends adaptive/<effort> verbatim (e.g. effort max), so authored configs already behave correctly.
RejectsTokenThinking() correctly coerces Opus 4.6/4.7/4.8 onto adaptive thinking.
agent-schema.json already accepts max / adaptive/max.
Suggested fix
- Replace the single-
Level return of anthropicTopEffort() with independent capability flags (e.g. supportsXHigh, supportsMax), and have thinkingLevelMap() set both effort.XHigh and effort.Max in the LevelMap where applicable.
- Add table-driven tests covering each Claude tier (Opus 4.5 / 4.6 / 4.7 / 4.8, Sonnet 4.6, Fable 5, and the Mythos variants).
Relevant files:
pkg/modelinfo/thinking_levels.go
pkg/effort/effort.go
pkg/runtime/model_switcher.go (consumer of SupportedThinkingLevels)
Summary
The interactive Shift+Tab thinking-level cycle (TUI model picker) hides the
maxeffort tier for several Claude models that actually support it — most visibly Claude Opus 4.7 / 4.8. The cycle offersoff / low / medium / high / xhighand never reachesmax.This is a gating bug only: authored
thinking_budgetvalues (e.g.adaptive/max) are still sent correctly to the API. It affects only the user-selectable Shift+Tab cycle.Reproduction
thinking auto adaptive.off → low → medium → high → xhigh → (wrap).maxis never offered.maxis offered, since Anthropic listsmaxas supported on Opus 4.8.Root cause
anthropicTopEffort()inpkg/modelinfo/thinking_levels.goreturns a single highest effortLevel, implicitly assuming the selectable tiers form a linear ladder. For Opus 4.7+ it returnsXHigh:thinkingLevelMap()then declares only that one tier as supported:Because
Maxis an explicit-only tier (explicitOnlyLevels = {XHigh, Max}inpkg/effort/effort.go) and the map only declaresXHigh: true,SupportedLevels()dropsMax. Net result for Opus 4.7/4.8:off / low / medium / high / xhigh.The deeper problem:
xhighandmaxare independent capabilities, not a single ladder. A single returned "top tier" cannot represent a model that supports bothxhighandmax(Opus 4.7/4.8, Fable 5), nor distinguish it from a model that supportsmaxwithoutxhigh(Opus 4.6, Sonnet 4.6).Authoritative reference
Anthropic — Effort: https://platform.claude.com/docs/en/build-with-claude/effort
Per-level model availability, quoted from that page:
max— Claude Fable 5, Claude Mythos 5, Claude Opus 4.8, Claude Mythos Preview, Claude Opus 4.7, Claude Opus 4.6, Claude Sonnet 4.6xhigh— Claude Fable 5, Claude Mythos 5, Claude Opus 4.8, Claude Opus 4.7Resulting capability matrix:
Affected models (cycle gating incorrect)
maxmissing (cycle caps atxhigh)maxmissing (cycle caps athigh)maxmissing (cycle caps atxhigh)high)Correct today: Opus 4.6 (
max, noxhigh) and Opus 4.5 (high).Not affected (scope clarification)
pkg/model/provider/anthropic/thinking.gosendsadaptive/<effort>verbatim (e.g. effortmax), so authored configs already behave correctly.RejectsTokenThinking()correctly coerces Opus 4.6/4.7/4.8 onto adaptive thinking.agent-schema.jsonalready acceptsmax/adaptive/max.Suggested fix
Levelreturn ofanthropicTopEffort()with independent capability flags (e.g.supportsXHigh,supportsMax), and havethinkingLevelMap()set botheffort.XHighandeffort.Maxin theLevelMapwhere applicable.Relevant files:
pkg/modelinfo/thinking_levels.gopkg/effort/effort.gopkg/runtime/model_switcher.go(consumer ofSupportedThinkingLevels)