diff --git a/src/shared/agent-hook-listener-jcode.test.ts b/src/shared/agent-hook-listener-jcode.test.ts index b2aa8abaa9e..3d35729fa61 100644 --- a/src/shared/agent-hook-listener-jcode.test.ts +++ b/src/shared/agent-hook-listener-jcode.test.ts @@ -158,6 +158,31 @@ describe('shared agent-hook-listener: jcode', () => { }) }) + it('keeps the finished turn detail a completion notification needs', () => { + // Why: the desktop banner only fires when the done row carries a reply, a tool + // name, or a tool input (hasAgentNotificationDetail). A turn that ends with no + // assistant prose must therefore still carry its last tool. + ingest(state, { + event: 'turn_start', + session_id: 'session_jc_6', + model: 'claude-haiku-4-5', + source: 'chat' + }) + ingest(state, { + event: 'pre_tool', + session_id: 'session_jc_6', + tool_name: 'write', + tool_input: '{"file_path":"SUMMARY.md","content":"# Summary"}' + }) + const event = ingest(state, { + event: 'turn_end', + session_id: 'session_jc_6', + status: 'ok', + duration_ms: '18000' + }) + expect(event?.payload).toMatchObject({ state: 'done', toolName: 'write' }) + }) + it('clears the previous turn tool when a new turn starts', () => { ingest(state, { event: 'pre_tool', diff --git a/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts b/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts index 9220a9741b7..be1e794e95c 100644 --- a/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts @@ -75,19 +75,14 @@ export function extractJcodeToolFields( // matching pre_tool preview survive the tool's completion instead of blanking. return toolUpdate({ toolName, toolInput: undefined }, { hasToolInputField: false }) } - if (eventName === 'turn_start') { - // Why: a new turn starts with no tool; clearing both fields stops the previous - // turn's last tool from being shown as this turn's live work. - return toolUpdate({ toolName: undefined, toolInput: undefined }, { hasToolInputField: true }) - } if (eventName === 'turn_end') { + // Why no tool clearing here: `turn_start` already resets the pane's tool cache + // for the next turn, and the completion notification needs the finished turn's + // detail (tool or reply) to be worth showing at all — same shape as Claude's Stop. const message = readString(hookPayload, 'last_assistant_text') ?? readString(hookPayload, 'last_assistant_message') - return { - ...(message ? { lastAssistantMessage: message } : { clearLastAssistantMessage: true }), - ...toolUpdate({ toolName: undefined, toolInput: undefined }, { hasToolInputField: true }) - } + return message ? { lastAssistantMessage: message } : {} } return {} }