feat: introduce notification system and claude notifications (#450)

* feat: introduce notification system and claude notifications

* fix(sw): prevent caching of API requests and WebSocket upgrades

* default to false for webpush notifications and translations for the button

* fix: notifications orchestrator and add a notification when  first enabled

* fix: remove unused state update and dependency in settings controller hook

* fix: show notifications settings tab

* fix: add notifications for response completion for all providers

* feat: show session name in notification and don't reload tab on clicking
--- the notification

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Haileyesus <something@gmail.com>
This commit is contained in:
Simos Mikelatos
2026-03-13 16:59:09 +01:00
committed by GitHub
parent 6f6dacad5e
commit 45e71a0e73
36 changed files with 1629 additions and 69 deletions

View File

@@ -1,5 +1,6 @@
import { spawn } from 'child_process';
import crossSpawn from 'cross-spawn';
import { notifyRunFailed, notifyRunStopped } from './services/notification-orchestrator.js';
// Use cross-spawn on Windows for better command execution
const spawnFunction = process.platform === 'win32' ? crossSpawn : spawn;
@@ -23,7 +24,7 @@ function isWorkspaceTrustPrompt(text = '') {
async function spawnCursor(command, options = {}, ws) {
return new Promise(async (resolve, reject) => {
const { sessionId, projectPath, cwd, resume, toolsSettings, skipPermissions, model } = options;
const { sessionId, projectPath, cwd, resume, toolsSettings, skipPermissions, model, sessionSummary } = options;
let capturedSessionId = sessionId; // Track session ID throughout the process
let sessionCreatedSent = false; // Track if we've already sent session-created event
let hasRetriedWithTrust = false;
@@ -81,6 +82,35 @@ async function spawnCursor(command, options = {}, ws) {
const isTrustRetry = runReason === 'trust-retry';
let runSawWorkspaceTrustPrompt = false;
let stdoutLineBuffer = '';
let terminalNotificationSent = false;
const notifyTerminalState = ({ code = null, error = null } = {}) => {
if (terminalNotificationSent) {
return;
}
terminalNotificationSent = true;
const finalSessionId = capturedSessionId || sessionId || processKey;
if (code === 0 && !error) {
notifyRunStopped({
userId: ws?.userId || null,
provider: 'cursor',
sessionId: finalSessionId,
sessionName: sessionSummary,
stopReason: 'completed'
});
return;
}
notifyRunFailed({
userId: ws?.userId || null,
provider: 'cursor',
sessionId: finalSessionId,
sessionName: sessionSummary,
error: error || `Cursor CLI exited with code ${code}`
});
};
if (isTrustRetry) {
console.log('Retrying Cursor CLI with --trust after workspace trust prompt');
@@ -255,7 +285,8 @@ async function spawnCursor(command, options = {}, ws) {
ws.send({
type: 'cursor-error',
error: stderrText,
sessionId: capturedSessionId || sessionId || null
sessionId: capturedSessionId || sessionId || null,
provider: 'cursor'
});
});
@@ -287,12 +318,15 @@ async function spawnCursor(command, options = {}, ws) {
type: 'claude-complete',
sessionId: finalSessionId,
exitCode: code,
provider: 'cursor',
isNewSession: !sessionId && !!command // Flag to indicate this was a new session
});
if (code === 0) {
notifyTerminalState({ code });
settleOnce(() => resolve());
} else {
notifyTerminalState({ code });
settleOnce(() => reject(new Error(`Cursor CLI exited with code ${code}`)));
}
});
@@ -308,8 +342,10 @@ async function spawnCursor(command, options = {}, ws) {
ws.send({
type: 'cursor-error',
error: error.message,
sessionId: capturedSessionId || sessionId || null
sessionId: capturedSessionId || sessionId || null,
provider: 'cursor'
});
notifyTerminalState({ error });
settleOnce(() => reject(error));
});