它做什么
dsh-notification 是「跑完了告诉我一声」这件事最小的答案。当某个会话在 DeepSeek Harness 的 Web GUI 里跑完一轮,浏览器会通过标准的 Notification API 弹出一条系统通知。你切到别的标签页、别的应用,完成消息依然能找到你。
它刻意不做桥接:没有 bot 要注册,没有 token 要粘贴,没有 webhook 要暴露,也没有第二个运行时——README 的安装故事就一条命令加一次重启。插件分成两半,各自走官方接缝:
- 宿主侧对每个会话「最后一次完成的轮次」折叠出一个有界投影——结束原因、回复正文、工具名——再通过 session-projection 接缝投递出去;
- 客户端侧监听会话列表的完成提醒,套用它自己持久化的偏好,然后弹出通知。
全程不往会话日志里写任何东西,也不注册任何面向模型的工具。这是设计取向,不是缺陷:通知观察的是运行时本来就已经算出来的状态。
模型这一侧看到的是:什么都没有
README 公开了一张「Model experience」表,这在同类里不常见,而且它值得被当作这个插件的核心主张来读:
| 方面 | 效果 |
|---|---|
| Token 开销 | 无——通知只属于 UI,从不进入请求 |
| 工具调用 | 无——模型没有拿到任何新工具 |
| 会话日志 | 不变——投影读取既有日志,不新增事件 |
| 提示词 | 不变——没有注册任何系统提示词段落 |
对于一个「负责打断你」的插件来说,这张表才是重点。一个注册了工具的提醒插件,会在每一轮都把「要不要通知」这个决定推给模型;一个往日志里追加事件的提醒插件,会改变你事后读到的对话记录。它两件事都不做,所以它和任何别的插件都能共存,且不改变 agent 的行为。
真正决定「弹不弹」的四个闸门
从「一轮跑完」到「通知出现」之间有四道闸门,而每一个默认值都是安静的那一侧:
- 浏览器权限。 在设置区授予,那里也会顺手发一条测试通知。站点权限一旦被拒,页面内部无法翻案。
- 结束原因。
completed与error默认开;aborted、blocked、token limit默认关,要你自己打开。 - 关键词规则。 可选的包含/排除过滤器,匹配对象是会话标题、该轮回复正文与工具名——支持字面量或正则,可选用大小写敏感开关。包含规则至少要命中一条;排除规则命中即压制。
- 可见性。 「只在任务不在视野内时才通知」默认开启,而且 README 对它说得非常精确:页面隐藏时、或你正开着另一个会话/工作区时,完成照样通知;它只压制你此刻正在看的那个会话。
偏好存在浏览器的 localStorage 里,不在 profile 里——所以它是「按浏览器」的,不会跟着你的 cordis.yml 走。
npm 上那个名字属于另一个项目
装之前请先读这一段。npm 上的 dsh-notification 这个名字归另一位作者——nishit130/dsh-notification,最新 0.1.1,首次发布于 2026-08-21,是一个用途相近的独立项目。本条收录的 omdsh-dev 项目版本是 0.1.4,并没有以该名字发布到 npm,这正是它的 README 写的是「发布 tarball 安装」而不是 dsh plugin add dsh-notification 的原因。
所以不要把命令写短。请按原样安装 tarball 地址,否则你装到的是另一个作者的另一款插件。本站这一侧有同样的约束:pnpm check:packages 会把每个 package: 名字与 npm 上真正拥有它的仓库做交叉核对——这也是本条只声明 install:、完全不声明 package: 的原因。
要求与安装
-
从发布 tarball 安装——这是作者唯一发布的渠道:
dsh plugin --profile web add https://github.com/omdsh-dev/dsh-notification/archive/refs/tags/v0.1.4.tar.gz -
装完要重启 web 服务。 两半都得加载:宿主侧提供投影,客户端侧由服务出去的 bundle 承载。默认的
dsh webprofile 已经具备它需要的组合——会话列表、设置外壳、locale。 -
插件加载后,设置区出现在 Settings > Notifications。
-
Node。 这个包完全没有声明
engines,而且它的十个 harness peer 依赖加上react@^18.2.0全部标记为 optional。这是一个真实信号,也正是本条记为 L2 而不是 L3 的原因——见下一节。
为什么本条是 L2,不是 L3
README 说 0.1.4 版「同时支持 0.1.1 与 0.1.2 两套 Harness 客户端包布局与投影契约」。请精确地读这句话:它是在说插件能容忍两种客户端包的形状,而不是在声明一个 harness 版本范围。清单也印证了这一点——没有 dsh.compatibility 块,每个 peer 依赖都是 *。
所以本条不记录 dshTarget。在本站别处,「声明了一个范围」才是拿到 L3 的条件;这里作者声明的是兼容性容忍度而不是范围,而把「0.1.1 与 0.1.2 两种布局」改写成 0.1.1 || 0.1.2,等于替作者断言了一个他从没声明过的版本范围。将来某个版本真的声明了范围,本条应当重新核验并升级。
配置
宿主侧只有一个可调项,位于 cordis.yml 的插件行上:
| 键 | 默认值 | 含义 |
|---|---|---|
maxBodyChars |
400 |
投影正文预算;更长的回复在宿主侧就被省略号截断 |
其余都是客户端设置,位于 Settings > Notifications:
| 设置 | 默认值 | 效果 |
|---|---|---|
| Enable notifications | 开 | 总开关;关掉后所有通知停止,但规则保留 |
| Notify on completed / error / aborted / blocked / token limit | completed + error 开,其余关 | 哪些结束原因会通知 |
| Keyword rules | 无 | 针对会话标题、该轮回复正文与工具名的包含/排除过滤 |
| Require manual dismiss | 关 | 通知会一直留着,直到你手动关掉 |
| Only notify when the task is out of view | 开 | 只压制你此刻正在看的那个会话 |
已知限制
- 页面必须开着。 标签页隐藏时浏览器仍会弹通知,但关掉之后不会。还需要授予通知权限,而被拒之后页面内部无法翻案。
- 不会补发。 页面断连期间发生的完成,重连后不会补通知;一次完成的轮次也只通知一次。
- 规则只看最后一轮。 匹配对象是会话标题加上最后一轮的回复正文与工具名,更早的轮次不参与——所以三轮前出现过的关键词不会触发任何东西。
- 通知正文是一段扁平文本,点击它只会把窗口切到前台——没有跳到该轮次的深链。
- 不要按名字安装
dsh-notification。 npm 上这个名字属于另一个仓库,见上文;请用 tarball 地址。 - 这个命名空间极其拥挤,而本条是唯二超过 50 star 的条目之一。 在夜间索引里,有 144 个仓库的名字里带 “notif”;其中 119 个停在 0–2 star,25 个达到 3 以上,而超过 50 的恰好只有两个——本条与
dsh-notifier。请确认你装的是你想装的那一个。 - 83 个 star 是关注度,不是审查。 本条记为
security: unverified;没有第三方扫描或独立审计在案,本站也没有任何条目声称 L4 或 L5。
何时用 / 何时不用
当跑 harness 的那台机器就是你正看着的这台机器,而你想要的是最便宜的那种打断时,就用它:没有要配的东西,没有会泄的凭证,模型上下文里什么都没加,对话记录也不变。对单机笔记本的 profile 来说,它是解决这个问题的最小方案——这也是为什么它更适合当这个中心的入口,而不是那些更重的选项的替代品。
当你人不在浏览器前面时,就该换形态了。dsh-notifier 推到 27 个渠道(含手机推送服务),并把审批、提问和一个停止按钮带回手机上——一旦你离开工位,它才是对的工具,代价是凭证和远为庞大的表面。dsh-im 走得更远,它把聊天窗口本身当成界面,让你可以和会话对话而不只是应答;那是更大的承诺,它自己的条目也写明了:一旦接入支持群聊的渠道,那张访问控制页就是必配项。
FAQ
它需要改动 harness 吗?
不需要。宿主侧基于它本来就保留的日志折叠出一个会话投影,客户端侧搭的是会话列表已有的完成提醒。README 说明投影读取既有日志、不新增事件。
关掉标签页之后还会通知我吗?
不会。页面隐藏时浏览器仍能弹通知,但关闭之后不能。如果你的需求是「浏览器关着也要被通知」,那要的是推送类插件,不是它。
模型能主动发通知吗?
不能。它没有注册工具、没有加提示词段落,所以模型既没法触发通知,也不会被问「要不要通知」。通知纯粹是客户端对「轮次结束」状态的反应。
默认哪些完成会通知?
completed 与 error。aborted、blocked、token limit 是可选项但默认关闭——这个默认值对「哪些真的值得打断你」的判断是合理的。
为什么安装用的是 tarball 地址而不是包名?
因为 npm 上的名字被另一个项目占着。见上文「npm 上那个名字属于另一个项目」;按名字安装会装到别人的插件。