fix: correct notification session id

This commit is contained in:
Haileyesus
2026-06-11 19:31:13 +03:00
parent f5eac2ec12
commit 881e72d4a0
9 changed files with 254 additions and 22 deletions

View File

@@ -98,6 +98,44 @@ function normalizeSessionName(sessionName) {
return normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
}
function rowMatchesProvider(row, provider) {
return row && (!provider || row.provider === provider);
}
function resolveSessionRow(sessionId, provider) {
if (!sessionId) {
return null;
}
const appSessionRow = sessionsDb.getSessionById(sessionId);
if (rowMatchesProvider(appSessionRow, provider)) {
return appSessionRow;
}
const providerSessionRow = sessionsDb.getSessionByProviderSessionId(sessionId);
if (rowMatchesProvider(providerSessionRow, provider)) {
return providerSessionRow;
}
return null;
}
function normalizeNotificationSession(event) {
if (!event?.sessionId || !event.provider || event.provider === 'system') {
return event;
}
const row = resolveSessionRow(event.sessionId, event.provider);
if (!row || row.session_id === event.sessionId) {
return event;
}
return {
...event,
sessionId: row.session_id
};
}
function resolveSessionName(event) {
const explicitSessionName = normalizeSessionName(event.meta?.sessionName);
if (explicitSessionName) {
@@ -112,28 +150,29 @@ function resolveSessionName(event) {
}
function buildPushBody(event) {
const normalizedEvent = normalizeNotificationSession(event);
const CODE_MAP = {
'permission.required': event.meta?.toolName
? `Action Required: Tool "${event.meta.toolName}" needs approval`
'permission.required': normalizedEvent.meta?.toolName
? `Action Required: Tool "${normalizedEvent.meta.toolName}" needs approval`
: 'Action Required: A tool needs your approval',
'run.stopped': event.meta?.stopReason || 'Run Stopped: The run has stopped',
'run.failed': event.meta?.error ? `Run Failed: ${event.meta.error}` : 'Run Failed: The run encountered an error',
'agent.notification': event.meta?.message ? String(event.meta.message) : 'You have a new notification',
'run.stopped': normalizedEvent.meta?.stopReason || 'Run Stopped: The run has stopped',
'run.failed': normalizedEvent.meta?.error ? `Run Failed: ${normalizedEvent.meta.error}` : 'Run Failed: The run encountered an error',
'agent.notification': normalizedEvent.meta?.message ? String(normalizedEvent.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';
const providerLabel = PROVIDER_LABELS[normalizedEvent.provider] || 'Assistant';
const sessionName = resolveSessionName(normalizedEvent);
const message = CODE_MAP[normalizedEvent.code] || 'You have a new notification';
return {
title: sessionName || 'CloudCLI',
body: `${providerLabel}: ${message}`,
data: {
sessionId: event.sessionId || null,
code: event.code,
provider: event.provider || null,
sessionId: normalizedEvent.sessionId || null,
code: normalizedEvent.code,
provider: normalizedEvent.provider || null,
sessionName,
tag: `${event.provider || 'assistant'}:${event.sessionId || 'none'}:${event.code}`
tag: `${normalizedEvent.provider || 'assistant'}:${normalizedEvent.sessionId || 'none'}:${normalizedEvent.code}`
}
};
}
@@ -175,15 +214,16 @@ function notifyUserIfEnabled({ userId, event }) {
return;
}
const normalizedEvent = normalizeNotificationSession(event);
const preferences = notificationPreferencesDb.getPreferences(userId);
if (!shouldSendPush(preferences, event)) {
if (!shouldSendPush(preferences, normalizedEvent)) {
return;
}
if (isDuplicate(event)) {
if (isDuplicate(normalizedEvent)) {
return;
}
sendWebPush(userId, event).catch((err) => {
sendWebPush(userId, normalizedEvent).catch((err) => {
console.error('Web push send error:', err);
});
}

View File

@@ -0,0 +1,80 @@
import assert from 'node:assert/strict';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import test from 'node:test';
import webPush from 'web-push';
import {
closeConnection,
initializeDatabase,
notificationPreferencesDb,
pushSubscriptionsDb,
sessionsDb,
userDb,
} from '../modules/database/index.js';
import { notifyRunStopped } from './notification-orchestrator.js';
async function withIsolatedDatabase(runTest) {
const previousDatabasePath = process.env.DATABASE_PATH;
const tempDirectory = await mkdtemp(path.join(tmpdir(), 'notification-orchestrator-'));
const databasePath = path.join(tempDirectory, 'auth.db');
closeConnection();
process.env.DATABASE_PATH = databasePath;
await initializeDatabase();
try {
await runTest();
} finally {
closeConnection();
if (previousDatabasePath === undefined) {
delete process.env.DATABASE_PATH;
} else {
process.env.DATABASE_PATH = previousDatabasePath;
}
await rm(tempDirectory, { recursive: true, force: true });
}
}
test('push payload uses the app session id when notified with a provider session id', async () => {
const originalSendNotification = webPush.sendNotification;
const sentPayloads = [];
webPush.sendNotification = async (_subscription, payload) => {
sentPayloads.push(JSON.parse(payload));
return {};
};
try {
await withIsolatedDatabase(async () => {
const user = userDb.createUser('notify-user', 'hash');
const userId = Number(user.id);
notificationPreferencesDb.updatePreferences(userId, {
channels: { webPush: true },
events: { actionRequired: true, stop: true, error: true },
});
pushSubscriptionsDb.saveSubscription(userId, 'https://example.test/push', 'p256dh', 'auth');
sessionsDb.createAppSession('app-session-1', 'claude', '/workspace/demo');
sessionsDb.assignProviderSessionId('app-session-1', 'claude-native-1');
notifyRunStopped({
userId,
provider: 'claude',
sessionId: 'claude-native-1',
stopReason: 'completed',
});
await new Promise((resolve) => setImmediate(resolve));
assert.equal(sentPayloads.length, 1);
assert.equal(sentPayloads[0]?.data?.sessionId, 'app-session-1');
assert.match(sentPayloads[0]?.data?.tag, /app-session-1/);
});
} finally {
webPush.sendNotification = originalSendNotification;
}
});