dsh
OpenPetsHQ/openpets将 DeepSeek Harness 的生命周期状态、错误与审批请求,桥接到本地运行的 OpenPets 桌面伙伴。
DeepSeek Harness 的 macOS 系统通知插件:任务中止、超限、阻塞、中断、出错、等待提问或批准、完成或子代理结束等事件通过 terminal-notifier 弹系统横幅;点击横幅打开指定应用,回到页面自动清空。
macOS system notifications for DeepSeek Harness — a banner exactly when you need it.
English · 简体中文
· macOS · DeepSeek Harness
Posts native macOS Notification Center banners at the moments that genuinely need the human: abnormal termination, action needed (questions / approvals) and task complete. Click a banner to open your app; notifications auto-dismiss when you return to the DSH page — never nagging.
| 🗂️ Three notification groups | "Abnormal termination / Action needed / Completed" — each group with its own toggle, sound and title/message templates |
| 🖱️ Click-to-open | Clicking a banner runs open -a '<AppName>' (or activates a bundle id / a custom shell command); auto-scans installed DeepSeek Harness desktop apps and browsers |
| ✨ Auto-dismiss on return | Switching back to the DSH page (browser tab or standalone PWA window) removes all of this plugin's notifications — visibilitychange + focus, pure event-driven, zero polling, zero standing overhead |
| 🛡️ No-spam trio | 3-second coalescing for identical notifications, un-clicked dedupe (marker file with expiry), configurable repeat policy |
| 🚀 Zero manual install | Ships its own macOS notification binary (arm64 out of the box; Intel falls back to the x86_64 bundled with node-notifier) — nothing to install |
| ⚙️ Visual settings panel | Settings dialog →「通知管理」: toggles / sounds / templates take effect instantly, with one-click Test |
| 🧩 Host-side event driven | Listens to host session/event and subagent/end — banners appear even when the tab is backgrounded |
Platform: macOS only — notification posting, click behavior and app scanning rely on macOS-specific mechanisms (
terminal-notifier, theopencommand,/Applications).
| Group | Default | Sound | Covers |
|---|---|---|---|
| Abnormal termination | ✅ on | Sosumi | Context over limit, task blocked, task aborted, task interrupted, answer error |
| Action needed | ✅ on | Ping | Awaiting your answer (question / continue), awaiting your approval (authorization) |
| Completed | ✅ on | Glass | Answer complete, subagent finished |
💡 A single tool-call failure is not notified — it is an in-process failure and the agent usually retries or continues within the same turn, so it does not mean the task abnormally terminated; only a turn ending abnormally is alerted.
Default templates (all customizable):
| Group | Title | Message |
|---|---|---|
| Abnormal termination | DSH · 任务异常终止 | 任务未能正常完成:{reason} |
| Action needed | DSH · 需要你操作 | 对话已暂停,等待你的确认或授权 |
| Completed | DSH · 任务完成 | 任务已顺利完成 |
Template variables: {reason} (specific reason — Abnormal termination), {tool} (tool awaiting approval — Action needed).
After install, open the DSH settings dialog (⚙️ at the sidebar foot); the left nav gains a 「通知管理」 page:
~/.dsh/dsh-niao-message.config.json) and 测试通知 (Test) (posts a test banner immediately).Install the package in a DSH profile (e.g. web):
# Option 1 (official CLI — maintains the bundles list automatically)
dsh plugin --profile web add dsh-niao-message
# Option 2 (manual pnpm)
cd ~/.dsh/profiles/web
pnpm add dsh-niao-message
For the manual way, add the package to the profile's dsh.profile.bundles and restart dsh web:
{
"dependencies": { "dsh-niao-message": "^0.1.0" },
"dsh": { "profile": { "bundles": [ "...", "dsh-niao-message" ] } }
}
The package declares
dsh.bundle(a bundle package), sodsh plugin addappends the bundles entry automatically; dependencies (@deepseek-ai/schemastery,node-notifier) are resolved at install time.
Priority: Config schema defaults (validated and filled by Cordis) < profile patch cordis.patch.yml config < settings-panel file ~/.dsh/dsh-niao-message.config.json (highest).
Patch-layer override example (config hot-reloads):
- insert:
- id: dsh-niao-message
name: 'dsh-niao-message'
config:
click:
open: DeepSeek Harness
groups:
abnormal:
enabled: true
sound: Basso
title: 'DSH · 工具出错'
message: '任务异常:{reason}'
| Key | Meaning | Default |
|---|---|---|
enabled | master switch | true |
tool | terminal-notifier binary path (auto: plugin-bundled → node-notifier bundled → system paths) | auto |
click.open | app name for open -a on click | empty (dismiss only) |
click.activate | bundle id to activate on click | — |
click (string) | custom click shell command | — |
allowRepeat | keep posting while an un-clicked notification exists | false |
throttleMs | same-kind coalescing window (ms) | 3000 |
approvalGraceMs | grace window before an unresolved approval notifies (ms) | 1000 |
pendingFile | dedupe marker file | ~/.dsh/dsh-niao-message-pending.json |
configFile | settings-panel persistence file | ~/.dsh/dsh-niao-message.config.json |
pendingMaxAgeMs | marker expiry (ms) | 300000 |
groups.<key>.enabled / sound / title / message | per-group config (key ∈ abnormal / waiting / success) | see table above |
skipped (un-clicked notification pending) in logs).npm install # install deps (@deepseek-ai/schemastery + node-notifier + esbuild)
npm run build # build browser bundle (src/client.js → lib/client.js)
npm run check # syntax-check host / client / build script / smoke
npm run smoke # smoke test (fake ctx + fake webServer + capture script, 38 checks, no real banners)
Zero manual install. Everything is shipped with the plugin:
lib/vendor/terminal-notifier.app (arm64, bundled — Apple Silicon works out of the box).node-notifier (installed automatically): bundles an x86_64 terminal-notifier for Intel Macs.open command: click-to-open.tool in the config.MIT (bundled component licenses in THIRD_PARTY_NOTICES.md)