dsh-native-notifications:让 DSH 的审批、提问与任务完成在系统层弹通知

前言

DSH 跑一个长任务时,人常离开窗口去别处。审批来了要确认权限,提问来了要选答案,任务跑完要看结果——这几类事件如果只在页面里体现,人不在就错过了。已有做法是靠辅助进程发通知,系统会把横幅归属到那个脚本运行器上,点回去也落不到 DSH 会话。

下面介绍的 tommy00748-tech/dsh-native-notifications 换一种方式:通知由 DSH 页面自己发出来,操作系统把它归属到 DeepSeek Harness,点一下横幅回到会话,并且可以选或自己写铃声。

这是什么

DSH 插件,目录页归在「客户端」分类,维护者 tommy00748-tech。解决的问题是:让 DSH 在需要人介入或跑完一轮时,弹一条归属正确、可点击回到会话的桌面通知,避免人一直盯着窗口或错过审批。

仓库显示 0 星,许可证 MIT,npm 包名 dsh-native-notifications。

触发哪些事件

插件监听以下 Harness 事件,事件含义和默认开关见 README 的表格:

Harness event 横幅内容 默认
agent/status → idle after a turn(root session) 任务完成 · 用时 … on
approval/request 在 approvalDelayMs 后仍挂着 需要你确认权限 · tool · reason on
user-questions/request 仍挂着 正在等待你的选择 · question on
agent/error 执行出错 · message on
deepseek-account/model-sign-in-required / session-expired 需要登录 / 登录已过期 on
subagent/end、workflow/end child agent / workflow finished off

横幅标题用会话名称,正文示例:

<会话名称>
任务完成 · 用时 5 分 43 秒,可以回来查看结果了。

几个防噪逻辑:

  • 被自动处理掉的审批(例如 Auto 权限预设里的 LLM reviewer)不会响,横幅会延迟,只在请求还在等你时触发。
  • 出错之后那一轮完成的通知被抑制,一次失败不会响两次。
  • 目标轮次和后续 turn 很快回到 running,完成通知要在会话空闲超过 idleGraceMs 后才触发。
  • 队友、子代理和 workflow 子进程按 onlyRootSessions 过滤,多智能体长跑只在 root session 真结束时响一次。

铃声配置

铃声由插件用 Web Audio 合成,没有音频文件,也不用系统默认音,系统音会被抑制,不会响两次。所有预设共用同一套动机,含义可辨:完成是上行的 do-mi-so,”需要你”是两声高频短音,出错下行。

打开 Settings → General → Notification sound,可选预设:

preset 音色
Playful bubbles(默认) triangle 主体,带小幅上滑和一个高音闪烁
Soft bell sine 主体,加一个轻的八度泛音
Bright pluck 短 triangle 拨弦,没有滑音
Custom 自己的音符序列,用 playful 音色播放
Muted 无声

