diff --git a/src/lib/aiAgentStreamCallbacks.test.ts b/src/lib/aiAgentStreamCallbacks.test.ts index 8b10bbc9..0c8b79c5 100644 --- a/src/lib/aiAgentStreamCallbacks.test.ts +++ b/src/lib/aiAgentStreamCallbacks.test.ts @@ -152,6 +152,43 @@ describe('aiAgentStreamCallbacks', () => { ]) }) + it('finishes with a readable empty state when Claude exits without assistant text', () => { + const messages = createMessageStore([ + { + id: 'msg-1', + userMessage: '/exit', + actions: [], + isStreaming: true, + }, + ]) + const status = createStatusStore('thinking') + + const callbacks = createStreamCallbacks({ + messageId: 'msg-1', + vaultPath: '/vault', + setMessages: messages.setMessages, + setStatus: status.setStatus, + abortRef: { current: { aborted: false } }, + responseAccRef: { current: '' }, + toolInputMapRef: { current: new Map() }, + fileCallbacksRef: { current: undefined }, + }) + + callbacks.onDone() + + expect(status.getStatus()).toBe('done') + expect(messages.getMessages()).toEqual([ + { + id: 'msg-1', + userMessage: '/exit', + actions: [], + isStreaming: false, + reasoningDone: true, + response: 'Claude Code finished without returning a reply.', + }, + ]) + }) + it('ignores stream events after the request has been aborted', () => { const messages = createMessageStore([ { diff --git a/src/lib/aiAgentStreamCallbacks.ts b/src/lib/aiAgentStreamCallbacks.ts index 6419b873..d01c9f7b 100644 --- a/src/lib/aiAgentStreamCallbacks.ts +++ b/src/lib/aiAgentStreamCallbacks.ts @@ -20,6 +20,12 @@ export interface StreamMutationContext { fileCallbacksRef: MutableRefObject } +const EMPTY_CLAUDE_RESPONSE = 'Claude Code finished without returning a reply.' + +function finalResponseText(response: string): string { + return response.trim() ? response : EMPTY_CLAUDE_RESPONSE +} + export function createStreamCallbacks(context: StreamMutationContext) { const { messageId, @@ -95,7 +101,7 @@ export function createStreamCallbacks(context: StreamMutationContext) { if (abortRef.current.aborted) return setStatus('done') - const finalResponse = responseAccRef.current || undefined + const finalResponse = finalResponseText(responseAccRef.current) updateMessage(setMessages, messageId, (message) => ({ ...message, isStreaming: false,