Files
openchamber/packages/web/server/lib/notifications/emitter-runtime.js
T
Bohdan Triapitsyn 91de51d1a5 fix: deduplicate desktop notifications and tighten notification text extraction
Desktop notifications no longer duplicate when native delivery succeeds
Reasoning chain-of-thought is excluded from notification body text
Untyped message parts are ignored in notification text extraction
2026-06-16 15:21:27 +03:00

110 lines
3.0 KiB
JavaScript

export const createNotificationEmitterRuntime = (dependencies) => {
const {
process,
getDesktopNotifyEnabled,
desktopNotifyPrefix,
getUiNotificationClients,
getBroadcastGlobalUiEvent,
// Optional: in-process desktop shells (Electron main) inject a callback so
// notifications are delivered as a direct function call instead of a stdout
// stringly-typed IPC.
onDesktopNotification: initialOnDesktopNotification,
} = dependencies;
// Late-bindable: main() in server/index.js may call setOnDesktopNotification
// after runtime construction so the in-process shell can subscribe without
// restructuring the module-level wiring.
let onDesktopNotification = typeof initialOnDesktopNotification === 'function'
? initialOnDesktopNotification
: null;
const setOnDesktopNotification = (cb) => {
onDesktopNotification = typeof cb === 'function' ? cb : null;
};
const writeSseEvent = (res, payload) => {
res.write(`data: ${JSON.stringify(payload)}\n\n`);
};
const emitDesktopNotification = (payload) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!desktopNotifyEnabled) {
return false;
}
if (!payload || typeof payload !== 'object') {
return false;
}
if (onDesktopNotification) {
try {
onDesktopNotification(payload);
return true;
} catch {
// ignore host-side throw
}
return false;
}
try {
// stdout fallback for runtimes that parse the one-line `${prefix}{json}` protocol.
process.stdout.write(`${desktopNotifyPrefix}${JSON.stringify(payload)}\n`);
return true;
} catch {
// ignore
}
return false;
};
const broadcastUiNotification = (payload, options = {}) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!payload || typeof payload !== 'object') {
return;
}
const desktopNotificationDelivered = options.desktopNotificationDelivered === true;
const syntheticPayload = {
type: 'openchamber:notification',
properties: {
...payload,
// Tell local desktop UI whether a native channel already accepted this
// notification. If so, the SSE/WS event is informational only and must
// not create a second OS notification.
desktopNotificationDelivered,
// Legacy marker retained for older clients that only know about stdout.
desktopStdoutActive: desktopNotifyEnabled,
},
};
const broadcastGlobalUiEvent = typeof getBroadcastGlobalUiEvent === 'function'
? getBroadcastGlobalUiEvent()
: null;
if (broadcastGlobalUiEvent) {
broadcastGlobalUiEvent(syntheticPayload);
return;
}
const clients = getUiNotificationClients();
if (clients.size === 0) {
return;
}
for (const res of clients) {
try {
writeSseEvent(res, syntheticPayload);
} catch {
// ignore
}
}
};
return {
writeSseEvent,
emitDesktopNotification,
broadcastUiNotification,
setOnDesktopNotification,
};
};