fix(#2544): add SSE heartbeat keepalive to Responses API transform stream (#2599)

Integrated into release/v3.8.2
This commit is contained in:
Hernan Javier Ardila Sanchez
2026-05-23 00:00:35 +02:00
committed by GitHub
parent 14b577256b
commit 6499ea72f7

View File

@@ -75,7 +75,7 @@ export function createResponsesLogger(model, logsDir = null) {
* @param {Object} logger - Optional logger instance
* @returns {TransformStream}
*/
export function createResponsesApiTransformStream(logger = null) {
export function createResponsesApiTransformStream(logger = null, keepaliveIntervalMs = 3000) {
const state = {
seq: 0,
responseId: `resp_${Date.now()}`,
@@ -99,6 +99,7 @@ export function createResponsesApiTransformStream(logger = null) {
buffer: "",
completedSent: false,
usage: null,
keepaliveTimer: null,
};
const encoder = new TextEncoder();
@@ -321,6 +322,12 @@ export function createResponsesApiTransformStream(logger = null) {
};
return new TransformStream({
start(controller) {
// Periodic keepalive heartbeat to prevent client timeouts (Codex CLI #2544)
state.keepaliveTimer = setInterval(() => {
controller.enqueue(encoder.encode(": keepalive\n\n"));
}, keepaliveIntervalMs);
},
transform(chunk, controller) {
const text = new TextDecoder().decode(chunk);
logger?.logInput(text.trim());
@@ -539,6 +546,11 @@ export function createResponsesApiTransformStream(logger = null) {
},
flush(controller) {
// Clear keepalive timer
if (state.keepaliveTimer) {
clearInterval(state.keepaliveTimer);
state.keepaliveTimer = null;
}
for (const i in state.msgItemAdded) closeMessage(controller, i);
closeReasoning(controller);
for (const i in state.funcCallIds) closeToolCall(controller, i);