Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f2603c8
πŸ€– promote gpt-5.6-sol as GPT default; add gpt-5.6 terra/luna; retire …
ThomasK33 Jul 9, 2026
594cd52
πŸ€– tests: pin gpt-5.5 context-limit expectations to literal model string
ThomasK33 Jul 9, 2026
e9d865c
πŸ€– tests: pin remaining gpt-5.5 expectations to literal model string
ThomasK33 Jul 9, 2026
98bfda8
πŸ€– feat: GPT-5.6 pro-mode toggle (reasoning.mode wire injection + sett…
ThomasK33 Jul 9, 2026
8dd7427
πŸ€– test+docs: pro-mode predicate/header/wrapper/schema/UI tests; model…
ThomasK33 Jul 9, 2026
eb83f3a
πŸ€– tests: update aiSettings payload expectations to include reasoningMode
ThomasK33 Jul 9, 2026
a886b20
πŸ€– fix: hide PRO chip on narrow layouts to prevent right-edge overflow
ThomasK33 Jul 9, 2026
5951610
πŸ€– fix: coerce persisted reasoningMode so corrupt values self-heal to …
ThomasK33 Jul 9, 2026
b3a91fc
πŸ€– fix: address Codex round 2 β€” coerce sendOptions reasoningMode, rout…
ThomasK33 Jul 9, 2026
07f9549
πŸ€– fix: move openaiProModeAvailable to browser-safe models.ts
ThomasK33 Jul 9, 2026
680983b
πŸ€– fix: gate pro mode on the Responses wire format (Codex round 3)
ThomasK33 Jul 9, 2026
0e20798
πŸ€– fix: address Codex round 4 β€” route-resolved PRO visibility, coerce …
ThomasK33 Jul 9, 2026
5ac5933
πŸ€– fix: hide PRO mode when Codex OAuth is the effective auth path (Cod…
ThomasK33 Jul 9, 2026
ca024a0
πŸ€– tests: isolate useReasoningMode in the draft-settings real-module c…
ThomasK33 Jul 9, 2026
09b8ed1
πŸ€– fix: address Codex round 6 β€” direct-only pro mode, seeding reset, p…
ThomasK33 Jul 10, 2026
eb44f7f
πŸ€– fix: resolve mapped OpenAI aliases for pro/max capability gates (Co…
ThomasK33 Jul 10, 2026
c478975
πŸ€– fix: GPT-5.6 GA capabilities β€” family-wide pro/max + tiered long-co…
ThomasK33 Jul 10, 2026
35c3a25
πŸ€– fix: resolve mapped aliases in the thinking-policy path (Codex roun…
ThomasK33 Jul 10, 2026
4a71d89
πŸ€– tests: pro-toggle hidden case uses gpt-5.5-pro now that Luna is pro…
ThomasK33 Jul 10, 2026
cccd808
πŸ€– fix: clamp GPT-5.6 native max effort on non-Responses paths (Codex …
ThomasK33 Jul 10, 2026
7b34f52
πŸ€– tests: token meter expects the 1.05M GA context for GPT-5.6 under C…
ThomasK33 Jul 10, 2026
137c3bd
πŸ€– fix: legacy per-agent entries default to standard mode; allow bare …
ThomasK33 Jul 10, 2026
1c0db1e
πŸ€– fix: keep pro mode available when an explicit gateway model falls b…
ThomasK33 Jul 10, 2026
1156376
πŸ€– fix: thread pro reasoning mode through agent task creation and resu…
ThomasK33 Jul 10, 2026
f854cfc
πŸ€– fix: bare gpt-5.6 stats alias + palette pro-toggle model fallback (…
ThomasK33 Jul 10, 2026
decab28
πŸ€– fix: gate the palette pro toggle on the persisted model selection (…
ThomasK33 Jul 10, 2026
822cacb
πŸ€– fix: palette thinking picker resolves mapped aliases via providersC…
ThomasK33 Jul 10, 2026
86aeafb
πŸ€– fix: preserve pro mode on parent auto-resumes (Codex round 18)
ThomasK33 Jul 10, 2026
0c74945
πŸ€– fix: GPT-5.6 off sends the explicit none effort (Codex round 19)
ThomasK33 Jul 10, 2026
16823b5
fix: inherit pro mode from parent's active agent when spawning other …
ThomasK33 Jul 10, 2026
847231a
fix: carry reasoningMode through workflow-result continuation sends
ThomasK33 Jul 10, 2026
d5f12ca
fix: inherit pro mode from parent's active agent in workspace turns
ThomasK33 Jul 10, 2026
77f0ce5
fix: thread providersConfig through task-path thinking clamps
ThomasK33 Jul 10, 2026
26abe11
fix: preserve pro mode in plan handoff and ACP prompt sends
ThomasK33 Jul 10, 2026
8eb5dda
fix: handle Request-object inputs in the pro-mode fetch wrapper
ThomasK33 Jul 10, 2026
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
8 changes: 7 additions & 1 deletion docs/config/models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Mux ships with curated models kept up to date with the frontier. Use any custom
| Opus 4.8 | anthropic:claude-opus-4-8 | `opus` | βœ“ |
| Sonnet 5 | anthropic:claude-sonnet-5 | `sonnet` | |
| Haiku 4.5 | anthropic:claude-haiku-4-5 | `haiku` | |
| GPT-5.5 | openai:gpt-5.5 | `gpt`, `gpt-5.5` | |
| GPT-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-5.5 Pro | openai:gpt-5.5-pro | `gpt-pro`, `gpt-5.5-pro` | |
| GPT-5.4 Mini | openai:gpt-5.4-mini | `gpt-mini` | |
| GPT-5.4 Nano | openai:gpt-5.4-nano | `gpt-nano` | |
Expand All @@ -35,6 +37,10 @@ Mux ships with curated models kept up to date with the frontier. Use any custom

{/* END KNOWN_MODELS_TABLE */}

### Pro reasoning mode (GPT-5.6)

Every GPT-5.6 model (Sol, Terra, and Luna) supports OpenAI's pro reasoning mode. Toggle the **PRO** badge next to the thinking slider (or run "Toggle Pro Reasoning Mode" from the Command Palette) to send `reasoning.mode: "pro"` with each request. The setting is saved per workspace. Pro mode uses the same per-token pricing but consumes more tokens and responses can take noticeably longer. Requests routed through non-passthrough gateways (e.g. OpenRouter, GitHub Copilot) fall back to standard mode.

## Model Selection

Keyboard shortcuts:
Expand Down
118 changes: 113 additions & 5 deletions src/browser/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ import {
import { buildCoreSources, type BuildSourcesParams } from "./utils/commands/sources";

import { getTopLevelProjectEntries } from "@/common/utils/subProjects";
import { THINKING_LEVELS, type ThinkingLevel } from "@/common/types/thinking";
import {
THINKING_LEVELS,
coerceOpenAIReasoningMode,
type OpenAIReasoningMode,
type ThinkingLevel,
} from "@/common/types/thinking";
import { CUSTOM_EVENTS } from "@/common/constants/events";
import { isWorkspaceForkSwitchEvent } from "./utils/workspaceEvents";
import {
Expand All @@ -57,6 +62,7 @@ import {
getModelKey,
getNotifyOnResponseKey,
getThinkingLevelByModelKey,
getReasoningModeKey,
getThinkingLevelKey,
getWorkspaceAISettingsByAgentKey,
getWorkspaceLastReadKey,
Expand Down Expand Up @@ -99,6 +105,8 @@ import { WindowsToolchainBanner } from "./components/WindowsToolchainBanner/Wind
import { RosettaBanner } from "./components/RosettaBanner/RosettaBanner";

import { useExperimentValue } from "@/browser/hooks/useExperiments";
import { useProvidersConfig } from "@/browser/hooks/useProvidersConfig";
import { useRouting } from "@/browser/hooks/useRouting";
import { EXPERIMENT_IDS } from "@/common/constants/experiments";
import { getErrorMessage } from "@/common/utils/errors";
import assert from "@/common/utils/assert";
Expand Down Expand Up @@ -440,6 +448,27 @@ function AppInner() {
[getModelForWorkspace]
);

// Pro mode is Responses-only; the palette command hides under chatCompletions
// and on non-passthrough routes (mirroring the send path's header gating).
const { config: providersConfig } = useProvidersConfig();
const routing = useRouting();
const getRouteForModel = useCallback(
(canonicalModel: string) => routing.resolveRoute(canonicalModel).route,
[routing]
);

const getReasoningModeForWorkspace = useCallback((workspaceId: string): OpenAIReasoningMode => {
if (!workspaceId) {
return "standard";
}
const stored = readPersistedState<OpenAIReasoningMode | null>(
getReasoningModeKey(workspaceId),
null
);
// Coerce untrusted persisted values so corrupt entries self-heal to "standard".
return coerceOpenAIReasoningMode(stored) ?? "standard";
}, []);

const setThinkingLevelFromPalette = useCallback(
(workspaceId: string, level: ThinkingLevel) => {
if (!workspaceId) {
Expand All @@ -449,13 +478,19 @@ function AppInner() {
const normalized = THINKING_LEVELS.includes(level) ? level : "off";
const model = getModelForWorkspace(workspaceId);
const key = getThinkingLevelKey(workspaceId);
// Carry the current pro-mode choice: the backend replaces the agent's
// settings wholesale, so omitting reasoningMode would wipe it.
const reasoningMode = getReasoningModeForWorkspace(workspaceId);

// Use the utility function which handles localStorage and event dispatch
// ThinkingProvider will pick this up via its listener
updatePersistedState(key, normalized);

type WorkspaceAISettingsByAgentCache = Partial<
Record<string, { model: string; thinkingLevel: ThinkingLevel }>
Record<
string,
{ model: string; thinkingLevel: ThinkingLevel; reasoningMode?: OpenAIReasoningMode }
>
>;

const normalizedAgentId =
Expand All @@ -470,7 +505,7 @@ function AppInner() {
prev && typeof prev === "object" ? prev : {};
return {
...record,
[normalizedAgentId]: { model, thinkingLevel: normalized },
[normalizedAgentId]: { model, thinkingLevel: normalized, reasoningMode },
};
},
{}
Expand All @@ -481,13 +516,14 @@ function AppInner() {
markPendingWorkspaceAiSettings(workspaceId, normalizedAgentId, {
model,
thinkingLevel: normalized,
reasoningMode,
});

api.workspace
.updateAgentAISettings({
workspaceId,
agentId: normalizedAgentId,
aiSettings: { model, thinkingLevel: normalized },
aiSettings: { model, thinkingLevel: normalized, reasoningMode },
})
.then((result) => {
if (!result.success) {
Expand All @@ -509,7 +545,75 @@ function AppInner() {
);
}
},
[api, getModelForWorkspace]
[api, getModelForWorkspace, getReasoningModeForWorkspace]
);

// Palette toggle for the OpenAI pro reasoning mode. Persists like the
// thinking-level palette action: localStorage first (ThinkingProvider listens),
// then best-effort backend sync with the full settings payload.
const toggleReasoningModeFromPalette = useCallback(
(workspaceId: string) => {
if (!workspaceId) {
return;
}

const next: OpenAIReasoningMode =
getReasoningModeForWorkspace(workspaceId) === "pro" ? "standard" : "pro";
const model = getModelForWorkspace(workspaceId);
const thinkingLevel = getThinkingLevelForWorkspace(workspaceId);

updatePersistedState(getReasoningModeKey(workspaceId), next);

type WorkspaceAISettingsByAgentCache = Partial<
Record<
string,
{ model: string; thinkingLevel: ThinkingLevel; reasoningMode?: OpenAIReasoningMode }
>
>;

const normalizedAgentId =
readPersistedState<string>(getAgentIdKey(workspaceId), WORKSPACE_DEFAULTS.agentId)
.trim()
.toLowerCase() || WORKSPACE_DEFAULTS.agentId;

updatePersistedState<WorkspaceAISettingsByAgentCache>(
getWorkspaceAISettingsByAgentKey(workspaceId),
(prev) => {
const record: WorkspaceAISettingsByAgentCache =
prev && typeof prev === "object" ? prev : {};
return {
...record,
[normalizedAgentId]: { model, thinkingLevel, reasoningMode: next },
};
},
{}
);

if (api) {
markPendingWorkspaceAiSettings(workspaceId, normalizedAgentId, {
model,
thinkingLevel,
reasoningMode: next,
});

api.workspace
.updateAgentAISettings({
workspaceId,
agentId: normalizedAgentId,
aiSettings: { model, thinkingLevel, reasoningMode: next },
})
.then((result) => {
if (!result.success) {
clearPendingWorkspaceAiSettings(workspaceId, normalizedAgentId);
}
})
.catch(() => {
clearPendingWorkspaceAiSettings(workspaceId, normalizedAgentId);
// Best-effort only.
});
}
},
[api, getModelForWorkspace, getReasoningModeForWorkspace, getThinkingLevelForWorkspace]
);

const registerParamsRef = useRef<BuildSourcesParams | null>(null);
Expand Down Expand Up @@ -745,6 +849,10 @@ function AppInner() {
themePreference,
getThinkingLevel: getThinkingLevelForWorkspace,
onSetThinkingLevel: setThinkingLevelFromPalette,
getReasoningMode: getReasoningModeForWorkspace,
onToggleReasoningMode: toggleReasoningModeFromPalette,
providersConfig,
getRouteForModel,
getMinThinkingOverride,
onStartWorkspaceCreation: openNewWorkspaceFromPalette,
onStartMultiProjectWorkspaceCreation: openNewMultiProjectWorkspaceFromPalette,
Expand Down
66 changes: 66 additions & 0 deletions src/browser/components/ThinkingSlider/ProModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react";
import { normalizeToCanonical } from "@/common/utils/ai/models";
import { openaiProModeAvailable } from "@/common/utils/ai/proMode";
import { useProvidersConfig } from "@/browser/hooks/useProvidersConfig";
import { useReasoningMode } from "@/browser/hooks/useReasoningMode";
import { useRouting } from "@/browser/hooks/useRouting";
import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";

interface ProModeToggleProps {
modelString: string;
}

/**
* Small "PRO" toggle for OpenAI's pro reasoning mode (GPT-5.6 family).
* Renders nothing for models without pro-mode support and for gateway routes
* where the pro-mode header is never emitted β€” otherwise the toggle would
* persist a setting that can never affect the request. An explicit gateway
* prefix (openrouter:openai/...) only hides the toggle while that gateway can
* win the route; when it is disabled/unconfigured the backend falls back to
* the resolved route, which openaiProModeAvailable re-checks.
*/
export const ProModeToggle: React.FC<ProModeToggleProps> = (props) => {
const [reasoningMode, setReasoningMode] = useReasoningMode();
// Availability mirrors the send path (see openaiProModeAvailable): hides for
// chatCompletions wire format, non-passthrough routes, and Codex OAuth auth.
const { config: providersConfig } = useProvidersConfig();
const routing = useRouting();
const resolvedRoute = routing.resolveRoute(normalizeToCanonical(props.modelString)).route;

if (
!openaiProModeAvailable(props.modelString, {
providersConfig,
resolvedRouteProvider: resolvedRoute,
})
) {
return null;
}

const isActive = reasoningMode === "pro";

return (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
data-component="ProModeToggle"
data-pro-mode-toggle
aria-pressed={isActive}
aria-label={`Pro reasoning mode: ${isActive ? "on" : "off"}. Click to toggle.`}
onClick={() => setReasoningMode(isActive ? "standard" : "pro")}
className="hover:bg-hover shrink-0 rounded-sm bg-transparent px-1 text-center text-[11px] transition-all duration-200 select-none"
style={
isActive
? { color: "var(--color-thinking-mode)", fontWeight: 700 }
: { color: "var(--color-text-secondary)", fontWeight: 400 }
}
>
PRO
</button>
</TooltipTrigger>
<TooltipContent align="center">
Pro reasoning mode: slower, more thorough responses. Saved per workspace.
</TooltipContent>
</Tooltip>
);
};
13 changes: 11 additions & 2 deletions src/browser/components/ThinkingSlider/ThinkingSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@/common/types/thinking";
import { useThinkingLevel } from "@/browser/hooks/useThinkingLevel";
import { useMinThinkingLevels } from "@/browser/hooks/useMinThinkingLevels";
import { useProvidersConfig } from "@/browser/hooks/useProvidersConfig";
import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
import { formatKeybind, KEYBINDS } from "@/browser/utils/ui/keybinds";
import { enforceThinkingPolicy, getAvailableThinkingLevels } from "@/common/utils/thinking/policy";
Expand Down Expand Up @@ -45,9 +46,17 @@ export const ThinkingSliderComponent: React.FC<ThinkingControlProps> = ({ modelS
// Apply the per-model minimum floor so off/low are hidden unless the user lowers it
// in Models settings. The floor must match the backend send-path enforcement.
const { getMinimum } = useMinThinkingLevels();
// Resolve mapped aliases so the slider offers the target model's ladder
// (e.g. an alias mapped to GPT-5.6 exposes native max).
const { config: providersConfig } = useProvidersConfig();
const minimum = getMinimum(modelString);
const allowed = getAvailableThinkingLevels(modelString, minimum);
const effectiveThinkingLevel = enforceThinkingPolicy(modelString, thinkingLevel, minimum);
const allowed = getAvailableThinkingLevels(modelString, minimum, providersConfig);
const effectiveThinkingLevel = enforceThinkingPolicy(
modelString,
thinkingLevel,
minimum,
providersConfig
);

// Map current level to index within the *allowed* subset
const currentIndex = allowed.indexOf(effectiveThinkingLevel);
Expand Down
35 changes: 22 additions & 13 deletions src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@/browser/hooks/usePersistedState";
import {
getModelKey,
getReasoningModeKey,
getThinkingLevelKey,
getWorkspaceAISettingsByAgentKey,
AGENT_AI_DEFAULTS_KEY,
Expand All @@ -17,7 +18,7 @@ import {
resolveWorkspaceAiSettingsForAgent,
type WorkspaceAISettingsCache,
} from "@/browser/utils/workspaceModeAi";
import type { ThinkingLevel } from "@/common/types/thinking";
import type { OpenAIReasoningMode, ThinkingLevel } from "@/common/types/thinking";
import type { AgentAiDefaults } from "@/common/types/agentAiDefaults";
import { normalizeAgentId } from "@/common/utils/agentIds";

Expand Down Expand Up @@ -60,19 +61,23 @@ export function WorkspaceModeAISync(props: { workspaceId: string }): null {

const existingModel = readPersistedState<string>(modelKey, fallbackModel);
const existingThinking = readPersistedState<ThinkingLevel>(thinkingKey, "off");
const reasoningKey = getReasoningModeKey(workspaceId);
const existingReasoning = readPersistedState<OpenAIReasoningMode>(reasoningKey, "standard");

const { resolvedModel, resolvedThinking } = resolveWorkspaceAiSettingsForAgent({
agentId: normalizedAgentId,
agentAiDefaults,
// Keep deterministic handoff behavior: background sync should trust the
// currently active workspace model, but explicit mode switches should
// restore the selected agent's per-workspace override (if any).
workspaceByAgent,
useWorkspaceByAgentFallback: isExplicitAgentSwitch,
fallbackModel,
existingModel,
existingThinking,
});
const { resolvedModel, resolvedThinking, resolvedReasoningMode } =
resolveWorkspaceAiSettingsForAgent({
agentId: normalizedAgentId,
agentAiDefaults,
// Keep deterministic handoff behavior: background sync should trust the
// currently active workspace model, but explicit mode switches should
// restore the selected agent's per-workspace override (if any).
workspaceByAgent,
useWorkspaceByAgentFallback: isExplicitAgentSwitch,
fallbackModel,
existingModel,
existingThinking,
existingReasoningMode: existingReasoning,
});

if (existingModel !== resolvedModel) {
setWorkspaceModelWithOrigin(
Expand All @@ -85,6 +90,10 @@ export function WorkspaceModeAISync(props: { workspaceId: string }): null {
if (existingThinking !== resolvedThinking) {
updatePersistedState(thinkingKey, resolvedThinking);
}

if (existingReasoning !== resolvedReasoningMode) {
updatePersistedState(reasoningKey, resolvedReasoningMode);
}
}, [agentAiDefaults, agentId, workspaceByAgent, workspaceId]);

return null;
Expand Down
Loading
Loading