Skip to content

Commit c2fe775

Browse files
committed
feat: add desktop notification system for user interactions
- Add node-notifier dependency for cross-platform desktop notifications - Create NotificationService with support for Windows, macOS, and Linux - Add comprehensive VSCode settings for notification preferences - Integrate notifications with askApproval flow in presentAssistantMessage - Add error notifications to handleError function - Include localization support for all notification settings - Provide graceful fallback to VSCode notifications on failure Addresses #5015
1 parent 0c014f0 commit c2fe775

File tree

7 files changed

+516
-2
lines changed

7 files changed

+516
-2
lines changed

pnpm-lock.yaml

Lines changed: 51 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/assistant-message/presentAssistantMessage.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { TelemetryService } from "@roo-code/telemetry"
66

77
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
88
import type { ToolParamName, ToolResponse } from "../../shared/tools"
9+
import { NotificationService, NotificationType } from "../../services/notification"
910

1011
import { fetchInstructionsTool } from "../tools/fetchInstructionsTool"
1112
import { listFilesTool } from "../tools/listFilesTool"
@@ -266,6 +267,47 @@ export async function presentAssistantMessage(cline: Task) {
266267
progressStatus?: ToolProgressStatus,
267268
isProtected?: boolean,
268269
) => {
270+
// Initialize notification service and send desktop notification for approval requests
271+
const provider = cline.providerRef.deref()
272+
if (provider) {
273+
try {
274+
const notificationService = new NotificationService(provider.context)
275+
276+
// Determine the tool name from the current block for better notification context
277+
let toolName = block.name
278+
let notificationMessage = partialMessage || `Approval required for ${toolName}`
279+
280+
// Customize notification message based on tool type
281+
switch (toolName) {
282+
case "execute_command":
283+
notificationMessage = `Execute command: ${block.params.command}`
284+
break
285+
case "write_to_file":
286+
notificationMessage = `Write to file: ${block.params.path}`
287+
break
288+
case "read_file":
289+
// Handle both old and new read_file parameter formats
290+
const filePath =
291+
typeof block.params.args === "string"
292+
? "multiple files"
293+
: block.params.path || "unknown"
294+
notificationMessage = `Read file: ${filePath}`
295+
break
296+
case "browser_action":
297+
notificationMessage = `Browser action: ${block.params.action}`
298+
break
299+
default:
300+
notificationMessage = `${toolName} - Approval required`
301+
break
302+
}
303+
304+
await notificationService.sendApprovalRequest(notificationMessage, toolName)
305+
} catch (error) {
306+
// Don't let notification errors break the approval flow
307+
console.error("Failed to send desktop notification:", error)
308+
}
309+
}
310+
269311
const { response, text, images } = await cline.ask(
270312
type,
271313
partialMessage,
@@ -307,6 +349,18 @@ export async function presentAssistantMessage(cline: Task) {
307349
const handleError = async (action: string, error: Error) => {
308350
const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
309351

352+
// Send desktop notification for errors
353+
const provider = cline.providerRef.deref()
354+
if (provider) {
355+
try {
356+
const notificationService = new NotificationService(provider.context)
357+
await notificationService.sendError(`Error ${action}`, error)
358+
} catch (notificationError) {
359+
// Don't let notification errors break the error handling flow
360+
console.error("Failed to send error notification:", notificationError)
361+
}
362+
}
363+
310364
await cline.say(
311365
"error",
312366
`Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,

src/package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,48 @@
373373
"type": "string",
374374
"default": "",
375375
"description": "%settings.autoImportSettingsPath.description%"
376+
},
377+
"roo-cline.notifications.enabled": {
378+
"type": "boolean",
379+
"default": true,
380+
"description": "%settings.notifications.enabled.description%"
381+
},
382+
"roo-cline.notifications.showApprovalRequests": {
383+
"type": "boolean",
384+
"default": true,
385+
"description": "%settings.notifications.showApprovalRequests.description%"
386+
},
387+
"roo-cline.notifications.showErrors": {
388+
"type": "boolean",
389+
"default": true,
390+
"description": "%settings.notifications.showErrors.description%"
391+
},
392+
"roo-cline.notifications.showTaskCompletion": {
393+
"type": "boolean",
394+
"default": true,
395+
"description": "%settings.notifications.showTaskCompletion.description%"
396+
},
397+
"roo-cline.notifications.showUserInputRequired": {
398+
"type": "boolean",
399+
"default": true,
400+
"description": "%settings.notifications.showUserInputRequired.description%"
401+
},
402+
"roo-cline.notifications.showSessionTimeouts": {
403+
"type": "boolean",
404+
"default": true,
405+
"description": "%settings.notifications.showSessionTimeouts.description%"
406+
},
407+
"roo-cline.notifications.timeout": {
408+
"type": "number",
409+
"default": 10000,
410+
"minimum": 0,
411+
"maximum": 60000,
412+
"description": "%settings.notifications.timeout.description%"
413+
},
414+
"roo-cline.notifications.sound": {
415+
"type": "boolean",
416+
"default": true,
417+
"description": "%settings.notifications.sound.description%"
376418
}
377419
}
378420
}
@@ -432,6 +474,7 @@
432474
"monaco-vscode-textmate-theme-converter": "^0.1.7",
433475
"node-cache": "^5.1.2",
434476
"node-ipc": "^12.0.0",
477+
"node-notifier": "^10.0.1",
435478
"openai": "^5.0.0",
436479
"os-name": "^6.0.0",
437480
"p-limit": "^6.2.0",
@@ -476,6 +519,7 @@
476519
"@types/mocha": "^10.0.10",
477520
"@types/node": "20.x",
478521
"@types/node-cache": "^4.1.3",
522+
"@types/node-notifier": "^8.0.5",
479523
"@types/node-ipc": "^9.2.3",
480524
"@types/proper-lockfile": "^4.1.4",
481525
"@types/ps-tree": "^1.1.6",

src/package.nls.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,13 @@
3434
"settings.vsCodeLmModelSelector.family.description": "The family of the language model (e.g. gpt-4)",
3535
"settings.customStoragePath.description": "Custom storage path. Leave empty to use the default location. Supports absolute paths (e.g. 'D:\\RooCodeStorage')",
3636
"settings.enableCodeActions.description": "Enable Roo Code quick fixes",
37-
"settings.autoImportSettingsPath.description": "Path to a RooCode configuration file to automatically import on extension startup. Supports absolute paths and paths relative to the home directory (e.g. '~/Documents/roo-code-settings.json'). Leave empty to disable auto-import."
37+
"settings.autoImportSettingsPath.description": "Path to a RooCode configuration file to automatically import on extension startup. Supports absolute paths and paths relative to the home directory (e.g. '~/Documents/roo-code-settings.json'). Leave empty to disable auto-import.",
38+
"settings.notifications.enabled.description": "Enable desktop notifications for Roo Code events",
39+
"settings.notifications.showApprovalRequests.description": "Show desktop notifications when approval is required for tool operations",
40+
"settings.notifications.showErrors.description": "Show desktop notifications for errors and failures",
41+
"settings.notifications.showTaskCompletion.description": "Show desktop notifications when tasks are completed",
42+
"settings.notifications.showUserInputRequired.description": "Show desktop notifications when user input is required",
43+
"settings.notifications.showSessionTimeouts.description": "Show desktop notifications for session timeouts",
44+
"settings.notifications.timeout.description": "Notification timeout in milliseconds (0 = no timeout, max 60 seconds)",
45+
"settings.notifications.sound.description": "Play sound with desktop notifications"
3846
}

0 commit comments

Comments
 (0)