Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@
**ThinkingBindingControls:**
- Purpose: Controls how Fable 5.1, Opus 5.5, Sonnet 5.5, and Haiku 5.5 handle replayed thinking whose original conversation prefix changed after host compaction or prompt transformation
- Location: `packages/core/src/thinking-binding.ts`
- Pattern: Shared outgoing-body inspection recognizes non-empty signed `thinking` and `redacted_thinking` blocks. When an OAuth request replays thinking from an earlier assistant response, changes to the messages, instructions or tools before that response can cause Anthropic to reject its signature. For Fable 5.1, Opus 5.5, Sonnet 5.5, and Haiku 5.5 requests using adaptive thinking, `thinkingBinding.prefixMismatchBehavior` controls that case: `account-default` sends no override and leaves the decision to Anthropic, `error` requests rejection of mismatched thinking, and `drop_block` requests removal of the affected thinking blocks. Both explicit settings add `thinking.block_binding.prefix_mismatch_behavior` and beta `thinking-binding-controls-2026-08-01`. Sonnet 5.5's `between_tools` mode rejects this field and does not receive it. First turns, Mythos 5.1, older models, and API-key routes remain unchanged.
- Pattern: Shared outgoing-body inspection recognizes non-empty signed `thinking` and `redacted_thinking` blocks. When an OAuth request replays thinking from an earlier assistant response, changes to the messages, instructions or tools before that response can cause Anthropic to reject its signature. For Fable 5.1, Opus 5.5, Sonnet 5.5, and Haiku 5.5 requests using adaptive thinking, `thinkingBinding.prefixMismatchBehavior` controls that case: `account-default` sends no override and leaves the decision to Anthropic, `error` requests rejection of mismatched thinking, and `drop_block` requests removal of the affected thinking blocks. Both explicit settings add `thinking.block_binding.prefix_mismatch_behavior`. OAuth main requests advertise `thinking-binding-controls-2026-08-01` regardless of whether a binding field is present, matching Claude Code 2.1.302; auxiliary title/quota lists omit it. Sonnet 5.5's `between_tools` mode rejects this field and does not receive it. First turns, Mythos 5.1, older models, and API-key routes receive no new binding field.

