Files
openchamber/packages/web/server/lib/notifications/DOCUMENTATION.md
T
Bohdan Triapitsyn f884919165 fix: keep notification summaries in plain text
- Force notification summaries to avoid markdown formatting
- Normalize generated notification text before system display
- Cover plain-text notification formatting with tests
2026-04-06 17:40:20 +03:00

6.3 KiB

Notifications Module Documentation

Purpose

This module provides notification message preparation utilities for the web server runtime, including text truncation, plain-text normalization, and optional message summarization for system notifications.

Entrypoints and structure

  • packages/web/server/lib/notifications/index.js: public entrypoint imported by packages/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, zen-model helpers, and session text/title enrichment runtime.
  • 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, summarize }): Prepares the last message for notification display, with optional summarization support.

Route registration API (routes.js)

  • registerNotificationRoutes(app, dependencies): Registers notification-owned endpoints:
    • GET /api/push/vapid-public-key
    • POST /api/push/subscribe
    • DELETE /api/push/subscribe
    • POST /api/push/visibility
    • GET /api/push/visibility
    • GET /api/session-activity
    • GET /api/sessions/snapshot
    • GET /api/sessions/status
    • GET /api/sessions/:id/status
    • GET /api/sessions/attention
    • GET /api/sessions/:id/attention
    • POST /api/sessions/:id/view
    • POST /api/sessions/:id/unview
    • POST /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 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 + zen helper runtime.
  • Returned API:
    • resolveNotificationTemplate(template, variables)
    • shouldApplyResolvedTemplateMessage(template, resolved, variables)
    • fetchFreeZenModels()
    • resolveZenModel(override)
    • validateZenModelAtStartup()
    • summarizeText(text, targetLength, zenModel)
    • extractLastMessageText(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).
  • DEFAULT_NOTIFICATION_SUMMARY_THRESHOLD: 200 (minimum message length to trigger summarization).
  • DEFAULT_NOTIFICATION_SUMMARY_LENGTH: 100 (target length for summarized messages).

Settings object format

The settings parameter for prepareNotificationLastMessage supports:

  • summarizeLastMessage (boolean): Whether to enable summarization for long messages.
  • summaryThreshold (number): Minimum message length to trigger summarization (default: 200).
  • summaryLength (number): Target length for summarized messages (default: 100).
  • maxLastMessageLength (number): Maximum length for the final notification text (default: 250).

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 if summarization disabled, message under threshold, or summarization fails.
  • Returns truncated summary if summarization succeeds and returns non-empty string.
  • Normalizes markdown-like formatting to plain text before truncation.
  • Always applies maxLastMessageLength truncation to final result.

Notes for contributors

Adding new notification helpers

  1. Add new helper functions to packages/web/server/lib/notifications/message.js.
  2. Export functions that are intended for public use.
  3. Follow existing patterns for input validation (e.g., type checking for strings).
  4. Use resolvePositiveNumber for numeric parameters with fallbacks to maintain safe defaults.
  5. Add corresponding unit tests in packages/web/server/lib/notifications/message.test.js.

Error handling

  • prepareNotificationLastMessage catches summarization errors and falls back to original message.
  • 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, and bun run build before finalizing changes.
  • Unit tests should cover truncation behavior, summarization success/failure, and edge cases (empty strings, invalid inputs).