dsh-native-notifications: System-level notifications for DSH approvals, questions, and task completion

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 running quickly; the completion notification only triggers after the session is idle for more than idleGraceMs.
  • 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 (desktop or web);
  • 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 → onlyWhenUnfocused skipped the focused window;
  • Has banner :: but no client :: shown → Page didn’t poll (check poll-failed / unauthorized);
  • Has client :: shown but no client :: 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 on ctx.webServer: /native-notifications/events (long polling) and /native-notifications/report (client diagnostics). Like other Web routes on the Host, it uses ctx.connection.requestRejection for 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 calls uiWorkspace.openSession(sessionId). The cursor and Host epoch are stored in localStorage, 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.

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye