From a8b26e1342f85ff6d26fc7255cb8e3d1fa07812d Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 10:34:42 +0900 Subject: [PATCH 1/6] docs(devlog): plan the gpt-5.4 retirement on the Codex login surface OpenAI retired gpt-5.4 and gpt-5.4-mini. This unit is the docs-first roadmap for removing them from the ChatGPT OAuth lane: 000 records the scope boundary and the evidence, 010 covers native catalog membership, 020 the defaults that still dispatch the retired slugs, and 030 the GUI/docs/structure surfaces and the closing gate. The inventory came from three parallel read-only grok-4.6 verifiers (146 files, 592 hits) and the plan was then audited: the planned deletion of the two pinned upstream-models.json rows was reversed, and an independent reviewer's seven findings were folded in, including the maintainer-facing docs/ tree that the first sweep missed. --- .../260912_codex_gpt54_retirement/000_plan.md | 108 +++++++++++++++ .../010_catalog_removal.md | 125 ++++++++++++++++++ .../020_defaults_repoint.md | 112 ++++++++++++++++ .../030_surfaces_and_gate.md | 96 ++++++++++++++ 4 files changed, 441 insertions(+) create mode 100644 devlog/_plan/260912_codex_gpt54_retirement/000_plan.md create mode 100644 devlog/_plan/260912_codex_gpt54_retirement/010_catalog_removal.md create mode 100644 devlog/_plan/260912_codex_gpt54_retirement/020_defaults_repoint.md create mode 100644 devlog/_plan/260912_codex_gpt54_retirement/030_surfaces_and_gate.md diff --git a/devlog/_plan/260912_codex_gpt54_retirement/000_plan.md b/devlog/_plan/260912_codex_gpt54_retirement/000_plan.md new file mode 100644 index 0000000000..2f9e01b486 --- /dev/null +++ b/devlog/_plan/260912_codex_gpt54_retirement/000_plan.md @@ -0,0 +1,108 @@ +# gpt-5.4 / gpt-5.4-mini retirement on the Codex login surface + +## Objective + +OpenAI retired `gpt-5.4` and `gpt-5.4-mini`. Remove them from the Codex (ChatGPT +OAuth) login surface of this proxy: the native catalog, everything that projects it +(`/v1/models`, the dashboard picker, the desktop projection, Claude discovery), and +every opencodex-owned default that still dispatches one of the two slugs. + +The replacement floor is `gpt-5.6-luna` — it is now the cheapest native model on the +ChatGPT login lane, so every helper/sidecar/warmup default lands there. The login +provider's own `defaultModel` moves there too: `gpt-5.6-sol` was considered because it +is priority 1 in the pinned snapshot and `gpt-5.4` held a general-purpose role, but the +owner chose luna so a default that nobody asked for stays the cheapest live model +(owner decision, 2026-09-12). + +## Constraints and scope boundary + +In scope: `src/codex/**`, `src/oauth/**`, `src/vision/**`, the Codex-login parts of +`src/server/**`, `src/cli/**`, `src/types/**`, `src/lib/shadow-call.ts`, `gui/`, +`docs/` (the maintainer-facing pages, not only `docs-site/`), `docs-site/` (all locales), +`structure/`, `scripts/release-notes.ts`, and `tests/`. + +Out of scope, deliberately: + +- Third-party vendor rosters that publish their own snapshots — `github-copilot` + (`src/providers/registry.ts`), `cursor` (`src/adapters/cursor/*`), `codebuddy`, + `opencode`, `command-code`, and `scripts/model-metadata.source.json`. This follows + the `deepseek-v4-pro` precedent (`e86ab5bd8d`): a first-party retirement notice does + not end a vendor's deployment, and deleting their row would strip a live route's + context window and effort ladder while the model keeps arriving from `/models`. +- Historical pricing in `src/usage/expected-prices.ts` and its tests. Past usage rows + still have to cost correctly after the model stops being routable. +- Slugs that only look related: `gpt-5.4-nano`, `gpt-5.4-pro`, `gpt-5.4-high`, + `openai/gpt-5.4-mini` (OpenRouter metadata). None are part of this retirement. + +No push, PR, merge, release, or deploy. Local commits only. + +## Evidence gathered at P + +Three read-only `xai/grok-4.6` verifier subagents swept the tree in parallel. Their +combined inventory: 146 files, 592 `gpt-5.4*` hits, of which the Codex-login-owned +set is the one this unit changes. + +Structural findings that shape the phase order: + +1. `NATIVE_OPENAI_MODELS` (`src/codex/catalog/native-models.ts:156`) is the single + membership list. `SUPPORTED_NATIVE_OPENAI_SLUGS`, `nativeModelRows`, + `nativeOpenAiSlugs`, `accountBoundNativeOpenAiSlugsBySelector`, + `filterSupportedNativeSlugs`, `model-routes.ts` `supportedNative`, and + `CANONICAL_NATIVE_CATALOG_CONTENT_POLICY.nativeBackfillSlugs` all derive from it. + Removing the two slugs there propagates to every projection without further edits. +2. Persisted BARE rows clean themselves up. Once the slugs leave the list, + `isUnsupportedOpenAiNativeSlug` returns true for `gpt-5.4` and `gpt-5.4-mini` and the + canonical merge runs `unsupportedNativeEntries: "drop"` (`sync.ts:847`, filter at + `1068-1070`), so a user's on-disk catalog loses them on the next sync. + Account-namespaced rows are a different path: that predicate returns false for any slug + containing `/` (`metadata.ts:113`), so `team/gpt-5.4` is not dropped by it. Those rows + stop being *generated* because `accountBoundNativeOpenAiSlugsBySelector` and + `availableAccountNativeSlugs` both seed from `NATIVE_OPENAI_MODELS`. wp2 must prove what + happens to an already-persisted `selector/gpt-5.4` row with a focused test rather than + assuming it disappears. +3. `UPSTREAM_NATIVE_ENTRIES` never contained either slug — `upstreamNativeEntryForSlug` + admits only `gpt-5.6-*` and self-described natives — so deleting the two pinned rows + in `src/codex/data/upstream-models.json` changes capability fallbacks only, not the + sync-replacement authority. `gpt-5.2` and `codex-auto-review` stay pinned, which is + why `SELF_DESCRIBED_NATIVE_OPENAI_MODELS` must remain an explicit allowlist. +4. The defaults are independent of the catalog list and fail separately. Warmup + (`src/codex/warmup.ts:30`), the token guardian (`src/oauth/token-guardian.ts:55`), + and the vision describer (`src/vision/plan.ts:14`) all still dispatch + `gpt-5.4-mini` and would 404 after retirement regardless of catalog membership. +5. The startup sidecar migration (`src/server/index.ts:686`) rewrites a *stored* + `gpt-5.4-mini` to `gpt-5.6-luna`, but an unset vision model never equals that + string, so it falls through to `DEFAULT_VISION_MODEL` and still calls the retired + model. That gap is the reason wp3 exists as its own cycle. +6. `DEFAULT_SHADOW_SOURCE_MODELS` is already `["gpt-5.6-luna"]`. `gpt-5.4-mini` there + is an *inbound* prefix for Codex 0.144.x helper calls, not a dispatch target, so it + stays documented as a restore option and is not treated as a retired default. + +## Work-phase map + +| Phase | Unit doc | Outcome | Depends on | +|---|---|---|---| +| wp1 | this document | Roadmap locked, scope boundary recorded | — | +| wp2 | `010_catalog_removal.md` | The two slugs leave the native catalog and its pinned metadata | wp1 | +| wp3 | `020_defaults_repoint.md` | Every opencodex-owned default moves to a live slug | wp1 | +| wp4 | `030_surfaces_and_gate.md` | GUI, docs locales, structure docs, full gate, closing record | wp2, wp3 | + +wp2 and wp3 touch disjoint files and could run in either order; wp2 runs first because +its membership decision is what the wp3 tests assert against. + +## Risks + +- **Over-removal.** Deleting a vendor roster row would break a live Copilot or Cursor + route. Mitigation: the scope boundary above, plus a final `rg` sweep that expects + vendor hits to remain. +- **Under-removal.** A default left on `gpt-5.4-mini` turns into a silent 404 on every + warmup or image description. Mitigation: wp3 enumerates each default site explicitly. +- **Test churn masking a real break.** ~278 test hits are in scope. Mitigation: each + test edit is classified as membership (must change), floor (must repoint), or + historical (must not change), and the full suite is the closing gate. + +## Acceptance + +DONE requires: no retired slug in a Codex-login-owned surface, every default on a live +slug, `bun run typecheck` clean, the focused domain suites green, the full +`bun run test` green, `bun run structure:check` green, and this unit carrying a closing +record with quoted evidence. diff --git a/devlog/_plan/260912_codex_gpt54_retirement/010_catalog_removal.md b/devlog/_plan/260912_codex_gpt54_retirement/010_catalog_removal.md new file mode 100644 index 0000000000..2e479cadc5 --- /dev/null +++ b/devlog/_plan/260912_codex_gpt54_retirement/010_catalog_removal.md @@ -0,0 +1,125 @@ +# wp2 — Remove the retired slugs from the Codex-login native catalog + +Membership only. No default moves here; that is wp3. + +## MODIFY src/codex/catalog/native-models.ts + +`NATIVE_OPENAI_MODELS` line 156. Before: + +```ts + "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark", +``` + +After: + +```ts + "gpt-5.5", "gpt-5.3-codex-spark", +``` + +Two comments name the removed slugs as examples and must stop doing so, because after +this change they would describe a list that no longer holds them: + +- line 75, in the `SELF_DESCRIBED_NATIVE_OPENAI_MODELS` doc comment: the sentence "the + pin also holds `gpt-5.5`, `gpt-5.4` and `gpt-5.4-mini`" stays true of the snapshot, but + the retired slugs are no longer admission candidates at all. Rewrite it around the pins + that remain reachable — `gpt-5.5`, `gpt-5.2`, `codex-auto-review` — so the reason the + allowlist is explicit rather than structural survives the retirement. +- line 175, in `NATIVE_MAIN_DRAIN_SENTINEL_MODELS`: drop `gpt-5.4` and + `gpt-5.4-mini` from the "would have widened the sentinel to" enumeration, leaving + `gpt-5.5` and `gpt-5.3-codex-spark`. The set itself is unchanged — neither slug + was ever a member. + +## MODIFY src/codex/catalog/metadata.ts + +Line 165, `NATIVE_OPENAI_CONTEXT_OVERRIDES`: DELETE +`"gpt-5.4": { contextWindow: 1_000_000, maxContextWindow: 1_000_000 },`. This was the +only 1M native; `gpt-5.4-mini` has no entry and took its window from the pin. + +Line 123, the operating-cap comment: the sentence "and gpt-5.4 runs 272,000 against +1,000,000" describes a row that is going away. Rewrite it to cite only the GPT-5.6 +slugs it already discusses. + +Line 540, the `upstreamNativeEntryForSlug` allowlist comment: "would also admit +gpt-5.5/gpt-5.4/gpt-5.4-mini" becomes "would also admit gpt-5.5/gpt-5.2/codex-auto-review". +The behaviour is unchanged; the example set follows the pins that can still be reached. + +## KEEP src/codex/data/upstream-models.json (audit reversal) + +The plan originally called for deleting the two pinned objects (`"slug": "gpt-5.4"` at +line 460, `"slug": "gpt-5.4-mini"` at line 565). The A phase reversed that. + +This file is a snapshot of upstream's bundled catalog, and `metadata.ts:580` states the +contract: "The pinned JSON is left byte-identical to upstream; only the projection fills +in." The snapshot already carries `gpt-5.2` and `codex-auto-review`, neither of which is +in `NATIVE_OPENAI_MODELS`, so a pinned row has never been an exposure decision. + +Nothing re-exposes a retired slug from the snapshot alone. `PINNED_NATIVE_CAPABILITY_ENTRIES` +and `UPSTREAM_NATIVE_ENTRIES` are both built by iterating `NATIVE_OPENAI_MODELS`, so once +the slugs leave that list their pinned rows are never looked up. The one consumer that +reads raw snapshot rows, `GATED_MODEL_CLIENT_VERSION_FLOOR` in +`src/codex/model-entitlements.ts:135`, filters on `ACCOUNT_GATED_NATIVE_OPENAI_MODELS` +(Daybreak only) and never sees them. + +Deleting ~205 lines of upstream-owned JSON would change no behaviour while breaking the +file's fidelity to its source and churning `reserve-catalog-lifecycle.test.ts` and +`codex-model-entitlements.test.ts`, which read it directly. Membership is the lever. + +## MODIFY src/codex/catalog/parsing.ts, src/codex/catalog/effort.ts, src/codex/catalog/sync.ts + +Comment-only. Each names `gpt-5.4` or `gpt-5.4-mini` as the illustrative "older native" +(`parsing.ts:521` preserved-row cap, `effort.ts:62` xhigh clamp, `sync.ts:263` mock +max/ultra). Replace the examples with `gpt-5.5` / `gpt-5.3-codex-spark`. No predicate +changes: the clamp keys on "is not a gpt-5.6 native", so a request that still names a +retired slug is still clamped correctly. + +## Tests + +Membership assertions that must drop the slugs: + +- `tests/codex-integration/codex-catalog.test.ts` — `filterSupportedNativeSlugs` + expectation at 6954 and the visibility inputs at 6943-6944; delete the "native gpt-5.4 + uses its 1M context window override" test at 3459-3466 with the override itself. + The "preserved gpt-5.4-mini rows get the openai cap" test at 3640-3678 cannot simply be + kept: it feeds a preserved row through `mergeCatalogEntriesForSync`, and after membership + removal that row is a droppable unsupported native. Run it first — if it drops, repoint + the fixture onto a surviving non-overridden native so the #1430 cap regression keeps its + coverage. Line 3686 (`nativeOpenAiContextWindow("gpt-5.4", 272_000)`) names a retired + slug in a test about the generic cap, so repoint it too. + KEEP 3817 (negative: the slugs must not leak into `UPSTREAM_NATIVE_ENTRIES` — still true + and now trivially so), KEEP the Nova1 routed alias fixtures at 1175-1205 and the cursor + rows at 4340-4893. +- `codex-catalog-sync-hardening.test.ts` 118-140, 275-281, 373-376 — repoint the native + fixtures onto `gpt-5.5` / `gpt-5.6-luna`; 729-765 likewise. +- `codex-catalog-golden.test.ts` 41 and the `"gpt-5.4@9"` golden projection at 76. +- `codex-catalog-model-picker-order.test.ts` 153-167. +- `codex-catalog-restore.test.ts` — the hide/priority/window fixtures listed in the + audit; the 1M expectation at 398 goes with the override. +- `native-model-toggle.test.ts` 71-72 and 299. KEEP 79 (`cursor/gpt-5.4` proves vendor + slugs are ignored by native visibility) and 234-243 (preserved compact-limit map). +- `model-visibility-management-api.test.ts` 376-436 — a removed slug is no longer a + valid native visibility target, so these move to a surviving native. +- `codex-convergence-account-selectors.test.ts`, `codex-auth-context.test.ts`, + `codex-metadata-integrity.test.ts`, `codex-v2-gate.test.ts`, `effort-policy.test.ts` + 281-315 — these use the slug as a live native request id; repoint to `gpt-5.5`. + `effort-policy.test.ts` 435-439 keeps testing the clamp, on a surviving old native. +- `tests/claude-integration/` — `claude-models-discovery.test.ts` expected roster, + `claude-model-info.test.ts` (its "only authoritative 1M native" claim dies with the + override), `claude-context-windows.test.ts` 24-29, `claude-inbound.test.ts`. +- `tests/clients/desktop-3p.test.ts` 215-221 — same 1M native subject. + +Do not touch: `tests/usage/**`, `tests/providers/**` vendor suites, +`tests/fixtures/commandcode-models.json`, `tests/responses/responses-shadow-intercept.test.ts`, +`tests/routing/subagent-*` (operator rosters and a negative sentinel assertion). + +Four more in-scope files the first sweep left unclassified: +`tests/codex-integration/codex-app-server-processes.test.ts:491-500` KEEP (the +`codex --config model=...` fixture exercises a command-line detector; any token works), +`tests/codex-integration/slug-codec.test.ts:106` KEEP (codec round-trip pair, not +membership), `tests/responses/empty-completion-guard.test.ts:467` KEEP (string formatting), +`tests/vision/vision-eligibility.test.ts:225` REPOINT to `gpt-5.6-luna` — unlike the +OpenRouter rows at 22-26 this one is the native eligibility subject and belongs to wp3. + +## Proof for this phase + +`bun test tests/codex-integration tests/claude-integration tests/clients` green, plus +`bun run typecheck`. diff --git a/devlog/_plan/260912_codex_gpt54_retirement/020_defaults_repoint.md b/devlog/_plan/260912_codex_gpt54_retirement/020_defaults_repoint.md new file mode 100644 index 0000000000..06943334e0 --- /dev/null +++ b/devlog/_plan/260912_codex_gpt54_retirement/020_defaults_repoint.md @@ -0,0 +1,112 @@ +# wp3 — Move every opencodex-owned default off the retired slugs + +Every lane goes to `gpt-5.6-luna`, including the login provider default (owner +decision, 2026-09-12). Each site below dispatches a real request today and would 404 +after retirement. + +## MODIFY src/oauth/index.ts + +Line 331, the `chatgpt` OAuth definition: `defaultModel: "gpt-5.4"` becomes +`defaultModel: "gpt-5.6-luna"`. + +Be accurate about what this constant does, because the first draft of this plan was not: +`upsertOAuthProvider` returns at `src/oauth/index.ts:1476` for `chatgpt`, so the value is +never persisted onto a provider row, and `src/cli/models.ts:139`, `src/cli/provider.ts` +and `src/server/fast-row.ts:133` all read `config.providers[*].defaultModel` rather than +this constant. It is the ChatGPT login definition's declared default, not a live 404 +dispatch path. It still moves: leaving a retired slug as the login surface's stated +default is wrong on its own terms, and any future consumer would inherit it. + +## MODIFY src/oauth/token-guardian.ts + +Line 55, `DEFAULTS.codexWarmupModel`: `"gpt-5.4-mini"` becomes `"gpt-5.6-luna"`. +Line 87 reads a stored override first, so a user who explicitly set +`tokenGuardian.codexWarmupModel: "gpt-5.4-mini"` keeps calling the retired model. +Extend the existing startup migration (below) to rewrite that stored value too. + +## MODIFY src/codex/warmup.ts + +Line 30: `const DEFAULT_MODEL = "gpt-5.4-mini"` becomes `"gpt-5.6-luna"`. +Line 31: `FALLBACK_MODELS = ["gpt-5.5", "gpt-5.6-luna"]` becomes `["gpt-5.5"]` — luna +is now the primary, and the loop already skips a fallback equal to the primary, so +leaving it would be dead weight that reads as a second chance. + +## MODIFY src/vision/plan.ts + +Line 14: `const DEFAULT_VISION_MODEL = "gpt-5.4-mini"` becomes `"gpt-5.6-luna"`. +Lines 71 and 87 consume it and need no edit. This is the one that the startup sidecar +migration cannot reach: an unset `visionSidecar.model` never equals the old string, so +today an untouched install still describes images with a retired model. + +After this, `src/vision/eligibility.ts:51` and `src/vision/backends.ts:51` — both +already `gpt-5.6-luna` — agree with the runtime instead of contradicting it. + +## MODIFY src/server/management/config-routes.ts + +Lines 823-824, the vision effort-table normalization: both `"gpt-5.4-mini"` literals +become `"gpt-5.6-luna"`. Lines 724 and 963 are already luna and stay. + +## MODIFY src/cli/config-command.ts + +Line 148: `const model = vision.model || "gpt-5.4-mini"` becomes `"gpt-5.6-luna"`, and +the line 147 comment that names the old bounded default follows it. + +## MODIFY src/server/index.ts + +KEEP the sidecar migration block at 686-701 — it is the only thing that rewrites a +stored `gpt-5.4-mini` for existing users, and its destination is already luna. Two +changes: + +1. Extend it to `config.tokenGuardian?.codexWarmupModel === "gpt-5.4-mini"`, which is + currently not migrated and is a live dispatch path. +2. Correct the comment: it claims "explicit user choices are preserved", but the check + is exact equality, so an explicitly chosen `gpt-5.4-mini` is rewritten too. After + retirement that is the right behaviour; the comment should say so rather than + describe a guarantee the code does not make. + +The `SIDECAR_MIGRATION_CUTOFF` date gate stays as-is. + +## MODIFY src/types/config.ts, src/types/tools.ts, src/types/request.ts + +Doc comments only, but they are the published contract: + +- `config.ts:1125` — "Default gpt-5.4-mini" for `codexWarmupModel` becomes luna. +- `config.ts:604` and `:614` — the shadow-intercept comments claim both slugs are + defaults while the code ships luna only. Correct them to state the default is + `gpt-5.6-luna` and `gpt-5.4-mini` is an opt-in `sourceModels` value for 0.144.x + clients. +- `tools.ts:16` and `request.ts:103` — the synthetic web_search comments still name a + "gpt-5.4-mini sidecar"; `src/web-search/index.ts:23` has been luna for a while. + +## KEEP src/lib/shadow-call.ts + +`DEFAULT_SHADOW_SOURCE_MODELS` stays `["gpt-5.6-luna"]` and the 0.144.x note stays. +This list is what the proxy *intercepts*, not what it sends: a 0.144.x client emitting +`gpt-5.4-mini` helper calls is exactly who benefits from an intercept, and an operator +can restore the prefix through `sourceModels`. Adding it back to the default would +change intercept behaviour for every install, which is a separate decision from +retiring the model. + +## Tests + +- `tests/codex-integration/warmup.test.ts` 97-141, `codex-warmup.test.ts` 38/55, + `token-guardian.test.ts` 253, `codex-quota-auto-refresh-main-admission.test.ts` 290 — + the warmup chain becomes `gpt-5.6-luna` then `gpt-5.5`. +- `tests/vision/**` — `sidecar-abort.test.ts` (21 fixtures), `vision-reasoning-contract.test.ts` + (14, retune to luna's ladder), `sidecar-settings-vision-filter.test.ts` 137/189, + `sidecar-settings-vision-controls.test.ts` 89, `vision-anthropic.test.ts` 419. + KEEP `vision-eligibility.test.ts` 22-26 (OpenRouter `openai/gpt-5.4-mini` metadata). +- `tests/web-search/web-search.test.ts` — 26 settings fixtures move to luna. +- `tests/server/server-combo-failover-e2e.test.ts` — 10 live-forward model ids. +- A new regression for the widened migration: a stored + `tokenGuardian.codexWarmupModel: "gpt-5.4-mini"` is rewritten to luna at startup. +- `tests/vision/vision-eligibility.test.ts:225` — the native eligibility subject moves to + luna; 22-26 stay (OpenRouter `openai/gpt-5.4-mini` metadata). +- KEEP `tests/server/config.test.ts` 98 and `server-startup-reconcile-resilience.test.ts` 57 + (legacy roster inputs), `tests/server/api-debug.test.ts` (log-parser fixture), + `tests/responses/**` (shadow restore hatch), `tests/usage/**` (historical pricing). + +## Proof for this phase + +`bun test tests/vision tests/web-search tests/server tests/codex-integration` green, +plus `bun run typecheck`. diff --git a/devlog/_plan/260912_codex_gpt54_retirement/030_surfaces_and_gate.md b/devlog/_plan/260912_codex_gpt54_retirement/030_surfaces_and_gate.md new file mode 100644 index 0000000000..e3c6615fb2 --- /dev/null +++ b/devlog/_plan/260912_codex_gpt54_retirement/030_surfaces_and_gate.md @@ -0,0 +1,96 @@ +# wp4 — GUI, docs, structure docs, and the closing gate + +## MODIFY gui/src + +- `gui/src/pages/dashboard-overview-sections.tsx:447` — `sidecar?.vision.model ?? "gpt-5.4-mini"` + becomes `?? "gpt-5.6-luna"`, matching the runtime default from wp3. Left alone, the + dashboard would display a retired model for an unset vision config. +- `gui/src/pages/api-keys-panels.tsx:322,330` — the copy-paste curl samples name + `gpt-5.4`; move them to `gpt-5.6-luna` so a user pasting the sample gets a live model. +- KEEP `gui/src/pages/shadow-call-source.ts:5` — 0.144.x history, already luna in code. + +## MODIFY gui/tests + +Vision floor: `vision-sidecar-dashboard.test.tsx` 39/107/307/311/372/377, +`vision-reasoning-contract.test.ts` 15/19 → luna. +Native examples: `api-access-models.test.ts`, `apikeys-actions.test.tsx`, +`apikeys-model-test-wire.test.tsx`, `apikeys-models-states.test.tsx`, +`client-config-panel.test.tsx`, `subagents-fallback.test.tsx` → a surviving native. +KEEP `models-native-group-controls.test.ts` (custom, non-native row id) and +`shadow-call-source.test.ts` (explicit override rendering). + +## MODIFY docs-site (English first, then the seven locales) + +Each family below changes in `docs/` and in `fr`, `ja`, `ko`, `ru`, `tr`, `zh-cn`, +`zh-tw`. The English page is the source; a locale must not disagree with it. + +- `guides/codex-app-models.md` — drop `gpt-5.4` and `gpt-5.4-mini` from the native + fallback set. zh-tw `guides/sub-agent-surface.md:218` additionally carries an effort + row for the retired natives that English does not have; delete that row. +- `getting-started/quickstart.md` — the advertised five native picker models still end + in `gpt-5.4-mini`, while `DEFAULT_SUBAGENT_MODELS` is already Astra/sol/terra/luna/5.5. + Make the docs match the shipped default. +- `guides/codex-integration.md` — the account-verification warmup sentence becomes + "defaults to `gpt-5.6-luna`, retries with `gpt-5.5`". +- `reference/configuration/providers.md` — `codexWarmupModel` default cell → luna. +- `reference/configuration/server.md` — the vision `model?` default cell → luna. KEEP + the "legacy explicit `gpt-5.4-mini` migrates on start" sentence; that is still true. +- `guides/sidecars.md` — the code fallback → luna; keep the migration sentence. +- `reference/configuration/agents.md` and `guides/sub-agent-surface.md` — the + `subagentModelFallback` examples name a retired model; move to luna. +- `docs-site/src/components/Landing.astro` 89/320 — the marketing line advertises a + "gpt-5.4-mini sidecar" in every translated string; update all of them together. +- KEEP the Copilot mixed-wire lists in `guides/providers.md` and + `reference/configuration/providers.md`; those describe a vendor roster this unit does + not touch. KEEP the shadow-intercept restore notes, but fix zh-tw + `reference/cli/providers-accounts.md:283`, which states the default is both slugs + while English says luna only. +- `guides/codex-integration.md` explicit-account example `work/gpt-5.4` → `work/gpt-5.5`, + so no page advertises a retired slug even as an illustration. + +## MODIFY docs/ (maintainer-facing, separate from docs-site) + +- `docs/shadow-call-intercept.md:14-17` — says the default source-prefix set is + `gpt-5.4-mini` and `gpt-5.6-luna`. `DEFAULT_SHADOW_SOURCE_MODELS` is luna-only, so this + page is already wrong today. State luna as the default and `gpt-5.4-mini` as the 0.144.x + restore value, matching the correction in `src/types/config.ts` from wp3. +- `docs/codex-app-model-catalog.md:111` — uses `gpt-5.5`/`gpt-5.4` as the example of + snapshot entries that are staler than the installed catalog. Replace the retired half of + the example. + +## MODIFY structure/ + +- `structure/ops/service-and-sidecars.md:52` — vision default cell → `gpt-5.6-luna`. +- `structure/gui-and-management-api.md:129` — the shadow `sourceModels` sentence says + the default is `gpt-5.4-mini` + `gpt-5.6-luna`; the code ships luna only. Correct the + default and keep mini as the documented restore value. + +## MODIFY scripts/release-notes.ts + +Line 1137 — `process.env.OPENAI_MODEL ?? "gpt-5.4"` becomes `?? "gpt-5.6-luna"`. The +tool is maintainer-facing but would fail against a retired model. +KEEP `scripts/model-metadata.source.json` entirely: 129 hits across vendor snapshots +plus openai/openai-codex pricing rows, none of which are the Codex-login catalog. + +## Closing gate + +1. `bun run typecheck` +2. `bun run test` (full suite, PR-ready gate) +3. `bun run lint:gui` and the GUI tests +4. `bun run structure:check` +5. Final `rg "gpt-5\\.4"` sweep, read against an explicit allowlist rather than an + expectation of zero hits. Survivors that are CORRECT and must remain: + vendor rosters (`src/providers/registry.ts` Copilot, `src/adapters/cursor/*`, + `src/providers/codebuddy-models.ts`, `scripts/model-metadata.source.json`, + `tests/providers/**`, `tests/fixtures/commandcode-models.json`, the Copilot/mixed-wire + docs pages); historical pricing (`src/usage/expected-prices.ts`, `tests/usage/**`, + `docs-site/src/data/frontier-benchmarks.json` benchmark rows); + generated metadata (`src/generated/model-metadata.ts`); different slugs + (`-nano`, `-pro`, `-high`, `openai/gpt-5.4-mini`, `cursor/gpt-5.4`); the shadow-intercept + restore hatch (`src/lib/shadow-call.ts`, `tests/responses/**`, the sourceModels docs); + negative and fixture assertions (`tests/routing/subagent-*`, `tests/server/api-debug.test.ts`, + `tests/server/config.test.ts`, `server-startup-reconcile-resilience.test.ts`, + `codex-app-server-processes.test.ts`, `slug-codec.test.ts`, `empty-completion-guard.test.ts`, + `codex-catalog.test.ts:3817`); and this devlog unit. A hit outside that list is a defect. + +Receipts go to `.tmp/`, and `040_done.md` records the outcome with quoted evidence. From 5d664b1a6bae2d17c7bf24fb3115856543d9232a Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 11:13:15 +0900 Subject: [PATCH 2/6] fix(codex): retire gpt-5.4 and gpt-5.4-mini from the Codex login surface OpenAI retired both models, so the ChatGPT OAuth lane must stop offering them and stop dispatching them. Catalog: NATIVE_OPENAI_MODELS drops both slugs, which is the single lever every projection reads - /v1/models, the dashboard picker, the desktop projection, Claude discovery, native visibility targets, account-selector clones and catalog restore all derive from it. A persisted bare row is then dropped by the canonical merge, since isUnsupportedOpenAiNativeSlug now matches it. The gpt-5.4 entry in NATIVE_OPENAI_CONTEXT_OVERRIDES goes with it; it was the only 1M native, and nothing replaces it, so tests that asserted a 1M window now assert its absence. src/codex/data/upstream-models.json deliberately keeps both pinned rows. It is an upstream snapshot that already carries non-member rows (gpt-5.2, codex-auto-review), and both maps built from it iterate NATIVE_OPENAI_MODELS, so the rows are unreachable once membership is gone. Defaults: every lane that still dispatched a retired slug moves to gpt-5.6-luna, the new floor model - ChatGPT OAuth defaultModel, Codex pool warmup (fallbacks are now just gpt-5.5), the token guardian warmup slug, the vision describer, and the vision effort-table defaults in the management API and the CLI. Startup migration now also rewrites a stored tokenGuardian.codexWarmupModel, which was the one live 404 path the existing sidecar migration could not reach; an unset vision model was the other, and that one is fixed by the new default. Out of scope on purpose: vendor rosters that publish their own snapshots (Copilot, Cursor, CodeBuddy, opencode, models.dev), historical usage pricing, and the shadow-call intercept, where gpt-5.4-mini stays a valid opt-in sourceModels prefix so Codex 0.144.x helper calls can still be intercepted. Local suite, typecheck and build: NOT RUN, per the owner's instruction for this machine. Verification is remote CI on this head. Plan and audit record: devlog/_plan/260912_codex_gpt54_retirement/ --- docs-site/src/components/Landing.astro | 4 +- .../docs/fr/getting-started/quickstart.md | 2 +- .../docs/fr/guides/codex-app-models.md | 4 +- .../docs/fr/guides/codex-integration.md | 4 +- .../src/content/docs/fr/guides/sidecars.md | 2 +- .../docs/fr/guides/sub-agent-surface.md | 2 +- .../docs/fr/reference/configuration/agents.md | 4 +- .../fr/reference/configuration/providers.md | 2 +- .../docs/fr/reference/configuration/server.md | 2 +- .../docs/getting-started/quickstart.md | 2 +- .../content/docs/guides/codex-app-models.md | 4 +- .../content/docs/guides/codex-integration.md | 4 +- docs-site/src/content/docs/guides/sidecars.md | 2 +- .../content/docs/guides/sub-agent-surface.md | 2 +- .../docs/ja/getting-started/quickstart.md | 2 +- .../docs/ja/guides/codex-app-models.md | 2 +- .../docs/ja/guides/codex-integration.md | 4 +- .../src/content/docs/ja/guides/sidecars.md | 2 +- .../docs/ja/guides/sub-agent-surface.md | 2 +- .../docs/ja/reference/configuration/agents.md | 4 +- .../ja/reference/configuration/providers.md | 2 +- .../docs/ja/reference/configuration/server.md | 2 +- .../docs/ko/getting-started/quickstart.md | 2 +- .../docs/ko/guides/codex-app-models.md | 4 +- .../docs/ko/guides/codex-integration.md | 4 +- .../src/content/docs/ko/guides/sidecars.md | 2 +- .../docs/ko/guides/sub-agent-surface.md | 2 +- .../docs/ko/reference/configuration/agents.md | 4 +- .../ko/reference/configuration/providers.md | 2 +- .../docs/ko/reference/configuration/server.md | 2 +- .../docs/reference/configuration/agents.md | 4 +- .../docs/reference/configuration/providers.md | 2 +- .../docs/reference/configuration/server.md | 2 +- .../docs/ru/getting-started/quickstart.md | 2 +- .../docs/ru/guides/codex-app-models.md | 4 +- .../docs/ru/guides/codex-integration.md | 4 +- .../src/content/docs/ru/guides/sidecars.md | 2 +- .../docs/ru/guides/sub-agent-surface.md | 2 +- .../docs/ru/reference/configuration/agents.md | 4 +- .../ru/reference/configuration/providers.md | 2 +- .../docs/ru/reference/configuration/server.md | 2 +- .../docs/tr/getting-started/quickstart.md | 2 +- .../docs/tr/guides/codex-app-models.md | 4 +- .../docs/tr/guides/codex-integration.md | 4 +- .../src/content/docs/tr/guides/sidecars.md | 2 +- .../docs/tr/guides/sub-agent-surface.md | 2 +- .../docs/tr/reference/configuration/agents.md | 4 +- .../tr/reference/configuration/providers.md | 2 +- .../docs/tr/reference/configuration/server.md | 2 +- .../docs/zh-cn/getting-started/quickstart.md | 2 +- .../docs/zh-cn/guides/codex-app-models.md | 2 +- .../docs/zh-cn/guides/codex-integration.md | 4 +- .../src/content/docs/zh-cn/guides/sidecars.md | 2 +- .../docs/zh-cn/guides/sub-agent-surface.md | 2 +- .../zh-cn/reference/configuration/agents.md | 4 +- .../reference/configuration/providers.md | 2 +- .../zh-cn/reference/configuration/server.md | 2 +- .../docs/zh-tw/getting-started/quickstart.md | 2 +- .../docs/zh-tw/guides/codex-app-models.md | 4 +- .../docs/zh-tw/guides/codex-integration.md | 4 +- .../src/content/docs/zh-tw/guides/sidecars.md | 2 +- .../docs/zh-tw/guides/sub-agent-surface.md | 2 +- .../zh-tw/reference/cli/providers-accounts.md | 2 +- .../zh-tw/reference/configuration/agents.md | 2 +- .../reference/configuration/providers.md | 2 +- .../zh-tw/reference/configuration/server.md | 2 +- docs/codex-app-model-catalog.md | 4 +- docs/shadow-call-intercept.md | 8 +- gui/src/pages/api-keys-panels.tsx | 4 +- gui/src/pages/dashboard-overview-sections.tsx | 2 +- gui/tests/api-access-models.test.ts | 6 +- gui/tests/apikeys-actions.test.tsx | 6 +- gui/tests/apikeys-model-test-wire.test.tsx | 8 +- gui/tests/apikeys-models-states.test.tsx | 8 +- gui/tests/client-config-panel.test.tsx | 4 +- gui/tests/subagents-fallback.test.tsx | 4 +- gui/tests/vision-reasoning-contract.test.ts | 12 +-- gui/tests/vision-sidecar-dashboard.test.tsx | 12 +-- scripts/release-notes.ts | 2 +- src/cli/config-command.ts | 4 +- src/codex/catalog/effort.ts | 7 +- src/codex/catalog/metadata.ts | 6 +- src/codex/catalog/native-models.ts | 10 ++- src/codex/catalog/parsing.ts | 3 +- src/codex/catalog/sync.ts | 2 +- src/codex/warmup.ts | 4 +- src/oauth/index.ts | 2 +- src/oauth/token-guardian.ts | 2 +- src/server/index.ts | 10 ++- src/server/management/config-routes.ts | 4 +- src/types/config.ts | 8 +- src/types/request.ts | 2 +- src/types/tools.ts | 2 +- src/vision/plan.ts | 2 +- structure/gui-and-management-api.md | 2 +- structure/ops/service-and-sidecars.md | 2 +- .../claude-context-windows.test.ts | 9 +- .../claude-integration/claude-inbound.test.ts | 4 +- .../claude-model-info.test.ts | 26 +++--- .../claude-models-discovery.test.ts | 20 +++-- tests/clients/desktop-3p.test.ts | 20 +++-- .../codex-auth-context.test.ts | 16 ++-- .../codex-catalog-golden.test.ts | 4 +- .../codex-catalog-model-picker-order.test.ts | 6 +- .../codex-catalog-restore.test.ts | 26 +++--- .../codex-catalog-sync-hardening.test.ts | 32 +++---- tests/codex-integration/codex-catalog.test.ts | 83 ++++++++++++------- ...odex-convergence-account-selectors.test.ts | 16 ++-- .../codex-metadata-integrity.test.ts | 22 ++--- ...-quota-auto-refresh-main-admission.test.ts | 2 +- tests/codex-integration/codex-v2-gate.test.ts | 4 +- tests/codex-integration/codex-warmup.test.ts | 4 +- tests/codex-integration/effort-policy.test.ts | 20 ++--- .../model-visibility-management-api.test.ts | 18 ++-- .../native-model-toggle.test.ts | 17 ++-- .../codex-integration/token-guardian.test.ts | 2 +- tests/codex-integration/warmup.test.ts | 19 ++--- .../cyber-policy-error-fidelity.test.ts | 10 +-- tests/providers/opencode-cli.test.ts | 6 +- .../server/server-combo-failover-e2e.test.ts | 20 ++--- tests/vision/sidecar-abort.test.ts | 42 +++++----- .../sidecar-settings-vision-controls.test.ts | 2 +- .../sidecar-settings-vision-filter.test.ts | 4 +- tests/vision/vision-anthropic.test.ts | 2 +- tests/vision/vision-eligibility.test.ts | 2 +- .../vision/vision-reasoning-contract.test.ts | 36 ++++---- tests/web-search/web-search.test.ts | 52 ++++++------ 127 files changed, 467 insertions(+), 407 deletions(-) diff --git a/docs-site/src/components/Landing.astro b/docs-site/src/components/Landing.astro index 222057ce33..bbb71b13ae 100644 --- a/docs-site/src/components/Landing.astro +++ b/docs-site/src/components/Landing.astro @@ -86,7 +86,7 @@ const fr = { 'ocx claude launches Claude Code against the same port — every routed model in the /model picker, auto-context up to 1M, roster sub-agents, and your claude.ai login stays active.': 'ocx claude lance Claude Code sur le même port : tous les modèles routés figurent dans le sélecteur /model, le contexte automatique atteint 1 M, la liste de sous-agents est disponible et votre connexion claude.ai reste active.', 'Claude Code running a routed model through opencodex — gpt-5.6-luna-medium active in the status bar': 'Claude Code exécutant un modèle routé par opencodex, avec gpt-5.6-luna-medium actif dans la barre d’état', 'Search & vision sidecars': 'Services auxiliaires de recherche et de vision', - 'Give non-OpenAI models real web search and image understanding through a gpt-5.4-mini sidecar.': 'Ajoutez aux modèles autres qu’OpenAI une véritable recherche web et la compréhension des images grâce à un service auxiliaire gpt-5.4-mini.', + 'Give non-OpenAI models real web search and image understanding through a gpt-5.6-luna sidecar.': 'Ajoutez aux modèles autres qu’OpenAI une véritable recherche web et la compréhension des images grâce à un service auxiliaire gpt-5.6-luna.', 'ocx init writes a provider into your Codex config and shares one model catalog with Codex CLI, TUI, App, and SDK. ocx stop restores native Codex cleanly.': 'ocx init ajoute un fournisseur à votre configuration Codex et partage un catalogue de modèles unique entre Codex CLI, TUI, App et SDK. ocx stop restaure proprement Codex dans son état natif.', 'Documentation map': 'Plan de la documentation', 'Explore the docs': 'Explorer la documentation', @@ -317,7 +317,7 @@ const docsMap = [

