* fix(sse): release native Codex turn pin when pinned model is model-scoped unusable (#13564)
Long-running Codex sessions die with 400 NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE whenever
the model pinned to the current turn becomes model-scoped unusable mid-session (per-model
quota lockout, connection cooldown, exhausted accounts). Claude Code has no equivalent
pin and already falls back to the next healthy combo model; Codex now matches.
Release the turn pin when all pinned provider+model targets are model-scoped unusable
and fall through to full combo routing, re-pinning to whichever model succeeds. Preserve
the pin on provider-wide outages (circuit breaker OPEN, provider cooldown) and when the
pinned target is still healthy.
Also prunes the stale ESLint suppression entry for combo.ts that this change orphaned
(createPinnedModelUnavailableResponse import dropped; pre-existing getBootstrapLatencyMs
remains the sole residual unused var).
* fix(api): resolve codex-settings apiKey via canonical resolver instead of 400 (#13563)
Applying Codex settings from /dashboard/cli-code/codex always failed with
400 "baseUrl, apiKey and model are required" when the dashboard sent an empty
apiKey (cloud mode with no management key selected) — baseUrl and model are
already Zod-gated, so that response could only ever fire on the empty key.
The codex-settings route had diverged from the sibling CLI tools (cline/forge/
openclaw/grok-build/jcode): an inline if(!apiKey) 400 guard plus a hand-rolled
getApiKeyById lookup, instead of the shared resolveApiKey(keyId, apiKey) helper
which resolves by keyId, falls back to the submitted apiKey, then to
sk_omniroute. This change makes codex-settings use the canonical resolver, so:
- empty apiKey + valid keyId -> the real DB key is written to auth.json
- empty apiKey + no keyId -> sk_omniroute default (config still applies)
- explicit apiKey -> written verbatim (unchanged)
* docs(changelog): add fragments for Codex turn-pin fallback and codex-settings apiKey resolution
Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca.
O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
PR #10573 landed with two real defects surfaced by typecheck/tests on
the combined release tip:
- TS2322: allowedConnectionIds (string[]) was built from
compatible.map(t => t.connectionId), whose type includes null.
Filter nulls before assigning.
- applyNativeCodexTurnPin never assigned the pinned connectionId onto
a compatible candidate that didn't already carry it (e.g. an
unresolved placeholder target with connectionId: null) — the pin
was silently dropped instead of applied. Now resolves the pinned
slot's connectionId explicitly (in original order, so
allowedConnectionIds stays consistent regardless of pinned-first
reordering) before building the returned target list.
Confirmed via the existing focused suites:
tests/unit/chatgpt-web-codex-turn-pin.test.ts and
tests/unit/native-codex-turn-pin-10379.test.ts (14/14 pass),
typecheck:core clean.
* fix(combo): allow fill-first failover across Codex OAuth connections
applyNativeCodexTurnPin previously narrowed the target pool to the
single pinned connection, making same-provider failover impossible when
the pinned connection was rejected by pre-dispatch checks. Return all
compatible connections (same provider + model) with the pinned connection
first, so the combo engine can fall over to siblings. Also allow
pinNativeCodexTurn to update connectionId for failover recovery while
still rejecting provider/model changes.
Fixes#10379
Signed-off-by: Minxi Hou <houminxi@gmail.com>
* fix(test): replace as any with properly typed ResolvedComboTarget literal
Addresses ESLint no-explicit-any error in tests/.
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---------
Signed-off-by: Minxi Hou <houminxi@gmail.com>
* Bypass proxy compaction for native Codex context
* Add native ChatGPT Web provider pipeline
* Add managed browser and tunnel deployment
* Add ChatGPT Web setup and doctor UI
* Document and test ChatGPT Web integration
* fix(security): register chatgpt-web-codex-doctor in LOCAL_ONLY_API_PATTERNS
The diagnostic route under /api/providers/{id}/chatgpt-web-codex-doctor
was not registered in the spawn-capable route guard. Adding it for
parity with the existing /login pattern.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): route chatgpt-web-codex admin routes through a service boundary
The provider CRUD/doctor routes imported chatgpt-web-codex helpers
(finalizeValidatedChatGptWebCodexSecrets, encode/decodeChatGptWebCodexSecrets,
getChatGptWebCodexDoctorStatus) directly from open-sse/executors/**, which
no-restricted-imports (EXECUTOR_IMPORT_RESTRICTION) forbids for src/app/**
files — executor implementations must stay behind an open-sse handler or
service boundary.
Add open-sse/services/chatgptWebCodexAdmin.ts as a thin re-export boundary
(mirroring the existing tokenRefresh.ts re-export pattern) and import from
there instead. No behavior change.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>