**MidConversationOutputConfig:**
- Purpose: Preserves Fable 5.1, Opus 5.5 and Sonnet 5.5 prompt-cache prefixes while users change reasoning effort between turns
- Location: `packages/core/src/mid-conversation-output-config.ts`, with host history adapters in `packages/opencode/src/effort-history.ts` and `packages/pi/src/effort-history.ts`
- Pattern: The top-level `output_config.effort` remains anchored to the earliest retained effort. Later changes become empty `role: "system"` messages immediately before their user/tool-result boundary. OAuth Fable 5.1 requests always include `mid-conversation-output-config-2026-07-01`. OAuth Opus 5.5 and Sonnet 5.5 requests always include `per-turn-control-2026-07-01`, the beta Claude Code sends for those two models, and receive effort markers only with adaptive thinking (Sonnet 5.5 `between_tools` rejects a changed per-message effort). For those models a request without an effort change is left as the host built it, and a turn without a selected effort uses the model default (`medium` on Opus 5.5, `high` on Sonnet 5.5). Anthropic also documents per-message effort for Mythos 5.1, Opus 5 and Haiku 5.5, but there is no Claude Code capture to match, so those models, other models and API-key routes get neither beta nor markers. Each model family keeps its own timeline, so turns sent to another family in the same session are ignored. Before OpenCode lowers host records into Anthropic messages, it adds stable, checksum-bound transition markers to lowerable user boundaries and, when transitions exist, a plan-bound anchor at the current user boundary. The messages hook records the expected timeline in a bounded request tracker; the later `chat.headers` hook attaches a fixed-size plan through an internal header after every message transform has run and retains it for automatic retries of the same message. The fetch transform validates the lowered markers against that trusted timeline. A current-boundary anchor may precede a trailing user-role `tool_result` only when every such block matches a `tool_use` in the immediately preceding assistant. Consecutive host user records can merge into one wire user message: the flat marker sequence must still match the plan, and the last transition on that boundary supplies the effective effort. If a downstream transform legitimately trims a history prefix, the surviving markers must be an exact suffix and the current-boundary anchor must match the original plan; consumed transitions are folded into the retained top-level baseline. A bounded, message-revocable history retains an older request plan if the same user message is re-planned after compaction while its header is still in flight. Total marker loss without a surviving authenticated anchor remains fail-closed: the remaining body cannot establish that only a prefix was removed. Holes, reorderings, mutations, or a missing/mismatched anchor still fail locally before cache shaping, billing-suffix derivation, dumping, signing, or transport. A missing-all refusal logs only its marker count, resolved-plan status, last retained user index/block counts and expected anchor hash; no prompt content or marker bytes are emitted. The transform strips all markers and the internal header before dispatch. Stable marker identity prevents a downstream transform's persisted source text from resurrecting an obsolete per-pass nonce. This avoids both assistant-record count assumptions and transform-order coupling in tool-heavy or compacted histories. Pi rebuilds transitions directly from active compaction-aware session entries on each turn.
- Pattern: The top-level `output_config.effort` remains anchored to the earliest retained effort. Later changes become empty `role: "system"` messages immediately before their user/tool-result boundary. OAuth Fable 5.1, Opus 5.5 and Sonnet 5.5 requests always include `per-turn-control-2026-07-01`, matching Claude Code 2.1.302. Opus 5.5 and Sonnet 5.5 receive effort markers only with adaptive thinking (Sonnet 5.5 `between_tools` rejects a changed per-message effort). For those models a request without an effort change is left as the host built it, and a turn without a selected effort uses the model default (`medium` on Opus 5.5, `high` on Sonnet 5.5). Haiku 5.5 also carries the per-turn-control beta to match Claude Code 2.1.302, but still uses top-level effort without markers. Mythos 5.1, Opus 5, other models and API-key routes get neither this beta nor effort markers. Each model family keeps its own timeline, so turns sent to another family in the same session are ignored. Before OpenCode lowers host records into Anthropic messages, it adds stable, checksum-bound transition markers to lowerable user boundaries and, when transitions exist, a plan-bound anchor at the current user boundary. The messages hook records the expected timeline in a bounded request tracker; the later `chat.headers` hook attaches a fixed-size plan through an internal header after every message transform has run and retains it for automatic retries of the same message. The fetch transform validates the lowered markers against that trusted timeline. A current-boundary anchor may precede a trailing user-role `tool_result` only when every such block matches a `tool_use` in the immediately preceding assistant. Consecutive host user records can merge into one wire user message: the flat marker sequence must still match the plan, and the last transition on that boundary supplies the effective effort. If a downstream transform legitimately trims a history prefix, the surviving markers must be an exact suffix and the current-boundary anchor must match the original plan; consumed transitions are folded into the retained top-level baseline. A bounded, message-revocable history retains an older request plan if the same user message is re-planned after compaction while its header is still in flight. Total marker loss without a surviving authenticated anchor remains fail-closed: the remaining body cannot establish that only a prefix was removed. Holes, reorderings, mutations, or a missing/mismatched anchor still fail locally before cache shaping, billing-suffix derivation, dumping, signing, or transport. A missing-all refusal logs only its marker count, resolved-plan status, last retained user index/block counts and expected anchor hash; no prompt content or marker bytes are emitted. The transform strips all markers and the internal header before dispatch. Stable marker identity prevents a downstream transform's persisted source text from resurrecting an obsolete per-pass nonce. This avoids both assistant-record count assumptions and transform-order coupling in tool-heavy or compacted histories. Pi rebuilds transitions directly from active compaction-aware session entries on each turn.

