Skip to content

Commit 949e83d

Browse files
committed
fix(gateway): flush throttled delta before emitChatFinal
The 150ms throttle in emitChatDelta can suppress the last text chunk before emitChatFinal fires, causing streaming clients (e.g. ACP) to receive truncated responses. The final event carries the complete text, but clients that build responses incrementally from deltas miss the tail end. Flush one last unthrottled delta with the complete buffered text immediately before sending the final event. This ensures all streaming consumers have the full response without needing to reconcile deltas against the final payload.
1 parent ddb7ec9 commit 949e83d

File tree

1 file changed

+21
-0
lines changed

1 file changed

+21
-0
lines changed

src/gateway/server-chat.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,27 @@ export function createAgentEventHandler({
335335
const text = normalizedHeartbeatText.text.trim();
336336
const shouldSuppressSilent =
337337
normalizedHeartbeatText.suppress || isSilentReplyText(text, SILENT_REPLY_TOKEN);
338+
// Flush any throttled delta so streaming clients receive the complete text
339+
// before the final event. The 150 ms throttle in emitChatDelta may have
340+
// suppressed the most recent chunk, leaving the client with stale text.
341+
if (text && !shouldSuppressSilent) {
342+
const lastSent = chatRunState.deltaSentAt.get(clientRunId) ?? 0;
343+
if (lastSent > 0) {
344+
const flushPayload = {
345+
runId: clientRunId,
346+
sessionKey,
347+
seq,
348+
state: "delta" as const,
349+
message: {
350+
role: "assistant",
351+
content: [{ type: "text", text }],
352+
timestamp: Date.now(),
353+
},
354+
};
355+
broadcast("chat", flushPayload, { dropIfSlow: true });
356+
nodeSendToSession(sessionKey, "chat", flushPayload);
357+
}
358+
}
338359
chatRunState.buffers.delete(clientRunId);
339360
chatRunState.deltaSentAt.delete(clientRunId);
340361
if (jobState === "done") {

0 commit comments

Comments
 (0)