Files
session-notify/README.md
T

98 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 会话通知
DeepSeek Harness(DSH)插件:会话**需要你注意**时提醒你——窗口不在前台时用**系统通知**,窗口在前台时用**应用内轻弹窗**。
Windows / macOS / Linux 三个平台走的是同一条通知通道(渲染进程的 Web Notification API),插件里没有平台分支。
## 提醒什么
| 触发 | 通知标题 |
| --- | --- |
| 会话从「运行中」变为「空闲」(一轮回答结束) | 会话已完成 |
| 待处理的工具授权请求(`approval`) | 需要授权 |
| 待处理的提问 / 计划确认(`question` / `plan-review`) | 需要回答 |
每条只提醒一次:同一会话的同一轮只报一次完成,同一个待处理请求只报一次;请求消失后再来才会再报。
## 走哪个通道
| 状态 | 行为 |
| --- | --- |
| DSH 窗口**不在前台** | **系统通知**(这是插件唯一能拿到的系统通知通道,三个平台一致)。点通知:窗口回到前台并跳到该会话 |
| DSH 窗口在前台,且**不是**你正在看的那个会话 | **应用内轻弹窗**:右上角悬浮,点「查看」跳过去,6 秒自动消失,鼠标悬停时不消失,Esc 关掉最上面一条 |
| DSH 窗口在前台,且就是你**正在看的**那个会话 | 不打扰(你在看,不需要弹) |
## 设置
**设置 → 通用 → 会话通知**:
- 显示系统通知权限状态;
- 未授权时点「允许通知」请求权限(浏览器要求必须由你手动点击才能弹权限框),授权后自动发一条测试通知;
- 已授权时点「测试通知」随时验证;
- 权限被系统层关闭时,按平台给出开启路径(Windows / macOS 的通知设置、Linux 桌面环境的通知设置);
- 三个开关分别控制**完成 / 授权 / 提问**三类提醒(默认全开;开关状态存在当前页面内存里,刷新回到默认——本插件不写配置文件)。
## 安装
在 DSH 的 **插件 → 添加插件** 里填本目录的绝对路径即可(`plugin_manager` 也会做同样的安装):
```text
D:\DeepSeek Harness Plugins\dsh-session-notify
```
安装后:
- **浏览器半**随页面加载,刷新页面即生效;
- **宿主半**为空壳(不改任何 DSH 状态),如需重启才生效,完全退出 DSH 再打开即可;
- 本插件没有第三方依赖,不需要 pnpm 下载,也不需要构建步骤。
卸载:插件页里移除 `@dsh-plugin/session-notify`(它同时是 profile 的一个 bundle)。
## 平台说明与验证范围
- **通道是跨平台统一的**:插件的提醒都通过页面(Electron 渲染进程)的 `Notification` 构造器发出,Windows 通知中心 / macOS 通知中心 / Linux 通知守护(libnotify、GNOME、KDE)都由系统把它转成原生通知;应用内轻弹窗是纯 DOM,与平台无关。
- **宿主半拿不到系统通知**:DSH Desktop 的宿主进程是纯 Node 进程(不是 Electron 主进程),没有 Electron 的 `Notification` 可用,所以插件的所有提醒都在浏览器半产生——这也正是"三个平台一套代码"的原因。
- **已验证**:Windows 上的安装、三个 slot 条目注册、以及 22 条投递规则(见下)的离线验证;设置行与轻弹窗的文案/交互按 DSH 自带的 toast 与 switch 组件对齐。
- **未验证**:macOS 与 Linux 上的实际弹窗效果本机无法测试。结构上它们与 Windows 共用同一条通道、同一份代码,只有"权限被系统关闭时显示的开启路径"是分平台的。
- 提醒只在 DSH 进程运行、页面打开时产生:**DSH 完全退出期间结束的会话不会再补发通知**。
## 行为细节(都已验证)
- **只有观测到「运行中 → 空闲」的转变才提醒**:打开应用时已经在跑的会话只建立基线,不补发;历史里早已结束的会话不会被翻出来提醒。
- **子智能体会话不单独提醒**(`origin === 'subagent'`),归属它的主会话结束时才提醒。
- **新会话(空白会话)完成不提醒**。
- **授权/提问在会话仍在运行时被延后提醒**:先记下,等这一轮真正停下来再报,避免在模型还在跑的时候就打扰你。
- **节流**:1.5 秒内只投递一条,多个会话同时完成不会刷屏;完成提醒前有 400ms 的确认窗口,如果那一轮马上又跑起来就不报。
- **同一会话的完成提醒会覆盖上一条**(通知带 `tag`),不堆叠。
## 兼容性
基于 **DeepSeek Harness 0.2.0-rc.2** 编写,遵循 DSH 插件规范:`dsh.manifestVersion: 1`、`dsh.bundle.patch` 声明宿主行、`dsh.client` 声明 `platform: "web"` 的浏览器半。
依赖的都是稳定契约:
- 浏览器半只依赖 **slot 的标准 props**(`useSessions`、`useSessionStatus`)读取会话状态,这是 DSH 官方给插件读会话数据的方式;不轮询、不自己订阅会话事件、不读别的插件的 DOM;
- 只往两个官方 slot 注册:`settings.general.item`(设置行)与 `shell.overlay`(两个条目:轻弹窗层 + 无渲染的观察器,内容永远是 `null`);
- **不要**把"无渲染"条目放进 `sidebar.panellist`:该 slot 的宿主会把**每个条目的 id 当成一个左侧面板按钮**(`entriesOfSlot('sidebar.panellist')` → 面板列表,按钮文字取 `options.label ?? options.id`,条目本身作为图标内联渲染),放进去会在左侧多出一行空面板并挤坏侧边栏。本插件第一版踩过这个坑,现已改挂到通用浮层;
- **文案必须注册字典**:`ctx.locale.register('session-notify', { zh, en })`,否则 `locale.bind(ns)` 在查不到该命名空间时会把 key 原样返回——界面上就会显示成 `settings.title` 这样的原始键(本插件第二版踩过这个坑)。注册会在 locale 修订号上打点,已经渲染出来的条目会自动换上文案,不需要刷新;
- 另外自带一份本地字典兜底:即使拿不到框架的 `t`、或 locale 服务没有保存注册,也会渲染中文/英文文案而不是原始键;
- 样式只用主题 token,不 import 任何 `@deepseek-ai/dsh-client-*` 包(规范要求,也是渲染不被上游改动打断的前提);
- 宿主半不声明 `inject`、不注册服务、不注册路由。
上游若改动 slot 名或 hook 名:注册会静默跳过而不是把页面弄坏,插件会退化成"不提醒";这类情况请按上面的契约名核对。
## 验证方式(开发记录)
`client.js` 是纯 JavaScript、浏览器端运行,可以在 Node 里用桩模块加载器评估并驱动:
- 模块接线:加载后返回的插件、`inject` 声明、注册集合(含"观察器绝不在 `sidebar.*` 里"这一条);
- 国际化:字典已注册且中英键集合一致、设置行与弹窗渲染出的每个字符串都不是原始键、拿不到框架 `t` 时本地字典兜底、locale 服务不保存注册时仍然有文案;
- 渲染健壮性:各组件在空状态 / 权限被拒 / 无 Notification API 下渲染都不报错;
- 投递规则:完成提醒一次、当前会话不打扰、后台走系统通知、授权/提问各提醒一次、运行中延后、子会话跳过、空白会话跳过、历史不补发、开关生效、弹窗自动消失等。
合计 44 项断言。
## License
[MIT](LICENSE)