@ag-dsh/dsh-web-notify

August 15, 2026 · View on GitHub

中文 | English

@ag-dsh/dsh-web-notify

DSH web notification plugin: when a conversation turn completes, the assistant asks you a question, an approval is required, or a turn errors, it alerts you through the browser's native system notifications (Web Notifications API) — even when the tab is in the background, the window is minimized or unfocused, the notification still appears in the system notification center (macOS Notification Center / Windows Action Center). Supports Chrome / Edge / Firefox / Safari.

Installation

Using the most common web profile as an example:

# Install
dsh plugin --profile web add @ag-dsh/dsh-web-notify

# ⚠️ You must restart dsh after install for the plugin to load:
# exit the current dsh process first, then start it again
dsh --profile web

dsh plugin add only installs the package and registers it in dsh.profile.bundles (the profile's package.json); the running process composes its configuration tree at startup and hot reload does not watch the bundles list, so a restart is required.

Alternatively, skip the bundle mechanism and add the plugin row directly to the profile's cordis.yml / cordis.patch.yml (hot-applied by HMR, no restart needed):

- id: web-notify
  name: '@ag-dsh/dsh-web-notify'

The plugin row (host half) is only a carrier for the client bundle: dsh-client-modules scans loaded packages that declare dsh.client and serves their ./client export (lib/client.js) as the browser bundle. All functionality runs in the browser; the host half has no business logic.

Host services used

ServiceDependencyDescription
slots (client)ctx.get optionalRegisters the invisible shell.overlay background-session engine, the settings.plugin.item config card, and the conversation.session.header.utilities observer
sessions (client)ctx.get optionalOpens the target session when a notification is clicked
timer (client)inject hardAuto-closes notifications after 12 seconds

Usage

  1. After install and restart, open Settings → Plugins → Plugin config and find the Web 通知 card (settings.plugin.item). The default mode is always (notify whether focused or not) and the beep is on by default.
  2. Click 「开启通知」 in the config page and allow the browser permission prompt (Firefox / Safari require this button gesture).
  3. System notifications appear for (in 「始终」 mode they also fire while focused; switch back to 「仅后台」 to reduce interruptions):
    • 对话完成 (turn complete): any session's turn ended;
    • 需要你的回答 (answer needed): the assistant called ask_user_question (including plan-review confirmations);
    • 需要授权 (approval needed): a pending approval request;
    • 对话出错 (turn error): the open session reported an error.
  4. Clicking a notification focuses the window and opens the target session.
  5. Config page controls:
    • 通知模式: choose one of 仅后台 (notify when the window is unfocused), 始终 (also notify while focused, default), 关闭 (disabled);
    • 提示音: on/off toggle (on by default; event-dependent: 1 beep on completion, 2 on question/approval, 3 on error), plus a 「🎵 试听」 button that plays all three beeps in sequence;
    • 通知权限: shows the current state (已允许 / not enabled / 被拒绝); if denied, allow notifications for 127.0.0.1 (or your deployment domain) manually in the browser's site settings.

Config

None.

Model-visible text

The following is a stable contract: notification titles and bodies are generated by notificationSpec in src/core.ts; changes must update this table and the tests (tests/index.spec.ts).

EventTitleBody (priority order)
Turn complete对话完成「[session title] last reply summary」(truncated to 220 chars) → Session title → 「助手已回复,点击查看」
Answer needed需要你的回答 (counts questions as 「(N 个问题)」 when multiple)Question text (truncated to 220 chars) → Session title → 「请回答助手的问题」
Approval needed需要授权 (marks subagent sessions as 「(子代理)」)「[session title] tool: reason」 → Session title → 「需要你的授权」
Turn error对话出错Error message (truncated to 160 chars) → 「对话运行出错」

Notification bodies collapse consecutive whitespace (including newlines) before truncation for single-line readability; approval notifications carry the initiating session title plus the tool/reason, question notifications carry the question count and the first question's text, so you can decide from the notification center whether action is needed.

Pill texts (NOTIFY_MODE_LABELS in src/core.ts): 仅后台 / 始终 / 关闭; button texts: 开启通知 / 通知被拒 / 通知不可用.

Behavior

  • Signal source: subscribes directly to DSH Web's existing session-state snapshots (useSessions / useSession, pushed over the websocket, no polling); no Host RPC, no extra network requests.
  • Coverage: the shell.overlay engine observes all sessions (including background sessions/subagents) for running and pendingInteraction edges; the conversation.session.header.utilities engine provides open-session rich notifications (question text, approval tool/reason, error message). That slot sits outside the composer takeover region, so observation survives question/approval cards.
  • False-positive guard: every observer seeds its baseline on first observation (page load, session switch, or a session joining the list never mis-notifies); notification tag = dsh-notify:<kind>:<sessionId> replaces same-kind notifications for the same session instead of stacking.
  • Browser compatibility: Notification is natively supported by Chrome / Edge / Firefox / Safari; 127.0.0.1 / localhost is a secure context and can request permission normally.
  • Reversible side effects: all Slot registrations (observer engines and the settings page) are cleaned up with the plugin fiber; timers use ctx.timeout (the timer service); HMR / hot-reload safe.

Known Limitations and Deferred Work

  • No notifications when the browser is fully closed or the tab is closed: this plugin uses in-page native notifications without server push; receiving notifications with the browser closed requires Web Push (FCM for Chrome, APNs for Safari), which a local 127.0.0.1 service cannot integrate — out of scope.
  • Mode and sound are in-memory state: they reset to 「始终 / on」 after a page reload; the browser-level permission itself is persisted by the browser and unaffected.
  • Error notifications cover the open session only: when a background session errors, its turn end still raises a 「对话完成」 notification (running falling back is the shared signal).
  • Multiple tabs each fire their own notifications; the tag prevents stacking.
  • Transient snapshots while the session list loads: items may be briefly empty before the first list build — handled defensively (empty array), no crash or mis-notification.
  • Beeps are subject to the browser autoplay policy: audio can only start after at least one click on any button in the settings page (e.g. 「🎵 试听」); otherwise it degrades silently.