diff --git a/open-sse/config/providers/registry/github/index.ts b/open-sse/config/providers/registry/github/index.ts index e72c98dd4484..96d42ee19652 100644 --- a/open-sse/config/providers/registry/github/index.ts +++ b/open-sse/config/providers/registry/github/index.ts @@ -163,18 +163,24 @@ export const githubProvider: RegistryEntry = { { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, diff --git a/open-sse/config/providers/registry/opencode/go/index.ts b/open-sse/config/providers/registry/opencode/go/index.ts index 1826cdd9e209..d8536678176f 100644 --- a/open-sse/config/providers/registry/opencode/go/index.ts +++ b/open-sse/config/providers/registry/opencode/go/index.ts @@ -357,6 +357,7 @@ export const opencode_goProvider: RegistryEntry = { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], targetFormat: "openai-responses", maxOutputTokens: 128000, }, diff --git a/open-sse/config/providers/registry/opencode/zen/index.ts b/open-sse/config/providers/registry/opencode/zen/index.ts index a7ffec42add2..11d87134a70a 100644 --- a/open-sse/config/providers/registry/opencode/zen/index.ts +++ b/open-sse/config/providers/registry/opencode/zen/index.ts @@ -36,9 +36,30 @@ export const opencode_zenProvider: RegistryEntry = { // opencode.ai/zen/v1 with the same key on both endpoints). #12196 made the // same declaration for gpt-5.6-luna on opencode-go; the zen entries here // never got it. - { id: "gpt-5.6-sol", name: "GPT 5.6 Sol", targetFormat: "openai-responses" }, - { id: "gpt-5.6-terra", name: "GPT 5.6 Terra", targetFormat: "openai-responses" }, - { id: "gpt-5.6-luna", name: "GPT 5.6 Luna", targetFormat: "openai-responses" }, + { + id: "gpt-5.6-sol", + name: "GPT 5.6 Sol", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-terra", + name: "GPT 5.6 Terra", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, + { + id: "gpt-5.6-luna", + name: "GPT 5.6 Luna", + supportsReasoning: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], + targetFormat: "openai-responses", + maxOutputTokens: 128000, + }, { id: "gpt-5.4", name: "GPT 5.4" }, { id: "gpt-5.4-mini", name: "GPT 5.4 Mini" }, { id: "gpt-5.4-nano", name: "GPT 5.4 Nano" }, diff --git a/open-sse/config/providers/shared.ts b/open-sse/config/providers/shared.ts index 3da468589790..9f52553471df 100644 --- a/open-sse/config/providers/shared.ts +++ b/open-sse/config/providers/shared.ts @@ -323,6 +323,7 @@ export const GPT_5_6_API_CAPABILITIES = { supportsReasoning: true, supportsVision: true, supportsXHighEffort: true, + supportedThinkingEfforts: ["low", "medium", "high", "xhigh", "max"], contextLength: 1050000, maxInputTokens: 922000, maxOutputTokens: 128000, diff --git a/src/lib/reasoningRouting/policy.ts b/src/lib/reasoningRouting/policy.ts index d73d93c183ef..26be6a46702a 100644 --- a/src/lib/reasoningRouting/policy.ts +++ b/src/lib/reasoningRouting/policy.ts @@ -318,7 +318,11 @@ function capabilityFor( if (capabilities.reasoningEffortsOverride && Array.isArray(declaredEfforts)) { return "unsupported" as const; } - if (codexModelFamilySupportsExtendedEffort(model, targetEffort)) return "supported" as const; + // Strip the provider namespace (`openai/`, `github/`, `opencode-zen/`, + // `codex/`…) so every provider serving the family resolves the same way. + if (codexModelFamilySupportsExtendedEffort(modelIdForRegistry, targetEffort)) { + return "supported" as const; + } if (capabilities.supportsThinking === null) return "unknown" as const; return "unsupported" as const; } diff --git a/src/shared/components/ReasoningRoutingRules.tsx b/src/shared/components/ReasoningRoutingRules.tsx index d9e5e109185b..e2c41d77733f 100644 --- a/src/shared/components/ReasoningRoutingRules.tsx +++ b/src/shared/components/ReasoningRoutingRules.tsx @@ -205,26 +205,39 @@ export default function ReasoningRoutingRules({ const targetModelForCapability = form.targetKind === "model" ? form.targetModel : form.modelPattern; + const isLunaTarget = useMemo(() => { + const normalized = targetModelForCapability + .trim() + .toLowerCase() + .replace(/^[^/]+\//, ""); + return /^gpt-5\.6-luna(?:-|$)/.test(normalized); + }, [targetModelForCapability]); + + // gpt-5.6-luna accepts `max` but not `ultra`: a saved `ultra` is coerced to + // `max` so the editor never re-offers (or re-saves) a tier the upstream 400s. + const currentTargetEffort = + isLunaTarget && form.targetEffort === "ultra" ? "max" : form.targetEffort; + const effortOptions = useMemo( () => - getReasoningRoutingTargetEffortOptions(targetModelForCapability, form.targetEffort).map( + getReasoningRoutingTargetEffortOptions(targetModelForCapability, currentTargetEffort).map( (value) => ({ value, label: value }) ), - [form.targetEffort, targetModelForCapability] + [currentTargetEffort, targetModelForCapability] ); const capabilityWarning = useMemo(() => { if (form.effortMode === "inherit") return ""; - if (!(EXTENDED_REASONING_EFFORTS as readonly string[]).includes(form.targetEffort)) return ""; + if (!(EXTENDED_REASONING_EFFORTS as readonly string[]).includes(currentTargetEffort)) return ""; if (form.targetKind === "combo") return t("extendedComboWarning"); if (!targetModelForCapability.trim()) return t("extendedUnknownWarning"); return supportsExtendedCodexEffort( targetModelForCapability, - form.targetEffort as "max" | "ultra" + currentTargetEffort as "max" | "ultra" ) ? "" : t("extendedUnsupportedWarning"); - }, [form.effortMode, form.targetEffort, form.targetKind, t, targetModelForCapability]); + }, [currentTargetEffort, form.effortMode, form.targetKind, t, targetModelForCapability]); const dirty = editorOpen && JSON.stringify(form) !== baseline; const confirmDiscard = (action: () => void) => { @@ -310,7 +323,7 @@ export default function ReasoningRoutingRules({ .split(",") .map((tag) => tag.trim()) .filter(Boolean), - targetEffort: form.effortMode === "inherit" ? null : form.targetEffort, + targetEffort: form.effortMode === "inherit" ? null : currentTargetEffort, targetKind, targetModel: targetKind === "model" ? form.targetModel || null : null, targetComboId: targetKind === "combo" ? form.targetComboId || null : null, @@ -756,13 +769,12 @@ export default function ReasoningRoutingRules({ options={[ { value: "any", label: t("any") }, { value: "missing", label: t("missing") }, - ...[ - ...STANDARD_REASONING_EFFORTS, - ...EXTENDED_REASONING_EFFORTS, - ].map((value) => ({ - value, - label: value, - })), + ...[...STANDARD_REASONING_EFFORTS, ...EXTENDED_REASONING_EFFORTS].map( + (value) => ({ + value, + label: value, + }) + ), ]} /> @@ -826,7 +838,7 @@ export default function ReasoningRoutingRules({