**FableFallbackManager:**
- Purpose: Provides deterministic 10-successful-response Opus 4.8 recovery as the default server policy's refusal backstop and as the exclusive policy under `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy`
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ This repo is a CortexKit-maintained Anthropic auth monorepo for OpenCode and Pi.
### Claude Code identity

- OAuth requests now present as Claude Code 2.1.302 instead of 2.1.280, matched against an interactive 2.1.302 capture: `User-Agent: claude-cli/2.1.302 (external, cli)`, `x-stainless-package-version: 0.128.0`, and the 2.1.302 billing block. A request for a user prompt now also carries `cc_turn_origin=human; cc_prompt_index=N; cc_turn_index=N;`, where `N` counts the prompts in the conversation and stays the same for tool-result follow-ups, plus the `x-claude-code-prompt-id` header. Every request carries `x-claude-code-request-class` (`main`, or `auxiliary` for title, summary and quota-window requests). The `cc_version` suffix and `cch` rules are unchanged.
- Beta lists follow 2.1.302: `claude-code-20250219` comes first in the full-agent list, `per-turn-control-2026-07-01` sits directly after `prompt-caching-scope-2026-01-05` and is now also sent on Fable 5.1 and Haiku 5.5, and full-agent requests add `mid-conversation-tool-changes-2026-07-01`, `mid-conversation-system-clear-at-2026-08-21` and `thinking-binding-controls-2026-08-01`. Betas Claude Code only sends behind a feature flag (`advisor-tool`, `fallback-credit`, `mid-conversation-system`) and `mid-conversation-output-config` for Fable 5.1 effort changes are kept. `thinking-display-updates-2026-08-18` is not sent because thinking stays `display: "summarized"`.
- Beta lists now follow Claude Code 2.1.302's request classes rather than body-shape heuristics: all main turns use the captured main list, even without tools or companion fields. Auxiliary one-token quota probes use the quota list; other auxiliary requests use title/summary betas, including redact-thinking. JSON schema, fast mode, context-window and server-side-fallback feature betas remain request-dependent. This deliberately replaces the previous shape-distinction contract and its tests.
- Main requests advertise `thinking-binding-controls-2026-08-01` even without replayed thinking; the binding field itself still requires replayed thinking and an explicit setting. `per-turn-control-2026-07-01` follows prompt-caching-scope (after claude-code on Haiku 5.5) on Sonnet 5.5, Opus 5.5, Fable 5.1 and Haiku 5.5. Fable 5.1 effort changes use it with the same empty system messages and `output_config.effort`. The obsolete mid-conversation-system and mid-conversation-output-config betas are no longer sent. Advisor-tool and fallback-credit are also removed: the plugin enables neither an advisor nor a fallback credit code. `server-side-fallback` is unchanged. `thinking-display-updates-2026-08-18` is also sent on main requests, so no baseline beta difference remains from the capture. The body keeps `thinking.display: "summarized"` where Claude Code sends `"updates"`, because `"updates"` streams no thinking text; the beta alone does not suppress summarized thinking.

### Request compression

Expand Down
12 changes: 10 additions & 2 deletions docs/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ OpenCode's OAuth model costs are zeroed by default because subscription use is q

Fable/Mythos reasoning uses `thinking: { type: "adaptive", display: "summarized" }` with `output_config.effort`, not manual `budget_tokens`. Opus 5/5.5 and Sonnet 5/5.5 also request summarized adaptive thinking so the host can display reasoning.

