feat: 会话通知插件 v1.0.1(系统通知 + 应用内轻弹窗)
会话完成、需要工具授权、需要回答时提醒: - 窗口不在前台走系统通知(Windows / macOS / Linux 同一条渲染进程通知通道,无平台分支),点击回到 DSH 并跳到该会话; - 窗口在前台走应用内轻弹窗(右上角、6 秒自动消失、可悬停暂停、Esc 关闭、点「查看」跳转); - 正在看的那个会话完成时不打扰。 触发与去重:只对「运行中 → 空闲」的转变提醒,同一会话同一轮只报一次,同一待处理请求只报一次; 子智能体会话与空白会话跳过;打开应用时已在运行的会话只建基线不补发; 多个会话同时完成有 1.5 秒节流,完成提醒前有 400ms 确认窗口(那一轮又跑起来就不报)。 设置 → 通用 → 会话通知:权限状态、请求权限、测试通知、完成/授权/提问三个开关, 以及观察器自身的健康状态(收不到会话状态时直接显示,不会静默失效)。 实现形态遵循 DSH 插件规范:dsh.manifestVersion 1、dsh.bundle.patch 声明宿主行、 dsh.client 声明 platform: web 的浏览器半;宿主半为空壳,不注册任何服务与路由。 会话状态只通过 slot 的标准 props(useSessions / useSessionStatus)读取, 只往 settings.general.item 与 shell.overlay 注册,样式只用主题 token,不 import 客户端包。 修复:观察器原先挂在 sidebar.panellist,而该 slot 的宿主会把每个条目的 id 当作一个 左侧面板按钮(label 取 options.label ?? options.id,条目作为图标内联渲染), 导致左侧多出一行空面板并挤坏侧边栏;现改挂通用浮层 shell.overlay。
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# 仓库内一律使用 LF:这是跨平台插件(Windows / macOS / Linux),
|
||||
# 换行符不该随检出机器变化。
|
||||
* text=auto eol=lf
|
||||
@@ -0,0 +1,6 @@
|
||||
# 忽略安装与构建产物;这个包没有依赖,也没有构建步骤。
|
||||
node_modules/
|
||||
pnpm-lock.yaml
|
||||
*.tgz
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 pyh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,91 @@
|
||||
# 会话通知
|
||||
|
||||
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`,条目本身作为图标内联渲染),放进去会在左侧多出一行空面板并挤坏侧边栏。本插件第一版踩过这个坑,现已改挂到通用浮层;
|
||||
- 样式只用主题 token,不 import 任何 `@deepseek-ai/dsh-client-*` 包(规范要求,也是渲染不被上游改动打断的前提);
|
||||
- 宿主半不声明 `inject`、不注册服务、不注册路由。
|
||||
|
||||
上游若改动 slot 名或 hook 名:注册会静默跳过而不是把页面弄坏,插件会退化成"不提醒";这类情况请按上面的契约名核对。
|
||||
|
||||
## 验证方式(开发记录)
|
||||
|
||||
`client.js` 是纯 JavaScript、浏览器端运行,可以在 Node 里用桩模块加载器评估并驱动:
|
||||
|
||||
- 模块接线:加载后返回的插件、`inject` 声明、注册集合(含"观察器绝不在 sidebar.* 里"这一条)、以及各组件在空状态 / 权限被拒 / 无 Notification API 下的渲染都不报错;
|
||||
- 投递规则:40 项断言覆盖上面"行为细节"里的每一条(完成提醒一次、当前会话不打扰、后台走系统通知、授权/提问各提醒一次、运行中延后、子会话跳过、历史不补发、开关生效、弹窗自动消失等)。
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Bundle patch layer for @dsh-plugin/session-notify.
|
||||
#
|
||||
# One Host row is enough: the Host half owns no services and no routes, and the
|
||||
# browser half is picked up automatically from this package's `dsh.client`
|
||||
# declaration (`dsh-client-modules` scans enabled Loader entries, serves
|
||||
# `/plugins/@dsh-plugin/session-notify/client.js`, and boots it with the page).
|
||||
- insert:
|
||||
- id: session-notify
|
||||
name: '@dsh-plugin/session-notify'
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<!-- Notification bell (bundle artwork; the only place a literal color is allowed). -->
|
||||
<path d="M8 2.5C5.72 2.5 3.88 4.34 3.88 6.62V9.1L3.1 11.2C3.03 11.39 3.17 11.58 3.37 11.58H12.63C12.83 11.58 12.97 11.39 12.9 11.2L12.12 9.1V6.62C12.12 4.34 10.28 2.5 8 2.5Z" fill="#247bbf"/>
|
||||
<path d="M6.3 12.7C6.52 13.62 7.2 14.25 8 14.25C8.8 14.25 9.48 13.62 9.7 12.7" stroke="#247bbf" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<circle cx="12.6" cy="3.4" r="2.1" fill="#e5484d" stroke="#ffffff" stroke-width="0.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 619 B |
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Host half of the session-notify bundle.
|
||||
*
|
||||
* Every notification in this plugin is produced by the browser half: the page
|
||||
* owns the DOM (the in-app light popup) and is the only place a Web
|
||||
* `Notification` can be constructed, which is the one system-notification
|
||||
* channel that behaves the same on Windows, macOS, and Linux inside the Harness
|
||||
* Desktop shell. The Host half therefore owns no state, registers no service,
|
||||
* no route, and no event listener — it exists so the bundle has the ordinary
|
||||
* profile-level plugin row its patch declares, and so a future Host-side
|
||||
* capability (for example persisting the per-kind switches into this profile's
|
||||
* patch) has a place to live.
|
||||
*
|
||||
* The export form is the documented one — `export function apply(ctx, config)`
|
||||
* with no `inject` and no `Config` — and it is deliberately the only export
|
||||
* form this package uses.
|
||||
*
|
||||
* @module @dsh-plugin/session-notify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mount the Host row.
|
||||
* @param ctx - Host context; no service is required, so this never blocks activation.
|
||||
*/
|
||||
export function apply(ctx) {
|
||||
ctx.logger?.debug?.(
|
||||
'session-notify: host half idle by design — notifications run in the browser half',
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Session notifications",
|
||||
"description": "Tells you when a conversation finishes, needs approval, or needs an answer — a system notification when the window is in the background, a light in-app popup when it is in the foreground."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "会话通知",
|
||||
"description": "会话完成、需要授权或需要回答时提醒你:窗口不在前台用系统通知,窗口在前台用应用内轻弹窗。"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* CommonJS-resolvable entry alias for the Host half.
|
||||
*
|
||||
* `package.json` `exports` names `./index.js`, which is what the Cordis Loader
|
||||
* and the plugin manager read. A loader that resolves this package by
|
||||
* `main` instead of `exports` would otherwise find nothing, so this file
|
||||
* re-exports the same plugin without adding behavior or export forms.
|
||||
*
|
||||
* @module @dsh-plugin/session-notify/main
|
||||
*/
|
||||
export { apply } from './index.js'
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@dsh-plugin/session-notify",
|
||||
"version": "1.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "会话完成、需要授权、需要回答时提醒你:窗口不在前台用系统通知,窗口在前台用应用内轻弹窗。",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitea.iwake.top/dsh-plugin/session-notify.git"
|
||||
},
|
||||
"homepage": "https://gitea.iwake.top/dsh-plugin/session-notify",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./client": "./client.js",
|
||||
"./package.json": "./package.json",
|
||||
"./locale/*.json": "./locale/*.json"
|
||||
},
|
||||
"icon": "./icon.svg",
|
||||
"meta": {
|
||||
"title": "会话通知",
|
||||
"description": "会话完成 / 需要授权 / 需要回答时提醒,支持系统通知与应用内轻弹窗。"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"main.js",
|
||||
"client.js",
|
||||
"cordis.patch.yml",
|
||||
"icon.svg",
|
||||
"locale/*.json",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
"dsh": {
|
||||
"manifestVersion": 1,
|
||||
"bundle": {
|
||||
"patch": "./cordis.patch.yml"
|
||||
},
|
||||
"client": {
|
||||
"platform": "web",
|
||||
"immediately": true,
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-ui-workspace"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user