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
This commit is contained in:
Roo Code 2026-04-03 02:21:28 +00:00
parent 137d3f4fd8
commit 2134c03893
2 changed files with 104 additions and 0 deletions

View file

@ -296,6 +296,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
setSecondaryButtonText(t("chat:startNewTask.title"))
break
case "followup":
if (!isPartial) {
playSound("notification")
}
setSendingDisabled(isPartial)
setClineAsk("followup")
// setting enable buttons to `false` would trigger a focus grab when

View file

@ -515,6 +515,107 @@ describe("ChatView - Notification Sound with Queued Messages", () => {
})
})
describe("ChatView - Followup Notification Sound", () => {
beforeEach(() => vi.clearAllMocks())
it("plays notification sound when a non-partial followup ask is received", async () => {
renderChatView()
// First hydrate state with initial task
mockPostMessage({
soundEnabled: true,
messageQueue: [],
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Clear any initial calls
mockPlayFunction.mockClear()
// Add a followup ask message
mockPostMessage({
soundEnabled: true,
messageQueue: [],
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "followup",
ts: Date.now(),
text: "Could you clarify which file you want me to edit?",
partial: false,
},
],
})
// Wait for sound to be played
await waitFor(() => {
expect(mockPlayFunction).toHaveBeenCalled()
})
})
it("does not play notification sound when followup ask is partial", async () => {
renderChatView()
// First hydrate state with initial task
mockPostMessage({
soundEnabled: true,
messageQueue: [],
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Clear any initial calls
mockPlayFunction.mockClear()
// Add a partial followup ask message
mockPostMessage({
soundEnabled: true,
messageQueue: [],
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "followup",
ts: Date.now(),
text: "Could you clarify...",
partial: true,
},
],
})
// Wait a bit to ensure the effect would have run
await waitFor(
() => {
expect(mockPlayFunction).not.toHaveBeenCalled()
},
{ timeout: 1000 },
)
})
})
describe("ChatView - Sound Debounce", () => {
beforeEach(() => vi.clearAllMocks())