diff --git a/src/hooks/useSSEStream.ts b/src/hooks/useSSEStream.ts index d955dd2..d313073 100644 --- a/src/hooks/useSSEStream.ts +++ b/src/hooks/useSSEStream.ts @@ -24,6 +24,7 @@ export interface SSECallbacks { onToolTimeout: (toolName: string, elapsedSeconds: number) => void; onModeChanged: (mode: string) => void; onTaskUpdate: (sessionId: string) => void; + onKeepAlive: () => void; onError: (accumulated: string) => void; } @@ -146,6 +147,11 @@ function handleSSEEvent( return accumulated; } + case 'keep_alive': { + callbacks.onKeepAlive(); + return accumulated; + } + case 'error': { const next = accumulated + '\n\n**Error:** ' + event.data; callbacks.onError(next); @@ -232,6 +238,7 @@ export function useSSEStream() { onToolTimeout: (n, s) => callbacksRef.current?.onToolTimeout(n, s), onModeChanged: (m) => callbacksRef.current?.onModeChanged(m), onTaskUpdate: (s) => callbacksRef.current?.onTaskUpdate(s), + onKeepAlive: () => callbacksRef.current?.onKeepAlive(), onError: (a) => callbacksRef.current?.onError(a), }; diff --git a/src/lib/claude-client.ts b/src/lib/claude-client.ts index 852860d..61a813f 100644 --- a/src/lib/claude-client.ts +++ b/src/lib/claude-client.ts @@ -873,6 +873,13 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream { + markActive(); + }, onError: (acc) => { markActive(); stream.accumulatedText = acc; diff --git a/src/types/index.ts b/src/types/index.ts index 260d91e..094b60e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -356,6 +356,7 @@ export type SSEEventType = | 'permission_request' // permission approval needed | 'mode_changed' // SDK permission mode changed (e.g. plan → code) | 'task_update' // SDK TodoWrite task sync + | 'keep_alive' // SDK keep-alive heartbeat (resets idle timer) | 'done'; // stream complete export interface SSEEvent {