Notifications & Integrations
dsh-desktop-notify-afap
AFAP/dsh-desktop-notify · ★ 0
Desktop notifications for the DeepSeek Harness web GUI: a system-level toast (via the browser Notification API) whenever the harness needs a human — tool approval, a question or plan review…
Manifest details
安装
dsh plugin --profile web add 'github:AFAP/dsh-desktop-notify#102034f94ef9a8dcc4c516abb51872874b0d591c'GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试
For install and environment setup, plus plugin development tutorials, see DSH community docs ↗
README
Table of contents
- 1. The problem it solves
- 2. Features
- 3. Repository layout
- 4. Quick start
- Install from a source checkout (development / preview)
- Verify it loaded
- 5. Usage
- 5.1 When it notifies
- 5.2 What the notification does
- 5.3 Foreground suppression and blur flush
- 5.4 Pending count
- 6. Settings
- 7. Troubleshooting
- 8. Security and compliance (read this)
- 9. FAQ
- 10. Development and implementation
- 10.1 Module layering
- 10.2 Why not hook the approval/request waterfall
- 10.3 What it does instead: observe the official state source
- 10.4 UI seat
- 10.5 Build and release
- 10.6 Local preview and self-checks
- 11. Compatibility
- 12. Related resources
- 13. License
A system notification the moment the harness stops and waits for you. Tool authorization, a question or plan review, a finished turn — stop staring at the page, and click the toast to jump straight back to the pending card.
1. The problem it solves
DeepSeek Harness has to wait for you at three moments: a tool needs one-shot authorization, the model asks you a question or submits a plan to confirm, and a turn finishes. When you are not in front of the page, the conversation simply sits there — and staring at a tab is not a workflow.
This plugin turns those three moments into system-level notifications (real Windows toasts, raised through the browser Notification API), so you find out while you are working in another window.
┌──────────────────────── DeepSeek Harness ────────────────────────┐
│ │
│ tool authorization ─┐ │
│ a question ─┼─▶ pendingInteraction ──▶ uiSession. │
│ turn finished ─┘ (official state source, read-only) │
│ │ subscribe │
│ ▼ │
│ dsh-desktop-notify │
│ │ │
└──────────────────────────────────────────────────┼───────────────┘
▼
browser Notification API
│
▼
Windows system toast + click focuses the GUI
The important part: read-only observation, no takeover. The official approval panel and question composer keep working exactly as before — this plugin only reads the state beside them.
2. Features
- ✅ Three triggers: tool authorization (
approval), questions and plan reviews (question/plan-review), and turn completion (a session going idle) - ✅ System-level notifications: raised by the browser (a system toast on Windows) — no host process, no background daemon
- ✅ Click to jump: focuses the window and scrolls to the pending approval / question card
- ✅ Blur flush: stays quiet while you are looking at the GUI, then flushes the suppressed notification once you switch away
- ✅ Subagent noise control: completion is announced only for sessions you track in the host list; a subagent going idle never pings you
- ✅ Dedupe and self-cleanup: one notification per request, and it closes itself once you answer in the UI
- ✅ Zero sidebar cost: one section in Settings, nothing added to the sidebar
- ✅ Four switches, persisted locally: stored in browser
localStorage, never uploaded - ✅ Zero dependencies: no host service, no external npm package, no session-log reads, no HTTP route
3. Repository layout
dsh-desktop-notify/
├── README.md # Chinese main document (primary)
├── README.en.md # English full version (parallel structure)
├── LICENSE # MIT
├── .gitignore # secrets / build output / editor noise
├── package.json # dsh.bundle.patch + dsh.client (platform: web)
├── cordis.patch.yml # bundle patch: inserts the plugin row into the web profile
└── lib/
├── index.js # host half: deliberately inert (see section 10)
└── client.js # browser half: state observation + notifications + settings page
There is no build step: the browser half is a classic script registered through window.__ModuleLoader__.load() and served by the host on request.
4. Quick start
dsh plugin --profile web add github:AFAP/dsh-desktop-notify
A page refresh is enough — no dsh web restart. Then:
- Open Settings → Notifications in the left nav
- Press Grant and allow notifications in the browser prompt
- Press Send a test notification to confirm the channel
Upgrade:
dsh plugin --profile web update dsh-desktop-notify
Remove:
dsh plugin --profile web remove dsh-desktop-notify
Install from a source checkout (development / preview)
dsh plugin --profile web add link:D:\path\to\dsh-desktop-notify
Verify it loaded
The Settings nav gains a Notifications section (after General, before Models); the sidebar gains nothing.
5. Usage
5.1 When it notifies
| Trigger | Title | Body |
|---|---|---|
| A tool needs authorization | Authorization needed | The asker's reason (e.g. "write denied by sandbox"), or "Tool X requests privileged execution"; plus the session title |
| The model asks / submits a plan | A question is waiting / A plan needs confirmation | The question text (plan review uses fixed copy); plus the session title |
| A turn finishes (session idle) | Turn finished | The session title |
5.2 What the notification does
- Clicking it: brings the window forward and scrolls to the matching card (approval card, question composer), then dismisses itself.
- After you answer: answering or dismissing in the UI closes the matching notification, so no stale reminder survives.
- The same request: notifies once; a repeated observation of the same request never re-fires.
5.3 Foreground suppression and blur flush
"Only when DSH is not in the foreground" is on by default: while you are demonstrably looking at the GUI (page visible and window focused) it stays quiet — and the moment you switch away or minimize, the suppressed notification is flushed. You are neither nagged while watching nor missed when you walk away.
5.4 Pending count
The settings section carries one status line: ● N request(s) are waiting on you while authorizations or questions are pending, and "Nothing is waiting on you right now" otherwise. That is the only place you need to check (no sidebar real estate).
6. Settings
All four are booleans, stored in the browser's localStorage under dsh-desktop-notify:settings (per browser, never uploaded).
| Key | Default | Meaning |
|---|---|---|
approval |
true |
Notify when a tool needs authorization |
question |
true |
Notify when you must answer a question or confirm a plan |
completion |
true |
Notify when a turn finishes (session idle); tracked host-list sessions only |
backgroundOnly |
true |
Only notify while DSH is not in the foreground; turning it off notifies even while you watch |
Notification permission is not a plugin setting: the browser owns it per site. After a block, re-allow it in the site settings from the address bar (see section 7).
7. Troubleshooting
| Symptom | Where to look |
|---|---|
| No notification at all | Settings → Notifications shows the permission state: Not granted → press Grant; Blocked by the browser → set Notifications to Allow in the address-bar site settings and reload |
| Permission is fine, still nothing | Windows Focus Assist / Do Not Disturb swallows toasts — allow browser notifications in the system settings; also check whether "Send a test notification" works |
| Pending work, but no toast | The default "only when DSH is not in the foreground" deliberately suppresses it while you watch; switch away to trigger the flush, or turn that switch off |
| The Settings nav has no Notifications section | The plugin did not load: check dsh plugin --profile web list and reload the page (Ctrl+Shift+R) |
| "Unsupported here" is shown | The page is not a secure context (not https, 127.0.0.1 or localhost), so the browser exposes no Notification API |
| Too noisy | Turn off completion and/or question, or keep foreground-only notifications on |
| Clicking the toast does not jump to a card | That card belongs to another session: switch to it in the session list, then click the notification |
| A subagent finishing shows "Turn finished" | Should not happen: completion is filtered by host-list membership — please open an issue with a screenshot if it reproduces |
8. Security and compliance (read this)
- No reads, no writes, no requests: the host half (
lib/index.js) is a deliberately inert no-op — no route, no session-log access, no network call; the browser half only reads the state the harness already pushed to the page. - The only permission is browser notifications: revocable at any time in the site settings; once revoked the plugin silently stops, without errors and without affecting DSH.
- Notification bodies can appear on the lock screen / in the notification center: they carry the tool name, the request reason and the session title. Keep this in mind while screen sharing, recording or presenting; turn the relevant switches off for sensitive work.
- No credentials, no secrets: the repository contains no tokens, keys, internal hosts or machine-absolute paths (docs use placeholders such as
D:\path\to\...and$DSH_HOME). - Settings stay local:
localStoragedoes not sync across sessions or accounts, and no preference data is ever uploaded. - Open an issue for problems; report security details privately through the repository's Security Advisory instead of a public issue.
9. FAQ
Q: Can I get authorization notifications without "turn finished"? A: Yes — turn off the completion switch; the three switches are independent.
Q: Why not a native OS toast (a PowerShell helper, say)? A: This version uses the browser Notification API only: it also renders as a real Windows toast, it can focus the window and scroll to the card when clicked, and it needs no external process per notification. A host-side helper can neither reliably bring the browser forward nor avoid spawning a process each time.
Q: Why is there no sidebar entry? A: This plugin carries attention state only, with no permanent action of its own. One settings section plus the pending count is enough, and the sidebar keeps its space.
Q: Does it notify when the tab is closed? A: No. Notifications come from the page; with no page there is also no UI able to answer, so the request fails closed.
Q: Two browsers open — do I get two notifications?
A: Each page decides and raises its own system notification; settings (localStorage) are per browser.
Q: Does it interfere with DSH's own approval flow? A: No. It registers no approval answerer and only observes the official state source, so the official allow / reject path is untouched.
10. Development and implementation
10.1 Module layering
| File | Responsibility |
|---|---|
lib/index.js |
Host half: deliberately inert (an empty apply), so the plugin row loads cleanly; it provides no service and needs no config |
lib/client.js |
Browser half: registers locale dictionaries, subscribes to the state source, raises notifications, contributes the settings section |
cordis.patch.yml |
Bundle patch: inserts the desktop-notify row into the web profile composition (no inject, no config) |
10.2 Why not hook the approval/request waterfall
ctx.remote.$on("approval/request", (req, next) => ...) looks like the natural seam, and it is the wrong one.
The official panel (@deepseek-ai/dsh-client-ui-approval) registers its waterfall listener during boot and intentionally holds the whole waterfall until the user answers (it awaits pending.result). Any listener registered later — every out-of-tree plugin — therefore never runs for a request the panel owns. $on has no prepend option either, so load order cannot fix it.
10.3 What it does instead: observe the official state source
ctx.uiSession.sessionStatus // HostObservable: { getSnapshot, subscribe }
└─ Map<SessionId, { running, pendingInteraction, completionUnread }>
pendingInteraction.kindalready discriminatesapproval/question/plan-reviewand carriestoolName/reason/questions, so all three cases come from one read;- reading state is ordering-free and outside the answering path, which is what makes the zero-intrusion claim true;
- decoupling from the waterfall is also what makes foreground suppression and blur-time flushing possible at all.
10.4 UI seat
The plugin contributes a settings page through settings.section (order: 5 — after General, before Models, because this is a plain preference rather than a plugin administration page). The sidebar gains no button.
10.5 Build and release
There is no build step (a pure JS classic-script bundle), so no GitHub Actions workflow is configured: dsh plugin add <repo> serves the browser half straight from package.json's exports["./client"]. Changes are committed in Conventional Commits style and releases are SemVer tags.
10.6 Local preview and self-checks
- Manual: after a
link:install, reload the page, use "Send a test notification" and trigger one real authorization request. - Theme self-check: render the section with the real DSW theme tokens in light and dark, confirming the surfaces are opaque and legible (this plugin uses no translucent overlay token).
11. Compatibility
Requires DeepSeek Harness 0.1.7-alpha.1 or newer. The contracts it relies on:
| Contract | Used for |
|---|---|
ctx.uiSession.sessionStatus (HostObservable) |
Pending requests and running state |
pendingInteraction.kind: approval / question / plan-review |
The three notification classes |
settings.section slot |
The settings page seat |
Theme tokens --dsw-alias-bg-layer-2 / --dsw-alias-bg-skeleton and friends |
Section styling |
Browser half only (dsh.client.platform = web): no host dependency, no external npm dependency.
12. Related resources
- DeepSeek Harness: https://github.com/deepseek-ai/deepseek-harness
- Same author, same bilingual minimal-repository style: https://github.com/AFAP/dsh-token-usage
- GitHub Actions / README documentation: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes
13. License
MIT © contributors · see LICENSE
Disclaimer: this plugin only projects pending state that DSH already maintains into a system notification and changes no approval outcome. Focus Assist / Do Not Disturb, browser permission policy, or a closed page can all prevent a notification from appearing — do not make critical approval workflows depend on notifications alone; any consequence of a missed notification rests with the user.