dsh-session-notify
Reply toasts for DeepSeek Harness sessions.
Run several sessions in parallel across different workspaces, and the one you are not looking at still needs your attention. This plugin watches every session on the Host and, when a background session finishes answering, pushes a WeChat-style toast into the top center of the Web UI:
- Title —
<workspace folder>-<session title>, one line, ellipsized. - Body — the first characters of the reply, two lines, ellipsized.
- Stack — up to six cards, newest on top; older cards stay until you close them.
- Close — every card has its own
×in the top-right corner. - Jump — clicking a card opens that session.
- Look — a light card on a wide, soft shadow: 18px radius, top-to-bottom gradient, and a top-down fade in and out.
- Quiet — the session you are already reading never toasts, and neither do internal subagent children.
Install
The package is a DSH bundle: a Host row plus a browser half. Install it into the
running profile from the directory that holds this package.json:
plugin_manager install_bundle target: /absolute/path/to/dsh-session-notify
Or, from a shell inside the profile:
pnpm add /absolute/path/to/dsh-session-notify
then add dsh-session-notify to the profile's cordis.patch.yml (or enable the
row in the Plugin Manager). Reload the page once: the browser half is served from
the installed package, so a fresh page load is what activates it.
Uninstall with plugin_manager remove_bundle (or remove the row).
Configuration
The browser half reads no Loader row config, so the knobs are constants at the
top of client.js. Edit them and reload the page:
| Option | Default | Meaning |
|---|---|---|
maxToasts |
6 |
Cards kept on screen; the oldest fall off the bottom. |
ttlMs |
0 |
Auto-dismiss delay. 0 keeps a card until it is closed; a positive value pauses while the window is hidden. |
pageMessages |
8 |
Message records requested by one tail read. |
maxBodyChars |
200 |
Longest reply excerpt kept; CSS owns the visible lines. |
includeActiveSession |
false |
Also toast the session open in this window. |
How it works
Everything runs in the browser; the Host half is an empty Loader entry.
- The browser half subscribes to the forwarded
api-session/statusevent and reacts when a session stops running. - It reads that session's projection watermark (
session/projections), which carries the durable title and the effective working directory. - It reads one message-aligned history page through
session/page, ending at that watermark, and takes the last assistant message of the page. - If that message is the reply that ends the turn, the toast is published into
the frame-wide
shell.overlayslot. The session is never opened, so the notification costs one short read and does not touch the workspace view.
The plugin keeps no durable state: a page reload starts from an empty stack, and only replies that finish while the page is open are announced.
The session Remote namespace is read with ctx.get('remote.session'), not
ctx.remote.session: a namespace is mounted on the Client root, while the
remote.session property resolves through the calling fiber's associate table
and is undefined from a plugin context.
Compatibility
Built against DeepSeek Harness 0.2.x (@deepseek-ai/dsh-*). It depends on the
shipped browser wire API — the forwarded api-session/* events and the
session.projections / session.page Remote methods — and on the
shell.overlay slot. Those are pre-stable: a Harness release that renames one of
them needs a matching plugin release.
The plugin styles itself with the public --dsw-alias-* theme tokens and the
--dsh-frame-overlay-top inset, so light/dark and the Windows caption are
inherited rather than re-implemented.
Development
npm test # node --test: behaviour tests through a fake Client context
node --check client.js
The tests load client.js the way the page does — through
window.__ModuleLoader__ — and drive the real event handlers, projection and
page reads, store and card markup. No build step, no dependencies.
License
MIT. See LICENSE.
No comments yet. Be the first to write one.