dsh-plugin-notify-sounds
Manifest valid★ 4DeepSeek Harness Reminder Plugin
dsh-plugin-notify-sounds
English | 中文
A soft chime when the agent is waiting for you — or when it is done.
Walk away from the window without missing anything: an OS notification in the background (click it to jump back), and a "Waiting for you" / "Finished" mark on the taskbar title. Once you have seen it, the plugin goes quiet — it will not nag you like an alarm clock.
Install: DSH → Settings → Plugins → Add plugin, paste the line below and press Install:
dsh-plugin-notify-sounds
🎨 Consistent with DSH's design language
Every visual detail of the settings section comes from DSH's own design system: row geometry, dividers, type sizes and spacing use the official design tokens, the sound picker is the official Menu primitive, the icons come from the official family, and the layout follows the native text-left, control-right row shape. Installing it does not introduce a visually disjoint third-party block into your settings.
✨ What it can do
| What | What you get |
|---|---|
| Your decision is needed | A chime when the agent raises an approval, a plan review, or a question — separate sounds for approvals and questions |
| Task finished | A chime when a session goes from running to idle |
| Aware even when you are away | An OS notification in the background (body is the approval reason or the question itself; click to return to the window) plus a taskbar title mark |
| Missed it? It reminds again | Unanswered decisions re-ping every 1/5/15/30 minutes — and stop for good once you have seen them, even if you switch away again |
| Only what you care about | Sub-agents (background tasks) are completely silent by default — only your own conversation is announced |
| Quiet when it should be | Quiet hours (e.g. 22:00–08:00, wrapping past midnight), notify-only-when-hidden, and a master switch |
| Your sound, your way | Nine built-in sounds, custom ringtone upload, volume — each feature with its own toggle |
| Update checks | Check npm for a new version and install it in one click: verified against the published digest before anything is replaced, restored on failure; restart DSH to load it |
💡 Why
DSH tells you things visually — status dots, sidebar marks. The moment you switch to another window, you can neither see nor hear anything, while the agent may be stuck waiting for one sentence from you.
This plugin turns "it is waiting for you" and "it is done" into sound. With system notifications and the title mark you know what happened even while away; and once you come back and see it, it goes quiet — the repeat reminder only chases decisions you have never seen.
🔔 Sounds
Bell · Chime · Ding · Drop · Pulse · Arc · Coin · Success · Knock — nine built-in sounds, all synthesized live with Web Audio: no audio files, no network requests.
Or upload your own ringtone (mp3/wav/ogg, ≤512KB, validated and volume-normalized automatically), or pick "None" for notifications only. Each trigger (approval / question / completion) gets its own sound, with preview right in the settings.
⚙️ Settings
Settings → Sounds (between General and Models). Every feature has its own toggle, styled exactly like DSH's own preference pages:
master switch and volume · per-trigger sounds with preview · sub-agent alerts · system notifications · window title alert · repeat interval · quiet hours · hidden-only mode · custom ringtone upload · update checks and one-click update
Everything applies immediately — no restart needed.
📦 Install
🖱️ Simplest: two clicks inside DSH (recommended)
-
In DSH open Settings → Plugins and press Add plugin;
-
Paste the package name into the box (this plugin is published on npm):
dsh-plugin-notify-sounds(the GitHub repository address
https://github.com/BillCx330/dsh-plugin-notify-soundsor a local directory path work too) -
Press Install, then restart DSH once.
The "install source" dropdown above the box picks the download mirror (mainland China mirror by default; leave it alone unless downloads are slow). DSH warns that a plugin runs with your permissions — this one only reads session state and plays sounds locally; its single request goes to your own DSH to raise the window when you click a notification, and nothing is sent anywhere else.
⬆️ Upgrading
DSH does not update plugins on its own, but this plugin updates itself: Settings → Sounds → Updates can check for a new version and install it in one click. Uninstalling and installing again also works.
🛠️ Other ways
Command line — one command against the profile you actually use:
dsh plugin --profile <your profile> add github:BillCx330/dsh-plugin-notify-sounds
Release archive — download the .tgz from Releases and hand it to dsh plugin add.
Local directory — for development or trying unreleased changes:
dsh plugin --profile <your profile> add ./dsh-plugin-notify-sounds
By hand — add "dsh-plugin-notify-sounds": "github:BillCx330/dsh-plugin-notify-sounds" to dependencies in the profile's package.json, append "dsh-plugin-notify-sounds" to dsh.profile.bundles, then run a package install (e.g. pnpm install) in the profile directory so the dependency lands in node_modules.
The command line writes dsh.profile.bundles and installs the dependency for you. The package ships prebuilt artifacts and declares no lifecycle scripts, so nothing is built at install time. Restart DSH after installing (everyday upgrades that touch only the client half hot-reload).
⚠️ Known limitations
- Hand-edited config: preferences live in the profile's
cordis.patch.yml; values must match the schema (volume 0–100, a preset id,HH:MMtimes) or the plugin declines to load — just fix the value. The settings UI only ever writes valid values. - One-click update only swaps files: it replaces the seven files this package ships and never touches the profile's configuration. Downloaded code is never executed — the new code loads on a DSH restart. Let the row report a result before closing the window (an interrupted update restores itself, but there is no reason to gamble); if a future release adds a dependency, that needs installing separately (this one has none).
- Multiple windows fire independently: the same event may chime once per window; turn on notify-only-when-hidden to avoid it.
- Click-to-return is fully implemented and tested on Windows; macOS / Linux fall back to the system deep link and are unverified.
- The window is found by its title — deliberately without inspecting other processes (that trips security software). If DSH ever changes its title format, click-to-return silently stops working; sounds and notifications are unaffected.
- The nav glyph depends on the settings panel's DOM structure and silently falls back to the official gear after a DSH redesign — cosmetic only.
- Notification permission is granted on the Desktop; under restrictive policies the row shows how to allow it manually, everything else keeps working.
- macOS system notifications: the desktop shell grants every permission request, so settings always say "granted" — whether a banner actually appears is up to macOS (System Settings → Notifications → DeepSeek Harness), and unsigned development builds may fail silently. "It says granted but nothing shows" is usually not this plugin.
- Upgrading from v1.0.0: since v1.1.0 approvals and questions are separate tracks, so questions default to "Chime" instead of "Bell" — change it back in settings if you prefer.
🧩 Design notes
For anyone hacking on the code (exact details live in the source comments):
- Triggers never touch the event waterfall: approval/question requests are waterfall events that a late listener can miss, so this plugin reads
uiSession.sessionStatus(the same aggregated pending/running facts the sidebar status dots use) and just observes. - Repeat cadence: unanswered decisions are checked every 15s; seen means done — appearing while you are at the window, or coming back to it, ends the reminders for good (switching away does not re-arm); a new question replacing an old one resets the timer. A reminder that comes due during quiet hours is dropped rather than deferred.
- Click-to-return: a renderer cannot raise an OS window, so a click POSTs to the Host's fenced
/notify-sounds/focusroute and the Host raises it. On Windows the Host callsuser32.dllthrough DSH's own bundledkoffi(no new dependency, window calls only, no PowerShell — that shape trips security software and gets blamed on DSH); Desktop vs browser is derived from theOriginheader, so a browser click never drags the Desktop window forward. - Notification collapsing: the
tagis not handed to the platform (a superseded toast swallows clicks aimed at it); the plugin closes the old notification itself and holds every generation's object so a late click always has a handler. - Title mark: prefix add/remove only, so DSH's own title management is untouched; the written prefix is remembered verbatim, so a language switch neither stacks nor leaks it. "Finished" is unread news — set by a finish that happens while the page is hidden, retired when you return — not a standing idle fact, so it does not reappear on every later switch-away.
- Quiet hours mean forgotten, not queued: a decision or finish that arrives during the quiet range is not recorded at all — no sound, no notification, no title mark — and nothing that was missed overnight is delivered when the window ends.
- Cooldown and robustness: the same trigger does not fire twice within 2s for the same session (a second conversation's decision still rings); volume is guarded with
Number.isFinite(a hand-edited.nanfalls back to the default); a refused write snaps the control back and shows an error row; toggles disable mid-write against double-tap races. - The volume slider is uncontrolled: dragging paints one CSS custom property and only the release commits, so it never stutters or snaps back.
- Theme and layout:
--dsw-alias-*tokens throughout, section and row geometry copied from the official preference pages; sound pickers use the officialMenudropdown so nothing scrolls sideways.
📜 Changelog
-
v1.6.9 (2026-10-12) Fixes a README filename compatibility issue in v1.6.8: reverted to
README.md(English) +README.zh.md(Chinese) so the update whitelist in every prior version can recognise them. A new filename would be refused by old users' updates — the whitelist is one-directional because the update runs old code. -
v1.6.8 (2026-10-12) Review fixes and optimizations: the auto-check first-frame timing gap is closed (
undefinedis no longer treated as permission to reach out); an orphaned JSDoc is removed; the teardown assertion now actually verifies listener removal;normalizedGainsamples every 8th frame (2.6M → 330K iterations for a 30-second upload); the master gain disconnects after playback ends (no more audio-node leak);swapNavIconis wrapped in try/catch and preserves the original node for restore on dispose (React re-renders can no longer crash it); the cooldown table evicts the oldest entry instead of clearing all; andsessionRowscaches one snapshot per synchronous block. -
v1.6.7 (2026-10-12) Three fixes from the review: a macOS browser tab is no longer mislabeled "not supported" for the title alert (
isDesktopShellis the right criterion, notisMacalone); the first-frame timing gap in the auto-check guard is closed (undefinedis no longer treated as permission); and three comments that no longer matched the code are corrected. Plus BUG-1 regression tests covering the suspended AudioContext path. -
v1.6.6 (2026-10-12) Nine fixes from an audit: sounds are no longer lost under the browser autoplay policy, and no longer "banked" to fire together at some later click (first-gesture unlock, and nothing is scheduled while the context cannot play); the macOS window raise now activates by bundle id with the deep link as fallback and logs its failures; update rollback is complete — files the update added are removed again, no temp file survives, and the backup is cleaned up once the swap succeeds; checks and updates are single-flight, so two pages clicking at once neither collide nor double-request; turning automatic checks off now really keeps the plugin quiet; a mobile browser that refuses to build a notification is reported as unsupported instead of showing a switch that can never work; and the nav glyph is back on the artwork's own 17×17 coordinate system. Platform notes are now labelled where they apply: the title alert says macOS does not support it, and repeat reminders note that background browser tabs throttle timers.
-
v1.6.5 (2026-10-11) Quiet hours now mean forgotten, not queued: decisions and finishes that arrive during the quiet range get no reminder and no title mark, and nothing that was missed overnight is delivered when the window ends; reminders that come due inside it are dropped rather than deferred. The update outcome now appears at the top of the section (visible the moment the page reloads, no scrolling), and the update row's copy is simpler.
-
v1.6.4 (2026-10-11) No more scrolling to find the news: an update swaps the plugin's own client file and the page reloads at the top, so the row now scrolls itself back into view when the outcome appears.
-
v1.6.3 (2026-10-11) The update-success message no longer flashes and vanishes: the Host remembers the install and repeats it with every check, so "Updated, restart DSH to load it" survives the page reload the update itself triggers (it swaps the client half's own file) and stays until the restart. The cached check is invalidated after an install too, so the row can no longer claim a newer version than the one it just wrote.
-
v1.6.2 (2026-10-11) Fixed one-click update always failing when the mainland mirror answered the check: the mirror serves its tarballs from its CDN, and the downloader refused every redirect — so the download could never finish (found by the live drill). Redirects are now followed one hop at a time with every hop checked against the trusted hosts (the mirror's CDN is the one legitimate hop); the sha512 check is unchanged. A failure now names the stage it died at (check / download / verify / unpack / replace).
-
v1.6.1 (2026-10-11) The README is rebuilt as a release page: a new Consistent with DSH's own UI section on how the UI matches DSH's own settings, copy polished throughout, and the upgrade notes corrected — the plugin updates itself, so no uninstall is needed. This release doubles as the live drill for one-click update.
-
v1.6.0 (2026-10-11) New: check for updates and install them in one click. The settings section can fetch the newest npm version and install it: the download is checked against the registry's published sha512, only whitelisted files are unpacked (symlinks and path traversal are refused), the old files are kept aside, and any failure restores them. Downloaded code is never executed — the new code loads when DSH restarts. Automatic checks run at most once a day and can be turned off, with the mainland-China mirror as fallback.
-
v1.5.0 (2026-10-08) The "finished" title mark is unread news now — set by a finish that happens while you are away, cleared when you return — instead of reappearing on every switch-away. The retrigger cooldown is per session, so two conversations deciding within 2 seconds both ring. The Host half gained tests (Config schema, focus route, title matching) that cross-check the shared constants between the halves; the sessions provider is now an explicit load prerequisite, and quiet-hours catch-up is documented.
-
v1.4.4 (2026-10-08) Quiet hours now suppress the taskbar title alert too, and the title refreshes as the quiet-hours window begins or ends. Custom-sound upload/removal writes are ordered and roll back on refusal; synchronous config-write errors are handled as save failures.
-
v1.4.3 (2026-10-08) Three fixes: an upload is no longer accepted when the environment cannot decode audio (it could never play anyway); a synchronous throw from the notification-permission request is handled like a denial instead of escaping; quiet hours only accept valid
HH:MM, and a throwing native window raise now falls through to the deep-link fallback. Plus two regression tests and a decode-error message that names both causes. -
v1.4.2 (2026-10-07) Clean release point: the
v1.4.1tag was re-pinned during publishing, so this republishes under a tag downstream can trust. No behaviour change. -
v1.4.1 (2026-10-07) Removed the "could not raise the window" notice — it misfired even when the window came back, and success is its own feedback; a notification click is now fully silent. Also: the READMEs are rewritten as a release page.
-
v1.4.0 (2026-10-07) Seen once, never repeated; fixed notification clicks doing nothing (root cause: platform
tagreplacement swallowing the click); new Sub-agent alerts toggle; plus five smaller fixes. -
v1.3.3 (2026-10-07) Repeat reminders no longer interrupt you while you are looking at the window; the title alert uses words.
-
v1.3.2 (2026-10-05) Fixes from two code reviews: malformed base64 aborting the tracker, notification objects collected, repeat pings eating the cooldown, backwards clock muting,
titleFlashapplying immediately. -
v1.3.1 (2026-10-05) Fixed the intermittent window raise (Windows foreground lock).
-
v1.3.0 (2026-10-05) The raise runs in-process over FFI; Desktop vs browser derived automatically.
-
v1.2.1 (2026-10-05) Fixed the click doing nothing.
-
v1.2.0 (2026-10-05) Added the Host focus route (clicks did not work in this release; fixed in v1.3.x).
-
v1.1.2 (2026-10-05) Fixed blank dropdown rows.
-
v1.1.1 (2026-10-05) Layout and copy fixes.
-
v1.1.0 (2026-10-05) Custom ringtones, system notifications, per-trigger sounds, repeat reminders, quiet hours, three new sounds.
-
v1.0.0 (2026-10-04) Initial release.
💻 Development
npm install # once: the Host tests need the schemastery devDependency
node test/smoke.mjs # client half: assembly, triggers, cooldown, quiet hours, repeat reminders, notification clicks, sub-agent filtering
node test/host.mjs # host half: config schema, focus route, title matching, cross-half contracts
To publish to npm: bump the version, then npm publish (npm login and 2FA the first time). npm versions are immutable — bump for every change.
dsh-plugin-notify-sounds/
├── package.json # dsh.bundle.patch + dsh.client
├── cordis.patch.yml # Loader row: id: notify-sounds
├── lib/
│ ├── index.js # Host half: config schema + click-to-return route
│ └── client.js # Client half: sound engine, status watcher, settings section
└── test/
├── smoke.mjs # client half
└── host.mjs # host half
package.json is publishable as-is (files carries runtime artifacts only, @deepseek-ai/schemastery is a peerDependency plus a devDependency copy for the Host tests, pure ESM with no build step) — npm publish whenever you want it on npm.
📄 License
MIT © BillCx330
Versions
| Latest version | Published | Size |
|---|---|---|
| 0.0.0-stage | — | — |
| 1.5.0 | — | — |
| 1.6.0 | — | — |
| 1.6.1 | — | — |
| 1.6.2 | — | — |
| 1.6.3 | — | — |
| 1.6.4 | — | — |
| 1.6.5 | — | — |
| 1.6.8 | — | — |
| 1.6.9 | — | — |
Comments
Loading…