* fix: keep notification SSE stream alive * Fix PR comments * fix: cover notification stream error cleanup --------- Co-authored-by: Konstantin Zolin <zolin_ka@vk.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
6.2 KiB
6.2 KiB
Notifications Module Documentation
Purpose
This module provides notification message preparation utilities for the web server runtime, including text truncation and plain-text normalization for system notifications.
Entrypoints and structure
packages/web/server/lib/notifications/index.js: public entrypoint imported bypackages/web/server/index.js.packages/web/server/lib/notifications/routes.js: route registration for push, visibility, and session status/attention endpoints.packages/web/server/lib/notifications/push-runtime.js: push subscription persistence, VAPID initialization, and UI visibility runtime.packages/web/server/lib/notifications/emitter-runtime.js: desktop/stdout + UI SSE notification emission runtime.packages/web/server/lib/notifications/runtime.js: trigger runtime for OpenCode event-driven notification fanout.packages/web/server/lib/notifications/template-runtime.js: notification template variables and session text/title enrichment runtime. Zen-model helpers are retained as compatibility stubs only.packages/web/server/lib/notifications/message.js: helper implementation module.packages/web/server/lib/notifications/message.test.js: unit tests for notification message helpers.
Public exports
Notifications API (re-exported from message.js)
truncateNotificationText(text, maxLength): Truncates text to specified max length, appending...if truncated.prepareNotificationLastMessage({ message, settings }): Prepares the last message for notification display by normalizing and truncating text.
Route registration API (routes.js)
registerNotificationRoutes(app, dependencies): Registers notification-owned endpoints:GET /api/push/vapid-public-keyPOST /api/push/subscribeDELETE /api/push/subscribePOST /api/push/visibilityGET /api/push/visibilityGET /api/notifications/streamGET /api/session-activityGET /api/sessions/snapshotGET /api/sessions/statusGET /api/sessions/:id/statusGET /api/sessions/attentionGET /api/sessions/:id/attentionPOST /api/sessions/:id/viewPOST /api/sessions/:id/unviewPOST /api/sessions/:id/message-sent
Trigger runtime API (runtime.js)
createNotificationTriggerRuntime(dependencies): creates runtime-owned debounced trigger handling for OpenCode events.- Returned API:
maybeSendPushForTrigger(payload)
- Owns:
- completion/error/question/permission trigger routing
- session parent cache for subtask suppression
- template resolution and fallback behavior
- native notification fanout and web push payload fanout
- push suppression while any fresh UI visibility heartbeat reports a focused client
Push runtime API (push-runtime.js)
createPushRuntime(dependencies): creates runtime for web push and UI visibility state.- Returned API:
getOrCreateVapidKeys()ensurePushInitialized()setPushInitialized(value)addOrUpdatePushSubscription(uiSessionToken, subscription, userAgent)removePushSubscription(uiSessionToken, endpoint)sendPushToAllUiSessions(payload, options?)updateUiVisibility(token, visible)isAnyUiVisible()isUiVisible(token)
Emitter runtime API (emitter-runtime.js)
createNotificationEmitterRuntime(dependencies): creates runtime for unified notification emission channels.- Returned API:
writeSseEvent(res, payload)emitDesktopNotification(payload)broadcastUiNotification(payload)
Template runtime API (template-runtime.js)
createNotificationTemplateRuntime(dependencies): creates shared notification/template runtime. Model-backed summarization was retired after the Zen provider became unavailable.- Returned API:
resolveNotificationTemplate(template, variables)shouldApplyResolvedTemplateMessage(template, resolved, variables)fetchFreeZenModels()compatibility stub returning[]resolveZenModel(override)compatibility stub preserving stored values without validationvalidateZenModelAtStartup()compatibility no-opsummarizeText(text, targetLength, zenModel)compatibility stub returning local fallback textextractLastMessageText(payload, maxLength?)fetchLastAssistantMessageText(sessionId, messageId, maxLength?)maybeCacheSessionInfoFromEvent(payload)buildTemplateVariables(payload, sessionId)getCachedZenModels()
Constants
Default values
DEFAULT_NOTIFICATION_MESSAGE_MAX_LENGTH: 250 (default max length for notification text).NOTIFICATION_SSE_HEARTBEAT_INTERVAL_MS: 20000 (notification SSE comment heartbeat interval).
Settings object format
The settings parameter for prepareNotificationLastMessage supports maxLastMessageLength (number), the maximum length for the final notification text (default: 250). Legacy summarization settings may still exist in persisted settings but are ignored.
Response contracts
truncateNotificationText
- Returns empty string for non-string input.
- Returns original text if under max length.
- Returns
${text.slice(0, maxLength)}...for truncated text.
prepareNotificationLastMessage
- Returns empty string for empty/null message.
- Returns truncated original message. Model-backed notification summarization is retired.
- Normalizes markdown-like formatting to plain text before truncation.
- Always applies
maxLastMessageLengthtruncation to final result.
Notes for contributors
Adding new notification helpers
- Add new helper functions to
packages/web/server/lib/notifications/message.js. - Export functions that are intended for public use.
- Follow existing patterns for input validation (e.g., type checking for strings).
- Use
resolvePositiveNumberfor numeric parameters with fallbacks to maintain safe defaults. - Add corresponding unit tests in
packages/web/server/lib/notifications/message.test.js.
Error handling
prepareNotificationLastMessagedoes not call model summarization.- Invalid numeric parameters default to safe fallback values.
- Non-string inputs are handled gracefully (return empty string).
Testing
- Run
bun run type-check,bun run lint, andbun run buildbefore finalizing changes. - Unit tests should cover truncation behavior and edge cases (empty strings, invalid inputs).