这一行有 Preview 按钮。Custom 用文本框输入,最多八个以空格或逗号分隔的音符,可以是音名(C6 E6 G#6、Bb5)也可以是频率(1046.5 1318.5):

C6 E6 G6          ← do-mi-so
G6 G6             ← double boop
1046.5 1318.5     ← 同上两个音符的频率写法

偏好存页面 localStorage 的 dsh-native-notifications:sound,所以是按设备/浏览器生效的,下一次横幅即生效,不需要走 Host 往返。

安装与启用

安装命令来自仓库 README,按 profile 区分 Web 和桌面:

# from GitHub
dsh plugin --profile web add github:tommy00748-tech/dsh-native-notifications
dsh plugin --profile desktop add github:tommy00748-tech/dsh-native-notifications

# from npm(npm 发布后可用)
dsh plugin --profile web add dsh-native-notifications

# from a local checkout
dsh plugin --profile desktop add link:/absolute/path/to/dsh-native-notifications

装完在 Settings → Plugins 确认 desktop-notifications 那一行是启用的,bundle patch 会插入并默认启用它。没有构建步骤、没有依赖、没有原生模块:Host 半部分是普通 ESM,Client 半部分是一个纯 JS bundle。

要求:

  • 有提供 Web UI 的 DSH profile(desktop 或 web);
  • 页面在安装时自动加载 Client 半部分(Host graph 重建会同步给已连接的页面);如果手工拷贝后横幅一直没出现,重载一次窗口;
  • 操作系统里要为 DeepSeek Harness 允许桌面通知(macOS 在 System Settings → Notifications)。Electron 会授予页面权限,但 OS 层面的允许是独立的。

配置项

所有开关都在那一行的 config 上,默认值连同说明写在 cordis.patch.yml 里。注意 patch 会整体替换 config,所以只想改一个字段时要把想保留的字段一并写回去:

- id: desktop-notifications
  config:
    notifyComplete: true
    notifyQuestion: false      # do not ring for questions
    idleGraceMs: 2000
    onlyRootSessions: true
    delivery: client           # client | osascript | both

可用字段:notifyComplete、notifyApproval、notifyQuestion、notifyError、notifyAccount、onlyWhenUnfocused、silent、idleGraceMs、errorSuppressMs、approvalDelayMs、onlyRootSessions、notifyOnMount、logPath。

delivery: osascript 是给页面无法展示 Web 通知的宿主的遗留回退,macOS 下这类横幅归属到 Script Editor,默认关闭。

诊断日志

每次横幅和每次客户端上报都会追加到审计日志,默认 ~/.dsh/dsh-native-notifications.log,可用 logPath 改路径。日志样例:

2026-11-05T08:58:26.102Z mounted :: pid=9027 :: delivery=client :: epoch=... :: platform=darwin
2026-11-05T08:58:26.178Z client :: boot :: permission=granted protocol=dsh-app: path=/ visibility=hidden sound=cute chime=v5-robust
2026-11-05T08:59:47.177Z banner :: 能不能做个DSH原生的通知插件 :: 任务完成 · 用时 5 分 43 秒,可以回来查看结果了。
2026-11-05T08:59:47.186Z client :: shown :: kind=complete permission=granted
2026-11-05T08:59:47.188Z client :: sound :: kind=complete preset=cute

针对”为什么没弹横幅”,按日志判断:

  • 没有 client :: boot → Client 半部分没加载,重载窗口;
  • client :: permission :: denied → 系统/浏览器不允许通知;
  • client :: suppressed → onlyWhenUnfocused 跳过了已聚焦的窗口;
  • 有 banner :: 但没有 client :: shown → 页面没轮询到(看 poll-failed / unauthorized);
  • 有 client :: shown 但没有 client :: sound → 铃声没响(音频仍被锁,或静音了)。

想直接发一条通知验证 osascript 通道:

node scripts/notify-test.mjs "title" "body"

工作原理

一个 bundle,两半:

  • Host(index.js)监听上面那些事件,维护一个小的环状缓冲区存横幅事件。它在 ctx.webServer 上提供两个同源路由:/native-notifications/events(长轮询)和 /native-notifications/report(客户端诊断),和 Host 其他 Web 路由一样用 ctx.connection.requestRejection 做门禁,所以只有已认证页面能读。
  • Client(client.js,无渲染、始终挂载)长轮询这两个路由,通过平台通知 API(new Notification(...))展示每个事件,点击时聚焦 DSH 并调用 uiWorkspace.openSession(sessionId)。游标和 Host epoch 存在 localStorage,所以重载不会重放旧横幅,Host 重启从”现在”开始。

长轮询在 wait + 10s 后中断并重试,所以 Host 重挂不会卡住客户端。插件不追加 session 事件、不 import Harness Client 包,只贡献一个 bundle 行和一个 Settings 行。

兼容性

README 标明在 dsh 0.2.1-rc.5 和 0.2.2-rc.1 上测试过。运行时兼容性检查读的是顶层 engines.dsh 字段,package.json 里是 >=0.2.1-rc.5 <0.3.0,Node 要求 >=20。

适用场景与注意

适合需要在 DSH 上跑长任务、又不想一直守着窗口的人,以及团队里跑多智能体长任务、希望只在 root session 结束时响一次的场景。如果你的宿主页面无法展示 Web 通知,delivery: osascript 是备选,但要接受横幅归属到 Script Editor。

两点提醒:通知以当前 DSH 进程的权限发出,OS 层面要为 DeepSeek Harness 打开通知授权;插件来源是社区维护的 GitHub 仓库(社区目录站上也有它的条目,该目录与 DeepSeek / 幻方无官方从属关系),装之前建议先过一遍源码和 MIT 许可证。

结尾

一句话回顾:它把 DSH 的审批、提问、完成、出错和登录横幅归到 DeepSeek Harness 名下,可选或自定义合成铃声,并提供审计日志方便排查没响的原因。目录页在 https://www.skillhub.cn/plugins/tommy00748-tech/dsh-native-notifications,仓库在 https://github.com/tommy00748-tech/dsh-native-notifications。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