Install
Inside DeepSeek Harness, with dsh-market
dsh plugin --profile web add dshmarket
Or from the command line
dsh plugin --profile web add "https://github.com/TaoYe599/dsh-notify/releases/download/v0.1.0/dsh-notify-0.1.0.tgz"
Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.
README
System notifications for DeepSeek Harness: know when a task finishes, or when DSH is waiting on you.
A web notification while a browser page is open; a native Windows toast raised by the DSH host when the browser is closed.
What it solves
DSH needs you in exactly two states:
| State | How it shows up | Notification |
|---|---|---|
| It is waiting for you | ask_user_question, an approval request, a plan confirmation |
"Needs your reply / confirmation" |
| It is done with you | A conversation goes from running to idle (the turn finished) |
"Task finished" |
Coverage follows one rule: whichever process is still alive when the turn ends raises the notification.
| Where you are | Who notifies | Channel |
|---|---|---|
| Looking at DSH | nobody | the state change is already on screen |
| Another tab / another app / browser minimised | the page | web notification (click focuses the tab) + (N) unread badge in the tab title |
| Browser closed | the DSH host | native Windows toast via PowerShell 5.1 + WinRT ToastNotificationManager |
Design notes
- Layered by surviving process. A web notification is physically impossible once the page is gone, so the host half takes over — the only process still able to speak.
- No double notifications. A presence handshake keeps the two apart: each page pings
/dsh-notify/presenceevery 20s while it holds the plugin and sends asendBeaconfarewell onpagehide. While any page is alive the host stays silent and lets the client own delivery — only the page knows whether you are actually looking. - Invisible by default. No permanent settings row. One small card asks once for notification permission; if the browser refuses to grant it (Edge's "quiet notification requests" can swallow the prompt entirely) the card comes back with the reason instead of failing silently.
- No lost events. Detection reads the authoritative client stores through the
shell.overlaystandard props, so delivery is push-driven by DSH's own connection — no polling, and therefore no background-tab timer throttling. - Subagents do not notify. The host only reports top-level sessions (
agents.roots()); a subagent finishing inside a running turn is not news.
Install
This package is a standard DSH bundle (it declares dsh.bundle.patch and ships one configuration layer). Install it with the official CLI — dsh plugin forwards its arguments to pnpm inside the profile directory and appends the bundle to dsh.profile.bundles automatically.
# from a local checkout
dsh plugin --profile web add <path-to-this-package>
# from a tarball (handy for sharing)
pnpm pack
dsh plugin --profile web add ./dsh-notify-0.1.0.tgz
# from npm or GitHub
dsh plugin --profile web add dsh-notify
dsh plugin --profile web add github:TaoYe599/dsh-notify
Verify without booting:
dsh --profile web --dump-config # should print a "# == dsh-notify" layer
Restart dsh web after installing or updating. The bundle list and the host half are read at profile boot; client-bundle-only changes just need a page refresh.
Uninstall
dsh plugin --profile web remove dsh-notify
then restart. To disable without removing, set disabled: true on this plugin's row in the profile's cordis.patch.yml.
Why this is a community plugin rather than a PR
The official repository has has_pull_requests: false — pull requests are disabled at the repository level (issues too; only Discussions are open). Its CONTRIBUTING.md states that external pull requests cannot be accepted at the moment and asks contributors to grow the ecosystem instead, associating their project with the dsh-plugin topic, adding that "we do not believe that packages in the official repository are inherently more important than packages created by the community."
Troubleshooting
The host half appends one diagnostic line per event to ~/.dsh/dsh-notify.log.jsonl (or under $DSH_HOME):
| event | meaning |
|---|---|
presence-route |
the presence route registered (the handshake is live) |
toast |
a native notification was raised (sessionId / title / presenceReady) |
skip + reason: page-alive |
a page is alive, so the client owns this one |
skip + reason: subagent |
a subagent session — not reported |
skip + reason: repeat-window |
same session went idle twice within 10s |
toast-failed |
the PowerShell call failed (with the error) |
no-web-server |
the webServer service was unavailable, so presence cannot gate delivery |
Client-side logging goes to the browser console with a [dsh-notify] prefix.
Known boundaries
- Native notifications are Windows-only for now. Detection still runs elsewhere; only the display step is skipped.
- Presence TTL is 150 seconds. A clean browser exit sends the farewell beacon immediately; if the browser crashes, the host takes over within 150 seconds.
- Remote access may double-notify. When DSH is reached through a non-localhost origin, the presence ping is a same-origin request; if that path is not proxied the host concludes "no page" and both channels fire. Local
127.0.0.1is unaffected. - Subagent sessions are intentionally silent.
Layout
dsh-notify/
├── package.json # dsh.bundle.patch + dsh.client(platform: web)
├── cordis.patch.yml # inserts this plugin's row into the profile composition
├── lib/
│ ├── index.js # host half: presence route + turn-end detection + Windows toast
│ └── client.js # client bundle (hand-written; no build toolchain)
├── LICENSE
└── README.md # 中文说明
Comments
Comments live in GitHub Discussions. Sign in with GitHub to post or react.