Two silent data-loss bugs in the OpenAI Responses-family stream parsers in packages/ai.
1. Codex parseSSE drops a trailing unterminated event
packages/ai/src/providers/openai-codex-responses.ts, parseSSE (~535-586). Events are only extracted when a blank-line delimiter arrives inside the read loop. If a server closes the connection right after the final data: line without a trailing blank line, the terminal response.completed event is dropped and the final usage and stop reason are lost. The Anthropic provider's iterateSseMessages already handles this by draining the decoder and flushing the remaining buffer, the Codex one does not. CRLF line endings are also not normalized.
2. output_text.delta is silently dropped when content_part.added never arrives
packages/ai/src/providers/openai-responses-shared.ts (~376-392, same pattern for refusal.delta at ~393-409). The delta handler only appends when a matching content part already exists, and silently continues otherwise. Responses-compatible endpoints that are not fully spec-compliant (Azure, OpenRouter, various proxies) can stream response.output_text.delta without a preceding response.content_part.added, and the assistant text just vanishes with no error.
Fix for both: flush the decoder and any remaining buffered event after the read loop, normalize line endings, and lazily create the missing content part before appending a delta.
Two silent data-loss bugs in the OpenAI Responses-family stream parsers in
packages/ai.1. Codex
parseSSEdrops a trailing unterminated eventpackages/ai/src/providers/openai-codex-responses.ts,parseSSE(~535-586). Events are only extracted when a blank-line delimiter arrives inside the read loop. If a server closes the connection right after the finaldata:line without a trailing blank line, the terminalresponse.completedevent is dropped and the final usage and stop reason are lost. The Anthropic provider'siterateSseMessagesalready handles this by draining the decoder and flushing the remaining buffer, the Codex one does not. CRLF line endings are also not normalized.2.
output_text.deltais silently dropped whencontent_part.addednever arrivespackages/ai/src/providers/openai-responses-shared.ts(~376-392, same pattern forrefusal.deltaat ~393-409). The delta handler only appends when a matching content part already exists, and silently continues otherwise. Responses-compatible endpoints that are not fully spec-compliant (Azure, OpenRouter, various proxies) can streamresponse.output_text.deltawithout a precedingresponse.content_part.added, and the assistant text just vanishes with no error.Fix for both: flush the decoder and any remaining buffered event after the read loop, normalize line endings, and lazily create the missing content part before appending a delta.