Skip to content

fix(server): Codex threads survive protocol drift on resume - #8342

Open
D3OXY wants to merge 3 commits into
pingdotgg:mainfrom
D3OXY:d3oxy/fix-codex-resume-activity
Open

fix(server): Codex threads survive protocol drift on resume#8342
D3OXY wants to merge 3 commits into
pingdotgg:mainfrom
D3OXY:d3oxy/fix-codex-resume-activity

Conversation

@D3OXY

@D3OXY D3OXY commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Problem

Codex CLI 0.150.0 added a fourth SubAgentActivityKind"completed" — alongside started, interacted, and interrupted. Our generated app-server bindings are pinned to an openai/codex revision from 2026-07-19 that knows only three, so decoding a thread/resume response whose history contained that kind failed:

SchemaError: Expected "started" | "interacted" | "interrupted"
  at ["thread"]["turns"][0]["items"][18]["kind"]

isRecoverableThreadResumeError only matches "thread not found"-style messages, so a schema error was fatal. Every reopen of an affected thread failed the same way and the conversation could never be continued — the reporter's words: "Blocks work completely."

The same drift hit item/started / item/completed notifications, where decode failures were swallowed silently, so subagent lifecycle events were dropped with no trace.

Fix

thread/start and thread/resume now decode only the three fields the session runtime actually consumes (thread.id, cwd, model). Codex replays an entire thread history in those responses; session state is rebuilt from notifications anyway, so history this build cannot name no longer fails the request.

client.request takes an optional response schema for that purpose — params are still encoded with the generated schema, only the response decode is narrowed.

Two smaller changes complete the recovery path:

  • A response that still cannot be decoded now counts as a recoverable resume error, so the thread falls back to a fresh Codex session instead of becoming permanently unopenable.
  • Notifications that cannot be decoded are logged as a warning instead of vanishing.

Why not just regenerate the bindings

Regenerating to 0.150.0 moves the breakage rather than removing it: upstream also made Thread.projectId required in that release, so bindings pinned forward would reject responses from every CLI at 0.149 or older. Bindings should still be refreshed on their own cadence — this change is what absorbs the gap between refreshes. docs/internals/providers.md records the constraint.

Verification

packages/effect-codex-app-server/src/client.test.ts drives the real client against the mock peer replaying a resume payload with kind: "completed", and asserts the generated schema rejects it with operation: "decode-payload" while the narrow schema resumes cleanly. CodexSessionRuntime.test.ts covers the same shape through openCodexThread, plus the decode-error fallback. Both new tests fail against the previous decode path.

apps/server        CodexSessionRuntime/CodexAdapter/CodexCollabWire  80 passed
effect-codex-app-server                                              21 passed
typecheck + lint   clean on touched packages

No UI surface changes.

Fixes #8322


Model: Claude Opus 5. Harness: T3 Code.

Note

Make openCodexThread survive protocol drift on resume by decoding minimal fields

  • openCodexThread now decodes only thread.id, cwd, and model from thread/start and thread/resume responses using a narrow CodexThreadOpenResponse schema, instead of requiring the full generated payload to validate.
  • isRecoverableThreadResumeError now treats CodexAppServerRequestError with operation decode-payload as recoverable, so decode failures during resume trigger a warning and fall back to thread/start.
  • CodexAppServerClient.Service.request accepts an optional caller-supplied response schema; undecodable notifications are dropped with a single warning per method instead of being silently ignored.
  • Added guidance in providers.md on handling generated Codex bindings drift via narrow schemas and recoverable decode failures.
  • Risk: the narrowed CodexThreadOpenResponse schema in CodexSessionRuntime.ts only extracts three fields; if the runtime later needs additional response fields, the schema must be widened or decode will silently drop them.

Macroscope summarized 0577e31.

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1327e674-40f3-488a-b049-070ce334c401

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 27, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 27, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 0577e31

