fix(jcode): keep finished-turn detail so completion notifications fire

Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
This commit is contained in:
Neil
2026-10-03 01:49:40 -07:00
committed by Neil
co-authored by czzczz
parent 6164d3f370
commit 5b43e2029a
2 changed files with 29 additions and 9 deletions
@@ -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',
@@ -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 {}
}