DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

guanhaisen /

guanhaisen/dsh-desktop-notify

Verified

This plugin has no description yet.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: master@66bc7b9d

dsh-desktop-notify

English | 中文

Desktop notifications for DeepSeek Harness. A Windows toast appears when a Session needs you — and clicking it brings that Session back to the front.

Two triggers:

Trigger When it fires
Needs attention The Agent is blocked waiting for your approval or your answer.
Run finished A Session that was running has stopped, and the run lasted at least minRunMs.

Install

Requires the Desktop application (the Windows toast path depends on Electron running the renderer; a browser tab has no AppUserModelID to notify under).

From the DSH plugin page (no terminal)

Open Settings → Plugins → Add plugin, and paste one of these into Package name or address:

What you paste When to use it
github:guanhaisen/dsh-desktop-notify Straight from this repository. Nothing published to npm.
https://github.com/guanhaisen/dsh-desktop-notify Same thing, written as a URL.
D:\path\to\dsh-desktop-notify A local checkout. Must be an absolute path.

Then press Install. The plugin manager runs pnpm add in your profile, adds the bundle to the profile's list, and the notification half activates without a restart in most cases — reload the page (Ctrl+R) if the first toast does not arrive.

Installing from git resolves the repository's default branch. If you fork this and your default branch differs, paste the full spec with a ref instead: github:you/dsh-desktop-notify#main.

The plugin page states that installed plugins do not auto-update: to upgrade, uninstall and install again. A github: install follows the default branch, so reinstalling picks up new commits.

From the command line

# In your DSH desktop profile directory:
#   Windows: %USERPROFILE%\.dsh\profiles\desktop
pnpm add github:guanhaisen/dsh-desktop-notify

Or link a working copy, so edits apply on save:

pnpm add link:/path/to/dsh-desktop-notify

Either way, the package must then appear in that directory's package.json bundle list — the plugin page does this for you; a manual install does not:

{
  "dsh": {
    "profile": {
      "bundles": ["...existing bundles...", "dsh-desktop-notify"]
    }
  }
}

Keep this package free of runtime dependencies. pnpm does not install a linked package's own dependencies, and a resolution failure during Cordis tree initialisation stops DSH from starting.

You will not see a toast when…

This is the part that looks like a bug and is not:

Situation Why it is silent
You are looking at that Session The prompt is already on screen.
The run was shorter than minRunMs (20 s) A quick reply is not news.
The Session was already running when the window opened The first snapshot is a baseline, so opening DSH does not fire a burst.
Windows Do Not Disturb is on The OS suppresses the banner. The notification still reaches the notification centre.

If none of those apply and nothing appears, read the decision log — see Verifying it actually works. It records every suppression with its reason, so "why did I not get a toast?" is answerable.

Why this is a plugin and not a shell patch

The obvious implementation — add a Notification call to the Electron main process — cannot work here, and understanding why explains the whole design.

The Desktop Host is not the Electron main process. lib/main.js spawns it as a separate Node child process:

spawn(this.node, ['--expose-internals', entry, ...], { stdio: ['ignore','pipe','pipe','ipc'] })

That child is the Electron executable re-entered in Node mode, so require('electron') inside a Host plugin yields no Notification, and the process holds no AppUserModelID. Meanwhile the shell's own notification code (lib/main.js) only ever fires for one thing: a mandatory update.

The renderer, however, has everything needed:

  • dsh-app: is registered as a privileged scheme with standard: true and secure: true, making the window a secure context, which the Web Notification API requires.
  • The application session installs a permission handler that returns true for every non-media permission.

So a plain Web Notification in the window becomes a real Windows toast under the application's own identity (electron.app.DeepSeek Harness). No shell patch, no helper process, and it survives application updates.

Where the state comes from

Nothing is polled and no second source of truth is created. @deepseek-ai/dsh-client-ui-session already folds exactly these facts and publishes them as ctx.uiSession.sessionStatus:

Map<sessionId, { running, pendingInteraction, completionUnread }>

It is fed by the api-session/status Remote event. This plugin only reads it.

pendingInteraction is the interaction object itself, whose kind is one of:

kind Raised by Copy shown
approval @deepseek-ai/dsh-client-ui-approval Tool named in the request is asking for privileged execution
question @deepseek-ai/dsh-client-ui-user-questions The Agent asked a question
plan-review same, when the questions are a plan A plan is waiting for review

Its key is the identity used to de-duplicate, so a status re-emission for the same wait never produces a second toast.

Layout

File Role
index.js Host half. Owns the Loader row the browser half binds to. Registers nothing — no tool, no service, no route.
client.js Browser half. Watches Session status, decides when to notify, renders the toast, wires click-through, and publishes the DesktopNotify Inspect provider.
cordis.patch.yml The one Host row the bundle inserts.
test/watcher.test.mjs Behavioural test of the decision logic, runnable with plain node.

Configuration

Set on the plugin's row in the profile patch, or via the Plugins page:

Key Default Meaning
notifyOnAttention true Notify when a Session is blocked on you.
notifyOnComplete true Notify when a run finishes.
minRunMs 20000 Ignore runs shorter than this. Quick replies are not news.
sound true Play the system notification sound.

De-duplication

A repeated interaction never re-notifies: identity is the interaction's own key, so a status re-emission for the same pending approval stays silent. The first snapshot after the window opens is treated as a baseline rather than as news, which is what stops a launch from firing a burst of toasts for Sessions that were already running.

Verifying it actually works

Delivery is asynchronous and visual, and neither half can observe the toast the operating system renders. So the browser half publishes a Cordis Inspect provider, DesktopNotify, which can be queried from outside the page:

Method Answers
status Is the half mounted? What is Notification.permission? Is this a secure context? Which Sessions are tracked, and since when?
testToast Fire one real toast now, and report what was constructed.
decisions The in-memory decision log — every notification sent and every one deliberately suppressed, with the reason.

decisions is the one that answers "why did I not get a toast?": a suppressed entry carries suppressed: 'watched' (you were looking at that Session) or suppressed: 'too-short' (the run ended before minRunMs).

The log is in memory only, bounded to the last 100 entries. It is deliberately not written to disk: a file that can silently fail to appear is worse than no file at all.

Confirming against the OS

testToast reporting delivered: true only means a Notification was constructed. To confirm it reached the operating system, compare Windows' own record for this app's AUMID before and after:

$k = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\electron.app.DeepSeek Harness'
[DateTime]::FromFileTime([int64](Get-ItemProperty $k).LastNotificationAddedTime)

This is how the plugin was verified: the timestamp held steady across a 12-second idle control, then advanced on each testToast.

Development

node --check index.js
node --check client.js
node test/watcher.test.mjs

The test loads client.js under a fake __ModuleLoader__, feeds it synthetic Session-status transitions, and asserts which toasts fire and what the decision log records. It exits non-zero on failure.

Constraints

Keep dependencies empty. The desktop profile installs this package with pnpm's link: protocol, and pnpm does not install a linked package's own dependencies. Declaring one would make dependency resolution fail during Cordis tree initialisation and stop DSH from starting.

—/ 5

No ratings yet

Verified DSH bundle

Commit 66bc7b9d69f8

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout