fix(server): Codex threads survive protocol drift on resume - #8342
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
ApprovabilityVerdict: Approved at 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:
You can add or adjust custom eligibility rules. Learn more. |
|
Follow-up commit addressing review findings:
Known gap, deliberately not in this PR: the same enum drift affects |
Dismissing prior approval to re-evaluate b806269
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.
b806269 to
0577e31
Compare
|
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
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 Codex tests green post-rebase: 80 in |
Dismissing prior approval to re-evaluate 0577e31
Problem
Codex CLI 0.150.0 added a fourth
SubAgentActivityKind—"completed"— alongsidestarted,interacted, andinterrupted. Our generated app-server bindings are pinned to an openai/codex revision from 2026-07-19 that knows only three, so decoding athread/resumeresponse whose history contained that kind failed:isRecoverableThreadResumeErroronly 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/completednotifications, where decode failures were swallowed silently, so subagent lifecycle events were dropped with no trace.Fix
thread/startandthread/resumenow 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.requesttakes 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:
Why not just regenerate the bindings
Regenerating to 0.150.0 moves the breakage rather than removing it: upstream also made
Thread.projectIdrequired 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.mdrecords the constraint.Verification
packages/effect-codex-app-server/src/client.test.tsdrives the real client against the mock peer replaying a resume payload withkind: "completed", and asserts the generated schema rejects it withoperation: "decode-payload"while the narrow schema resumes cleanly.CodexSessionRuntime.test.tscovers the same shape throughopenCodexThread, plus the decode-error fallback. Both new tests fail against the previous decode path.No UI surface changes.
Fixes #8322
Model: Claude Opus 5. Harness: T3 Code.
Note
Make
openCodexThreadsurvive protocol drift on resume by decoding minimal fieldsopenCodexThreadnow decodes onlythread.id,cwd, andmodelfromthread/startandthread/resumeresponses using a narrowCodexThreadOpenResponseschema, instead of requiring the full generated payload to validate.isRecoverableThreadResumeErrornow treatsCodexAppServerRequestErrorwith operationdecode-payloadas recoverable, so decode failures during resume trigger a warning and fall back tothread/start.CodexAppServerClient.Service.requestaccepts an optional caller-supplied response schema; undecodable notifications are dropped with a single warning per method instead of being silently ignored.CodexThreadOpenResponseschema 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.