# dsh-notify [English](../README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) **dsh(DeepSeek Harness)插件:当 dsh 需要你注意时,在桌面发出消息提醒。** dsh 本身在“需要用户介入”时没有任何提示:权限确认弹起、`ask_user_question` 提问、计划审批、 回合完成、目标完成/受阻、出错、工作流结束时,如果你正盯着别的窗口或浏览器标签页在后台, 很容易错过。这个插件监听 dsh 主机事件总线,在这些时刻立刻弹出一条原生桌面通知—— 系统通知(macOS / Linux)、弹窗(Windows)、终端响铃,或你自己的自定义命令。 插件是**纯观察者**:所有监听器都是被动的。尤其是 `approval/request` 监听器只负责提醒, **永远不会替你做决定**,它总是调用 `next()` 转发请求,审批链路的行为与未安装本插件时完全一致。 ## 功能 | 触发器 | 触发时机 | 默认 | |---|---|---| | `approval` | 权限确认请求(沙箱升级、需要批准的操作等) | 开 | | `question` | `ask_user_question` 提问 / `exit_plan_mode` 计划待审批 | 开 | | `turnComplete` | 回合完成(agent 回复完毕,等待你的下一步输入) | 开 | | `goal` | 目标完成 / 目标受阻 | 开 | | `error` | 会话出错 | 开 | | `workflow` | `tool_workflow` 运行结束 | 开 | 通知渠道(`channel`): - `auto`(默认):macOS → `osascript` 系统通知;Windows → PowerShell 弹窗;其他平台 → `notify-send` - `osascript` / `notify-send` / `powershell`:强制指定渠道 - `bell`:终端响铃(`\x07`),零依赖兜底 - `custom`:执行 `customCommand` 模板(占位符 `{title}` `{body}` `{app}`) - `none`:关闭 零运行时依赖(仅两个 `@deepseek-ai` 包:settings 与 schemastery),可在任意 profile(`web` / `headless` / 自定义)使用。 所有选项也都可以在 Web 界面直接修改:**设置 → 通知** 标签页(见下文「设置页」)。 改动写入设置文件后立即生效,无需重启。 ## 安装 以 `web` profile 为例(其他 profile 同理): ```sh # 1. 把插件装进 profile 的依赖。 # 本地目录会被 pnpm 链接进 node_modules;git URL 或已发布的 npm 包名也可以。 dsh plugin --profile web add /path/to/dsh-notify # 或从本仓库安装: dsh plugin --profile web add git+https://github.com/knownothing114/dsh-notify.git # 或(发布到 npm 后):dsh plugin --profile web add dsh-notify ``` 该命令会安装依赖**并自动注册 bundle**。请确认 profile 清单的 `dsh.profile.bundles` 中已包含 `dsh-notify`;若你的 dsh 版本没有自动追加,请手动添加: ```jsonc // ~/.dsh/profiles/web/package.json { "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-notify" // ← 追加这一行 ] } } } ``` ```sh # 2. 重启 dsh web(插件在启动时装载;HMR 只热载配置,不加载新插件包) ``` ## 配置 插件默认配置已内置,通常无需改动。如需调整,在 profile 的 `cordis.patch.yml` (或 `--patch` 覆盖层)中按行 id `dsh-notify` 覆盖。覆盖会**整体替换**该行的 `config`, 插件会在代码内与默认值做深合并,所以可以只写要改的键: ```yaml # ~/.dsh/profiles/web/cordis.patch.yml - id: dsh-notify config: channel: auto # auto | osascript | notify-send | powershell | bell | custom | none appName: dsh # 通知里显示的来源名称(custom 模板的 {app}) sound: true # macOS 通知是否带提示音 minIntervalMs: 3000 # 两次通知的最小间隔(防轰炸) rootsOnly: true # 只提醒根会话(忽略子代理/后台子任务) verbose: false # 同时在 dsh 日志里打印每条通知 customCommand: "" # channel 为 custom 时的命令模板,如: # terminal-notifier -message {body} -title {title} triggers: approval: true question: true turnComplete: true goal: true error: true workflow: true enabled: true # 总开关 ``` ## 设置页(Web「通知」标签页) 使用 `web` profile 时,插件会在 **设置** 中注册一个独立的 **「通知」** 标签页。它展示上面 配置小节里的全部选项,并可以通过表单修改(保存 / 放弃 / 整体「恢复默认」): - 总开关、通知渠道、来源名称、提示音、最小通知间隔、仅根会话、记录日志、自定义命令 - 六个触发器开关 配置优先级(高者生效):**设置文件**(`$DSH_HOME/settings.yaml`,由标签页写入、热生效) → **profile 补丁**(`cordis.patch.yml` 的 `config:`)→ 内置默认值。主机插件在每次通知时 实时读取配置,因此标签页里的改动立即生效。 浏览器端实现位于 `dist/client.js`(dsh 客户端模块格式的预构建 bundle,由宿主在 `/plugins/dsh-notify/client.js` 提供);主机端注册 `notify` 设置命名空间。 首次安装或升级插件包需要重启 `dsh web`;此后标签页内的改动无需重启。 说明:dsh 的 API 代理默认只向 Web 界面暴露**白名单内**的设置命名空间(模型提供方 + 显式 许可列表)。因此插件会包装代理的 settings 处理器,让 `notify` 命名空间可以通过标准 settings RPC 读写——其他命名空间仍保持核心白名单行为。 ## 工作原理(事件映射) | 触发器 | 监听的事件 | |---|---| | `approval` | 主机侧 `approval/request` 瀑布(被动监听,`return next()` 转发,不参与决策) | | `question` / 计划审批 | `session/event` 中的 `tool/call`(工具名 `ask_user_question` / `exit_plan_mode`),提问正文从工具参数中提取 | | `turnComplete` | `session/event` 中的 `turn/end`(`reason.kind === "completed"`) | | `goal` | `goal/changed`(操作 `complete` / `block`),正文含目标描述 | | `error` | `agent/error` | | `workflow` | `session/event` 中的 `tool-workflow/run-end`(`stopReason` 为 `completed` 之外的失败也会提醒) | 所有监听器注册在根上下文,按 dsh 的作用域路由规则可以收到全部 agent/会话事件; `rootsOnly` 通过 `ctx.agents.roots()` 过滤掉子代理的噪音。通知发送使用 `spawn` 分离进程, 永不阻塞 agent 循环,失败只记日志。 ## 卸载 ```sh dsh plugin --profile web remove dsh-notify # 并从 ~/.dsh/profiles/web/package.json 的 bundles 中移除 "dsh-notify",重启 ``` ## 常见问题 - **没有弹通知?** 先确认 `channel` 检测结果与系统匹配:macOS 上手动设 `channel: osascript` 试一条(也可设 `verbose: true` 看日志里是否打印了通知); Linux 需要 `notify-send`(`libnotify`);检查系统勿扰模式/通知权限。 - **提醒太频繁?** 调大 `minIntervalMs`,或按需关闭某个 `triggers` 项。 - **子代理刷屏?** 保持 `rootsOnly: true`。 - **改了配置没生效?** `cordis.patch.yml` 的改动由 HMR 热载;新增/移除插件、改 `package.json` 的 bundles 需要重启 `dsh web`。 ## 开发 ```sh cd dsh-notify npm install # 安装 @deepseek-ai/dsh-settings 与 @deepseek-ai/schemastery npm test # node --test:纯函数单元测试 + apply() 接线冒烟测试 + 客户端表单测试(30 个用例) ``` ## 项目结构 ``` ├── lib/index.mjs 主机插件(事件监听、设置命名空间、apiProxy 暴露包装) ├── dist/client.js 浏览器 bundle(设置 → 通知 标签页),由 /plugins/dsh-notify/client.js 提供 ├── cordis.patch.yml bundle 补丁,挂载插件行 ├── docs/ │ ├── README.zh-CN.md 简体中文文档 │ ├── README.ja.md 日本語ドキュメント │ └── third-party-settings-namespace-exposure.md 开发者笔记:第三方设置命名空间如何暴露给 Web 设置页 ├── test/ node --test 测试套件(单元 / 渲染 / 交互 / 集成) └── package.json 插件清单(exports、dsh.bundle / dsh.client 声明) ``` ## 许可证 [MIT](../LICENSE)