Skip to content

fix(codex): isolate continuation state per Claude Agent - #96

Merged
raine merged 4 commits into
raine:mainfrom
ItsAlbertZhang:fix/codex-agent-continuation
Aug 2, 2026
Merged

fix(codex): isolate continuation state per Claude Agent#96
raine merged 4 commits into
raine:mainfrom
ItsAlbertZhang:fix/codex-agent-continuation

Conversation

@ItsAlbertZhang

@ItsAlbertZhang ItsAlbertZhang commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Claude Code SubAgents naturally share a session ID while carrying distinct direct Agent IDs. Codex continuation state was previously keyed only by the shared session, allowing Main and sibling Agents to consume or supersede one another's continuation and reusable WebSocket state.

This PR provides the narrowest safe end-to-end behavioral fix for issue #95:

  • isolate Main and direct Agent continuation owners;
  • require continuation to use the exact live WebSocket that produced it;
  • fall back safely when identity or origin-socket reuse cannot be proven;
  • preserve genuine downstream-cancellation cleanup across retries and streams.

The behavioral scope is intentionally narrow, although the implementation and regression coverage cross HTTP ingress, continuation state, and WebSocket lifecycle boundaries.

Ownership model

Continuation identity is defined as:

  • Main: session ID
  • Agent: (session ID, direct Agent ID)
  • Nested Agent: keyed by its own direct Agent ID
  • Parent Agent ID: validated, but not included in owner equality

Missing, malformed, duplicated, or ambiguous identity does not reject the HTTP request. It disables continuation and reusable-socket state for that request.

Detected auto-review classifier requests are also stateless.

Socket provenance and recovery

A reusable response ID remains bound to the exact live WebSocket that produced it. It is never moved to a new or replacement connection.

If the originating socket is missing, dead, or replaced, Codex continuation performs at most one full-context recovery attempt without the stale response ID.

Provider-controlled live retries preserve the logical continuation reservation while closing the abandoned attempt socket. Genuine downstream cancellation still clears continuation and compaction state during retry backoff, replacement startup, and post-first-chunk streaming.

Same-owner stale completions cannot overwrite newer state, while completion ordering across different owners remains independent.

Compatibility

Existing public Rust forms remain source-compatible.

Direct use of legacy continuation recording without socket provenance intentionally fails closed and cannot publish reusable continuation state. This PR does not claim full behavioral compatibility for that unsupported low-level path.

Existing feature defaults are unchanged, and continuation state remains in-memory only.

Validation

  • cargo fmt --all -- --check
  • git diff --check
  • cargo check --locked --all-targets
  • cargo test --locked --all-targets -- --test-threads=1
    • 890 passed
    • 0 failed
    • 0 ignored
  • public_codex_api_compat
    • 1 passed
  • codex_agent_continuation
    • 11 passed
    • run twice consecutively
  • WebSocket suites
    • 10 passed
  • Relevant smoke_codex coverage
    • 24 passed
  • cargo clippy --locked --all-targets
    • no branch-introduced warnings
  • Documentation build
    • 21 pages

Tests used local mocks and an isolated configuration directory. No live-wire provider or OAuth testing was performed.

Non-goals

This PR does not change:

  • provider affinity;
  • prompt-cache identity;
  • server-compaction ownership;
  • general route, account, or credential binding;
  • native OpenAI identity behavior;
  • Read-correction state;
  • other providers;
  • 401 replay behavior;
  • persistence;
  • continuation feature defaults.

Addresses #95

🤖 Generated with Claude Code

@ItsAlbertZhang
ItsAlbertZhang marked this pull request as ready for review August 2, 2026 13:22
ItsAlbertZhang and others added 4 commits August 2, 2026 17:52
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@raine
raine force-pushed the fix/codex-agent-continuation branch from 798ebd8 to a5d4e4c Compare August 2, 2026 14:52
@raine
raine merged commit ff68823 into raine:main Aug 2, 2026
2 checks passed
@raine

raine commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Thanks @ItsAlbertZhang for the thorough fix and comprehensive regression coverage!

@ItsAlbertZhang

ItsAlbertZhang commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

@raine It looks like main was rewritten after #96 was squash-merged. The squash commit ff68823 and the actual changes are no longer reachable from main (and #54 appears to have disappeared as well). Was this intentional, or should I reopen/rebase the fix?

@raine

raine commented Aug 2, 2026

Copy link
Copy Markdown
Owner

A little mishap, sorry for the confusion

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