Skip to content

fix(combo): auto-resume pinned native Codex turns (re-land of #13180) - #14162

Merged
diegosouzapw merged 9 commits into
release/v3.8.51from
fix/13180-native-codex-auto-resume-reland
Sep 19, 2026
Merged

diegosouzapw merged 9 commits into
release/v3.8.51from
fix/13180-native-codex-auto-resume-reland

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Re-land of #13180 by @mdigitalbh81 — the fork does not accept maintainer pushes, so this PR carries their commits with authorship intact.

Follow-up to #12240. Supersedes #13180.

⚠️ base-red inherited: #13866 (unrelated: .env.example docs-sync gap opened by #13344; not touched by this PR).

Summary (original, by @mdigitalbh81)

Native Codex turns remain pinned to the same provider/model while eligible sibling connections for that model still exist.

If every target for the pinned provider/model becomes unusable for a model-scoped reason, such as quota exhaustion or model lockout, this change safely advances the logical turn generation and resumes the same Codex turn on another healthy model from the combo.

The resumed model is then pinned for the new logical generation, so subsequent requests for the same thread_id + turn_id continue consistently on that model.

Safety constraints

  • Pin scope remains thread_id + turn_id; it is not global.
  • Sibling connections for the same pinned provider/model are preferred before cross-model resume.
  • Auto-resume is limited to model-scoped unavailability.
  • Provider-wide circuit breaker/cooldown conditions do not trigger cross-model resume.
  • Pending tool calls or opaque/provider-specific continuation state reject auto-resume.
  • Partial-stream failure does not dispatch another model mid-stream.
  • At most one auto-resume is allowed per logical turn.
  • Failed auto-resume dispatch does not advance generation or cascade to a third model.

What this PR adds on top of #13180

The base branch moved from under this PR while it was parked: #13564 (merged today,
2026-09-18) independently changed the same code path — when a pinned native Codex turn's
model becomes model-scoped unusable, release the pin and fall through to full combo
routing instead of terminating with 400 NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE.

