🤖 feat: integrate GPT-5.6 Sol/Terra/Luna with native max effort and pro-mode toggle#3708
Conversation
|
@codex review |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: acb47ad1d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f437558e8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f74e2c4651
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 75ffab3124
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
1 similar comment
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0cd3ee5d6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d8fe65c16
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51e82436c3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
51e8243 to
0043870
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0043870b08
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
1 similar comment
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca2606390f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 929ab42526
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: baefa06202
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
…agent types When a parent workspace stores per-agent AI settings, pro mode is toggled on the parent's ACTIVE agent bucket, so spawning a task with a different agentId (e.g. exec parent -> explore child) resolved the child bucket and dropped the user's pro choice. Fall back to the parent's persisted selected-agent bucket (default exec), then legacy workspace settings.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 16823b59c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The continuation options built in onBackgroundRunTerminal copied the turn's model/thinkingLevel/providerOptions but not reasoningMode, so a pro-mode GPT-5.6 turn that ran a dynamic workflow resumed in standard mode.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 847231a4ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The workspace-turn path only read the exec bucket + legacy settings for reasoningMode, so a turn launched while a non-exec parent agent had pro enabled silently dropped to standard. Fall back to the parent's active-agent bucket, mirroring resolveTaskAISettings. Also clarify the chat-completions effort comment: "none" is a first-class member of @ai-sdk/openai's Chat Completions enum (only "max" is missing), so it must NOT be stripped there — omitting it would default GPT-5.6 back to medium reasoning.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5f12ca0fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
resolveTaskAISettings clamped inherited/requested thinking levels without the providers config, so mapped GPT-5.6 aliases (mappedToModel, e.g. openai:team-sol -> gpt-5.6-sol) fell back to the default four-level ladder and max/xhigh tasks were silently downgraded to high before sending and persisting. Thread aiService.getProvidersConfig() through the clamp and the numeric override resolver (resolveThinkingInput now accepts providersConfig), including the workspace-turn path.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 77f0ce5db5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Plan -> Exec handoff read only legacy workspace.aiSettings for reasoningMode, but a PRO toggle during the plan phase persists under aiSettingsByAgent.plan — resolve the still-current plan agent's bucket first (red/green regression test included). ACP sessions built prompt options with only model/thinkingLevel/agentId, so workspaces with reasoningMode: "pro" ran every ACP prompt in standard mode. ResolvedAiSettings now carries the optional field (populated from workspace metadata buckets in refreshSessionState) and all ACP send sites (prompt, skill, fork/new start messages, compaction options + follow-up) thread it through.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26abe11575
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
When the wrapper was called as fetch(new Request(...)) with headers/body on the Request and no init, it saw an empty header set, took the fast path, never injected reasoning.mode, and let the internal x-mux-openai-reasoning-mode header leak upstream inside the Request. Read headers/method/body from the same source the underlying fetch uses (init members override the Request's per fetch semantics; Request bodies are read from a clone), and pass the stripped header set via init so it overrides Request-input headers on every forwarding path.
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(model, providersConfig ?? null)) call after #3708 added alias resolution to each. Extract a private getExplicitThinkingPolicyForModel helper; behavior-preserving.
Summary
First-class integration of OpenAI's GPT-5.6 family (released July 9, 2026): Sol (flagship), Terra (balanced), and Luna (fast/cheap) — all with a 1.05M-token context window and 272K-threshold long-context pricing — become built-in models with
gpt/sol,terra, andlunaaliases; the baregpt-5.5entry is retired (GPT-5.5 Pro stays). On top of that, a per-workspace PRO reasoning-mode toggle delivers the Responses API's newreasoning.mode: "pro", available on every GPT-5.6 model.Background
maxreasoning effort above xhigh, and areasoning.mode: "standard" | "pro"field orthogonal to effort ("Sol Pro" is the same model ID served in pro mode; per-token pricing unchanged). Early preview coverage calledmaxSol-only and pro Sol/Terra-only; the GA reasoning guide and API changelog confirm both are family-wide (Sol/Terra/Luna and the baregpt-5.6alias), and the GA model pages publish 1.05M contexts plus a 272K long-context pricing tier (2x input / 1.5x output for the full request, cache writes 1.25x the active input rate) — folded in during review round 8.797d5a254) and Sonnet 5 integration (e1169d7f5).Implementation
Model family — registry entries (
knownModels.ts), flat-rate pricing incl. 1.25x cache writes / 90%-off reads (models-extra.ts; no long-context tier published, unlike GPT-5.5), thinking policy (Sol: 6 levels incl.max; Terra/Luna: 5), Codex OAuth allow-list, docs + regenerated skill content. The legacygpt-5.5stats entry is retained so historical sessions keep pricing.Sol-native
maxeffort —@ai-sdk/openaiaccepts the effort as a plain string, so a model-awaregetOpenAIReasoningEffort()mapsmax -> "max"on Sol only (all other OpenAI models keepmax -> "xhigh"), applied at both providerOptions call sites (direct + gateway).Pro mode — the AI SDK drops unknown provider-option keys, so
reasoning.moderides the proven Anthropic-xhigh pattern:buildRequestHeadersemits a Mux-internalx-mux-openai-reasoning-modeheader (passthrough routes only), and a strip-first fetch wrapper rewrites the JSON body (reasoning.mode: "pro", preserving effort/summary) on Responses-shaped requests. Codex OAuth routes get defense-in-depth: no injection at composition time ANDnormalizeCodexResponsesBodydeletes the field (the ChatGPT backend is stricter; a silent standard-mode no-op beats a hard 4xx). Setting is an optionalreasoningModeon workspace AI settings (legacy configs parse unchanged), threaded along the exact paththinkingLeveltakes, including backend-initiated turns (startup retry, compaction, continuations, heartbeats). UI: a small PRO chip next to the thinking slider (pro-capable models only, hidden ≤420px container width) plus a "Toggle Pro Reasoning Mode" command-palette action.Validation
llmDebugLogs: the Responses APIresponse.createdevent echoes the effective config — direct route with PRO on echoes"reasoning":{...,"mode":"pro"}; PRO off echoes"mode":"standard"; Sol at max thinking sendsreasoningEffort: "max"and OpenAI accepts it. Cost telemetry priced a 9,987-token Sol turn at $0.05 ($5/M ✓); context meter shows 1M.reasoning.mode(the gateway's own SDK filters unknown keys — same root cause as the client-side SDK). Pro mode currently only reaches the wire on direct-API-key routing; needs a gateway-side change to close.openaiProModeAvailableunit table.)Risks
gptalias now resolves togpt-5.6-sol; users pinned toopenai:gpt-5.5are unaffected (stats retained), but bare-gptusers get the new model silently — intended, matches prior promotions.reasoningModeis optional everywhere; wholesale-replace clobbering atupdateAgentAISettingscall sites was audited and fixed via merge-preserve.📋 Implementation Plan — GPT-5.6 Sol/Terra/Luna family
Plan: Integrate OpenAI GPT-5.6 Sol / Terra / Luna into Mux
Context
Per the implementation brief (source material; launch facts are brief-derived, not independently verified): OpenAI released the GPT-5.6 family on July 9, 2026 — Sol (
gpt-5.6-sol, flagship), Terra (gpt-5.6-terra, balanced), Luna (gpt-5.6-luna, fast/cheap). All are Responses-API models with vision, function calling, and reasoning; also served via Codex. Sol introduces a new nativemaxreasoning effort (Sol only). Follow repo precedent from commits797d5a254(gpt-5.5 promotion / gpt-5.4 retirement) ande1169d7f5(Sonnet 5).Verified repo facts (via exploration):
src/common/constants/knownModels.ts:GPTentry (lines ~89–95) isopenai / gpt-5.5, aliases["gpt", "gpt-5.5"],warm: true,tokenizerOverride: "openai/gpt-5".GPT_PROisgpt-5.5-pro.MODEL_ABBREVIATIONSis derived from entry aliases.src/common/utils/tokens/models-extra.ts:"gpt-5.5"entry (lines ~216–240) uses tiered pricing (*_above_200k_tokens,tiered_pricing_threshold_tokens: 272000); entries conform toModelData(Record<string, ModelData>), which includescache_creation_input_token_cost.src/common/utils/thinking/policy.ts:getExplicitThinkingPolicystrips provider/namespace prefixes viastripModelProviderPrefixes, then matches regexes like/^gpt-5\.(?:2|5)-pro(?!-[a-z])/(negative lookahead tolerates-2026-04-23date suffixes but rejects-mini-style variants). GPT-5.5 gets["off","low","medium","high","xhigh"]via/^gpt-5\.(?:4|5)(?:-(?:mini|nano))?(?!-[a-z])/.src/common/types/thinking.ts:THINKING_LEVELS = ["off","low","medium","high","xhigh","max"].OPENAI_REASONING_EFFORTmapsmax -> "xhigh"for ALL OpenAI models (lines ~273–280).getThinkingDisplayLabelreturns"XHIGH"for bothxhigh/maxon anyopenai:-prefixed model; Anthropic native-xhigh is detected byanthropicSupportsNativeXhigh(modelString)(lines ~229–238), the pattern to mirror.getThinkingOptionLabelderives fromgetThinkingDisplayLabel.src/common/utils/ai/providerOptions.ts: twoOPENAI_REASONING_EFFORT[effectiveThinking]call sites — direct OpenAI branch (~line 356) and the OpenAI-origin-via-gateway branch (~line 508,github-copilotoptions).modelStringis in scope at both.src/common/constants/codexOAuth.ts:CODEX_OAUTH_ALLOWED_MODELS(lines ~91–101),CODEX_OAUTH_REQUIRED_MODELS(only spark),CODEX_OAUTH_CONTEXT_WINDOW_OVERRIDES(onlygpt-5.5: 272_000).docs/config/models.mdx: built-in models table rows at lines ~21–28; prose at ~75–78 (/gpt-pro+0→ GPT-5.5 Pro minimum) refers to GPT_PRO and stays valid.formatModelDisplayName("gpt-5.6-sol")already yields"GPT-5.6 Sol"— no modelDisplay changes.src/node/services/providerService.tsfirst-time mux-gateway model list includesopenai/gpt-5.5— out of scope, stays unchanged.797d5a254also regeneratedsrc/node/services/agentSkills/builtInSkillContent.generated.ts(docs snapshot embedded in built-in skills) — must re-run the generator after touchingdocs/config/models.mdx.Non-goals (from the brief)
promptCacheKey).providerService.ts),DEFAULT_MODEL(stays OPUS), gpt-5.4-mini/nano/codex retirements, or terminal-bench CI model configs.Implementation steps
0. Pre-implementation safety grep
src/, excluding tests) forgpt-5.5,OPENAI_REASONING_EFFORT, and GPT-5.x regexes to catch any model-gated helper the brief didn't list. Expected retained refs:models-extra.ts, GPT-5.5 Pro entries,providerService.tsfirst-time gateway defaults, comments.supportsOpenAIReasoningSummary(used in the OpenAI branch ofproviderOptions.ts) and any other GPT-5.x whitelist: if gpt-5.6 would miss intended existing GPT-5 behavior (e.g. reasoning summaries), extend the match — only where GPT-5.5 already gets the behavior and GPT-5.6 clearly should inherit it. Report anything ambiguous instead of guessing.1.
src/common/constants/knownModels.ts(~net +14 LoC)GPTentry: keep keyGPT(comment: "GPT alias tracks the latest stable GPT tier"),provider: "openai",providerModelId: "gpt-5.6-sol",aliases: ["gpt", "sol"],warm: true,tokenizerOverride: "openai/gpt-5".GPT_56_TERRA(gpt-5.6-terra, aliases["terra"], tokenizerOverride"openai/gpt-5", nowarm) andGPT_56_LUNA(gpt-5.6-luna, aliases["luna"], nowarm), placed after the Sol entry, beforeGPT_PRO.maxreasoning effort; note the tokenizer reuse ofopenai/gpt-5as an approximation (same as gpt-5.5 did).gpt-5.5alias retires with the old entry (per797d5a254precedent);openai:gpt-5.5keeps working as a custom model string because its stats stay in models-extra.GPT_PROunchanged (no GPT-5.6 Pro exists).gpt-5.6-soletc.), unlike the old["gpt", "gpt-5.5"]pattern —sol/terra/lunaare the durable tier abbreviations.2.
src/common/utils/tokens/models-extra.ts(~net +60 LoC)Add three flat-rate entries after
"gpt-5.5"(keep"gpt-5.5"— legacy models remain), styled like it but WITHOUT tiered fields (no long-context tier published):0.0000025, output0.000015, cache read0.00000025, cache creation0.000003125.0.000001, output0.000006, cache read0.0000001, cache creation0.00000125.knowledge_cutoff(not published — per brief, don't guess).3. Thinking levels / reasoning effort (~net +30 LoC)
3a.
src/common/utils/thinking/policy.tsgetExplicitThinkingPolicy(before the gpt-5.2/5.4/5.5 branch):The
(?!-[a-z])lookahead follows file convention: tolerates date suffixes (gpt-5.6-sol-2026-07-09), rejects hypotheticalgpt-5.6-sol-mini. Prefix stripping is already handled bystripModelProviderPrefixes.getThinkingPolicyForModeldoc-comment examples (add the two gpt-5.6 lines).3b.
src/common/types/thinking.tsanthropicSupportsNativeXhigh:getThinkingDisplayLabel: inside the existing OpenAI branch, return"MAX"whenlevel === "max" && openaiSupportsNativeMaxEffort(modelString); keep"XHIGH"otherwise.xhighon Sol still displays"XHIGH"(distinct native efforts).getThinkingOptionLabelneeds no change — it derives from the display label (Solmax→"max", Solxhigh→"xhigh").3c.
src/common/utils/ai/providerOptions.tsOPENAI_REASONING_EFFORT[effectiveThinking]withgetOpenAIReasoningEffort(effectiveThinking, modelString). Pass the fullmodelString(NOTcapModelName) — the helper strips provider/gateway prefixes itself. Update imports (OPENAI_REASONING_EFFORT→getOpenAIReasoningEffort) if the map is no longer referenced there.gpt-5.5-pro) keepmax -> "xhigh"."max"string, widen the local type/union minimally (neveras any) — today the effort flows asstring | undefined, so no friction is expected.4.
src/common/constants/codexOAuth.ts(~net +3 LoC)"gpt-5.6-sol","gpt-5.6-terra","gpt-5.6-luna"toCODEX_OAUTH_ALLOWED_MODELS.CODEX_OAUTH_REQUIRED_MODELS(public API exists) orCODEX_OAUTH_CONTEXT_WINDOW_OVERRIDES(no evidence of a lower ChatGPT-route cap).5.
docs/config/models.mdx+ generated skill contentGPT-5.6 Sol | openai:gpt-5.6-sol | \gpt`, `sol``GPT-5.6 Terra | openai:gpt-5.6-terra | \terra``GPT-5.6 Luna | openai:gpt-5.6-luna | \luna``/gpt-pro+0→ GPT-5.5 Pro) still valid — verify no other stale gpt-5.5 prose.scripts/gen_builtin_skills.ts—797d5a254regeneratedbuiltInSkillContent.generated.tsafter model changes), run it after the docs edit, and commit the generated file only if it diffs.6. Tests (behavioral only, no tautologies)
Update breaking assertions and add new coverage:
src/common/utils/thinking/policy.test.tsmax(also withmux-gateway:openai/prefix + date suffix); Terra/Luna → 5 levels;gpt-5.6-sol-mini-style non-match falls to default. Existing gpt-5.5/5.5-pro tests stay (models still matched).src/common/types/thinking.test.tsgetThinkingDisplayLabel("max", "openai:gpt-5.6-sol")→"MAX";("xhigh", sol)→"XHIGH";("max", "openai:gpt-5.5-pro")→"XHIGH"unchanged; option labels (max/xhighon Sol);openaiSupportsNativeMaxEffortfor prefixed/dated/-miniinputs.src/common/utils/ai/providerOptions.test.tsmax→reasoningEffort: "max"; Sol atxhigh→"xhigh";gpt-5.5-proatmax→"xhigh"; gateway path (github-copilot:gpt-5.6-solatmax) →"max"via second call site.src/common/utils/tokens/modelStats.test.tsmax_input_tokens); add a dated-fallback row (openai:gpt-5.6-sol-2026-07-09→openai:gpt-5.6-sol) alongside the existingtest.eachcases; existing gpt-5.5 fallback tests still pass (entry retained). Line 18 usesKNOWN_MODELS.GPT.id— passes once models-extra entry exists.src/common/constants/codexOAuth.test.tssrc/common/constants/knownModels.test.tsMODEL_ABBREVIATIONS["gpt-5.5"]is undefined (alias retired) andgpt/sol/terra/lunamap to the new ids.src/common/routing/resolve.test.ts,src/browser/hooks/useModelsFromSettings.test.ts,tests/ui/config/customModels.test.ts,src/common/utils/ai/models.test.tsopenai:gpt-5.5remains valid; dynamicKNOWN_MODELS.GPT.idrefs auto-track). Run them; fix only actual breaks.7. Format, validate, commit
make fmt, then targeted tests (below), thenMUX_ESLINT_CONCURRENCY=1 make static-check.pull-requestsskill viaagent_skill_read(AGENTS.md requires this before any commit, PR or not) and follow its attribution/footer conventions for the commit message.797d5a254style (e.g.🤖 promote gpt-5.6-sol as GPT default; add gpt-5.6 terra/luna; retire bare gpt-5.5). No PR, no push.Quality gates between phases
MODEL_ABBREVIATIONSandgetModelStatsbun -esmoke checks before moving on.policy.test.ts,thinking.test.ts,providerOptions.test.ts.mintlify broken-links(part ofstatic-check).Validation
Targeted test runs (avoid the monolithic
bun test srcand QuickJS-heavy workflow tests):Quick functional smoke checks:
Dogfooding (reviewable evidence)
The user-visible surfaces are the model picker and the thinking-level slider, both registry-derived. Produce screenshots via the dev-server sandbox + agent-browser (works headless here):
dev-server-sandboxskill (tempMUX_ROOT, free ports).agent-browser open http://localhost:<port>; create/open a workspace.agent-browser record startbefore the picker/slider flow;record stopafter and attach the webm. If recording is environmentally blocked, document the exact blocker and fall back to a step-by-step screenshot series. (Known quirk:record stopcan hang — kill the daemon by PID if needed, the webm is still written.)sol,terra,luna, andgptabbreviations resolve (screenshot at leastgpt→ GPT-5.6 Sol).openai:gpt-5.6-sol, screenshot the slider showing MAX as top level and XHIGH as the distinct level below; select Terra, screenshot slider topping out at XHIGH.agent-browserviewport option) and re-screenshot the model picker and thinking slider — verify no overflow/clipping at narrow width per repo mobile-validation rule."llmDebugLogs": true) in the sandbox config, send a message with Sol atmax(it will fail auth, butdevtools.jsonlrawRequestshould carryreasoningEffort: "max"); otherwise rely on the providerOptions unit tests and note why.openai:gpt-5.5still resolves stats (UI context/pricing display or thebun -echeck above).attach_file.Acceptance criteria
MODEL_ABBREVIATIONS:gpt→openai:gpt-5.6-sol,sol→openai:gpt-5.6-sol,terra→openai:gpt-5.6-terra,luna→openai:gpt-5.6-luna; aliasgpt-5.5no longer present.getModelStats("openai:gpt-5.6-sol")→ input 0.000005, output 0.00003, cache read 0.0000005, cache creation 0.00000625, max_input_tokens 1,000,000; Terra/Luna analogous (Luna 400K).getModelStats("openai:gpt-5.5")unchanged.["off","low","medium","high","xhigh","max"]; Terra/Luna =["off","low","medium","high","xhigh"]; gpt-5.5-pro policy unchanged.reasoningEffort: "max"; Sol@xhigh →"xhigh"; gpt-5.5-pro@max →"xhigh"; Anthropic paths untouched."MAX", Sol xhigh →"XHIGH", other OpenAI models unchanged ("XHIGH"for both).MUX_ESLINT_CONCURRENCY=1 make static-checkpasses;make fmtrun before committing; committed locally, no PR/push.Risks / assumptions
"max": assumed string for the Responses API per launch reporting; noted in a code comment at the mapping site. If the AI SDK's typedreasoningEffortunion rejects"max", pass it via the same string channel the existing"xhigh"uses (they're both plain strings today perOPENAI_REASONING_EFFORT: Record<ThinkingLevel, string | undefined>).["gpt","sol"](no full-id alias) follows the brief verbatim;openai:gpt-5.6-solfull model strings work regardless.hasNative1MContext/supports1MContext(src/common/utils/ai/models.ts) return false for gpt-5.5 today despite its 1.05M window (Anthropic-beta-specific helpers); leaving gpt-5.6 out of them matches existing OpenAI behavior — no change.models.mdx; re-run the generator and commit only if it diffs.Net LoC estimate (product code, excl. tests/docs/generated)
~+110 LoC: knownModels +14, models-extra +60 (3 entries + comments), policy.ts +10, thinking.ts +18, providerOptions.ts ±2, codexOAuth.ts +3.
📋 Implementation Plan — Pro-mode toggle
Plan: GPT-5.6 Pro-Mode Toggle (Option B —
reasoning.mode: "pro"wire injection)Summary
Add a per-workspace "PRO" reasoning-mode toggle for OpenAI GPT-5.6 Sol/Terra. Since
@ai-sdk/openai@^3.0.62drops unknown provider-option keys, the mode is delivered via the proven Mux-internal-header + fetch-wrapper body-rewrite pattern already used for Anthropic xhigh (x-mux-anthropic-effort): emitx-mux-openai-reasoning-mode: proinbuildRequestHeaders, then strip the header and mergereasoning.mode: "pro"into the JSON body in an OpenAI fetch wrapper inproviderModelFactory.ts.Net product-code estimate: ~330 LoC (breakdown per phase below; tests ~280 LoC additional, not counted).
Verified repo facts (from Explore reports)
buildRequestHeaders(modelString, muxProviderOptions, workspaceId, providersConfig, routeProvider, thinkingLevel)—src/common/utils/ai/providerOptions.tsL579–626. Gates Anthropic xhigh header onorigin === "anthropic" && routePassesHeaders && thinkingLevel === "xhigh" && anthropicSupportsNativeXhigh(modelString), whereroutePassesHeaders = resolveProviderOptionsNamespaceKey(origin, routeProvider) === origin. Call sites:src/node/services/aiService.ts~L2343 (main) and ~L2624 (model-fallback prepare);thinkingLevelarrives viaStreamMessageOptions(aiService.ts L216–227).wrapFetchWithAnthropicCacheControl(src/node/services/providerModelFactory.tsL325–461) is the strip-and-rewrite template: early-return non-POST/non-string-body; read + delete x-mux header; parse JSON; handle direct shape (json.model, top-level fields) and gateway shape (json.prompt+json.providerOptions.anthropic, model viaai-model-idheader); deletecontent-length; on JSON-parse failure still forward with the header stripped.webSocketTransport.fetch→fetchWithOpenAICodexNormalization→ devtools-header strip → network.normalizeCodexResponsesBodyruns only whenshouldRouteThroughCodexOauth(L1355–1372);shouldRouteThroughCodexOauth(L1242) is in scope where the chain is composed.CODEX_ALLOWED_PARAMSkeeps top-levelreasoning, so pruning would NOT remove a nestedreasoning.mode— an explicit decision is required (we strip; see D3).gpt-5.6-sol/gpt-5.6-terra/gpt-5.6-lunaare inCODEX_OAUTH_ALLOWED_MODELS(not required) — the Codex route is reachable for Sol/Terra (src/common/constants/codexOAuth.tsL91–105).mux-gateway:openai/...models are handled by a separate provider block (~L1693–1786) that composeswrapFetchWithAnthropicCacheControlonly for Anthropic models; an OpenAI wrapper added only in the openai block would never see gateway requests, and the header would leak to the gateway. Gateway bodies are{ prompt, providerOptions: { openai: {...} } }with model in theai-model-idheader.thinkingLevel:ThinkingProvider(src/browser/contexts/ThinkingContext.tsx) owns state viausePersistedState(getThinkingLevelKey(scopeId)), hydrates fallback from workspace metadata, and persists viaapi.workspace.updateAgentAISettings({ workspaceId, agentId, aiSettings: { model, thinkingLevel } }). Send path:useThinkingLevel()→useSendMessageOptions→buildSendMessageOptions(src/browser/utils/messages/buildSendMessageOptions.ts) →SendMessageOptionsSchema(src/common/orpc/schemas/stream.tsL725–769, has optionalthinkingLevel) →agentSession.ts(~L3601enforceThinkingPolicy) →aiService.streamMessage→buildRequestHeaders.api.workspace.updateAgentAISettingsis called with a full{ model, thinkingLevel }object from 4 sites:ThinkingContext.tsxL150,App.tsxL487 (model change),ChatInput/index.tsxL840 (send-time persistence),useCreationWorkspace.tsL528. The backend replacesaiSettingsByAgent[agentId]wholesale, so every caller must includereasoningModeor it gets wiped on model change / send.ThinkingSliderComponentrenders inChatInput/index.tsx(~L3268–3347) insidedata-component="ThinkingSliderGroup"; active levels usevar(--color-thinking-mode)/var(--color-thinking-mode-light)+ font-weight ramp; sharedTooltipused throughout. Palette:src/browser/utils/commands/sources.ts(thinkingSetLevelpattern withCommandIds); keybind hints hidden on mobile via.mobile-hide-shortcut-hints.src/common/types/thinking.test.ts(openaiSupportsNativeMaxEffortdescribe, L70); header tests insrc/common/utils/ai/providerOptions.test.ts(buildRequestHeadersdescribe, L968); wrapper tests insrc/node/services/providerModelFactory.test.ts(L1563–1708,createCapturingFetchharness, direct + gateway body shapes); UI harness intests/ui/agents/thinkingPersistence.test.ts(createAppHarness+selectModel). No existing schema test forworkspaceAiSettings.ts.Design decisions
origin === "openai" && routePassesHeaders && reasoningMode === "pro" && openaiSupportsProMode(modelString)— independent ofthinkingLevel(mode is orthogonal to effort).webSocketTransport.fetch, so (a) the body rewrite happens before any transport decision, and (b) the x-mux header is stripped before the Codex/WS layers regardless of route. Codex normalization runs later in the request flow and keeps top-levelreasoning, so ordering is safe.{ inject: false }to the wrapper whenshouldRouteThroughCodexOauthis true (flag is in scope at composition time), AND (b) explicitlydelete json.reasoning.modeinsidenormalizeCodexResponsesBody(runs only on Codex-OAuth routes), preservingeffort/summary; ifreasoningis left empty, delete the wholereasoningkey — so Codex can never seereasoning.modeeven if a future path emits it. Rationale (leave as code comment): the ChatGPT Codex backend is stricter than the public API and unknown nested reasoning fields risk a hard 4xx; a silent standard-mode no-op is strictly safer. Header is still stripped on that route.modelId.startsWith("openai/")(mirrors the Anthropic conditional there). It strips the header (never leak upstream, even to our own gateway) and best-effort injectsproviderOptions.openai.reasoningMode = "pro"into the gateway body shape (creatingproviderOptions.openaiif the body is otherwise gateway-shaped, i.e. has apromptarray), with a comment that gateway-side SDK translation of this key is unverified (the gateway's@ai-sdk/openaimay drop it); dogfooding checks empirically via the Responses API echo. Gateway delivery is explicitly NOT part of the guaranteed-wire acceptance criteria.reasoningMode?: "standard" | "pro", absent ⇒ standard. Type + Zod enum live insrc/common/types/thinking.tsnext to the predicate (mirrorsThinkingLevelSchemaplacement). Optional everywhere so legacy persisted configs parse unchanged (self-healing rule).ThinkingProviderwith a siblingreasoningModestate (persisted atgetReasoningModeKey(scopeId)), exposed via auseReasoningMode()hook — the provider already owns the aiSettings sync + pending machinery, so this is the minimal change (no new context). All 4updateAgentAISettingscall sites include the currentreasoningModeto avoid the clobber hazard.{ model, thinkingLevel }from persistedaiSettings/aiSettingsByAgentgetreasoningModethreaded alongside, reading from the same settings object each path already uses: agentSession startup retry (~L1414), auto-compaction (~L1438/L3240), idle compaction (workspaceService ~L9907), goal/workflow continuations (~L9703), heartbeats (~L10297). This matches the brief's "same path as thinkingLevel" and avoids a surprising silent downgrade on restart-retry. Compaction naturally stays standard unless the user set pro on the compact agent's settings (it readsaiSettingsByAgent.compact), so no special-case policy is needed./responses); chat-completions requests get header-strip only, body untouched (blindly adding top-levelreasoningthere could hard-fail). Header values other than"pro"are treated as invalid: strip header, leave body unchanged.Implementation steps
Phase 1 — Capability predicate + setting type (
src/common/types/thinking.ts)openaiSupportsNativeMaxEffort(~L252):reasoning.mode: "pro"on the Responses API; Sol/Terra only (no Luna Pro); lookahead tolerates date suffixes (gpt-5.6-sol-2026-07-09), rejects named variants (-mini). IfThinkingLevelSchemaactually lives elsewhere, colocateOpenAIReasoningModeSchemawith it instead. (~15 LoC)Phase 2 — Header constant + emission (
src/common/utils/ai/providerOptions.ts)export const MUX_OPENAI_REASONING_MODE_HEADER = "x-mux-openai-reasoning-mode";next toMUX_ANTHROPIC_EFFORT_OVERRIDE_HEADER(L40).buildRequestHeaderswith a 7th paramreasoningMode?: OpenAIReasoningModeand emit:reasoning.modeon the wire; passthrough-route gating keeps it away from non-passthrough gateways (OpenRouter, github-copilot). (~15 LoC)Phase 3 — Fetch wrapper + composition (
src/node/services/providerModelFactory.ts)wrapFetchWithOpenAIReasoningMode(baseFetch: typeof fetch, options?: { inject?: boolean }): typeof fetch, modeled onwrapFetchWithAnthropicCacheControlbut strip-first (D10):MUX_OPENAI_REASONING_MODE_HEADERfrominit.headers; if absent → forward untouched (no parse, byte-identical). If present → delete it from aHeaderscopy; every path below forwards the stripped headers.inject === false(Codex route, D3 comment) or header value ≠"pro"(D9 defensive) → forward with stripped headers, body untouched.promptarray): setproviderOptions.openai.reasoningMode = "pro", creatingproviderOptions/openairecords as needed (D4 comment: unverified gateway-side support)./responses, D9), deriving the pathname defensively from thestring | URL | Requestinput (if it can't be determined, do NOT inject — strip-only):json.reasoning = { ...(isRecord(json.reasoning) ? json.reasoning : {}), mode: "pro" }— never clobber existingeffort/summary. Non-Responses URLs: body untouched.content-length, re-serialize, forward.catchon JSON parse: forward with stripped headers, body untouched.(~60 LoC)
const providerFetch = wrapFetchWithOpenAIReasoningMode(webSocketTransport.fetch, { inject: !shouldRouteThroughCodexOauth });and pass tocreateOpenAI. (~5 LoC)modelId.startsWith("openai/"), wrapfetchWithAutoLogoutwithwrapFetchWithOpenAIReasoningMode(..., { inject: true }). (~5 LoC)6b.
normalizeCodexResponsesBody(~L759): ifisRecord(json.reasoning),delete json.reasoning.mode(preserveeffort/summary); ifreasoningis then empty, deletejson.reasoningentirely. D3 "why" comment. Codex-only by construction — this function runs solely onshouldRouteThroughCodexOauthrequests (verified L1355–1372). (~7 LoC)Phase 4 — Setting plumbing (schema → send options → aiService)
src/common/orpc/schemas/workspaceAiSettings.ts: addreasoningMode: OpenAIReasoningModeSchema.optional()with a description noting it currently applies only to OpenAI GPT-5.6 Sol/Terra and is inert elsewhere. (~5 LoC)src/common/orpc/schemas/stream.tsSendMessageOptionsSchema(L725): addreasoningMode: OpenAIReasoningModeSchema.optional(). (~3 LoC)src/browser/utils/messages/buildSendMessageOptions.ts: addreasoningMode?: OpenAIReasoningModetoSendMessageOptionsInputand pass through. (~3 LoC)src/browser/hooks/useSendMessageOptions.ts: readuseReasoningMode()and include in the built options. (~4 LoC)src/node/services/agentSession.ts(~L3601 region whereoptions?.thinkingLevelis consumed): passoptions?.reasoningModeintoaiService.streamMessage.src/node/services/aiService.ts: addreasoningMode?: OpenAIReasoningModetoStreamMessageOptions(L216–227) and pass to bothbuildRequestHeaderscall sites (~L2343 main and ~L2624 fallback-prepare; the predicate re-gates per fallback model). (~10 LoC)11b. Backend-initiated turns (D7, mechanical): wherever a path already reads
thinkingLevelfrom persistedaiSettings/aiSettingsByAgentto build send/stream options, readreasoningModefrom the same object and thread it alongside: agentSession startup retry (~L1414–1439), auto-compaction defaults (~L3240), workspaceService idle compaction (~L9907), goal/workflow continuations (~L9703), heartbeats (~L10297). No new resolution logic — same fallback order asthinkingLevelat each site. (~20 LoC)Phase 5 — Frontend state + persistence
src/common/constants/storage.ts: addgetReasoningModeKey(scopeId)→`reasoningMode:${scopeId}`next togetThinkingLevelKey(L177). (~7 LoC)src/browser/contexts/ThinkingContext.tsx: sibling stateusePersistedState<OpenAIReasoningMode | null>(getReasoningModeKey(scopeId), null, { listener: true }); derived value falls back to workspace-metadata aiSettings (getWorkspaceAiSettingsFromMetadata) then"standard";setReasoningModemirrorssetThinkingLevel(localStorage +markPendingWorkspaceAiSettings+updateAgentAISettingsincluding{ model, thinkingLevel, reasoningMode }). ExportuseReasoningMode()hook (new filesrc/browser/hooks/useReasoningMode.tsmirroringuseThinkingLevel.ts, or colocated per existing pattern). ExtendgetWorkspaceAiSettingsFromMetadatato surfacereasoningMode. (~45 LoC)updateAgentAISettingscall sites:ThinkingContext.tsxL150 (setThinkingLevelmust include the currentreasoningMode, andsetReasoningModethe currentthinkingLevel— step 13),App.tsxL487,ChatInput/index.tsxL840,useCreationWorkspace.tsL528 (read via hook where available, elsereadPersistedState(getReasoningModeKey(...))— never raw localStorage). (~10 LoC)Phase 6 — UI toggle + palette command
src/browser/components/ThinkingSlider/ProModeToggle.tsx(colocated with the slider it sits beside): self-contained; props =modelString(likeThinkingSliderComponent); returnsnullunlessopenaiSupportsProMode(modelString); renders a smallPROtext button witharia-pressed,data-component="ProModeToggle"; inactive:var(--color-text-secondary)/fw 400; active:var(--color-thinking-mode)/fw 700 (matches the slider's MAX treatment); sharedTooltip("Pro reasoning mode: slower, more thorough responses. Saved per workspace."); onClick toggles viauseReasoningMode(). No emoji, no native title. (~55 LoC)ChatInput/index.tsxinside/next todata-component="ThinkingSliderGroup"(~L3290):<ProModeToggle modelString={baseModel} />. Verify at ~375px container width that the toolbar doesn't overflow (the group isshrink-0; PRO adds ~3ch — check the@containerbreakpoints still behave; adjust hide rules only if overflow is observed). (~3 LoC)CommandIds.toggleProReasoning()+ entry insrc/browser/utils/commands/sources.tspushed only whenopenaiSupportsProMode(p.selectedWorkspaceState?.currentModel ?? "");title: "Toggle Pro Reasoning Mode", subtitle showing current state;runcalls a newp.onToggleReasoningMode(workspaceId)wired whereonSetThinkingLevelis wired (App.tsx / palette host). (~40 LoC)Phase 7 — Tests (behavioral; no tautologies)
src/common/types/thinking.test.ts—openaiSupportsProModetruth table:openai:gpt-5.6-sol,openai:gpt-5.6-terra,mux-gateway:openai/gpt-5.6-sol,gpt-5.6-terra-2026-07-09→ true;openai:gpt-5.6-luna,openai:gpt-5.5-pro,openai:gpt-5.6-sol-mini→ false.src/common/utils/ai/providerOptions.test.ts—buildRequestHeaders: header present for openai+pro+sol/terra direct and viamux-gatewayrouteProvider (passthrough); absent when mode"standard"/undefined, for luna, for anthropic origin + pro, and for non-passthroughrouteProvider(openrouter / github-copilot).src/node/services/providerModelFactory.test.ts—wrapFetchWithOpenAIReasoningMode(reusecreateCapturingFetch/parseSentBody), against a Responses-like URL (https://api.openai.com/v1/responses) unless noted:{ model, reasoning: { effort, summary } }+ header →reasoning === { effort, summary, mode: "pro" }and header stripped;reasoning→ object created with onlymode;/v1/chat/completions) + header → header stripped, body unchanged (D9);"pro"→ header stripped, body unchanged (D9);promptarray,ai-model-id: openai/gpt-5.6-sol) →providerOptions.openai.reasoningMode === "pro"(including whenproviderOptions.openaiis initially absent);{ inject: false }→ header stripped, body unchanged (Codex decision);20b.
normalizeCodexResponsesBody: body withreasoning: { effort, summary, mode: "pro" }→moderemoved,effort/summarypreserved; body withreasoning: { mode: "pro" }only → no top-levelreasoningkey remains (D3).src/common/orpc/schemas/workspaceAiSettings.test.ts— legacy{ model, thinkingLevel }parses (noreasoningMode);{ ..., reasoningMode: "pro" }parses;reasoningMode: "ultra"rejected.tests/ui/agents/proModeToggle.test.ts(mirrorthinkingPersistence.test.tsharness +selectModel): selectopenai:gpt-5.6-sol→[data-component="ProModeToggle"]visible; click → active (aria-pressed="true") and survives model switch away/back (persistence); selectopenai:gpt-5.6-lunaand an Anthropic model → toggle absent. If the palette command source is unit-testable in the harness (or as a pure-function test onsources.ts), assert the "Toggle Pro Reasoning Mode" entry appears only when the current model supports pro; otherwise cover via dogfood step 2.Phase 8 — Docs
docs/config/models.mdx: one short note — GPT-5.6 Sol/Terra support OpenAI pro reasoning mode via the PRO toggle next to the thinking slider (per-workspace; sendsreasoning.mode: "pro"; same per-token pricing, more tokens/longer runs; not available for Luna or via non-passthrough gateways). Then runbun scripts/gen_docs.ts check && bun scripts/gen_builtin_skills.ts check --sync-mux-docs-skilland sync if flagged.Validation
MUX_ESLINT_CONCURRENCY=1 make static-check(memory-safe lint concurrency).thinkingLevelused to build send/stream options also threadsreasoningModefrom the same object with the same fallback order — agentSession ~L1414–1439 and ~L3240; workspaceService ~L9703, ~L9907, ~L10297. Add targeted assertions where those paths already have test harnesses; where they don't, the checklist + typecheck is the gate (do not build new harnesses just for this).bun test src, no QuickJS-heavy workflow tests):Dogfooding (reviewable evidence)
Setup:
make dev-server-sandbox(pins BACKEND_PORT/VITE_PORT, seeds real providers), set"llmDebugLogs": truein the sandboxconfig.json(picked up without restart). Drive withagent-browser; record a session (agent-browser record start/stop→ webm) covering steps 2–4 and attach screenshots viaattach_file.gpt-5.6-sol(toggle visible),gpt-5.6-terra(visible),gpt-5.6-luna(hidden), an Anthropic model (hidden). Picker options render in a portal invisible tosnapshot -i— select viaagent-browser evalon[role=option], [cmdk-item]dispatching mousedown/mouseup/click, and verify state after each click.Ctrl+Shift+P→ "Toggle Pro Reasoning Mode") and screenshot the palette entry.sessions/<ws>/devtools.jsonlstep-update entries:update.requestHeaderscontainsx-mux-openai-reasoning-mode: pro(SDK layer),update.rawResponseshows the Responses API echoing"mode": "pro"in its reasoning config — this empirically confirms on-wire delivery and settles the gateway-vs-direct question. If the sandbox routes via mux-gateway and the echo is missing, record it as a documented limitation (D4), not a failure — the direct-route rewrite is proven by the Phase-7 unit tests.Acceptance criteria
openaiSupportsProModematches the exact truth table (step 18).buildRequestHeadersoutput containsx-mux-openai-reasoning-mode, and the post-wrapper direct-route body containsreasoning: { ...existing effort/summary..., mode: "pro" }(unit-proven; dogfood-confirmed via devtools.jsonl where routing allows)."standard", or unsupported model ⇒ wire body byte-identical to today; all pre-existingproviderOptions/providerModelFactorytests pass unchanged.reasoning.mode(wrapper no-inject andnormalizeCodexResponsesBodystrip) and never leak the x-mux header; mux-gateway and chat-completions requests never leak the x-mux header; no request shape can forward the header upstream (strip-before-early-return).updateAgentAISettingscall sites include it); palette command toggles it.reasoningModeparse unchanged (schema test).MUX_ESLINT_CONCURRENCY=1 make static-checkpasses; all targeted test files pass; docs generators clean.Non-goals / known limitations
ultramode; no streaming/timeout redesign; no providers.jsonc modelParameters; no Anthropic-path changes; no PR/push.mux-gateway) delivery ofreasoning.modeis best-effort and unverified (D4); direct-route delivery is the guaranteed path and the only one in the wire acceptance criteria.Risks
shrink-0group; step 16 + dogfood step 4 verify 375px explicitly.buildRequestHeaderssignature growth (7 positional params): stay consistent with existing style; converting to an options object is out of scope (would churn Anthropic tests).ThinkingContext(step 13/14):setThinkingLevelandsetReasoningModeeach need the other setting's current value for theupdateAgentAISettingspayload — read the current values at call time (not from a possibly stale render closure).Plan reviewed with advisor (openai:gpt-5.5-pro, xhigh) across three rounds; final verdict: "fully approved as implementation-ready."
Generated with
mux• Model:anthropic:claude-fable-5• Thinking:xhigh• Cost:$287.02