dsh-notify
A DeepSeek Harness (DSH) Web UI plugin that tells you, audibly and visibly, when the model has stopped and needs you:
- raises a system notification (Windows/OS toast with the system sound and
a popup message) whenever any session finishes a turn while you are not
looking at that exact session — a question is asked (
ask_user_question), a plan is finished (exit_plan_mode), a turn/session completes, or a turn ends in error — and - shows a pending-notification count as a number badge on the app icon
(1 notification →
1, two →2, …).
Windows 系统通知(弹窗 + 系统提示音)见 README.zh.md。
中文说明见 README.zh.md。
What it does
The trigger is per session, not per window: whenever any session's turn finishes and you are not actively looking at that specific session, dsh-notify raises an alert. That covers background tasks/sessions completing while you chat in the foreground one, and the current session completing while the app is in the background. The one case that is skipped is a turn finishing in the exact session you are looking at while the window has focus.
For each completed turn that needs your attention:
raises a system popup notification via the Web Notifications API (
new Notification(...)) — on Windows this is a toast with the system notification sound and a popup message. Each toast shows:- title = the conversation (session) name, e.g.
Test1; - body = the prompt that was sent (
提示词:…), then the status (已完成/正在等待你的回答/计划已提交审阅/本轮出错), plus(当前共 N 个待处理提醒)when several are pending. A one-time permission prompt appears on your first interaction with the app. If notifications are unavailable or denied, it falls back to a synthesized Web Audio chime (no asset, works offline, CSP-friendly).
- title = the conversation (session) name, e.g.
increments the badge count and displays it on the app icon. One pending notification shows
1, a second shows2, and so on. Returning to the window (focus / visibilitychange) clears the badge.A short
SETTLE_MSwait is applied before each alert, so a turn that resumes immediately (e.g. the model continues with more autonomous steps, todo updates, or sub-agent hops inside a background task) is not treated as a finished notification — only a session that has truly gone idle alerts.Click the notification to jump back: clicking the toast focuses the app, opens the conversation that just finished, and clears the badge. Internal sub-agent child sessions do not raise alerts or count toward the badge.
The badge is delivered through three layers, in order of capability:
- OS dock / taskbar badge via the App Badging API
(
navigator.setAppBadge(n)) — this is the real “number on the app icon” when DSH runs inside Electron with badging enabled, or as an installed PWA. - Tab title badge — the count is prefixed to
document.title(e.g.(2) DeepSeek Harness); always works in any browser tab. - Favicon badge — a red dot with the number is drawn onto the tab icon.
All host-listed sessions are watched for background completions; the currently selected session additionally gets a precise conversation-snapshot watcher. Event detection uses the running bit and the latest assistant message’s tool calls from the session’s conversation snapshot — no DOM poking, no host RPC.
Requirements
- DeepSeek Harness ≥
0.1.0-rc.6(Web UI profile) - For the popup + sound: grant the site/browser notification permission (a one-time prompt appears on your first click/key press). Notifications unavailable or denied → auto-fallback to a synthesized Web Audio chime (which needs a prior user gesture on the page due to the autoplay policy).
Installation
The package ships prebuilt (lib/ is committed), so no build step is needed.
With DSH
Hand this repository URL to your DSH agent (it reads AGENTS.md and installs
it), or:
dsh plugin --profile web add github:<owner>/dsh-notify
Manual
- Clone/unpack to a stable path
$P(e.g.~/dsh-notify). - Make the package resolvable from the profile’s module fallback (the linked
name must equal
dsh-notify):# POSIX mkdir -p "$DSH_HOME/profiles/node_modules" ln -s "$P" "$DSH_HOME/profiles/node_modules/dsh-notify" # Windows (no admin needed): junction New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\node_modules\dsh-notify" -Target "$P" - In
$DSH_HOME/profiles/web/package.json:- add
"dsh-notify": "link:$P"todependencies; - append
"dsh-notify"todsh.profile.bundles.
- add
- Restart DSH (plugin-set changes take effect on restart), then refresh the Web UI.
Configuration
Plain-JS knobs at the top of lib/client.js (no rebuild or restart beyond the
plugin reload):
| Flag | Default | Meaning |
|---|---|---|
USE_SYSTEM_NOTIFICATION |
true |
raise a Windows/OS popup toast (with system sound) as the primary alert |
USE_BEEP_FALLBACK |
true |
fall back to the synthesized chime when notifications are unavailable/denied |
MIN_BEEP_GAP_MS |
800 |
minimum gap between alerts to avoid rapid duplicates |
SETTLE_MS |
1500 |
wait for a session to truly idle before alerting (suppresses sub-agent / todo milestone bursts) |
Development
The client bundle is hand-written plain JS (lib/client.js) and runs through
DSH’s client-modules loader as a window.__ModuleLoader__.load({ id, factory })
CJS factory — exactly the pattern used by the other hand-written DSH plugins.
Run node --check lib/client.js to syntax-check.
Known limitations
- The OS dock/taskbar badge only appears when the browser context supports the App Badging API (Electron with badging enabled, or an installed PWA); all environments still get the tab-title + favicon badge, which always work.
- The system popup needs notification permission; the badge/title/favicon always update regardless.
- All host-listed sessions are watched for background completions. Pause
classification (question / plan review) comes from the live session list's
pendingInteractionfield, so it works even for sessions whose conversation window was never opened. The sent prompt (提示词) is read from a session's conversation snapshot when its window/binding is available; otherwise the toast shows the session name + status and no prompt.
No comments yet. Be the first to write one.