Re-landing #13180 on top of #13564 needed two small follow-up commits, both under my own
authorship, on top of Felipe's unmodified commits:

  1. releaseNativeCodexTurnPin renamed map — fix(providers): codex: long native sessions die with NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE instead of falling back to a healthy combo model #13564 renamed the module-level pin map
    from pins to turns; one stray pins.delete(key) call needed the same rename.

  2. Reconcile auto-resume's safety bounds with fix(providers): codex: long native sessions die with NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE instead of falling back to a healthy combo model #13564's fallback — the merge conflict
    resolution had applied fix(providers): codex: long native sessions die with NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE instead of falling back to a healthy combo model #13564's "release pin and fall through" behavior to fix(combo): auto-resume pinned native Codex turns #13180's
    auto-resume-not-eligible branch as well. That silently dropped two of fix(combo): auto-resume pinned native Codex turns #13180's explicit
    safety constraints:

    • "Pending tool calls or opaque/provider-specific continuation state reject auto-resume"
      was only supposed to reject the auto-resume mechanism — falling through to plain
      combo routing instead hands that same unsafe state to an untested alternate model,
      which is exactly what the check exists to prevent.
    • "At most one auto-resume is allowed per logical turn" was bypassed: once eligibility
      was rejected for max_resumes_exceeded, falling through let the request cascade to a
      third model instead of terminating.

    Fix: only the rejection reasons that mean unsafe state or an exhausted resume budget
    (pending_tool_call, unsafe_provider_state, no_alternate_target,
    no_healthy_alternate_target, max_resumes_exceeded) still terminate the turn with
    400. Every other reason (e.g. a request body that doesn't use the Responses-API
    input/messages shape fix(combo): auto-resume pinned native Codex turns #13180's eligibility check needs) keeps fix(providers): codex: long native sessions die with NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE instead of falling back to a healthy combo model #13564's plain
    fallback, so non-native-turn-shaped Codex requests are unaffected.

Validation

  • tests/unit/native-codex-auto-resume.test.ts: 15/15 passed (4 failed against the raw
    merge before the reconciliation fix above — opaque continuation state, pending tool
    call, no healthy alternate, and the max-resumes cascade all dispatched to an alternate
    model instead of terminating; TDD-verified per Hard Rule fix(ci): add environment for npm token access #18)
  • tests/unit/native-codex-turn-pin-model-scoped-fallback.test.ts (fix(providers): codex: long native sessions die with NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE instead of falling back to a healthy combo model #13564's own suite):
    7/7 passed
  • Broader regression sweep: chatgpt-web-codex-turn-pin, chatgpt-web-codex,
    native-codex-turn-pin-10379, combo-context-overflow-compression-probe,
    combo-context-window-filter, combo-responses-sse-failure-fallback,
    perf-waterfall-elimination: 81/81 passed
  • npm run typecheck:core: passed
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json on all
    touched files: clean
  • node scripts/check/check-complexity-ratchets.mjs --base-ref origin/release/v3.8.51:
    flags a pre-existing new-code complexity regression in
    open-sse/services/combo/nativeCodexTurnPin.ts, entirely inside fix(combo): auto-resume pinned native Codex turns #13180's original
    ~500-line addition (not touched by either follow-up commit here). Not rebaselined or
    refactored in this PR — flagging for an owner decision rather than restructuring a
    contributor's logic without a failing test driving it.
  • Live validation (from fix(combo): auto-resume pinned native Codex turns #13180, unchanged by this re-land): performed on ARM64 with a real
    Antigravity Opus 429/quota exhaustion — the pinned turn became eligible for auto-resume,
    routed to Gemini, completed with HTTP 200, and subsequent requests for the same turn
    remained pinned to Gemini.

mdigitalbh81 and others added 9 commits September 10, 2026 03:06
…13180)

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…reland

Resolves the combo.ts conflict against #13564 (merged 2026-09-18), which
also touches the native-Codex-turn-pin-unusable path: it now releases the
pin and falls through to full combo routing instead of terminating the
turn. Kept #13180's bounded, safety-gated auto-resume
(canAutoResumeNativeCodexTurn: sibling-connection preference, pending
tool-call / opaque continuation-state blocks, 1-resume-per-turn cap) as
the first recourse when the pinned model becomes model-scoped unusable;
when auto-resume is not eligible, fall through to #13564's general
release-and-full-combo-routing path instead of the PR's original
terminate-the-turn fallback, so neither fix regresses the other.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…ter base merge

The base merge renamed the module-level pin map from `pins` to `turns`,
but releaseNativeCodexTurnPin() still called the old, now-undefined
`pins.delete(key)`. The remaining `rec.pins.*` calls are a record field
on turn entries and are unaffected.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…13564 merge

The base merge brought in #13564 (release the native Codex turn pin and fall
through to full combo routing whenever the pinned model is model-scoped
unusable). The merge conflict resolution applied that same "release pin and
fall through" behavior to #13180's auto-resume-not-eligible branch too,
which silently dropped #13180's explicit safety constraints:

- "Pending tool calls or opaque/provider-specific continuation state reject
  auto-resume" only meant reject AUTO-RESUME in the original design; falling
  through to #13564's plain combo routing hands that same unsafe state to an
  untested alternate model instead, which is exactly what the check exists
  to prevent.
- "At most one auto-resume is allowed per logical turn" was bypassed:
  once eligibility was rejected for `max_resumes_exceeded`, falling through
  let the request cascade to a THIRD model instead of terminating.

Confirmed via tests/unit/native-codex-auto-resume.test.ts: 4 of 15 tests
failed against the merged tip before this fix (opaque continuation state,
pending tool call, no healthy alternate, and max-resumes cascade all
dispatched to an alternate model instead of terminating with 400
NATIVE_CODEX_PINNED_MODEL_UNAVAILABLE).

Fix: only the reasons that indicate unsafe state or an exhausted resume
budget (pending_tool_call, unsafe_provider_state, no_alternate_target,
no_healthy_alternate_target, max_resumes_exceeded) still terminate the turn.
Every other rejection reason (e.g. a request body that doesn't use the
Responses-API input/messages shape #13180's eligibility check needs) keeps
#13564's plain fallback, so non-native-turn-shaped Codex requests are
unaffected.

Verified: tests/unit/native-codex-auto-resume.test.ts (15/15) and
tests/unit/native-codex-turn-pin-model-scoped-fallback.test.ts (7/7, #13564's
own suite) both pass; typecheck:core and ESLint (with suppressions) clean.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…ection branch typechecks

Without strictNullChecks the truthiness test on the `eligible` discriminant does
not remove the eligible:true member in the else branch, so `.reason` raised
TS2339 x3 in check:api-typecheck and check:open-sse-typecheck (both red on the
PR run, both green on the tip). `=== true` narrows in both directions.
…ex-auto-resume.test.ts

The re-landed suite was 1540 lines, above the 1200-line cap for new test files.
The three cases that only exercise hasUnresolvedToolCalls /
hasProviderSpecificUnsafeContinuationState / MAX_AUTORESUMES_PER_TURN need no DB
or combo fixture, so they move verbatim to native-codex-auto-resume-guards.test.ts
(416 lines); the combo-flow file drops to 1131. 15/15 across both files.
@diegosouzapw
diegosouzapw merged commit cd4c6f6 into release/v3.8.51 Sep 19, 2026
12 of 21 checks passed
diegosouzapw added a commit that referenced this pull request Sep 21, 2026
Three squash merges on release/v3.8.51 lost the contributor attribution that
the pipeline had preserved on the branches:

- 7a92129 (#14159) re-landed #12630: the dual-layer semantic cache is
  @BillyOutlast's work; the squash author is the maintainer.
- cd4c6f6 (#14162) re-landed #13180: the native Codex auto-resume fix is
  Felipe Reis's (@mdigitalbh81); the squash author is the maintainer.
- 53a147c (#13295) shipped the same one-line resolvedExtensionEnd reorder
  that @ggiak landed first in #13036; the squash carried no trailer.

The changelog fragment and these trailers record that credit in the branch
history, the contributors graph and the release notes.

Co-authored-by: BillyOutlast <172061051+BillyOutlast@users.noreply.github.com>
Co-authored-by: Felipe Reis <mdigitalbh81@gmail.com>
Co-authored-by: Giorgos Giakoumettis <giorgos@yiakoumettis.gr>
Co-authored-by: ggiak <20743694+ggiak@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…uzapw#13180) (diegosouzapw#14162)

Re-land of diegosouzapw#13180 by @mdigitalbh81 (their commits carried with authorship intact), merged via /merge-batch (2026-09-19) on top of the current `release/v3.8.51` tip.

**Reconciled before landing (three maintainer commits on top of the re-land):**
- `fe80cfb5` — `check:api-typecheck` / `check:open-sse-typecheck` were red on the PR's own head with `TS2339 'reason' does not exist on type 'AutoResumeDecision'` ×3 in `open-sse/services/combo.ts` (green on the tip). Without `strictNullChecks` the truthiness test on the `eligible` discriminant does not narrow the else-branch; `=== true` does.
- `7ee0e5d5` — `tests/unit/native-codex-auto-resume.test.ts` was 1540 lines, above the 1200-line new-test cap. The three pure guard cases (`hasUnresolvedToolCalls`, `hasProviderSpecificUnsafeContinuationState`, `MAX_AUTORESUMES_PER_TURN`) moved verbatim to `native-codex-auto-resume-guards.test.ts`; the combo-flow file is now 1131 lines.
- `93d75989` — `executeTargetAttempt.ts` 1258→1273 rebaselined with a dated justification (own growth: generation advance + log at the pin site).

**Evidence on the merged tree:** `native-codex-auto-resume{,-guards}.test.ts` 15/15; `native-codex-turn-pin-model-scoped-fallback`, `native-codex-turn-pin-10379`, `chatgpt-web-codex-turn-pin` 33/33 total; broader combo sweep (`combo-responses-sse-failure-fallback`, `combo-context-window-filter`, `perf-waterfall-elimination`, `chatgpt-web-codex`, `combo-context-overflow-compression-probe`) 70/70; api/open-sse typecheck clean for the PR's files; file-size, changelog-integrity, complexity and cognitive-complexity gates OK.

**Inherited, not from this PR** (reproduced on the pure tip): the 23 unit reds in the fast-path shards (vi locale parity, pack-artifact allowlists, `.env.example` sync, casing, budget fallback, etc.), the 5 `no-unused-vars` lint errors (`cliRuntime.ts`, `arena-elo-sync-redesign`, `compressionAnalyticsWriterFlatRate`, `waitForServer-slow-first-response`), the `omni-version-manager` generated-skill drift, `tinycmsDomMocks.ts` / `rerankProviderNodes.ts` / `antigravity.ts` typecheck errors, and the 4 env vars missing from `.env.example`.

Supersedes diegosouzapw#13180. Follow-up to diegosouzapw#12240.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants