dsh-system-notifications
Desktop notifications for DeepSeek Harness (dsh) Web sessions: get notified when a session waits for approval or finishes, then click to jump back to it.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 23, 2026
- Updated
- Oct 26, 2026
Introduction
System Notifications · DeepSeek Harness plugin
A client-only plugin for the DeepSeek Harness (dsh) Web UI that raises a desktop
notification when a session needs you, and jumps back to that session when you click it.
- A session waits for permission approval / an answer / a plan review → notification.
- A session finishes a run (
runningflipstrue → false) → notification. - Clicking the notification brings the browser window to the front and opens that session.
- Settings live in Settings → General → System Notifications.
Notifications are edge-triggered: the first snapshot after page load is taken as the baseline, so a refresh never floods you with a backlog.
Version 1.2.0 — a second General row, Persistent notifications, asks the OS to keep a notification on screen until you click or dismiss it (Web Notification
requireInteraction).Version 1.1.0 — notification titles name the project (
Project · Event, e.g.harness-plugins · Approval required), and the body names the session.
Requirements
- DeepSeek Harness with plugin support (
engines.dsh: >=0.2.1-rc.2), Web GUI served in a Chromium-based browser (Chrome/Edge).http://127.0.0.1is a secure context, so no HTTPS is required for the Notification API. - The page must stay open — notifications are raised by the page's JavaScript, not by the host.
Install
The package is not published on npm yet; install it straight from this repository:
dsh plugin --profile web add github:purejiang/dsh-system-notifications
Or in the Web UI: Plugins → Install, and paste
github:purejiang/dsh-system-notifications.
The plugin is build-free: it ships index.js (an empty host half that registers the bundle row)
and client.js (the whole feature). Refreshing the page after install is enough.
Enable and test
- Open Settings → General → System Notifications and switch it on.
- Grant the browser's notification permission. The plugin asks once, on your first click or
keypress after the page loads; you can also use the Authorize button in the same row.
If you dismissed or denied the prompt, allow
http://127.0.0.1:3080inchrome://settings/content/notifications(or the site icon in the address bar) and retry. - Optional: turn on the second row, Persistent notifications, to keep each notification on screen until you click or dismiss it. It is off by default; Windows may still route the toast to the notification center instead of leaving it on top.
- The Test notification button is hidden in the shipped UI. To use it, double-click the
row title “System Notifications” (stored per session in
sessionStorage), or open the page with?dshNotificationTest=1.
Behaviour
- When: a new
pendingInteractionappears (approval,question, orplan-review), orrunningchanges fromtruetofalse. - Title:
<project> · <event>. The project is the title of the workspace that owns the session (the default workspace is localized, e.g. “Default Workspace”); if the workspace list is not loaded yet it falls back to the directory name of the session'scwd. - Body: the session's display title plus the reason, e.g. ““Refactor the toolbar” requests to run bash and is waiting for your approval.” Approval notifications include the tool name; completion notifications include the elapsed time when the plugin observed the whole run.
- Icon: the embedded 128×128 DeepSeek whale mark (PNG data URL, no external requests).
- Quiet while you watch: no notification while that session's conversation panel is visible and the window has focus.
- No noise: subagent sessions are skipped, duplicate notifications collapse per session
(
tag), and at most 6 notifications are kept open. - Persistent mode (optional): when enabled, notifications set
requireInteractionso the OS keeps the toast up until the user acts on it, instead of hiding it after the default timeout.
Settings rows
Both rows are built from the host's own primitives (Switch/Button from
@deepseek-ai/dsh-client-ui-primitives), so their spacing and type match the built-in settings
rows. The main row shows the current state and explains why notifications are off when the
browser permission is denied; the second row controls persistent notifications.
How it works
Everything is done through documented client extension points; no host-side code is needed:
| Need | Extension point |
|---|---|
| Detect waiting / finished | shell.overlay component reading props.useSessionStatus() |
| Session title | props.useSessions() → SessionListState.byId[id].displayTitle |
| Project name | props.useWorkspaces() (workspace title / session ownership), fallback to cwd |
| Am I looking at it | props.usePanelInfo().activePanelId, document.hasFocus(), visibilityState |
| Raise the notification | the browser Notification API |
| Click → open session | ctx.get('uiWorkspace').openSession(sessionId) |
| Settings controls | settings.general.item |
The running transition is observed both through React snapshots and by subscribing to the
client uiSession.sessionStatus store, because background tabs throttle rendering and would
otherwise coalesce a short run into no visible change.
Files
package.json # dsh.bundle + dsh.client declarations
cordis.patch.yml # inserts the bundle row
index.js # host half: registers the bundle, no logic
client.js # observer, project-name resolution, session tracker, settings rows
locale/en.json # plugin list title/description (English)
locale/zh.json # plugin list title/description (Chinese)
icon.svg # plugin list / settings row icon
notification-icon.png # notification icon source (also embedded in client.js)
notification-icon.svg # vector source of the notification icon
Limitations
- The page must remain open; a closed tab cannot notify.
- Notification layout (font, colour, extra buttons) is controlled by the browser and the OS.
- Clicking an old notification after a page reload focuses the window but may not reopen the session (the page context is gone).
- Notification titles are truncated by the OS; the project name is placed first so it stays visible.
- If the browser permission is denied, nothing is sent and the settings row states the reason.
- Persistent mode is best-effort: some platforms ignore
requireInteractionand fall back to the normal notification timeout.
License
MIT