mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-15 02:47:23 +00:00
feat: show session name in notification and don't reload tab on clicking
--- the notification
This commit is contained in:
@@ -467,7 +467,7 @@ async function loadMcpConfig(cwd) {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function queryClaudeSDK(command, options = {}, ws) {
|
||||
const { sessionId } = options;
|
||||
const { sessionId, sessionSummary } = options;
|
||||
let capturedSessionId = sessionId;
|
||||
let sessionCreatedSent = false;
|
||||
let tempImagePaths = [];
|
||||
@@ -507,7 +507,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
||||
sessionId: capturedSessionId || sessionId || null,
|
||||
kind: 'action_required',
|
||||
code: 'agent.notification',
|
||||
meta: { message },
|
||||
meta: { message, sessionName: sessionSummary },
|
||||
severity: 'warning',
|
||||
requiresUserAction: true,
|
||||
dedupeKey: `claude:hook:notification:${capturedSessionId || sessionId || 'none'}:${message}`
|
||||
@@ -553,7 +553,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
||||
sessionId: capturedSessionId || sessionId || null,
|
||||
kind: 'action_required',
|
||||
code: 'permission.required',
|
||||
meta: { toolName },
|
||||
meta: { toolName, sessionName: sessionSummary },
|
||||
severity: 'warning',
|
||||
requiresUserAction: true,
|
||||
dedupeKey: `claude:permission:${capturedSessionId || sessionId || 'none'}:${requestId}`
|
||||
@@ -707,6 +707,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'claude',
|
||||
sessionId: capturedSessionId || sessionId || null,
|
||||
sessionName: sessionSummary,
|
||||
stopReason: 'completed'
|
||||
});
|
||||
console.log('claude-complete event sent');
|
||||
@@ -732,6 +733,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'claude',
|
||||
sessionId: capturedSessionId || sessionId || null,
|
||||
sessionName: sessionSummary,
|
||||
error
|
||||
});
|
||||
|
||||
|
||||
@@ -24,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;
|
||||
@@ -97,6 +97,7 @@ async function spawnCursor(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'cursor',
|
||||
sessionId: finalSessionId,
|
||||
sessionName: sessionSummary,
|
||||
stopReason: 'completed'
|
||||
});
|
||||
return;
|
||||
@@ -106,6 +107,7 @@ async function spawnCursor(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'cursor',
|
||||
sessionId: finalSessionId,
|
||||
sessionName: sessionSummary,
|
||||
error: error || `Cursor CLI exited with code ${code}`
|
||||
});
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { notifyRunFailed, notifyRunStopped } from './services/notification-orche
|
||||
let activeGeminiProcesses = new Map(); // Track active processes by session ID
|
||||
|
||||
async function spawnGemini(command, options = {}, ws) {
|
||||
const { sessionId, projectPath, cwd, resume, toolsSettings, permissionMode, images } = options;
|
||||
const { sessionId, projectPath, cwd, resume, toolsSettings, permissionMode, images, sessionSummary } = options;
|
||||
let capturedSessionId = sessionId; // Track session ID throughout the process
|
||||
let sessionCreatedSent = false; // Track if we've already sent session-created event
|
||||
let assistantBlocks = []; // Accumulate the full response blocks including tools
|
||||
@@ -189,6 +189,7 @@ async function spawnGemini(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'gemini',
|
||||
sessionId: finalSessionId,
|
||||
sessionName: sessionSummary,
|
||||
stopReason: 'completed'
|
||||
});
|
||||
return;
|
||||
@@ -198,6 +199,7 @@ async function spawnGemini(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'gemini',
|
||||
sessionId: finalSessionId,
|
||||
sessionName: sessionSummary,
|
||||
error: error || terminalFailureReason || `Gemini CLI exited with code ${code}`
|
||||
});
|
||||
};
|
||||
|
||||
@@ -192,6 +192,7 @@ function mapPermissionModeToCodexOptions(permissionMode) {
|
||||
export async function queryCodex(command, options = {}, ws) {
|
||||
const {
|
||||
sessionId,
|
||||
sessionSummary,
|
||||
cwd,
|
||||
projectPath,
|
||||
model,
|
||||
@@ -276,6 +277,7 @@ export async function queryCodex(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'codex',
|
||||
sessionId: currentSessionId,
|
||||
sessionName: sessionSummary,
|
||||
error: terminalFailure
|
||||
});
|
||||
}
|
||||
@@ -306,6 +308,7 @@ export async function queryCodex(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'codex',
|
||||
sessionId: currentSessionId,
|
||||
sessionName: sessionSummary,
|
||||
stopReason: 'completed'
|
||||
});
|
||||
}
|
||||
@@ -330,6 +333,7 @@ export async function queryCodex(command, options = {}, ws) {
|
||||
userId: ws?.userId || null,
|
||||
provider: 'codex',
|
||||
sessionId: currentSessionId,
|
||||
sessionName: sessionSummary,
|
||||
error
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import webPush from 'web-push';
|
||||
import { notificationPreferencesDb, pushSubscriptionsDb } from '../database/db.js';
|
||||
import { notificationPreferencesDb, pushSubscriptionsDb, sessionNamesDb } from '../database/db.js';
|
||||
|
||||
const KIND_TO_PREF_KEY = {
|
||||
action_required: 'actionRequired',
|
||||
@@ -7,6 +7,14 @@ const KIND_TO_PREF_KEY = {
|
||||
error: 'error'
|
||||
};
|
||||
|
||||
const PROVIDER_LABELS = {
|
||||
claude: 'Claude',
|
||||
cursor: 'Cursor',
|
||||
codex: 'Codex',
|
||||
gemini: 'Gemini',
|
||||
system: 'System'
|
||||
};
|
||||
|
||||
const recentEventKeys = new Map();
|
||||
const DEDUPE_WINDOW_MS = 20000;
|
||||
|
||||
@@ -76,6 +84,32 @@ function normalizeErrorMessage(error) {
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function normalizeSessionName(sessionName) {
|
||||
if (typeof sessionName !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalized = sessionName.replace(/\s+/g, ' ').trim();
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
|
||||
}
|
||||
|
||||
function resolveSessionName(event) {
|
||||
const explicitSessionName = normalizeSessionName(event.meta?.sessionName);
|
||||
if (explicitSessionName) {
|
||||
return explicitSessionName;
|
||||
}
|
||||
|
||||
if (!event.sessionId || !event.provider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizeSessionName(sessionNamesDb.getName(event.sessionId, event.provider));
|
||||
}
|
||||
|
||||
function buildPushBody(event) {
|
||||
const CODE_MAP = {
|
||||
'permission.required': event.meta?.toolName
|
||||
@@ -86,13 +120,19 @@ function buildPushBody(event) {
|
||||
'agent.notification': event.meta?.message ? String(event.meta.message) : 'You have a new notification',
|
||||
'push.enabled': 'Push notifications are now enabled!'
|
||||
};
|
||||
const providerLabel = PROVIDER_LABELS[event.provider] || 'Assistant';
|
||||
const sessionName = resolveSessionName(event);
|
||||
const message = CODE_MAP[event.code] || 'You have a new notification';
|
||||
|
||||
return {
|
||||
title: 'Claude Code UI',
|
||||
body: CODE_MAP[event.code] || 'You have a new notification',
|
||||
title: sessionName || 'Claude Code UI',
|
||||
body: `${providerLabel}: ${message}`,
|
||||
data: {
|
||||
sessionId: event.sessionId || null,
|
||||
code: event.code
|
||||
code: event.code,
|
||||
provider: event.provider || null,
|
||||
sessionName,
|
||||
tag: `${event.provider || 'assistant'}:${event.sessionId || 'none'}:${event.code}`
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -147,7 +187,7 @@ function notifyUserIfEnabled({ userId, event }) {
|
||||
});
|
||||
}
|
||||
|
||||
function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'completed' }) {
|
||||
function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'completed', sessionName = null }) {
|
||||
notifyUserIfEnabled({
|
||||
userId,
|
||||
event: createNotificationEvent({
|
||||
@@ -155,14 +195,14 @@ function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'co
|
||||
sessionId,
|
||||
kind: 'stop',
|
||||
code: 'run.stopped',
|
||||
meta: { stopReason },
|
||||
meta: { stopReason, sessionName },
|
||||
severity: 'info',
|
||||
dedupeKey: `${provider}:run:stop:${sessionId || 'none'}:${stopReason}`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function notifyRunFailed({ userId, provider, sessionId = null, error }) {
|
||||
function notifyRunFailed({ userId, provider, sessionId = null, error, sessionName = null }) {
|
||||
const errorMessage = normalizeErrorMessage(error);
|
||||
|
||||
notifyUserIfEnabled({
|
||||
@@ -172,7 +212,7 @@ function notifyRunFailed({ userId, provider, sessionId = null, error }) {
|
||||
sessionId,
|
||||
kind: 'error',
|
||||
code: 'run.failed',
|
||||
meta: { error: errorMessage },
|
||||
meta: { error: errorMessage, sessionName },
|
||||
severity: 'error',
|
||||
dedupeKey: `${provider}:run:error:${sessionId || 'none'}:${errorMessage}`
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user