fix(openai): avoid stale Responses message id replay#85277
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 31, 2026, 2:59 PM ET / 18:59 UTC. Summary PR surface: Source +36, Tests +196. Total +232 across 5 files. Reproducibility: yes. Current main source shows raw assistant Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow converter-level replay policy after the failed agents-core check is green, preserving text and phase replay while omitting provider-owned message ids when reasoning is absent. Do we have a high-confidence way to reproduce the issue? Yes. Current main source shows raw assistant Is this the best way to solve the issue? Yes. The PR is the best narrow fix because the merged sanitizer handles one history-rewrite path, while both Responses converters still need a final payload-level guard for stale persisted ids. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 9d54285b0d4a. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +36, Tests +196. Total +232 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
ClawSweeper PR egg: ✨ hatched 🥚 common Clockwork Shellbean. Rarity: 🥚 common. Trait: sparkles near resolved comments. DetailsShare on X: post this hatch
About:
|
3f861ea to
28cb124
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Maintainer coordination note for the adjacent Responses replay PRs: I checked #84267, #84904, and #85362 against this PR's item-id rule. Proposed final policy/layering:
Suggested merge order: #84904 -> #85277 -> #84267 -> #85362. If maintainers choose a different order, the same policy still applies, but the rebase that combines #84267/#85362 should keep a single exported Expected file-level coordination:
That gives a coherent final contract: store-disabled requests send no server-owned item ids; store-enabled requests only send ids that are still internally paired and replayable; invalid reasoning recovery retries without reasoning and without stale signed message ids; session reset remains the last-resort poisoned-history escape hatch. |
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. Re-review progress:
|
28cb124 to
6e4da12
Compare
|
@clawsweeper re-review Refreshed the What changed:
Verification:
No source files changed in this follow-up; this refresh only aligns the public proof with the live PR head. |
|
🦞👀 Command router queued. I will update this comment with the next step. Re-review progress:
|
f0125df to
b60fd8b
Compare
|
@clawsweeper re-review Maintainer fixup pushed in fe7c3ad. What changed:
Verification:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review Rebased maintainer fixup pushed in d6902ed. Verification after rebase:
Local node scripts/run-tsgo.mjs -p tsconfig.core.json is blocked by an unrelated latest-main UI missing three type dependency; PR CI should be the source of truth for the branch type lanes. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Land-ready verification for d6902ed. Local proof:
CI proof:
Known proof gap:
|
Summary
messageitem id after the correspondingreasoningitem has been pruned from local history.Motivation
This prevents a latent OpenAI Responses failure exposed by long-running sessions whose assistant text still has a persisted Responses item id but whose paired reasoning item is no longer replayable. In that state, the next request can be rejected before the model responds.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof (required for external PRs)
messageitem id was included without the required pairedreasoningitem, while phase metadata still needs to survive without replaying orphan provider-owned assistant ids.gpt-5.5on May 31, 2026 after maintainer fixupd6902ed1a0f13c7b73e69fcfdc6566086b3c532f.Before this patch, the live OpenClaw Telegram agent diagnosed a real OpenAI Responses failure after conversation pruning:
After this patch, I reran the current-head OpenAI SDK loopback on
b60fd8baa5901ec405764edfa476b867f65c1f60. It importedbuildOpenAIResponsesParamsfromsrc/agents/openai-transport-stream.ts, sent that exact payload through OpenAI Node SDK 6.39.0 with an in-processfetchloopback, and confirmed that both the OpenClaw payload and the serialized SDK/v1/responsesrequest omitted the stale raw signed assistant message id while preserving phase metadata and omitting the phase-tagged provider-owned replay id:{ "proof": "openai-responses-sdk-loopback", "pr_head": "b60fd8baa5901ec405764edfa476b867f65c1f60", "request_path": "/v1/responses", "stale_signed_message_id": "msg_real_response_item_requiring_reasoning", "stale_id_present_in_openclaw_payload": false, "stale_id_present_in_sdk_request": false, "commentary_phase_id_present_in_openclaw_payload": true, "commentary_phase_id_present_in_sdk_request": true }Maintainer live proof on
d6902ed1a0f13c7b73e69fcfdc6566086b3c532fsent the PR-built payload to the real OpenAI Responses API. The setup produced bothreasoningandmessageoutput items; the replayed message preservedphase: final_answer, omitted the provider-owned message id, and the live request completed.{ "proof": "live-openai-responses-phase-id-omitted", "model": "gpt-5.5", "setup_output_types": ["reasoning", "message"], "replayed_message_has_id": false, "replayed_message_phase": "final_answer", "live_result_status": "completed" }node scripts/run-vitest.mjs src/agents/openai-transport-stream.test.ts src/agents/openai-responses.reasoning-replay.test.ts src/llm/providers/openai-responses-shared.test.ts(3 shards, 228 tests), alongside a live OpenAI Responses API proof.Root Cause (if applicable)
Regression Test Plan (if applicable)
src/agents/openai-responses.reasoning-replay.test.tsUser-visible / Behavior Changes
None, except affected OpenAI Responses sessions avoid a provider-side 400 when replay history has a stale signed message id without its paired reasoning item.
Diagram (if applicable)
Security Impact (required)
Yes/No) NoYes/No) NoYes/No) NoYes/No) NoYes/No) NoYes, explain risk + mitigation: N/ARepro + Verification
Environment
openai/gpt-5.4, OpenAI ResponsesSteps
messageid.Expected
Actual
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
gpt-5.5API proof via an inlinenode --import tsx --input-type=modulescript that importedbuildOpenAIResponsesParamsfromsrc/agents/openai-transport-stream.ts; focused replay regression coverage vianode scripts/run-vitest.mjs src/agents/openai-transport-stream.test.ts src/agents/openai-responses.reasoning-replay.test.ts src/llm/providers/openai-responses-shared.test.ts(3 shards, 228 tests);node scripts/run-tsgo.mjs -p tsconfig.core.json;node scripts/run-oxlint.mjson touched files;git diff --check; autoreview clean.Review Conversations
Compatibility / Migration
Yes/No) YesYes/No) NoYes/No) NoRisks and Mitigations