Macroscope's review found this PR approvable — This is a focused Codex protocol-drift compatibility fix: existing request behavior remains intact, while thread opening decodes only the fields it uses and handles unsupported history through the established recovery path. Tests cover resumed drift, fallback, caller-supplied schemas, and bounded notification warnings.

Notes:

  • Macroscope's correctness review did not run, so approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

@D3OXY

D3OXY commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up commit addressing review findings:

  • cwd and model are now required in the narrow thread-open schema. Defaulting them to the requested values could record a model Codex never actually honored; both fields are required in every Codex release in range, so reading them honestly is right.
  • Undecodable notifications now warn once per method rather than once per message. Drift in a high-frequency stream such as item/agentMessage/delta would otherwise emit a warning per token.
  • The thread-open test double is faithful to the real client: generated param types, and the same invalidPayload error the client raises.
  • Added a test covering the drop-and-warn path via the mock peer.

Known gap, deliberately not in this PR: the same enum drift affects item/started / item/completed, so a subAgentActivity item with kind: "completed" is still dropped during a live turn. Subagent registration also happens via thread/started, so this costs a terminal activity item rather than hanging a turn — but it is real. Fixing it needs either a bindings refresh or per-notification narrowing, which is a separate concern from the fatal resume path.

@macroscopeapp
macroscopeapp Bot dismissed their stale review August 27, 2026 01:19

Dismissing prior approval to re-evaluate b806269

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 27, 2026
D3OXY added 3 commits August 27, 2026 10:47
Codex CLI 0.150.0 added a fourth subAgentActivity kind ("completed").
The generated app-server bindings are pinned to a July revision that
knows three, so decoding a thread/resume response containing that kind
failed, and because a schema error is not a recoverable resume error the
thread could never be opened again.

thread/start and thread/resume now decode only the fields the session
runtime consumes, so history this build cannot name no longer fails the
request. A response that still cannot be decoded is treated as
recoverable and falls back to a fresh Codex thread, and notifications we
cannot decode are logged instead of silently dropped.

Fixes pingdotgg#8322
Require cwd and model in the narrow thread-open schema rather than
inventing fallbacks: both are required in every Codex release we
support, and defaulting to the requested model could record a model
Codex never honored.

Warn once per method for undecodable notifications instead of once per
message, so drift in a high-frequency stream such as agent message
deltas cannot warn per token, and cover that with a test.

Make the thread-open test double faithful to the real client: generated
param types, and the same invalidPayload error the client raises.
pingdotgg#8346 taught the generated bindings the Codex 0.150 enum values, so
asserting that they reject "completed" no longer tests anything. Point
the fixtures at a kind the bindings do not name instead, which keeps
exercising drift after they catch up with any given release.

Also record the generator's definition overrides alongside the two
drift rules in the providers doc: overrides fix the variants we already
know about, the rules cover the ones we do not.
@D3OXY
D3OXY force-pushed the d3oxy/fix-codex-resume-activity branch from b806269 to 0577e31 Compare August 27, 2026 05:19
@D3OXY

D3OXY commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main now that #8346 has landed. No file overlap, so it merged clean — but one assertion here went stale and is fixed in 0577e31.

client.test.ts asserted that the generated bindings reject kind: "completed". #8346 taught them that value, so the assertion tested nothing (and failed outright, since the request now succeeds where the test expected a decode error). The fixtures now use a kind no build names, which keeps exercising drift after the bindings catch up with any given release.

That is the honest division between the two PRs: #8346 fixes the variants we know about today, this one keeps the ones we do not know about yet from being fatal. Also recorded the generator's definition overrides next to the two drift rules in docs/internals/providers.md, so the doc describes both halves.

Codex tests green post-rebase: 80 in apps/server, 23 in effect-codex-app-server.

@macroscopeapp
macroscopeapp Bot dismissed their stale review August 27, 2026 05:19

Dismissing prior approval to re-evaluate 0577e31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Regression of #3742: Codex thread/resume rejects persisted subagent activity kind

1 participant