{t('Search & vision sidecars', '검색 & 비전 사이드카', '搜索与视觉边车', 'Сайдкары поиска и зрения', '検索 & ビジョンサイドカー', '搜尋與視覺邊車', 'Arama & görme sidecar’ları')}

-

{t('Give non-OpenAI models real web search and image understanding through a gpt-5.4-mini sidecar.', 'gpt-5.4-mini 사이드카로 비 OpenAI 모델에 실제 웹 검색과 이미지 이해를 붙입니다.', '通过 gpt-5.4-mini 边车,让非 OpenAI 模型获得真实的网页搜索与图像理解能力。', 'Дайте моделям не от OpenAI настоящий веб-поиск и понимание изображений через сайдкар gpt-5.4-mini.', 'gpt-5.4-mini サイドカーで非 OpenAI モデルに本物のウェブ検索と画像理解を提供します。', '透過 gpt-5.4-mini 邊車,讓非 OpenAI 模型獲得真實的網頁搜尋與圖像理解能力。', 'gpt-5.4-mini sidecar’ı sayesinde OpenAI harici modellere gerçek web araması ve görsel anlama yeteneği kazandırın.')}

+

{t('Give non-OpenAI models real web search and image understanding through a gpt-5.6-luna sidecar.', 'gpt-5.6-luna 사이드카로 비 OpenAI 모델에 실제 웹 검색과 이미지 이해를 붙입니다.', '通过 gpt-5.6-luna 边车,让非 OpenAI 模型获得真实的网页搜索与图像理解能力。', 'Дайте моделям не от OpenAI настоящий веб-поиск и понимание изображений через сайдкар gpt-5.6-luna.', 'gpt-5.6-luna サイドカーで非 OpenAI モデルに本物のウェブ検索と画像理解を提供します。', '透過 gpt-5.6-luna 邊車,讓非 OpenAI 模型獲得真實的網頁搜尋與圖像理解能力。', 'gpt-5.6-luna sidecar’ı sayesinde OpenAI harici modellere gerçek web araması ve görsel anlama yeteneği kazandırın.')}

{t('Quickstart', '퀵스타트', '快速开始', 'Быстрый старт', 'クイックスタート', '快速入門', 'Hızlı Başlangıç')}

