Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 2134c03

Browse files
committed
feat: play notification sound immediately on followup ask
Add immediate notification sound when the AI asks a follow-up question (followup ask type), so users get audio feedback right when the AI needs their attention rather than waiting for the 2-second interactionRequired timeout. Also adds tests to verify the sound plays for non-partial followup asks and does not play for partial (streaming) followup asks. Closes #12047
1 parent 137d3f4 commit 2134c03

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
296296
setSecondaryButtonText(t("chat:startNewTask.title"))
297297
break
298298
case "followup":
299+
if (!isPartial) {
300+
playSound("notification")
301+
}
299302
setSendingDisabled(isPartial)
300303
setClineAsk("followup")
301304
// setting enable buttons to `false` would trigger a focus grab when

webview-ui/src/components/chat/__tests__/ChatView.notification-sound.spec.tsx

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,107 @@ describe("ChatView - Notification Sound with Queued Messages", () => {
515515
})
516516
})
517517

518+
describe("ChatView - Followup Notification Sound", () => {
519+
beforeEach(() => vi.clearAllMocks())
520+
521+
it("plays notification sound when a non-partial followup ask is received", async () => {
522+
renderChatView()
523+
524+
// First hydrate state with initial task
525+
mockPostMessage({
526+
soundEnabled: true,
527+
messageQueue: [],
528+
clineMessages: [
529+
{
530+
type: "say",
531+
say: "task",
532+
ts: Date.now() - 2000,
533+
text: "Initial task",
534+
},
535+
],
536+
})
537+
538+
// Clear any initial calls
539+
mockPlayFunction.mockClear()
540+
541+
// Add a followup ask message
542+
mockPostMessage({
543+
soundEnabled: true,
544+
messageQueue: [],
545+
clineMessages: [
546+
{
547+
type: "say",
548+
say: "task",
549+
ts: Date.now() - 2000,
550+
text: "Initial task",
551+
},
552+
{
553+
type: "ask",
554+
ask: "followup",
555+
ts: Date.now(),
556+
text: "Could you clarify which file you want me to edit?",
557+
partial: false,
558+
},
559+
],
560+
})
561+
562+
// Wait for sound to be played
563+
await waitFor(() => {
564+
expect(mockPlayFunction).toHaveBeenCalled()
565+
})
566+
})
567+
568+
it("does not play notification sound when followup ask is partial", async () => {
569+
renderChatView()
570+
571+
// First hydrate state with initial task
572+
mockPostMessage({
573+
soundEnabled: true,
574+
messageQueue: [],
575+
clineMessages: [
576+
{
577+
type: "say",
578+
say: "task",
579+
ts: Date.now() - 2000,
580+
text: "Initial task",
581+
},
582+
],
583+
})
584+
585+
// Clear any initial calls
586+
mockPlayFunction.mockClear()
587+
588+
// Add a partial followup ask message
589+
mockPostMessage({
590+
soundEnabled: true,
591+
messageQueue: [],
592+
clineMessages: [
593+
{
594+
type: "say",
595+
say: "task",
596+
ts: Date.now() - 2000,
597+
text: "Initial task",
598+
},
599+
{
600+
type: "ask",
601+
ask: "followup",
602+
ts: Date.now(),
603+
text: "Could you clarify...",
604+
partial: true,
605+
},
606+
],
607+
})
608+
609+
// Wait a bit to ensure the effect would have run
610+
await waitFor(
611+
() => {
612+
expect(mockPlayFunction).not.toHaveBeenCalled()
613+
},
614+
{ timeout: 1000 },
615+
)
616+
})
617+
})
618+
518619
describe("ChatView - Sound Debounce", () => {
519620
beforeEach(() => vi.clearAllMocks())
520621

0 commit comments

Comments
 (0)