From 77226d27e3e5794b1042d03887d2918f19c046ef Mon Sep 17 00:00:00 2001 From: Mindrocket42 <161140830+Mindrocket42@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:42:28 +0200 Subject: [PATCH 1/4] Add GPT-5.4 and GPT-5.5 model support Adds general-purpose, Codex, and (new) mini/nano presets for GPT-5.4 and GPT-5.5 across MODEL_MAP and both config templates. Generalizes getReasoningConfig to derive xhigh/none support from the GPT-5.x point release number instead of a hardcoded flag per version, so future 5.x releases following the same reasoning schema won't need a code change here - only new MODEL_MAP/config entries. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 12 ++ README.md | 8 +- config/opencode-legacy.json | 336 +++++++++++++++++++++++++++++ config/opencode-modern.json | 248 +++++++++++++++++++++ lib/oauth-success.html | 2 +- lib/request/helpers/model-map.ts | 60 +++++- lib/request/request-transformer.ts | 81 ++++--- package.json | 2 +- test/request-transformer.test.ts | 94 ++++++++ 9 files changed, 810 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec29e07..2757ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project are documented here. Dates use the ISO format (YYYY-MM-DD). +## [4.5.0] - 2026-07-06 + +**Model release**: GPT-5.4 and GPT-5.5 support. + +### Added +- **GPT-5.5 / GPT-5.5 Codex**: 1.05M context, 128k output; same none/low/medium/high/xhigh (general) and low/medium/high/xhigh (codex) schema as GPT-5.2. +- **GPT-5.4 / GPT-5.4 Codex**: 1.05M context, 128k output; same reasoning schema as GPT-5.5. +- **GPT-5.4 Mini / GPT-5.4 Nano**: 400k context, 128k output; first-class lightweight general-purpose models (default to `low` reasoning, can opt into `xhigh`). + +### Changed +- **Reasoning capability logic**: `getReasoningConfig` now derives `xhigh`/`none` support from the GPT-5.x point-release number instead of a hardcoded flag per version, so future 5.x point releases that follow the same schema (per OpenAI docs) won't require a code change here — only new `MODEL_MAP`/config entries. + ## [4.4.0] - 2026-01-09 **Maintenance release**: OAuth success page version sync. diff --git a/README.md b/README.md index 6708114..69bb3f6 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ npx -y opencode-openai-codex-auth@latest --uninstall --all ``` --- ## 📦 Models +- **gpt-5.5** (none/low/medium/high/xhigh) +- **gpt-5.5-codex** (low/medium/high/xhigh) +- **gpt-5.4** (none/low/medium/high/xhigh) +- **gpt-5.4-codex** (low/medium/high/xhigh) +- **gpt-5.4-mini** (none/low/medium/high/xhigh) +- **gpt-5.4-nano** (none/low/medium/high/xhigh) - **gpt-5.2** (none/low/medium/high/xhigh) - **gpt-5.2-codex** (low/medium/high/xhigh) - **gpt-5.1-codex-max** (low/medium/high/xhigh) @@ -58,7 +64,7 @@ Minimal configs are not supported for GPT‑5.x; use the full configs above. --- ## ✅ Features - ChatGPT Plus/Pro OAuth authentication (official flow) -- 22 model presets across GPT‑5.2 / GPT‑5.2 Codex / GPT‑5.1 families +- 50 model presets across GPT‑5.5 / GPT‑5.4 / GPT‑5.2 / GPT‑5.1 families - Variant system support (v1.0.210+) + legacy presets - Multimodal input enabled for all models - Usage‑aware errors + automatic token refresh diff --git a/config/opencode-legacy.json b/config/opencode-legacy.json index 5381226..991e7c6 100644 --- a/config/opencode-legacy.json +++ b/config/opencode-legacy.json @@ -15,6 +15,342 @@ "store": false }, "models": { + "gpt-5.5-none": { + "name": "GPT 5.5 None (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-low": { + "name": "GPT 5.5 Low (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-medium": { + "name": "GPT 5.5 Medium (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-high": { + "name": "GPT 5.5 High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-xhigh": { + "name": "GPT 5.5 Extra High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-codex-low": { + "name": "GPT 5.5 Codex Low (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-codex-medium": { + "name": "GPT 5.5 Codex Medium (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-codex-high": { + "name": "GPT 5.5 Codex High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.5-codex-xhigh": { + "name": "GPT 5.5 Codex Extra High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-none": { + "name": "GPT 5.4 None (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-low": { + "name": "GPT 5.4 Low (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-medium": { + "name": "GPT 5.4 Medium (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-high": { + "name": "GPT 5.4 High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-xhigh": { + "name": "GPT 5.4 Extra High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-codex-low": { + "name": "GPT 5.4 Codex Low (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-codex-medium": { + "name": "GPT 5.4 Codex Medium (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-codex-high": { + "name": "GPT 5.4 Codex High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-codex-xhigh": { + "name": "GPT 5.4 Codex Extra High (OAuth)", + "limit": { "context": 1050000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-mini-none": { + "name": "GPT 5.4 Mini None (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-mini-low": { + "name": "GPT 5.4 Mini Low (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-mini-medium": { + "name": "GPT 5.4 Mini Medium (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-mini-high": { + "name": "GPT 5.4 Mini High (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-mini-xhigh": { + "name": "GPT 5.4 Mini Extra High (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-nano-none": { + "name": "GPT 5.4 Nano None (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-nano-low": { + "name": "GPT 5.4 Nano Low (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-nano-medium": { + "name": "GPT 5.4 Nano Medium (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-nano-high": { + "name": "GPT 5.4 Nano High (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, + "gpt-5.4-nano-xhigh": { + "name": "GPT 5.4 Nano Extra High (OAuth)", + "limit": { "context": 400000, "output": 128000 }, + "modalities": { "input": ["text", "image"], "output": ["text"] }, + "options": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium", + "include": ["reasoning.encrypted_content"], + "store": false + } + }, "gpt-5.2-none": { "name": "GPT 5.2 None (OAuth)", "limit": { diff --git a/config/opencode-modern.json b/config/opencode-modern.json index 9161024..191df38 100644 --- a/config/opencode-modern.json +++ b/config/opencode-modern.json @@ -15,6 +15,254 @@ "store": false }, "models": { + "gpt-5.5": { + "name": "GPT 5.5 (OAuth)", + "limit": { + "context": 1050000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.5-codex": { + "name": "GPT 5.5 Codex (OAuth)", + "limit": { + "context": 1050000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.4": { + "name": "GPT 5.4 (OAuth)", + "limit": { + "context": 1050000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.4-codex": { + "name": "GPT 5.4 Codex (OAuth)", + "limit": { + "context": 1050000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.4-mini": { + "name": "GPT 5.4 Mini (OAuth)", + "limit": { + "context": 400000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.4-nano": { + "name": "GPT 5.4 Nano (OAuth)", + "limit": { + "context": 400000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, "gpt-5.2": { "name": "GPT 5.2 (OAuth)", "limit": { diff --git a/lib/oauth-success.html b/lib/oauth-success.html index 9ab0252..d11c9bd 100644 --- a/lib/oauth-success.html +++ b/lib/oauth-success.html @@ -548,7 +548,7 @@
-
opencode-openai-codex-auth@4.4.0 — OAuth Authentication
+
opencode-openai-codex-auth@4.5.0 — OAuth Authentication
diff --git a/lib/request/helpers/model-map.ts b/lib/request/helpers/model-map.ts index 20ca329..156ea15 100644 --- a/lib/request/helpers/model-map.ts +++ b/lib/request/helpers/model-map.ts @@ -13,8 +13,66 @@ */ export const MODEL_MAP: Record = { // ============================================================================ -// GPT-5.1 Codex Models +// GPT-5.5 Models (supports none/low/medium/high/xhigh, schema carried over from 5.2) // ============================================================================ + "gpt-5.5": "gpt-5.5", + "gpt-5.5-none": "gpt-5.5", + "gpt-5.5-low": "gpt-5.5", + "gpt-5.5-medium": "gpt-5.5", + "gpt-5.5-high": "gpt-5.5", + "gpt-5.5-xhigh": "gpt-5.5", + + // ============================================================================ + // GPT-5.5 Codex Models (low/medium/high/xhigh) + // ============================================================================ + "gpt-5.5-codex": "gpt-5.5-codex", + "gpt-5.5-codex-low": "gpt-5.5-codex", + "gpt-5.5-codex-medium": "gpt-5.5-codex", + "gpt-5.5-codex-high": "gpt-5.5-codex", + "gpt-5.5-codex-xhigh": "gpt-5.5-codex", + + // ============================================================================ + // GPT-5.4 Models (supports none/low/medium/high/xhigh, schema carried over from 5.2) + // ============================================================================ + "gpt-5.4": "gpt-5.4", + "gpt-5.4-none": "gpt-5.4", + "gpt-5.4-low": "gpt-5.4", + "gpt-5.4-medium": "gpt-5.4", + "gpt-5.4-high": "gpt-5.4", + "gpt-5.4-xhigh": "gpt-5.4", + + // ============================================================================ + // GPT-5.4 Codex Models (low/medium/high/xhigh) + // ============================================================================ + "gpt-5.4-codex": "gpt-5.4-codex", + "gpt-5.4-codex-low": "gpt-5.4-codex", + "gpt-5.4-codex-medium": "gpt-5.4-codex", + "gpt-5.4-codex-high": "gpt-5.4-codex", + "gpt-5.4-codex-xhigh": "gpt-5.4-codex", + + // ============================================================================ + // GPT-5.4 Mini Models (400k context; same reasoning schema as gpt-5.4) + // ============================================================================ + "gpt-5.4-mini": "gpt-5.4-mini", + "gpt-5.4-mini-none": "gpt-5.4-mini", + "gpt-5.4-mini-low": "gpt-5.4-mini", + "gpt-5.4-mini-medium": "gpt-5.4-mini", + "gpt-5.4-mini-high": "gpt-5.4-mini", + "gpt-5.4-mini-xhigh": "gpt-5.4-mini", + + // ============================================================================ + // GPT-5.4 Nano Models (400k context; same reasoning schema as gpt-5.4) + // ============================================================================ + "gpt-5.4-nano": "gpt-5.4-nano", + "gpt-5.4-nano-none": "gpt-5.4-nano", + "gpt-5.4-nano-low": "gpt-5.4-nano", + "gpt-5.4-nano-medium": "gpt-5.4-nano", + "gpt-5.4-nano-high": "gpt-5.4-nano", + "gpt-5.4-nano-xhigh": "gpt-5.4-nano", + + // ============================================================================ + // GPT-5.1 Codex Models + // ============================================================================ "gpt-5.1-codex": "gpt-5.1-codex", "gpt-5.1-codex-low": "gpt-5.1-codex", "gpt-5.1-codex-medium": "gpt-5.1-codex", diff --git a/lib/request/request-transformer.ts b/lib/request/request-transformer.ts index 07f9961..5f4c722 100644 --- a/lib/request/request-transformer.ts +++ b/lib/request/request-transformer.ts @@ -47,6 +47,36 @@ export function normalizeModel(model: string | undefined): string { const normalized = modelId.toLowerCase(); // Priority order for pattern matching (most specific first): + // 0a. GPT-5.5 Codex / GPT-5.4 Codex (newest codex models) + if ( + normalized.includes("gpt-5.5-codex") || + normalized.includes("gpt 5.5 codex") + ) { + return "gpt-5.5-codex"; + } + if ( + normalized.includes("gpt-5.4-codex") || + normalized.includes("gpt 5.4 codex") + ) { + return "gpt-5.4-codex"; + } + + // 0b. GPT-5.4 Mini / Nano (distinct context limits from base gpt-5.4) + if (normalized.includes("gpt-5.4-mini") || normalized.includes("gpt 5.4 mini")) { + return "gpt-5.4-mini"; + } + if (normalized.includes("gpt-5.4-nano") || normalized.includes("gpt 5.4 nano")) { + return "gpt-5.4-nano"; + } + + // 0c. GPT-5.5 / GPT-5.4 (general purpose) + if (normalized.includes("gpt-5.5") || normalized.includes("gpt 5.5")) { + return "gpt-5.5"; + } + if (normalized.includes("gpt-5.4") || normalized.includes("gpt 5.4")) { + return "gpt-5.4"; + } + // 1. GPT-5.2 Codex (newest codex model) if ( normalized.includes("gpt-5.2-codex") || @@ -195,15 +225,6 @@ export function getReasoningConfig( ): ReasoningConfig { const normalizedName = modelName?.toLowerCase() ?? ""; - // GPT-5.2 Codex is the newest codex model (supports xhigh, but not "none") - const isGpt52Codex = - normalizedName.includes("gpt-5.2-codex") || - normalizedName.includes("gpt 5.2 codex"); - - // GPT-5.2 general purpose (not codex variant) - const isGpt52General = - (normalizedName.includes("gpt-5.2") || normalizedName.includes("gpt 5.2")) && - !isGpt52Codex; const isCodexMax = normalizedName.includes("codex-max") || normalizedName.includes("codex max"); @@ -212,39 +233,41 @@ export function getReasoningConfig( normalizedName.includes("codex mini") || normalizedName.includes("codex_mini") || normalizedName.includes("codex-mini-latest"); - const isCodex = normalizedName.includes("codex") && !isCodexMini; + const isCodex = + normalizedName.includes("codex") && !isCodexMini && !isCodexMax; + const isGeneralPurpose = !isCodex && !isCodexMax && !isCodexMini; const isLightweight = !isCodexMini && (normalizedName.includes("nano") || normalizedName.includes("mini")); - // GPT-5.1 general purpose (not codex variants) - supports "none" per OpenAI API docs - const isGpt51General = - (normalizedName.includes("gpt-5.1") || normalizedName.includes("gpt 5.1")) && - !isCodex && - !isCodexMax && - !isCodexMini; + // GPT-5.x point release (e.g. "gpt-5.2" -> 2, "gpt-5.4" -> 4). Bare "gpt-5" + // (no minor version) is always remapped to "gpt-5.1" by MODEL_MAP before + // reaching this function, so it's treated like 5.1 here as a safe default. + const versionMatch = normalizedName.match(/gpt[ -]?5\.(\d+)/); + const minorVersion = versionMatch ? parseInt(versionMatch[1], 10) : 1; - // GPT 5.2, GPT 5.2 Codex, and Codex Max support xhigh reasoning - const supportsXhigh = isGpt52General || isGpt52Codex || isCodexMax; + // xhigh reasoning was introduced with the 5.2 generation (GPT-5.2, GPT-5.2 + // Codex) and the same schema has carried forward to every later point + // release (5.4, 5.5, ...); Codex Max supports it regardless of version. + const supportsXhigh = + isCodexMax || (minorVersion >= 2 && (isCodex || isGeneralPurpose)); - // GPT 5.1 general and GPT 5.2 general support "none" reasoning per: - // - OpenAI API docs: "gpt-5.1 defaults to none, supports: none, low, medium, high" - // - Codex CLI: ReasoningEffort enum includes None variant (codex-rs/protocol/src/openai_models.rs) - // - Codex CLI: docs/config.md lists "none" as valid for model_reasoning_effort - // - gpt-5.2 (being newer) also supports: none, low, medium, high, xhigh - // - Codex models (including GPT-5.2 Codex) do NOT support "none" - const supportsNone = isGpt52General || isGpt51General; + // "none" reasoning is only offered on general-purpose models per OpenAI API + // docs (gpt-5.1 and gpt-5.2 both support it, and later point releases + // carry the same schema forward). Codex-tuned models (including Codex + // Max/Mini) never support it. + const supportsNone = isGeneralPurpose; // Default based on model type (Codex CLI defaults) // Note: OpenAI docs say gpt-5.1 defaults to "none", but we default to "medium" // for better coding assistance unless user explicitly requests "none" const defaultEffort: ReasoningConfig["effort"] = isCodexMini ? "medium" - : supportsXhigh - ? "high" - : isLightweight - ? "minimal" + : isLightweight + ? "minimal" + : supportsXhigh + ? "high" : "medium"; // Get user-requested effort diff --git a/package.json b/package.json index caf949c..703363d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opencode-openai-codex-auth", - "version": "4.4.0", + "version": "4.5.0", "description": "OpenAI ChatGPT (Codex backend) OAuth auth plugin for opencode - use your ChatGPT Plus/Pro subscription instead of API credits", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/test/request-transformer.test.ts b/test/request-transformer.test.ts index b3c0404..4931d08 100644 --- a/test/request-transformer.test.ts +++ b/test/request-transformer.test.ts @@ -104,6 +104,42 @@ describe('Request Transformer Module', () => { expect(normalizeModel('openai/gpt-5.1')).toBe('gpt-5.1'); expect(normalizeModel('GPT 5.1 High')).toBe('gpt-5.1'); }); + + it('should normalize gpt-5.4 presets', async () => { + expect(normalizeModel('gpt-5.4')).toBe('gpt-5.4'); + expect(normalizeModel('gpt-5.4-none')).toBe('gpt-5.4'); + expect(normalizeModel('gpt-5.4-high')).toBe('gpt-5.4'); + expect(normalizeModel('gpt-5.4-xhigh')).toBe('gpt-5.4'); + expect(normalizeModel('openai/gpt-5.4-medium')).toBe('gpt-5.4'); + }); + + it('should normalize gpt-5.4-codex presets', async () => { + expect(normalizeModel('gpt-5.4-codex')).toBe('gpt-5.4-codex'); + expect(normalizeModel('gpt-5.4-codex-low')).toBe('gpt-5.4-codex'); + expect(normalizeModel('gpt-5.4-codex-xhigh')).toBe('gpt-5.4-codex'); + expect(normalizeModel('openai/gpt-5.4-codex-high')).toBe('gpt-5.4-codex'); + }); + + it('should normalize gpt-5.4-mini and gpt-5.4-nano presets distinctly from gpt-5.4', async () => { + expect(normalizeModel('gpt-5.4-mini')).toBe('gpt-5.4-mini'); + expect(normalizeModel('gpt-5.4-mini-high')).toBe('gpt-5.4-mini'); + expect(normalizeModel('gpt-5.4-nano')).toBe('gpt-5.4-nano'); + expect(normalizeModel('gpt-5.4-nano-low')).toBe('gpt-5.4-nano'); + }); + + it('should normalize gpt-5.5 and gpt-5.5-codex presets', async () => { + expect(normalizeModel('gpt-5.5')).toBe('gpt-5.5'); + expect(normalizeModel('gpt-5.5-xhigh')).toBe('gpt-5.5'); + expect(normalizeModel('gpt-5.5-codex')).toBe('gpt-5.5-codex'); + expect(normalizeModel('gpt-5.5-codex-high')).toBe('gpt-5.5-codex'); + expect(normalizeModel('openai/gpt-5.5-codex-xhigh')).toBe('gpt-5.5-codex'); + }); + + it('should fall back correctly for unmapped gpt-5.4/5.5 variants', async () => { + // Not in MODEL_MAP, but should still pattern-match to the right family + expect(normalizeModel('gpt-5.4-super-high')).toBe('gpt-5.4'); + expect(normalizeModel('gpt-5.5-codex-super-high')).toBe('gpt-5.5-codex'); + }); }); // Edge case tests - legacy gpt-5 models now map to gpt-5.1 @@ -1070,6 +1106,64 @@ describe('Request Transformer Module', () => { expect(result.reasoning?.effort).toBe('low'); }); + describe('GPT-5.4 / GPT-5.5 (schema carried forward from GPT-5.2)', () => { + it('should default gpt-5.4 and gpt-5.5 general purpose to high effort (supports xhigh)', async () => { + const gpt54 = await transformRequestBody({ model: 'gpt-5.4', input: [] }, codexInstructions); + expect(gpt54.model).toBe('gpt-5.4'); + expect(gpt54.reasoning?.effort).toBe('high'); + + const gpt55 = await transformRequestBody({ model: 'gpt-5.5', input: [] }, codexInstructions); + expect(gpt55.model).toBe('gpt-5.5'); + expect(gpt55.reasoning?.effort).toBe('high'); + }); + + it('should default gpt-5.4-codex and gpt-5.5-codex to high effort (supports xhigh)', async () => { + const result = await transformRequestBody({ model: 'gpt-5.4-codex', input: [] }, codexInstructions); + expect(result.model).toBe('gpt-5.4-codex'); + expect(result.reasoning?.effort).toBe('high'); + + const result2 = await transformRequestBody({ model: 'gpt-5.5-codex', input: [] }, codexInstructions); + expect(result2.model).toBe('gpt-5.5-codex'); + expect(result2.reasoning?.effort).toBe('high'); + }); + + it('should preserve xhigh for gpt-5.4/5.5 general purpose and codex', async () => { + const userConfig: UserConfig = { global: { reasoningEffort: 'xhigh' }, models: {} }; + + const general = await transformRequestBody({ model: 'gpt-5.4-xhigh', input: [] }, codexInstructions, userConfig); + expect(general.reasoning?.effort).toBe('xhigh'); + + const codex = await transformRequestBody({ model: 'gpt-5.5-codex-xhigh', input: [] }, codexInstructions, userConfig); + expect(codex.reasoning?.effort).toBe('xhigh'); + }); + + it('should preserve none for gpt-5.4/5.5 general purpose but upgrade to low for codex', async () => { + const userConfig: UserConfig = { global: { reasoningEffort: 'none' }, models: {} }; + + const general = await transformRequestBody({ model: 'gpt-5.5-none', input: [] }, codexInstructions, userConfig); + expect(general.reasoning?.effort).toBe('none'); + + const codex = await transformRequestBody({ model: 'gpt-5.4-codex', input: [] }, codexInstructions, userConfig); + expect(codex.reasoning?.effort).toBe('low'); + }); + + it('should default gpt-5.4-mini/nano to a low effort despite supporting xhigh', async () => { + const mini = await transformRequestBody({ model: 'gpt-5.4-mini', input: [] }, codexInstructions); + expect(mini.model).toBe('gpt-5.4-mini'); + expect(mini.reasoning?.effort).toBe('low'); + + const nano = await transformRequestBody({ model: 'gpt-5.4-nano', input: [] }, codexInstructions); + expect(nano.model).toBe('gpt-5.4-nano'); + expect(nano.reasoning?.effort).toBe('low'); + }); + + it('should still allow explicit xhigh/none overrides for gpt-5.4-mini/nano', async () => { + const userConfig: UserConfig = { global: { reasoningEffort: 'xhigh' }, models: {} }; + const mini = await transformRequestBody({ model: 'gpt-5.4-mini', input: [] }, codexInstructions, userConfig); + expect(mini.reasoning?.effort).toBe('xhigh'); + }); + }); + it('should use minimal effort for lightweight models', async () => { const body: RequestBody = { model: 'gpt-5-nano', From 89cb57bd5b2cfe04d02247f58d70c0ecbf9bc365 Mon Sep 17 00:00:00 2001 From: Mindrocket42 <161140830+Mindrocket42@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:49:33 +0200 Subject: [PATCH 2/4] Fix GPT-5.4/5.5 Codex presets to send the plain model name on the wire Confirmed via real ChatGPT-backend testing: unlike GPT-5.1/5.2, GPT-5.4 and GPT-5.5 have no distinct "-codex" backend deployment. Sending "gpt-5.4-codex"/"gpt-5.5-codex" as the literal model name gets rejected with "model is not supported when using Codex with a ChatGPT account". Adds resolveWireModel() in model-map.ts to decouple our internal normalized "family key" (still used to pick codex-flavored system instructions and reasoning-effort defaults) from the literal model string sent over the wire, which now collapses to the real "gpt-5.4"/ "gpt-5.5" model for these two presets. Also relabels the GPT-5.4/5.5 config entries from "(OAuth)" / "... Codex (OAuth)" to "(OA-chat)" / "(OA-codex)" so the UI reflects that these are prompt/reasoning-defaults presets of the same underlying model, not distinct model deployments. Co-Authored-By: Claude Sonnet 5 --- config/opencode-legacy.json | 56 +++++++++++++++--------------- config/opencode-modern.json | 12 +++---- lib/request/helpers/model-map.ts | 28 +++++++++++++++ lib/request/request-transformer.ts | 9 +++-- test/request-transformer.test.ts | 14 ++++++-- 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/config/opencode-legacy.json b/config/opencode-legacy.json index 991e7c6..0585ea3 100644 --- a/config/opencode-legacy.json +++ b/config/opencode-legacy.json @@ -16,7 +16,7 @@ }, "models": { "gpt-5.5-none": { - "name": "GPT 5.5 None (OAuth)", + "name": "GPT 5.5 None (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -28,7 +28,7 @@ } }, "gpt-5.5-low": { - "name": "GPT 5.5 Low (OAuth)", + "name": "GPT 5.5 Low (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -40,7 +40,7 @@ } }, "gpt-5.5-medium": { - "name": "GPT 5.5 Medium (OAuth)", + "name": "GPT 5.5 Medium (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -52,7 +52,7 @@ } }, "gpt-5.5-high": { - "name": "GPT 5.5 High (OAuth)", + "name": "GPT 5.5 High (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -64,7 +64,7 @@ } }, "gpt-5.5-xhigh": { - "name": "GPT 5.5 Extra High (OAuth)", + "name": "GPT 5.5 Extra High (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -76,7 +76,7 @@ } }, "gpt-5.5-codex-low": { - "name": "GPT 5.5 Codex Low (OAuth)", + "name": "GPT 5.5 Low (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -88,7 +88,7 @@ } }, "gpt-5.5-codex-medium": { - "name": "GPT 5.5 Codex Medium (OAuth)", + "name": "GPT 5.5 Medium (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -100,7 +100,7 @@ } }, "gpt-5.5-codex-high": { - "name": "GPT 5.5 Codex High (OAuth)", + "name": "GPT 5.5 High (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -112,7 +112,7 @@ } }, "gpt-5.5-codex-xhigh": { - "name": "GPT 5.5 Codex Extra High (OAuth)", + "name": "GPT 5.5 Extra High (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -124,7 +124,7 @@ } }, "gpt-5.4-none": { - "name": "GPT 5.4 None (OAuth)", + "name": "GPT 5.4 None (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -136,7 +136,7 @@ } }, "gpt-5.4-low": { - "name": "GPT 5.4 Low (OAuth)", + "name": "GPT 5.4 Low (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -148,7 +148,7 @@ } }, "gpt-5.4-medium": { - "name": "GPT 5.4 Medium (OAuth)", + "name": "GPT 5.4 Medium (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -160,7 +160,7 @@ } }, "gpt-5.4-high": { - "name": "GPT 5.4 High (OAuth)", + "name": "GPT 5.4 High (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -172,7 +172,7 @@ } }, "gpt-5.4-xhigh": { - "name": "GPT 5.4 Extra High (OAuth)", + "name": "GPT 5.4 Extra High (OA-chat)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -184,7 +184,7 @@ } }, "gpt-5.4-codex-low": { - "name": "GPT 5.4 Codex Low (OAuth)", + "name": "GPT 5.4 Low (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -196,7 +196,7 @@ } }, "gpt-5.4-codex-medium": { - "name": "GPT 5.4 Codex Medium (OAuth)", + "name": "GPT 5.4 Medium (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -208,7 +208,7 @@ } }, "gpt-5.4-codex-high": { - "name": "GPT 5.4 Codex High (OAuth)", + "name": "GPT 5.4 High (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -220,7 +220,7 @@ } }, "gpt-5.4-codex-xhigh": { - "name": "GPT 5.4 Codex Extra High (OAuth)", + "name": "GPT 5.4 Extra High (OA-codex)", "limit": { "context": 1050000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -232,7 +232,7 @@ } }, "gpt-5.4-mini-none": { - "name": "GPT 5.4 Mini None (OAuth)", + "name": "GPT 5.4 Mini None (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -244,7 +244,7 @@ } }, "gpt-5.4-mini-low": { - "name": "GPT 5.4 Mini Low (OAuth)", + "name": "GPT 5.4 Mini Low (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -256,7 +256,7 @@ } }, "gpt-5.4-mini-medium": { - "name": "GPT 5.4 Mini Medium (OAuth)", + "name": "GPT 5.4 Mini Medium (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -268,7 +268,7 @@ } }, "gpt-5.4-mini-high": { - "name": "GPT 5.4 Mini High (OAuth)", + "name": "GPT 5.4 Mini High (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -280,7 +280,7 @@ } }, "gpt-5.4-mini-xhigh": { - "name": "GPT 5.4 Mini Extra High (OAuth)", + "name": "GPT 5.4 Mini Extra High (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -292,7 +292,7 @@ } }, "gpt-5.4-nano-none": { - "name": "GPT 5.4 Nano None (OAuth)", + "name": "GPT 5.4 Nano None (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -304,7 +304,7 @@ } }, "gpt-5.4-nano-low": { - "name": "GPT 5.4 Nano Low (OAuth)", + "name": "GPT 5.4 Nano Low (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -316,7 +316,7 @@ } }, "gpt-5.4-nano-medium": { - "name": "GPT 5.4 Nano Medium (OAuth)", + "name": "GPT 5.4 Nano Medium (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -328,7 +328,7 @@ } }, "gpt-5.4-nano-high": { - "name": "GPT 5.4 Nano High (OAuth)", + "name": "GPT 5.4 Nano High (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { @@ -340,7 +340,7 @@ } }, "gpt-5.4-nano-xhigh": { - "name": "GPT 5.4 Nano Extra High (OAuth)", + "name": "GPT 5.4 Nano Extra High (OA-chat)", "limit": { "context": 400000, "output": 128000 }, "modalities": { "input": ["text", "image"], "output": ["text"] }, "options": { diff --git a/config/opencode-modern.json b/config/opencode-modern.json index 191df38..cb3fa12 100644 --- a/config/opencode-modern.json +++ b/config/opencode-modern.json @@ -16,7 +16,7 @@ }, "models": { "gpt-5.5": { - "name": "GPT 5.5 (OAuth)", + "name": "GPT 5.5 (OA-chat)", "limit": { "context": 1050000, "output": 128000 @@ -59,7 +59,7 @@ } }, "gpt-5.5-codex": { - "name": "GPT 5.5 Codex (OAuth)", + "name": "GPT 5.5 (OA-codex)", "limit": { "context": 1050000, "output": 128000 @@ -97,7 +97,7 @@ } }, "gpt-5.4": { - "name": "GPT 5.4 (OAuth)", + "name": "GPT 5.4 (OA-chat)", "limit": { "context": 1050000, "output": 128000 @@ -140,7 +140,7 @@ } }, "gpt-5.4-codex": { - "name": "GPT 5.4 Codex (OAuth)", + "name": "GPT 5.4 (OA-codex)", "limit": { "context": 1050000, "output": 128000 @@ -178,7 +178,7 @@ } }, "gpt-5.4-mini": { - "name": "GPT 5.4 Mini (OAuth)", + "name": "GPT 5.4 Mini (OA-chat)", "limit": { "context": 400000, "output": 128000 @@ -221,7 +221,7 @@ } }, "gpt-5.4-nano": { - "name": "GPT 5.4 Nano (OAuth)", + "name": "GPT 5.4 Nano (OA-chat)", "limit": { "context": 400000, "output": 128000 diff --git a/lib/request/helpers/model-map.ts b/lib/request/helpers/model-map.ts index 156ea15..3e8778a 100644 --- a/lib/request/helpers/model-map.ts +++ b/lib/request/helpers/model-map.ts @@ -178,3 +178,31 @@ export function getNormalizedModel(modelId: string): string | undefined { export function isKnownModel(modelId: string): boolean { return getNormalizedModel(modelId) !== undefined; } + +/** + * Wire model overrides + * + * Some normalized model names are our own internal "family key" - used to pick + * system instructions and reasoning-effort defaults - but are NOT valid model + * names on the ChatGPT/Codex backend itself. Confirmed via testing: unlike + * GPT-5.1/5.2, GPT-5.4/5.5 do not have a distinct "-codex" backend deployment - + * the ChatGPT backend rejects "gpt-5.4-codex"/"gpt-5.5-codex" outright + * ("model is not supported when using Codex with a ChatGPT account"). The + * "codex" flavor for these is purely a prompt/reasoning-defaults choice on our + * side, so the literal API call must use the plain model name. + */ +const WIRE_MODEL_OVERRIDES: Record = { + "gpt-5.4-codex": "gpt-5.4", + "gpt-5.5-codex": "gpt-5.5", +}; + +/** + * Resolve the literal model name to send to the ChatGPT/Codex backend. + * Defaults to the normalized model name unchanged unless a wire override applies. + * + * @param normalizedModel - Our internal normalized/family-key model name + * @returns The model string to actually put on the wire + */ +export function resolveWireModel(normalizedModel: string): string { + return WIRE_MODEL_OVERRIDES[normalizedModel] ?? normalizedModel; +} diff --git a/lib/request/request-transformer.ts b/lib/request/request-transformer.ts index 5f4c722..53352ad 100644 --- a/lib/request/request-transformer.ts +++ b/lib/request/request-transformer.ts @@ -2,7 +2,7 @@ import { logDebug, logWarn } from "../logger.js"; import { TOOL_REMAP_MESSAGE } from "../prompts/codex.js"; import { CODEX_OPENCODE_BRIDGE } from "../prompts/codex-opencode-bridge.js"; import { getOpenCodeCodexPrompt } from "../prompts/opencode-codex.js"; -import { getNormalizedModel } from "./helpers/model-map.js"; +import { getNormalizedModel, resolveWireModel } from "./helpers/model-map.js"; import { filterOpenCodeSystemPromptsWithCachedPrompt, normalizeOrphanedToolOutputs, @@ -465,8 +465,11 @@ export async function transformRequestBody( }, ); - // Normalize model name for API call - body.model = normalizedModel; + // Normalize model name for API call. Note: this may differ from `normalizedModel` + // itself - some normalized names are internal-only "family keys" used for + // reasoning/instructions selection but aren't valid literal model names on the + // ChatGPT backend (e.g. "gpt-5.4-codex" -> "gpt-5.4"). See resolveWireModel. + body.model = resolveWireModel(normalizedModel); // Codex required fields // ChatGPT backend REQUIRES store=false (confirmed via testing) diff --git a/test/request-transformer.test.ts b/test/request-transformer.test.ts index 4931d08..528d9f1 100644 --- a/test/request-transformer.test.ts +++ b/test/request-transformer.test.ts @@ -1119,14 +1119,24 @@ describe('Request Transformer Module', () => { it('should default gpt-5.4-codex and gpt-5.5-codex to high effort (supports xhigh)', async () => { const result = await transformRequestBody({ model: 'gpt-5.4-codex', input: [] }, codexInstructions); - expect(result.model).toBe('gpt-5.4-codex'); expect(result.reasoning?.effort).toBe('high'); const result2 = await transformRequestBody({ model: 'gpt-5.5-codex', input: [] }, codexInstructions); - expect(result2.model).toBe('gpt-5.5-codex'); expect(result2.reasoning?.effort).toBe('high'); }); + it('should send the plain model name on the wire for gpt-5.4-codex/gpt-5.5-codex (confirmed: backend rejects the -codex suffix for these)', async () => { + // Unlike GPT-5.1/5.2, GPT-5.4/5.5 have no distinct "-codex" backend + // deployment - "codex" here is only a prompt/reasoning-defaults choice + // on our side. Sending the literal "-codex" suffix as the model name + // gets rejected by the ChatGPT backend with a 400 Bad Request. + const result = await transformRequestBody({ model: 'gpt-5.4-codex', input: [] }, codexInstructions); + expect(result.model).toBe('gpt-5.4'); + + const result2 = await transformRequestBody({ model: 'gpt-5.5-codex-high', input: [] }, codexInstructions); + expect(result2.model).toBe('gpt-5.5'); + }); + it('should preserve xhigh for gpt-5.4/5.5 general purpose and codex', async () => { const userConfig: UserConfig = { global: { reasoningEffort: 'xhigh' }, models: {} }; From 2de58a02159e47409621799a2d114c038ce496b8 Mon Sep 17 00:00:00 2001 From: Mindrocket42 <161140830+Mindrocket42@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:41:23 +0200 Subject: [PATCH 3/4] Correct README/CHANGELOG wording on GPT-5.4/5.5 Codex presets Clarify that gpt-5.4-codex/gpt-5.5-codex are not separate backend models - they're OA-chat/OA-codex presets of the same underlying gpt-5.4/gpt-5.5 model, confirmed via testing against the real ChatGPT backend. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 5 +++-- README.md | 6 ++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2757ae1..1210053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,13 @@ All notable changes to this project are documented here. Dates use the ISO forma **Model release**: GPT-5.4 and GPT-5.5 support. ### Added -- **GPT-5.5 / GPT-5.5 Codex**: 1.05M context, 128k output; same none/low/medium/high/xhigh (general) and low/medium/high/xhigh (codex) schema as GPT-5.2. -- **GPT-5.4 / GPT-5.4 Codex**: 1.05M context, 128k output; same reasoning schema as GPT-5.5. +- **GPT-5.5 (OA-chat) / GPT-5.5 (OA-codex)**: 1.05M context, 128k output; same none/low/medium/high/xhigh (chat) and low/medium/high/xhigh (codex) reasoning schema as GPT-5.2. Both presets call the same underlying `gpt-5.5` model — confirmed via testing that GPT-5.4/5.5, unlike GPT-5.1/5.2, have no distinct `-codex` backend deployment. "Codex" here only changes which system prompt and reasoning defaults the plugin applies. +- **GPT-5.4 (OA-chat) / GPT-5.4 (OA-codex)**: 1.05M context, 128k output; same reasoning schema as GPT-5.5, same "same model either way" caveat above. - **GPT-5.4 Mini / GPT-5.4 Nano**: 400k context, 128k output; first-class lightweight general-purpose models (default to `low` reasoning, can opt into `xhigh`). ### Changed - **Reasoning capability logic**: `getReasoningConfig` now derives `xhigh`/`none` support from the GPT-5.x point-release number instead of a hardcoded flag per version, so future 5.x point releases that follow the same schema (per OpenAI docs) won't require a code change here — only new `MODEL_MAP`/config entries. +- **Wire model resolution**: added `resolveWireModel()` in `model-map.ts` to decouple the plugin's internal "family key" (used to pick codex-flavored instructions/reasoning defaults) from the literal model string sent to the ChatGPT backend. The `gpt-5.4-codex`/`gpt-5.5-codex` config keys now correctly resolve to `gpt-5.4`/`gpt-5.5` on the wire instead of being rejected with `"model is not supported when using Codex with a ChatGPT account"`. ## [4.4.0] - 2026-01-09 diff --git a/README.md b/README.md index 69bb3f6..fe1f983 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,8 @@ npx -y opencode-openai-codex-auth@latest --uninstall --all ``` --- ## 📦 Models -- **gpt-5.5** (none/low/medium/high/xhigh) -- **gpt-5.5-codex** (low/medium/high/xhigh) -- **gpt-5.4** (none/low/medium/high/xhigh) -- **gpt-5.4-codex** (low/medium/high/xhigh) +- **gpt-5.5** (OA-chat: none/low/medium/high/xhigh, OA-codex: low/medium/high/xhigh — same underlying model, no separate `-codex` deployment) +- **gpt-5.4** (OA-chat: none/low/medium/high/xhigh, OA-codex: low/medium/high/xhigh — same underlying model, no separate `-codex` deployment) - **gpt-5.4-mini** (none/low/medium/high/xhigh) - **gpt-5.4-nano** (none/low/medium/high/xhigh) - **gpt-5.2** (none/low/medium/high/xhigh) From 892515fff765f87423eee8ef22b6e595f1739eee Mon Sep 17 00:00:00 2001 From: Mindrocket42 <161140830+Mindrocket42@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:54:19 +0200 Subject: [PATCH 4/4] Reformat GPT-5.4/5.5 changelog entry as a table Dense multi-clause bullets bury the key fact (OA-chat/OA-codex share one backend model) where a quick vertical scan misses it. Table form makes the model/context/reasoning/same-backend-model facts scannable at a glance instead of requiring a full read of each line. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 16 +++++++++++----- README.md | 6 ++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1210053..4d6de33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,19 @@ All notable changes to this project are documented here. Dates use the ISO forma **Model release**: GPT-5.4 and GPT-5.5 support. ### Added -- **GPT-5.5 (OA-chat) / GPT-5.5 (OA-codex)**: 1.05M context, 128k output; same none/low/medium/high/xhigh (chat) and low/medium/high/xhigh (codex) reasoning schema as GPT-5.2. Both presets call the same underlying `gpt-5.5` model — confirmed via testing that GPT-5.4/5.5, unlike GPT-5.1/5.2, have no distinct `-codex` backend deployment. "Codex" here only changes which system prompt and reasoning defaults the plugin applies. -- **GPT-5.4 (OA-chat) / GPT-5.4 (OA-codex)**: 1.05M context, 128k output; same reasoning schema as GPT-5.5, same "same model either way" caveat above. -- **GPT-5.4 Mini / GPT-5.4 Nano**: 400k context, 128k output; first-class lightweight general-purpose models (default to `low` reasoning, can opt into `xhigh`). + +| Preset | Context | Output | Chat reasoning | Codex reasoning | Same backend model as chat? | +|---|---|---|---|---|---| +| GPT-5.5 | 1.05M | 128K | none/low/medium/high/xhigh | low/medium/high/xhigh | **Yes** — no separate `-codex` deployment | +| GPT-5.4 | 1.05M | 128K | none/low/medium/high/xhigh | low/medium/high/xhigh | **Yes** — no separate `-codex` deployment | +| GPT-5.4 Mini | 400K | 128K | none/low/medium/high/xhigh | — | n/a (chat-only, defaults to `low`) | +| GPT-5.4 Nano | 400K | 128K | none/low/medium/high/xhigh | — | n/a (chat-only, defaults to `low`) | + +Confirmed via testing: GPT-5.4/5.5, unlike GPT-5.1/5.2, have no distinct `-codex` backend deployment. The OA-chat/OA-codex split is purely which system prompt and reasoning defaults the plugin applies — same underlying model either way. ### Changed -- **Reasoning capability logic**: `getReasoningConfig` now derives `xhigh`/`none` support from the GPT-5.x point-release number instead of a hardcoded flag per version, so future 5.x point releases that follow the same schema (per OpenAI docs) won't require a code change here — only new `MODEL_MAP`/config entries. -- **Wire model resolution**: added `resolveWireModel()` in `model-map.ts` to decouple the plugin's internal "family key" (used to pick codex-flavored instructions/reasoning defaults) from the literal model string sent to the ChatGPT backend. The `gpt-5.4-codex`/`gpt-5.5-codex` config keys now correctly resolve to `gpt-5.4`/`gpt-5.5` on the wire instead of being rejected with `"model is not supported when using Codex with a ChatGPT account"`. +- `getReasoningConfig` now derives `xhigh`/`none` support from the GPT-5.x point-release number, not a hardcoded flag per version — future point releases with the same schema need only new `MODEL_MAP`/config entries. +- New `resolveWireModel()` in `model-map.ts` sends the real model name (`gpt-5.4`/`gpt-5.5`) on the wire for the OA-codex presets, instead of the rejected `gpt-5.4-codex`/`gpt-5.5-codex` strings. ## [4.4.0] - 2026-01-09 diff --git a/README.md b/README.md index fe1f983..049e11b 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,10 @@ npx -y opencode-openai-codex-auth@latest --uninstall --all ``` --- ## 📦 Models -- **gpt-5.5** (OA-chat: none/low/medium/high/xhigh, OA-codex: low/medium/high/xhigh — same underlying model, no separate `-codex` deployment) -- **gpt-5.4** (OA-chat: none/low/medium/high/xhigh, OA-codex: low/medium/high/xhigh — same underlying model, no separate `-codex` deployment) +- **gpt-5.5 (OA-chat)** (none/low/medium/high/xhigh) +- **gpt-5.5 (OA-codex)** (low/medium/high/xhigh) — same backend model as OA-chat, no separate `-codex` deployment +- **gpt-5.4 (OA-chat)** (none/low/medium/high/xhigh) +- **gpt-5.4 (OA-codex)** (low/medium/high/xhigh) — same backend model as OA-chat, no separate `-codex` deployment - **gpt-5.4-mini** (none/low/medium/high/xhigh) - **gpt-5.4-nano** (none/low/medium/high/xhigh) - **gpt-5.2** (none/low/medium/high/xhigh)