In-app local notification center for DSH: a sidebar Notifications entry with unread badge, full-screen center with level color coding (info/success/warn/error), time/source filters, sorting, title/content search, read/unread, delete, detail popup with one-click copy, and pagination; written by the notify_add agent tool or the dsh-localnotify CLI (with auto-cleanup retention), page refreshes live, UI follows dsh web language (zh/en).
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:yakoylp/dsh-localnotify
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
🌐 中文: README.md · 🤖 Agent Guide: README.agent.md
A local notification center plugin for DSH (DeepSeek Harness): adds a Notifications entry to the Web UI sidebar, opening a full-screen notification center. Notifications are shown as cards (title + one-line body preview) with level color coding, time/source filtering, sorting, title/body search, read/unread marking, and deletion. Clicking a card opens a detail popup with the full information and one-click copy of the title/body. Notifications are written by the agent tool (notify_add) or the CLI (dsh-localnotify) into a local JSON file, and the page refreshes in real time — fully local, no external services.
Typical use: the agent writes a notification to remind you when a task finishes; scripts and cron jobs can deliver notifications through the CLI.
Features
- Sidebar Notifications entry: bell icon + unread-count badge (dot badge in collapsed rail), beside Settings at the sidebar foot — additive, does not cover existing UI
- Full-screen notification center (
shell.overlay, independent of any session):- Card list: title + one-line body preview (ellipsis when truncated), unread cards highlighted with a level-colored left border and dot; compact card height
- Level color coding:
info(brand) /success(green) /warn(yellow) /error(red); level tags shown on cards and in the detail popup - Pagination: bottom bar with "per page 10/20/50/100" (default 20) and prev/next paging (shows
N total · page x/y); only the current page is rendered - Time filter: All / Today / Last 7 days / Last 30 days
- Source filter: filter by notification source (agent / cron / cli …, collected automatically)
- Sorting: newest first (default) / oldest first / unread first
- Title + content search: real-time filtering (case-insensitive)
- Detail popup: clicking a card shows full info (full timestamp / level / source / read state + full body) with one-click copy of title, body, or both (the button shows "已复制 ✓" after copying); opening a detail auto-marks it read
- "Mark all read" button; card "Delete" with two-step confirmation (prevents accidental deletion)
- Close via the close button or clicking the backdrop
- Real-time refresh: polls every 3s while the page is open, every 30s otherwise (agent/CLI writes appear within seconds)
- Theme aware: all colors use DSH official theme tokens (
--dsw-alias-*), following light/dark themes automatically - i18n: UI text follows the dsh web language automatically (zh / en; switches take effect live)
- Auto cleanup: optional retention (in days); older notifications are removed automatically on each write (disabled by default; the 500-cap always applies)
- Write paths (no create-form in the UI, keeping the interface clean):
- Agent tool: the agent calls
notify_addduring a conversation (supportslevel) - CLI:
dsh-localnotify add "title" -b "body" [--level success], for scripts / cron
- Agent tool: the agent calls
Data storage
Notifications live in ~/.dsh/notify/notifications.json (DSH_HOME takes precedence), shaped as:
{
"version": 2,
"notifications": [
{
"id": "n_mtisij3d_q32zcn",
"title": "Task done",
"body": "23 pages converted to Markdown.",
"createdAt": 1788274582825,
"read": false,
"source": "agent",
"level": "success"
}
],
"settings": { "retainDays": 0 }
}
level:info | success | warn | error(missing on old data renders asinfo)settings.retainDays: auto-cleanup retention in days;0= keep forever (default);>0removes notifications older than N days on each write (set via CLIconfig --retain-days). version-1 files are upgraded to version 2 on the first write- Writes are atomic (temp file + rename), so a partial file can never appear
- A missing or corrupt file falls back to an empty store and is never overwritten
- In-process writes are serialized; at most 500 notifications are kept (oldest trimmed)
- The file is transparent and editable, easy to back up or migrate with
~/.dsh
Install
From GitHub (recommended)
dsh plugin --profile web add github:yakoylp/dsh-localnotify
From a local directory (development)
Clone the repository (or unpack the source archive) anywhere, then install from that directory:
git clone https://github.com/yakoylp/dsh-localnotify.git
dsh plugin --profile web add ./dsh-localnotify # macOS / Linux
dsh plugin --profile web add "C:\path\to\dsh-localnotify" # Windows (use your real path)
Restart dsh web after installing (the client bundle is loaded at web startup). The 🔔 Notifications entry appears at the sidebar foot; verify with dsh-localnotify add "test".
CLI
dsh-localnotify add "Task done" -b "converted" # add a notification
dsh-localnotify add "Reminder" --source cron --level warn # custom source & level
dsh-localnotify add "Deploy OK" --level success # level: info/success/warn/error
dsh-localnotify list # list (* = unread; [E]/[W]/[OK] level marks)
dsh-localnotify list --unread # unread only
dsh-localnotify list --level error # error-level only
dsh-localnotify list --json # JSON output (script-friendly)
dsh-localnotify read n_xxxx # mark one as read
dsh-localnotify read --all # mark all as read
dsh-localnotify delete n_xxxx # delete one
dsh-localnotify config [--retain-days 7] # show/set auto-cleanup days (0=keep forever)
dsh-localnotify prune # clean up expired notifications now
dsh-localnotify --help # help
dsh-localnotify --file /path/to/notifications.json add ... # override storage path
The CLI reads/writes the same JSON file directly and does not require cordis — usable in any terminal, script, or cron job.
Agent tool
The plugin registers notify_add; the agent calls it in-conversation to write notifications (validated: title required ≤200 chars, body ≤5000 chars). Example when a task completes:
notify_add(title: "Conversion done", body: "23 scanned pages converted to Markdown ...", level: "success")
level is an optional enum info | success | warn | error for visual coding: success for completed tasks, error for failures, warn for things needing attention, omit for ordinary messages (default info).
Notification submission paths (how agents / scripts deliver)
| Path | Who uses it | Prerequisite |
|---|---|---|
notify_add tool |
DSH agents (same profile) | Registered automatically on install; agents see the tool in their tool list and call it per its description (task done, important progress, needs attention) — no extra config |
dsh-localnotify CLI |
scripts / cron / terminal | Installed (bin linked into the profile's node_modules/.bin); or node <plugin-path>/lib/cli.js directly |
| Direct JSON file write | any program | Write ~/.dsh/notify/notifications.json per the format above; visible within ~3s |
Note: the
notify_addtool is visible to agents in the same DSH profile where the plugin is installed; other environments/agents need their own install for the tool path, while the CLI and file write are environment-independent.
HTTP API
The host registers same-origin POST /dsh-localnotify/api/<method> (reachable only by the DSH web server); JSON in/out with an { ok, value } or { ok: false, error: { code, message } } envelope:
| Method | Args | Description |
|---|---|---|
list |
{} |
Returns { notifications, storagePath } |
markRead |
{ id } |
Mark one as read |
markAllRead |
{} |
Mark all as read |
delete |
{ id } |
Delete one |
Architecture
- Node half (mounted via
cordis.patch.yml,lib/index.js): registers thenotify_addtool (ctx.tools.register) and the/dsh-localnotify/api/*route (ctx.webServer.register, prefix match); storage logic lives inlib/store.js(shared by host and CLI) - Browser half (
dsh.client+exports["./client"]→lib/client.js): a hand-written ModuleLoader bundle (window.__ModuleLoader__.load({ id, factory }), same format as official tsdown output, depending only on the baseline externalreact); registerssidebar.footer.action(entry) andshell.overlay(center) viactx.slots.inject/register, fetching data from the host API - Theme: all colors reference DSH theme tokens (
--dsw-alias-bg-*/--dsw-alias-label-*/--dsw-alias-border-*/--dsw-alias-brand-primary, etc.)
Development
node --test # run all tests (store layer + API handler + CLI end-to-end)
node lib/cli.js --file ./tmp-notify.json add "test" -b "body" --level success
node lib/cli.js --file ./tmp-notify.json list
node lib/cli.js --file ./tmp-notify.json config --retain-days 7
Troubleshooting
- Sidebar entry visible but the notification center list stays empty: first check whether the host route is registered —
curl -X POST http://127.0.0.1:<port>/dsh-localnotify/api/list -H "Content-Type: application/json" -d "{}". If it returns 405 (empty body), the host route is not registered; upgrade to ≥1.1.0 (1.0.0 silently skipped route registration becausewebServerwas missing frominject). If it returns{ ok:true, value: { notifications, ... } }, the route works — check the storage file for data - Cards compressed to thin lines with many notifications / no scrollbar: flex-layout bug in 1.0.0; upgrade to ≥1.1.0 (cards are
flex: none, the list scrolls properly)
Known limitations
- The notification center is a full-screen overlay (not a routed page); closing returns to the previous session — a deliberate trade-off to stay lightweight
- Real-time updates rely on polling (3s/30s); no Host→Client push yet. Polling cost is negligible (small same-origin requests); will be revisited if DSH provides a push channel
- Concurrent writers (CLI + host) rely on atomic rename; the last writer wins under extreme concurrency. Write frequency is low for notifications, so this is safe enough (in-process writes are already serialized)
- External file edits are picked up on read (visible within ~3s)
Tests & CI
- Test coverage spans three layers: the store (add/validation/level/auto-cleanup/legacy-file compat/corrupt fallback/500 cap), the HTTP API (method dispatch/error envelope/read-write flows), and the CLI (spawn end-to-end: add/list/read/delete/config/prune/error paths)
- GitHub Actions runs the full suite on Node 18 / 20 / 22 (
.github/workflows/test.yml)
License
MIT
Links
More in this category
xmanrui/dsh-im★ 1283
Connect IM bots to DeepSeek Harness via QR codes or bot credentials (9 channels: Feishu, WeChat, DingTalk, WeCom, QQ, Slack, Telegram, Discord, and WhatsApp).
alvinunreal/openpets#dsh★ 1186
Bridges DeepSeek Harness lifecycle status, errors, and approval requests to a locally running OpenPets desktop companion.
shaobeichen/dsh-pocket★ 1096
Remote phone access to the DSH Web UI: scan a QR code for LAN or public (cloudflared tunnel) access with real-time sync, a mobile-adaptive layout, and a settings tab.
inclusionAI/Avernet#deepseek-harness-channel-bcn★ 551
Connects DeepSeek Harness to Avernet's Bot Collaboration Network over WebSocket V2, with automatic onboarding, isolated agent sessions, tool-call events, and multi-bot routing tools.
THEWOLFWALKER/dsh-notifier★ 98
Multi-channel notifications and phone control for DSH: one `notify()` API across 27 channels, event-driven pushes, phone approvals and questions, phone task takeover (`/tasks` `/use`), images into the session, six inbound control channels, a loopback web console, bilingual messages (zh/en via `lang`), multi-agent routing, and zero runtime dependencies.
omdsh-dev/dsh-notification★ 83
Desktop notifications for turn completions, with per-outcome controls and keyword rules.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.