diff --git a/.changeset/stream-idle-timeout.md b/.changeset/stream-idle-timeout.md new file mode 100644 index 0000000000..e6c95cf0b2 --- /dev/null +++ b/.changeset/stream-idle-timeout.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Add 60s idle timeout to streaming response loop to prevent indefinite hangs. diff --git a/packages/kosong/src/generate.ts b/packages/kosong/src/generate.ts index d17fef39c5..8f4edadc44 100644 --- a/packages/kosong/src/generate.ts +++ b/packages/kosong/src/generate.ts @@ -111,7 +111,21 @@ export async function generate( // the stream. await throwIfAborted(options?.signal, stream); + // Idle timeout: if no chunk arrives within this period, abort the stream + // to prevent indefinite hangs when the connection stalls without closing. + const IDLE_TIMEOUT_MS = 60_000; + let idleTimer: ReturnType | undefined; + const resetIdleTimer = () => { + if (idleTimer) clearTimeout(idleTimer); + idleTimer = setTimeout(() => { + stream.return?.(undefined); + }, IDLE_TIMEOUT_MS); + }; + resetIdleTimer(); + + try { for await (const part of stream) { + resetIdleTimer(); await throwIfAborted(options?.signal, stream); // Notify raw part callback (deep copy to avoid aliasing mutations). @@ -155,6 +169,9 @@ export async function generate( pendingPart = part; } } + } finally { + if (idleTimer) clearTimeout(idleTimer); + } await throwIfAborted(options?.signal, stream); options?.onStreamEnd?.();