From 080d873f9a03412cba95d9ab11609ec4d6609050 Mon Sep 17 00:00:00 2001 From: pyh Date: Wed, 30 Sep 2026 17:43:27 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20docs:=20README=20=E5=8F=AA?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E5=8A=9F=E8=83=BD=E4=B8=8E=E7=94=A8=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=BC=80=E5=8F=91=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 127 +++++++++++++++++++++++------------------------------- 1 file changed, 54 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 2dd5d1c..e13c271 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,62 @@ # 会话通知 -DeepSeek Harness(DSH)插件:会话**需要你注意**时提醒你——窗口不在前台时用**系统通知**,窗口在前台时用**应用内轻弹窗**。 - -Windows / macOS / Linux 三个平台走的是同一条通知通道(渲染进程的 Web Notification API),插件里没有平台分支。 +DeepSeek Harness(DSH)插件:会话**需要你注意**时提醒你——窗口不在前台用**系统通知**,窗口在前台用**应用内轻弹窗**。 ## 提醒什么 -| 触发 | 通知标题 | +| 什么时候提醒 | 通知标题 | 内容里带什么 | +| --- | --- | --- | +| 会话从「运行中」变为「空闲」(一轮回答结束) | 会话已完成 | 会话题目 | +| 出现等待你处理的工具授权请求 | 需要授权 | 等待授权的工具名 | +| 出现等待你回答的提问,或等待你确认的计划 | 需要回答 | 提问提醒带问题原文,计划确认带会话题目 | + +三类提醒可以分别开关。 + +每条只提醒一次:同一会话的同一轮只报一次完成,同一个待处理请求只报一次;请求消失之后再次出现,会重新提醒。 + +## 提醒送到哪里 + +按 DSH 窗口**当时**的状态选通道,你不需要预先设置: + +| 窗口状态 | 怎么提醒 | | --- | --- | -| 会话从「运行中」变为「空闲」(一轮回答结束) | 会话已完成 | -| 待处理的工具授权请求(`approval`) | 需要授权 | -| 待处理的提问 / 计划确认(`question` / `plan-review`) | 需要回答 | +| 窗口**不在前台** | **系统通知**(Windows 通知中心 / macOS 通知中心 / Linux 通知守护)。点一下通知:窗口回到前台,并跳到那个会话 | +| 窗口在**前台**,但不是你正在看的那个会话 | **应用内轻弹窗**:右上角浮出一张卡片,标题是提醒类型,下面一行是会话题目与细节 | +| 窗口在**前台**,且正是你**在看的**那个会话 | 不打扰(你就在看着,不需要弹) | -每条只提醒一次:同一会话的同一轮只报一次完成,同一个待处理请求只报一次;请求消失后再来才会再报。 +轻弹窗的用法: -## 走哪个通道 - -| 状态 | 行为 | -| --- | --- | -| DSH 窗口**不在前台** | **系统通知**(这是插件唯一能拿到的系统通知通道,三个平台一致)。点通知:窗口回到前台并跳到该会话 | -| DSH 窗口在前台,且**不是**你正在看的那个会话 | **应用内轻弹窗**:右上角悬浮,标题是提醒类型、下面一行是会话题目与细节;点「查看」跳过去,6 秒自动消失,鼠标悬停时不消失,Esc 或者 ✕ 关掉 | -| DSH 窗口在前台,且就是你**正在看的**那个会话 | 不打扰(你在看,不需要弹) | +- 点「查看」跳到该会话; +- 6 秒后自动消失;鼠标停在上面期间不消失,移开后重新计时; +- 按 `Esc` 关掉最新的一条,或点 ✕ 关掉这一条; +- 同屏最多叠 3 条,更早的会被挤掉。 ## 设置 -**插件 → 会话通知**(插件自己的介绍页里): +**插件 → 会话通知**: -- 三个开关分别控制**完成 / 授权 / 提问**三类提醒(默认全开),状态存在浏览器本地,重装插件不丢; -- 系统通知权限状态;未授权时点「允许通知」请求权限(浏览器要求必须由你手动点击才能弹权限框),授权后自动发一条测试通知;权限被系统层关闭时按平台给出开启路径; -- 「测试系统通知」直接走系统通知通道(窗口在前台也照发),专门用来验证系统通知本身是否可用; -- 「运行状态」一段显示:窗口现在是前台还是后台(决定会走哪条通道)、观察器是否真的收到了会话状态、以及**最近一次投递走的通道与时间**。 +| 分组 | 内容 | +| --- | --- | +| 提醒内容 | **完成 / 授权 / 提问** 三个开关,分别控制三类提醒,默认全开;开关保存在浏览器本地,重开页面、重装插件都保留 | +| 系统通知权限 | 当前权限状态;尚未授权时点「允许通知」请求授权(浏览器要求必须由你手动点击),授权成功后会自动发一条测试提醒。权限被系统层关闭时,会按当前平台告诉你去哪里打开 | +| 运行状态 | 窗口现在是前台还是后台(即会走哪条通道)、插件是否真的在读会话状态、以及**最近一次提醒走的通道与时间**;旁边「测试提醒」按钮发一条测试提醒 | -设置放在插件自己的页面里,而不是「设置 → 通用」:它是这个插件的配置,不是 DSH 的通用偏好。页面顶部是 DSH 自己渲染的插件标题与描述,所以这块配置不再重复标题,直接用一句「提醒是怎么送出去的」开头。 +- 已经授权时,权限行的按钮变成「测试系统通知」:它直接走系统通知通道(窗口在前台也照发),专门用来验证系统通知本身是否可用。 +- 开关只影响提醒,不改 DSH 本身的任何设置。 ## 安装 -### 1. 从 Git 仓库安装(推荐) +### 从 Git 仓库安装(推荐) 在 DSH 的 **插件 → 添加插件** 的上方输入框里填: ```text -https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.3 +https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.5 ``` -`#` 后面跟标签或提交,用来锁定版本;不写则取默认分支。跟随 1.x 最新版可以写 `#semver:^1.0.3`。仓库是公开的,不需要凭据,也不用改「安装源」。 +`#` 后面跟标签或提交,用来锁定版本;不写则取默认分支。跟随 1.x 最新版可以写 `#semver:^1.0.5`。仓库是公开的,不需要凭据,也不用改「安装源」。 -### 2. 本地路径安装 +### 本地路径安装 把仓库克隆或下载到本机,在同一个输入框里填包目录的绝对路径,例如: @@ -53,62 +64,32 @@ https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.3 D:\DeepSeek Harness Plugins\dsh-session-notify ``` -### 装完之后 +### 安装之后 -- **浏览器半**随页面加载,**刷新页面**即生效; -- **宿主半**是空壳(不改任何 DSH 状态、不注册服务与路由),只有它变化时才需要完全退出 DSH 再打开; -- 本插件没有第三方依赖,不需要 pnpm 下载,也没有构建步骤。 +- 装好**刷新页面**即生效;本插件没有第三方依赖,也不需要 pnpm 下载或构建步骤。 +- 卸载:在插件页里移除 `@dsh-plugin/session-notify`。 -卸载:插件页里移除 `@dsh-plugin/session-notify`(它同时是 profile 的一个 bundle)。 +## 提醒规则 -## 平台说明与验证范围 +- **只在「运行中 → 空闲」的转变上提醒**:打开应用时已经在跑的会话只建立基线,不补发;历史里早已结束的会话不会被翻出来提醒。 +- **正在看的那个会话完成后不打扰**。 +- **子智能体的会话不单独提醒**,等它归属的主会话结束时才提醒。 +- **空白会话完成不提醒**。 +- **授权 / 提问如果出现的当下会话仍在运行,会先记下、等这一轮真正停下来再提醒**,不在模型还在跑的时候打扰你。 +- **节流**:1.5 秒内只投递一条,多个会话同时完成不会刷屏。 +- **完成提醒有 400ms 的确认窗口**:如果那一轮马上又跑起来,就不报了。 +- **同一会话的完成提醒会覆盖上一条**(通知带 `tag`),不叠加堆积。 +- 提醒只在 DSH 运行、页面打开时产生:**DSH 完全退出期间结束的会话不会再补发通知**。 -- **通道是跨平台统一的**:插件的提醒都通过页面(Electron 渲染进程)的 `Notification` 构造器发出,Windows 通知中心 / macOS 通知中心 / Linux 通知守护(libnotify、GNOME、KDE)都由系统把它转成原生通知;应用内轻弹窗是纯 DOM,与平台无关。 -- **前台/后台的判定不依赖单一信号**:每次都现读页面的可见性与文档焦点(任一为"不可见"就算后台),不靠"记住上次的 blur 事件"——桌面外壳里漏掉一次 blur 曾让插件误判为前台而吃掉提醒(v1.0.5 修)。 -- **宿主半拿不到系统通知**:DSH Desktop 的宿主进程是纯 Node 进程(不是 Electron 主进程),没有 Electron 的 `Notification` 可用,所以插件的所有提醒都在浏览器半产生——这也正是"三个平台一套代码"的原因。 -- **已验证**:Windows 上的安装、三个 slot 条目注册、以及 28 项离线断言(接线 / 国际化 / 两种后台信号 / 系统通道被拒时的降级 / 配置页诊断 / 全部投递规则)。 -- **未验证**:macOS 与 Linux 上的实际弹窗效果本机无法测试。结构上它们与 Windows 共用同一条通道、同一份代码,只有"权限被系统关闭时显示的开启路径"是分平台的。 -- 提醒只在 DSH 进程运行、页面打开时产生:**DSH 完全退出期间结束的会话不会再补发通知**。 +## 平台 -## 行为细节(都已验证) - -- **只有观测到「运行中 → 空闲」的转变才提醒**:打开应用时已经在跑的会话只建立基线,不补发;历史里早已结束的会话不会被翻出来提醒。 -- **子智能体会话不单独提醒**(`origin === 'subagent'`),归属它的主会话结束时才提醒。 -- **新会话(空白会话)完成不提醒**。 -- **授权/提问在会话仍在运行时被延后提醒**:先记下,等这一轮真正停下来再报,避免在模型还在跑的时候就打扰你。 -- **节流**:1.5 秒内只投递一条,多个会话同时完成不会刷屏;完成提醒前有 400ms 的确认窗口,如果那一轮马上又跑起来就不报。 -- **同一会话的完成提醒会覆盖上一条**(通知带 `tag`),不堆叠。 +- Windows / macOS / Linux 都支持,三个平台走同一条通知通道,行为一致;应用内轻弹窗与平台无关。 +- 当前环境不支持系统通知时(权限一行会显示「不可用」),提醒仍然以应用内轻弹窗的形式送达。 +- 系统通知被拒绝或发送失败时,插件会改用轻弹窗,并把原因写在插件页的权限 / 状态那一段,不会静默失败。 ## 兼容性 -基于 **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 注册:`plugins.bundle.config`(本插件在插件页里的配置,key 就是包名)与 `shell.overlay`(两个条目:轻弹窗层 + 无渲染的观察器,内容永远是 `null`); -- **配置放在插件页**:`plugins.bundle.config` 的 key 取包名(`@dsh-plugin/session-notify`),插件页就会渲染这块配置;官方 `dsh-experimental-voice-input-bundle` 用的就是这个口子。行级口子是 `plugins.row.config`,key 形如 `<包名>#<行 id>`,会在该行上多出一个「配置」入口; -- **不要**把"无渲染"条目放进 `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 修订号上打点,已经渲染出来的部件会自动换上文案,不需要刷新; -- **前台/后台要现读、不能只靠事件**:桌面外壳里 `blur` 可能不到,只记事件会让插件一直以为在前台而吃掉系统通知(本插件第三版踩过这个坑)。现在每次投递都读 `document.visibilityState` 与 `document.hasFocus()`; -- 另外自带一份本地字典兜底:即使拿不到框架的 `t`、或 locale 服务没有保存注册,也会渲染中文/英文文案而不是原始键; -- 样式只用主题 token,不 import 任何 `@deepseek-ai/dsh-client-*` 包(规范要求,也是渲染不被上游改动打断的前提); -- 宿主半不声明 `inject`、不注册服务、不注册路由;三个开关存浏览器本地,不改 DSH 的配置文件。 - -上游若改动 slot 名或 hook 名:注册会静默跳过而不是把页面弄坏,插件会退化成"不提醒";这类情况请按上面的契约名核对。 - -## 验证方式(开发记录) - -`client.js` 是纯 JavaScript、浏览器端运行,可以在 Node 里用桩模块加载器评估并驱动: - -- 模块接线:插件返回值、注册集合(含"配置挂在插件页""观察器绝不在 `sidebar.*` 里"这两条); -- 国际化:字典中英键集合一致且没有死键、插件页与弹窗渲染出的每个字符串都不是原始键、拿不到框架 `t` 时本地字典兜底; -- 前台/后台判定:**只给"页面不可见"信号**、或**只给"文档失焦"信号**(都不发 blur 事件)时,都必须走系统通知; -- 系统通道被拒时降级:构造器抛错时改用轻弹窗,并在配置页写明被拒; -- 配置页诊断:窗口状态、观察器健康、最近一次投递的通道与时间都能渲染; -- 投递规则:完成提醒一次、当前会话不打扰、授权/提问各提醒一次、运行中延后、子会话跳过、空白会话跳过、历史不补发等。 - -合计 28 项断言。 +需要 **DeepSeek Harness 0.2.0-rc.2 或更高**。 ## License