Introduction¶
Running long tasks with DSH is the norm: initiate a task, go do something else, and return only to find the agent finished five minutes ago—or worse, it’s stuck on a prompt waiting for your approval, doing nothing. The issue isn’t that the agent is slow, but that you weren’t notified in time.
dsh-notification solves exactly this. It listens to the harness’s own lifecycle events and issues a notification the moment attention is needed. Below is an introduction to its features, installation, and configuration.
What is it¶
dsh-notification is a DeepSeek Harness (DSH) plugin maintained by nishit130, currently at version 0.1.1 under the MIT license. One-sentence positioning: it notifies you via desktop, browser, or webhook when the agent finishes a turn, encounters an error, or waits for your approval, without you needing to stare at the tab.
DSH’s philosophy is “everything is a plugin,” and peripheral capabilities like notifications are perfectly suited to be mounted as plugins without requiring changes to the harness itself.
Which three event types does it monitor¶
- Agent finished: Triggered when
agent/statuschanges fromrunningtoidle, provided the turn duration is ≥minTurnDurationMs. Enabled by default. - Agent error: Listens for
agent/error, triggered when a step or turn encounters an error. Enabled by default. - Approval needed: Listens to the
approval/requestwaterfall. The plugin observes (observe-only) and always delegates withnext(), never making approval or rejection decisions for you. Enabled by default.
The role of minTurnDurationMs is filtering: quick turns that finish in seconds are not worth disturbing; only turns reaching the threshold trigger a notification.
Three notification channels, and where they trigger¶
First, it is important to clarify on which machine each channel triggers, as this determines which one you should enable:
- Desktop notification (
desktop): Zero dependencies. Usesosascripton macOS,notify-sendon Linux, and PowerShell toasts on Windows. Triggers on the machine running the dsh server, suitable for cases wheredsh webruns on your own machine. - Browser notification (
browser): Uses the standard Notification API and triggers on the machine viewing the Web UI. By default, it only pops up when the tab is hidden—since a visible tab implies you are looking at it. SetbrowserOnlyWhenHiddentofalseto change this behavior. - Webhook (
webhookUrl): POSTs JSON to the configured URL. Thetextfield is Slack-compatible, so Slack, Discord, or generic incoming-webhook URLs work out of the box. Suitable for sending messages to a phone, a Slack channel, or unattended scenarios.
The three channels can be toggled independently, for example: desktop: false or browser: false.
One combination to note: when running on a single machine and the tab is hidden, the same event might trigger both desktop and browser notifications. If this is redundant, simply turn off one of the channels.
Installation¶
dsh plugin --profile web add dsh-notification
# or straight from git:
dsh plugin --profile web add github:nishit130/dsh-notification
Choose either command. The plugin is pure ESM JavaScript with no build steps, so installing from git does not require adding an entry to allowBuilds.
Configuration¶
Override plugin configuration in the profile’s cordis.patch.yml (or via the Settings UI):
- insert:
- id: notify
name: dsh-notification
config:
minTurnDurationMs: 10000 # Only notify turns ≥ 10 seconds
webhookUrl: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
notifyOnApproval: true
desktop: true
title: 'DSH'
This configuration sets the turn duration threshold to 10 seconds, hooks up a Slack webhook, keeps desktop notifications enabled, and changes the notification title to DSH.
All configuration items and their defaults:
| Configuration Item | Type | Default Value | Meaning |
|---|---|---|---|
notifyOnIdle |
boolean | true |
Notify when a turn ends |
notifyOnError |
boolean | true |
Notify on agent/error |
notifyOnApproval |
boolean | true |
Notify when a tool call is waiting for approval |
minTurnDurationMs |
number | 5000 |
Do not notify turns shorter than this duration |
desktop |
boolean | true |
Send native desktop notifications on the server host |
browser |
boolean | true |
Show browser notifications in the Web UI |
browserOnlyWhenHidden |
boolean | true |
Suppress browser notifications when the tab is visible |
webhookUrl |
string | '' |
Optional POST target (Slack-compatible payload) |
title |
string | 'DeepSeek Harness' |
Desktop notification title |
The webhook payload looks like this:
{
"text": "Agent finished — done in 2m 14s",
"summary": "Agent finished",
"body": "done in 2m 14s",
"level": "info",
"ts": "2026-08-21T12:34:56.000Z"
}
text is the Slack-compatible field, while summary and body allow custom consumers to separate the display. level and ts provide the severity level and timestamp.
Three design decisions¶
After the steps above, the plugin should be working. Its implementation features a few design decisions worth knowing about:
- Everything registered via
ctxis an effect. Listeners are automatically removed on unloading or hot reloading; there is no manual cleanup path (Cordis revertible effects). approval/requestis a waterfall. The plugin only observes; the listener always callsnext()—failing to do so would seize the decision-making power and swallow the actual responder.- Never interrupts the agent loop. Desktop notifications are spawned in a detached “fire-and-forget” manner, webhook failures are swallowed, and the notifier never throws errors to the agent turn.
Local Development¶
To modify the code, first copy dev.patch.example.yml to dev.patch.yml (which is gitignored), point it to the absolute path of your checkout, and then run:
pnpm dsh web --patch ./path/to/dsh-notification/dev.patch.yml
Changes to index.js can be hot-reloaded without restarting. Run tests with npm test.
Use Cases and Notes¶
Choose the channel based on your deployment method:
- Harness runs on your own machine: Enable desktop notifications.
- Harness runs on a remote server, but you view the Web UI locally: Enable browser notifications.
- Unattended or long-running tasks: Configure a webhook to send messages to Slack, Discord, or any service that accepts POST requests.
Two reminders:
- The plugin runs with the permissions of the current dsh process. It is recommended to review the source code and license before installing (this project is MIT licensed). From the
filesfield in package.json, the plugin only containsindex.js,client.js, andcordis.patch.yml, so the cost of inspection is low. - Browser notifications require the Web UI to request permission upon your first click or keypress. After installation, interact with the page once to grant permission.
Conclusion¶
dsh-notification solves a specific problem: when the agent runs long tasks, you no longer need to periodically return to refresh the tab. With three event types, three channels, and one duration threshold, the configuration scope is small, and the default values work out of the box.
- Plugin directory page: https://www.skillhub.cn/plugins/nishit130/dsh-notification
- GitHub: https://github.com/nishit130/dsh-notification
skillhub.cn, which hosts this plugin, is a community-maintained plugin directory and has no affiliation with the DeepSeek official project.