Skip to content

fix(responses): emit a spec-valid response.in_progress keepalive (#17825) - #18197

Merged
diegosouzapw merged 2 commits into
release/v3.8.53from
fix/17825-responses-keepalive-1025
Oct 26, 2026
Merged

diegosouzapw merged 2 commits into
release/v3.8.53from
fix/17825-responses-keepalive-1025

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Closes #17825

Root cause

open-sse/utils/sseHeartbeat.ts built the synthetic response.in_progress keepalive once, as a module constant with a stub response ({id, status}). The same stub was used for the early/startup keepalive (src/app/api/v1/responses/route.ts) and for mid-stream heartbeats. Spec-validating clients (Vercel AI SDK @ai-sdk/openai) require response.id, response.created_at and response.model on response.in_progress and abort the whole stream with AI_TypeValidationError.

Fix

  • New open-sse/utils/responsesKeepaliveFrame.ts: buildResponsesInProgressFrame() builds a complete Response (object, created_at, model, output, error, incomplete_details, background, parallel_tool_calls, tool_choice, tools), plus createResponsesInProgressTracker().
  • sseHeartbeat.ts: OPENAI_RESPONSES_IN_PROGRESS_FRAME is now the complete fallback frame (neutral model). For the Responses shape the transform tracks id / created_at / model / sequence_number from passing response.created / response.in_progress lines (substring guard, split-chunk tolerant, never throws) and heartbeats reuse them.
  • responses/route.ts: per-request startup and application keepalive frame (unique resp_keepalive_<uuid>, request-time created_at, request model).
  • Keepalive stays a typed event (Codex CLI needs a parser-visible one, [BUG] Codex CLI 0.130.0 Responses API: stream disconnected before completion (~5s) #2544).

Regression test

tests/unit/responses-keepalive-spec-valid-17825.test.ts (4 cases).

RED on unfixed code:

node --import tsx/esm --test --test-force-exit tests/unit/responses-keepalive-spec-valid-17825.test.ts
✖ #17825: startup/keepalive response.in_progress frame is a complete Response event
✖ #17825: mid-stream heartbeat reuses the real response id/created_at/model
ℹ tests 2 / pass 0 / fail 2

GREEN after the fix: all cases in the file pass. Old tests that pinned the stub byte-for-byte (sse-heartbeat, early-stream-keepalive, sseHeartbeat, responses-route-early-keepalive-wiring) were realigned to the complete shape without removing assertions (the route wiring test now matches the per-request keepaliveFrame).

Gates

eslint (with suppressions), prettier, typecheck:core, check:open-sse-typecheck, check-file-size, check-complexity, check-cognitive-complexity, check-changelog-integrity all OK. Pre-commit hooks passed.

Note: sseHeartbeat.test.ts and terminal-heartbeat-cleanup-16258.test.ts use real 10-35 ms timers and flaked intermittently on the base as well while the host was at load average 50-100.

⚠️ base-red inherited: #17219 — Docs sync strict (docs/i18n/am/README.md migration count), not touched by this PR

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.

fix(api): synthetic response.in_progress keepalive is not a valid Responses event; spec-validating clients (Vercel AI SDK) abort the stream

1 participant