From 0e74c85c0816b1c9366db9befa88ba32248703a9 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 9 Jul 2026 20:09:15 +0200 Subject: [PATCH 01/11] fix(coding-agent/utils): filtered gpt-5 reasoning comment noise - Removed literal HTML comment sentinels (``) from thinking block displays. - Added logic to hide blocks that consist entirely of reasoning noise and updated display validation to omit empty formatted output. - Refactored the memoization cache to maintain separate slots for prose and raw modes. --- packages/coding-agent/CHANGELOG.md | 7 ++ .../src/utils/thinking-display.ts | 102 +++++++++++++----- 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index d1ad895e4..636167fe5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] +### Fixed + +- Improved rendering of raw thinking blocks by stripping empty HTML comment noise +- Fixed display of thinking blocks consisting entirely of hidden comment noise + +- Fixed gpt-5.6 reasoning summaries rendering literal `` sentinel lines in thinking blocks; empty HTML comments (and the unterminated ``), streamed as a ``. Comments with actual content are left untouched. +const EMPTY_COMMENT_RE = /^$/; +const OPEN_COMMENT_RE = /^` + * sentinel lines outside code fences (see {@link isCommentNoise}); prose-only + * mode additionally elides fenced code down to a trailing ellipsis. + */ export function formatThinkingForDisplay(text: string, proseOnly: boolean): string { - if (!proseOnly || !text) return text; - if (text === formatCacheKey) return formatCacheValue; + if (!text) return text; + const hasComment = text.includes("` leaves no blank tail. + if (hasComment && isCommentNoise(line, i === lines.length - 1)) continue; + + const open = FENCE.exec(line); + if (open) { const marker = open[2]!; const ch = marker[0]!; // A backtick fence's info string may not contain a backtick. @@ -79,18 +116,25 @@ export function formatThinkingForDisplay(text: string, proseOnly: boolean): stri inFence = true; fenceChar = ch; fenceLen = marker.length; - appendEllipsis(); - } else { - resultLines.push(line); + if (proseOnly) { + appendEllipsis(); + } else { + resultLines.push(line); + } + continue; } - } else { - resultLines.push(line); } + resultLines.push(line); } const formatted = resultLines.join("\n"); - formatCacheKey = text; - formatCacheValue = formatted; + if (proseOnly) { + proseCacheKey = text; + proseCacheValue = formatted; + } else { + rawCacheKey = text; + rawCacheValue = formatted; + } return formatted; } @@ -99,9 +143,11 @@ export function hasDisplayableThinking( text: string | null | undefined, formattedText: string | null | undefined, ): boolean { - if (!text) return false; - if (!formattedText) return false; - return formattedText.length > 0 && canonicalizeMessage(text).length > 0; + if (!text || !formattedText) return false; + // Visibility keys off the formatted text: a block whose raw text is only + // comment noise (`\n`) formats to whitespace and stays hidden. The + // raw canonicalize check still hides dot/ellipsis-only placeholder blocks. + return formattedText.trim().length > 0 && canonicalizeMessage(text).length > 0; } /** Whether an assistant message contains thinking content the TUI can reveal. */ From 9d5207ea36357e7e84df7da640be1cae376f8894 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 9 Jul 2026 20:32:30 +0200 Subject: [PATCH 02/11] feat: integrated gpt-5.6 models and unified logical model resolution - Added support for GPT-5.6 (Luna, Sol, Terra) models including configuration updates and context window values. - Implemented automatic effort tier remapping for wire-effort models to ensure proper translation between user-facing tiers and provider requirements. - Updated Codex request transformers to handle effort shifting and added validation for reasoning configurations. - Collapsed Devin-specific model variants to unify logical model handling and added comprehensive test coverage for effort resolution. --- packages/ai/CHANGELOG.md | 8 + .../openai-codex/request-transformer.ts | 62 +- packages/ai/src/providers/openai-shared.ts | 12 +- packages/ai/test/openai-codex.test.ts | 30 + packages/catalog/CHANGELOG.md | 7 + packages/catalog/src/model-thinking.ts | 52 +- packages/catalog/src/models.json | 974 +++++++----------- packages/catalog/src/variant-collapse.ts | 73 +- packages/catalog/test/model-thinking.test.ts | 90 ++ 9 files changed, 658 insertions(+), 650 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 1c9a41476..b3dcc30ab 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +### Changed + +- Updated Codex reasoning effort mapping to support shifted wire tiers for newer models + +### Fixed + +- Fixed the Codex Responses request transformer bypassing catalog/compat reasoning effort maps: the clamped user effort is now remapped to the provider wire tier (GPT-5.6's shifted five-tier scale sends `max` for user `xhigh` and `xhigh` for `high`), failing loudly if a map produces a value outside the Codex wire vocabulary. + ## [16.3.13] - 2026-07-09 ### Changed diff --git a/packages/ai/src/providers/openai-codex/request-transformer.ts b/packages/ai/src/providers/openai-codex/request-transformer.ts index d603130c3..67af8f232 100644 --- a/packages/ai/src/providers/openai-codex/request-transformer.ts +++ b/packages/ai/src/providers/openai-codex/request-transformer.ts @@ -1,19 +1,33 @@ -import type { Effort } from "@oh-my-pi/pi-catalog/effort"; +import { Effort } from "@oh-my-pi/pi-catalog/effort"; import { supportsAllTurnsReasoningContext, supportsCodexReasoningSummary } from "@oh-my-pi/pi-catalog/identity"; import { requireSupportedEffort } from "@oh-my-pi/pi-catalog/model-thinking"; -import type { Api, Model } from "../../types"; +import type { Model } from "../../types"; +import { mapOpenAIReasoningEffort } from "../openai-shared"; /** Reasoning replay scope for the Codex Responses API (`reasoning.context`). */ export type CodexReasoningContext = "auto" | "current_turn" | "all_turns"; +/** User-facing effort levels accepted by Codex request options. */ +type CodexCallerEffort = "minimal" | "low" | "medium" | "high" | "xhigh"; + +/** Caller literal → catalog `Effort` bridge (the enum is nominal). */ +const EFFORT_BY_NAME: Record = { + minimal: Effort.Minimal, + low: Effort.Low, + medium: Effort.Medium, + high: Effort.High, + xhigh: Effort.XHigh, +}; + export interface ReasoningConfig { - effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh"; + effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"; summary?: "auto" | "concise" | "detailed"; context?: CodexReasoningContext; } export interface CodexRequestOptions { - reasoningEffort?: ReasoningConfig["effort"]; + /** User-facing effort; the wire-only `max` tier is reached via the model's effort map. */ + reasoningEffort?: CodexCallerEffort | "none"; reasoningSummary?: ReasoningConfig["summary"] | null; /** Explicit `reasoning.context` override; defaults to `all_turns` when unset. The `all_turns` value is gated to gpt-5.4+ Codex models — older ids reject it, so it is suppressed and `context` omitted. */ reasoningContext?: CodexReasoningContext; @@ -80,10 +94,40 @@ export function shouldUseCodexResponsesLite(body: RequestBody, requested: boolea return requested === true && !containsInputImage(body.input); } -function getReasoningConfig(model: Model, options: CodexRequestOptions): ReasoningConfig { +/** + * Clamp a user-facing effort to the model's ladder, then remap to the wire + * tier (e.g. GPT-5.6's shifted five-tier scale sends `max` for user `xhigh`). + * A mapped value outside the Codex wire vocabulary is a broken compat/model + * effort map — fail loudly rather than silently sending a different tier. + */ +function mapCodexWireEffort( + model: Model<"openai-codex-responses">, + effort: CodexCallerEffort, +): ReasoningConfig["effort"] { + const mapped = mapOpenAIReasoningEffort(model, model.compat, requireSupportedEffort(model, EFFORT_BY_NAME[effort])); + switch (mapped) { + case "none": + case "minimal": + case "low": + case "medium": + case "high": + case "xhigh": + case "max": + return mapped; + default: + throw new Error( + `Effort map for ${model.provider}/${model.id} produced invalid Codex reasoning effort "${mapped}"`, + ); + } +} + +function getReasoningConfig( + model: Model<"openai-codex-responses">, + effort: NonNullable, + options: CodexRequestOptions, +): ReasoningConfig { const config: ReasoningConfig = { - effort: - options.reasoningEffort === "none" ? "none" : requireSupportedEffort(model, options.reasoningEffort as Effort), + effort: effort === "none" ? "none" : mapCodexWireEffort(model, effort), }; // `reasoning.summary` is accepted only from gpt-5.4 onward; earlier Codex ids // (gpt-5.1-codex, gpt-5.3-codex, gpt-5.3-codex-spark) reject it with @@ -216,7 +260,7 @@ function stripImageDetails(input: InputItem[]): void { export async function transformRequestBody( body: RequestBody, - model: Model, + model: Model<"openai-codex-responses">, options: CodexRequestOptions = {}, prompt?: { developerMessages: string[] }, ): Promise { @@ -300,7 +344,7 @@ export async function transformRequestBody( } if (options.reasoningEffort !== undefined) { - const reasoningConfig = getReasoningConfig(model, options); + const reasoningConfig = getReasoningConfig(model, options.reasoningEffort, options); body.reasoning = { ...body.reasoning, ...reasoningConfig, diff --git a/packages/ai/src/providers/openai-shared.ts b/packages/ai/src/providers/openai-shared.ts index a0ac83994..bacf6d8af 100644 --- a/packages/ai/src/providers/openai-shared.ts +++ b/packages/ai/src/providers/openai-shared.ts @@ -695,13 +695,19 @@ export interface OpenAICompatPolicy { }; } -function mapOpenAIReasoningEffort( +/** + * Map a user-facing effort to the provider wire value: explicit compat + * override first, then the model's baked `thinking.effortMap`, else identity. + * Shared by the chat-completions/Responses policy resolver and the Codex + * request transformer. + */ +export function mapOpenAIReasoningEffort( model: Pick, - compat: OpenAICompatPolicyCompat, + compat: { reasoningEffortMap?: Partial> } | undefined, effort: string, ): string { const level = effort as Effort; - return compat.reasoningEffortMap?.[level] ?? model.thinking?.effortMap?.[level] ?? effort; + return compat?.reasoningEffortMap?.[level] ?? model.thinking?.effortMap?.[level] ?? effort; } function isImplicitDisableWhenNotRequested(disableMode: OpenAIReasoningDisableMode): boolean { diff --git a/packages/ai/test/openai-codex.test.ts b/packages/ai/test/openai-codex.test.ts index e28633fd1..1c7492afc 100644 --- a/packages/ai/test/openai-codex.test.ts +++ b/packages/ai/test/openai-codex.test.ts @@ -314,6 +314,36 @@ describe("openai-codex reasoning effort validation", () => { }); }); +describe("openai-codex reasoning effort wire mapping", () => { + it("shifts gpt-5.6 user efforts one wire tier up via the baked effort map", async () => { + const model = createCodexModel("gpt-5.6-sol"); + const shifted = [ + ["minimal", "low"], + ["low", "medium"], + ["medium", "high"], + ["high", "xhigh"], + ["xhigh", "max"], + ] as const; + + for (const [requested, wire] of shifted) { + const transformed = await transformRequestBody({ model: model.id }, model, { + reasoningEffort: requested, + }); + expect(transformed.reasoning?.effort).toBe(wire); + } + }); + + it("keeps pre-5.6 efforts unshifted and passes none through unmapped", async () => { + const gpt55 = createCodexModel("gpt-5.5"); + const unshifted = await transformRequestBody({ model: gpt55.id }, gpt55, { reasoningEffort: "xhigh" }); + expect(unshifted.reasoning?.effort).toBe("xhigh"); + + const gpt56 = createCodexModel("gpt-5.6-sol"); + const none = await transformRequestBody({ model: gpt56.id }, gpt56, { reasoningEffort: "none" }); + expect(none.reasoning?.effort).toBe("none"); + }); +}); + describe("openai-codex error parsing", () => { it("produces friendly usage-limit messages and rate limits", async () => { const resetAt = Math.floor(Date.now() / 1000) + 600; diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index b6f938072..bbd58a0ca 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] +### Added + +- Added support for GPT-5.6 (Luna, Sol, Terra) model variants +- Enabled expanded five-tier reasoning effort scale (minimal to xhigh) for GPT-5.6 models + +- Added GPT-5.6 (Terra/Luna/Sol) support for the new `max` reasoning tier: on wire-effort APIs (OpenAI Responses, Codex, Azure, openai-compat/OpenRouter models that advertise reasoning) user efforts shift up one notch — `xhigh` sends `max`, `high` sends `xhigh` — mirroring the Claude Fable/Opus 4.7+ five-tier mapping, and the exposed ladder becomes `minimal..xhigh` with `minimal` reaching the native `low` tier. Devin's per-tier GPT-5.6 sibling rows now collapse into `gpt-5-6-{luna,sol,terra}` logical models with the same shifted routing (`xhigh` → `-max`), plus `-fast` families that keep the direct `low..xhigh` `-priority` scale since Devin serves no `-max-priority` tier. + ## [16.3.13] - 2026-07-09 ### Added diff --git a/packages/catalog/src/model-thinking.ts b/packages/catalog/src/model-thinking.ts index b0e01af59..611eb1f60 100644 --- a/packages/catalog/src/model-thinking.ts +++ b/packages/catalog/src/model-thinking.ts @@ -17,6 +17,7 @@ import { type ParsedModel, parseAnthropicModel, parseKnownModel, + parseOpenAIModel, semverEqual, semverGte, } from "./identity/classify"; @@ -101,12 +102,14 @@ const MIMO_REASONING_EFFORT_MAP: Readonly = { }; /** - * Effort → wire-value map for the 5-tier adaptive scale (Opus 4.7+ and - * Fable/Mythos 5 on the Messages API). User-facing efforts shift up one notch - * so the top tier reaches the genuine "max" and "high" lands on Anthropic's - * recommended "xhigh" coding/agentic default. + * Effort → wire-value map for a shifted five-tier scale (`low..max`): + * user-facing efforts shift up one notch so the top tier reaches the genuine + * "max" and "high" lands on the recommended "xhigh" coding/agentic default. + * Used by Anthropic adaptive models with a real xhigh tier (Opus 4.7+ and + * Fable/Mythos 5 on the Messages API) and by GPT-5.6+ wire-effort models, + * which expose the same genuine `max` tier above `xhigh`. */ -export const ANTHROPIC_ADAPTIVE_EFFORT_MAP_5_TIER: Readonly>> = { +export const SHIFTED_FIVE_TIER_EFFORT_MAP: Readonly>> = { [Effort.Minimal]: "low", [Effort.Low]: "medium", [Effort.Medium]: "high", @@ -295,6 +298,27 @@ function isOpenAICompatReasoningApi(api: Api): boolean { return api === "openai-completions" || api === "openrouter"; } +/** + * GPT-5.6+ addressed through a wire `reasoning.effort`/`reasoning_effort` + * field, where the shifted five-tier map applies. Devin (`devin-agent`) + * selects effort by routing to per-tier sibling model ids instead and must + * stay unmapped. + */ +function isGpt56PlusWireEffortModel(spec: ModelSpec): boolean { + switch (spec.api) { + case "openai-responses": + case "openai-codex-responses": + case "azure-openai-responses": + case "openai-completions": + case "openrouter": + break; + default: + return false; + } + const parsed = parseOpenAIModel(bareModelId(spec.id)); + return parsed !== null && semverGte(parsed.version, "5.6"); +} + function getModelDefinedEfforts( spec: ModelSpec, compat: CompatOf, @@ -313,6 +337,12 @@ function getModelDefinedEfforts( if (isSakanaFuguReasoningModel(spec)) { return FUGU_REASONING_EFFORTS; } + if (isGpt56PlusWireEffortModel(spec)) { + // Normalize stale baked/discovered `low..xhigh` surfaces to the full + // five-tier ladder so the shifted map keeps the native `low` tier + // reachable (user `minimal`). + return DEFAULT_REASONING_EFFORTS_WITH_XHIGH; + } return isOpenAICompatReasoningApi(spec.api) && (isMinimaxM2FamilyModelId(spec.id) || isOpenAIGptOssModelId(spec.id) || @@ -373,7 +403,7 @@ function inferDetectedEffortMap( return MINIMAX_ANTHROPIC_ADAPTIVE_EFFORT_MAP; } return anthropicModelHasRealXHighEffort(spec, parsedModel) - ? ANTHROPIC_ADAPTIVE_EFFORT_MAP_5_TIER + ? SHIFTED_FIVE_TIER_EFFORT_MAP : ANTHROPIC_ADAPTIVE_EFFORT_MAP_4_TIER; } // GLM-5.2 coding SKUs accept `reasoning_effort`, but the effort dialect is @@ -397,6 +427,9 @@ function inferDetectedEffortMap( if (isSakanaFuguReasoningModel(spec)) { return FUGU_REASONING_EFFORT_MAP; } + if (isGpt56PlusWireEffortModel(spec)) { + return SHIFTED_FIVE_TIER_EFFORT_MAP; + } if (!isOpenAICompatReasoningApi(spec.api)) { return undefined; } @@ -446,7 +479,7 @@ function getOpenRouterAnthropicReasoningEffortMap(modelId: string): EffortMap | if (!isAnthropicAdaptiveGenAtLeast(parsed, "4.6")) return undefined; const hasRealXHigh = isAnthropicAdaptiveGenAtLeast(parsed, "4.7"); - return hasRealXHigh ? ANTHROPIC_ADAPTIVE_EFFORT_MAP_5_TIER : ANTHROPIC_ADAPTIVE_EFFORT_MAP_4_TIER; + return hasRealXHigh ? SHIFTED_FIVE_TIER_EFFORT_MAP : ANTHROPIC_ADAPTIVE_EFFORT_MAP_4_TIER; } function inferSupportedEfforts( @@ -474,6 +507,11 @@ function inferOpenAISupportedEfforts(model: OpenAIModel): readonly Effort[] { if (model.variant === "codex-mini" && semverEqual(model.version, "5.1")) { return GPT_5_1_CODEX_MINI_EFFORTS; } + // 5.6+ exposes the full five-tier ladder: the shifted wire map spans + // low..max, with user `minimal` reaching the native `low` tier. + if (semverGte(model.version, "5.6")) { + return DEFAULT_REASONING_EFFORTS_WITH_XHIGH; + } if (semverGte(model.version, "5.2")) { return GPT_5_2_PLUS_EFFORTS; } diff --git a/packages/catalog/src/models.json b/packages/catalog/src/models.json index 0d39e022c..55d87779c 100644 --- a/packages/catalog/src/models.json +++ b/packages/catalog/src/models.json @@ -17214,9 +17214,9 @@ }, "requestModelId": "gpt-5-5-none-priority" }, - "gpt-5-6-luna-high": { - "id": "gpt-5-6-luna-high", - "name": "GPT-5.6 Luna High Thinking", + "gpt-5-6-luna": { + "id": "gpt-5-6-luna", + "name": "GPT-5.6 Luna", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17233,11 +17233,30 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-luna-none", + "minimal": "gpt-5-6-luna-low", + "low": "gpt-5-6-luna-medium", + "medium": "gpt-5-6-luna-high", + "high": "gpt-5-6-luna-xhigh", + "xhigh": "gpt-5-6-luna-max" + } + }, + "requestModelId": "gpt-5-6-luna-none" }, - "gpt-5-6-luna-high-priority": { - "id": "gpt-5-6-luna-high-priority", - "name": "GPT-5.6 Luna High Thinking Fast", + "gpt-5-6-luna-fast": { + "id": "gpt-5-6-luna-fast", + "name": "GPT-5.6 Luna Fast", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17254,11 +17273,30 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-luna-none-priority", + "minimal": "gpt-5-6-luna-low-priority", + "low": "gpt-5-6-luna-low-priority", + "medium": "gpt-5-6-luna-medium-priority", + "high": "gpt-5-6-luna-high-priority", + "xhigh": "gpt-5-6-luna-xhigh-priority" + } + }, + "requestModelId": "gpt-5-6-luna-none-priority" }, - "gpt-5-6-luna-low": { - "id": "gpt-5-6-luna-low", - "name": "GPT-5.6 Luna Low Thinking", + "gpt-5-6-sol": { + "id": "gpt-5-6-sol", + "name": "GPT-5.6 Sol", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17275,11 +17313,30 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-sol-none", + "minimal": "gpt-5-6-sol-low", + "low": "gpt-5-6-sol-medium", + "medium": "gpt-5-6-sol-high", + "high": "gpt-5-6-sol-xhigh", + "xhigh": "gpt-5-6-sol-max" + } + }, + "requestModelId": "gpt-5-6-sol-none" }, - "gpt-5-6-luna-low-priority": { - "id": "gpt-5-6-luna-low-priority", - "name": "GPT-5.6 Luna Low Thinking Fast", + "gpt-5-6-sol-fast": { + "id": "gpt-5-6-sol-fast", + "name": "GPT-5.6 Sol Fast", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17296,11 +17353,30 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-sol-none-priority", + "minimal": "gpt-5-6-sol-low-priority", + "low": "gpt-5-6-sol-low-priority", + "medium": "gpt-5-6-sol-medium-priority", + "high": "gpt-5-6-sol-high-priority", + "xhigh": "gpt-5-6-sol-xhigh-priority" + } + }, + "requestModelId": "gpt-5-6-sol-none-priority" }, - "gpt-5-6-luna-max": { - "id": "gpt-5-6-luna-max", - "name": "GPT-5.6 Luna Max Thinking", + "gpt-5-6-terra": { + "id": "gpt-5-6-terra", + "name": "GPT-5.6 Terra", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17317,11 +17393,30 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-terra-none", + "minimal": "gpt-5-6-terra-low", + "low": "gpt-5-6-terra-medium", + "medium": "gpt-5-6-terra-high", + "high": "gpt-5-6-terra-xhigh", + "xhigh": "gpt-5-6-terra-max" + } + }, + "requestModelId": "gpt-5-6-terra-none" }, - "gpt-5-6-luna-medium": { - "id": "gpt-5-6-luna-medium", - "name": "GPT-5.6 Luna Medium Thinking", + "gpt-5-6-terra-fast": { + "id": "gpt-5-6-terra-fast", + "name": "GPT-5.6 Terra Fast", "api": "devin-agent", "provider": "devin", "baseUrl": "https://server.codeium.com", @@ -17338,574 +17433,26 @@ "cacheWrite": 0 }, "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-luna-medium-priority": { - "id": "gpt-5-6-luna-medium-priority", - "name": "GPT-5.6 Luna Medium Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 + "maxTokens": 64000, + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortRouting": { + "off": "gpt-5-6-terra-none-priority", + "minimal": "gpt-5-6-terra-low-priority", + "low": "gpt-5-6-terra-low-priority", + "medium": "gpt-5-6-terra-medium-priority", + "high": "gpt-5-6-terra-high-priority", + "xhigh": "gpt-5-6-terra-xhigh-priority" + } }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-luna-none": { - "id": "gpt-5-6-luna-none", - "name": "GPT-5.6 Luna No Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-luna-none-priority": { - "id": "gpt-5-6-luna-none-priority", - "name": "GPT-5.6 Luna No Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-luna-xhigh": { - "id": "gpt-5-6-luna-xhigh", - "name": "GPT-5.6 Luna XHigh Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-luna-xhigh-priority": { - "id": "gpt-5-6-luna-xhigh-priority", - "name": "GPT-5.6 Luna XHigh Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-high": { - "id": "gpt-5-6-sol-high", - "name": "GPT-5.6 Sol High Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-high-priority": { - "id": "gpt-5-6-sol-high-priority", - "name": "GPT-5.6 Sol High Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-low": { - "id": "gpt-5-6-sol-low", - "name": "GPT-5.6 Sol Low Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-low-priority": { - "id": "gpt-5-6-sol-low-priority", - "name": "GPT-5.6 Sol Low Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-max": { - "id": "gpt-5-6-sol-max", - "name": "GPT-5.6 Sol Max Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-medium": { - "id": "gpt-5-6-sol-medium", - "name": "GPT-5.6 Sol Medium Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-medium-priority": { - "id": "gpt-5-6-sol-medium-priority", - "name": "GPT-5.6 Sol Medium Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-none": { - "id": "gpt-5-6-sol-none", - "name": "GPT-5.6 Sol No Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-none-priority": { - "id": "gpt-5-6-sol-none-priority", - "name": "GPT-5.6 Sol No Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-xhigh": { - "id": "gpt-5-6-sol-xhigh", - "name": "GPT-5.6 Sol XHigh Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-sol-xhigh-priority": { - "id": "gpt-5-6-sol-xhigh-priority", - "name": "GPT-5.6 Sol XHigh Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-high": { - "id": "gpt-5-6-terra-high", - "name": "GPT-5.6 Terra High Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-high-priority": { - "id": "gpt-5-6-terra-high-priority", - "name": "GPT-5.6 Terra High Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-low": { - "id": "gpt-5-6-terra-low", - "name": "GPT-5.6 Terra Low Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-low-priority": { - "id": "gpt-5-6-terra-low-priority", - "name": "GPT-5.6 Terra Low Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-max": { - "id": "gpt-5-6-terra-max", - "name": "GPT-5.6 Terra Max Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-medium": { - "id": "gpt-5-6-terra-medium", - "name": "GPT-5.6 Terra Medium Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-medium-priority": { - "id": "gpt-5-6-terra-medium-priority", - "name": "GPT-5.6 Terra Medium Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-none": { - "id": "gpt-5-6-terra-none", - "name": "GPT-5.6 Terra No Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-none-priority": { - "id": "gpt-5-6-terra-none-priority", - "name": "GPT-5.6 Terra No Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": false, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-xhigh": { - "id": "gpt-5-6-terra-xhigh", - "name": "GPT-5.6 Terra XHigh Thinking", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 - }, - "gpt-5-6-terra-xhigh-priority": { - "id": "gpt-5-6-terra-xhigh-priority", - "name": "GPT-5.6 Terra XHigh Thinking Fast", - "api": "devin-agent", - "provider": "devin", - "baseUrl": "https://server.codeium.com", - "reasoning": true, - "input": [ - "text", - "image" - ], - "supportsTools": true, - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": 1000000, - "maxTokens": 64000 + "requestModelId": "gpt-5-6-terra-none-priority" }, "kimi-k2-6": { "id": "kimi-k2-6", @@ -60549,6 +60096,120 @@ }, "contextPromotionTarget": "openai/gpt-5.4" }, + "gpt-5.6-luna": { + "id": "gpt-5.6-luna", + "name": "GPT-5.6 Luna", + "api": "openai-responses", + "provider": "openai", + "baseUrl": "https://api.openai.com/v1", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 1, + "output": 6, + "cacheRead": 0.1, + "cacheWrite": 1.25 + }, + "contextWindow": 1050000, + "maxTokens": 128000, + "applyPatchToolType": "freeform", + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } + } + }, + "gpt-5.6-sol": { + "id": "gpt-5.6-sol", + "name": "GPT-5.6 Sol", + "api": "openai-responses", + "provider": "openai", + "baseUrl": "https://api.openai.com/v1", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 5, + "output": 30, + "cacheRead": 0.5, + "cacheWrite": 6.25 + }, + "contextWindow": 1050000, + "maxTokens": 128000, + "applyPatchToolType": "freeform", + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } + } + }, + "gpt-5.6-terra": { + "id": "gpt-5.6-terra", + "name": "GPT-5.6 Terra", + "api": "openai-responses", + "provider": "openai", + "baseUrl": "https://api.openai.com/v1", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 2.5, + "output": 15, + "cacheRead": 0.25, + "cacheWrite": 3.125 + }, + "contextWindow": 1050000, + "maxTokens": 128000, + "applyPatchToolType": "freeform", + "thinking": { + "mode": "effort", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } + } + }, "o1": { "id": "o1", "name": "o1", @@ -61330,7 +60991,7 @@ }, "gpt-5.6-luna": { "id": "gpt-5.6-luna", - "name": "GPT-5.6-Luna", + "name": "GPT-5.6 Luna", "api": "openai-codex-responses", "provider": "openai-codex", "baseUrl": "https://chatgpt.com/backend-api", @@ -61340,10 +61001,10 @@ "image" ], "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 + "input": 1, + "output": 6, + "cacheRead": 0.1, + "cacheWrite": 1.25 }, "remoteCompaction": { "enabled": true, @@ -61358,16 +61019,24 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "gpt-5.6-sol": { "id": "gpt-5.6-sol", - "name": "GPT-5.6-Sol", + "name": "GPT-5.6 Sol", "api": "openai-codex-responses", "provider": "openai-codex", "baseUrl": "https://chatgpt.com/backend-api", @@ -61377,10 +61046,10 @@ "image" ], "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 + "input": 5, + "output": 30, + "cacheRead": 0.5, + "cacheWrite": 6.25 }, "remoteCompaction": { "enabled": true, @@ -61395,16 +61064,24 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "gpt-5.6-terra": { "id": "gpt-5.6-terra", - "name": "GPT-5.6-Terra", + "name": "GPT-5.6 Terra", "api": "openai-codex-responses", "provider": "openai-codex", "baseUrl": "https://chatgpt.com/backend-api", @@ -61414,10 +61091,10 @@ "image" ], "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 + "input": 2.5, + "output": 15, + "cacheRead": 0.25, + "cacheWrite": 3.125 }, "remoteCompaction": { "enabled": true, @@ -61432,11 +61109,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } } }, @@ -69869,11 +69554,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-5.6-luna-pro": { @@ -69898,11 +69591,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-5.6-sol": { @@ -69927,11 +69628,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-5.6-sol-pro": { @@ -69956,11 +69665,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-5.6-terra": { @@ -69985,11 +69702,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-5.6-terra-pro": { @@ -70014,11 +69739,19 @@ "thinking": { "mode": "effort", "efforts": [ + "minimal", "low", "medium", "high", "xhigh" - ] + ], + "effortMap": { + "minimal": "low", + "low": "medium", + "medium": "high", + "high": "xhigh", + "xhigh": "max" + } } }, "openai/gpt-audio": { @@ -73625,13 +73358,13 @@ "text" ], "cost": { - "input": 0.546, - "output": 1.716, - "cacheRead": 0.10139999999999999, + "input": 0.54, + "output": 1.76, + "cacheRead": 0.09999999999999999, "cacheWrite": 0 }, "contextWindow": 1048576, - "maxTokens": 128000, + "maxTokens": 101376, "thinking": { "mode": "effort", "efforts": [ @@ -75776,7 +75509,7 @@ "cacheRead": 0, "cacheWrite": 0 }, - "contextWindow": 1048576, + "contextWindow": 1000000, "maxTokens": 1048576, "compat": { "supportsUsageInStreaming": false @@ -82443,6 +82176,7 @@ "thinking": { "mode": "budget", "efforts": [ + "minimal", "low", "medium", "high", @@ -82472,6 +82206,7 @@ "thinking": { "mode": "budget", "efforts": [ + "minimal", "low", "medium", "high", @@ -82501,6 +82236,7 @@ "thinking": { "mode": "budget", "efforts": [ + "minimal", "low", "medium", "high", diff --git a/packages/catalog/src/variant-collapse.ts b/packages/catalog/src/variant-collapse.ts index b3042503d..46d1f9d52 100644 --- a/packages/catalog/src/variant-collapse.ts +++ b/packages/catalog/src/variant-collapse.ts @@ -113,6 +113,14 @@ function thinkingPair(baseId: string, name: string): EffortVariantFamily { type DevinTierRoutes = Partial>; +const DEVIN_FIVE_TIER_EFFORTS: readonly Effort[] = [ + Effort.Minimal, + Effort.Low, + Effort.Medium, + Effort.High, + Effort.XHigh, +]; + function devinTierFamily( id: string, name: string, @@ -160,6 +168,44 @@ function devinTierFamily( }; } +/** + * GPT-5.6 (Luna/Sol/Terra) adds a genuine `max` tier above `xhigh`, so the + * standard family shifts every user effort up one notch (`minimal` → `-low` + * … `xhigh` → `-max`), mirroring the Opus 4.7+ five-tier mapping. Devin + * serves no `-max-priority` sibling, so the fast family keeps the direct + * `low..xhigh` `-priority` scale. + */ +function devinGpt56Families(variant: "luna" | "sol" | "terra", name: string): readonly EffortVariantFamily[] { + const base = `gpt-5-6-${variant}`; + return [ + devinTierFamily( + base, + name, + { + off: `${base}-none`, + minimal: `${base}-low`, + low: `${base}-medium`, + medium: `${base}-high`, + high: `${base}-xhigh`, + xhigh: `${base}-max`, + }, + DEVIN_FIVE_TIER_EFFORTS, + ), + devinTierFamily( + `${base}-fast`, + `${name} Fast`, + { + off: `${base}-none-priority`, + low: `${base}-low-priority`, + medium: `${base}-medium-priority`, + high: `${base}-high-priority`, + xhigh: `${base}-xhigh-priority`, + }, + DEVIN_FIVE_TIER_EFFORTS, + ), + ]; +} + const GEMINI_3_FLASH_FAMILY_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High]; const GEMINI_3_PRO_FAMILY_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High]; @@ -330,7 +376,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { }, thinking: { mode: "effort", - efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + efforts: DEVIN_FIVE_TIER_EFFORTS, requiresEffort: true, }, }, @@ -353,7 +399,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { }, thinking: { mode: "effort", - efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + efforts: DEVIN_FIVE_TIER_EFFORTS, requiresEffort: true, }, }, @@ -376,7 +422,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { }, thinking: { mode: "effort", - efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + efforts: DEVIN_FIVE_TIER_EFFORTS, requiresEffort: true, }, }, @@ -399,7 +445,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { }, thinking: { mode: "effort", - efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + efforts: DEVIN_FIVE_TIER_EFFORTS, requiresEffort: true, }, }, @@ -413,7 +459,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "MODEL_GPT_5_2_HIGH", xhigh: "MODEL_GPT_5_2_XHIGH", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-3-codex", @@ -424,7 +470,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-3-codex-high", xhigh: "gpt-5-3-codex-xhigh", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-3-codex-fast", @@ -435,7 +481,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-3-codex-high-priority", xhigh: "gpt-5-3-codex-xhigh-priority", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-4", @@ -447,7 +493,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-4-high", xhigh: "gpt-5-4-xhigh", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-4-fast", @@ -459,7 +505,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-4-high-priority", xhigh: "gpt-5-4-xhigh-priority", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-4-mini", @@ -470,7 +516,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-4-mini-high", xhigh: "gpt-5-4-mini-xhigh", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-5", @@ -482,7 +528,7 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-5-high", xhigh: "gpt-5-5-xhigh", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), devinTierFamily( "gpt-5-5-fast", @@ -494,8 +540,11 @@ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = { high: "gpt-5-5-high-priority", xhigh: "gpt-5-5-xhigh-priority", }, - [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + DEVIN_FIVE_TIER_EFFORTS, ), + ...devinGpt56Families("luna", "GPT-5.6 Luna"), + ...devinGpt56Families("sol", "GPT-5.6 Sol"), + ...devinGpt56Families("terra", "GPT-5.6 Terra"), devinTierFamily( "gemini-3-1-pro", "Gemini 3.1 Pro", diff --git a/packages/catalog/test/model-thinking.test.ts b/packages/catalog/test/model-thinking.test.ts index 6d6019fe1..719d8c59e 100644 --- a/packages/catalog/test/model-thinking.test.ts +++ b/packages/catalog/test/model-thinking.test.ts @@ -567,6 +567,96 @@ describe("model thinking derivation", () => { expect(getSupportedEfforts(model)).toEqual([]); expect(clampThinkingLevelForModel(model, Effort.High)).toBeUndefined(); }); + + it("bakes the GPT-5.6 shifted five-tier effort map on wire-effort APIs", () => { + const codex = createModel({ + id: "gpt-5.6-sol", + api: "openai-codex-responses", + provider: "openai-codex", + }); + + expect(codex.thinking).toEqual({ + mode: "effort", + efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + effortMap: { + minimal: "low", + low: "medium", + medium: "high", + high: "xhigh", + xhigh: "max", + }, + }); + + // Stale baked four-tier metadata (caches/discovery) normalizes back to + // the five-tier ladder with the map attached — the wire-defaults + // backfill path — and namespaced OpenRouter ids parse. + const staleOpenRouter = createModel({ + id: "openai/gpt-5.6-terra", + api: "openrouter", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + thinking: { + mode: "effort", + efforts: [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + }, + }); + + expect(staleOpenRouter.thinking).toEqual({ + mode: "effort", + efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + effortMap: { + minimal: "low", + low: "medium", + medium: "high", + high: "xhigh", + xhigh: "max", + }, + }); + }); + + it("keeps pre-5.6 and Devin-routed GPT models off the shifted effort map", () => { + const gpt55 = createModel({ + id: "gpt-5.5", + api: "openai-responses", + provider: "openai", + }); + + expect(gpt55.thinking).toEqual({ + mode: "effort", + efforts: [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + }); + expect(gpt55.thinking?.effortMap).toBeUndefined(); + + // Devin selects effort by routing to per-tier sibling model ids, never + // via a wire reasoning.effort field — the shifted map must not attach. + const devin = createModel({ + id: "gpt-5-6-sol", + api: "devin-agent", + provider: "devin", + baseUrl: "https://server.codeium.com", + thinking: { + mode: "effort", + efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh], + effortRouting: { + off: "gpt-5-6-sol-none", + minimal: "gpt-5-6-sol-low", + low: "gpt-5-6-sol-medium", + medium: "gpt-5-6-sol-high", + high: "gpt-5-6-sol-xhigh", + xhigh: "gpt-5-6-sol-max", + }, + }, + }); + + expect(devin.thinking?.effortMap).toBeUndefined(); + expect(devin.thinking?.efforts).toEqual([ + Effort.Minimal, + Effort.Low, + Effort.Medium, + Effort.High, + Effort.XHigh, + ]); + }); }); describe("model thinking runtime helpers", () => { From 4a20b51ca8024868a187e32a347f5269b134cc3c Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 9 Jul 2026 20:35:43 +0200 Subject: [PATCH 03/11] feat: implemented auto-sealing for transcript blocks and TUI row emission - Added auto-sealing logic to `FinalizableBlock` to finalize displaceable snapshots when they enter the scrollback area. - Updated TUI frame emission to publish committed rows and clamp them to segment bounds, ensuring accurate component updates. - Introduced component tracking and cleanup in event controller tests to prevent resource leaks during finalization. - Validated state transitions and post-emit synchronization through comprehensive new test suites for transcript and TUI components. --- packages/catalog/test/model-thinking.test.ts | 8 +- packages/coding-agent/CHANGELOG.md | 3 +- .../modes/components/transcript-container.ts | 33 ++++ .../src/prompts/system/workflow-notice.md | 10 +- .../components/transcript-container.test.ts | 160 ++++++++++++++++++ ...event-controller-toolcall-finalize.test.ts | 9 +- packages/tui/CHANGELOG.md | 4 + packages/tui/src/tui.ts | 31 +++- .../test/streaming-scrollback-defer.test.ts | 141 +++++++++++++++ 9 files changed, 382 insertions(+), 17 deletions(-) diff --git a/packages/catalog/test/model-thinking.test.ts b/packages/catalog/test/model-thinking.test.ts index 719d8c59e..a21a323fc 100644 --- a/packages/catalog/test/model-thinking.test.ts +++ b/packages/catalog/test/model-thinking.test.ts @@ -649,13 +649,7 @@ describe("model thinking derivation", () => { }); expect(devin.thinking?.effortMap).toBeUndefined(); - expect(devin.thinking?.efforts).toEqual([ - Effort.Minimal, - Effort.Low, - Effort.Medium, - Effort.High, - Effort.XHigh, - ]); + expect(devin.thinking?.efforts).toEqual([Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh]); }); }); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 636167fe5..896d13193 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,9 +4,10 @@ ### Fixed +- Fixed issue where unfinalized tool blocks could incorrectly pin the live-region scroll seam + - Improved rendering of raw thinking blocks by stripping empty HTML comment noise - Fixed display of thinking blocks consisting entirely of hidden comment noise - - Fixed gpt-5.6 reasoning summaries rendering literal `` sentinel lines in thinking blocks; empty HTML comments (and the unterminated `` sentinel lines in thinking blocks; empty HTML comments (and the unterminated `