9.3 KiB
会话通知
DeepSeek Harness(DSH)插件:会话需要你注意时提醒你——窗口不在前台时用系统通知,窗口在前台时用应用内轻弹窗。
Windows / macOS / Linux 三个平台走的是同一条通知通道(渲染进程的 Web Notification API),插件里没有平台分支。
提醒什么
| 触发 | 通知标题 |
|---|---|
| 会话从「运行中」变为「空闲」(一轮回答结束) | 会话已完成 |
待处理的工具授权请求(approval) |
需要授权 |
待处理的提问 / 计划确认(question / plan-review) |
需要回答 |
每条只提醒一次:同一会话的同一轮只报一次完成,同一个待处理请求只报一次;请求消失后再来才会再报。
走哪个通道
| 状态 | 行为 |
|---|---|
| DSH 窗口不在前台 | 系统通知(这是插件唯一能拿到的系统通知通道,三个平台一致)。点通知:窗口回到前台并跳到该会话 |
| DSH 窗口在前台,且不是你正在看的那个会话 | 应用内轻弹窗:右上角悬浮,标题是提醒类型、下面一行是会话题目与细节;点「查看」跳过去,6 秒自动消失,鼠标悬停时不消失,Esc 或者 ✕ 关掉 |
| DSH 窗口在前台,且就是你正在看的那个会话 | 不打扰(你在看,不需要弹) |
设置
插件 → 会话通知(插件自己的介绍页里):
- 显示系统通知权限状态;
- 未授权时点「允许通知」请求权限(浏览器要求必须由你手动点击才能弹权限框),授权后自动发一条测试通知;
- 已授权时点「测试通知」随时验证;
- 权限被系统层关闭时,按平台给出开启路径(Windows / macOS 的通知设置、Linux 桌面环境的通知设置);
- 三个开关分别控制完成 / 授权 / 提问三类提醒(默认全开),状态存在浏览器本地,重装插件不丢;
- 最后一段显示观察器自身的健康状态(收不到会话状态时直接标红)。
设置放在插件自己的页面里,而不是「设置 → 通用」:它是这个插件的配置,不是 DSH 的通用偏好。页面顶部是 DSH 自己渲染的插件标题与描述,所以这块配置不再重复标题,直接用一句「提醒是怎么送出去的」开头。
关于「包含的组件」
插件页下方那一段(组件 / 包含的组件)是 DSH 插件页固定渲染的:ui-plugin-manager 的 RowsSection 无条件列出这个 bundle 在 cordis.patch.yml 里声明的每一行(行 id、运行状态、单独开关),没有"跳过"的口子。因为任何 bundle 都至少要声明一行才能挂上宿主半,所以这一节必然出现——本插件就是这一行。
它也不是纯冗余:它标出运行状态、允许单独关掉这个组件,并且当 bundle 与组件的关系变得复杂时是唯一的入口。如果你觉得一个组件时看着奇怪,那是 DSH 的呈现方式,插件侧没法隐藏。
安装
1. 从 Git 仓库安装(推荐)
在 DSH 的 插件 → 添加插件 的上方输入框里填:
https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.3
# 后面跟标签或提交,用来锁定版本;不写则取默认分支。跟随 1.x 最新版可以写 #semver:^1.0.3。仓库是公开的,不需要凭据,也不用改「安装源」。
2. 本地路径安装
把仓库克隆或下载到本机,在同一个输入框里填包目录的绝对路径,例如:
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 条目注册、以及 52 项离线断言(接线 / 国际化 / 轻弹窗结构 / 开关持久化 / 全部投递规则)。
- 未验证: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 注册:
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 修订号上打点,已经渲染出来的部件会自动换上文案,不需要刷新; - 另外自带一份本地字典兜底:即使拿不到框架的
t、或 locale 服务没有保存注册,也会渲染中文/英文文案而不是原始键; - 样式只用主题 token,不 import 任何
@deepseek-ai/dsh-client-*包(规范要求,也是渲染不被上游改动打断的前提); - 宿主半不声明
inject、不注册服务、不注册路由;三个开关存浏览器本地,不改 DSH 的配置文件。
上游若改动 slot 名或 hook 名:注册会静默跳过而不是把页面弄坏,插件会退化成"不提醒";这类情况请按上面的契约名核对。
验证方式(开发记录)
client.js 是纯 JavaScript、浏览器端运行,可以在 Node 里用桩模块加载器评估并驱动:
- 模块接线:插件返回值、
inject声明、注册集合(含"配置挂在插件页""观察器绝不在sidebar.*里"这两条); - 国际化:字典已注册且中英键集合一致、插件页与弹窗渲染出的每个字符串都不是原始键、拿不到框架
t时本地字典兜底、locale 服务不保存注册时仍然有文案; - 轻弹窗结构:标题是触发类型、描述含会话名与细节;
- 开关:切换即写入浏览器本地,重开页面能读回;
- 渲染健壮性:各组件在空状态 / 权限被拒 / 无 Notification API 下渲染都不报错;
- 投递规则:完成提醒一次、当前会话不打扰、后台走系统通知、授权/提问各提醒一次、运行中延后、子会话跳过、空白会话跳过、历史不补发、开关生效、弹窗自动消失等。
合计 52 项断言。