fix(openai): normalize responses replay tool ids#74850
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 27, 2026, 8:30 AM ET / 12:30 UTC. Summary PR surface: Source +204, Tests +99, Docs 0. Total +303 across 8 files. Reproducibility: yes. from source inspection, though I did not execute the repro in this read-only review. pi-ai keeps same-model tool IDs unchanged and then splits raw Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge Security Review detailsBest possible solution: Land the focused replay-boundary normalizer after standard maintainer review and exact-head CI, preserving canonical Do we have a high-confidence way to reproduce the issue? Yes from source inspection, though I did not execute the repro in this read-only review. pi-ai keeps same-model tool IDs unchanged and then splits raw Is this the best way to solve the issue? Yes. Normalizing at OpenClaw's replay boundary before pi-ai payload conversion is the narrow maintainable fix for stored history and live continuations, and the patch preserves canonical same-model pairs. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 3e351b718e28. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +204, Tests +99, Docs 0. Total +303 across 8 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
|
f1f7c92 to
4e1a675
Compare
7dca7c7 to
b816933
Compare
|
Rebased onto Real behavior proof — focused tests + formatter run against the rebased branch (no creds required): The replay-boundary cases from #74665 (overlong stored Responses tool IDs and non- @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
The currently-failing required checks ( Reproduction against bare upstream The other failing files ( Focused tests + formatter for this PR's actual changes still pass cleanly (5 files / 41 tests; oxfmt clean) — see the prior comment for the redacted terminal output. Happy to rebase onto a later |
Real-behavior payload-capture proof for #74665To address the previous A provider-side validator using the same regexes the PR's normalizer enforces ( Captured live stdout from the rebased branch ( Net result: with current main's split-on-pipe path the captured Responses payload contains 5 violations the provider would reject ( |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Rebased onto current @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review Rebased onto current |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Pearl Diff Drake Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
|
@clawsweeper re-review Rebased onto current
No production logic touched. New head: |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review Rebased onto current |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Summary
Fixes #74665
Tests
Notes
Real behavior proof
Behavior or issue addressed: OpenAI Responses replay rejected with
Invalid 'input[N].call_id'(and matchingfunction_call.id/function_call_output.call_idrejections) when stored OpenClaw tool ids encode overlong or non-fc_*item ids, because pi-ai splitscall_id|fc_idand feeds the malformed half directly into the outbound Responses payload (#74665).Real environment tested: macOS 25.3.0, real OpenClaw checkout at
/private/tmp/openclaw-rebase(clone ofopenclaw/openclawrebased onto upstreammain@445ed9b0b4), node 22 + pnpm v11.1.0, freshpnpm install --frozen-lockfileagainst the patched branch. Repro and capture run viapnpm exec tsxagainst the real production helper atsrc/agents/pi-embedded-helpers/openai.ts(no mocks, no stubs).Exact steps or command run after this patch:
where
repro-74665.tsimports the productionnormalizeOpenAIResponsesToolCallIdshelper, feeds it the malformed shapes from #74665, mirrors pi-ai'sid.split("|")to build the actual outbound Responses payload items, then runs them through a provider-side validator using the sameOPENAI_RESPONSES_CALL_ID_RE/OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE/ 64-char cap rules the PR's normalizer enforces.Evidence after fix: copied live terminal output (full payload-capture run, redacted of nothing — no PII to redact) showing the BEFORE Responses payload (current-main split-on-pipe path) and AFTER Responses payload (this PR's normalizer) for two malformed input shapes from #74665, plus the provider-side validation verdict. Full capture pasted in #74850 (comment).
Observed result after fix: with current main's split-on-pipe path the captured Responses payload carries 5 provider-side replay violations from a single set of stored OpenClaw tool ids; after this PR's normalizer the captured payload from the same stored ids is fully accepted (all
function_call.call_id,function_call.id, andfunction_call_output.call_idvalues match the Responses replay-id rules and stay under the 64-char cap), and the canonical-id pair preservation case continues to round-trip unchanged (covered bypi-embedded-runner.openai-tool-id-preservation.test.ts).What was not tested: no live network call against a real OpenAI Responses account was exercised in this run — the captured payload is the actual outbound JSON that pi-ai's Responses converter produces (via
id.split("|"), mirrored in the repro), and the validator uses the regexes the PR itself treats as authoritative for replay-safe ids; a paidapi.openai.comround-trip was deliberately avoided.