OpenCode exposes native `low`, `medium`, `high`, `xhigh` and `max` effort variants for Fable 5.1, Opus 5/5.5, Sonnet 5.5 and Haiku 5.5. On OAuth routes, effort changes for Fable 5.1, Opus 5.5 and Sonnet 5.5 are carried as mid-conversation system messages rather than rewriting the cached prefix. Opus 5.5 and Sonnet 5.5 need adaptive thinking for this; Sonnet 5.5 `between_tools` keeps the host's top-level effort. Opus 5, Haiku 5.5 and Mythos 5.1 still change the top-level effort. OpenCode 2 supplies its own system messages recording each effort change at that point in the conversation; the plugin does not insert a second copy of those effort-change system messages or alter earlier cached history.
OpenCode exposes native `low`, `medium`, `high`, `xhigh` and `max` effort variants for Fable 5.1, Opus 5/5.5, Sonnet 5.5 and Haiku 5.5. On OAuth routes, effort changes for Fable 5.1, Opus 5.5 and Sonnet 5.5 are carried as empty mid-conversation system messages with `output_config.effort`, under `per-turn-control-2026-07-01`, rather than rewriting the cached prefix. Opus 5.5 and Sonnet 5.5 need adaptive thinking for this; Sonnet 5.5 `between_tools` keeps the host's top-level effort. Opus 5, Haiku 5.5 and Mythos 5.1 still change the top-level effort; Haiku 5.5 carries the per-turn-control beta to match Claude Code but does not receive effort markers. OpenCode 2 supplies its own system messages recording each effort change at that point in the conversation; the plugin does not insert a second copy of those effort-change system messages or alter earlier cached history.

- **Opus 5.5:** adaptive thinking is always on. Disabled thinking and manual budgets are replaced with summarized adaptive thinking. Forced tool choice is removed.
- **Opus 5:** an explicit disabled-thinking request is preserved; incompatible `xhigh`/`max` effort is capped to `high`.
Expand All @@ -50,7 +50,15 @@ Anthropic associates replayed reasoning blocks with the messages, instructions a
| `error` | Ask Anthropic to reject a prefix mismatch |
| `drop_block` | Ask Anthropic to discard mismatched thinking blocks |

For an explicit `error` or `drop_block` value, the plugin sends the selected server policy as `thinking.block_binding` and enables the thinking-binding beta, but only when adaptive thinking replays a signed `thinking` or nonempty `redacted_thinking` block. It does not apply to Sonnet's `between_tools` mode or a first turn without replayed thinking. See [settings ownership](configuration.md#settings).
For an explicit `error` or `drop_block` value, the plugin sends the selected server policy as `thinking.block_binding`, but only when adaptive thinking replays a signed `thinking` or nonempty `redacted_thinking` block. The field does not apply to Sonnet's `between_tools` mode or a first turn without replayed thinking. OAuth main requests advertise the thinking-binding beta even without a binding field, matching Claude Code 2.1.302; advertising support alone does not select a server policy. See [settings ownership](configuration.md#settings).

## OAuth beta lists

Beta selection follows the request class, not the presence of tools or companion body fields. Conversation turns use Claude Code 2.1.302's main list, including `thinking-binding-controls-2026-08-01`. Auxiliary one-token, non-streaming quota probes without thinking, tools or structured output use the smaller quota list; other auxiliary requests use the title/summary list, including `redact-thinking-2026-02-12`. JSON-schema requests additionally advertise structured output; fast mode, server-side fallback and context-window feature betas retain their existing request-dependent selection.

The main list sends `per-turn-control-2026-07-01` for Sonnet 5.5, Opus 5.5, Fable 5.1 and Haiku 5.5, but not Opus 4.8. Haiku 5.5 places `claude-code-20250219` after the prompt-caching-scope beta. The obsolete mid-conversation-system and mid-conversation-output-config betas are absent. Advisor-tool and fallback-credit are also absent because the plugin neither enables an advisor nor supplies a fallback credit code. `server-side-fallback` is a separate request feature and is unchanged.

The beta lists match the capture, including `thinking-display-updates-2026-08-18` on main requests. Our bodies keep `thinking.display: "summarized"` where Claude Code sends `"updates"`, because `"updates"` streams no thinking text. The beta alone does not suppress summarized thinking. The binding fields and effort-marker bodies are unchanged.

## Claude fast mode

Expand Down
Loading
Loading