diff --git a/docs-site/src/content/docs/fr/getting-started/quickstart.md b/docs-site/src/content/docs/fr/getting-started/quickstart.md index 79cce13afa..984294b476 100644 --- a/docs-site/src/content/docs/fr/getting-started/quickstart.md +++ b/docs-site/src/content/docs/fr/getting-started/quickstart.md @@ -74,7 +74,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## Choisissez des modèles de sous-agents (facultatif) Une nouvelle configuration propose cinq modèles natifs dans le sélecteur de sous-agents de Codex : `gpt-5.5`, -`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` et `gpt-5.4-mini`. Ouvrez `ocx gui` pour remplacer ou +`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` et `gpt-6-astra`. Ouvrez `ocx gui` pour remplacer ou réorganiser jusqu’à cinq modèles natifs ou routés. Le tableau de bord peut également définir un modèle de sous-agent préféré et un effort de raisonnement. Consultez [Interface des sous-agents](/fr/guides/sub-agent-surface/) pour choisir v1, base ou v2 et comprendre quand s’appliquent les instructions, les valeurs natives par défaut et les replis. diff --git a/docs-site/src/content/docs/fr/guides/codex-app-models.md b/docs-site/src/content/docs/fr/guides/codex-app-models.md index fb81b9597e..10dce00320 100644 --- a/docs-site/src/content/docs/fr/guides/codex-app-models.md +++ b/docs-site/src/content/docs/fr/guides/codex-app-models.md @@ -130,8 +130,8 @@ service OpenAI. ## Couverture stable actuelle des modèles -L'ensemble natif de secours comprend `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex-spark` et GPT-5.6 -Sol/Terra/Luna. Pour la famille GPT-5.5/5.4, opencodex conserve les entrées dynamiques plus riches du +L'ensemble natif de secours comprend `gpt-5.5`, `gpt-5.3-codex-spark` et GPT-5.6 +Sol/Terra/Luna. Pour la famille GPT-5.5, opencodex conserve les entrées dynamiques plus riches du catalogue Codex installé et ne synthétise qu'une entrée manquante. L'instantané amont fourni n'est employé que pour GPT-5.6, auquel il apporte l'identité et les métadonnées réelles de chaque modèle plutôt qu'une approximation fondée sur un ancien modèle d'entrée. diff --git a/docs-site/src/content/docs/fr/guides/codex-integration.md b/docs-site/src/content/docs/fr/guides/codex-integration.md index e8a4deae4f..fa2457395d 100644 --- a/docs-site/src/content/docs/fr/guides/codex-integration.md +++ b/docs-site/src/content/docs/fr/guides/codex-integration.md @@ -375,7 +375,7 @@ délégation v1/base/v2 et de ses mécanismes de repli. ## Préchauffage des comptes Codex -L’ajout ou la réauthentification vérifie normalement le compte avant son enregistrement par une petite requête attendant `response.completed`. Le modèle par défaut est `gpt-5.4-mini`, avec un essai sur `gpt-5.5` et `gpt-5.6-luna` en cas de HTTP 400 ou HTTP 404. Les erreurs publiques contiennent des catégories fixes, sans corps de réponse brut. +L’ajout ou la réauthentification vérifie normalement le compte avant son enregistrement par une petite requête attendant `response.completed`. Le modèle par défaut est `gpt-5.6-luna`, avec un essai sur `gpt-5.5` en cas de HTTP 400 ou HTTP 404. Les erreurs publiques contiennent des catégories fixes, sans corps de réponse brut. Si la lecture authentifiée des quotas avec le nouveau jeton OAuth confirme un quota de 5 heures, hebdomadaire ou mensuel épuisé, le compte est enregistré sans appel au modèle et affiche **Validation en attente**. Il reste exclu du routage après un redémarrage ou un renouvellement du jeton. Après récupération du quota, actualisez les quotas : une lecture récente et complète avec de la capacité disponible permet une petite requête de validation. Seule sa réussite active le compte. Tout échec conserve la restriction. Les lectures passives ne déclenchent pas cette requête. Un quota inconnu à l’inscription conserve la vérification habituelle. @@ -397,7 +397,7 @@ Un renouvellement du compte principal qui n'aboutit pas répond toujours `503` a ocx config set codexPool '{"excludedPlans":["free"]}' ``` -C'est une politique de sélection, pas un blocage. Un compte écarté conserve ses identifiants, son historique de quota et son affinité de thread, reste visible dans la liste des comptes et demeure joignable par sélection explicite comme `work/gpt-5.4`. Seule la rotation automatique cesse de le choisir, y compris lorsqu'il est déjà le compte actif ou déjà lié à un thread — l'état exact que laisse un abonnement expiré. +C'est une politique de sélection, pas un blocage. Un compte écarté conserve ses identifiants, son historique de quota et son affinité de thread, reste visible dans la liste des comptes et demeure joignable par sélection explicite comme `work/gpt-5.5`. Seule la rotation automatique cesse de le choisir, y compris lorsqu'il est déjà le compte actif ou déjà lié à un thread — l'état exact que laisse un abonnement expiré. Deux limites volontaires. Le compte Codex principal n'est jamais écarté par forfait, car le routage en mode sélection seule ne lit pas son forfait dans les identifiants natifs protégés ; une règle le couvrant se contredirait. Et lorsqu'il ne reste aucun compte non écarté, le compte écarté répond quand même au lieu d'échouer : mettre tous les comptes en pause reste le moyen d'arrêter complètement le service. Il n'existe pas de `minimumPlan`, car classer les forfaits ChatGPT entre eux exige un ordre total qui n'existe pas ici. diff --git a/docs-site/src/content/docs/fr/guides/sidecars.md b/docs-site/src/content/docs/fr/guides/sidecars.md index 27bcda75cb..367b98d9c3 100644 --- a/docs-site/src/content/docs/fr/guides/sidecars.md +++ b/docs-site/src/content/docs/fr/guides/sidecars.md @@ -93,7 +93,7 @@ modèle couvert par le sidecar. Les combos annoncent l'entrée image seulement l nativement ou via un sidecar, et que le paramètre `imageInput` du combo n'est pas désactivé, afin que des clients comme l'application Codex autorisent les pièces jointes au lieu de les bloquer avant l'exécution du sidecar. Lorsque `visionSidecar.model` est absent ou vide, le chemin d'exécution OpenAI, le tableau de bord et l'API de gestion -utilisent le modèle de repli `gpt-5.4-mini`. Au démarrage, une ancienne valeur `gpt-5.4-mini` explicitement enregistrée +utilisent le modèle de repli `gpt-5.6-luna`. Au démarrage, une ancienne valeur `gpt-5.6-luna` explicitement enregistrée est toujours migrée vers `gpt-5.6-luna` ; cette migration s'applique à une valeur stockée, et non à l'absence du champ du modèle. diff --git a/docs-site/src/content/docs/fr/guides/sub-agent-surface.md b/docs-site/src/content/docs/fr/guides/sub-agent-surface.md index 14bf8bb0d3..a5b172a4e4 100644 --- a/docs-site/src/content/docs/fr/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/fr/guides/sub-agent-surface.md @@ -191,7 +191,7 @@ Utilisez `ocx agent` pour les paramètres de délégation, de liste, de plafond ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/fr/reference/configuration/agents.md b/docs-site/src/content/docs/fr/reference/configuration/agents.md index 4f04a20d44..03e289e3e9 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/agents.md +++ b/docs-site/src/content/docs/fr/reference/configuration/agents.md @@ -67,9 +67,9 @@ opencodex ignore les candidats désactivés, non routables, en mauvais état, en "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index 492548c30f..576b13c1bb 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -253,7 +253,7 @@ Les entrées `codexAccounts[]` exigent `id`, `email` et `isMain` ; `plan`, | `failureBackoffMaxSeconds?` | `number` | `3600` | Plafond du délai d'attente et délai après un échec permanent. | | `codexWarmupEnabled?` | `boolean` | `false` | Active la validation synthétique des comptes du pool Codex. | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | Revalidez un compte après 8 jours. | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | Modèle natif utilisé pour l'échauffement facultatif. | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | Modèle natif utilisé pour l'échauffement facultatif. | ## Points de terminaison du fournisseur fixes diff --git a/docs-site/src/content/docs/fr/reference/configuration/server.md b/docs-site/src/content/docs/fr/reference/configuration/server.md index 27fd80b36a..d957d9b50f 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/server.md +++ b/docs-site/src/content/docs/fr/reference/configuration/server.md @@ -240,7 +240,7 @@ une garde d'inactivité, pas un délai de génération total. | --- | --- | --- | --- | | `enabled?` | `boolean` | activé lorsqu'il est utilisable | Commutateur principal de description d'images. | | `backend?` | `"openai" \| "anthropic"` | automatique | La valeur explicite prévaut ; si elle est omise, un identifiant OAuth Anthropic stocké et utilisable est privilégié, sinon `openai`. | -| `model?` | `string` | dépendant du backend | `gpt-5.4-mini` pour OpenAI ou `claude-sonnet-5` pour Anthropic. | +| `model?` | `string` | dépendant du backend | `gpt-5.6-luna` pour OpenAI ou `claude-sonnet-5` pour Anthropic. | | `maxDescriptionsPerTurn?` | `number` | `8` | Nouvelles descriptions des ratés du cache admises par tour principal. `0` désactive les appels ; les valeurs non valides utilisent la valeur par défaut. | | `timeoutMs?` | `number` | `45000` | Délai d'expiration de la récupération par le service auxiliaire. Entier 1–2147483647. | diff --git a/docs-site/src/content/docs/getting-started/quickstart.md b/docs-site/src/content/docs/getting-started/quickstart.md index 867a06cec1..137b532118 100644 --- a/docs-site/src/content/docs/getting-started/quickstart.md +++ b/docs-site/src/content/docs/getting-started/quickstart.md @@ -114,7 +114,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## Choose sub-agent models (optional) A fresh config features five native models in Codex's sub-agent picker: `gpt-5.5`, -`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, and `gpt-5.4-mini`. Open `ocx gui` to replace or +`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, and `gpt-6-astra`. Open `ocx gui` to replace or reorder up to five native or routed models. The dashboard can also set one preferred sub-agent model and reasoning effort. See [Sub-agent Surface](/guides/sub-agent-surface/) to choose v1/base/v2 and understand when guidance, native defaults, and fallback apply. diff --git a/docs-site/src/content/docs/guides/codex-app-models.md b/docs-site/src/content/docs/guides/codex-app-models.md index b30240bf0e..f99fbc5fd8 100644 --- a/docs-site/src/content/docs/guides/codex-app-models.md +++ b/docs-site/src/content/docs/guides/codex-app-models.md @@ -188,8 +188,8 @@ including OpenAI service-tier metadata. ## Current stable model coverage -The native fallback set includes `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, -`gpt-5.3-codex-spark`, and GPT-5.6 Sol/Terra/Luna. For the GPT-5.5/5.4 family, opencodex preserves +The native fallback set includes `gpt-5.5`, `gpt-5.3-codex-spark`, and GPT-5.6 Sol/Terra/Luna. +For the GPT-5.5 family, opencodex preserves the installed Codex catalog's richer live entries and only synthesizes a missing entry. The bundled upstream snapshot is used only for GPT-5.6, where it supplies the real per-model identity and metadata instead of an older-template approximation. diff --git a/docs-site/src/content/docs/guides/codex-integration.md b/docs-site/src/content/docs/guides/codex-integration.md index a99b880fc1..ffc383ac8c 100644 --- a/docs-site/src/content/docs/guides/codex-integration.md +++ b/docs-site/src/content/docs/guides/codex-integration.md @@ -706,7 +706,7 @@ Catalog sync makes the selected sub-agent models available to Codex; see [Codex ## Codex account warmup -When a ChatGPT account is added or reauthenticated, OpenCodex normally verifies it before saving with a small streaming request to the Codex Responses backend. It waits for `response.completed`, defaults to `gpt-5.4-mini`, and retries with `gpt-5.5` and `gpt-5.6-luna` on HTTP 400 or HTTP 404. Public errors contain fixed failure categories rather than raw upstream response bodies. +When a ChatGPT account is added or reauthenticated, OpenCodex normally verifies it before saving with a small streaming request to the Codex Responses backend. It waits for `response.completed`, defaults to `gpt-5.6-luna`, and retries with `gpt-5.5` on HTTP 400 or HTTP 404. Public errors contain fixed failure categories rather than raw upstream response bodies. If the new OAuth credential's authenticated usage lookup confirms an exhausted 5-hour, weekly, or monthly quota, the account is saved without this model request and shows **Validation pending**. It cannot serve pool requests, even after a restart or token refresh. Once quota recovers, **Refresh quotas** finishes validation: a fresh, complete usage reading with headroom permits one small model request, and only a completed response enables the account. Failed or incomplete readings and failed validation preserve the restriction. Passive account polling does not trigger deferred validation. Unknown usage during initial registration retains the normal warmup gate. @@ -728,7 +728,7 @@ A main-account refresh that does not complete still answers `503` with `Retry-Af ocx config set codexPool '{"excludedPlans":["free"]}' ``` -This is a selection policy, not a block. An excluded account keeps its credential, quota history, and thread affinity, stays visible on the account surface, and is still reachable by explicit account selection such as `work/gpt-5.4`. What changes is that automatic rotation stops choosing it, including when it is already the active account or already bound to a thread — which is the state a lapsed subscription leaves behind. +This is a selection policy, not a block. An excluded account keeps its credential, quota history, and thread affinity, stays visible on the account surface, and is still reachable by explicit account selection such as `work/gpt-5.5`. What changes is that automatic rotation stops choosing it, including when it is already the active account or already bound to a thread — which is the state a lapsed subscription leaves behind. Two deliberate limits. The main Codex account is never excluded by plan, because selection-only routing withholds its plan rather than reading the fenced native credential, so a rule covering it would disagree with itself. And when no unexcluded account remains, the excluded one still answers rather than failing closed; pausing every account is still the way to stop serving entirely. There is no `minimumPlan` counterpart, because ranking ChatGPT plans against each other needs a total ordering that does not exist here. diff --git a/docs-site/src/content/docs/guides/sidecars.md b/docs-site/src/content/docs/guides/sidecars.md index 543fe49e96..cee0da3f07 100644 --- a/docs-site/src/content/docs/guides/sidecars.md +++ b/docs-site/src/content/docs/guides/sidecars.md @@ -132,7 +132,7 @@ Combos advertise image input only when every member accepts images, either nativ sidecar, and the combo's `imageInput` setting is not disabled, so clients such as the Codex app allow attachments instead of blocking them before the sidecar runs. When `visionSidecar.model` is absent or blank, the OpenAI execution path, Dashboard, and management API -use the `gpt-5.4-mini` fallback. Startup still migrates an explicitly persisted legacy +use the `gpt-5.6-luna` fallback. Startup still migrates an explicitly persisted legacy `gpt-5.4-mini` value to `gpt-5.6-luna`; that migration applies to a stored value, not to an absent model field. diff --git a/docs-site/src/content/docs/guides/sub-agent-surface.md b/docs-site/src/content/docs/guides/sub-agent-surface.md index 8c9a838e77..49faeb9370 100644 --- a/docs-site/src/content/docs/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/guides/sub-agent-surface.md @@ -225,7 +225,7 @@ Use `ocx agent` for delegation, roster, effort-cap, and fallback settings: ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx effort set --subagent max ``` diff --git a/docs-site/src/content/docs/ja/getting-started/quickstart.md b/docs-site/src/content/docs/ja/getting-started/quickstart.md index 4d251fc1d7..dddd327cbb 100644 --- a/docs-site/src/content/docs/ja/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ja/getting-started/quickstart.md @@ -67,7 +67,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## サブエージェント モデルの選択 (オプション) -新しい設定には、Codex のサブエージェント ピッカーの 5 つのネイティブ モデル、`gpt-5.5`、`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-5.6-luna`、および `gpt-5.4-mini` が含まれています。 `ocx gui` を開いて、最大 5 つのネイティブ モデルまたはルーティング モデルを置換または並べ替えます。ダッシュボードでは、優先サブエージェント モデルと推論負荷を 1 つ設定することもできます。 v1/base/v2 を選択し、ガイダンス、ネイティブのデフォルト、およびフォールバックがいつ適用されるかを理解するには、[サブエージェントサーフェス](/guides/sub-agent-surface/) を参照してください。 +新しい設定には、Codex のサブエージェント ピッカーの 5 つのネイティブ モデル、`gpt-5.5`、`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-5.6-luna`、および `gpt-6-astra` が含まれています。 `ocx gui` を開いて、最大 5 つのネイティブ モデルまたはルーティング モデルを置換または並べ替えます。ダッシュボードでは、優先サブエージェント モデルと推論負荷を 1 つ設定することもできます。 v1/base/v2 を選択し、ガイダンス、ネイティブのデフォルト、およびフォールバックがいつ適用されるかを理解するには、[サブエージェントサーフェス](/guides/sub-agent-surface/) を参照してください。 ## キーを貼り付ける代わりにログインする diff --git a/docs-site/src/content/docs/ja/guides/codex-app-models.md b/docs-site/src/content/docs/ja/guides/codex-app-models.md index 93385300ec..13f9b6517b 100644 --- a/docs-site/src/content/docs/ja/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ja/guides/codex-app-models.md @@ -48,7 +48,7 @@ visibility = "list" ## 現在の安定したモデルの範囲 -ネイティブ フォールバック セットには、`gpt-5.5`、`gpt-5.4`、`gpt-5.4-mini`、`gpt-5.3-codex-spark`、および GPT-5.6 Sol/Terra/Luna が含まれます。 GPT-5.5/5.4 ファミリの場合、opencodex は、インストールされている Codex カタログの豊富なライブ エントリを保存し、欠落しているエントリのみを合成します。バンドルされたアップストリーム スナップショットは GPT-5.6 でのみ使用され、古いテンプレートの近似値の代わりに実際のモデルごとの ID とメタデータが提供されます。 +ネイティブ フォールバック セットには、`gpt-5.5`、`gpt-5.3-codex-spark`、および GPT-5.6 Sol/Terra/Luna が含まれます。 GPT-5.5 ファミリの場合、opencodex は、インストールされている Codex カタログの豊富なライブ エントリを保存し、欠落しているエントリのみを合成します。バンドルされたアップストリーム スナップショットは GPT-5.6 でのみ使用され、古いテンプレートの近似値の代わりに実際のモデルごとの ID とメタデータが提供されます。 |ルート |ピッカー ID とカタログのメタデータ | | --- | --- | diff --git a/docs-site/src/content/docs/ja/guides/codex-integration.md b/docs-site/src/content/docs/ja/guides/codex-integration.md index 4ce54074d3..b1a499dcbc 100644 --- a/docs-site/src/content/docs/ja/guides/codex-integration.md +++ b/docs-site/src/content/docs/ja/guides/codex-integration.md @@ -241,7 +241,7 @@ ocx service install # persistent: auto-starts on login and respawns on crash ## Codex アカウントのウォームアップ -アカウントの追加・再認証では通常、保存前に小さなモデルリクエストで `response.completed` を確認します。既定モデルは `gpt-5.4-mini` で、HTTP 400 または HTTP 404 の場合は `gpt-5.5` および `gpt-5.6-luna` で再試行します。公開エラーには固定の分類のみを表示し、生の応答本文は公開しません。 +アカウントの追加・再認証では通常、保存前に小さなモデルリクエストで `response.completed` を確認します。既定モデルは `gpt-5.6-luna` で、HTTP 400 または HTTP 404 の場合は `gpt-5.5` で再試行します。公開エラーには固定の分類のみを表示し、生の応答本文は公開しません。 新しい OAuth トークンによる使用量取得で5時間・週次・月次の上限到達が確認された場合、モデルを呼ばずに保存し、**検証待ち**と表示します。再起動やトークン更新後も使用できません。上限回復後に使用量を更新すると、十分な空き容量を示す完全な最新情報を確認してから小さなモデルリクエストを送り、完了した場合のみ使用可能になります。取得や検証の失敗では待機状態を維持します。通常の状態ポーリングは検証リクエストを送りません。初回登録時の使用量が不明な場合は通常の検証が必要です。 @@ -263,7 +263,7 @@ ocx service install # persistent: auto-starts on login and respawns on crash ocx config set codexPool '{"excludedPlans":["free"]}' ``` -これはブロックではなく選択ポリシーです。除外されたアカウントも資格情報・使用量履歴・スレッドアフィニティを保持し、アカウント一覧に表示され、`work/gpt-5.4` のような明示的な指定では引き続き利用できます。変わるのは自動ローテーションが選ばなくなる点で、すでにアクティブなアカウントやスレッドに紐づいている場合も含みます。サブスクリプションが失効した直後は、まさにその状態です。 +これはブロックではなく選択ポリシーです。除外されたアカウントも資格情報・使用量履歴・スレッドアフィニティを保持し、アカウント一覧に表示され、`work/gpt-5.5` のような明示的な指定では引き続き利用できます。変わるのは自動ローテーションが選ばなくなる点で、すでにアクティブなアカウントやスレッドに紐づいている場合も含みます。サブスクリプションが失効した直後は、まさにその状態です。 意図的な制限が2つあります。メインの Codex アカウントはプランによって除外されません。選択のみのルーティングは保護されたネイティブ資格情報を読まずにプランを伏せるため、メインを対象にすると挙動が食い違うからです。また、除外されていないアカウントが1つも残らない場合は、失敗させずに除外済みのアカウントが応答します。完全に停止したい場合は従来どおり全アカウントを一時停止してください。`minimumPlan` に相当する設定はありません。ChatGPT のプランを順位付けするには、ここに存在しない全順序が必要になるためです。 diff --git a/docs-site/src/content/docs/ja/guides/sidecars.md b/docs-site/src/content/docs/ja/guides/sidecars.md index 8d2fd18ef1..30a7f5a404 100644 --- a/docs-site/src/content/docs/ja/guides/sidecars.md +++ b/docs-site/src/content/docs/ja/guides/sidecars.md @@ -76,7 +76,7 @@ stall は全体生成 timeout ではありません。SSE 開始前の失敗は サイドカー対象の各モデルに画像入力を広告します。コンボは、すべてのメンバーがネイティブまたはサイドカーを 通じて画像を受け入れ、かつコンボの `imageInput` 設定が無効でない場合にのみ画像入力を広告します。これにより Codex アプリなどのクライアントは、サイドカー実行前に添付をブロックせず許可できます。`visionSidecar.model` が未設定または空の場合、 -OpenAI 実行経路、ダッシュボード、管理 API は `gpt-5.4-mini` をフォールバックとして使います。起動時には +OpenAI 実行経路、ダッシュボード、管理 API は `gpt-5.6-luna` をフォールバックとして使います。起動時には 明示的に保存された旧 `gpt-5.4-mini` 値を引き続き `gpt-5.6-luna` にマイグレーションしますが、この マイグレーションは保存済みの値だけが対象で、モデルフィールドがない場合には適用されません。 diff --git a/docs-site/src/content/docs/ja/guides/sub-agent-surface.md b/docs-site/src/content/docs/ja/guides/sub-agent-surface.md index 8d2be3b2e8..c8d4e06b54 100644 --- a/docs-site/src/content/docs/ja/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ja/guides/sub-agent-surface.md @@ -119,7 +119,7 @@ ocx v2 threads 8 ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/ja/reference/configuration/agents.md b/docs-site/src/content/docs/ja/reference/configuration/agents.md index b4a5106b81..def092132c 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/agents.md +++ b/docs-site/src/content/docs/ja/reference/configuration/agents.md @@ -72,9 +72,9 @@ opencodex は、無効、ルーティング不能、異常、冷却期間、ま "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index 118ea830b9..5523430c80 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -219,7 +219,7 @@ Anthropic アカウント ポリシーのリスクを理解していない限り | `failureBackoffMaxSeconds?` | `number` | `3600` |バックオフの上限と永続的な障害による遅延。 | | `codexWarmupEnabled?` | `boolean` | `false` |合成 Codex プールアカウント検証をオプトインします。 | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | 8 日後にアカウントを再認証します。 | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` |オプションのウォームアップに使用されるネイティブ モデル。 | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` |オプションのウォームアップに使用されるネイティブ モデル。 | ## 固定プロバイダーエンドポイント diff --git a/docs-site/src/content/docs/ja/reference/configuration/server.md b/docs-site/src/content/docs/ja/reference/configuration/server.md index b1dd316c7f..0dd9cf59e4 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/server.md +++ b/docs-site/src/content/docs/ja/reference/configuration/server.md @@ -153,7 +153,7 @@ OpenAI バックエンドには、ChatGPT ログインと有効な ChatGPT `forw | --- | --- | --- | --- | | `enabled?` | `boolean` |使用可能な場合はオン |マスターイメージと説明のスイッチ。 | | `backend?` | `"openai" \| "anthropic"` |自動 | 明示的な値が優先されます。未設定の場合、使用可能な保存済み Anthropic OAuth 認証情報が優先され、それ以外は `openai` になります。 | -| `model?` | `string` |バックエンド依存 | OpenAI の場合は `gpt-5.4-mini`、Anthropic の場合は `claude-sonnet-5`。 | +| `model?` | `string` |バックエンド依存 | OpenAI の場合は `gpt-5.6-luna`、Anthropic の場合は `claude-sonnet-5`。 | | `reasoning?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max"` | `"low"` | OpenAI Responses の推論負荷。Anthropic は無視します。 | | `maxDescriptionsPerTurn?` | `number` | `8` |新しい説明のキャッシュミスはメインターンごとに許可されます。 `0` は通話を無効にします。無効な値にはデフォルトが使用されます。 | | `timeoutMs?` | `number` | `45000` |サイドカーのフェッチタイムアウト。整数 1–2147483647。 | diff --git a/docs-site/src/content/docs/ko/getting-started/quickstart.md b/docs-site/src/content/docs/ko/getting-started/quickstart.md index 971c15785e..bc83df1b21 100644 --- a/docs-site/src/content/docs/ko/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ko/getting-started/quickstart.md @@ -65,7 +65,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## Sub-agent 모델 선택(선택 사항) -새 구성에는 Codex의 sub-agent 선택기에 네이티브 모델 다섯 개인 `gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.4-mini`가 표시됩니다. `ocx gui`를 열어 네이티브 또는 라우팅 모델을 최대 다섯 개까지 바꾸거나 순서를 다시 정할 수 있습니다. 대시보드에서는 선호하는 sub-agent 모델과 추론 강도도 설정할 수 있습니다. [Sub-agent Surface](/guides/sub-agent-surface/)에서 v1/base/v2를 고르고, guidance, 네이티브 기본값, fallback이 언제 적용되는지 확인합니다. +새 구성에는 Codex의 sub-agent 선택기에 네이티브 모델 다섯 개인 `gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-6-astra`가 표시됩니다. `ocx gui`를 열어 네이티브 또는 라우팅 모델을 최대 다섯 개까지 바꾸거나 순서를 다시 정할 수 있습니다. 대시보드에서는 선호하는 sub-agent 모델과 추론 강도도 설정할 수 있습니다. [Sub-agent Surface](/guides/sub-agent-surface/)에서 v1/base/v2를 고르고, guidance, 네이티브 기본값, fallback이 언제 적용되는지 확인합니다. ## 키를 붙여넣는 대신 로그인하기 diff --git a/docs-site/src/content/docs/ko/guides/codex-app-models.md b/docs-site/src/content/docs/ko/guides/codex-app-models.md index 0642712082..23dfd131c6 100644 --- a/docs-site/src/content/docs/ko/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ko/guides/codex-app-models.md @@ -97,8 +97,8 @@ visibility = "list" ## 현재 안정 모델 범위 -네이티브 폴백 목록에는 `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, -`gpt-5.3-codex-spark`, 그리고 GPT-5.6 Sol/Terra/Luna가 들어 있습니다. GPT-5.5/5.4 계열은 설치된 +네이티브 폴백 목록에는 `gpt-5.5`, +`gpt-5.3-codex-spark`, 그리고 GPT-5.6 Sol/Terra/Luna가 들어 있습니다. GPT-5.5 계열은 설치된 Codex 카탈로그의 더 풍부한 실시간 항목을 보존하고, 빠진 항목만 합성합니다. 번들 업스트림 스냅샷은 GPT-5.6에만 사용합니다. 오래된 템플릿으로 근사하지 않고 모델별 실제 식별 정보와 메타데이터를 제공하기 위해서입니다. diff --git a/docs-site/src/content/docs/ko/guides/codex-integration.md b/docs-site/src/content/docs/ko/guides/codex-integration.md index 584e77adb0..0dc6021bd6 100644 --- a/docs-site/src/content/docs/ko/guides/codex-integration.md +++ b/docs-site/src/content/docs/ko/guides/codex-integration.md @@ -252,7 +252,7 @@ catalog sync는 선택된 서브에이전트 모델을 Codex가 쓸 수 있게 ## Codex 계정 워밍업 -ChatGPT 계정을 추가하거나 재인증할 때 OpenCodex는 일반적으로 저장 전에 작은 모델 요청으로 확인합니다. `gpt-5.4-mini`의 `response.completed`를 기다리며 HTTP 400 또는 HTTP 404이면 `gpt-5.5`와 `gpt-5.6-luna`로 재시도합니다. 오류에는 고정된 실패 분류만 표시하고 원본 응답 본문은 노출하지 않습니다. +ChatGPT 계정을 추가하거나 재인증할 때 OpenCodex는 일반적으로 저장 전에 작은 모델 요청으로 확인합니다. `gpt-5.6-luna`의 `response.completed`를 기다리며 HTTP 400 또는 HTTP 404이면 `gpt-5.5`로 재시도합니다. 오류에는 고정된 실패 분류만 표시하고 원본 응답 본문은 노출하지 않습니다. 새 OAuth 토큰으로 인증된 사용량 조회에서 5시간·주간·월간 한도 소진이 확인되면 모델 요청 없이 계정을 저장하고 **검증 대기**로 표시합니다. 재시작이나 토큰 갱신 후에도 요청에 사용되지 않습니다. 한도 회복 후 **사용량 새로고침**을 실행하면, 여유가 있는 완전한 최신 사용량을 확인한 뒤 작은 모델 요청을 보내고 완료 응답을 받아야 계정을 사용할 수 있습니다. 조회나 검증 실패 시 대기 상태를 유지합니다. 일반적인 화면 상태 조회는 이 모델 요청을 보내지 않습니다. 최초 등록 때 사용량이 불명확하면 기존 워밍업 검증이 필요합니다. @@ -274,7 +274,7 @@ ChatGPT 계정을 추가하거나 재인증할 때 OpenCodex는 일반적으로 ocx config set codexPool '{"excludedPlans":["free"]}' ``` -차단이 아니라 선택 정책입니다. 제외된 계정도 자격 증명과 사용량 기록, 스레드 어피니티를 그대로 유지하고 계정 목록에도 계속 보이며 `work/gpt-5.4` 같은 명시적 지정으로는 여전히 쓸 수 있습니다. 달라지는 것은 자동 로테이션이 그 계정을 고르지 않는다는 점이고, 이미 활성 계정이거나 스레드에 묶여 있는 경우도 포함합니다. 구독이 만료된 계정이 바로 그 상태입니다. +차단이 아니라 선택 정책입니다. 제외된 계정도 자격 증명과 사용량 기록, 스레드 어피니티를 그대로 유지하고 계정 목록에도 계속 보이며 `work/gpt-5.5` 같은 명시적 지정으로는 여전히 쓸 수 있습니다. 달라지는 것은 자동 로테이션이 그 계정을 고르지 않는다는 점이고, 이미 활성 계정이거나 스레드에 묶여 있는 경우도 포함합니다. 구독이 만료된 계정이 바로 그 상태입니다. 의도한 제한이 두 가지 있습니다. 메인 Codex 계정은 플랜으로 제외하지 않습니다. 선택 전용 라우팅은 보호된 네이티브 자격 증명을 읽지 않고 플랜을 감추기 때문에, 메인까지 적용하면 상황에 따라 판정이 어긋납니다. 그리고 제외되지 않은 계정이 하나도 남지 않으면 실패시키지 않고 제외된 계정이 그대로 응답합니다. 완전히 멈추려면 지금처럼 모든 계정을 일시 중지하면 됩니다. `minimumPlan`에 해당하는 설정은 없습니다. ChatGPT 플랜에 순위를 매기려면 여기 존재하지 않는 전순서가 필요합니다. diff --git a/docs-site/src/content/docs/ko/guides/sidecars.md b/docs-site/src/content/docs/ko/guides/sidecars.md index c07e8c589d..b3d4309dbf 100644 --- a/docs-site/src/content/docs/ko/guides/sidecars.md +++ b/docs-site/src/content/docs/ko/guides/sidecars.md @@ -78,7 +78,7 @@ stall은 전체 생성 timeout이 아닙니다. SSE가 시작되기 전 실패 콤보는 모든 멤버가 네이티브로 또는 사이드카를 통해 이미지를 수용하고 콤보의 `imageInput` 설정이 비활성화되지 않은 경우에만 image input을 알립니다. 따라서 Codex 앱 같은 클라이언트는 사이드카가 실행되기 전에 첨부를 차단하지 않고 허용합니다. `visionSidecar.model`이 없거나 빈 값이면 -OpenAI 실행 경로, Dashboard, 관리 API는 `gpt-5.4-mini`를 폴백으로 사용합니다. 시작 시 명시적으로 +OpenAI 실행 경로, Dashboard, 관리 API는 `gpt-5.6-luna`를 폴백으로 사용합니다. 시작 시 명시적으로 저장된 기존 `gpt-5.4-mini` 값은 계속 `gpt-5.6-luna`로 마이그레이션되지만, 이 마이그레이션은 저장된 값에만 적용되고 모델 필드가 없는 경우에는 적용되지 않습니다. diff --git a/docs-site/src/content/docs/ko/guides/sub-agent-surface.md b/docs-site/src/content/docs/ko/guides/sub-agent-surface.md index 1372ed7c15..c0273412ec 100644 --- a/docs-site/src/content/docs/ko/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ko/guides/sub-agent-surface.md @@ -117,7 +117,7 @@ ocx v2 threads 8 ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/ko/reference/configuration/agents.md b/docs-site/src/content/docs/ko/reference/configuration/agents.md index da3b95fbd5..5393052849 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/agents.md +++ b/docs-site/src/content/docs/ko/reference/configuration/agents.md @@ -72,9 +72,9 @@ opencodex는 비활성, 라우팅 불가, 비정상, 쿨다운 중, 또는 할 "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index dfda7297e8..c63dfc9bc7 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -223,7 +223,7 @@ Anthropic 계정 정책 위험을 이해하지 못한다면 이 기능은 꺼두 | `failureBackoffMaxSeconds?` | `number` | `3600` | backoff 상한이자 영구 실패 지연입니다. | | `codexWarmupEnabled?` | `boolean` | `false` | 합성 Codex 풀 계정 검증을 선택적으로 켭니다. | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | 8일 후 계정을 다시 검증합니다. | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | 선택적 워밍업에 쓰는 네이티브 모델입니다. | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | 선택적 워밍업에 쓰는 네이티브 모델입니다. | ## 고정 공급자 엔드포인트 diff --git a/docs-site/src/content/docs/ko/reference/configuration/server.md b/docs-site/src/content/docs/ko/reference/configuration/server.md index 163685a645..577379fed9 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/server.md +++ b/docs-site/src/content/docs/ko/reference/configuration/server.md @@ -201,7 +201,7 @@ OpenAI 백엔드는 ChatGPT 로그인과 활성화된 ChatGPT `forward` provider | --- | --- | --- | --- | | `enabled?` | `boolean` | on when usable | 주 이미지 설명 스위치입니다. | | `backend?` | `"openai" \| "anthropic"` | auto | 명시값이 우선하며, 미설정 시 사용 가능한 저장된 Anthropic OAuth 자격 증명을 우선하고 없으면 `openai`를 사용합니다. | -| `model?` | `string` | backend-dependent | OpenAI는 `gpt-5.4-mini`, Anthropic은 `claude-sonnet-5`입니다. | +| `model?` | `string` | backend-dependent | OpenAI는 `gpt-5.6-luna`, Anthropic은 `claude-sonnet-5`입니다. | | `reasoning?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max"` | `"low"` | OpenAI Responses 추론 강도입니다. Anthropic은 무시합니다. | | `maxDescriptionsPerTurn?` | `number` | `8` | 메인 턴당 허용되는 새 설명 캐시 미스 수입니다. `0`이면 호출이 비활성화되며, 잘못된 값은 기본값을 사용합니다. | | `timeoutMs?` | `number` | `45000` | 사이드카 fetch 제한 시간입니다. 정수 1–2147483647. | diff --git a/docs-site/src/content/docs/reference/configuration/agents.md b/docs-site/src/content/docs/reference/configuration/agents.md index 8fd5db5080..24dd9dec16 100644 --- a/docs-site/src/content/docs/reference/configuration/agents.md +++ b/docs-site/src/content/docs/reference/configuration/agents.md @@ -153,9 +153,9 @@ on a mid-thread model switch. "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 8553d8a899..d9e25f347c 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -643,7 +643,7 @@ provider in question. | `failureBackoffMaxSeconds?` | `number` | `3600` | Backoff ceiling and permanent-failure delay. | | `codexWarmupEnabled?` | `boolean` | `false` | Opt into synthetic Codex pool-account validation. | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | Revalidate an account after 8 days. | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | Native model used for optional warmup. | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | Native model used for optional warmup. | ## Fixed provider endpoints diff --git a/docs-site/src/content/docs/reference/configuration/server.md b/docs-site/src/content/docs/reference/configuration/server.md index e9a3faa914..108d2e0925 100644 --- a/docs-site/src/content/docs/reference/configuration/server.md +++ b/docs-site/src/content/docs/reference/configuration/server.md @@ -510,7 +510,7 @@ an inactivity guard, not a total generation deadline. | --- | --- | --- | --- | | `enabled?` | `boolean` | on when usable | Master image-description switch. | | `backend?` | `"openai" \| "anthropic"` | auto | Explicit wins; unset prefers a usable stored Anthropic OAuth credential, else `openai`. | -| `model?` | `string` | backend-dependent | `gpt-5.4-mini` for OpenAI or `claude-sonnet-5` for Anthropic. | +| `model?` | `string` | backend-dependent | `gpt-5.6-luna` for OpenAI or `claude-sonnet-5` for Anthropic. | | `maxDescriptionsPerTurn?` | `number` | `8` | New description cache misses admitted per main turn. `0` disables calls; invalid values use default. | | `timeoutMs?` | `number` | `45000` | Sidecar fetch timeout. Integer 1–2147483647. | diff --git a/docs-site/src/content/docs/ru/getting-started/quickstart.md b/docs-site/src/content/docs/ru/getting-started/quickstart.md index c0f87f1986..20460589f9 100644 --- a/docs-site/src/content/docs/ru/getting-started/quickstart.md +++ b/docs-site/src/content/docs/ru/getting-started/quickstart.md @@ -79,7 +79,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## Выбор моделей подагентов (опционально) В свежей конфигурации в селекторе подагентов Codex представлены пять нативных моделей: `gpt-5.5`, -`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` и `gpt-5.4-mini`. Откройте `ocx gui`, чтобы +`gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` и `gpt-6-astra`. Откройте `ocx gui`, чтобы заменить или переупорядочить до пяти нативных или маршрутизируемых моделей. В панели управления также можно задать одну предпочитаемую модель подагента и уровень рассуждений. Раздел [Поверхность подагентов](/guides/sub-agent-surface/) поможет выбрать v1/base/v2 и понять, когда diff --git a/docs-site/src/content/docs/ru/guides/codex-app-models.md b/docs-site/src/content/docs/ru/guides/codex-app-models.md index e208ab4b0b..5f8c458184 100644 --- a/docs-site/src/content/docs/ru/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ru/guides/codex-app-models.md @@ -77,8 +77,8 @@ visibility = "list" ## Текущее покрытие стабильных моделей -Нативный fallback-набор включает `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, -`gpt-5.3-codex-spark` и GPT-5.6 Sol/Terra/Luna. Для семейства GPT-5.5/5.4 opencodex сохраняет +Нативный fallback-набор включает `gpt-5.5`, +`gpt-5.3-codex-spark` и GPT-5.6 Sol/Terra/Luna. Для семейства GPT-5.5 opencodex сохраняет более богатые живые записи установленного каталога Codex и синтезирует только отсутствующую запись. Bundled upstream-snapshot используется только для GPT-5.6, где он даёт настоящую per-model identity и метаданные вместо приближения по старому шаблону. diff --git a/docs-site/src/content/docs/ru/guides/codex-integration.md b/docs-site/src/content/docs/ru/guides/codex-integration.md index e695830370..4e5acb9ee6 100644 --- a/docs-site/src/content/docs/ru/guides/codex-integration.md +++ b/docs-site/src/content/docs/ru/guides/codex-integration.md @@ -368,7 +368,7 @@ v1/base/v2 при делегировании и fallback — в ## Прогрев аккаунтов Codex -При добавлении или повторной аутентификации аккаунт обычно проверяется до сохранения небольшим запросом, ожидающим `response.completed`. По умолчанию используется `gpt-5.4-mini`, при HTTP 400 или HTTP 404 — повтор с `gpt-5.5` и `gpt-5.6-luna`. Публичные ошибки содержат фиксированные категории без исходного тела ответа. +При добавлении или повторной аутентификации аккаунт обычно проверяется до сохранения небольшим запросом, ожидающим `response.completed`. По умолчанию используется `gpt-5.6-luna`, при HTTP 400 или HTTP 404 — повтор с `gpt-5.5`. Публичные ошибки содержат фиксированные категории без исходного тела ответа. Если запрос квоты с новым OAuth-токеном подтверждает исчерпание пятичасовой, недельной или месячной квоты, аккаунт сохраняется без вызова модели со статусом **Ожидает проверки**. Перезапуск и обновление токена не включают маршрутизацию. После восстановления квоты обновите её: полные свежие данные с доступной ёмкостью разрешают небольшой проверочный запрос. Только успешное завершение включает аккаунт. Ошибки сохраняют ограничение. Пассивный опрос не отправляет такой запрос. Неизвестная квота при регистрации требует обычной проверки. @@ -390,7 +390,7 @@ v1/base/v2 при делегировании и fallback — в ocx config set codexPool '{"excludedPlans":["free"]}' ``` -Это политика выбора, а не блокировка. Исключённый аккаунт сохраняет учётные данные, историю квот и привязку к треду, остаётся видимым в списке и по-прежнему доступен при явном выборе вроде `work/gpt-5.4`. Меняется только то, что автоматическая ротация перестаёт его выбирать — в том числе когда он уже активен или уже привязан к треду, а именно это состояние остаётся после истёкшей подписки. +Это политика выбора, а не блокировка. Исключённый аккаунт сохраняет учётные данные, историю квот и привязку к треду, остаётся видимым в списке и по-прежнему доступен при явном выборе вроде `work/gpt-5.5`. Меняется только то, что автоматическая ротация перестаёт его выбирать — в том числе когда он уже активен или уже привязан к треду, а именно это состояние остаётся после истёкшей подписки. Два намеренных ограничения. Основной аккаунт Codex никогда не исключается по тарифу: маршрутизация в режиме «только выбор» скрывает его тариф, чтобы не читать защищённые нативные учётные данные, и правило для него противоречило бы само себе. А если не осталось ни одного неисключённого аккаунта, исключённый всё равно отвечает вместо отказа; чтобы остановить обслуживание полностью, по-прежнему нужно поставить на паузу все аккаунты. Аналога `minimumPlan` нет: чтобы ранжировать тарифы ChatGPT, нужен полный порядок, которого здесь не существует. diff --git a/docs-site/src/content/docs/ru/guides/sidecars.md b/docs-site/src/content/docs/ru/guides/sidecars.md index 57b437c1e6..54cf37020e 100644 --- a/docs-site/src/content/docs/ru/guides/sidecars.md +++ b/docs-site/src/content/docs/ru/guides/sidecars.md @@ -88,7 +88,7 @@ opencodex описывает каждое изображение **до** осн объявляют вход изображений только если каждый участник принимает изображения нативно или через сайдкар и параметр комбо `imageInput` не отключён; поэтому такие клиенты, как приложение Codex, разрешают вложения вместо их блокировки до запуска сайдкара. Если `visionSidecar.model` отсутствует или пуст, путь выполнения OpenAI, дашборд и API управления -используют фолбэк `gpt-5.4-mini`. При запуске явно сохранённое устаревшее значение +используют фолбэк `gpt-5.6-luna`. При запуске явно сохранённое устаревшее значение `gpt-5.4-mini` по-прежнему мигрирует на `gpt-5.6-luna`; миграция применяется только к сохранённому значению, а не к отсутствующему полю модели. diff --git a/docs-site/src/content/docs/ru/guides/sub-agent-surface.md b/docs-site/src/content/docs/ru/guides/sub-agent-surface.md index 7dfd7ea94c..edfb1fe75c 100644 --- a/docs-site/src/content/docs/ru/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ru/guides/sub-agent-surface.md @@ -169,7 +169,7 @@ ocx v2 threads 8 ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/ru/reference/configuration/agents.md b/docs-site/src/content/docs/ru/reference/configuration/agents.md index 5f8c0eef35..831f49bcf9 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/agents.md +++ b/docs-site/src/content/docs/ru/reference/configuration/agents.md @@ -114,9 +114,9 @@ native ChatGPT-target'ами и прямыми key-auth Responses-маршрут "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index 6c76dd556b..9d132b0e94 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -262,7 +262,7 @@ backoff и может переключить аккаунт уже внутри | `failureBackoffMaxSeconds?` | `number` | `3600` | Верхний предел backoff'а и задержки после permanent-failure. | | `codexWarmupEnabled?` | `boolean` | `false` | Включить synthetic validation для аккаунтов пула Codex. | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | Повторно валидировать аккаунт через 8 дней. | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | Нативная модель, используемая для необязательного warmup'а. | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | Нативная модель, используемая для необязательного warmup'а. | ## Фиксированные endpoint'ы провайдеров diff --git a/docs-site/src/content/docs/ru/reference/configuration/server.md b/docs-site/src/content/docs/ru/reference/configuration/server.md index f8bc9f2a25..84b3caf183 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/server.md +++ b/docs-site/src/content/docs/ru/reference/configuration/server.md @@ -193,7 +193,7 @@ routed-model и hosted-search timeout. Эффективный watchdog мост | --- | --- | --- | --- | | `enabled?` | `boolean` | on when usable | Главный переключатель описания изображений. | | `backend?` | `"openai" \| "anthropic"` | auto | Явное значение имеет приоритет; если оно не задано, предпочтение отдаётся пригодным сохранённым учётным данным Anthropic OAuth, иначе используется `openai`. | -| `model?` | `string` | backend-dependent | `gpt-5.4-mini` для OpenAI или `claude-sonnet-5` для Anthropic. | +| `model?` | `string` | backend-dependent | `gpt-5.6-luna` для OpenAI или `claude-sonnet-5` для Anthropic. | | `reasoning?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max"` | `"low"` | Уровень рассуждений OpenAI Responses. Anthropic его игнорирует. | | `maxDescriptionsPerTurn?` | `number` | `8` | Максимум новых промахов description-cache за один main turn. `0` отключает вызовы; некорректные значения возвращают дефолт. | | `timeoutMs?` | `number` | `45000` | Таймаут запроса sidecar'а. Целое число 1–2147483647. | diff --git a/docs-site/src/content/docs/tr/getting-started/quickstart.md b/docs-site/src/content/docs/tr/getting-started/quickstart.md index 0ae770231f..dfff3c6104 100644 --- a/docs-site/src/content/docs/tr/getting-started/quickstart.md +++ b/docs-site/src/content/docs/tr/getting-started/quickstart.md @@ -84,7 +84,7 @@ codex -m "ollama-cloud/glm-5.2" "Bir SQL geçişi yaz" ## Alt ajan modellerini seçin (isteğe bağlı) Yeni bir yapılandırma, Codex'in alt ajan seçicisinde beş yerel model sunar: -`gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` ve `gpt-5.4-mini`. En +`gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` ve `gpt-6-astra`. En fazla beş yerel veya yönlendirilmiş modeli değiştirmek veya yeniden sıralamak için `ocx gui`'yi açın. Kontrol paneli ayrıca tercih edilen bir alt ajan modelini ve akıl yürütme çabasını ayarlayabilir. v1/base/v2 seçmek ve rehberlik, diff --git a/docs-site/src/content/docs/tr/guides/codex-app-models.md b/docs-site/src/content/docs/tr/guides/codex-app-models.md index 2ddd5bd63a..d26343e9eb 100644 --- a/docs-site/src/content/docs/tr/guides/codex-app-models.md +++ b/docs-site/src/content/docs/tr/guides/codex-app-models.md @@ -144,8 +144,8 @@ yerel yetenekleri kaldırır. ## Mevcut kararlı model kapsamı -Yerel geri dönüş kümesi `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, -`gpt-5.3-codex-spark` ve GPT-5.6 Sol/Terra/Luna modellerini içerir. GPT-5.5/5.4 +Yerel geri dönüş kümesi `gpt-5.5`, +`gpt-5.3-codex-spark` ve GPT-5.6 Sol/Terra/Luna modellerini içerir. GPT-5.5 ailesi için opencodex, kurulu Codex kataloğunun daha zengin canlı girdilerini korur ve yalnızca eksik bir girdiyi sentezler. Paketlenmiş yukarı akış anlık görüntüsü yalnızca eski şablon yaklaşımı yerine gerçek model başına kimliği ve diff --git a/docs-site/src/content/docs/tr/guides/codex-integration.md b/docs-site/src/content/docs/tr/guides/codex-integration.md index 23b9b87f2e..09493150a4 100644 --- a/docs-site/src/content/docs/tr/guides/codex-integration.md +++ b/docs-site/src/content/docs/tr/guides/codex-integration.md @@ -425,7 +425,7 @@ Arayüzü](/tr/guides/sub-agent-surface/) sayfasına bakın. ## Codex hesap ısınması -Hesap ekleme veya yeniden kimlik doğrulama, normalde kaydetmeden önce `response.completed` bekleyen küçük bir model isteğiyle doğrulanır. Varsayılan model `gpt-5.4-mini` olup HTTP 400 veya HTTP 404 durumunda `gpt-5.5` ve `gpt-5.6-luna` denenir. Genel hatalar ham yanıt gövdesi yerine sabit hata kategorilerini içerir. +Hesap ekleme veya yeniden kimlik doğrulama, normalde kaydetmeden önce `response.completed` bekleyen küçük bir model isteğiyle doğrulanır. Varsayılan model `gpt-5.6-luna` olup HTTP 400 veya HTTP 404 durumunda `gpt-5.5` denenir. Genel hatalar ham yanıt gövdesi yerine sabit hata kategorilerini içerir. Yeni OAuth belirteciyle yapılan kota sorgusu 5 saatlik, haftalık veya aylık kotanın tükendiğini doğrularsa hesap model çağrısı olmadan kaydedilir ve **Doğrulama bekleniyor** gösterilir. Yeniden başlatma veya belirteç yenileme yönlendirmeyi açmaz. Kota geri geldiğinde kotaları yenileyin: kullanılabilir kapasite gösteren eksiksiz güncel veri küçük bir doğrulama isteğine izin verir. Yalnızca tamamlanan yanıt hesabı etkinleştirir. Hatalarda kısıtlama korunur. Pasif sorgulama bu isteği göndermez. İlk kayıtta bilinmeyen kota normal doğrulamayı gerektirir. @@ -447,7 +447,7 @@ Tamamlanmayan bir ana hesap yenilemesi, yeniden denemede başarılı olabileceğ ocx config set codexPool '{"excludedPlans":["free"]}' ``` -Bu bir engelleme değil, seçim politikasıdır. Dışarıda bırakılan hesap kimlik bilgisini, kota geçmişini ve iş parçacığı bağını korur, hesap listesinde görünmeye devam eder ve `work/gpt-5.4` gibi açık bir seçimle hâlâ erişilebilir. Değişen tek şey, otomatik rotasyonun onu artık seçmemesidir; hesap zaten etkin olsa ya da bir iş parçacığına bağlı olsa bile. Süresi dolan bir abonelik tam olarak bu durumu bırakır. +Bu bir engelleme değil, seçim politikasıdır. Dışarıda bırakılan hesap kimlik bilgisini, kota geçmişini ve iş parçacığı bağını korur, hesap listesinde görünmeye devam eder ve `work/gpt-5.5` gibi açık bir seçimle hâlâ erişilebilir. Değişen tek şey, otomatik rotasyonun onu artık seçmemesidir; hesap zaten etkin olsa ya da bir iş parçacığına bağlı olsa bile. Süresi dolan bir abonelik tam olarak bu durumu bırakır. İki kasıtlı sınır var. Ana Codex hesabı plana göre hiçbir zaman dışarıda bırakılmaz: yalnızca-seçim yönlendirmesi korunan yerel kimlik bilgisini okumamak için planını saklar, dolayısıyla ana hesabı kapsayan bir kural kendisiyle çelişirdi. Ayrıca dışarıda bırakılmamış hiçbir hesap kalmadığında, dışarıda bırakılan hesap başarısız olmak yerine yine yanıt verir; hizmeti tamamen durdurmak için hâlâ tüm hesapları duraklatmak gerekir. `minimumPlan` karşılığı yoktur, çünkü ChatGPT planlarını sıralamak burada bulunmayan bir tam sıralama gerektirir. diff --git a/docs-site/src/content/docs/tr/guides/sidecars.md b/docs-site/src/content/docs/tr/guides/sidecars.md index 6dc32cd15d..19c7f16ee8 100644 --- a/docs-site/src/content/docs/tr/guides/sidecars.md +++ b/docs-site/src/content/docs/tr/guides/sidecars.md @@ -113,7 +113,7 @@ her üye görselleri yerel olarak veya bir sidecar üzerinden kabul ettiğinde v `imageInput` ayarı devre dışı olmadığında görsel girdisini bildirir; böylece Codex uygulaması gibi istemciler, sidecar çalışmadan önce ekleri engellemek yerine kabul eder. `visionSidecar.model` olmadığında veya boş -olduğunda, OpenAI yürütme yolu, Kontrol Paneli ve yönetim API'si `gpt-5.4-mini` +olduğunda, OpenAI yürütme yolu, Kontrol Paneli ve yönetim API'si `gpt-5.6-luna` geri dönüşünü kullanır. Başlangıç hala açıkça kalıcı hale getirilmiş eski bir `gpt-5.4-mini` değerini `gpt-5.6-luna`'ya geçirir; bu geçiş, bulunmayan bir model alanına değil, saklanan bir değere uygulanır. diff --git a/docs-site/src/content/docs/tr/guides/sub-agent-surface.md b/docs-site/src/content/docs/tr/guides/sub-agent-surface.md index c03f23ba44..1367d66c36 100644 --- a/docs-site/src/content/docs/tr/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/tr/guides/sub-agent-surface.md @@ -214,7 +214,7 @@ kullanın: ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/tr/reference/configuration/agents.md b/docs-site/src/content/docs/tr/reference/configuration/agents.md index fa181b1328..128ac3920a 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/agents.md +++ b/docs-site/src/content/docs/tr/reference/configuration/agents.md @@ -136,9 +136,9 @@ kurtarır. "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index 9fce4f0ba9..3d72f129a7 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -284,7 +284,7 @@ yönetilir. | `failureBackoffMaxSeconds?` | `number` | `3600` | Geri çekilme tavanı ve kalıcı arıza gecikmesi. | | `codexWarmupEnabled?` | `boolean` | `false` | Sentetik Codex havuz hesabı doğrulamasına dahil olun. | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | 8 gün sonra bir hesabı yeniden doğrulayın. | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | İsteğe bağlı ısınma için kullanılan yerel model. | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | İsteğe bağlı ısınma için kullanılan yerel model. | ## Sabit sağlayıcı uç noktaları diff --git a/docs-site/src/content/docs/tr/reference/configuration/server.md b/docs-site/src/content/docs/tr/reference/configuration/server.md index 3b6ee8a9f3..439cd70606 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/server.md +++ b/docs-site/src/content/docs/tr/reference/configuration/server.md @@ -267,7 +267,7 @@ hareketsizlik korumasıdır, toplam bir üretim süresi sınırı değildir. | --- | --- | --- | --- | | `enabled?` | `boolean` | kullanılabilir olduğunda açık | Ana görsel açıklama anahtarı. | | `backend?` | `"openai" \| "anthropic"` | auto | Açık değer önceliklidir; ayarlanmadığında kullanılabilir kayıtlı bir Anthropic OAuth kimlik bilgisi tercih edilir, aksi halde `openai` kullanılır. | -| `model?` | `string` | arka uca bağlı | OpenAI için `gpt-5.4-mini` veya Anthropic için `claude-sonnet-5`. | +| `model?` | `string` | arka uca bağlı | OpenAI için `gpt-5.6-luna` veya Anthropic için `claude-sonnet-5`. | | `maxDescriptionsPerTurn?` | `number` | `8` | Ana tur başına kabul edilen yeni açıklama önbellek ıskalamaları. `0` çağrıları devre dışı bırakır; geçersiz değerler varsayılanı kullanır. | | `timeoutMs?` | `number` | `45000` | Sidecar getirme zaman aşımı. Tamsayı 1–2147483647. | diff --git a/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md b/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md index d3495a3ce4..f4aeabc2d7 100644 --- a/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md +++ b/docs-site/src/content/docs/zh-cn/getting-started/quickstart.md @@ -65,7 +65,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## 选择 sub-agent 模型(可选) -全新配置会在 Codex 的 sub-agent 选择器中提供五个原生模型:`gpt-5.5`、`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-5.6-luna` 和 `gpt-5.4-mini`。打开 `ocx gui`,可以替换或重新排序最多五个原生或已路由模型。仪表盘还可以设置一个首选 sub-agent 模型和 reasoning effort。参见 [Sub-agent Surface](/guides/sub-agent-surface/) 以选择 v1/base/v2,并了解何时适用 guidance、原生默认值和 fallback。 +全新配置会在 Codex 的 sub-agent 选择器中提供五个原生模型:`gpt-5.5`、`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-5.6-luna` 和 `gpt-6-astra`。打开 `ocx gui`,可以替换或重新排序最多五个原生或已路由模型。仪表盘还可以设置一个首选 sub-agent 模型和 reasoning effort。参见 [Sub-agent Surface](/guides/sub-agent-surface/) 以选择 v1/base/v2,并了解何时适用 guidance、原生默认值和 fallback。 ## 登录而非粘贴 key diff --git a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md index 241b812252..d47a080ac5 100644 --- a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md +++ b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md @@ -61,7 +61,7 @@ visibility = "list" ## 当前稳定模型覆盖 -原生回退集合包含 `gpt-5.5`、`gpt-5.4`、`gpt-5.4-mini`、`gpt-5.3-codex-spark` 以及 GPT-5.6 Sol/Terra/Luna。对于 GPT-5.5/5.4 家族,opencodex 会保留已安装 Codex 目录中更丰富的实时条目,只在缺失时才合成条目。内置的上游快照只用于 GPT-5.6,因为它提供的是每个模型真实的身份和元数据,而不是较旧模板的近似版本。 +原生回退集合包含 `gpt-5.5`、`gpt-5.3-codex-spark` 以及 GPT-5.6 Sol/Terra/Luna。对于 GPT-5.5 家族,opencodex 会保留已安装 Codex 目录中更丰富的实时条目,只在缺失时才合成条目。内置的上游快照只用于 GPT-5.6,因为它提供的是每个模型真实的身份和元数据,而不是较旧模板的近似版本。 | 路由 | 选择器 id 与目录元数据 | | --- | --- | diff --git a/docs-site/src/content/docs/zh-cn/guides/codex-integration.md b/docs-site/src/content/docs/zh-cn/guides/codex-integration.md index 71d2b81473..1e4cea1d43 100644 --- a/docs-site/src/content/docs/zh-cn/guides/codex-integration.md +++ b/docs-site/src/content/docs/zh-cn/guides/codex-integration.md @@ -312,7 +312,7 @@ fallback 行为,参见 [Sub-agent Surface](/guides/sub-agent-surface/)。 ## Codex 账号预热 -添加或重新认证账号时,通常会在保存前发送一个小型模型请求并等待 `response.completed`。默认使用 `gpt-5.4-mini`,HTTP 400 或 HTTP 404 时改用 `gpt-5.5` 与 `gpt-5.6-luna` 重试。公开错误仅包含固定分类,不包含原始响应正文。 +添加或重新认证账号时,通常会在保存前发送一个小型模型请求并等待 `response.completed`。默认使用 `gpt-5.6-luna`,HTTP 400 或 HTTP 404 时改用 `gpt-5.5` 重试。公开错误仅包含固定分类,不包含原始响应正文。 如果新 OAuth 凭据的已认证用量查询确认5小时、每周或每月额度耗尽,则不调用模型而直接保存账号,显示**等待验证**。重启或刷新令牌也不会使其可用。额度恢复后刷新额度:只有完整的最新用量显示有余额,才会发送一个小型验证请求;请求完成后账号才可用于路由。查询或验证失败将保留等待状态。普通状态轮询不会发送该请求。初次注册时用量未知仍需常规预热验证。 @@ -334,7 +334,7 @@ fallback 行为,参见 [Sub-agent Surface](/guides/sub-agent-surface/)。 ocx config set codexPool '{"excludedPlans":["free"]}' ``` -这是选择策略,不是封禁。被排除的账号保留凭据、用量历史和线程亲和性,仍显示在账号列表中,也仍可通过 `work/gpt-5.4` 这类显式选择使用。改变的只是自动轮换不再选它,包括它已经是活跃账号或已绑定线程的情况——订阅到期后留下的正是这种状态。 +这是选择策略,不是封禁。被排除的账号保留凭据、用量历史和线程亲和性,仍显示在账号列表中,也仍可通过 `work/gpt-5.5` 这类显式选择使用。改变的只是自动轮换不再选它,包括它已经是活跃账号或已绑定线程的情况——订阅到期后留下的正是这种状态。 有两处刻意的限制。主 Codex 账号不会因套餐被排除:仅选择模式的路由不读取受保护的原生凭据而隐去其套餐,覆盖主账号的规则会自相矛盾。另外,当没有未被排除的账号时,被排除的账号仍会应答而不是失败;要彻底停止服务,仍然是暂停全部账号。没有对应的 `minimumPlan`,因为给 ChatGPT 套餐排序需要一个这里并不存在的全序。 diff --git a/docs-site/src/content/docs/zh-cn/guides/sidecars.md b/docs-site/src/content/docs/zh-cn/guides/sidecars.md index 9d148c85b8..2ffeb8ff1e 100644 --- a/docs-site/src/content/docs/zh-cn/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-cn/guides/sidecars.md @@ -69,7 +69,7 @@ OAuth 账户时使用 `anthropic`,否则使用 `openai`。显式选择 `anthro 如果没有可用 plan,原始图像会被移除,而不会继续转发给纯文本后端。模型目录会为每个由 sidecar 覆盖的模型声明图像输入。 只有当每个 combo 成员都能原生或通过 sidecar 接受图像、且 combo 的 `imageInput` 设置未禁用时,combo 才会声明图像输入; 这样 Codex 应用等客户端会允许附件,而不会在 sidecar 运行前阻止它们。当 `visionSidecar.model` 缺失或为空时,OpenAI 执行路径、 -Dashboard 和管理 API 都使用 `gpt-5.4-mini` 作为回退。启动时仍会把明确保存的旧 +Dashboard 和管理 API 都使用 `gpt-5.6-luna` 作为回退。启动时仍会把明确保存的旧 `gpt-5.4-mini` 值迁移到 `gpt-5.6-luna`;该迁移只作用于已保存值,不适用于缺失的 model 字段。 - 图像可以来自 user、developer 和 tool-result message,也包括 Codex 的 `view_image` 结果。 diff --git a/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md b/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md index c8c6bcba3d..b0d1dc6536 100644 --- a/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md @@ -117,7 +117,7 @@ ocx v2 threads 8 ocx agent status ocx agent injection set --model anthropic/claude-sonnet-5 --effort xhigh ocx agent subagents set gpt-5.6-sol,anthropic/claude-sonnet-5 -ocx agent fallback set gpt-5.4-mini,xai/grok-4.5 --poll-ms 60000 +ocx agent fallback set gpt-5.6-luna,xai/grok-4.5 --poll-ms 60000 ocx agent effort set --subagent max ``` diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/agents.md b/docs-site/src/content/docs/zh-cn/reference/configuration/agents.md index 51558e2e11..094e6df720 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/agents.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/agents.md @@ -71,9 +71,9 @@ opencodex 会跳过已禁用、不可路由、不健康、处于冷却中,或 "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackByModel": { - "gpt-5.5": ["gpt-5.4-mini"] + "gpt-5.5": ["gpt-5.6-luna"] }, "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 2e287e14e4..9d0ed2dd76 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -217,7 +217,7 @@ affinity。这些策略不能规避 provider enforcement。 | `failureBackoffMaxSeconds?` | `number` | `3600` | 退避上限和永久故障延迟。 | | `codexWarmupEnabled?` | `boolean` | `false` | 启用合成的 Codex 池账户验证。 | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | 8 天后重新验证账户。 | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | 用于可选预热的原生模型。 | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | 用于可选预热的原生模型。 | ## 固定提供者端点 diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/server.md b/docs-site/src/content/docs/zh-cn/reference/configuration/server.md index 211e141650..4031f9ff19 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/server.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/server.md @@ -167,7 +167,7 @@ routed 重放会把主 ChatGPT 认证注入内部请求。Anthropic 后端使用 | --- | --- | --- | --- | | `enabled?` | `boolean` | 在可用时启用 | 图像描述总开关。 | | `backend?` | `"openai" \| "anthropic"` | auto | 显式值优先;未设置时优先使用可用的已保存 Anthropic OAuth 凭据,否则使用 `openai`。 | -| `model?` | `string` | 依后端而定 | OpenAI 使用 `gpt-5.4-mini`,Anthropic 使用 `claude-sonnet-5`。 | +| `model?` | `string` | 依后端而定 | OpenAI 使用 `gpt-5.6-luna`,Anthropic 使用 `claude-sonnet-5`。 | | `reasoning?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max"` | `"low"` | OpenAI Responses 推理强度;Anthropic 会忽略该项。 | | `maxDescriptionsPerTurn?` | `number` | `8` | 每个主轮次允许的新增描述缓存未命中次数。`0` 会禁用调用;无效值会使用默认值。 | | `timeoutMs?` | `number` | `45000` | 侧车获取超时。整数 1–2147483647。 | diff --git a/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md b/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md index a7d0ca4e87..8236a6f511 100644 --- a/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md +++ b/docs-site/src/content/docs/zh-tw/getting-started/quickstart.md @@ -73,7 +73,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration" ## 選擇 sub-agent 模型(可選) 新設定會讓 Codex 的 sub-agent 選擇器包含五個原生模型:`gpt-5.5`、`gpt-5.6-sol`、 -`gpt-5.6-terra`、`gpt-5.6-luna` 和 `gpt-5.4-mini`。開啟 `ocx gui` 即可替換或重新排序最多五個 +`gpt-5.6-terra`、`gpt-5.6-luna` 和 `gpt-6-astra`。開啟 `ocx gui` 即可替換或重新排序最多五個 原生或已路由模型。儀表板也可以設定一個首選 sub-agent 模型及 reasoning effort。參見 [子代理介面](/zh-tw/guides/sub-agent-surface/) 選擇 v1/base/v2,並了解指引、原生預設與回退 何時生效。 diff --git a/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md b/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md index 260ab7ad36..efe0dfb6ee 100644 --- a/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md +++ b/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md @@ -80,8 +80,8 @@ visibility = "list" ## 目前穩定模型涵蓋範圍 -原生回退列表包含 `gpt-5.5`、`gpt-5.4`、`gpt-5.4-mini`、 -`gpt-5.3-codex-spark` 以及 GPT-5.6 Sol/Terra/Luna。對於 GPT-5.5/5.4 系列,opencodex 會 +原生回退列表包含 `gpt-5.5`、 +`gpt-5.3-codex-spark` 以及 GPT-5.6 Sol/Terra/Luna。對於 GPT-5.5 系列,opencodex 會 保留已安裝 Codex 目錄中資訊更完整的即時條目,僅在條目缺失時才合成。內建的上游快照只用於 GPT-5.6,以便提供每個模型真實的身份和後設資料,而不是套用舊模板近似生成。 diff --git a/docs-site/src/content/docs/zh-tw/guides/codex-integration.md b/docs-site/src/content/docs/zh-tw/guides/codex-integration.md index c9f8090987..a17997b848 100644 --- a/docs-site/src/content/docs/zh-tw/guides/codex-integration.md +++ b/docs-site/src/content/docs/zh-tw/guides/codex-integration.md @@ -319,7 +319,7 @@ ocx service install # 常駐:登入時自動啟動,崩潰後自動重新 ## Codex 帳號預熱 -新增或重新驗證帳號時,通常會在儲存前傳送小型模型請求並等待 `response.completed`。預設使用 `gpt-5.4-mini`,HTTP 400 或 HTTP 404 時改用 `gpt-5.5` 與 `gpt-5.6-luna` 重試。公開錯誤僅包含固定分類,不包含原始回應本文。 +新增或重新驗證帳號時,通常會在儲存前傳送小型模型請求並等待 `response.completed`。預設使用 `gpt-5.6-luna`,HTTP 400 或 HTTP 404 時改用 `gpt-5.5` 重試。公開錯誤僅包含固定分類,不包含原始回應本文。 若新 OAuth 憑證的已驗證用量查詢確認5小時、每週或每月額度耗盡,則不呼叫模型而直接儲存帳號,顯示**等待驗證**。重新啟動或更新權杖也不會使其可用。額度恢復後重新整理額度:只有完整的最新用量顯示有餘額,才會傳送小型驗證請求;請求完成後帳號才可用於路由。查詢或驗證失敗將保留等待狀態。一般狀態輪詢不會傳送該請求。首次註冊時用量未知仍需一般預熱驗證。 @@ -341,7 +341,7 @@ ocx service install # 常駐:登入時自動啟動,崩潰後自動重新 ocx config set codexPool '{"excludedPlans":["free"]}' ``` -這是選擇策略,不是封鎖。被排除的帳號保留憑證、用量紀錄與執行緒親和性,仍顯示在帳號清單中,也仍可透過 `work/gpt-5.4` 這類明確選擇使用。改變的只是自動輪換不再挑它,包括它已經是使用中帳號或已綁定執行緒的情況——訂閱到期後留下的正是這種狀態。 +這是選擇策略,不是封鎖。被排除的帳號保留憑證、用量紀錄與執行緒親和性,仍顯示在帳號清單中,也仍可透過 `work/gpt-5.5` 這類明確選擇使用。改變的只是自動輪換不再挑它,包括它已經是使用中帳號或已綁定執行緒的情況——訂閱到期後留下的正是這種狀態。 有兩處刻意的限制。主 Codex 帳號不會因方案被排除:僅選擇模式的路由不讀取受保護的原生憑證而隱去其方案,涵蓋主帳號的規則會自相矛盾。此外,當沒有未被排除的帳號時,被排除的帳號仍會回應而不是失敗;要完全停止服務,仍然是暫停所有帳號。沒有對應的 `minimumPlan`,因為為 ChatGPT 方案排序需要一個這裡並不存在的全序。 diff --git a/docs-site/src/content/docs/zh-tw/guides/sidecars.md b/docs-site/src/content/docs/zh-tw/guides/sidecars.md index 6462a5df2d..97c073d3ba 100644 --- a/docs-site/src/content/docs/zh-tw/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-tw/guides/sidecars.md @@ -70,7 +70,7 @@ OAuth 帳號時使用 `anthropic`,否則使用 `openai`。明確選擇 `anthro 只有當每個 combo 成員都能原生或透過 sidecar 接受圖像,且 combo 的 `imageInput` 設定未停用時,combo 才會宣告圖像輸入; 如此 Codex 應用程式等用戶端會允許附件,而不會在 sidecar 執行前阻擋它們。Dashboard 和管理 API 目前顯示的預設值是 `gpt-5.6-luna`,啟動時也會把明確儲存的舊 `gpt-5.4-mini` 值遷移到 Luna。只有在 -`visionSidecar.model` 欄位不存在或為空字串時,vision 執行路徑才會使用程式碼中的 `gpt-5.4-mini` 回退值。 +`visionSidecar.model` 欄位不存在或為空字串時,vision 執行路徑才會使用程式碼中的 `gpt-5.6-luna` 回退值。 - 圖像可以來自 user、developer 和 tool-result message,也包括 Codex 的 `view_image` 結果。 - 每張圖像會以 `reasoning.effort: "low"` 傳送給設定的原生 vision 模型,描述結果會就地替換 diff --git a/docs-site/src/content/docs/zh-tw/guides/sub-agent-surface.md b/docs-site/src/content/docs/zh-tw/guides/sub-agent-surface.md index 59be70f24d..b18e2d34c1 100644 --- a/docs-site/src/content/docs/zh-tw/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/zh-tw/guides/sub-agent-surface.md @@ -215,7 +215,7 @@ opencodex 會將磁碟上的模型目錄與目前使用者擁有的每個 Codex | 模型 | 線路上的 `max` | 選擇 `ultra` 後的線路值 | | --- | --- | --- | -| gpt-5.5、gpt-5.4、gpt-5.4-mini | xhigh | xhigh(先轉為 max,再經 `nativeEffortClamp`) | +| gpt-5.5 | xhigh | xhigh(先轉為 max,再經 `nativeEffortClamp`) | | gpt-5.6-sol、gpt-5.6-terra | max | max | | gpt-5.6-luna | max | 其精確上游階梯不提供該選項 | | 路由模型 | 由適配器對映或限制 | 先轉為 max,再由適配器對映或限制 | diff --git a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md index e979fa35b6..0e3bbe4b78 100644 --- a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md @@ -280,7 +280,7 @@ Preview 建置使用 `/native-main-profiles`。該配置絕不 | `provider ` | `--json` | 在單次寫入中啟用或停用一個供應商的所有模型。 | | `selected ` | `--set `, `--clear`, `--json` | 讀取或替換供應商模型允許清單。`--clear` 移除允許清單,使每個模型都被提供。 | | `context \|provider \|all >` | `--json` | 讀取或設定 context-window 上限,全域或 per 供應商。 | -| `shadow [model\|-]` | `--enabled `, `--json` | 讀取或設定 Codex 背景 helper 呼叫的替換模型。`-` 清除模型。`status` 亦回報 `sourceModels`,即代理攔截的 helper slug(預設:`gpt-5.4-mini` 與 `gpt-5.6-luna`)。 | +| `shadow [model\|-]` | `--enabled `, `--json` | 讀取或設定 Codex 背景 helper 呼叫的替換模型。`-` 清除模型。`status` 亦回報 `sourceModels`,即代理攔截的 helper slug(預設:`gpt-5.6-luna`;0.144.x 以前的用戶端使用已退役的 `gpt-5.4-mini`,可透過 `sourceModels` 還原)。 | ```bash ocx models live --json # Codex 目前實際可見的模型 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/agents.md b/docs-site/src/content/docs/zh-tw/reference/configuration/agents.md index 4fb07c6d29..d3fdf7059b 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/agents.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/agents.md @@ -59,7 +59,7 @@ opencodex 會跳過已停用、不可路由、不健康、冷卻中或達到配 "injectionModel": "gpt-5.5", "injectionEffort": "high", "syncCodexSubagentDefaults": true, - "subagentModelFallback": ["gpt-5.4-mini"], + "subagentModelFallback": ["gpt-5.6-luna"], "subagentModelFallbackPollMs": 60000, "subagentEffortCap": "high" } diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 4303ca73aa..ff67793990 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -182,7 +182,7 @@ API-key 供應商可持有字面值金鑰或環境參考。OAuth 供應商使用 | `failureBackoffMaxSeconds?` | `number` | `3600` | Backoff 上限與永久失敗延遲。 | | `codexWarmupEnabled?` | `boolean` | `false` | 選擇加入合成 Codex 池帳號驗證。 | | `codexWarmupMaxAgeSeconds?` | `number` | `691200` | 8 天後重新驗證帳號。 | -| `codexWarmupModel?` | `string` | `gpt-5.4-mini` | 用於可選暖機的原生模型。 | +| `codexWarmupModel?` | `string` | `gpt-5.6-luna` | 用於可選暖機的原生模型。 | ## 固定供應商端點 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/server.md b/docs-site/src/content/docs/zh-tw/reference/configuration/server.md index 4649b51f97..402015f060 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/server.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/server.md @@ -188,7 +188,7 @@ OpenAI backend 需要 ChatGPT 登入與啟用的 ChatGPT `forward` 供應商。C | --- | --- | --- | --- | | `enabled?` | `boolean` | 可用時開啟 | 主圖片描述開關。 | | `backend?` | `"openai" \| "anthropic"` | 自動 | 明確值優先;未設定時優先使用可用的已儲存 Anthropic OAuth 憑證,否則使用 `openai`。 | -| `model?` | `string` | 視 backend 而定 | OpenAI 為 `gpt-5.4-mini` 或 Anthropic 為 `claude-sonnet-5`。 | +| `model?` | `string` | 視 backend 而定 | OpenAI 為 `gpt-5.6-luna` 或 Anthropic 為 `claude-sonnet-5`。 | | `maxDescriptionsPerTurn?` | `number` | `8` | 每個主回合允許的新描述快取未命中。`0` 停用呼叫;無效值使用預設。 | | `timeoutMs?` | `number` | `45000` | Sidecar 擷取逾時。整數 1–2147483647。 | diff --git a/docs/codex-app-model-catalog.md b/docs/codex-app-model-catalog.md index a6cd01da27..2e8c7e3c63 100644 --- a/docs/codex-app-model-catalog.md +++ b/docs/codex-app-model-catalog.md @@ -107,8 +107,8 @@ The recognized Codex effort ladder is `low < medium < high < xhigh < max < ultra `prefer_websockets`/`supports_websockets` follow the central websocket gate. A future `gpt-5.6-*` slug the snapshot predates falls back to template synthesis plus `ensureGpt56ReasoningLevels` (appends `max`+`ultra`). -- Snapshot scope is deliberately gpt-5.6-only: the bundled upstream entries for - `gpt-5.5`/`gpt-5.4` are staler than the installed catalog's live entries (e.g. snapshot +- Snapshot scope is deliberately gpt-5.6-only: the bundled upstream entries for older natives + such as `gpt-5.5` are staler than the installed catalog's live entries (e.g. snapshot gpt-5.5 carries `tool_mode: null`), so substituting them would downgrade real data. On-disk sync also self-heals fallback-quality 5.6 entries (display_name stamped with the bare slug) by upgrading them to the snapshot entry; genuine entries from a newer installed codex are diff --git a/docs/shadow-call-intercept.md b/docs/shadow-call-intercept.md index d0e7248734..dc271588b4 100644 --- a/docs/shadow-call-intercept.md +++ b/docs/shadow-call-intercept.md @@ -14,9 +14,11 @@ its configured reasoning effort. The helper model is not stable across client versions. Codex used `gpt-5.4-mini` up to 0.144.x and moved to `gpt-5.6-luna` in 0.145.0, which silently disabled a single-literal intercept ([#311](https://github.com/lidge-jun/opencodex/issues/311)). The intercept therefore matches a -**set** of source-model prefixes — `gpt-5.4-mini` and `gpt-5.6-luna` by default — so a client bump -does not quietly turn the feature off. Routed ids (`provider/model`) are never matched: a shadow -call is always a bare native slug, and an explicit routed selection must not be hijacked. +**set** of source-model prefixes — `gpt-5.6-luna` by default — so a client bump does not quietly +turn the feature off. `gpt-5.4-mini` is retired upstream and is no longer a default prefix, but it +remains a valid `sourceModels` entry for anyone still serving 0.144.x clients. Routed ids +(`provider/model`) are never matched: a shadow call is always a bare native slug, and an explicit +routed selection must not be hijacked. ## The problem diff --git a/gui/src/pages/api-keys-panels.tsx b/gui/src/pages/api-keys-panels.tsx index 54dce72098..661dd006e4 100644 --- a/gui/src/pages/api-keys-panels.tsx +++ b/gui/src/pages/api-keys-panels.tsx @@ -319,7 +319,7 @@ export function ApiKeysUsagePanel({ -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\ -H "Content-Type: application/json" \\ -d '{ - "model": "gpt-5.4", + "model": "gpt-5.6-luna", "messages": [{"role": "user", "content": ${sampleInput}}] }'`; @@ -327,7 +327,7 @@ export function ApiKeysUsagePanel({ -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\ -H "Content-Type: application/json" \\ -d '{ - "model": "gpt-5.4", + "model": "gpt-5.6-luna", "input": ${sampleInput} }'`; diff --git a/gui/src/pages/dashboard-overview-sections.tsx b/gui/src/pages/dashboard-overview-sections.tsx index c71687acb9..a62bc47eea 100644 --- a/gui/src/pages/dashboard-overview-sections.tsx +++ b/gui/src/pages/dashboard-overview-sections.tsx @@ -444,7 +444,7 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) { shadowCall, shadowCallSaving, shadowCallHelpTriggerRef, shadowCallHelpOpen, setShadowCallHelpOpen, saveShadowCall, } = d; const visionEnabled = sidecar?.vision.enabled !== false; - const visionModel = visionEnabled ? (sidecar?.vision.model ?? "gpt-5.4-mini") : ""; + const visionModel = visionEnabled ? (sidecar?.vision.model ?? "gpt-5.6-luna") : ""; const persistedVisionReasoning = sidecar?.vision.reasoning ?? "low"; const visionLadder = visionReasoningLadder(models, visionModel); const visionReasoning = clampVisionReasoningToLadder(visionLadder, persistedVisionReasoning); diff --git a/gui/tests/api-access-models.test.ts b/gui/tests/api-access-models.test.ts index 8fb404ebcb..8fe582a117 100644 --- a/gui/tests/api-access-models.test.ts +++ b/gui/tests/api-access-models.test.ts @@ -6,9 +6,9 @@ import { describe("classifyExternalModel", () => { test("keeps bare native OpenAI ids and marks them native via owned_by", () => { - expect(classifyExternalModel({ id: "gpt-5.4", owned_by: "openai" })).toEqual({ - id: "gpt-5.4", - displayName: "gpt-5.4", + expect(classifyExternalModel({ id: "gpt-5.5", owned_by: "openai" })).toEqual({ + id: "gpt-5.5", + displayName: "gpt-5.5", provider: "openai", native: true, custom: false, diff --git a/gui/tests/apikeys-actions.test.tsx b/gui/tests/apikeys-actions.test.tsx index ae3bffecef..02015a3b1d 100644 --- a/gui/tests/apikeys-actions.test.tsx +++ b/gui/tests/apikeys-actions.test.tsx @@ -237,7 +237,7 @@ test("a failed delete keeps the detail pane open", async () => { test("without a fresh key the protocol chips are disabled, not silently passing", async () => { const container = await mount({ - filteredModels: [{ id: "gpt-5.4", displayName: "gpt-5.4", provider: "openai", native: true }], + filteredModels: [{ id: "gpt-5.5", displayName: "gpt-5.5", provider: "openai", native: true }], modelCount: 1, canTestModels: false, }); @@ -286,10 +286,10 @@ test("rotation start, one-time secret, commit, and abort stay explicit", async ( test("a protocol result belongs to its own chip", async () => { const container = await mount({ - filteredModels: [{ id: "gpt-5.4", displayName: "gpt-5.4", provider: "openai", native: true }], + filteredModels: [{ id: "gpt-5.5", displayName: "gpt-5.5", provider: "openai", native: true }], modelCount: 1, canTestModels: true, - modelTests: { "gpt-5.4": { chat: { state: "error", detail: "boom" } } }, + modelTests: { "gpt-5.5": { chat: { state: "error", detail: "boom" } } }, }); const notes = [...container.querySelectorAll(".api-test-note")]; // Exactly one result rendered, announced, and attached to the chat chip only. diff --git a/gui/tests/apikeys-model-test-wire.test.tsx b/gui/tests/apikeys-model-test-wire.test.tsx index 943d4d6f3d..c62cca7e20 100644 --- a/gui/tests/apikeys-model-test-wire.test.tsx +++ b/gui/tests/apikeys-model-test-wire.test.tsx @@ -89,7 +89,7 @@ function installFetch(sent: SentRequest[], dataPlaneStatus = 200): void { const url = String(input); const method = (init?.method ?? "GET").toUpperCase(); if (url.endsWith("/v1/models") && method === "GET") { - return Response.json({ data: [{ id: "gpt-5.4", owned_by: "openai" }] }); + return Response.json({ data: [{ id: "gpt-5.5", owned_by: "openai" }] }); } if (url.endsWith("/api/keys") && method === "GET") return Response.json(KEYS_OK); if (url.endsWith("/api/keys") && method === "POST") return Response.json({ key: ONE_TIME_KEY }); @@ -163,11 +163,11 @@ test("each protocol chip posts its own endpoint and body, carrying the one-time // Each protocol speaks its own wire. A chat body posted at /v1/responses // would be rejected for its shape, not for the key, and the green chip // would then be lying about what it proved. - expect(sent[0]!.body).toMatchObject({ model: "gpt-5.4", input: "ping", stream: false }); + expect(sent[0]!.body).toMatchObject({ model: "gpt-5.5", input: "ping", stream: false }); expect(sent[0]!.body).not.toHaveProperty("messages"); - expect(sent[1]!.body).toMatchObject({ model: "gpt-5.4", max_tokens: 1, stream: false }); + expect(sent[1]!.body).toMatchObject({ model: "gpt-5.5", max_tokens: 1, stream: false }); expect(sent[1]!.body).toHaveProperty("messages"); - expect(sent[2]!.body).toMatchObject({ model: "gpt-5.4", max_tokens: 1 }); + expect(sent[2]!.body).toMatchObject({ model: "gpt-5.5", max_tokens: 1 }); expect(sent[2]!.body).not.toHaveProperty("stream"); // The dedicated header is the only one every data-plane endpoint accepts; diff --git a/gui/tests/apikeys-models-states.test.tsx b/gui/tests/apikeys-models-states.test.tsx index ef98e24fa6..017dba1a71 100644 --- a/gui/tests/apikeys-models-states.test.tsx +++ b/gui/tests/apikeys-models-states.test.tsx @@ -135,11 +135,11 @@ test("an empty catalog says the catalog is empty, with no query in the sentence" test("a query matching nothing names the query, and does not claim the catalog is empty", async () => { const counter = { gets: 0 }; installFetch(() => Response.json({ - data: [{ id: "gpt-5.4", owned_by: "openai" }, { id: "claude/opus-4-6", owned_by: "anthropic" }], + data: [{ id: "gpt-5.5", owned_by: "openai" }, { id: "claude/opus-4-6", owned_by: "anthropic" }], }), counter); const { container, root } = await mountPage(); try { - expect(container.textContent).toContain("gpt-5.4"); + expect(container.textContent).toContain("gpt-5.5"); await typeQuery(container, "nothing-matches-this"); await tick(); @@ -158,7 +158,7 @@ test("a failed cold load offers a retry that really refetches, and no false empt installFetch( () => (fail ? new Response("upstream unavailable", { status: 503 }) - : Response.json({ data: [{ id: "gpt-5.4", owned_by: "openai" }] })), + : Response.json({ data: [{ id: "gpt-5.5", owned_by: "openai" }] })), counter, ); const { container, root } = await mountPage(); @@ -176,7 +176,7 @@ test("a failed cold load offers a retry that really refetches, and no false empt await tick(); expect(counter.gets).toBe(2); - expect(container.textContent).toContain("gpt-5.4"); + expect(container.textContent).toContain("gpt-5.5"); expect(container.textContent).not.toContain("Could not load the external model catalog."); } finally { await act(async () => { root.unmount(); }); diff --git a/gui/tests/client-config-panel.test.tsx b/gui/tests/client-config-panel.test.tsx index 37f2df193b..c04edc1bae 100644 --- a/gui/tests/client-config-panel.test.tsx +++ b/gui/tests/client-config-panel.test.tsx @@ -40,7 +40,7 @@ const OPENCODE_ENVELOPE_BASE = { npm: "@ai-sdk/openai-compatible", name: "OpenCodex", options: { baseURL: "http://127.0.0.1:10100/v1", apiKey: "{env:OPENCODEX_OPENCODE_API_KEY}" }, - models: { "gpt-5.4": { name: "gpt-5.4 (native)" } }, + models: { "gpt-5.5": { name: "gpt-5.5 (native)" } }, }, }, }, @@ -57,7 +57,7 @@ const PI_ENVELOPE_BASE = { format: "json", mediaType: "application/json", // Pi keys its models as an ARRAY — the shape swap is what proves a real refetch. - config: { providers: { opencodex: { models: [{ id: "gpt-5.4" }, { id: "claude-sonnet-4-6" }] } } }, + config: { providers: { opencodex: { models: [{ id: "gpt-5.5" }, { id: "claude-sonnet-4-6" }] } } }, }; /** diff --git a/gui/tests/subagents-fallback.test.tsx b/gui/tests/subagents-fallback.test.tsx index bf838d63ca..71001416ed 100644 --- a/gui/tests/subagents-fallback.test.tsx +++ b/gui/tests/subagents-fallback.test.tsx @@ -99,7 +99,7 @@ beforeEach(() => { model: preferredModel, effort: null, available: [ - { provider: "openai", model: "gpt-5.4", namespaced: "gpt-5.4" }, + { provider: "openai", model: "gpt-5.5", namespaced: "gpt-5.5" }, { provider: "anthropic", model: "claude-sonnet-4-6", namespaced: "anthropic/claude-sonnet-4-6" }, ], efforts: [], @@ -735,7 +735,7 @@ const compatibilityCases: Array<{ keepNative: boolean; warning: boolean; }> = [ - { name: "native preferred model", model: "gpt-5.4", enabled: true, mode: "v2", keepNative: false, warning: false }, + { name: "native preferred model", model: "gpt-5.5", enabled: true, mode: "v2", keepNative: false, warning: false }, { name: "routed preferred model on the default surface", model: "anthropic/claude-sonnet-4-6", enabled: false, mode: "default", keepNative: false, warning: true }, { name: "routed preferred model on V1", model: "anthropic/claude-sonnet-4-6", enabled: false, mode: "v1", keepNative: false, warning: false }, { name: "forced V2 preserving native V1 with global V2 disabled", model: "anthropic/claude-sonnet-4-6", enabled: false, mode: "v2", keepNative: true, warning: false }, diff --git a/gui/tests/vision-reasoning-contract.test.ts b/gui/tests/vision-reasoning-contract.test.ts index ca6d2699c5..40fb449b90 100644 --- a/gui/tests/vision-reasoning-contract.test.ts +++ b/gui/tests/vision-reasoning-contract.test.ts @@ -12,15 +12,15 @@ import { test("vision reasoning uses advertised model ladders and clamps unsupported persisted values", () => { const models: ModelInfo[] = [ { id: "gpt-5.6-luna", provider: "openai", namespaced: "gpt-5.6-luna", reasoningEfforts: ["low", "medium", "high", "xhigh", "max"] }, - { id: "gpt-5.4-mini", provider: "openai", namespaced: "gpt-5.4-mini", reasoningEfforts: ["low", "medium", "high", "xhigh"] }, + { id: "gpt-5.5", provider: "openai", namespaced: "gpt-5.5", reasoningEfforts: ["low", "medium", "high", "xhigh"] }, ]; expect(visionReasoningLadder(models, "gpt-5.6-luna")).toEqual(VISION_REASONING_LEVELS); - const mini = visionReasoningLadder(models, "gpt-5.4-mini"); - expect(mini).toEqual(["low", "medium", "high", "xhigh"]); - expect(clampVisionReasoningToLadder(mini, "max")).toBe("xhigh"); - expect(clampVisionReasoningToLadder(mini, "high")).toBe("high"); - expect(visionReasoningOptionsFor(mini, "max")).toEqual(mini); + const shorter = visionReasoningLadder(models, "gpt-5.5"); + expect(shorter).toEqual(["low", "medium", "high", "xhigh"]); + expect(clampVisionReasoningToLadder(shorter, "max")).toBe("xhigh"); + expect(clampVisionReasoningToLadder(shorter, "high")).toBe("high"); + expect(visionReasoningOptionsFor(shorter, "max")).toEqual(shorter); }); test("vision reasoning clamp matches the server for non-prefix ladders", () => { diff --git a/gui/tests/vision-sidecar-dashboard.test.tsx b/gui/tests/vision-sidecar-dashboard.test.tsx index fd41cc89fd..5a2c7cec3b 100644 --- a/gui/tests/vision-sidecar-dashboard.test.tsx +++ b/gui/tests/vision-sidecar-dashboard.test.tsx @@ -36,7 +36,7 @@ const initialSidecar: SidecarData = { }, visionModels: [ { value: "gpt-5.6-luna", label: "gpt-5.6-luna", backend: "openai", baseline: true }, - { value: "gpt-5.4-mini", label: "gpt-5.4-mini", backend: "openai", baseline: true }, + { value: "gpt-5.6-terra", label: "gpt-5.6-terra", backend: "openai", baseline: true }, ], }; @@ -104,7 +104,7 @@ function harness(sidecar: SidecarData = initialSidecar) { visionModels: sidecar.visionModels ?? [], models: [ { id: "gpt-5.6-luna", provider: "openai", namespaced: "gpt-5.6-luna", reasoningEfforts: ["low", "medium", "high", "xhigh", "max"] }, - { id: "gpt-5.4-mini", provider: "openai", namespaced: "gpt-5.4-mini", reasoningEfforts: ["low", "medium", "high", "xhigh", "max"] }, + { id: "gpt-5.6-terra", provider: "openai", namespaced: "gpt-5.6-terra", reasoningEfforts: ["low", "medium", "high", "xhigh", "max"] }, ], saveSidecar, shadowCall: { enabled: false, model: "" }, @@ -304,11 +304,11 @@ test("choosing a model from Off sends enabled:true plus that model and backend", const { d, patches } = harness({ ...initialSidecar, vision: { ...initialSidecar.vision, enabled: false } }); await mount(d); await act(async () => { modelTrigger().click(); }); - const next = pickOption("gpt-5.4-mini"); + const next = pickOption("gpt-5.6-terra"); expect(next).toBeTruthy(); await act(async () => { next!.click(); }); expect(patches).toEqual([ - { vision: { model: "gpt-5.4-mini", backend: "openai", reasoning: "medium", enabled: true } }, + { vision: { model: "gpt-5.6-terra", backend: "openai", reasoning: "medium", enabled: true } }, ]); }); @@ -369,12 +369,12 @@ test("model and reasoning saves still omit enabled, limit, and timeout", async ( ) as HTMLButtonElement; await act(async () => { modelTrigger.click(); }); - const nextModel = pickOption("gpt-5.4-mini"); + const nextModel = pickOption("gpt-5.6-terra"); expect(nextModel).toBeTruthy(); await act(async () => { nextModel!.click(); }); expect(patches).toHaveLength(1); expect(patches[0]).toEqual({ - vision: { model: "gpt-5.4-mini", backend: "openai", reasoning: "medium" }, + vision: { model: "gpt-5.6-terra", backend: "openai", reasoning: "medium" }, }); assertVisionControlFieldsOmitted(patches[0]!); diff --git a/scripts/release-notes.ts b/scripts/release-notes.ts index df68ed2b07..76a364a980 100644 --- a/scripts/release-notes.ts +++ b/scripts/release-notes.ts @@ -1134,7 +1134,7 @@ async function main(argv: string[]): Promise { console.error("✗ polish --base-url must be https: or a loopback http: host (the API key must not travel in plaintext)"); process.exit(1); } - const model = args.get("model") ?? process.env.OPENAI_MODEL ?? "gpt-5.4"; + const model = args.get("model") ?? process.env.OPENAI_MODEL ?? "gpt-5.6-luna"; if (!(await Bun.file(inputPath).exists())) { console.error(`✗ polish input not found: ${inputPath}`); diff --git a/src/cli/config-command.ts b/src/cli/config-command.ts index b06ef38d15..a063602b59 100644 --- a/src/cli/config-command.ts +++ b/src/cli/config-command.ts @@ -144,8 +144,8 @@ function normalizeVisionConfig(config: OcxConfig): OcxConfig { const vision = config.visionSidecar; if (!vision || vision.reasoning === undefined) return config; // Keep CLI import/set semantics aligned with the execution path: an omitted or blank model means - // the bounded OpenAI vision default, gpt-5.4-mini, not the Dashboard's web-search default. - const model = vision.model || "gpt-5.4-mini"; + // the bounded OpenAI vision default, gpt-5.6-luna, not the Dashboard's web-search default. + const model = vision.model || "gpt-5.6-luna"; const normalized = normalizeVisionReasoningForModel(model, vision.reasoning); if (normalized === undefined) delete vision.reasoning; else vision.reasoning = normalized; diff --git a/src/codex/catalog/effort.ts b/src/codex/catalog/effort.ts index f54792b2ec..7aa0f006dc 100644 --- a/src/codex/catalog/effort.ts +++ b/src/codex/catalog/effort.ts @@ -59,9 +59,10 @@ export function nativeEffortClamp(slug: string, effort: string | undefined): str : []; if (levels.length === 0) { // Not snapshot-covered. gpt-5.6 natives have a REAL max rung (ensureGpt56ReasoningLevels - // restores it even off-snapshot) -> never clamp. Every other bare native (gpt-5.5/5.4/ - // 5.4-mini/5.3-codex-spark and future old-ladder slugs) really stops at xhigh — the - // ChatGPT backend error names exactly none..xhigh — so clamp the synthetic top tier. + // restores it even off-snapshot) -> never clamp. Every other bare native (gpt-5.5, + // 5.3-codex-spark, a retired slug a client still asks for, and future old-ladder slugs) + // really stops at xhigh — the ChatGPT backend error names exactly none..xhigh — so clamp + // the synthetic top tier. return isGpt56NativeSlug(slug) ? null : "xhigh"; } const supported = levels.flatMap(l => typeof l.effort === "string" ? [l.effort] : []); diff --git a/src/codex/catalog/metadata.ts b/src/codex/catalog/metadata.ts index f239ce48b1..d3ebf109a0 100644 --- a/src/codex/catalog/metadata.ts +++ b/src/codex/catalog/metadata.ts @@ -120,8 +120,7 @@ export function isUnsupportedOpenAiNativeSlug(slug: string): boolean { * * This is an OPERATING CAP, not the hard ceiling — the same shape upstream uses. The live * catalog reports `context_window: 272000` against a `max_context_window: 872000` for these - * slugs, and gpt-5.4 runs 272,000 against 1,000,000: the advertised window is always well - * inside what the model can take. + * slugs: the advertised window is always well inside what the model can take. * * The hard ceiling here was measured on 2026-08-17 against a real Codex-login account: * `POST /backend-api/codex/responses` admitted 921,508 input tokens and refused 922,013 with @@ -162,7 +161,6 @@ const NATIVE_GPT56_FAMILY = new Set([ export const NATIVE_OPENAI_CONTEXT_OVERRIDES: Record = { "gpt-5.5": { contextWindow: 272_000, maxContextWindow: 272_000 }, - "gpt-5.4": { contextWindow: 1_000_000, maxContextWindow: 1_000_000 }, "gpt-5.3-codex-spark": { contextWindow: 100_000, maxContextWindow: 100_000 }, "gpt-5.6-sol": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS }, "gpt-5.6-terra": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS }, @@ -537,7 +535,7 @@ function upstreamNativeEntryForSlug(slug: string): RawEntry | undefined { const sourceSlug = nativeOpenAiCapabilitySourceSlug(slug); // A self-described native returns its OWN pinned row; the alias-cloning branch below stays // reserved for slugs that genuinely borrow another model's identity. The allowlist is explicit - // rather than "has a pinned entry", which would also admit gpt-5.5/gpt-5.4/gpt-5.4-mini into + // rather than "has a pinned entry", which would also admit gpt-5.5/gpt-5.2/codex-auto-review into // the sync-replacement authority this map carries. if (!sourceSlug.startsWith("gpt-5.6-") && !SELF_DESCRIBED_NATIVE_OPENAI_MODELS.has(slug)) { return undefined; diff --git a/src/codex/catalog/native-models.ts b/src/codex/catalog/native-models.ts index 691849fbdd..fb14a64911 100644 --- a/src/codex/catalog/native-models.ts +++ b/src/codex/catalog/native-models.ts @@ -72,9 +72,11 @@ const NATIVE_OPENAI_CAPABILITY_SOURCES: Readonly> = Objec * * Membership authorizes `upstreamNativeEntryForSlug` to return the pinned entry directly. It is * an explicit list, not a structural `PINNED_UPSTREAM_MODELS.has(slug)` predicate: the pin also - * holds `gpt-5.5`, `gpt-5.4` and `gpt-5.4-mini`, and admitting those into + * holds `gpt-5.5`, `gpt-5.2` and `codex-auto-review`, and admitting those into * `UPSTREAM_NATIVE_ENTRIES` would newly authorize replacing their persisted catalog rows during - * sync — an invariant that map's own comment reserves for the GPT-5.6 family. + * sync — an invariant that map's own comment reserves for the GPT-5.6 family. The snapshot + * keeps rows this runtime does not expose, which is exactly why presence in the pin cannot be + * the predicate: `gpt-5.4` and `gpt-5.4-mini` are still pinned after their retirement. */ export const SELF_DESCRIBED_NATIVE_OPENAI_MODELS: ReadonlySet = new Set([ NATIVE_GPT6_ASTRA_MODEL, @@ -153,7 +155,7 @@ export function nativeOpenAiAliasPresentation(slug: string): { displayName: stri * Devlog: 260816_codexrs_multiagent_v2_and_history_perf/011 §4-bis. */ export const NATIVE_OPENAI_MODELS = [ - "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark", + "gpt-5.5", "gpt-5.3-codex-spark", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", NATIVE_DAYBREAK_BLUE_MODEL, NATIVE_GPT6_ASTRA_MODEL, @@ -172,7 +174,7 @@ export const SUPPORTED_NATIVE_OPENAI_SLUGS = new Set(NATIVE_OPENAI_MODELS); * flipped false — letting a drain silently rewrite the operator's configured subagent model. * * It is an explicit list rather than `SUPPORTED_NATIVE_OPENAI_SLUGS`, which would have widened - * the sentinel to `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini` and `gpt-5.3-codex-spark` as well. Those + * the sentinel to `gpt-5.5` and `gpt-5.3-codex-spark` as well. Those * models were never covered, and widening would turn "fell back and answered" into a * maintenance error for the most commonly configured fallback slug in the repo. Membership is * the set the drain behaviour was actually reasoned about: the account-gated natives plus the diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index 6ddf6512e6..06747a8553 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -518,7 +518,8 @@ export function applyNativeOpenAiContextOverride(entry: RawEntry, limits?: Nativ } // providerContextCaps.openai is a ceiling for native OpenAI rows regardless of where the // advertised window came from (#1430): preserved rows without a hardcoded override (e.g. - // gpt-5.4-mini) must stay under the cap too, and auto-compaction follows the capped window. + // gpt-5.3-codex-spark) must stay under the cap too, and auto-compaction follows the capped + // window. // The per-model window narrows the same rows for the same reason. const currentContext = typeof entry.context_window === "number" ? entry.context_window : undefined; const cappedContext = narrowNativeMaxContextWindow(nativeSlug, currentContext, limits); diff --git a/src/codex/catalog/sync.ts b/src/codex/catalog/sync.ts index 074d9ddef0..2f756663ad 100644 --- a/src/codex/catalog/sync.ts +++ b/src/codex/catalog/sync.ts @@ -260,7 +260,7 @@ export function finishUpstreamNativeEntry(clone: RawEntry, priority: number, con if (priority !== 9) clone.priority = priority; applyNativeOpenAiContextOverride(clone, contextCap); // GPT-5.6 natives keep their exact upstream ladders (e.g. luna has max but no ultra). - // Older natives (gpt-5.5 / 5.4 / 5.4-mini / 5.3-codex-spark) get mock max + ultra + // Older natives (gpt-5.5 / 5.3-codex-spark) get mock max + ultra // (wire-clamped to xhigh). Ultra is always advertised regardless of v2 toggle. if (!isGpt56NativeSlug(String(clone.slug ?? ""))) ensureUltraReasoningLevel(clone); return ensureStrictCatalogFields(normalizeServiceTiers(clone)); diff --git a/src/codex/warmup.ts b/src/codex/warmup.ts index 2fbd87d582..7be16980c6 100644 --- a/src/codex/warmup.ts +++ b/src/codex/warmup.ts @@ -27,8 +27,8 @@ export interface CodexWarmupOptions { } const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses"; -const DEFAULT_MODEL = "gpt-5.4-mini"; -const FALLBACK_MODELS = ["gpt-5.5", "gpt-5.6-luna"]; +const DEFAULT_MODEL = "gpt-5.6-luna"; +const FALLBACK_MODELS = ["gpt-5.5"]; const isRetryableWarmupStatus = (status?: number): boolean => status === 400 || status === 404; const DEFAULT_TIMEOUT_MS = 30_000; const MAX_TIMEOUT_MS = 0x7fff_ffff; diff --git a/src/oauth/index.ts b/src/oauth/index.ts index f98854cc3e..866496cd84 100644 --- a/src/oauth/index.ts +++ b/src/oauth/index.ts @@ -328,7 +328,7 @@ export const OAUTH_PROVIDERS: Record = { login: (ctrl, opts) => loginChatGPT(ctrl, { forceLogin: opts?.forceLogin, flow: opts?.flow }), refresh: (rt) => refreshChatGPTToken(rt), providerConfig: { adapter: "openai-responses", baseUrl: "https://chatgpt.com/backend-api/codex", authMode: "forward" as const }, - defaultModel: "gpt-5.4", + defaultModel: "gpt-5.6-luna", }, }; diff --git a/src/oauth/token-guardian.ts b/src/oauth/token-guardian.ts index ff87c1f532..6815e83a3b 100644 --- a/src/oauth/token-guardian.ts +++ b/src/oauth/token-guardian.ts @@ -52,7 +52,7 @@ const DEFAULTS = { failureBackoffBaseSeconds: 300, failureBackoffMaxSeconds: 3600, codexWarmupMaxAgeSeconds: 691_200, // 8d — matches Codex managed-auth last_refresh cadence. - codexWarmupModel: "gpt-5.4-mini", + codexWarmupModel: "gpt-5.6-luna", }; interface BackoffEntry { diff --git a/src/server/index.ts b/src/server/index.ts index e1aa2be2bd..6c74f1e704 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -683,8 +683,10 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server= SIDECAR_MIGRATION_CUTOFF) { @@ -697,6 +699,10 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server; /** Hosted image_generation tool config stashed for the image bridge sidecar (see src/images). */ diff --git a/src/types/tools.ts b/src/types/tools.ts index fcc0689819..7a4cdc4ebb 100644 --- a/src/types/tools.ts +++ b/src/types/tools.ts @@ -13,7 +13,7 @@ export interface OcxTool { loadedFromToolSearch?: boolean; /** Cursor-only synthetic exact-match edit tool; never inferred from the wire name. */ cursorStructuredEdit?: true; - /** Synthetic web_search tool: the model's call is executed by the gpt-5.4-mini sidecar, not relayed to Codex. */ + /** Synthetic web_search tool: the model's call is executed by the gpt-5.6-luna sidecar, not relayed to Codex. */ webSearch?: boolean; /** Synthetic image_gen tool: the model's call is executed by the xAI image bridge sidecar, not relayed to Codex. */ imageGeneration?: boolean; diff --git a/src/vision/plan.ts b/src/vision/plan.ts index 3cb0b2f3e2..408c88533d 100644 --- a/src/vision/plan.ts +++ b/src/vision/plan.ts @@ -11,7 +11,7 @@ import { resolveSidecarAuth } from "../sidecar/auth"; import { DEFAULT_VISION_TIMEOUT_MS, MAX_VISION_TIMEOUT_MS, MIN_VISION_TIMEOUT_MS } from "./timeout-bounds"; import { carriesImages } from "./image-rewrite"; -const DEFAULT_VISION_MODEL = "gpt-5.4-mini"; +const DEFAULT_VISION_MODEL = "gpt-5.6-luna"; const DEFAULT_ANTHROPIC_VISION_MODEL = "claude-sonnet-5"; const DEFAULT_REASONING: VisionReasoningEffort = "low"; export const DEFAULT_MAX_DESCRIPTIONS_PER_TURN = 8; diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index c6b35fbad8..e0bc5c856f 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -126,7 +126,7 @@ this document owns is which module holds which area and what invariant that area | System | `POST /api/system/restart` restarts the proxy in place. Local CLI/tray callers first attest the exact runtime PID and port, then send a process-scoped HMAC capability bound to that method, path, PID, and port; the capability authorizes no other management route and is invalid after replacement. The caller observes one absolute deadline and accepts success only after a different runtime PID is healthy on the same port. `GET /api/system/health` is the authenticated scalar-only identity used by shared-plane Dashboard status and restart reconnect polling; it does not widen a Remote Hub management ingress to unauthenticated `/healthz`. `GET /api/system/memory` — service-process runtime/memory identity (pid, Bun version/revision, optional `bunRuntimeSource` provenance, platform, RSS/heap/external/ArrayBuffers scalars, observed memory = max(RSS, external, ArrayBuffers), `bun:jsc` heap context, streamMode + eager-relay gate decision, watchdog snapshot sliced to the last 60 samples) plus privacy-safe `appOwnedBytes` retained-store totals/counters under static store ids. Its response-state block also reports spill-write `initial`/`healthy`/`degraded` status, a consecutive-failure streak, fixed error class, and failure/success timestamps. A successful publication clears the streak in the same process; raw error text and paths never enter this surface. Scalar-only payload; dashboard/admin callers use the standard management gate, while `ocx doctor` may use only the exact process-scoped local-read capability. It must never move to unauthenticated `/healthz`. | | Stop | `POST /api/stop` — restore native Codex, stop any installed service, and exit the proxy. | | Diagnostics/sync | `src/server/management/config-routes.ts` — `GET /api/diagnostics/project-config` reports project-level Codex config that bypasses managed routing; `POST /api/sync` re-runs catalog/config sync. The diagnostic reports the bypass; it does not rewrite the project file. | -| Sidecar/shadow-call settings | `src/server/management/config-routes.ts` — `GET/PUT /api/sidecar-settings` and `GET/PUT /api/shadow-call-settings`. PUT accepts model and backend (web-search union: openai/anthropic/xai/gemini/exa; xAI is live through stored Grok OAuth, while Gemini/Exa remain inert until their executors ship) plus validated `webSearch.xSearch`, optional `webSearch.exaApiKey` (write/clear only — never echoed by GET or the PUT response; redact.ts strips it from logs), `webSearch.reasoning`, `vision.reasoning`, `vision.enabled`, `vision.maxDescriptionsPerTurn`, and `vision.timeoutMs`; the read and PUT-response payload reports model, backend, reasoning, enabled, the vision per-turn limit, and timeout. `timeoutMs` is validated against the runtime integer bounds in `src/vision/timeout-bounds.ts`. Provider/OAuth credentials live in their stores; `exaApiKey` is the one sidecar-owned secret and follows the write-only contract above. Both shadow-call responses also report the resolved `sourceModels` — the prefixes the runtime actually intercepts (`src/lib/shadow-call.ts`, default `gpt-5.4-mini` + `gpt-5.6-luna`), so no client hard-codes a helper slug that a Codex release can invalidate. | +| Sidecar/shadow-call settings | `src/server/management/config-routes.ts` — `GET/PUT /api/sidecar-settings` and `GET/PUT /api/shadow-call-settings`. PUT accepts model and backend (web-search union: openai/anthropic/xai/gemini/exa; xAI is live through stored Grok OAuth, while Gemini/Exa remain inert until their executors ship) plus validated `webSearch.xSearch`, optional `webSearch.exaApiKey` (write/clear only — never echoed by GET or the PUT response; redact.ts strips it from logs), `webSearch.reasoning`, `vision.reasoning`, `vision.enabled`, `vision.maxDescriptionsPerTurn`, and `vision.timeoutMs`; the read and PUT-response payload reports model, backend, reasoning, enabled, the vision per-turn limit, and timeout. `timeoutMs` is validated against the runtime integer bounds in `src/vision/timeout-bounds.ts`. Provider/OAuth credentials live in their stores; `exaApiKey` is the one sidecar-owned secret and follows the write-only contract above. Both shadow-call responses also report the resolved `sourceModels` — the prefixes the runtime actually intercepts (`src/lib/shadow-call.ts`, default `gpt-5.6-luna`; the retired `gpt-5.4-mini` stays available as an explicit `sourceModels` entry for 0.144.x clients), so no client hard-codes a helper slug that a Codex release can invalidate. | | Storage | `src/server/management/logs-usage-routes.ts` — `GET /api/storage`, `POST /api/storage/cleanup/preview` and `/api/storage/cleanup`, `GET /api/storage/trash`, `POST /api/storage/trash/restore`, and `GET/PUT /api/storage/cleanup-policy` plus `POST /api/storage/cleanup-policy/run`. `GET /api/storage/cleanup-policy/test-stream` and `GET /api/storage/trash/restore/test-stream` exist for progress-stream testing. Cleanup takes an explicit `mode`: `quarantine` moves to trash and is restorable, `permanent` is not. The caller must name the mode — there is no default that silently deletes. | | Provider quotas and tests | `src/server/management/provider-routes.ts` — `GET /api/provider-quotas`, `POST /api/providers/test`, `GET/PUT /api/provider-context-caps`, `GET /api/provider-presets`. A quota read may be served from cache or force-refreshed; absent quota data is reported as unknown rather than as a measured zero. | | Models and visibility | `src/server/management/model-routes.ts` — `GET /api/models`, `PUT /api/disabled-models`, `PUT /api/model-visibility`, `PUT /api/selected-models`, `GET/POST /api/custom-models`. Visibility writes trigger catalog sync through the owning server path. | diff --git a/structure/ops/service-and-sidecars.md b/structure/ops/service-and-sidecars.md index 31319b28de..dd5f58e345 100644 --- a/structure/ops/service-and-sidecars.md +++ b/structure/ops/service-and-sidecars.md @@ -49,7 +49,7 @@ Gemini and Exa remain inert until their executors ship. Selection differs per si | Sidecar | Backend selection | Default model | Activation | | --- | --- | --- | --- | | `web-search/` | Explicit configuration only: unset always resolves to the OpenAI forward path. No backend — Anthropic or otherwise — is auto-selected from credential availability (doing so once sent OpenAI model ids to the Anthropic API). Explicit xAI requires usable stored Grok OAuth and may add hosted `x_search`; explicit Gemini/Exa remain fail-closed until their executors land. | `gpt-5.6-luna` (OpenAI), `claude-sonnet-5` (Anthropic), `grok-4.6` (xAI) | Hosted `web_search` requested by a non-passthrough routed model. | -| `vision/` | Explicit configuration wins for both backends. Only an unset backend auto-selects: Anthropic when a usable Anthropic OAuth provider exists, otherwise the OpenAI forward authority. An explicitly selected backend whose authority is unavailable produces no plan rather than falling back. | `claude-sonnet-5` (Anthropic), `gpt-5.4-mini` (OpenAI) | Input contains images for a model listed in `noVisionModels`. | +| `vision/` | Explicit configuration wins for both backends. Only an unset backend auto-selects: Anthropic when a usable Anthropic OAuth provider exists, otherwise the OpenAI forward authority. An explicitly selected backend whose authority is unavailable produces no plan rather than falling back. | `claude-sonnet-5` (Anthropic), `gpt-5.6-luna` (OpenAI) | Input contains images for a model listed in `noVisionModels`. | The asymmetry is in the unset case only: vision may describe an image with whichever model can see it, while a hosted search tool is tied to a provider-specific tool contract, so search never infers diff --git a/tests/claude-integration/claude-context-windows.test.ts b/tests/claude-integration/claude-context-windows.test.ts index d411fce9aa..f22c418cb2 100644 --- a/tests/claude-integration/claude-context-windows.test.ts +++ b/tests/claude-integration/claude-context-windows.test.ts @@ -21,12 +21,15 @@ describe("claude context-window map (devlog 260712 B2)", () => { }); test("registers native slugs (bare + desktop alias + legacy alias)", () => { - const map = buildClaudeContextWindows(["gpt-5.6-sol", "gpt-5.4"], []); - // Authoritative native overrides: gpt-5.6 natives follow Codex 272k, gpt-5.4 native 1M. + const map = buildClaudeContextWindows(["gpt-5.6-sol", "gpt-5.3-codex-spark", "gpt-5.4"], []); + // Authoritative native overrides: gpt-5.6 natives follow Codex 272k, spark 100k. + // gpt-5.4 was the only 1M native override; that window is gone, so a retired + // slug passed here does not register. expect(map["gpt-5.6-sol"]).toBe(272_000); expect(map[desktop3pAlias("native", "gpt-5.6-sol")]).toBe(272_000); expect(map["claude-ocx-native--gpt-5.6-sol"]).toBe(272_000); - expect(map["gpt-5.4"]).toBe(1_000_000); + expect(map["gpt-5.3-codex-spark"]).toBe(100_000); + expect(map["gpt-5.4"]).toBeUndefined(); }); test("first-wins on alias collisions (registry policy)", () => { diff --git a/tests/claude-integration/claude-inbound.test.ts b/tests/claude-integration/claude-inbound.test.ts index a89f9c928b..3db1a4a627 100644 --- a/tests/claude-integration/claude-inbound.test.ts +++ b/tests/claude-integration/claude-inbound.test.ts @@ -739,7 +739,7 @@ describe("#3922 translated tools carry the source strict intent", () => { additionalProperties: false, }; const request = (tool: Record) => ({ - model: "openai/gpt-5.4", + model: "openai/gpt-5.6-luna", max_tokens: 32, messages: [{ role: "user", content: "Run a local agent." }], tools: [tool], @@ -795,7 +795,7 @@ describe("#3922 translated tools carry the source strict intent", () => { [agent({ strict: false }), false], ] as const) { const expectedSchema = structuredClone(tool.input_schema); - const parsed = parseRequest({ ...anthropicToResponsesBody(request(tool)), model: "gpt-5.4" }); + const parsed = parseRequest({ ...anthropicToResponsesBody(request(tool)), model: "gpt-5.6-luna" }); expect(parsed.context.tools?.[0]?.strict).toBe(expected); const outbound = await adapter.buildRequest(parsed); diff --git a/tests/claude-integration/claude-model-info.test.ts b/tests/claude-integration/claude-model-info.test.ts index 34a49be617..b8648a0a68 100644 --- a/tests/claude-integration/claude-model-info.test.ts +++ b/tests/claude-integration/claude-model-info.test.ts @@ -61,7 +61,7 @@ describe("anthropic-flavor ModelInfo discovery entries (devlog 130 B4b)", () => }); test("native effective ladder only advertises clamp-identity rungs (audit R4#1)", () => { - for (const slug of ["gpt-5.5", "gpt-5.4", "gpt-5.6-sol"]) { + for (const slug of ["gpt-5.5", "gpt-5.3-codex-spark", "gpt-5.6-sol"]) { for (const rung of nativeEffectiveLadder(slug)) { expect(rung).not.toBe("ultra"); const clamped = nativeEffortClamp(slug, rung); @@ -96,13 +96,11 @@ describe("anthropic-flavor ModelInfo discovery entries (devlog 130 B4b)", () => expect(String(lunaBase)).toBeDefined(); }); - test("[1m] variants cover 1M NATIVES too (audit R1#1) — and skip sub-1M natives", () => { - // gpt-5.4 is the only authoritative 1M native. gpt-5.6-sol advertises 922k — a cap held - // under its measured ceiling — so it stays out, and so does gpt-5.5 at 272k. - const infos = buildAnthropicModelInfos(["gpt-5.4", "gpt-5.6-sol", "gpt-5.5"], []); - const variants = infos.filter(i => i.id.endsWith("[1m]")); - expect(variants).toHaveLength(1); - expect(variants[0]!.display_name.includes("gpt-5.4")).toBe(true); + test("[1m] variants skip natives — none have a >=1M window after gpt-5.4 retirement", () => { + // gpt-5.4 was the only authoritative 1M native; that override is gone. gpt-5.6-sol + // advertises 922k under its measured ceiling, gpt-5.5 is 272k, spark is 100k. + const infos = buildAnthropicModelInfos(["gpt-5.6-sol", "gpt-5.5", "gpt-5.3-codex-spark"], []); + expect(infos.filter(i => i.id.endsWith("[1m]"))).toHaveLength(0); }); test("native OpenAI rows carry max_input_tokens so Claude Code skips the 200k fallback (#1218)", () => { @@ -146,17 +144,15 @@ describe("anthropic-flavor ModelInfo discovery entries (devlog 130 B4b)", () => test("no [1m] rows for sub-1M models, even with auto-context enabled (#854 contract)", () => { const auto = { enabled: true, compactWindow: 350_000 }; - const infos = buildAnthropicModelInfos(["gpt-5.4", "gpt-5.5"], [ + const infos = buildAnthropicModelInfos(["gpt-5.5", "gpt-5.6-sol"], [ { provider: "mock", id: "small-model", contextWindow: 128_000 }, { provider: "mock", id: "mid-model", contextWindow: 300_000 }, // < compact window: unsafe, no row ], auto); const variants = infos.filter(i => i.id.endsWith("[1m]")); - // The [1m] marker makes Claude Code account 1e6 tokens: only the - // authoritative 1M model may carry it — never the 272K gpt-5.5 route. - expect(variants).toHaveLength(1); - expect(variants[0]!.display_name.includes("gpt-5.4")).toBe(true); - expect(variants[0]!.display_name.endsWith("· 1M")).toBe(true); - expect(variants[0]!.max_input_tokens).toBe(1_000_000); + // The [1m] marker makes Claude Code account 1e6 tokens. No surviving native + // is >=1M, and auto-context must not mint the marker for 272k natives or + // sub-compact-window mocks (#854). + expect(variants).toHaveLength(0); }); test("auto-context never widens anthropic passthrough rows (audit 021 #3)", () => { diff --git a/tests/claude-integration/claude-models-discovery.test.ts b/tests/claude-integration/claude-models-discovery.test.ts index 608a1ffa80..5c29fd7ef0 100644 --- a/tests/claude-integration/claude-models-discovery.test.ts +++ b/tests/claude-integration/claude-models-discovery.test.ts @@ -325,6 +325,10 @@ test("Codex discovery restores account rows for supported natives hidden on disk expect(listCatalogNativeSlugs()).toContain("gpt-5.5"); expect(listCatalogNativeSlugs()).not.toContain("gpt-99-internal"); expect(listCatalogNativeSlugs()).not.toContain("provider/gpt-5.5"); + // Retired slugs may still sit in a custom catalog or the upstream pin; membership + // does not follow either of those. + expect(listCatalogNativeSlugs()).not.toContain("gpt-5.4"); + expect(listCatalogNativeSlugs()).not.toContain("gpt-5.4-mini"); expect(visibleNativeSlugs(config)).toContain("gpt-5.5"); expect(visibleNativeSlugs({ ...config, disabledModels: ["gpt-5.5"] })).not.toContain("gpt-5.5"); @@ -332,6 +336,7 @@ test("Codex discovery restores account rows for supported natives hidden on disk try { const plain = await fetch(new URL("/v1/models", server.url)) .then(response => response.json()) as { data: Array<{ id: string }> }; + expect(plain.data.some(model => model.id === "gpt-5.4")).toBe(false); expect(plain.data.some(model => model.id === "gpt-5.4-mini")).toBe(false); const catalog = await fetch(new URL("/v1/models?client_version=1.0.0", server.url)) @@ -344,10 +349,11 @@ test("Codex discovery restores account rows for supported natives hidden on disk } config.codexAccountNamespaces = { team: "@main" }; - config.disabledModels = ["gpt-5.4"]; + config.disabledModels = ["gpt-5.6-sol"]; saveConfig(config); resetCatalogRuntimeStateForTests(); expect(visibleNativeSlugs(config)).toContain("gpt-5.5"); + expect(visibleNativeSlugs(config)).not.toContain("gpt-5.6-sol"); expect(visibleNativeSlugs(config)).not.toContain("gpt-5.4"); server = startServer(0); try { @@ -362,10 +368,10 @@ test("Codex discovery restores account rows for supported natives hidden on disk expect(plain.data.some(model => model.id === "team/gpt-5.4")).toBe(false); // Activating account selectors makes both bare and qualified discovery mirror the complete // enabled supported set, even when a partial custom catalog omitted this native. - expect(plain.data.find(model => model.id === "gpt-5.4-mini")?.reasoning_efforts) + expect(plain.data.find(model => model.id === "gpt-5.6-luna")?.reasoning_efforts) .toBeArray(); - expect(plain.data.find(model => model.id === "team/gpt-5.4-mini")?.reasoning_efforts) - .toEqual(plain.data.find(model => model.id === "gpt-5.4-mini")?.reasoning_efforts); + expect(plain.data.find(model => model.id === "team/gpt-5.6-luna")?.reasoning_efforts) + .toEqual(plain.data.find(model => model.id === "gpt-5.6-luna")?.reasoning_efforts); const catalog = await fetch(new URL("/v1/models?client_version=1.0.0", server.url)) .then(response => response.json()) as { @@ -380,9 +386,11 @@ test("Codex discovery restores account rows for supported natives hidden on disk visibility: "list", opencodex_catalog_kind: "account-selector-v1", }); - expect(catalog.models.find(model => model.slug === "team/gpt-5.4")?.visibility) + expect(catalog.models.find(model => model.slug === "team/gpt-5.6-sol")?.visibility) .toBe("hide"); - expect(catalog.models.find(model => model.slug === "team/gpt-5.4-mini")?.visibility) + expect(catalog.models.find(model => model.slug === "team/gpt-5.4")?.visibility) + .toBeUndefined(); + expect(catalog.models.find(model => model.slug === "team/gpt-5.6-luna")?.visibility) .toBe("list"); } finally { await server.stop(true); diff --git a/tests/clients/desktop-3p.test.ts b/tests/clients/desktop-3p.test.ts index 912c2c18af..cc116cb837 100644 --- a/tests/clients/desktop-3p.test.ts +++ b/tests/clients/desktop-3p.test.ts @@ -212,13 +212,21 @@ describe("Claude Desktop 3P models", () => { }); test("an openai context cap reaches the Desktop writer, not just the dashboard", () => { - // gpt-5.4 is the authoritative 1M native, so it earns supports1m. Capping the provider - // at 272k has to take that away here too, or the written Desktop config promises a - // window the proxy will not serve (#854's effective-window contract). - const uncapped = generateDesktop3pModels(["gpt-5.4"], []); - expect(uncapped[0]).toMatchObject({ supports1m: true, prefer1m: true }); + // No surviving native advertises a 1M window (gpt-5.4 was the last). Sol's + // opt-in ceiling is 922k, so even a 1M provider cap must not invent + // supports1m — nativeOpenAiContextWindow clamps it under the threshold. + // A 272k cap has to take the same path, or the written Desktop config + // would promise a window the proxy will not serve (#854's effective-window + // contract). + const uncapped = generateDesktop3pModels(["gpt-5.6-sol"], []); + expect(uncapped[0]!.supports1m).toBeUndefined(); + expect(uncapped[0]!.prefer1m).toBeUndefined(); - const capped = generateDesktop3pModels(["gpt-5.4"], [], undefined, 272_000); + const optedIn = generateDesktop3pModels(["gpt-5.6-sol"], [], undefined, 1_000_000); + expect(optedIn[0]!.supports1m).toBeUndefined(); + expect(optedIn[0]!.prefer1m).toBeUndefined(); + + const capped = generateDesktop3pModels(["gpt-5.6-sol"], [], undefined, 272_000); expect(capped[0]!.supports1m).toBeUndefined(); expect(capped[0]!.prefer1m).toBeUndefined(); }); diff --git a/tests/codex-integration/codex-auth-context.test.ts b/tests/codex-integration/codex-auth-context.test.ts index 2956c2135e..580f6d15bd 100644 --- a/tests/codex-integration/codex-auth-context.test.ts +++ b/tests/codex-integration/codex-auth-context.test.ts @@ -1755,7 +1755,7 @@ describe("Codex auth context", () => { }); // Spark owns a separate quota, so Terra can use the same account. - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-terra" })) .resolves.toMatchObject({ kind: "pool", accountId: "pool-a" }); await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.3-codex-spark" })) .rejects.toBeInstanceOf(CodexAccountCooldownError); @@ -1763,12 +1763,12 @@ describe("Codex auth context", () => { recordCodexUpstreamOutcome(cfg, "pool-a", 429, { now, resetAt, - modelId: "gpt-5.4", + modelId: "gpt-5.6-terra", }); // Terra and Luna stay in the shared native quota group, while Spark keeps // its independent cooldown instead of being overwritten by Terra's 429. - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4-mini" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-luna" })) .rejects.toBeInstanceOf(CodexAccountCooldownError); await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.3-codex-spark" })) .rejects.toBeInstanceOf(CodexAccountCooldownError); @@ -1780,7 +1780,7 @@ describe("Codex auth context", () => { retryAfter: "60", modelId: "gpt-5.3-codex-spark", }); - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-terra" })) .rejects.toBeInstanceOf(CodexAccountCooldownError); } finally { Date.now = originalNow; @@ -1813,7 +1813,7 @@ describe("Codex auth context", () => { Date.now = () => now; // Establish the shared-scope binding first. The Spark fallback below must // create a second binding rather than replacing this one. - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-terra" })) .resolves.toMatchObject({ kind: "pool", accountId: "pool-a" }); recordCodexUpstreamOutcome(cfg, "pool-a", 429, { @@ -1825,7 +1825,7 @@ describe("Codex auth context", () => { await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.3-codex-spark" })) .resolves.toMatchObject({ kind: "pool", accountId: "pool-b" }); expect(cfg.activeCodexAccountId).toBe("pool-a"); - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-terra" })) .resolves.toMatchObject({ kind: "pool", accountId: "pool-a" }); // This second Spark request proves routing retained the peer choice for // the Spark affinity instead of relying on an auth-layer substitution. @@ -1858,7 +1858,7 @@ describe("Codex auth context", () => { recordCodexUpstreamOutcome(cfg, "pool-a", 429, { now, resetAt, - modelId: "gpt-5.4", + modelId: "gpt-5.6-terra", }); const probeAt = now + CODEX_QUOTA_PROBE_INTERVAL_MS; @@ -1881,7 +1881,7 @@ describe("Codex auth context", () => { Date.now = () => probeAt + 1; await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.3-codex-spark" })) .resolves.toMatchObject({ kind: "pool", accountId: "pool-a" }); - await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.4-mini" })) + await expect(resolveCodexAuthContext(headers, cfg, "pool", { modelId: "gpt-5.6-luna" })) .resolves.toMatchObject({ kind: "pool", probeQuotaScope: "shared" }); } finally { Date.now = originalNow; diff --git a/tests/codex-integration/codex-catalog-golden.test.ts b/tests/codex-integration/codex-catalog-golden.test.ts index 35eeb82bbc..2219440790 100644 --- a/tests/codex-integration/codex-catalog-golden.test.ts +++ b/tests/codex-integration/codex-catalog-golden.test.ts @@ -38,7 +38,7 @@ describe("codex-catalog golden (pure buildCatalogEntries oracle)", () => { const entries = buildCatalogEntries( template() as unknown as Parameters[0], - ["gpt-5.5", "gpt-5.4"], + ["gpt-5.5", "gpt-5.3-codex-spark"], goModels, ["gpt-5.5", "kiro/claude-opus-4.6"], false, @@ -73,7 +73,7 @@ describe("codex-catalog golden (pure buildCatalogEntries oracle)", () => { // Full structural snapshot (the oracle): exact slug set + priority + ws projection. expect(projection.map(p => `${p.slug}@${p.priority}`).sort()).toEqual([ - "gpt-5.4@9", + "gpt-5.3-codex-spark@9", "gpt-5.5@0", "kiro/claude-opus-4.6@1", "opencode-go/glm-5.2@5", diff --git a/tests/codex-integration/codex-catalog-model-picker-order.test.ts b/tests/codex-integration/codex-catalog-model-picker-order.test.ts index 1bc57c76cc..2d1fb6cc73 100644 --- a/tests/codex-integration/codex-catalog-model-picker-order.test.ts +++ b/tests/codex-integration/codex-catalog-model-picker-order.test.ts @@ -150,10 +150,10 @@ describe("modelPickerOrder (#1649)", () => { test("the builder leaves a bare native row unchanged before the complete-order pass", () => { const entries = buildCatalogEntriesFromObservedState({ template: template() as never, - gptSlugs: ["gpt-5.5", "gpt-5.4"], + gptSlugs: ["gpt-5.5", "gpt-5.3-codex-spark"], goModels: [{ id: "glm-5.2", provider: "jd-chat", owned_by: "jd" }] as unknown as CatalogModel[], featured: [], - modelPickerOrder: ["gpt-5.4", "jd-chat/glm-5.2"], + modelPickerOrder: ["gpt-5.3-codex-spark", "jd-chat/glm-5.2"], wsEnabled: false, multiAgentMode: "default", exactComboSlugs: new Set(), @@ -164,7 +164,7 @@ describe("modelPickerOrder (#1649)", () => { }); const p = Object.fromEntries((entries as Record[]).map(e => [e.slug as string, e.priority as number])); // The native row keeps its native priority (9), untouched by modelPickerOrder. - expect(p["gpt-5.4"]).toBe(9); + expect(p["gpt-5.3-codex-spark"]).toBe(9); // The routed row IS placed in the high picker tier. expect(p["jd-chat/glm-5.2"]).toBeGreaterThanOrEqual(1000); }); diff --git a/tests/codex-integration/codex-catalog-restore.test.ts b/tests/codex-integration/codex-catalog-restore.test.ts index c268a55d19..ee215b823b 100644 --- a/tests/codex-integration/codex-catalog-restore.test.ts +++ b/tests/codex-integration/codex-catalog-restore.test.ts @@ -113,12 +113,12 @@ describe("Codex catalog restore", () => { const catalogPath = join(codexHome, "catalog.json"); writeFileSync(join(codexHome, "config.toml"), 'model_catalog_json = "catalog.json"\n', "utf8"); writeFileSync(join(opencodexHome, "config.json"), JSON.stringify({ - disabledModels: ["gpt-5.4", "desktop/gpt-5.5"], + disabledModels: ["gpt-5.6-luna", "desktop/gpt-5.5"], }), "utf8"); writeFileSync(catalogPath, JSON.stringify({ models: [ { slug: "gpt-5.5", visibility: "hide", priority: 7 }, - { slug: "gpt-5.4", visibility: "hide" }, + { slug: "gpt-5.6-luna", visibility: "hide" }, { slug: "gpt-5.3-codex-spark", visibility: "hide" }, { slug: "user-native", visibility: "hide" }, { @@ -132,7 +132,7 @@ describe("Codex catalog restore", () => { opencodex_catalog_kind: "account-selector-v1", }, { - slug: "team/gpt-5.4", + slug: "team/gpt-5.6-luna", visibility: "list", opencodex_catalog_kind: "account-selector-v1", }, @@ -158,7 +158,7 @@ describe("Codex catalog restore", () => { visibility: "list", priority: 7, }); - expect(restored.find(model => model.slug === "gpt-5.4")?.visibility).toBe("hide"); + expect(restored.find(model => model.slug === "gpt-5.6-luna")?.visibility).toBe("hide"); expect(restored.find(model => model.slug === "gpt-5.3-codex-spark")?.visibility).toBe("hide"); expect(restored.find(model => model.slug === "user-native")?.visibility).toBe("hide"); expect(restored.some(model => String(model.slug).includes("/"))).toBe(false); @@ -198,15 +198,15 @@ describe("Codex catalog restore", () => { const backupPath = backupPathForTestCatalog(codexHome, opencodexHome, "catalog.json"); writeFileSync(join(codexHome, "config.toml"), 'model_catalog_json = "catalog.json"\n', "utf8"); writeFileSync(backupPath, JSON.stringify({ - models: [{ slug: "gpt-5.4", visibility: "hide", priority: 50 }], + models: [{ slug: "gpt-5.6-luna", visibility: "hide", priority: 50 }], }, null, 2) + "\n"); writeFileSync(catalogPath, JSON.stringify({ models: [ - { slug: "gpt-5.4", visibility: "hide", priority: 0 }, + { slug: "gpt-5.6-luna", visibility: "hide", priority: 0 }, { slug: "gpt-5.5", visibility: "hide", priority: 7 }, { slug: "gpt-5.3-codex-spark", visibility: "hide" }, { - slug: "team/gpt-5.4", + slug: "team/gpt-5.6-luna", visibility: "list", opencodex_catalog_kind: "account-selector-v1", }, @@ -237,7 +237,7 @@ describe("Codex catalog restore", () => { expect(JSON.parse(r.stdout)).toMatchObject({ removed: 4, kept: 3 }); const restored = JSON.parse(readFileSync(catalogPath, "utf8")).models as Array>; expect(restored).toEqual([ - { slug: "gpt-5.4", visibility: "hide", priority: 50 }, + { slug: "gpt-5.6-luna", visibility: "hide", priority: 50 }, { slug: "gpt-5.5", visibility: "list", priority: 7 }, { slug: "gpt-5.3-codex-spark", visibility: "hide" }, ]); @@ -310,7 +310,7 @@ describe("Codex catalog restore", () => { writeFileSync(catalogPath, JSON.stringify({ models: [ { slug: "gpt-5.5", priority: 50, base_instructions: "native", visibility: "list" }, - { slug: "gpt-5.4", priority: 0, base_instructions: "native", visibility: "list" }, + { slug: "gpt-5.3-codex-spark", priority: 0, base_instructions: "native", visibility: "list" }, ], }, null, 2) + "\n"); @@ -333,7 +333,7 @@ describe("Codex catalog restore", () => { expect(JSON.parse(r.stdout)).toMatchObject({ added: 0 }); const synced = JSON.parse(readFileSync(catalogPath, "utf8")).models as Array>; expect(synced.find(m => m.slug === "gpt-5.5")?.priority).toBe(0); - expect(synced.find(m => m.slug === "gpt-5.4")?.priority).toBeGreaterThan(100); + expect(synced.find(m => m.slug === "gpt-5.3-codex-spark")?.priority).toBeGreaterThan(100); }, { timeout: 15_000 }); test("sync advertises documented Codex-native additions omitted by the bundled catalog", () => { @@ -383,7 +383,7 @@ describe("Codex catalog restore", () => { port: 10100, providers: {}, defaultProvider: "openai", - subagentModels: ["gpt-5.5", "gpt-5.4", "gpt-5.3-codex-spark", "gpt-5.6-sol"], + subagentModels: ["gpt-5.5", "gpt-5.3-codex-spark", "gpt-5.6-sol"], }); console.log(JSON.stringify(result)); })(); @@ -395,6 +395,8 @@ describe("Codex catalog restore", () => { expect(synced.map(m => m.slug)).toContain("gpt-5.6-sol"); expect(synced.map(m => m.slug)).toContain("gpt-5.6-terra"); expect(synced.map(m => m.slug)).toContain("gpt-5.6-luna"); - expect(synced.find(m => m.slug === "gpt-5.4")?.max_context_window).toBe(1_000_000); + // gpt-5.4 is no longer a native catalog member, and no surviving native has a 1M window. + expect(synced.map(m => m.slug)).not.toContain("gpt-5.4"); + expect(synced.find(m => m.slug === "gpt-5.3-codex-spark")?.max_context_window).toBe(100_000); }, { timeout: 15_000 }); }); diff --git a/tests/codex-integration/codex-catalog-sync-hardening.test.ts b/tests/codex-integration/codex-catalog-sync-hardening.test.ts index 21c60d5835..2521067764 100644 --- a/tests/codex-integration/codex-catalog-sync-hardening.test.ts +++ b/tests/codex-integration/codex-catalog-sync-hardening.test.ts @@ -115,8 +115,8 @@ describe("Codex catalog sync hardening", () => { writeFileSync(catalogPath, JSON.stringify({ models: [ nativeEntry("gpt-5.5", 0), - nativeEntry("gpt-5.4", 1), - nativeEntry("gpt-5.4-mini", 2), + nativeEntry("gpt-5.4", 1), // retired -> drop + nativeEntry("gpt-5.4-mini", 2), // retired -> drop nativeEntry("gpt-5.3-codex-spark", 3), nativeEntry("gpt-5.6-sol", 4), nativeEntry("gpt-5.6-terra", 5), @@ -136,8 +136,10 @@ describe("Codex catalog sync hardening", () => { const slugs = (JSON.parse(readFileSync(catalogPath, "utf8")).models as Array<{ slug: string }>).map(m => m.slug); expect(slugs).toContain("gpt-5.5"); - expect(slugs).toContain("gpt-5.4"); - expect(slugs).toContain("gpt-5.4-mini"); + // Retired from NATIVE_OPENAI_MODELS. A pinned upstream snapshot row is not catalog + // membership, so these drop with the other unsupported gpt-/codex- natives. + expect(slugs).not.toContain("gpt-5.4"); + expect(slugs).not.toContain("gpt-5.4-mini"); expect(slugs).toContain("gpt-5.3-codex-spark"); // This isolated fixture has no authenticated ChatGPT roster. The flagship natives list // anyway (owner decision 2026-09-04): asking upstream under an adequate client version @@ -272,13 +274,13 @@ describe("Codex catalog sync hardening", () => { auto_compact_token_limit: 115_200, }, { - ...nativeEntry("gpt-5.4", 1), - comp_hash: "native-5.4-hash", - base_instructions: "Native 5.4 instructions", - model_messages: { instructions_template: "Native 5.4 instructions" }, + ...nativeEntry("gpt-5.3-codex-spark", 1), + comp_hash: "native-spark-hash", + base_instructions: "Native spark instructions", + model_messages: { instructions_template: "Native spark instructions" }, tool_mode: "code_mode_only", }, - nativeEntry("gpt-5.4-mini", 2), + nativeEntry("gpt-5.6-luna", 2), routedEntry("vendor/stable-model", 5), { ...routedEntry("foreign/gpt-5.5", 6), description: "Foreign provider description" }, { @@ -370,10 +372,10 @@ describe("Codex catalog sync hardening", () => { expect(team?.description).toBe(bare?.description); expect(rows.filter(row => row.slug === "team/gpt-5.5")).toHaveLength(1); for (const selector of ["desktop", "team"]) { - expect(rows.some(row => row.slug === `${selector}/gpt-5.4`)).toBe(true); - expect(rows.some(row => row.slug === `${selector}/gpt-5.4-mini`)).toBe(true); + expect(rows.some(row => row.slug === `${selector}/gpt-5.3-codex-spark`)).toBe(true); + expect(rows.some(row => row.slug === `${selector}/gpt-5.6-luna`)).toBe(true); } - for (const nativeSlug of ["gpt-5.5", "gpt-5.4"]) { + for (const nativeSlug of ["gpt-5.5", "gpt-5.3-codex-spark"]) { const native = rows.find(row => row.slug === nativeSlug); const qualified = rows.find(row => row.slug === `team/${nativeSlug}`); expect(qualified).toMatchObject({ @@ -726,7 +728,7 @@ describe("Codex catalog sync hardening", () => { writeFileSync(catalogPath, JSON.stringify({ models: [ { ...nativeEntry("gpt-5.5", 0), visibility: "hide" }, - nativeEntry("gpt-5.4", 1), + nativeEntry("gpt-5.3-codex-spark", 1), ], }, null, 2) + "\n"); @@ -740,7 +742,7 @@ describe("Codex catalog sync hardening", () => { liveModels: false } }, - disabledModels: ["gpt-5.4", "team/gpt-5.5"], + disabledModels: ["gpt-5.3-codex-spark", "team/gpt-5.5"], codexAccounts: [{ id: "stored-side-account", isMain: false }], codexAccountNamespaces: { desktop: "@main", team: "stored-side-account" } }).then(res => console.log(JSON.stringify(res))); @@ -762,7 +764,7 @@ describe("Codex catalog sync hardening", () => { visibility: "list", opencodex_catalog_kind: "account-selector-v1", }); - expect(rows.find(row => row.slug === "team/gpt-5.4")?.visibility).toBe("hide"); + expect(rows.find(row => row.slug === "team/gpt-5.3-codex-spark")?.visibility).toBe("hide"); }); test("default catalog path merges from disk instead of replacing it with bundled rows", () => { diff --git a/tests/codex-integration/codex-catalog.test.ts b/tests/codex-integration/codex-catalog.test.ts index 7c92d0af5d..cc94f48085 100644 --- a/tests/codex-integration/codex-catalog.test.ts +++ b/tests/codex-integration/codex-catalog.test.ts @@ -3456,18 +3456,28 @@ describe("Codex catalog routed normalization", () => { expect(routed?.auto_compact_token_limit).toBe(115_200); }); - test("native gpt-5.4 uses its 1M context window override", () => { + test("retired gpt-5.4 no longer has a 1M native context override", () => { + expect(NATIVE_OPENAI_MODELS).not.toContain("gpt-5.4"); + expect(NATIVE_OPENAI_MODELS).not.toContain("gpt-5.4-mini"); + expect(nativeOpenAiContextWindow("gpt-5.4")).toBeUndefined(); + expect(nativeOpenAiContextWindow("gpt-5.4-mini")).toBeUndefined(); + + // gpt-5.4 was the only native 1M override. Nothing replaces it: remaining + // natives keep their own windows even when cloned from a 1M template or + // given a 2M cap large enough to raise a long-window family. const template = { ...nativeTemplate(), context_window: 272_000, max_context_window: 1_000_000, }; - const entries = buildCatalogEntries(template, ["gpt-5.4"], []); - const native = entries.find(e => e.slug === "gpt-5.4"); - - expect(native?.context_window).toBe(1_000_000); - expect(native?.max_context_window).toBe(1_000_000); - expect(native?.auto_compact_token_limit).toBe(900_000); + const entries = buildCatalogEntries(template, [...NATIVE_OPENAI_MODELS], []); + for (const slug of NATIVE_OPENAI_MODELS) { + const native = entries.find(e => e.slug === slug); + expect(native?.context_window).toBeDefined(); + expect(native!.context_window as number).toBeLessThan(1_000_000); + expect(native!.max_context_window as number).toBeLessThan(1_000_000); + expect(nativeOpenAiContextWindow(slug, 2_000_000)).toBeLessThan(1_000_000); + } }); test("native gpt-5.3-codex-spark uses its 100k context window instead of inherited codex max", () => { @@ -3637,11 +3647,14 @@ describe("Codex catalog routed normalization", () => { expect(luna?.auto_compact_token_limit).toBe(244_800); }); - test("preserved gpt-5.4-mini rows get the openai cap without a hardcoded override (#1430)", () => { + test("preserved old-ladder native rows get the openai cap; retired gpt-5.4-mini is dropped (#1430)", () => { const cap = 200_000; const template = nativeTemplate(); - // gpt-5.4-mini has no NATIVE_OPENAI_CONTEXT_OVERRIDES entry; its windows come - // from the preserved disk row and must still be capped on merge. + // gpt-5.4-mini is no longer a supported native, so merge drops it + // (CANONICAL_NATIVE_CATALOG_CONTENT_POLICY.unsupportedNativeEntries = "drop"). + // The #1430 cap still applies to a preserved old-ladder native without a + // long-window opt-in: gpt-5.5's hardcoded override is 272k/272k, so a 200k + // cap must still win. const genuine54Mini = { ...template, slug: "gpt-5.4-mini", @@ -3650,8 +3663,16 @@ describe("Codex catalog routed normalization", () => { max_context_window: 272_000, auto_compact_token_limit: 244_800, }; + const genuine55 = { + ...template, + slug: "gpt-5.5", + display_name: "GPT-5.5", + context_window: 272_000, + max_context_window: 272_000, + auto_compact_token_limit: 244_800, + }; const merged = mergeCatalogEntriesForSync( - [genuine54Mini], + [genuine54Mini, genuine55], [], new Map(), [], @@ -3669,10 +3690,11 @@ describe("Codex catalog routed normalization", () => { new Set(), cap, ); - const mini = merged.find(e => e.slug === "gpt-5.4-mini"); - expect(mini?.context_window).toBe(cap); - expect(mini?.max_context_window).toBe(cap); - expect(mini?.auto_compact_token_limit).toBe(180_000); + expect(merged.find(e => e.slug === "gpt-5.4-mini")).toBeUndefined(); + const gpt55 = merged.find(e => e.slug === "gpt-5.5"); + expect(gpt55?.context_window).toBe(cap); + expect(gpt55?.max_context_window).toBe(cap); + expect(gpt55?.auto_compact_token_limit).toBe(180_000); }); test("nativeOpenAiContextWindow applies the openai cap as a ceiling only when provided", () => { @@ -3682,8 +3704,11 @@ describe("Codex catalog routed normalization", () => { expect(nativeOpenAiContextWindow("gpt-5.6-sol", 500_000)).toBe(500_000); // A cap ABOVE the native value is a ceiling, not a floor. expect(nativeOpenAiContextWindow("gpt-5.6-sol", 2_000_000)).toBe(922_000); - // Non-5.6 natives are capped the same way. - expect(nativeOpenAiContextWindow("gpt-5.4", 272_000)).toBe(272_000); + // Non-5.6 natives have no long-window opt-in: a cap may only lower. + expect(nativeOpenAiContextWindow("gpt-5.5", 200_000)).toBe(200_000); + expect(nativeOpenAiContextWindow("gpt-5.5", 2_000_000)).toBe(272_000); + // The retired 1M native is gone; a cap cannot invent a window for it. + expect(nativeOpenAiContextWindow("gpt-5.4", 272_000)).toBeUndefined(); }); // Owner decision (devlog 260816_.../011 §4-bis): Daybreak Blue is now a GLOBALLY @@ -4335,41 +4360,43 @@ describe("Codex catalog routed normalization", () => { base_instructions: "installed native instructions", genuine_marker: "installed-native", }; - const nativeMini = { + // The second native is a surviving slug: a retired one would be dropped as an + // unsupported native before this test could say anything about adoption. + const nativeSpark = { ...nativeTemplate(), - slug: "gpt-5.4-mini", - display_name: "gpt-5.4-mini", + slug: "gpt-5.3-codex-spark", + display_name: "gpt-5.3-codex-spark", priority: 6, }; const routedCursorRows = buildCatalogEntries(nativeTemplate(), [], [ { provider: "cursor", id: "gpt-5.5", owned_by: "cursor" }, - { provider: "cursor", id: "gpt-5.4-mini", owned_by: "cursor" }, + { provider: "cursor", id: "gpt-5.3-codex-spark", owned_by: "cursor" }, ]); const merged = mergeCatalogEntriesForSync( - [native, nativeMini, { slug: "cursor/old", visibility: "list" }], + [native, nativeSpark, { slug: "cursor/old", visibility: "list" }], routedCursorRows, new Map([ ["gpt-5.5", 9], - ["gpt-5.4-mini", 10], + ["gpt-5.3-codex-spark", 10], ]), [], false, - new Set(["gpt-5.5", "gpt-5.4-mini"]), + new Set(["gpt-5.5", "gpt-5.3-codex-spark"]), ); const slugs = merged.map(entry => entry.slug); expect(slugs).toContain("gpt-5.5"); - expect(slugs).toContain("gpt-5.4-mini"); + expect(slugs).toContain("gpt-5.3-codex-spark"); expect(slugs).toContain("cursor/gpt-5.5"); - expect(slugs).toContain("cursor/gpt-5.4-mini"); + expect(slugs).toContain("cursor/gpt-5.3-codex-spark"); expect(slugs).not.toContain("cursor/old"); expect(merged.find(entry => entry.slug === "gpt-5.5")?.priority).toBe(9); expect(merged.find(entry => entry.slug === "gpt-5.5")?.base_instructions) .toBe("installed native instructions"); expect(merged.find(entry => entry.slug === "gpt-5.5")?.genuine_marker) .toBe("installed-native"); - expect(merged.find(entry => entry.slug === "gpt-5.4-mini")?.priority).toBe(10); + expect(merged.find(entry => entry.slug === "gpt-5.3-codex-spark")?.priority).toBe(10); }); test("buildCatalogEntries advertises supports_websockets only on explicit opt-in", () => { @@ -6951,7 +6978,7 @@ describe("native slug allowlist", () => { ]; expect(filterSupportedNativeSlugs(liveModels)).toEqual([ - "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark", + "gpt-5.5", "gpt-5.3-codex-spark", ]); }); diff --git a/tests/codex-integration/codex-convergence-account-selectors.test.ts b/tests/codex-integration/codex-convergence-account-selectors.test.ts index ceb34ef6a2..1586978e46 100644 --- a/tests/codex-integration/codex-convergence-account-selectors.test.ts +++ b/tests/codex-integration/codex-convergence-account-selectors.test.ts @@ -140,13 +140,13 @@ function generatedRoutedEntry(slug: string, marker?: string): RawEntry { } function nativeMetadataEntry( - slug: "gpt-5.5" | "gpt-5.4", + slug: "gpt-5.5" | "gpt-5.6-luna", baseInstructions: string, priority: number, ): RawEntry { return { slug, - display_name: slug === "gpt-5.5" ? "GPT-5.5 Live" : "GPT-5.4 Live", + display_name: slug === "gpt-5.5" ? "GPT-5.5 Live" : "GPT-5.6-Luna Live", description: `${slug} installed metadata`, priority, visibility: "list", @@ -201,7 +201,7 @@ function writeAutoReviewModel(value?: string): void { function autoReviewSeed(routeOverride: string | null = "stale-override"): RawEntry[] { return [ - { ...nativeEntry(), slug: "gpt-5.4", auto_review_model_override: "native-upstream" }, + { ...nativeEntry(), slug: "gpt-5.5", auto_review_model_override: "native-upstream" }, { ...generatedRoutedEntry("static/deepseek-v4-flash"), auto_review_model_override: routeOverride, @@ -693,7 +693,7 @@ test("retained and convergence writers resolve, clear, reject, and recover auto- writeAutoReviewModel(); writeCatalog(autoReviewSeed()); catalog = await write(autoReviewConfig(["deepseek-v4-flash"])); - expect(catalog.models?.find(entry => entry.slug === "gpt-5.4")) + expect(catalog.models?.find(entry => entry.slug === "gpt-5.5")) .toHaveProperty("auto_review_model_override", "native-upstream"); expect(catalog.models?.find(entry => entry.slug === "static/deepseek-v4-flash")) .toHaveProperty("auto_review_model_override", null); @@ -994,19 +994,19 @@ test("generated account rows silently win freshly gathered provider collisions", test("qualified rows retain the matching installed metadata for each native model", async () => { const gpt55Instructions = "Installed instructions unique to GPT-5.5."; - const gpt54Instructions = "Installed instructions unique to GPT-5.4."; + const lunaInstructions = "Installed instructions unique to GPT-5.6-Luna."; writeCatalog([ nativeMetadataEntry("gpt-5.5", gpt55Instructions, 3), - nativeMetadataEntry("gpt-5.4", gpt54Instructions, 4), + nativeMetadataEntry("gpt-5.6-luna", lunaInstructions, 4), ]); const catalog = await convergeCatalog(config(true)); const models = catalog.models ?? []; expect(models.find(entry => entry.slug === "gpt-5.5")?.base_instructions).toBe(gpt55Instructions); - expect(models.find(entry => entry.slug === "gpt-5.4")?.base_instructions).toBe(gpt54Instructions); + expect(models.find(entry => entry.slug === "gpt-5.6-luna")?.base_instructions).toBe(lunaInstructions); expect(models.find(entry => entry.slug === "team/gpt-5.5")?.base_instructions).toBe(gpt55Instructions); - expect(models.find(entry => entry.slug === "team/gpt-5.4")?.base_instructions).toBe(gpt54Instructions); + expect(models.find(entry => entry.slug === "team/gpt-5.6-luna")?.base_instructions).toBe(lunaInstructions); }); test("a missing supported native is backfilled and restored when the picker is disabled", async () => { diff --git a/tests/codex-integration/codex-metadata-integrity.test.ts b/tests/codex-integration/codex-metadata-integrity.test.ts index c03d03eb8c..ee70d4829e 100644 --- a/tests/codex-integration/codex-metadata-integrity.test.ts +++ b/tests/codex-integration/codex-metadata-integrity.test.ts @@ -23,11 +23,11 @@ const poolAuthContext = { function minimalParsed(): OcxParsedRequest { return { - modelId: "gpt-5.4", + modelId: "gpt-5.6-luna", context: { messages: [] }, stream: false, options: {}, - _rawBody: { model: "gpt-5.4", input: [] }, + _rawBody: { model: "gpt-5.6-luna", input: [] }, }; } @@ -184,7 +184,7 @@ describe("Codex request transport metadata", () => { test("canonical adapter forwards Lite through selected auth and derives the final wire tier/model", async () => { const parsed = minimalParsed(); - parsed.modelId = "gpt-5.4"; + parsed.modelId = "gpt-5.6-luna"; parsed._rawBody = { model: "gpt-5.6-sol", input: [], service_tier: "flex" }; parsed.options.tierDecision = { kind: "set", value: "priority" }; const before = JSON.stringify(parsed._rawBody); @@ -298,7 +298,7 @@ describe("Codex request transport metadata", () => { for (const lite of [undefined, "yes", "1", "TRUE", "true, false"]) { const headers = new Headers({ "openai-beta": "responses_websockets=existing" }); if (lite !== undefined) headers.set(liteHeader, lite); - const prepared = prepareCodexWsRequest(url, { headers, body: JSON.stringify({ model: "gpt-5.4", + const prepared = prepareCodexWsRequest(url, { headers, body: JSON.stringify({ model: "gpt-5.6-luna", client_metadata: { [liteKey]: "false", thread_id: "thread-fixture" }, stream: true, }) })!; expect(JSON.parse(prepared.frameText).client_metadata).toEqual({ [liteKey]: "false", thread_id: "thread-fixture" }); @@ -306,10 +306,10 @@ describe("Codex request transport metadata", () => { expect(new Headers(prepared.headers).has("originator")).toBe(false); expect(new Headers(prepared.headers).has("user-agent")).toBe(false); } - const absent = prepareCodexWsRequest(url, { body: '{"model":"gpt-5.4","stream":true}' })!; + const absent = prepareCodexWsRequest(url, { body: '{"model":"gpt-5.6-luna","stream":true}' })!; expect(JSON.parse(absent.frameText).client_metadata).toBeUndefined(); const explicit = prepareCodexWsRequest(url, { - headers: { [liteHeader]: "true" }, body: '{"model":"gpt-5.4","stream":true}', + headers: { [liteHeader]: "true" }, body: '{"model":"gpt-5.6-luna","stream":true}', })!; expect(JSON.parse(explicit.frameText).client_metadata).toEqual({ [liteKey]: "true" }); }); @@ -336,7 +336,7 @@ describe("Codex request transport metadata", () => { expect(prepareCodexWsRequest(url, { body })).toBeNull(); } for (const client_metadata of [null, [], true, 1, "text", { unrelated: false }, { [liteKey]: true }]) { - const init = { body: JSON.stringify({ model: "gpt-5.4", client_metadata }), headers: { [liteHeader]: "true" } }; + const init = { body: JSON.stringify({ model: "gpt-5.6-luna", client_metadata }), headers: { [liteHeader]: "true" } }; const before = JSON.stringify(init); expect(prepareCodexWsRequest(url, init)).toBeNull(); expect(JSON.stringify(init)).toBe(before); @@ -347,8 +347,8 @@ describe("Codex request transport metadata", () => { const { applyCodexRoutingHint } = await import("../../src/codex/forward-transport-headers"); const invalid = ["", " ", "model;service_tier=priority", "model=tier", "a b", "a\t", "a\n", "a\r", "a\0", "a\x7f", "é", null, 42]; for (const body of [null, [], "text", {}, ...invalid.map(model => ({ model })), - ...invalid.map(service_tier => ({ model: "gpt-5.4", service_tier })), - { model: "m".repeat(257) }, { model: "gpt-5.4", service_tier: "t".repeat(65) }]) { + ...invalid.map(service_tier => ({ model: "gpt-5.6-luna", service_tier })), + { model: "m".repeat(257) }, { model: "gpt-5.6-luna", service_tier: "t".repeat(65) }]) { const headers = new Headers({ [hintHeader]: "model=stale;service_tier=priority", originator: "unchanged" }); const before = JSON.stringify(body); applyCodexRoutingHint(headers, body); @@ -359,7 +359,7 @@ describe("Codex request transport metadata", () => { const headers = new Headers(); applyCodexRoutingHint(headers, { model: "m".repeat(256), service_tier: "t".repeat(64) }); expect(headers.get(hintHeader)).toBe(`model=${"m".repeat(256)};tier=${"t".repeat(64)}`); - applyCodexRoutingHint(headers, { model: "gpt-5.4" }); - expect(headers.get(hintHeader)).toBe("model=gpt-5.4"); + applyCodexRoutingHint(headers, { model: "gpt-5.6-luna" }); + expect(headers.get(hintHeader)).toBe("model=gpt-5.6-luna"); }); }); diff --git a/tests/codex-integration/codex-quota-auto-refresh-main-admission.test.ts b/tests/codex-integration/codex-quota-auto-refresh-main-admission.test.ts index 88080d5a4d..204436b3a8 100644 --- a/tests/codex-integration/codex-quota-auto-refresh-main-admission.test.ts +++ b/tests/codex-integration/codex-quota-auto-refresh-main-admission.test.ts @@ -287,7 +287,7 @@ describe("quota auto-refresh native-main admission", () => { }); await runCodexQuotaAutoRefresh(cfg, now, { persistCompleted: recordMarkers }); expect(calls).toEqual([responsesUrl, responsesUrl]); - expect(models).toEqual(["gpt-5.4-mini", "gpt-5.5"]); + expect(models).toEqual(["gpt-5.6-luna", "gpt-5.5"]); expect(cfg.codexQuotaAutoRefresh?.[MAIN]?.lastWeeklyResetAt).toBe(RESET_MILLISECONDS); expect(getNativeMainProfileRequestCount()).toBe(0); }); diff --git a/tests/codex-integration/codex-v2-gate.test.ts b/tests/codex-integration/codex-v2-gate.test.ts index 39ec9a342c..7b9ce035ae 100644 --- a/tests/codex-integration/codex-v2-gate.test.ts +++ b/tests/codex-integration/codex-v2-gate.test.ts @@ -1890,8 +1890,8 @@ describe("3-state multi-agent mode", () => { }]; const accountBoundEntries = [{ ...template(), - slug: "team/gpt-5.4", - display_name: "team / GPT-5.4", + slug: "team/gpt-5.6-luna", + display_name: "team / GPT-5.6 Luna", opencodex_catalog_kind: CODEX_ACCOUNT_BOUND_CATALOG_KIND, service_tier: "fast", }]; diff --git a/tests/codex-integration/codex-warmup.test.ts b/tests/codex-integration/codex-warmup.test.ts index d186fb7221..9339920918 100644 --- a/tests/codex-integration/codex-warmup.test.ts +++ b/tests/codex-integration/codex-warmup.test.ts @@ -35,7 +35,7 @@ describe("codex warmup", () => { expect(requests).toBe(1); }); - test("posts a minimal gpt-5.4-mini Responses stream request and accepts response.completed", async () => { + test("posts a minimal gpt-5.6-luna Responses stream request and accepts response.completed", async () => { let body: Record | undefined; let auth: string | null = null; let account: string | null = null; @@ -52,7 +52,7 @@ describe("codex warmup", () => { expect(auth).toBe("Bearer access-test"); expect(account).toBe("acct-test"); expect(body).toMatchObject({ - model: "gpt-5.4-mini", + model: "gpt-5.6-luna", instructions: "Reply with OK.", input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }], stream: true, diff --git a/tests/codex-integration/effort-policy.test.ts b/tests/codex-integration/effort-policy.test.ts index 2f1e65c10c..0d3ac76018 100644 --- a/tests/codex-integration/effort-policy.test.ts +++ b/tests/codex-integration/effort-policy.test.ts @@ -278,7 +278,7 @@ describe("supportedLadderFor (real routeModel routes)", () => { tempCodexHome = mkdtempSync(join(tmpdir(), "ocx-effort-catalog-")); process.env.CODEX_HOME = tempCodexHome; writeFileSync(join(tempCodexHome, "opencodex-catalog.json"), JSON.stringify({ - models: [{ slug: "gpt-5.4", display_name: "gpt-5.4", supported_reasoning_levels: [ + models: [{ slug: "gpt-5.5", display_name: "gpt-5.5", supported_reasoning_levels: [ { effort: "low", description: "low" }, { effort: "medium", description: "medium" }, ] }], })); @@ -286,13 +286,13 @@ describe("supportedLadderFor (real routeModel routes)", () => { providers: { selfhosted: { adapter: "openai-responses", baseUrl: "https://example.com/v1", authMode: "key", - apiKey: "k", models: ["gpt-5.4"], + apiKey: "k", models: ["gpt-5.5"], }, }, defaultProvider: "selfhosted", } as Partial); - expect(() => routeModel(config, "gpt-5.4")).toThrow(NoEnabledOpenAiProviderError); - const namespaced = routeModel(config, "selfhosted/gpt-5.4"); + expect(() => routeModel(config, "gpt-5.5")).toThrow(NoEnabledOpenAiProviderError); + const namespaced = routeModel(config, "selfhosted/gpt-5.5"); expect(namespaced.providerName).toBe("selfhosted"); expect(supportedLadderFor(namespaced)).toBeUndefined(); }); @@ -301,7 +301,7 @@ describe("supportedLadderFor (real routeModel routes)", () => { tempCodexHome = mkdtempSync(join(tmpdir(), "ocx-effort-catalog-")); process.env.CODEX_HOME = tempCodexHome; writeFileSync(join(tempCodexHome, "opencodex-catalog.json"), JSON.stringify({ - models: [{ slug: "gpt-5.4", display_name: "gpt-5.4", supported_reasoning_levels: [ + models: [{ slug: "gpt-5.5", display_name: "gpt-5.5", supported_reasoning_levels: [ { effort: "low", description: "low" }, { effort: "medium", description: "medium" }, { effort: "high", description: "high" }, { effort: "xhigh", description: "xhigh" }, ] }], @@ -312,7 +312,7 @@ describe("supportedLadderFor (real routeModel routes)", () => { }, defaultProvider: "openai", } as Partial); - const route = routeModel(config, "gpt-5.4"); + const route = routeModel(config, "gpt-5.5"); expect(supportedLadderFor(route)).toEqual(["low", "medium", "high", "xhigh"]); }); }); @@ -432,11 +432,11 @@ describe("cap composition with downstream clamps", () => { }); test("synthetic native top rung is still lowered by nativeEffortClamp after the cap block", () => { - // gpt-5.4's real ladder stops at xhigh: an uncapped (or xhigh-capped) max/ultra + // gpt-5.5's real ladder stops at xhigh: an uncapped (or xhigh-capped) max/ultra // arrival is repaired by the native clamp that runs AFTER applyEffortCap. - expect(nativeEffortClamp("gpt-5.4", "max")).toBe("xhigh"); - expect(nativeEffortClamp("gpt-5.4", "ultra")).toBe("xhigh"); - expect(nativeEffortClamp("gpt-5.4", "medium")).toBeNull(); + expect(nativeEffortClamp("gpt-5.5", "max")).toBe("xhigh"); + expect(nativeEffortClamp("gpt-5.5", "ultra")).toBe("xhigh"); + expect(nativeEffortClamp("gpt-5.5", "medium")).toBeNull(); }); }); diff --git a/tests/codex-integration/model-visibility-management-api.test.ts b/tests/codex-integration/model-visibility-management-api.test.ts index e11ebc2428..9fef0adefe 100644 --- a/tests/codex-integration/model-visibility-management-api.test.ts +++ b/tests/codex-integration/model-visibility-management-api.test.ts @@ -373,12 +373,12 @@ test("configured manual OpenAI rows can be toggled alongside native rows", async const config = loadConfig(); config.providers.openai = {adapter:"openai-responses",authMode:"forward",baseUrl:"https://chatgpt.com/backend-api/codex",liveModels:false}; config.customModels = [{id:"manual-gpt",provider:"openai",modelId:"gpt-5.5",contextWindow:128_000}]; - config.disabledModels = ["openai/gpt-5.5", "gpt-5.4"]; + config.disabledModels = ["openai/gpt-5.5", "gpt-5.6-luna"]; expect((await putWithConfig({scope:"models",provider:"openai",targets:[{id:"gpt-5.5",native:false}],enabled:true},config)).status).toBe(200); - expect(config.disabledModels).toEqual(["gpt-5.4"]); - expect((await putWithConfig({scope:"models",provider:"openai",targets:[{id:"gpt-5.5",native:false},{id:"gpt-5.4",native:true}],enabled:false},config)).status).toBe(200); + expect(config.disabledModels).toEqual(["gpt-5.6-luna"]); + expect((await putWithConfig({scope:"models",provider:"openai",targets:[{id:"gpt-5.5",native:false},{id:"gpt-5.6-luna",native:true}],enabled:false},config)).status).toBe(200); expect(config.disabledModels).toContain("openai/gpt-5.5"); - expect(config.disabledModels).toContain("gpt-5.4"); + expect(config.disabledModels).toContain("gpt-5.6-luna"); expect((await putWithConfig({scope:"models",provider:"openai",targets:[{id:"not-configured",native:false}],enabled:true},config)).status).toBe(400); }); @@ -391,15 +391,15 @@ test("provider-group toggles persist mixed native and manual OpenAI targets toge config.customModels = [{ id: "manual-gpt", provider: "openai", modelId: "gpt-5.5" }]; const unrelatedDisabled = [...config.disabledModels!]; const unrelatedProvider = structuredClone(config.providers["google-antigravity"]); - const targets = [{ id: "gpt-5.5", native: false }, { id: "gpt-5.4", native: true }]; + const targets = [{ id: "gpt-5.5", native: false }, { id: "gpt-5.6-luna", native: true }]; saveConfig(config); const disabled = await putWithConfig({ scope: "provider", provider: "openai", targets, enabled: false }, config); expect(disabled.status).toBe(200); expect(await disabled.json()).toMatchObject({ ok: true, scope: "provider", provider: "openai", enabled: false }); - expect(config.disabledModels).toEqual([...unrelatedDisabled, "openai/gpt-5.5", "gpt-5.4"]); + expect(config.disabledModels).toEqual([...unrelatedDisabled, "openai/gpt-5.5", "gpt-5.6-luna"]); expect(config.providers.openai.selectedModels).toEqual(["gpt-5.5"]); - expect(loadConfig().disabledModels).toEqual([...unrelatedDisabled, "openai/gpt-5.5", "gpt-5.4"]); + expect(loadConfig().disabledModels).toEqual([...unrelatedDisabled, "openai/gpt-5.5", "gpt-5.6-luna"]); expect(loadConfig().providers.openai.selectedModels).toEqual(["gpt-5.5"]); expect(loadConfig().providers["google-antigravity"]).toEqual(unrelatedProvider); expect(refreshes).toBe(1); @@ -426,14 +426,14 @@ test("an invalid trailing target leaves a mixed OpenAI provider-group update ato for (const enabled of [false, true]) { // Both valid targets would change state before the final invalid target is reached. - config.disabledModels = enabled ? ["other/keep", "openai/gpt-5.5", "gpt-5.4"] : ["other/keep"]; + config.disabledModels = enabled ? ["other/keep", "openai/gpt-5.5", "gpt-5.6-luna"] : ["other/keep"]; saveConfig(config); const before = structuredClone(config); const persistedBefore = loadConfig(); for (const invalid of [{ id: "not-configured", native: false }, { id: "gpt-9.9-imaginary", native: true }]) { const response = await putWithConfig({ scope: "provider", provider: "openai", enabled, - targets: [{ id: "gpt-5.5", native: false }, { id: "gpt-5.4", native: true }, invalid], + targets: [{ id: "gpt-5.5", native: false }, { id: "gpt-5.6-luna", native: true }, invalid], }, config); expect(response.status).toBe(400); expect(await response.json()).toMatchObject({ error: "invalid model visibility target" }); diff --git a/tests/codex-integration/native-model-toggle.test.ts b/tests/codex-integration/native-model-toggle.test.ts index 0ac18f1ad2..7e90a6f05c 100644 --- a/tests/codex-integration/native-model-toggle.test.ts +++ b/tests/codex-integration/native-model-toggle.test.ts @@ -68,8 +68,8 @@ function nativeTemplate(): Record { describe("native GPT model toggles (bare slugs in disabledModels)", () => { test("disabledNativeSlugs picks bare ids only; routed namespaced ids are ignored", () => { - const set = disabledNativeSlugs({ disabledModels: ["gpt-5.4", "kiro/claude-opus-4.6", "gpt-5.6-luna"] }); - expect([...set].sort()).toEqual(["gpt-5.4", "gpt-5.6-luna"]); + const set = disabledNativeSlugs({ disabledModels: ["gpt-5.5", "kiro/claude-opus-4.6", "gpt-5.6-luna"] }); + expect([...set].sort()).toEqual(["gpt-5.5", "gpt-5.6-luna"]); }); test("visibleNativeSlugs omits disabled natives from the bare availability list", () => { @@ -230,8 +230,12 @@ describe("native GPT model toggles (bare slugs in disabledModels)", () => { }); test("the on-disk catalog preserves a lower retained native compaction threshold", () => { + // A retired slug is no longer a valid subject: nativeOpenAiAutoCompactTokenLimit + // requires a known native window, so a configured lowering would not apply to + // gpt-5.4-mini after its override and membership were removed. gpt-5.5 is the + // surviving old-ladder native whose 272k window matches this retained row. const retained = { - slug: "gpt-5.4-mini", + slug: "gpt-5.5", context_window: 272_000, max_context_window: 272_000, auto_compact_token_limit: 100_000, @@ -240,7 +244,7 @@ describe("native GPT model toggles (bare slugs in disabledModels)", () => { expect(retained.auto_compact_token_limit).toBe(100_000); const configured = { - providers: { openai: { modelAutoCompactTokenLimits: { "gpt-5.4-mini": 80_000 } } }, + providers: { openai: { modelAutoCompactTokenLimits: { "gpt-5.5": 80_000 } } }, } as never; const lowered = { ...retained }; applyNativeOpenAiContextOverride(lowered as never, nativeContextLimits(configured)); @@ -296,7 +300,10 @@ describe("native GPT model toggles (bare slugs in disabledModels)", () => { const over = nativeModelRows({ providerContextCaps: { openai: 2_000_000 } }); expect(over.find(r => r.slug === "gpt-5.6-sol")?.contextWindow).toBe(922_000); expect(raised.find(r => r.slug === "gpt-5.5")?.contextWindow).toBe(272_000); - expect(raised.find(r => r.slug === "gpt-5.4")?.contextWindow).toBe(922_000); + // gpt-5.4 was the only native with a 1M override. Retirement deleted that + // membership and the override; nothing else inherits a 1M window. + expect(raised.find(r => r.slug === "gpt-5.4")).toBeUndefined(); + expect(raised.every(r => (r.contextWindow ?? 0) <= 922_000)).toBe(true); }); test("nativeModelRows applies providerContextCaps.openai as a ceiling (#1430)", () => { diff --git a/tests/codex-integration/token-guardian.test.ts b/tests/codex-integration/token-guardian.test.ts index f007211340..3bb215d7dc 100644 --- a/tests/codex-integration/token-guardian.test.ts +++ b/tests/codex-integration/token-guardian.test.ts @@ -250,7 +250,7 @@ describe("token guardian", () => { expect(res.refreshed).toEqual([]); expect(res.warmed).toContain("codex:acct-warm"); - expect(mock.body()).toMatchObject({ model: "gpt-5.4-mini", input: WARMUP_INPUT, stream: true, store: false }); + expect(mock.body()).toMatchObject({ model: "gpt-5.6-luna", input: WARMUP_INPUT, stream: true, store: false }); expect(readCodexAccountRecord("acct-warm")?.lastCodexValidationStatus).toBe("ok"); expect(readCodexAccountRecord("acct-warm")?.lastCodexValidatedAt).toBeGreaterThan(Date.now() - 30_000); }); diff --git a/tests/codex-integration/warmup.test.ts b/tests/codex-integration/warmup.test.ts index 5590e62ae1..1c25005860 100644 --- a/tests/codex-integration/warmup.test.ts +++ b/tests/codex-integration/warmup.test.ts @@ -94,7 +94,7 @@ describe("codex warmup improvements", () => { const body = JSON.parse(String(init?.body)) as Record; parsedBodies.push(body); - if (body.model === "gpt-5.4-mini") { + if (body.model === "gpt-5.6-luna") { return new Response(JSON.stringify({ detail: "unknown model" }), { status: 400 }); } @@ -110,23 +110,18 @@ describe("codex warmup improvements", () => { } expect(fetchMock).toHaveBeenCalledTimes(2); - expect(parsedBodies.map(body => body.model)).toEqual(["gpt-5.4-mini", "gpt-5.5"]); + expect(parsedBodies.map(body => body.model)).toEqual(["gpt-5.6-luna", "gpt-5.5"]); }); - test("warmCodexAccount retries FALLBACK_MODELS on HTTP 404 and falls through to gpt-5.6-luna", async () => { + test("warmCodexAccount retries FALLBACK_MODELS on HTTP 404", async () => { const parsedBodies: Record[] = []; const fetchMock = mock(async (_input: RequestInfo | URL, init?: RequestInit) => { const body = JSON.parse(String(init?.body)) as Record; parsedBodies.push(body); - if (body.model === "gpt-5.4-mini") { - return new Response(JSON.stringify({ detail: "model not found" }), { status: 404 }); - } - if (body.model === "gpt-5.5") { - return new Response(JSON.stringify({ detail: "model not supported for free tier" }), { status: 400 }); - } if (body.model === "gpt-5.6-luna") { - return sseResponse(); + return new Response(JSON.stringify({ detail: "model not found" }), { status: 404 }); } + if (body.model === "gpt-5.5") return sseResponse(); return new Response("unexpected model", { status: 500 }); }); const fetchSpy = spyOn(globalThis, "fetch").mockImplementation(fetchMock as unknown as typeof fetch); @@ -137,8 +132,8 @@ describe("codex warmup improvements", () => { fetchSpy.mockRestore(); } - expect(fetchMock).toHaveBeenCalledTimes(3); - expect(parsedBodies.map(body => body.model)).toEqual(["gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna"]); + expect(fetchMock).toHaveBeenCalledTimes(2); + expect(parsedBodies.map(body => body.model)).toEqual(["gpt-5.6-luna", "gpt-5.5"]); }); test("warmCodexAccount does not retry on 401 and immediately fails", async () => { diff --git a/tests/providers/cyber-policy-error-fidelity.test.ts b/tests/providers/cyber-policy-error-fidelity.test.ts index f949634b3c..bff86a295a 100644 --- a/tests/providers/cyber-policy-error-fidelity.test.ts +++ b/tests/providers/cyber-policy-error-fidelity.test.ts @@ -273,7 +273,7 @@ describe("cyber_policy error fidelity", () => { }); expect(events.find(e => e.type === "error")).toMatchObject({ errorType: "invalid_request" }); - const frames = await collectSse(bridgeToResponsesSSE(replay(events), "openai/gpt-5.4")); + const frames = await collectSse(bridgeToResponsesSSE(replay(events), "openai/gpt-5.6-luna")); const failed = frames.find(frame => frame.event === "response.failed")?.data.response as Record; expect(failed.error).toMatchObject({ type: "invalid_request", @@ -287,7 +287,7 @@ describe("cyber_policy error fidelity", () => { test("message-only cyber adapter error still classifies (no silent 502)", async () => { const frames = await collectSse(bridgeToResponsesSSE(replay([ { type: "error", message: SECRET_CYBER_MESSAGE, retryable: true }, - ]), "openai/gpt-5.4")); + ]), "openai/gpt-5.6-luna")); const failed = frames.find(frame => frame.event === "response.failed")?.data.response as Record; expect(failed.error).toMatchObject({ type: CYBER_POLICY_ERROR_CODE, @@ -299,7 +299,7 @@ describe("cyber_policy error fidelity", () => { const buffered = buildResponseJSON([ { type: "error", message: SECRET_CYBER_MESSAGE, retryable: true }, - ], "openai/gpt-5.4"); + ], "openai/gpt-5.6-luna"); expect(buffered).toMatchObject({ status: "failed", retryable: false, @@ -311,7 +311,7 @@ describe("cyber_policy error fidelity", () => { async function* throwingEvents(): AsyncGenerator { throw new Error(SECRET_CYBER_MESSAGE); } - const frames = await collectSse(bridgeToResponsesSSE(throwingEvents(), "openai/gpt-5.4")); + const frames = await collectSse(bridgeToResponsesSSE(throwingEvents(), "openai/gpt-5.6-luna")); const failed = frames.find(frame => frame.event === "response.failed")?.data.response as Record; expect(failed).toMatchObject({ status: "failed", @@ -386,7 +386,7 @@ describe("cyber_policy error fidelity", () => { controller.close(); }, }), - "gpt-5.4", + "gpt-5.6-luna", ); const frames = await collectSse(chatSse); const errorFrame = frames.find(frame => frame.data.error); diff --git a/tests/providers/opencode-cli.test.ts b/tests/providers/opencode-cli.test.ts index 1edfe9284f..bf3552d8c0 100644 --- a/tests/providers/opencode-cli.test.ts +++ b/tests/providers/opencode-cli.test.ts @@ -127,9 +127,9 @@ describe("ocx opencode provider block", () => { }); test("native slugs pick up authoritative context windows from the resolver", () => { - const block = buildOpencodeProviderBlock(10100, ["gpt-5.4", "unknown-native"], [], slug => - slug === "gpt-5.4" ? 1_000_000 : undefined); - expect(block.models["gpt-5.4"]?.limit).toEqual({ context: 1_000_000, output: SCHEMA_REQUIRED_OUTPUT_BUDGET }); + const block = buildOpencodeProviderBlock(10100, ["gpt-5.6-luna", "unknown-native"], [], slug => + slug === "gpt-5.6-luna" ? 1_000_000 : undefined); + expect(block.models["gpt-5.6-luna"]?.limit).toEqual({ context: 1_000_000, output: SCHEMA_REQUIRED_OUTPUT_BUDGET }); expect(block.models["unknown-native"]?.limit).toBeUndefined(); }); diff --git a/tests/server/server-combo-failover-e2e.test.ts b/tests/server/server-combo-failover-e2e.test.ts index 38f0368d54..6f649c52d3 100644 --- a/tests/server/server-combo-failover-e2e.test.ts +++ b/tests/server/server-combo-failover-e2e.test.ts @@ -1412,7 +1412,7 @@ describe("server combo failover 030 activation matrix", () => { authMode: "forward", codexAccountMode: "pool", }, - }, [{ provider: "openai", model: "gpt-5.4" }]); + }, [{ provider: "openai", model: "gpt-5.6-luna" }]); config.codexAccounts = [{ id: rawAccountId, email: "pool@example.test", @@ -1427,7 +1427,7 @@ describe("server combo failover 030 activation matrix", () => { expiresAt: Date.now() + 300_000, chatgptAccountId: "acct-pool-safe", }); - customTransientResponse = async () => Response.json(responsesSuccess("pool success", "gpt-5.4")); + customTransientResponse = async () => Response.json(responsesSuccess("pool success", "gpt-5.6-luna")); const response = await postLogged(config); expect(response.status).toBe(200); @@ -1459,7 +1459,7 @@ describe("server combo failover 030 activation matrix", () => { authMode: "forward", codexAccountMode: "pool", }, - }, [{ provider: "openai", model: "gpt-5.4" }]); + }, [{ provider: "openai", model: "gpt-5.6-luna" }]); config.codexAccounts = [{ id: rawAccountId, email: "combo-terminal@example.test", @@ -1504,7 +1504,7 @@ describe("server combo failover 030 activation matrix", () => { }, }, [ { provider: "openai", model: "gpt-5.3-codex-spark" }, - { provider: "openai", model: "gpt-5.4" }, + { provider: "openai", model: "gpt-5.6-luna" }, ]); config.codexAccounts = [{ id: rawAccountId, @@ -1531,7 +1531,7 @@ describe("server combo failover 030 activation matrix", () => { headers: { "x-codex-primary-reset-at": String(Math.floor(Date.now() / 1000) + 3600) }, }, ) - : Response.json(responsesSuccess("model fallback succeeded", "gpt-5.4")); + : Response.json(responsesSuccess("model fallback succeeded", "gpt-5.6-luna")); }; const response = await postLogged(config); @@ -1552,7 +1552,7 @@ describe("server combo failover 030 activation matrix", () => { }, }, [ { provider: "openai", model: "gpt-5.3-codex-spark" }, - { provider: "openai", model: "gpt-5.4" }, + { provider: "openai", model: "gpt-5.6-luna" }, ]); config.codexAccounts = [{ id: rawAccountId, @@ -1582,7 +1582,7 @@ describe("server combo failover 030 activation matrix", () => { }, }, ) - : Response.json(responsesSuccess("must not reach second upstream", "gpt-5.4")); + : Response.json(responsesSuccess("must not reach second upstream", "gpt-5.6-luna")); }; const response = await postLogged(config); @@ -3801,7 +3801,7 @@ describe("combo compact failover", () => { return chatStream("compact backup"); }); const { config } = canonicalPoolConfig([ - { provider: "openai-apikey", model: "gpt-5.4" }, + { provider: "openai-apikey", model: "gpt-5.6-luna" }, { provider: "backup", model: "m1" }, ], baseUrl(b)); globalThis.fetch = (async (input: unknown, init?: RequestInit) => { @@ -3857,7 +3857,7 @@ describe("combo compact failover", () => { test("combo compact runs the synthetic turn as SSE so a canonical child can serve it", async () => { const bodies: Array> = []; - const { config } = canonicalPoolConfig([{ provider: "openai-apikey", model: "gpt-5.4" }]); + const { config } = canonicalPoolConfig([{ provider: "openai-apikey", model: "gpt-5.6-luna" }]); globalThis.fetch = (async (input: unknown, init?: RequestInit) => { const url = typeof input === "object" && input !== null && "url" in input ? String((input as Request).url) @@ -3908,7 +3908,7 @@ describe("combo compact failover", () => { }); test("native compact rejects an empty ciphertext item", async () => { - const { config } = canonicalPoolConfig([{ provider: "openai-apikey", model: "gpt-5.4" }]); + const { config } = canonicalPoolConfig([{ provider: "openai-apikey", model: "gpt-5.6-luna" }]); globalThis.fetch = (async (input: unknown, init?: RequestInit) => { const url = typeof input === "object" && input !== null && "url" in input ? String((input as Request).url) diff --git a/tests/vision/sidecar-abort.test.ts b/tests/vision/sidecar-abort.test.ts index 348de2a69a..9e2c35ebdf 100644 --- a/tests/vision/sidecar-abort.test.ts +++ b/tests/vision/sidecar-abort.test.ts @@ -161,7 +161,7 @@ describe("sidecar abort propagation", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, abortSignal: turn.signal, }); @@ -189,7 +189,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, turn.signal, value => recorded.push(value), ); @@ -211,7 +211,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, undefined, outcome => recorded.push(outcome), ); @@ -230,7 +230,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, lateAbort.signal, outcome => recorded.push(outcome), ); @@ -246,7 +246,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, ); expect(outcome.error).toBe("sidecar HTTP 401: upstream echoed Bearer [REDACTED]"); }); @@ -288,7 +288,7 @@ describe("sidecar abort propagation", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, recordSidecarOutcome: outcome => recorded.push(outcome), }); @@ -314,7 +314,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, turn.signal, value => recorded.push(value), ); @@ -336,7 +336,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, webTurn.signal, value => webRecorded.push(value), ); @@ -358,7 +358,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, visionTurn.signal, value => visionRecorded.push(value), ); @@ -382,7 +382,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, webTurn.signal, value => webRecorded.push(value), ); @@ -402,7 +402,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, visionTurn.signal, value => visionRecorded.push(value), ); @@ -424,7 +424,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, turn.signal, value => recorded.push(value), ); @@ -443,7 +443,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, undefined, value => webRecorded.push(value), ); @@ -458,7 +458,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, undefined, value => visionRecorded.push(value), ); @@ -476,7 +476,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, undefined, outcome => recorded.push(outcome), ); @@ -496,7 +496,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, lateAbort.signal, outcome => recorded.push(outcome), ); @@ -513,7 +513,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 1 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 1 }, undefined, outcome => webRecorded.push(outcome), ); @@ -528,7 +528,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 1 }, + { model: "gpt-5.6-luna", timeoutMs: 1 }, undefined, outcome => visionRecorded.push(outcome), ); @@ -544,7 +544,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, new Headers({ authorization: "Bearer token" }), - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, ); expect(outcome.error).toBe("vision sidecar HTTP 403: upstream echoed Bearer [REDACTED]"); }); @@ -568,7 +568,7 @@ describe("sidecar abort propagation", () => { { type: "web_search" }, forwardProvider, selectedHeaders, - { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, ); expect(seenAuthorization).toBe("Bearer pool-token"); @@ -595,7 +595,7 @@ describe("sidecar abort propagation", () => { "inspect screenshot", forwardProvider, selectedHeaders, - { model: "gpt-5.4-mini", timeoutMs: 30_000 }, + { model: "gpt-5.6-luna", timeoutMs: 30_000 }, ); expect(seenAuthorization).toBe("Bearer pool-token"); diff --git a/tests/vision/sidecar-settings-vision-controls.test.ts b/tests/vision/sidecar-settings-vision-controls.test.ts index a57b646f78..1ac5670d06 100644 --- a/tests/vision/sidecar-settings-vision-controls.test.ts +++ b/tests/vision/sidecar-settings-vision-controls.test.ts @@ -86,7 +86,7 @@ describe("sidecar-settings remaining vision controls", () => { expect(unset.status).toBe(200); expect((await unset.json() as { vision: Record }).vision).toMatchObject({ enabled: true, - model: "gpt-5.4-mini", + model: "gpt-5.6-luna", reasoning: "low", maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(undefined), timeoutMs: DEFAULT_VISION_TIMEOUT_MS, diff --git a/tests/vision/sidecar-settings-vision-filter.test.ts b/tests/vision/sidecar-settings-vision-filter.test.ts index 0582f0cff0..948ab49585 100644 --- a/tests/vision/sidecar-settings-vision-filter.test.ts +++ b/tests/vision/sidecar-settings-vision-filter.test.ts @@ -134,7 +134,7 @@ describe("sidecar-settings vision model filter", () => { const body = await response.json() as { vision: { model: string } }; // Empty string clears the override; the effective reported model is the fallback. expect(config.visionSidecar?.model).toBeUndefined(); - expect(body.vision.model).toBe("gpt-5.4-mini"); + expect(body.vision.model).toBe("gpt-5.6-luna"); }); test("6. catalog failure degrades to baselines", async () => { @@ -186,7 +186,7 @@ describe("sidecar-settings vision model filter", () => { test("9. GET reports the effective Anthropic default for an explicitly selected backend", async () => { // Reports what the runtime WOULD use for this backend. No OAuth account is set up // here, so no plan would run; the point is that the projection stops answering - // gpt-5.4-mini for a configuration the OpenAI describer does not own. + // gpt-5.6-luna for a configuration the OpenAI describer does not own. const config = emptyConfig({ visionSidecar: { backend: "anthropic" } }); const response = await getSidecarSettings(config); expect(response.status).toBe(200); diff --git a/tests/vision/vision-anthropic.test.ts b/tests/vision/vision-anthropic.test.ts index f956cf6a4d..a42a238db9 100644 --- a/tests/vision/vision-anthropic.test.ts +++ b/tests/vision/vision-anthropic.test.ts @@ -416,7 +416,7 @@ describe("Anthropic vision planning and management config", () => { expect(clearBody.webSearch).toEqual({ enabled: true, model: "gpt-5.6-luna", streamRoutedModelOutput: false }); expect(clearBody.vision).toEqual({ enabled: true, - model: "gpt-5.4-mini", + model: "gpt-5.6-luna", reasoning: "low", maxDescriptionsPerTurn: 4, timeoutMs: 45_000, diff --git a/tests/vision/vision-eligibility.test.ts b/tests/vision/vision-eligibility.test.ts index 2344fc4d69..25fdc63d42 100644 --- a/tests/vision/vision-eligibility.test.ts +++ b/tests/vision/vision-eligibility.test.ts @@ -222,7 +222,7 @@ describe("vision eligibility core", () => { test("14. a non-native row's explicit text-only modality wins over a colliding native slug", () => { expect(modelAcceptsImageInput(emptyConfig, { provider: "custom-openai-compatible", - id: "gpt-5.4-mini", + id: "gpt-5.6-luna", inputModalities: ["text"], })).toBe(false); }); diff --git a/tests/vision/vision-reasoning-contract.test.ts b/tests/vision/vision-reasoning-contract.test.ts index 38d8378996..b55a4008b4 100644 --- a/tests/vision/vision-reasoning-contract.test.ts +++ b/tests/vision/vision-reasoning-contract.test.ts @@ -68,7 +68,7 @@ describe("vision reasoning capability contracts", () => { | { reasoningEfforts?: string[] } | undefined)?.reasoningEfforts; - expect(efforts("gpt-5.4-mini")).toEqual(["low", "medium", "high", "xhigh"]); + expect(efforts("gpt-5.5")).toEqual(["low", "medium", "high", "xhigh"]); expect(efforts("gpt-5.6-luna")).toEqual(["low", "medium", "high", "xhigh", "max"]); expect(efforts("gpt-5.6-sol")).toEqual(["low", "medium", "high", "xhigh", "max"]); expect(efforts("gpt-5.6-sol")).not.toContain("ultra"); @@ -85,7 +85,7 @@ describe("vision reasoning capability contracts", () => { const response = await getVision(config); expect(response.status).toBe(200); expect(await response.json()).toMatchObject({ - vision: { model: "gpt-5.4-mini", reasoning: "xhigh" }, + vision: { model: "gpt-5.6-luna", reasoning: "max" }, }); // Reads report effective execution state without mutating a hand-edited config in memory. expect(config.visionSidecar?.reasoning).toBe("max"); @@ -99,10 +99,10 @@ describe("vision reasoning capability contracts", () => { try { const direct = { port: 10100, defaultProvider: "none", providers: {} } as OcxConfig; - let response = await putVision(direct, { model: "gpt-5.4-mini", reasoning: "max" }); + let response = await putVision(direct, { model: "gpt-5.5", reasoning: "max" }); expect(response.status).toBe(200); expect(await response.json()).toMatchObject({ - vision: { model: "gpt-5.4-mini", reasoning: "xhigh" }, + vision: { model: "gpt-5.5", reasoning: "xhigh" }, }); expect(direct.visionSidecar?.reasoning).toBe("xhigh"); @@ -110,7 +110,7 @@ describe("vision reasoning capability contracts", () => { port: 10100, defaultProvider: "none", providers: {}, - visionSidecar: { model: "gpt-5.4-mini", reasoning: "low" }, + visionSidecar: { model: "gpt-5.5", reasoning: "low" }, } as OcxConfig; response = await putVision(reasoningOnly, { reasoning: "max" }); expect(response.status).toBe(200); @@ -125,10 +125,10 @@ describe("vision reasoning capability contracts", () => { response = await putVision(unsetModel, { reasoning: "max" }); expect(response.status).toBe(200); expect(await response.json()).toMatchObject({ - vision: { model: "gpt-5.4-mini", reasoning: "xhigh" }, + vision: { model: "gpt-5.6-luna", reasoning: "max" }, }); expect(unsetModel.visionSidecar?.model).toBeUndefined(); - expect(unsetModel.visionSidecar?.reasoning).toBe("xhigh"); + expect(unsetModel.visionSidecar?.reasoning).toBe("max"); const modelOnly = { port: 10100, @@ -136,23 +136,23 @@ describe("vision reasoning capability contracts", () => { providers: {}, visionSidecar: { model: "gpt-5.6-luna", reasoning: "max" }, } as OcxConfig; - response = await putVision(modelOnly, { model: "gpt-5.4-mini" }); + response = await putVision(modelOnly, { model: "gpt-5.5" }); expect(response.status).toBe(200); - expect(modelOnly.visionSidecar).toMatchObject({ model: "gpt-5.4-mini", reasoning: "xhigh" }); + expect(modelOnly.visionSidecar).toMatchObject({ model: "gpt-5.5", reasoning: "xhigh" }); const reset = { port: 10100, defaultProvider: "none", providers: {}, - visionSidecar: { model: "gpt-5.4-mini", reasoning: "max" }, + visionSidecar: { model: "gpt-5.5", reasoning: "max" }, } as OcxConfig; response = await putVision(reset, { model: "" }); expect(response.status).toBe(200); expect(await response.json()).toMatchObject({ - vision: { model: "gpt-5.4-mini", reasoning: "xhigh" }, + vision: { model: "gpt-5.6-luna", reasoning: "max" }, }); expect(reset.visionSidecar?.model).toBeUndefined(); - expect(reset.visionSidecar?.reasoning).toBe("xhigh"); + expect(reset.visionSidecar?.reasoning).toBe("max"); const custom = { port: 10100, defaultProvider: "none", providers: {} } as OcxConfig; response = await putVision(custom, { model: "custom-vision", reasoning: "max" }); @@ -175,14 +175,14 @@ describe("vision reasoning capability contracts", () => { writeFileSync(importPath, JSON.stringify(validCliConfig({ reasoning: "max" }))); expect(await handleConfigCommand(["import", importPath, "--yes", "--json"])).toBe(0); let persisted = JSON.parse(readFileSync(join(isolatedHome, "config.json"), "utf8")); - expect(persisted.visionSidecar).toMatchObject({ reasoning: "xhigh" }); + expect(persisted.visionSidecar).toMatchObject({ reasoning: "max" }); expect(persisted.visionSidecar.model).toBeUndefined(); writeFileSync(importPath, JSON.stringify(validCliConfig({ model: "", reasoning: "max" }))); expect(await handleConfigCommand(["import", importPath, "--yes", "--json"])).toBe(0); persisted = JSON.parse(readFileSync(join(isolatedHome, "config.json"), "utf8")); - expect(persisted.visionSidecar).toMatchObject({ model: "", reasoning: "xhigh" }); - expect(resolveOpenAiVisionModel({ visionSidecar: persisted.visionSidecar })).toBe("gpt-5.4-mini"); + expect(persisted.visionSidecar).toMatchObject({ model: "", reasoning: "max" }); + expect(resolveOpenAiVisionModel({ visionSidecar: persisted.visionSidecar })).toBe("gpt-5.6-luna"); } finally { if (previousHome === undefined) delete process.env.OPENCODEX_HOME; else process.env.OPENCODEX_HOME = previousHome; @@ -198,18 +198,18 @@ describe("vision reasoning capability contracts", () => { port: 10100, defaultProvider: "none", providers: {}, - visionSidecar: { model: "gpt-5.4-mini", reasoning: "high", maxDescriptionsPerTurn: 8 }, + visionSidecar: { model: "gpt-5.6-luna", reasoning: "high", maxDescriptionsPerTurn: 8 }, } as OcxConfig; try { let response = await putVision(config, { reasoning: "ultra" }); expect(response.status).toBe(400); - expect(config.visionSidecar).toMatchObject({ model: "gpt-5.4-mini", reasoning: "high" }); + expect(config.visionSidecar).toMatchObject({ model: "gpt-5.6-luna", reasoning: "high" }); response = await putVision(config, { maxDescriptionsPerTurn: 4 }); expect(response.status).toBe(200); expect(config.visionSidecar).toMatchObject({ - model: "gpt-5.4-mini", + model: "gpt-5.6-luna", reasoning: "high", maxDescriptionsPerTurn: 4, }); diff --git a/tests/web-search/web-search.test.ts b/tests/web-search/web-search.test.ts index c116743d86..81a06febfa 100644 --- a/tests/web-search/web-search.test.ts +++ b/tests/web-search/web-search.test.ts @@ -952,7 +952,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, onRequestBuilt: request => reasoningLogs.push(request.reasoningLog), on429: async retryAfter => { @@ -1023,7 +1023,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, retryOn429Policy: { enabled: true, attempts: 2, intervalMs: 120, maxIntervalMs: 60_000, respectRetryAfter: false }, on429: () => { @@ -1076,7 +1076,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, stallTimeoutSec: 1, retryOn429Policy: { enabled: true, attempts: 1, intervalMs: 1_500, maxIntervalMs: 60_000, respectRetryAfter: false }, @@ -1118,7 +1118,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, connectTimeoutMs: 100, retryOn429Policy: { enabled: true, attempts: 1, intervalMs: 150, maxIntervalMs: 60_000, respectRetryAfter: false }, @@ -1175,7 +1175,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, retryOn429Policy: { enabled: true, attempts: 1, intervalMs: 50, maxIntervalMs: 60_000, respectRetryAfter: false }, on429: () => { @@ -1208,7 +1208,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, on429: () => null, }); @@ -1231,7 +1231,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, connectTimeoutMs: 100, }); @@ -1269,7 +1269,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, connectTimeoutMs: 100, on429: () => rotatedAdapter, @@ -1300,7 +1300,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, connectTimeoutMs: 30_000, abortSignal: parent.signal, @@ -1372,7 +1372,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 2, }); await collectSse(response.body!); @@ -1437,7 +1437,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 2, }); await collectSse(response.body!); @@ -1507,7 +1507,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 2, }); await collectSse(response.body!); @@ -1581,7 +1581,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 2, }); await collectSse(response.body!); @@ -1620,7 +1620,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); @@ -1652,7 +1652,7 @@ describe("web-search sidecar native web_search_call emission", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); @@ -1725,7 +1725,7 @@ describe("web-search forced-answer nudge", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); // Iteration 2 (the forced-answer pass) runs live inside the SSE body — drain it so it executes. @@ -1764,7 +1764,7 @@ describe("web-search forced-answer nudge", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); await drain(response.body!); @@ -1804,7 +1804,7 @@ describe("web-search live spinner ordering", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); @@ -1881,7 +1881,7 @@ describe("web-search batched queries", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 3, }); @@ -1935,7 +1935,7 @@ describe("web-search sources -> url_citation annotations", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); @@ -1978,7 +1978,7 @@ describe("web-search sources -> url_citation annotations", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); @@ -2005,7 +2005,7 @@ describe("web-search sources -> url_citation annotations", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); const frames = await collectSse(response.body!); @@ -2048,7 +2048,7 @@ describe("web-search batched sources -> url_citation annotations", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 3, }); @@ -2094,7 +2094,7 @@ describe("web-search batched sources -> url_citation annotations", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 3, }); @@ -2172,7 +2172,7 @@ describe("web-search stall deadline", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 600_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 600_000 }, maxSearches: 1, // Bridge clamps to >= 1s and checks on its 2s tick: the hung search dies on the first // silent tick (~4s), proving deps.stallTimeoutSec actually reaches bridgeToResponsesSSE. @@ -2207,7 +2207,7 @@ describe("#398 sidecar failure degradation", () => { forwardProvider, hostedTool: { type: "web_search" }, selectedForwardHeaders: new Headers({ authorization: "Bearer token" }), - settings: { model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 }, + settings: { model: "gpt-5.6-luna", reasoning: "low", timeoutMs: 30_000 }, maxSearches: 1, }); From fa1fe32890a1f89c73f1a3fd024e3a61fba5a9f2 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 11:18:53 +0900 Subject: [PATCH 3/6] fix(codex): repoint a native-alias combo fixture off the retired mini The combo capability test used codex/gpt-5.4-mini as a native alias target, so the alias had no native capabilities left to inherit once membership was removed. gpt-5.5 carries the identical pinned shape (272k window, low..xhigh, default medium, text and image), so every asserted value stays the same and the test keeps proving that a native alias falls back to native capabilities when discovery returns only an id. --- tests/codex-integration/codex-catalog.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/codex-integration/codex-catalog.test.ts b/tests/codex-integration/codex-catalog.test.ts index cc94f48085..c675a862a8 100644 --- a/tests/codex-integration/codex-catalog.test.ts +++ b/tests/codex-integration/codex-catalog.test.ts @@ -1172,7 +1172,7 @@ describe("combo catalog capability intersection", () => { adapter: "openai-chat", baseUrl: "https://nova.example/v1", liveModels: false, - models: ["codex/gpt-5.6-sol", "codex/gpt-5.4-mini"], + models: ["codex/gpt-5.6-sol", "codex/gpt-5.5"], }, }, combos: { @@ -1182,11 +1182,14 @@ describe("combo catalog capability intersection", () => { displayName: "Nova1 - codex-gpt-5.6-sol", targets: [{ provider: "Nova1", model: "codex/gpt-5.6-sol" }], }, - "nova-mini": { - alias: "gpt-5.4-mini", + // gpt-5.5 stands in for the retired gpt-5.4-mini here: same pinned shape + // (272k window, low..xhigh, default medium), and it is still a native, so + // the alias has real capabilities to fall back to. + "nova-old-ladder": { + alias: "gpt-5.5", nativeAlias: true, - displayName: "Nova1 - codex-gpt-5.4-mini", - targets: [{ provider: "Nova1", model: "codex/gpt-5.4-mini" }], + displayName: "Nova1 - codex-gpt-5.5", + targets: [{ provider: "Nova1", model: "codex/gpt-5.5" }], }, }, }; @@ -1201,8 +1204,8 @@ describe("combo catalog capability intersection", () => { reasoningEfforts: ["low", "medium", "high", "xhigh", "max", "ultra"], defaultReasoningEffort: "low", }); - expect(rows.find(row => row.provider === "combo" && row.id === "nova-mini")).toMatchObject({ - alias: "gpt-5.4-mini", + expect(rows.find(row => row.provider === "combo" && row.id === "nova-old-ladder")).toMatchObject({ + alias: "gpt-5.5", nativeAlias: true, contextWindow: 272_000, maxInputTokens: 272_000, From fe01c0f605402fbcc3e0042f38af495c519c3f61 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 11:47:38 +0900 Subject: [PATCH 4/6] fix(codex): stop a persisted row from resurrecting a retired native Removing gpt-5.4 and gpt-5.4-mini from NATIVE_OPENAI_MODELS was not enough on its own. An account-bound observation deliberately admits any native it sees that is NOT already in SUPPORTED_NATIVE_OPENAI_SLUGS - that is how a genuinely new upstream model reaches one entitled account before this repo knows about it. A retired slug fails the same membership test, so a stale selector/gpt-5.4 row left in a user's catalog or models cache was re-observed as an unknown native and synthesized straight back into the picker, one sync after the removal took it out. RETIRED_NATIVE_OPENAI_MODELS is the difference between the two cases: unknown-and-new is admitted, known-and-dead is refused. The guard sits in observedAccountBoundNativeSlug because every observation path funnels through it - the per-selector map, the bare slug list, and the entries carried across a cache replacement. Found by an independent review of the pushed diff, and it is exactly the residual the plan recorded at wp2 as needing proof rather than assumption. The regression test seeds both a full-shape team/gpt-5.4 account row and a bare gpt-5.4-mini row. Local suite: NOT RUN, per the owner's instruction; verified by CI on this head. --- .../260912_codex_gpt54_retirement/040_done.md | 66 +++++++++++++++++++ src/codex/catalog/metadata.ts | 6 ++ src/codex/catalog/native-models.ts | 18 +++++ tests/codex-integration/codex-catalog.test.ts | 28 ++++++++ 4 files changed, 118 insertions(+) create mode 100644 devlog/_plan/260912_codex_gpt54_retirement/040_done.md diff --git a/devlog/_plan/260912_codex_gpt54_retirement/040_done.md b/devlog/_plan/260912_codex_gpt54_retirement/040_done.md new file mode 100644 index 0000000000..dc2eded6a5 --- /dev/null +++ b/devlog/_plan/260912_codex_gpt54_retirement/040_done.md @@ -0,0 +1,66 @@ +# Closing record + +## Outcome + +DONE. `gpt-5.4` and `gpt-5.4-mini` no longer exist on the Codex (ChatGPT OAuth) login +surface, and every default that used to dispatch one of them now uses `gpt-5.6-luna`. +Delivered as PR #4327 against `dev`, head `fa1fe32890`, CI 24 pass / 0 fail. + +Commits: `a8b26e1342` (this roadmap), `5d664b1a6b` (the retirement), +`fa1fe32890` (a combo-alias fixture CI caught). + +## What changed against the plan + +Two reversals, both from audit rather than from build convenience. + +The pinned rows in `src/codex/data/upstream-models.json` stayed. The plan called for +deleting them; the A phase found the file is upstream's snapshot by contract +(`metadata.ts:580`) and already carries rows this runtime does not expose (`gpt-5.2`, +`codex-auto-review`). Both maps built from it iterate `NATIVE_OPENAI_MODELS`, so the +rows are unreachable once membership is gone. Deleting ~205 lines would have changed +no behaviour. + +The maintainer-facing `docs/` tree was missing from the original scope entirely. The +independent reviewer caught it: `docs/shadow-call-intercept.md` still claimed the +default intercept set was both slugs when the code had been luna-only for a while, and +`docs/codex-app-model-catalog.md` used `gpt-5.4` as a staleness example. + +## What CI caught that reading did not + +Two defects survived the audit and the six parallel test workers, and were found only +by pushing: + +1. `tests/codex-integration/codex-catalog.test.ts` had a native-alias combo fixture + targeting `codex/gpt-5.4-mini`. Once membership was gone that alias had no native + capabilities to inherit. `gpt-5.5` carries an identical pinned shape (272k window, + `low..xhigh`, default `medium`, text and image), so every asserted value held after + repointing. +2. `enforce-target` fails on any `gui/` path change without screenshot evidence. There + is no visual change here, so it was waived through the repository's documented + maintainer-comment mechanism with a note stating exactly what the `gui/src` diff is. + +## Verification and its limits + +The owner instructed mid-loop that the local suite must not be run on this machine; a +baseline run confirmed why, reporting ~279 failures unrelated to this change. So +`bun run typecheck`, `bun run test`, `bun run structure:check` and the dashboard lint +are **NOT RUN locally**, and the evidence is repository CI against the final head. +`bun run privacy:scan` and `tests/ci-workflows/repo-hygiene.test.ts` were run before +that instruction arrived, on the devlog commit, and both passed. + +What this does not prove: nothing here exercised a live ChatGPT account. That the +retired slugs now 404 upstream is the premise of the task, not something this unit +verified. + +## What did not improve, and what would falsify this + +The `desktop-3p` 1M-native regression lost its positive control. `gpt-5.4` was the only +native with a 1M window, so the test that proved a provider cap can take `supports1m` +away now only proves no native ever gets it. If a 1M native returns, that test should +regain a positive case rather than stay an absence check. + +The account-namespaced cleanup question was never settled empirically. +`isUnsupportedOpenAiNativeSlug` returns false for any slug containing `/`, so a +persisted `selector/gpt-5.4` row is not dropped by that predicate; it merely stops +being regenerated. A user who had one on disk is the case that would falsify the claim +that this retirement is self-cleaning. diff --git a/src/codex/catalog/metadata.ts b/src/codex/catalog/metadata.ts index d3ebf109a0..0b16e7d7bb 100644 --- a/src/codex/catalog/metadata.ts +++ b/src/codex/catalog/metadata.ts @@ -48,6 +48,7 @@ import { NATIVE_OPENAI_MODELS, SELF_DESCRIBED_NATIVE_OPENAI_MODELS, SUPPORTED_NATIVE_OPENAI_SLUGS, + RETIRED_NATIVE_OPENAI_MODELS, hasNativeOpenAiCapabilityMetadata, isNativeOpenAiCapabilityAliasModel, nativeOpenAiAliasPresentation, @@ -63,6 +64,7 @@ export { NATIVE_OPENAI_MODELS, SELF_DESCRIBED_NATIVE_OPENAI_MODELS, SUPPORTED_NATIVE_OPENAI_SLUGS, + RETIRED_NATIVE_OPENAI_MODELS, hasNativeOpenAiCapabilityMetadata, isNativeOpenAiCapabilityAliasModel, nativeOpenAiAliasPresentation, @@ -694,6 +696,10 @@ function hasNativeCatalogRowShape(entry: RawEntry): boolean { function observedAccountBoundNativeSlug(entry: RawEntry): string | undefined { const accountBound = trustedAccountBoundNativeCatalogSlug(entry); const slug = accountBound ?? (typeof entry.slug === "string" ? entry.slug : ""); + // A retired native is refused at this one choke point rather than at each call site: every + // observation path funnels through here, and an admitted retired slug comes back as a real + // catalog row — bare or account-qualified — with capabilities synthesized from the template. + if (RETIRED_NATIVE_OPENAI_MODELS.has(slug)) return undefined; if (!isAccountBoundOpenAiNativeSlug(slug) || (entry.supported_in_api !== true && !(slug === NATIVE_RESERVE_MODEL && entry.supported_in_api === false)) || (slug === NATIVE_RESERVE_MODEL && entry[RESERVE_METADATA_SOURCE_FIELD] !== undefined diff --git a/src/codex/catalog/native-models.ts b/src/codex/catalog/native-models.ts index fb14a64911..c7bee6d1f8 100644 --- a/src/codex/catalog/native-models.ts +++ b/src/codex/catalog/native-models.ts @@ -163,6 +163,24 @@ export const NATIVE_OPENAI_MODELS = [ export const SUPPORTED_NATIVE_OPENAI_SLUGS = new Set(NATIVE_OPENAI_MODELS); +/** + * Natives this runtime used to ship that upstream has since retired. + * + * Leaving `NATIVE_OPENAI_MODELS` is not enough on its own. An account-bound observation admits + * any native it sees that is NOT already in `SUPPORTED_NATIVE_OPENAI_SLUGS` — that is how a + * genuinely new upstream model reaches one entitled account before this repo knows about it. A + * retired slug fails that same membership test, so a stale `selector/gpt-5.4` row persisted in a + * user's catalog or models cache would be re-observed as an unknown native and synthesized + * straight back into the picker, one sync after the removal took it out. + * + * This set is the difference between the two cases: unknown-and-new is admitted, known-and-dead + * is refused. It is deliberately explicit rather than a version heuristic, because the only + * thing that makes a slug retired is upstream withdrawing it. + */ +export const RETIRED_NATIVE_OPENAI_MODELS: ReadonlySet = new Set([ + "gpt-5.4", "gpt-5.4-mini", +]); + /** * Natives that retain the physical main account as a read-free sentinel during a native-main * drain, instead of reading as unavailable and letting the subagent fallback chain advance. diff --git a/tests/codex-integration/codex-catalog.test.ts b/tests/codex-integration/codex-catalog.test.ts index c675a862a8..794a92bd50 100644 --- a/tests/codex-integration/codex-catalog.test.ts +++ b/tests/codex-integration/codex-catalog.test.ts @@ -9,6 +9,7 @@ import { isGpt56NativeSlug } from "../../src/codex/catalog/effort"; import { nativeOpenAiContextTier, nativeOpenAiMaxInputTokens } from "../../src/codex/catalog"; import { shouldUpgradeToUpstreamEntry } from "../../src/codex/catalog/metadata"; import { applyNativeVisibility, augmentRoutedModelsWithMetadata, augmentRoutedModelsWithRegistryOpenAiApiRows, buildCatalogEntries, buildComboCatalogOmission, catalogModelSlug, clampCatalogModelsToCodexSupport, clampEntryToCodexSupportedEfforts, clampedDefaultEffort, CODEX_ACCOUNT_BOUND_CATALOG_KIND, CODEX_NATIVE_ALIAS_CATALOG_KIND, comboCatalogOmissionReason, deriveComboCatalogModel, exactComboCatalogSlugs, filterCatalogVisibleModels, filterSupportedNativeSlugs, gatherRoutedModels as gatherRoutedModelsDirect, isDatedVariantId, isMediaGenerationModelId, loadBundledCodexCatalog, materializeBundledCodexCatalog, mergeCatalogEntriesForSync, NATIVE_DAYBREAK_BLUE_MODEL, NATIVE_GPT6_ASTRA_MODEL, NATIVE_OPENAI_MODELS, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiCapabilitySourceSlug, nativeOpenAiContextWindow, nativeReasoningEfforts, normalizeRoutedCatalogEntry, resetCatalogRuntimeStateForTests, resetOpenAiApiCatalogWarningStateForTests, resolveComboCatalogMember, shouldExposeRoutedModel, upstreamNativeEntry } from "../../src/codex/catalog"; +import { accountBoundNativeOpenAiSlugsBySelector, observedAccountBoundNativeEntries } from "../../src/codex/catalog"; import { applyProviderConfigHints, fetchProviderModels, mergeConfiguredModelsIntoLiveCatalog } from "../../src/codex/catalog/provider-fetch"; import { CODEX_CUSTOM_MODEL_CATALOG_KIND, @@ -2922,6 +2923,33 @@ describe("legacy custom-model catalog ownership", () => { })); }); + test("a persisted row for a retired native is not re-observed back into the catalog", () => { + const retiredAccountRow = { + // A stale on-disk row for a retired native is the one way membership removal can be undone: + // an observation admits any native NOT in the supported set, which a retired slug also is not. + ...nativeTemplate(), + slug: "team/gpt-5.4", + display_name: "team / GPT-5.4", + supported_in_api: true, + opencodex_catalog_kind: CODEX_ACCOUNT_BOUND_CATALOG_KIND, + }; + const retiredBareRow = { + ...nativeTemplate(), + slug: "gpt-5.4-mini", + display_name: "GPT-5.4-Mini", + supported_in_api: true, + }; + const bySelector = accountBoundNativeOpenAiSlugsBySelector( + { codexAccounts: { team: { accountId: "acct_team" } } } as never, + [retiredAccountRow, retiredBareRow] as never, + ); + for (const slugs of bySelector.values()) { + expect(slugs).not.toContain("gpt-5.4"); + expect(slugs).not.toContain("gpt-5.4-mini"); + } + expect(observedAccountBoundNativeEntries([retiredBareRow] as never)).toEqual([]); + }); + test("legacy evidence cannot claim account-selector or combo rows", () => { const account = { ...nativeTemplate(), From 32bd5417cb59cf4f2168b0d9e5321e176146a2fd Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 12:02:26 +0900 Subject: [PATCH 5/6] refactor(codex): make the retired-model migration testable, and test it The migration was an inline block inside startServer, so the behaviour an operator actually depends on - a stored gpt-5.4-mini being rewritten before the first request - had no test, which the final review called out. It now lives in src/codex/retired-model-migration.ts with the same shape as the existing runClaudeAuthModeMigration: take the config, return whether the caller should persist. Behaviour is unchanged, including the cutoff and the exact-equality match. The tests cover what matters and was previously unproven: all three stored slugs move to gpt-5.6-luna, sibling keys in each block survive, the second call reports no write, and any other stored model is left alone. Local suite: NOT RUN, per the owner's instruction; verified by CI on this head. --- src/codex/retired-model-migration.ts | 41 ++++++++++++++++++++++++++++ src/server/index.ts | 30 ++++---------------- tests/server/config.test.ts | 36 ++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 src/codex/retired-model-migration.ts diff --git a/src/codex/retired-model-migration.ts b/src/codex/retired-model-migration.ts new file mode 100644 index 0000000000..deffe05cba --- /dev/null +++ b/src/codex/retired-model-migration.ts @@ -0,0 +1,41 @@ +/** + * One-time migration off Codex-login models that upstream has retired. + * + * Originally this only moved the two sidecar defaults, and it was written when + * `gpt-5.4-mini` was merely superseded rather than withdrawn. Once OpenAI retired the + * model the same stored value stopped being a stale preference and became a guaranteed + * 404 on every web-search, vision and pool-warmup call, so the pool warmup slug joined + * the set. + * + * The match is exact equality, which means an explicitly chosen `gpt-5.4-mini` is + * rewritten too. That is deliberate: there is no configuration in which continuing to + * send a withdrawn model is what the operator wanted. Any other value is left alone. + */ +import type { OcxConfig } from "../types"; + +/** July 9 21:00 UTC = KST July 10 06:00. */ +export const RETIRED_MODEL_MIGRATION_CUTOFF = Date.UTC(2026, 6, 9, 21, 0); + +const RETIRED_SIDECAR_MODEL = "gpt-5.4-mini"; +const REPLACEMENT_MODEL = "gpt-5.6-luna"; + +/** + * @returns true when the config changed and the caller should persist it. + */ +export function runRetiredCodexModelMigration(config: OcxConfig, now = Date.now()): boolean { + if (now < RETIRED_MODEL_MIGRATION_CUTOFF) return false; + let migrated = false; + if (config.webSearchSidecar?.model === RETIRED_SIDECAR_MODEL) { + config.webSearchSidecar = { ...config.webSearchSidecar, model: REPLACEMENT_MODEL }; + migrated = true; + } + if (config.visionSidecar?.model === RETIRED_SIDECAR_MODEL) { + config.visionSidecar = { ...config.visionSidecar, model: REPLACEMENT_MODEL }; + migrated = true; + } + if (config.tokenGuardian?.codexWarmupModel === RETIRED_SIDECAR_MODEL) { + config.tokenGuardian = { ...config.tokenGuardian, codexWarmupModel: REPLACEMENT_MODEL }; + migrated = true; + } + return migrated; +} diff --git a/src/server/index.ts b/src/server/index.ts index 6c74f1e704..d3d799e7f2 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -192,6 +192,7 @@ import { anthropicErrorResponse } from "../claude/outbound"; import { buildDesktop3pRegistry, generateDesktop3pModels } from "../claude/desktop-3p"; import { buildDesktopDiscoveryInputs } from "../claude/desktop-discovery-inputs"; import { runClaudeAuthModeMigration } from "../claude/auth-mode-migration"; +import { runRetiredCodexModelMigration } from "../codex/retired-model-migration"; import { bindNativeMainStartupLifecycle, blockNativeMainStartupForUnownedServiceHome, @@ -682,30 +683,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server= SIDECAR_MIGRATION_CUTOFF) { - let migrated = false; - if (config.webSearchSidecar?.model === "gpt-5.4-mini") { - config.webSearchSidecar = { ...config.webSearchSidecar, model: "gpt-5.6-luna" }; - migrated = true; - } - if (config.visionSidecar?.model === "gpt-5.4-mini") { - config.visionSidecar = { ...config.visionSidecar, model: "gpt-5.6-luna" }; - migrated = true; - } - if (config.tokenGuardian?.codexWarmupModel === "gpt-5.4-mini") { - config.tokenGuardian = { ...config.tokenGuardian, codexWarmupModel: "gpt-5.6-luna" }; - migrated = true; - } - if (migrated) saveConfig(config); - } - } + // Retired Codex-login models: a stored gpt-5.4-mini is a guaranteed 404 for the search and + // vision sidecars and for pool warmup, so it moves to gpt-5.6-luna. Extracted so the rule is + // testable on its own; see src/codex/retired-model-migration.ts for why exact equality also + // rewrites an explicit choice. + if (runRetiredCodexModelMigration(config)) saveConfig(config); // Resolve unattended service-home authority before any Codex lock, cache, owner, // journal, or credential path. Both positive foreign evidence and an unprovable // ownership state are non-authority. diff --git a/tests/server/config.test.ts b/tests/server/config.test.ts index 2b8cdaa63e..06f3f96a34 100644 --- a/tests/server/config.test.ts +++ b/tests/server/config.test.ts @@ -43,6 +43,7 @@ import { migrateXaiResponsesDefault } from "../../src/providers/xai-responses-op import { migrateStartupXaiResponses } from "../../src/server/xai-responses-startup"; import * as configStore from "../../src/config"; import { runClaudeAuthModeMigration } from "../../src/claude/auth-mode-migration"; +import { runRetiredCodexModelMigration, RETIRED_MODEL_MIGRATION_CUTOFF } from "../../src/codex/retired-model-migration"; import { providerManagementConfigError } from "../../src/server/auth-cors"; import { removeTreeWithRetry } from "../helpers/remove-tree"; let testDir = ""; @@ -186,6 +187,41 @@ describe("Astra-first subagent upgrade", () => { expect(loadConfig().subagentModels).toEqual(migrated.subagentModels); }); + test("a stored retired model moves to the live floor, including the pool warmup slug", () => { + const after = RETIRED_MODEL_MIGRATION_CUTOFF + 1; + const stored = { + ...getDefaultConfig(), + webSearchSidecar: { model: "gpt-5.4-mini", reasoning: "low" }, + visionSidecar: { model: "gpt-5.4-mini" }, + tokenGuardian: { codexWarmupEnabled: true, codexWarmupModel: "gpt-5.4-mini" }, + } as never as ReturnType; + + expect(runRetiredCodexModelMigration(stored, after)).toBe(true); + expect(stored.webSearchSidecar?.model).toBe("gpt-5.6-luna"); + expect(stored.visionSidecar?.model).toBe("gpt-5.6-luna"); + expect(stored.tokenGuardian?.codexWarmupModel).toBe("gpt-5.6-luna"); + // Sibling keys survive: this rewrites one slug, it does not rebuild the block. + expect(stored.webSearchSidecar?.reasoning).toBe("low"); + expect(stored.tokenGuardian?.codexWarmupEnabled).toBe(true); + // Idempotent, so a second start does not report a write it does not need. + expect(runRetiredCodexModelMigration(stored, after)).toBe(false); + }); + + test("the retired-model migration leaves any other stored slug alone", () => { + const after = RETIRED_MODEL_MIGRATION_CUTOFF + 1; + const chosen = { + ...getDefaultConfig(), + webSearchSidecar: { model: "claude-sonnet-5" }, + visionSidecar: { model: "gpt-5.6-terra" }, + tokenGuardian: { codexWarmupModel: "gpt-5.5" }, + } as never as ReturnType; + + expect(runRetiredCodexModelMigration(chosen, after)).toBe(false); + expect(chosen.webSearchSidecar?.model).toBe("claude-sonnet-5"); + expect(chosen.visionSidecar?.model).toBe("gpt-5.6-terra"); + expect(chosen.tokenGuardian?.codexWarmupModel).toBe("gpt-5.5"); + }); + test("unavailable persistence leaves malformed disk bytes untouched", () => { const legacy = { ...getDefaultConfig(), subagentModelsVersion: undefined, subagentModels: ["one"] }; writeConfig("{ invalid"); From 2c26549d003035db98ab1026f5f21768586c10c5 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 12:12:54 +0900 Subject: [PATCH 6/6] docs(devlog): close the gpt-5.4 retirement unit Records what the loop actually produced, including the two things that arrived after the first green CI run: the account-observation path that would have resurrected a retired slug from a stale on-disk row, and the startup migration that had no test. Also narrows the remaining caveat to what is genuinely unproven - a stale selector/gpt-5.4 row is no longer re-admitted as evidence, but it is not actively deleted from a user's catalog file either. --- .../260912_codex_gpt54_retirement/040_done.md | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/devlog/_plan/260912_codex_gpt54_retirement/040_done.md b/devlog/_plan/260912_codex_gpt54_retirement/040_done.md index dc2eded6a5..1c2d396f43 100644 --- a/devlog/_plan/260912_codex_gpt54_retirement/040_done.md +++ b/devlog/_plan/260912_codex_gpt54_retirement/040_done.md @@ -4,10 +4,11 @@ DONE. `gpt-5.4` and `gpt-5.4-mini` no longer exist on the Codex (ChatGPT OAuth) login surface, and every default that used to dispatch one of them now uses `gpt-5.6-luna`. -Delivered as PR #4327 against `dev`, head `fa1fe32890`, CI 24 pass / 0 fail. +Delivered as PR #4327 against `dev`, final head `32bd5417cb`, CI green. Commits: `a8b26e1342` (this roadmap), `5d664b1a6b` (the retirement), -`fa1fe32890` (a combo-alias fixture CI caught). +`fa1fe32890` (a combo-alias fixture CI caught), `fe01c0f605` (the resurrection guard a +review caught), `32bd5417cb` (the migration extracted and tested). ## What changed against the plan @@ -39,6 +40,29 @@ by pushing: is no visual change here, so it was waived through the repository's documented maintainer-comment mechanism with a note stating exactly what the `gui/src` diff is. +## What the final review caught that CI did not + +CI was green and the change was still wrong in one place. An independent reviewer read +the pushed diff and found that removing the slugs from `NATIVE_OPENAI_MODELS` does not +keep them out: an account-bound observation deliberately admits any native that is NOT +in `SUPPORTED_NATIVE_OPENAI_SLUGS`, which is how a genuinely new upstream model reaches +one entitled account early. A retired slug fails that same test, so a stale +`selector/gpt-5.4` row persisted in a user's catalog or models cache would have been +re-observed as an unknown native and synthesized straight back into the picker, one sync +after the removal took it out. + +`RETIRED_NATIVE_OPENAI_MODELS` is the distinction the code was missing: unknown-and-new +is admitted, known-and-dead is refused. The guard sits in `observedAccountBoundNativeSlug` +because every observation path funnels through it. + +This is the residual wp2 recorded as needing proof rather than assumption, and it is the +reason that residual was worth writing down: no test covered it, so no test failed. + +The same review noted the widened startup migration had no test at all. It is now +`src/codex/retired-model-migration.ts`, shaped like the existing +`runClaudeAuthModeMigration`, with tests for the three stored slugs, sibling-key +survival, idempotence, and leaving any other model alone. + ## Verification and its limits The owner instructed mid-loop that the local suite must not be run on this machine; a @@ -59,8 +83,10 @@ native with a 1M window, so the test that proved a provider cap can take `suppor away now only proves no native ever gets it. If a 1M native returns, that test should regain a positive case rather than stay an absence check. -The account-namespaced cleanup question was never settled empirically. -`isUnsupportedOpenAiNativeSlug` returns false for any slug containing `/`, so a -persisted `selector/gpt-5.4` row is not dropped by that predicate; it merely stops -being regenerated. A user who had one on disk is the case that would falsify the claim -that this retirement is self-cleaning. +The account-namespaced question turned out to be the real defect rather than a caveat, +and it is now fixed and tested. What remains unproven is the disk side: a persisted +`selector/gpt-5.4` row is no longer re-admitted as evidence, but +`isUnsupportedOpenAiNativeSlug` still returns false for any slug containing `/`, so the +stale row itself is not actively deleted from a user's catalog file. It stops being +regenerated and stops being observed; whether it lingers in a file until the next full +rewrite was not measured against a real installation.