From 8b6aaf521ec9c4b722f28d5fb9f15e430b4d1ba1 Mon Sep 17 00:00:00 2001
From: boggedbrush <90526147+boggedbrush@users.noreply.github.com>
Date: Wed, 29 Apr 2026 12:21:22 -0400
Subject: [PATCH 1/3] feat(codex): add gpt-5.5 support
---
apps/desktop/scripts/electron-launcher.mjs | 80 ++++++++++++--
apps/desktop/src/main.ts | 35 +-----
apps/server/src/codexAppServerManager.test.ts | 10 +-
apps/server/src/codexAppServerManager.ts | 3 +-
apps/server/src/os-jank.ts | 6 +-
.../persistence/Layers/ProjectionThreads.ts | 2 +-
.../src/provider/Layers/CodexProvider.ts | 101 ++++--------------
.../provider/Layers/ProviderRegistry.test.ts | 10 ++
apps/server/src/provider/codexAccount.ts | 4 +-
apps/server/src/serverSettings.test.ts | 66 +++++++++---
apps/server/src/serverSettings.ts | 48 +++++----
.../components/chat/ModelPickerContent.tsx | 13 ++-
.../chat/ProviderModelPicker.browser.tsx | 23 ++++
.../components/chat/ProviderModelPicker.tsx | 2 +
.../settings/SettingsModelsSection.test.tsx | 6 ++
.../settings/SettingsModelsSection.tsx | 46 ++++----
.../settings/SettingsPanelPrimitives.tsx | 1 +
packages/contracts/src/model.ts | 3 +-
packages/shared/src/model.test.ts | 3 +-
scripts/build-desktop-artifact.ts | 2 +-
20 files changed, 273 insertions(+), 191 deletions(-)
diff --git a/apps/desktop/scripts/electron-launcher.mjs b/apps/desktop/scripts/electron-launcher.mjs
index 1bd358fc..ae85448a 100644
--- a/apps/desktop/scripts/electron-launcher.mjs
+++ b/apps/desktop/scripts/electron-launcher.mjs
@@ -10,6 +10,7 @@ import {
readdirSync,
rmSync,
statSync,
+ symlinkSync,
writeFileSync,
} from "node:fs";
import { createRequire } from "node:module";
@@ -18,8 +19,8 @@ import { fileURLToPath } from "node:url";
const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
const APP_DISPLAY_NAME = isDevelopment ? "Kodo Code (Dev)" : "Kodo Code (Alpha)";
-const APP_BUNDLE_ID = isDevelopment ? "com.kodo.code.dev" : "com.kodo.code";
-const LAUNCHER_VERSION = 2;
+const APP_BUNDLE_ID = isDevelopment ? "app.kodocode.dev" : "app.kodocode";
+const LAUNCHER_VERSION = 4;
const ELECTRON_INSTALL_CANDIDATES = ["node", "bun"];
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -46,8 +47,33 @@ function setPlistString(plistPath, key, value) {
throw new Error(`Failed to update plist key "${key}" at ${plistPath}: ${details}`.trim());
}
+function ensureMainBundleInfoPlist(appBundlePath) {
+ const contentsDir = join(appBundlePath, "Contents");
+ const infoPlistPath = join(contentsDir, "Info.plist");
+ if (existsSync(infoPlistPath)) {
+ return infoPlistPath;
+ }
+
+ mkdirSync(contentsDir, { recursive: true });
+ writeFileSync(
+ infoPlistPath,
+ `
+
+
+
+ CFBundleExecutable
+ Electron
+ CFBundlePackageType
+ APPL
+
+
+`,
+ );
+ return infoPlistPath;
+}
+
function patchMainBundleInfoPlist(appBundlePath, iconPath) {
- const infoPlistPath = join(appBundlePath, "Contents", "Info.plist");
+ const infoPlistPath = ensureMainBundleInfoPlist(appBundlePath);
setPlistString(infoPlistPath, "CFBundleDisplayName", APP_DISPLAY_NAME);
setPlistString(infoPlistPath, "CFBundleName", APP_DISPLAY_NAME);
setPlistString(infoPlistPath, "CFBundleIdentifier", APP_BUNDLE_ID);
@@ -92,6 +118,45 @@ function patchHelperBundleInfoPlists(appBundlePath) {
}
}
+function repairFrameworkVersionSymlinks(appBundlePath) {
+ const frameworksDir = join(appBundlePath, "Contents", "Frameworks");
+ if (!existsSync(frameworksDir)) {
+ return;
+ }
+
+ for (const entry of readdirSync(frameworksDir, { withFileTypes: true })) {
+ if (!entry.isDirectory() || !entry.name.endsWith(".framework")) {
+ continue;
+ }
+
+ const frameworkPath = join(frameworksDir, entry.name);
+ const frameworkExecutable = entry.name.replace(/\.framework$/, "");
+ const versionsDir = join(frameworkPath, "Versions");
+ const versionAPath = join(versionsDir, "A");
+ const currentPath = join(versionsDir, "Current");
+ if (!existsSync(versionAPath)) {
+ continue;
+ }
+
+ if (!existsSync(currentPath)) {
+ rmSync(currentPath, { force: true });
+ symlinkSync("A", currentPath);
+ }
+
+ const executablePath = join(frameworkPath, frameworkExecutable);
+ if (existsSync(join(versionAPath, frameworkExecutable))) {
+ rmSync(executablePath, { force: true });
+ symlinkSync(`Versions/Current/${frameworkExecutable}`, executablePath);
+ }
+
+ const resourcesPath = join(frameworkPath, "Resources");
+ if (existsSync(join(versionAPath, "Resources"))) {
+ rmSync(resourcesPath, { force: true });
+ symlinkSync("Versions/Current/Resources", resourcesPath);
+ }
+ }
+}
+
function readJson(path) {
try {
return JSON.parse(readFileSync(path, "utf8"));
@@ -198,6 +263,7 @@ function buildMacLauncher(electronBinaryPath) {
const runtimeDir = join(desktopDir, ".electron-runtime");
const targetAppBundlePath = join(runtimeDir, `${APP_DISPLAY_NAME}.app`);
const targetBinaryPath = join(targetAppBundlePath, "Contents", "MacOS", "Electron");
+ const targetInfoPlistPath = join(targetAppBundlePath, "Contents", "Info.plist");
const iconPath = resolveMacLauncherIconPath();
const metadataPath = join(runtimeDir, "metadata.json");
@@ -205,10 +271,10 @@ function buildMacLauncher(electronBinaryPath) {
const expectedMetadata = {
launcherVersion: LAUNCHER_VERSION,
+ appDisplayName: APP_DISPLAY_NAME,
+ appBundleId: APP_BUNDLE_ID,
sourceAppBundlePath,
sourceAppMtimeMs: statSync(sourceAppBundlePath).mtimeMs,
- appBundleId: APP_BUNDLE_ID,
- appDisplayName: APP_DISPLAY_NAME,
iconPath,
iconMtimeMs: statSync(iconPath).mtimeMs,
};
@@ -216,6 +282,7 @@ function buildMacLauncher(electronBinaryPath) {
const currentMetadata = readJson(metadataPath);
if (
existsSync(targetBinaryPath) &&
+ existsSync(targetInfoPlistPath) &&
currentMetadata &&
JSON.stringify(currentMetadata) === JSON.stringify(expectedMetadata)
) {
@@ -223,7 +290,8 @@ function buildMacLauncher(electronBinaryPath) {
}
rmSync(targetAppBundlePath, { recursive: true, force: true });
- cpSync(sourceAppBundlePath, targetAppBundlePath, { recursive: true });
+ cpSync(sourceAppBundlePath, targetAppBundlePath, { recursive: true, verbatimSymlinks: true });
+ repairFrameworkVersionSymlinks(targetAppBundlePath);
patchMainBundleInfoPlist(targetAppBundlePath, iconPath);
patchHelperBundleInfoPlists(targetAppBundlePath);
writeFileSync(metadataPath, `${JSON.stringify(expectedMetadata, null, 2)}\n`);
diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts
index 5ab01ff0..f4dbba69 100644
--- a/apps/desktop/src/main.ts
+++ b/apps/desktop/src/main.ts
@@ -76,7 +76,7 @@ const LEGACY_DESKTOP_SCHEME = "t3";
const ROOT_DIR = Path.resolve(__dirname, "../../..");
const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
const APP_DISPLAY_NAME = isDevelopment ? "Kodo Code (Dev)" : "Kodo Code (Alpha)";
-const APP_USER_MODEL_ID = "com.kodo.code";
+const APP_USER_MODEL_ID = "app.kodocode";
const LINUX_DESKTOP_ENTRY_NAME = isDevelopment ? "kodo-code-dev.desktop" : "kodo-code.desktop";
const LINUX_WM_CLASS = isDevelopment ? "kodo-code-dev" : "kodo-code";
const USER_DATA_DIR_NAME = isDevelopment ? "kodo-code-dev" : "kodo-code";
@@ -803,32 +803,7 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null {
return resolveResourcePath(`icon.${ext}`);
}
-function resolveMacDockIcon(): Electron.NativeImage | null {
- const iconPath = resolveIconPath("icns");
- if (!iconPath) {
- return null;
- }
-
- const icon = nativeImage.createFromPath(iconPath);
- return icon.isEmpty() ? null : icon;
-}
-
-function resolveVectorIconPath(): string | null {
- const candidate = isDevelopment
- ? Path.join(ROOT_DIR, "assets/dev/blueprint.svg")
- : Path.join(ROOT_DIR, "assets/prod/logo.svg");
- return FS.existsSync(candidate) ? candidate : null;
-}
-
function resolveNativeAppIcon(ext: "ico" | "icns" | "png"): Electron.NativeImage | null {
- const vectorIconPath = resolveVectorIconPath();
- if (vectorIconPath) {
- const vectorIcon = nativeImage.createFromPath(vectorIconPath);
- if (!vectorIcon.isEmpty()) {
- return vectorIcon;
- }
- }
-
const rasterIconPath = resolveIconPath(ext);
if (!rasterIconPath) {
return null;
@@ -885,12 +860,8 @@ function configureAppIdentity(): void {
(app as LinuxDesktopNamedApp).setDesktopName?.(LINUX_DESKTOP_ENTRY_NAME);
}
- if (process.platform === "darwin" && app.dock) {
- const icon = resolveMacDockIcon();
- if (icon) {
- app.dock.setIcon(icon);
- }
- }
+ // macOS uses CFBundleIconFile from the app bundle. Calling app.dock.setIcon()
+ // renders the raw image payload and bypasses the bundle icon presentation.
}
function clearUpdatePollTimer(): void {
diff --git a/apps/server/src/codexAppServerManager.test.ts b/apps/server/src/codexAppServerManager.test.ts
index 708ebf0a..1a2e6db1 100644
--- a/apps/server/src/codexAppServerManager.test.ts
+++ b/apps/server/src/codexAppServerManager.test.ts
@@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
-import { ApprovalRequestId, ThreadId } from "@t3tools/contracts";
+import { ApprovalRequestId, DEFAULT_MODEL_BY_PROVIDER, ThreadId } from "@t3tools/contracts";
import {
buildCodexInitializeParams,
@@ -249,6 +249,10 @@ describe("normalizeCodexModelSlug", () => {
expect(normalizeCodexModelSlug("gpt-5.3")).toBe("gpt-5.3-codex");
});
+ it("maps 5.5 aliases to gpt-5.5", () => {
+ expect(normalizeCodexModelSlug("5.5")).toBe("gpt-5.5");
+ });
+
it("prefers codex id when model differs", () => {
expect(normalizeCodexModelSlug("gpt-5.3", "gpt-5.3-codex")).toBe("gpt-5.3-codex");
});
@@ -381,7 +385,7 @@ describe("resolveCodexModelForAccount", () => {
planType: "plus",
sparkEnabled: false,
}),
- ).toBe("gpt-5.3-codex");
+ ).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
});
it("keeps spark for supported plans", () => {
@@ -401,7 +405,7 @@ describe("resolveCodexModelForAccount", () => {
planType: null,
sparkEnabled: false,
}),
- ).toBe("gpt-5.3-codex");
+ ).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
});
});
diff --git a/apps/server/src/codexAppServerManager.ts b/apps/server/src/codexAppServerManager.ts
index b4cc8e5c..c4e3649d 100644
--- a/apps/server/src/codexAppServerManager.ts
+++ b/apps/server/src/codexAppServerManager.ts
@@ -27,6 +27,7 @@ import {
parseCodexCliVersion,
} from "./provider/codexCliVersion";
import {
+ CODEX_DEFAULT_MODEL,
readCodexAccountSnapshot,
resolveCodexModelForAccount,
type CodexAccountSnapshot,
@@ -384,7 +385,7 @@ function buildCodexCollaborationMode(input: {
}
const codexMode =
input.interactionMode === "plan" || input.interactionMode === "review" ? "plan" : "default";
- const model = normalizeCodexModelSlug(input.model) ?? "gpt-5.3-codex";
+ const model = normalizeCodexModelSlug(input.model) ?? CODEX_DEFAULT_MODEL;
return {
mode: codexMode,
settings: {
diff --git a/apps/server/src/os-jank.ts b/apps/server/src/os-jank.ts
index 5ea849cc..011ef196 100644
--- a/apps/server/src/os-jank.ts
+++ b/apps/server/src/os-jank.ts
@@ -56,9 +56,9 @@ export function fixPath(
}
}
-export const expandHomePath = Effect.fn(function* (input: string) {
- return expandHomePathValue(input);
-});
+export const expandHomePath = Effect.fn((input: string) =>
+ Effect.succeed(expandHomePathValue(input)),
+);
export const resolveBaseDir = Effect.fn(function* (raw: string | undefined) {
const { join, resolve } = yield* Path.Path;
diff --git a/apps/server/src/persistence/Layers/ProjectionThreads.ts b/apps/server/src/persistence/Layers/ProjectionThreads.ts
index c5e17343..05134458 100644
--- a/apps/server/src/persistence/Layers/ProjectionThreads.ts
+++ b/apps/server/src/persistence/Layers/ProjectionThreads.ts
@@ -1,6 +1,6 @@
import * as SqlClient from "effect/unstable/sql/SqlClient";
import * as SqlSchema from "effect/unstable/sql/SqlSchema";
-import { Effect, Layer, Option, Schema, Struct } from "effect";
+import { Effect, Layer, Option, Schema } from "effect";
import { toPersistenceSqlError } from "../Errors.ts";
import {
diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts
index 834c6ef8..e6c647ce 100644
--- a/apps/server/src/provider/Layers/CodexProvider.ts
+++ b/apps/server/src/provider/Layers/CodexProvider.ts
@@ -50,123 +50,68 @@ import { ServerSettingsService } from "../../serverSettings";
import { expandHomePath } from "../../pathExpansion";
import { ServerSettingsError } from "@t3tools/contracts";
-const DEFAULT_CODEX_MODEL_CAPABILITIES: ModelCapabilities = {
+const codexModelCapabilities = (
+ defaultReasoningEffort: "xhigh" | "high" | "medium" | "low" = "high",
+): ModelCapabilities => ({
reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
+ { value: "xhigh", label: "Extra High", isDefault: defaultReasoningEffort === "xhigh" },
+ { value: "high", label: "High", isDefault: defaultReasoningEffort === "high" },
+ { value: "medium", label: "Medium", isDefault: defaultReasoningEffort === "medium" },
+ { value: "low", label: "Low", isDefault: defaultReasoningEffort === "low" },
],
supportsFastMode: true,
supportsThinkingToggle: false,
contextWindowOptions: [],
promptInjectedEffortLevels: [],
-};
+});
+
+const DEFAULT_CODEX_MODEL_CAPABILITIES: ModelCapabilities = codexModelCapabilities();
const PROVIDER = "codex" as const;
const OPENAI_AUTH_PROVIDERS = new Set(["openai"]);
+
const BUILT_IN_MODELS: ReadonlyArray = [
+ {
+ slug: "gpt-5.5",
+ name: "GPT-5.5",
+ isCustom: false,
+ capabilities: codexModelCapabilities("low"),
+ },
{
slug: "gpt-5.4",
name: "GPT-5.4",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
{
slug: "gpt-5.4-mini",
name: "GPT-5.4 Mini",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
{
slug: "gpt-5.3-codex",
name: "GPT-5.3 Codex",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
{
slug: "gpt-5.3-codex-spark",
name: "GPT-5.3 Codex Spark",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
{
slug: "gpt-5.2-codex",
name: "GPT-5.2 Codex",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
{
slug: "gpt-5.2",
name: "GPT-5.2",
isCustom: false,
- capabilities: {
- reasoningEffortLevels: [
- { value: "xhigh", label: "Extra High" },
- { value: "high", label: "High", isDefault: true },
- { value: "medium", label: "Medium" },
- { value: "low", label: "Low" },
- ],
- supportsFastMode: true,
- supportsThinkingToggle: false,
- contextWindowOptions: [],
- promptInjectedEffortLevels: [],
- },
+ capabilities: codexModelCapabilities(),
},
];
diff --git a/apps/server/src/provider/Layers/ProviderRegistry.test.ts b/apps/server/src/provider/Layers/ProviderRegistry.test.ts
index 43af723d..cdee14da 100644
--- a/apps/server/src/provider/Layers/ProviderRegistry.test.ts
+++ b/apps/server/src/provider/Layers/ProviderRegistry.test.ts
@@ -176,6 +176,16 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsService.layerTest()))(
assert.strictEqual(status.status, "ready");
assert.strictEqual(status.installed, true);
assert.strictEqual(status.auth.status, "authenticated");
+ assert.strictEqual(
+ status.models.some((model) => model.slug === "gpt-5.5" && model.name === "GPT-5.5"),
+ true,
+ );
+ const gpt55 = status.models.find((model) => model.slug === "gpt-5.5");
+ assert.ok(gpt55?.capabilities);
+ assert.strictEqual(
+ gpt55.capabilities.reasoningEffortLevels.find((level) => level.isDefault)?.value,
+ "low",
+ );
}).pipe(
Effect.provide(
mockSpawnerLayer((args) => {
diff --git a/apps/server/src/provider/codexAccount.ts b/apps/server/src/provider/codexAccount.ts
index 52e59a57..8812c035 100644
--- a/apps/server/src/provider/codexAccount.ts
+++ b/apps/server/src/provider/codexAccount.ts
@@ -1,4 +1,4 @@
-import type { ServerProviderModel } from "@t3tools/contracts";
+import { DEFAULT_MODEL_BY_PROVIDER, type ServerProviderModel } from "@t3tools/contracts";
export type CodexPlanType =
| "free"
@@ -18,7 +18,7 @@ export interface CodexAccountSnapshot {
readonly sparkEnabled: boolean;
}
-export const CODEX_DEFAULT_MODEL = "gpt-5.3-codex";
+export const CODEX_DEFAULT_MODEL = DEFAULT_MODEL_BY_PROVIDER.codex;
export const CODEX_SPARK_MODEL = "gpt-5.3-codex-spark";
const CODEX_SPARK_ENABLED_PLAN_TYPES = new Set(["pro"]);
diff --git a/apps/server/src/serverSettings.test.ts b/apps/server/src/serverSettings.test.ts
index d9ca3e39..c03c590a 100644
--- a/apps/server/src/serverSettings.test.ts
+++ b/apps/server/src/serverSettings.test.ts
@@ -455,11 +455,45 @@ it.layer(NodeServices.layer)("server settings", (it) => {
assert.equal(next.modelSelectionPresets.codex[DEFAULT_MODEL_SELECTION_PRESET_ID], undefined);
assert.equal(next.modelSelectionPresets.codex["starter-codex-free"]?.name, "Free");
+ assert.deepEqual(
+ next.modelSelectionPresets.codex["starter-codex-free"]?.reviewModelSelection,
+ {
+ provider: "codex",
+ model: "gpt-5.4-mini",
+ options: {
+ reasoningEffort: "high",
+ },
+ },
+ );
+ assert.deepEqual(next.modelSelectionPresets.codex["starter-codex-go"]?.planModelSelection, {
+ provider: "codex",
+ model: "gpt-5.4-mini",
+ options: {
+ reasoningEffort: "high",
+ },
+ });
+ assert.deepEqual(next.modelSelectionPresets.codex["starter-codex-plus"]?.planModelSelection, {
+ provider: "codex",
+ model: "gpt-5.5",
+ options: {
+ reasoningEffort: "medium",
+ },
+ });
+ assert.deepEqual(
+ next.modelSelectionPresets.codex["starter-codex-plus"]?.reviewModelSelection,
+ {
+ provider: "codex",
+ model: "gpt-5.5",
+ options: {
+ reasoningEffort: "low",
+ },
+ },
+ );
assert.deepEqual(
next.modelSelectionPresets.codex["starter-codex-pro-5x"]?.askModelSelection,
{
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: {
reasoningEffort: "low",
},
@@ -469,9 +503,9 @@ it.layer(NodeServices.layer)("server settings", (it) => {
next.modelSelectionPresets.codex["starter-codex-pro-5x"]?.codeModelSelection,
{
provider: "codex",
- model: "gpt-5.3-codex",
+ model: "gpt-5.5",
options: {
- reasoningEffort: "high",
+ reasoningEffort: "medium",
},
},
);
@@ -479,9 +513,9 @@ it.layer(NodeServices.layer)("server settings", (it) => {
next.modelSelectionPresets.codex["starter-codex-pro-20x"]?.askModelSelection,
{
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: {
- reasoningEffort: "medium",
+ reasoningEffort: "low",
},
},
);
@@ -489,9 +523,9 @@ it.layer(NodeServices.layer)("server settings", (it) => {
next.modelSelectionPresets.codex["starter-codex-pro-20x"]?.reviewModelSelection,
{
provider: "codex",
- model: "gpt-5.3-codex",
+ model: "gpt-5.5",
options: {
- reasoningEffort: "xhigh",
+ reasoningEffort: "high",
},
},
);
@@ -515,21 +549,21 @@ it.layer(NodeServices.layer)("server settings", (it) => {
}).pipe(Effect.provide(makeServerSettingsLayer())),
);
- it.effect("preserves legacy starter preset entries while still seeding missing built-ins", () =>
+ it.effect("updates legacy starter preset entries while still seeding missing built-ins", () =>
Effect.gen(function* () {
const serverSettings = yield* ServerSettingsService;
const next = yield* serverSettings.getSettings;
assert.equal(next.modelSelectionPresets.codex["starter-codex-pro-100"]?.name, "pro 100");
assert.equal(next.modelSelectionPresets.codex["starter-codex-pro-200"]?.name, "pro 200");
- assert.equal(next.modelSelectionPresets.codex["starter-codex-free"]?.name, "free");
- assert.equal(next.modelSelectionPresets.codex["starter-codex-go"]?.name, "go");
- assert.equal(next.modelSelectionPresets.codex["starter-codex-plus"]?.name, "plus");
+ assert.equal(next.modelSelectionPresets.codex["starter-codex-free"]?.name, "Free");
+ assert.equal(next.modelSelectionPresets.codex["starter-codex-go"]?.name, "Go");
+ assert.equal(next.modelSelectionPresets.codex["starter-codex-plus"]?.name, "Plus");
assert.deepEqual(
next.modelSelectionPresets.codex["starter-codex-pro-5x"]?.askModelSelection,
{
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: {
reasoningEffort: "low",
},
@@ -539,9 +573,9 @@ it.layer(NodeServices.layer)("server settings", (it) => {
next.modelSelectionPresets.codex["starter-codex-pro-5x"]?.codeModelSelection,
{
provider: "codex",
- model: "gpt-5.3-codex",
+ model: "gpt-5.5",
options: {
- reasoningEffort: "high",
+ reasoningEffort: "medium",
},
},
);
@@ -549,9 +583,9 @@ it.layer(NodeServices.layer)("server settings", (it) => {
next.modelSelectionPresets.codex["starter-codex-pro-20x"]?.askModelSelection,
{
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: {
- reasoningEffort: "medium",
+ reasoningEffort: "low",
},
},
);
diff --git a/apps/server/src/serverSettings.ts b/apps/server/src/serverSettings.ts
index 342ee8dc..e95bc0dc 100644
--- a/apps/server/src/serverSettings.ts
+++ b/apps/server/src/serverSettings.ts
@@ -116,6 +116,8 @@ const PRESET_SELECTION_SETTINGS_KEYS = [
"reviewModelSelection",
] as const;
type PresetSelectionSettingsKey = (typeof PRESET_SELECTION_SETTINGS_KEYS)[number];
+const CODEX_EFFICIENT_MODEL = "gpt-5.4-mini";
+const CODEX_RECOMMENDED_MODEL = "gpt-5.5";
function makeCodexPreset(
id: string,
@@ -216,34 +218,34 @@ const BUILT_IN_MODEL_SELECTION_PRESETS: {
} = {
codex: [
makeCodexPreset("starter-codex-free", "Free", {
- ask: { model: "gpt-5.4-mini", effort: "low" },
- plan: { model: "gpt-5.4-mini", effort: "medium" },
- code: { model: "gpt-5.4-mini", effort: "low" },
- review: { model: "gpt-5.4", effort: "low" },
+ ask: { model: CODEX_EFFICIENT_MODEL, effort: "low" },
+ plan: { model: CODEX_EFFICIENT_MODEL, effort: "medium" },
+ code: { model: CODEX_EFFICIENT_MODEL, effort: "medium" },
+ review: { model: CODEX_EFFICIENT_MODEL, effort: "high" },
}),
makeCodexPreset("starter-codex-go", "Go", {
- ask: { model: "gpt-5.4-mini", effort: "low" },
- plan: { model: "gpt-5.4-mini", effort: "medium" },
- code: { model: "gpt-5.4-mini", effort: "medium" },
- review: { model: "gpt-5.3-codex", effort: "medium" },
+ ask: { model: CODEX_EFFICIENT_MODEL, effort: "low" },
+ plan: { model: CODEX_EFFICIENT_MODEL, effort: "high" },
+ code: { model: CODEX_EFFICIENT_MODEL, effort: "medium" },
+ review: { model: CODEX_EFFICIENT_MODEL, effort: "high" },
}),
makeCodexPreset("starter-codex-plus", "Plus", {
- ask: { model: "gpt-5.4-mini", effort: "low" },
- plan: { model: "gpt-5.4", effort: "medium" },
- code: { model: "gpt-5.4-mini", effort: "medium" },
- review: { model: "gpt-5.3-codex", effort: "medium" },
+ ask: { model: CODEX_EFFICIENT_MODEL, effort: "low" },
+ plan: { model: CODEX_RECOMMENDED_MODEL, effort: "medium" },
+ code: { model: CODEX_EFFICIENT_MODEL, effort: "medium" },
+ review: { model: CODEX_RECOMMENDED_MODEL, effort: "low" },
}),
makeCodexPreset("starter-codex-pro-5x", "Pro (5X)", {
- ask: { model: "gpt-5.4", effort: "low" },
- plan: { model: "gpt-5.4", effort: "high" },
- code: { model: "gpt-5.3-codex", effort: "high" },
- review: { model: "gpt-5.3-codex", effort: "high" },
+ ask: { model: CODEX_RECOMMENDED_MODEL, effort: "low" },
+ plan: { model: CODEX_RECOMMENDED_MODEL, effort: "medium" },
+ code: { model: CODEX_RECOMMENDED_MODEL, effort: "medium" },
+ review: { model: CODEX_RECOMMENDED_MODEL, effort: "high" },
}),
makeCodexPreset("starter-codex-pro-20x", "Pro (20X)", {
- ask: { model: "gpt-5.4", effort: "medium" },
- plan: { model: "gpt-5.4", effort: "high" },
- code: { model: "gpt-5.3-codex", effort: "high" },
- review: { model: "gpt-5.3-codex", effort: "xhigh" },
+ ask: { model: CODEX_RECOMMENDED_MODEL, effort: "low" },
+ plan: { model: CODEX_RECOMMENDED_MODEL, effort: "medium" },
+ code: { model: CODEX_RECOMMENDED_MODEL, effort: "medium" },
+ review: { model: CODEX_RECOMMENDED_MODEL, effort: "high" },
}),
],
claudeAgent: [
@@ -360,7 +362,11 @@ function seedBuiltInPresets(settings: ServerSettings): ServerSettings {
for (const provider of PROVIDER_ORDER) {
for (const preset of BUILT_IN_MODEL_SELECTION_PRESETS[provider]) {
- if (nextPresets[provider][preset.id]) {
+ if (provider !== "codex" && nextPresets[provider][preset.id]) {
+ continue;
+ }
+
+ if (Equal.equals(nextPresets[provider][preset.id], preset)) {
continue;
}
diff --git a/apps/web/src/components/chat/ModelPickerContent.tsx b/apps/web/src/components/chat/ModelPickerContent.tsx
index 991f1c95..0cf715d3 100644
--- a/apps/web/src/components/chat/ModelPickerContent.tsx
+++ b/apps/web/src/components/chat/ModelPickerContent.tsx
@@ -39,6 +39,7 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
providers?: ReadonlyArray;
keybindings?: ResolvedKeybindingsConfig;
modelOptionsByProvider: Record>;
+ allowAutoModel?: boolean;
showAsAuto?: boolean;
terminalOpen?: boolean;
onRequestClose?: () => void;
@@ -127,13 +128,18 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
return [];
}
- return [autoModelByProvider[providerKind as ProviderKind], ...models].map((model) =>
+ const providerModels =
+ props.allowAutoModel === false
+ ? models
+ : [autoModelByProvider[providerKind as ProviderKind], ...models];
+
+ return providerModels.map((model) =>
Object.assign({}, model, {
provider: providerKind as ProviderKind,
}),
) satisfies Array;
});
- }, [autoModelByProvider, props.modelOptionsByProvider, readyProviderSet]);
+ }, [autoModelByProvider, props.allowAutoModel, props.modelOptionsByProvider, readyProviderSet]);
const filteredModels = useMemo(() => {
let result = flatModels;
@@ -242,6 +248,9 @@ export const ModelPickerContent = memo(function ModelPickerContent(props: {
const handleModelSelect = useCallback(
(modelSlug: string, provider: ProviderKind) => {
if (modelSlug === COMPOSER_AUTO_MODEL_VALUE) {
+ if (props.allowAutoModel === false) {
+ return;
+ }
props.onProviderModelChange(provider, COMPOSER_AUTO_MODEL_VALUE);
return;
}
diff --git a/apps/web/src/components/chat/ProviderModelPicker.browser.tsx b/apps/web/src/components/chat/ProviderModelPicker.browser.tsx
index 280e5322..0b0c531f 100644
--- a/apps/web/src/components/chat/ProviderModelPicker.browser.tsx
+++ b/apps/web/src/components/chat/ProviderModelPicker.browser.tsx
@@ -121,6 +121,7 @@ async function mountPicker(props: {
providers?: ReadonlyArray;
keybindings?: ResolvedKeybindingsConfig;
triggerVariant?: "ghost" | "outline";
+ allowAutoModel?: boolean;
showAsAuto?: boolean;
}) {
const host = document.createElement("div");
@@ -141,6 +142,7 @@ async function mountPicker(props: {
providers={providers}
modelOptionsByProvider={modelOptionsByProvider}
{...(props.keybindings ? { keybindings: props.keybindings } : {})}
+ {...(props.allowAutoModel !== undefined ? { allowAutoModel: props.allowAutoModel } : {})}
{...(props.showAsAuto !== undefined ? { showAsAuto: props.showAsAuto } : {})}
triggerVariant={props.triggerVariant}
onProviderModelChange={onProviderModelChange}
@@ -287,6 +289,27 @@ describe("ProviderModelPicker", () => {
}
});
+ it("hides Auto when disabled for persisted settings pickers", async () => {
+ const mounted = await mountPicker({
+ provider: "claudeAgent",
+ model: "claude-opus-4-6",
+ lockedProvider: "claudeAgent",
+ allowAutoModel: false,
+ });
+
+ try {
+ await page.getByRole("button").click();
+ await vi.waitFor(() => {
+ expect(document.querySelector(".model-picker-list")).not.toBeNull();
+ });
+
+ expect(getVisibleModelNames().some((name) => name.includes("Auto"))).toBe(false);
+ expect(getVisibleModelNames().some((name) => name.includes("Claude Opus 4.6"))).toBe(true);
+ } finally {
+ await mounted.cleanup();
+ }
+ });
+
it("shows jump shortcut labels when keybindings are provided", async () => {
const mounted = await mountPicker({
provider: "codex",
diff --git a/apps/web/src/components/chat/ProviderModelPicker.tsx b/apps/web/src/components/chat/ProviderModelPicker.tsx
index 74db8970..22586d74 100644
--- a/apps/web/src/components/chat/ProviderModelPicker.tsx
+++ b/apps/web/src/components/chat/ProviderModelPicker.tsx
@@ -31,6 +31,7 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
keybindings?: ResolvedKeybindingsConfig;
modelOptionsByProvider: Record>;
activeProviderIconClassName?: string;
+ allowAutoModel?: boolean;
compact?: boolean;
disabled?: boolean;
showAsAuto?: boolean;
@@ -159,6 +160,7 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
{...(props.providers ? { providers: props.providers } : {})}
{...(props.keybindings ? { keybindings: props.keybindings } : {})}
modelOptionsByProvider={props.modelOptionsByProvider}
+ {...(props.allowAutoModel !== undefined ? { allowAutoModel: props.allowAutoModel } : {})}
{...(props.showAsAuto !== undefined ? { showAsAuto: props.showAsAuto } : {})}
terminalOpen={props.terminalOpen ?? false}
onRequestClose={() => setIsMenuOpen(false)}
diff --git a/apps/web/src/components/settings/SettingsModelsSection.test.tsx b/apps/web/src/components/settings/SettingsModelsSection.test.tsx
index 231d6abb..1ae1946d 100644
--- a/apps/web/src/components/settings/SettingsModelsSection.test.tsx
+++ b/apps/web/src/components/settings/SettingsModelsSection.test.tsx
@@ -25,6 +25,12 @@ vi.mock("../../rpc/serverState", () => ({
auth: { status: "authenticated" },
checkedAt: "2026-01-01T00:00:00.000Z",
models: [
+ {
+ slug: "gpt-5.5",
+ name: "GPT-5.5",
+ isCustom: false,
+ capabilities: null,
+ },
{
slug: "gpt-5.4",
name: "GPT-5.4",
diff --git a/apps/web/src/components/settings/SettingsModelsSection.tsx b/apps/web/src/components/settings/SettingsModelsSection.tsx
index 5716dbce..f0349b90 100644
--- a/apps/web/src/components/settings/SettingsModelsSection.tsx
+++ b/apps/web/src/components/settings/SettingsModelsSection.tsx
@@ -109,12 +109,12 @@ const BUILT_IN_PRESET_MODE_SELECTIONS: Readonly<
codeModelSelection: {
provider: "codex",
model: "gpt-5.4-mini",
- options: { reasoningEffort: "low" },
+ options: { reasoningEffort: "medium" },
},
reviewModelSelection: {
provider: "codex",
- model: "gpt-5.4",
- options: { reasoningEffort: "low" },
+ model: "gpt-5.4-mini",
+ options: { reasoningEffort: "high" },
},
},
"starter-codex-go": {
@@ -126,7 +126,7 @@ const BUILT_IN_PRESET_MODE_SELECTIONS: Readonly<
planModelSelection: {
provider: "codex",
model: "gpt-5.4-mini",
- options: { reasoningEffort: "medium" },
+ options: { reasoningEffort: "high" },
},
codeModelSelection: {
provider: "codex",
@@ -135,8 +135,8 @@ const BUILT_IN_PRESET_MODE_SELECTIONS: Readonly<
},
reviewModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
- options: { reasoningEffort: "medium" },
+ model: "gpt-5.4-mini",
+ options: { reasoningEffort: "high" },
},
},
"starter-codex-plus": {
@@ -147,7 +147,7 @@ const BUILT_IN_PRESET_MODE_SELECTIONS: Readonly<
},
planModelSelection: {
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: { reasoningEffort: "medium" },
},
codeModelSelection: {
@@ -157,52 +157,52 @@ const BUILT_IN_PRESET_MODE_SELECTIONS: Readonly<
},
reviewModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
- options: { reasoningEffort: "medium" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "low" },
},
},
"starter-codex-pro-5x": {
askModelSelection: {
provider: "codex",
- model: "gpt-5.4",
+ model: "gpt-5.5",
options: { reasoningEffort: "low" },
},
planModelSelection: {
provider: "codex",
- model: "gpt-5.4",
- options: { reasoningEffort: "high" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "medium" },
},
codeModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
- options: { reasoningEffort: "high" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "medium" },
},
reviewModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
+ model: "gpt-5.5",
options: { reasoningEffort: "high" },
},
},
"starter-codex-pro-20x": {
askModelSelection: {
provider: "codex",
- model: "gpt-5.4",
- options: { reasoningEffort: "medium" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "low" },
},
planModelSelection: {
provider: "codex",
- model: "gpt-5.4",
- options: { reasoningEffort: "high" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "medium" },
},
codeModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
- options: { reasoningEffort: "high" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "medium" },
},
reviewModelSelection: {
provider: "codex",
- model: "gpt-5.3-codex",
- options: { reasoningEffort: "xhigh" },
+ model: "gpt-5.5",
+ options: { reasoningEffort: "high" },
},
},
},
diff --git a/apps/web/src/components/settings/SettingsPanelPrimitives.tsx b/apps/web/src/components/settings/SettingsPanelPrimitives.tsx
index 8f448695..51132a35 100644
--- a/apps/web/src/components/settings/SettingsPanelPrimitives.tsx
+++ b/apps/web/src/components/settings/SettingsPanelPrimitives.tsx
@@ -111,6 +111,7 @@ export function ModelSelectionControl({
lockedProvider={lockedProvider ?? null}
providers={providers}
modelOptionsByProvider={modelOptionsByProvider}
+ allowAutoModel={false}
triggerVariant="outline"
triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"
onProviderModelChange={onProviderModelChange}
diff --git a/packages/contracts/src/model.ts b/packages/contracts/src/model.ts
index e62a957e..43a9377e 100644
--- a/packages/contracts/src/model.ts
+++ b/packages/contracts/src/model.ts
@@ -52,7 +52,7 @@ export const ModelCapabilities = Schema.Struct({
export type ModelCapabilities = typeof ModelCapabilities.Type;
export const DEFAULT_MODEL_BY_PROVIDER: Record = {
- codex: "gpt-5.4",
+ codex: "gpt-5.5",
claudeAgent: "claude-sonnet-4-6",
};
@@ -66,6 +66,7 @@ export const DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER: Record> = {
codex: {
+ "5.5": "gpt-5.5",
"5.4": "gpt-5.4",
"5.3": "gpt-5.3-codex",
"gpt-5.3": "gpt-5.3-codex",
diff --git a/packages/shared/src/model.test.ts b/packages/shared/src/model.test.ts
index acf1cc3f..170e4d51 100644
--- a/packages/shared/src/model.test.ts
+++ b/packages/shared/src/model.test.ts
@@ -92,6 +92,7 @@ const providersWithoutSpark: ReadonlyArray = [
describe("normalizeModelSlug", () => {
it("maps known aliases to canonical slugs", () => {
+ expect(normalizeModelSlug("5.5")).toBe("gpt-5.5");
expect(normalizeModelSlug("5.3")).toBe("gpt-5.3-codex");
expect(normalizeModelSlug("sonnet", "claudeAgent")).toBe("claude-sonnet-4-6");
});
@@ -345,7 +346,7 @@ describe("resolveApiModelId", () => {
});
it("returns the model as-is for Codex selections", () => {
- expect(resolveApiModelId({ provider: "codex", model: "gpt-5.4" })).toBe("gpt-5.4");
+ expect(resolveApiModelId({ provider: "codex", model: "gpt-5.5" })).toBe("gpt-5.5");
});
it("resolves codex auto for API model ids", () => {
diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts
index ddac9250..8c29f8dd 100644
--- a/scripts/build-desktop-artifact.ts
+++ b/scripts/build-desktop-artifact.ts
@@ -469,7 +469,7 @@ const createBuildConfig = Effect.fn("createBuildConfig")(function* (
mockUpdateServerPort: string | undefined,
) {
const buildConfig: Record = {
- appId: "com.kodo.code",
+ appId: "app.kodocode",
productName,
artifactName: "Kodo-Code-${version}-${arch}.${ext}",
asarUnpack: ["apps/server/dist/**", "node_modules/**"],
From 77ba0b6559ee0e5bdee3d4c9730ec6acb349a459 Mon Sep 17 00:00:00 2001
From: boggedbrush <90526147+boggedbrush@users.noreply.github.com>
Date: Wed, 29 Apr 2026 12:30:27 -0400
Subject: [PATCH 2/3] fix(codex): use utility default for text generation auto
---
.../src/git/Layers/CodexTextGeneration.test.ts | 2 +-
.../src/git/Layers/CodexTextGeneration.ts | 17 +++++++++++++----
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/apps/server/src/git/Layers/CodexTextGeneration.test.ts b/apps/server/src/git/Layers/CodexTextGeneration.test.ts
index 2c1f05e4..11fc1211 100644
--- a/apps/server/src/git/Layers/CodexTextGeneration.test.ts
+++ b/apps/server/src/git/Layers/CodexTextGeneration.test.ts
@@ -284,7 +284,7 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGenerationLive", (it) => {
subject: "Add important change",
body: "",
}),
- requireModel: "gpt-5.4",
+ requireModel: "gpt-5.4-mini",
},
Effect.gen(function* () {
const textGeneration = yield* TextGeneration;
diff --git a/apps/server/src/git/Layers/CodexTextGeneration.ts b/apps/server/src/git/Layers/CodexTextGeneration.ts
index 64099ade..0d6ad25b 100644
--- a/apps/server/src/git/Layers/CodexTextGeneration.ts
+++ b/apps/server/src/git/Layers/CodexTextGeneration.ts
@@ -3,7 +3,10 @@ import { randomUUID } from "node:crypto";
import { Effect, FileSystem, Layer, Option, Path, Schema, Scope, Stream } from "effect";
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
-import { CodexModelSelection } from "@t3tools/contracts";
+import {
+ CodexModelSelection,
+ DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER,
+} from "@t3tools/contracts";
import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git";
import { resolveAttachmentPath } from "../../attachmentStore.ts";
@@ -163,9 +166,15 @@ const makeCodexTextGeneration = Effect.gen(function* () {
const runCodexCommand = Effect.fn("runCodexJson.runCodexCommand")(function* () {
// Resolve frontend sentinel models like `auto` before invoking Codex CLI.
- const resolvedModelSelection = resolveModelSelectionDefault(
- modelSelection,
- ) as CodexModelSelection;
+ // Utility text generation should use the cheaper utility default, not the
+ // interactive composer default.
+ const resolvedModelSelection =
+ modelSelection.model.trim().toLowerCase() === "auto"
+ ? ({
+ ...modelSelection,
+ model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex,
+ } satisfies CodexModelSelection)
+ : (resolveModelSelectionDefault(modelSelection) as CodexModelSelection);
const normalizedOptions = normalizeCodexModelOptionsWithCapabilities(
getCodexModelCapabilities(resolvedModelSelection.model),
resolvedModelSelection.options,
From 4fa0a74447671228c87fa051c034a0d5078c810b Mon Sep 17 00:00:00 2001
From: boggedbrush <90526147+boggedbrush@users.noreply.github.com>
Date: Wed, 29 Apr 2026 12:40:41 -0400
Subject: [PATCH 3/3] fix(codex): update traits browser fixture for gpt-5.5
---
.../src/components/chat/TraitsPicker.browser.tsx | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/apps/web/src/components/chat/TraitsPicker.browser.tsx b/apps/web/src/components/chat/TraitsPicker.browser.tsx
index 67bda031..ac5970e6 100644
--- a/apps/web/src/components/chat/TraitsPicker.browser.tsx
+++ b/apps/web/src/components/chat/TraitsPicker.browser.tsx
@@ -38,6 +38,21 @@ const TEST_PROVIDERS: ReadonlyArray = [
auth: { status: "authenticated" },
checkedAt: "2026-01-01T00:00:00.000Z",
models: [
+ {
+ slug: "gpt-5.5",
+ name: "GPT-5.5",
+ isCustom: false,
+ capabilities: {
+ reasoningEffortLevels: [
+ { value: "xhigh", label: "Extra High" },
+ { value: "high", label: "High", isDefault: true },
+ ],
+ supportsFastMode: true,
+ supportsThinkingToggle: false,
+ contextWindowOptions: [],
+ promptInjectedEffortLevels: [],
+ },
+ },
{
slug: "gpt-5.4",
name: "GPT-5.4",