Introduction¶
When DSH runs a long task, people often leave the window to go elsewhere. When approvals arrive, you need to confirm permissions; when questions come, you need to select answers; when the task finishes, you need to check the results—if these events only appear on the page, they are missed if the person isn’t there. Existing approaches rely on auxiliary processes to send notifications. The system attributes banners to that script runner, and clicking back also doesn’t land in the DSH session.
The tommy00748-tech/dsh-native-notifications introduced below takes a different approach: notifications are emitted by the DSH page itself. The OS attributes them to DeepSeek Harness. Clicking the banner returns to the session, and you can choose or write custom ringtones.
What is this¶
A DSH plugin, categorized under “Client-side”, maintained by tommy00748-tech. The problem it solves is: making DSH pop up desktop notifications that are correctly attributed and clickable to return to the session when human intervention is needed or a round finishes, avoiding people from staring at the window or missing approvals.
The repo shows 0 stars, license is MIT, npm package name is dsh-native-notifications.
Triggered Events¶
The plugin listens to the following Harness events (event meanings and default switches see the table in README):
| Harness event | Banner content | Default |
|---|---|---|
agent/status → idle after a turn (root session) |
Task completed · Time taken … | on |
approval/request hanging after approvalDelayMs |
Need to confirm permissions · tool · reason | on |
user-questions/request hanging |
Waiting for your choice · question | on |
agent/error |
Execution error · message | on |
deepseek-account/model-sign-in-required / session-expired |
Need to log in / Login expired | on |
subagent/end、workflow/end |
child agent / workflow finished | off |
The banner title uses the session name, body example:
<Session Name>
Task completed · Time taken 5m 43s, feel free to come back and check the results.
Several anti-noise logic:
- Approvals automatically processed (e.g., LLM reviewer in Auto permission presets) won’t ring; banners are delayed, only triggered while the request is still waiting for you.
- The completion notification after an error is suppressed; one failure won’t ring twice.
- The target turn and subsequent turns return to
runningquickly; the completion notification only triggers after the session is idle for more thanidleGraceMs. - Teammates, subagents, and workflow subprocesses are filtered by
onlyRootSessions; multi-agent long runs only ring once for the root session actually ending.
Ringtone Configuration¶
Ringtones are synthesized by the plugin using Web Audio. There are no audio files, and no system default sounds are used; system sounds are suppressed so they won’t ring twice. All presets share the same motif, which is distinguishable: completion is an upward do-mi-so, “need you” is two short high-pitched beeps, and error is downward.
Open Settings → General → Notification sound, select a preset:
| preset | timbre |
|---|---|
| Playful bubbles (default) | triangle body, with a slight upward slide and a high-pitched flash |
| Soft bell | sine body, plus a light octave overtone |
| Bright pluck | short triangle pluck, no slide |
| Custom | your own note sequence, played with the playful timbre |
| Muted | silent |
This row has a Preview button. Custom uses a text box, max eight notes separated by spaces or commas, can be note names (e.g., C6 E6 G#6, Bb5) or frequencies (e.g., 1046.5 1318.5):
C6 E6 G6 ← do-mi-so
G6 G6 ← double boop
1046.5 1318.5 ← same as above, using frequency notation
Preferences are stored in localStorage key dsh-native-notifications:sound, so they are effective per device/browser, and take effect immediately on the next banner, without needing a round-trip to the Host.
Installation and Enabling¶
Installation commands are from the repo README, distinguishing Web and desktop by profile:
# from GitHub
dsh plugin --profile web add github:tommy00748-tech/dsh-native-notifications
dsh plugin --profile desktop add github:tommy00748-tech/dsh-native-notifications
# from npm (available after npm publish)
dsh plugin --profile web add dsh-native-notifications
# from a local checkout
dsh plugin --profile desktop add link:/absolute/path/to/dsh-native-notifications
After installation, confirm desktop-notifications is enabled in Settings → Plugins. The bundle patch will insert it and enable it by default. No build steps, no dependencies, no native modules: the Host half is standard ESM, the Client half is a pure JS bundle.
Requirements:
- A DSH profile that provides a Web UI (
desktoporweb); - The page automatically loads the Client half on install (Host graph rebuild syncs to connected pages); if manually copied and the banner never appears, reload the window;
- Desktop notifications for DeepSeek Harness must be allowed in the OS (macOS in System Settings → Notifications). Electron grants page permission, but the OS-level allow is independent.
Configuration Options¶
All switches are on that config line, with default values and descriptions written in cordis.patch.yml. Note that the patch replaces the config object entirely, so if you only want to change one field, you must write back the fields you want to keep:
- id: desktop-notifications
config:
notifyComplete: true
notifyQuestion: false # do not ring for questions
idleGraceMs: 2000
onlyRootSessions: true
delivery: client # client | osascript | both
Available fields: notifyComplete, notifyApproval, notifyQuestion, notifyError, notifyAccount, onlyWhenUnfocused, silent, idleGraceMs, errorSuppressMs, approvalDelayMs, onlyRootSessions, notifyOnMount, logPath.
delivery: osascript is a legacy fallback for hosts that cannot display Web notifications. On macOS, banners are attributed to Script Editor, disabled by default.
Diagnostic Logs¶
Each banner and each client report is appended to the audit log. Default path is ~/.dsh/dsh-native-notifications.log, changeable via logPath. Log example:
2026-11-05T08:58:26.102Z mounted :: pid=9027 :: delivery=client :: epoch=... :: platform=darwin
2026-11-05T08:58:26.178Z client :: boot :: permission=granted protocol=dsh-app: path=/ visibility=hidden sound=cute chime=v5-robust
2026-11-05T08:59:47.177Z banner :: Can we make a native notification plugin for DSH :: Task completed · Time taken 5m 43s, feel free to come back and check the results.
2026-11-05T08:59:47.186Z client :: shown :: kind=complete permission=granted
2026-11-05T08:59:47.188Z client :: sound :: kind=complete preset=cute
To diagnose “why didn’t the banner pop up”, check the logs:
- No
client :: boot→ Client half not loaded, reload window; client :: permission :: denied→ System/browser won’t allow notifications;client :: suppressed→onlyWhenUnfocusedskipped the focused window;- Has
banner ::but noclient :: shown→ Page didn’t poll (checkpoll-failed/unauthorized); - Has
client :: shownbut noclient :: sound→ Ringtone didn’t play (audio still locked or muted).
To directly send a notification to verify the osascript channel:
node scripts/notify-test.mjs "title" "body"
How it Works¶
One bundle, two halves:
- Host (
index.js) listens to the events above, maintaining a small ring buffer for banner events. It provides two same-origin routes onctx.webServer:/native-notifications/events(long polling) and/native-notifications/report(client diagnostics). Like other Web routes on the Host, it usesctx.connection.requestRejectionfor access control, so only authenticated pages can read. - Client (
client.js, no rendering, always mounted) long polls these two routes, displaying each event via the Platform Notification API (new Notification(...)). On click, it focuses DSH and callsuiWorkspace.openSession(sessionId). The cursor and Host epoch are stored inlocalStorage, so reloads won’t replay old banners, and Host restarts start from “now”.
Long polling interrupts after wait + 10s and retries, so a Host reconnection won’t block the client. The plugin doesn’t append session events, doesn’t import the Harness Client package, only contributes one bundle line and one Settings line.
Compatibility¶
The README states it has been tested on dsh 0.2.1-rc.5 and 0.2.2-rc.1. Runtime compatibility checks read the top-level engines.dsh field. In package.json it is >=0.2.1-rc.5 <0.3.0, Node requires >=20.
Use Cases and Notes¶
Suitable for people running long tasks on DSH who don’t want to stare at the window, and for teams running multi-agent long tasks who want to ring only once at the root session end. If your host page cannot display Web notifications, delivery: osascript is an alternative, but accept that banners are attributed to Script Editor.
Two reminders: notifications are sent with the permissions of the current DSH process; the OS layer must allow notifications for DeepSeek Harness. The plugin source is a community-maintained GitHub repo (there is also an entry on the community directory site, which has no official affiliation with DeepSeek / Synthflow). It is recommended to review the source code and the MIT license before installing.
Conclusion¶
One sentence summary: It attributes DSH’s approval, question, completion, error, and login banners to DeepSeek Harness, offers optional or custom synthesized ringtones, and provides audit logs for troubleshooting why they didn’t ring. The directory page is at https://www.skillhub.cn/plugins/tommy00748-tech/dsh-native-notifications, and the repo is at https://github.com/tommy00748-tech/dsh-native-notifications.