@telosmaylx/dsh-session-notify 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +545 -196
- package/cordis.patch.yml +10 -0
- package/lib/client.js +1208 -1180
- package/lib/core.js +303 -303
- package/lib/index.js +330 -291
- package/package.json +70 -66
- package/scripts/build.sh +16 -16
- package/scripts/probe-card-render.mjs +100 -100
- package/scripts/probe-client-e2e.mjs +134 -134
- package/scripts/probe-client.mjs +145 -145
- package/scripts/probe-diag-settings.mjs +79 -79
- package/scripts/probe-settings-card.mjs +137 -137
- package/scripts/probe-settings-check.mjs +87 -87
- package/scripts/verify-notice.mjs +110 -110
package/README.md
CHANGED
|
@@ -1,196 +1,545 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
###
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# dsh-session-notify
|
|
4
|
+
|
|
5
|
+
**DSH(DeepSeek Harness)会话完成提醒插件 —— 每一轮结束,让完成状态主动找你,而不是你盯着屏幕等。**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
8
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
11
|
+
[](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
12
|
+
[](https://github.com/TelosmaYLX/dsh-session-notify/pulls)
|
|
13
|
+
|
|
14
|
+
每轮对话结束时,把「已完成 / 出错 / 被阻塞 / 达到上限」连同用时、token 消耗写入会话日志,并推送浏览器系统通知与页内 toast。内置 5 种语言、可视化文案模板编辑器、自定义预设库,缓存命中率与生成速度取自官方投影,与状态栏同口径。
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 目录
|
|
21
|
+
|
|
22
|
+
- [功能特性](#功能特性)
|
|
23
|
+
- [环境要求](#环境要求)
|
|
24
|
+
- [安装](#安装)
|
|
25
|
+
- [卸载](#卸载)
|
|
26
|
+
- [快速开始](#快速开始)
|
|
27
|
+
- [通知行为](#通知行为)
|
|
28
|
+
- [触发条件](#触发条件)
|
|
29
|
+
- [推送正文从哪来](#推送正文从哪来)
|
|
30
|
+
- [通知示例](#通知示例)
|
|
31
|
+
- [通知权限](#通知权限)
|
|
32
|
+
- [配置](#配置)
|
|
33
|
+
- [设置面板](#设置面板)
|
|
34
|
+
- [文案模板与占位符](#文案模板与占位符)
|
|
35
|
+
- [预设系统](#预设系统)
|
|
36
|
+
- [宿主配置项](#宿主配置项)
|
|
37
|
+
- [工作原理](#工作原理)
|
|
38
|
+
- [项目结构](#项目结构)
|
|
39
|
+
- [开发与调试](#开发与调试)
|
|
40
|
+
- [常见问题](#常见问题)
|
|
41
|
+
- [更新日志](#更新日志)
|
|
42
|
+
- [贡献](#贡献)
|
|
43
|
+
- [相关链接](#相关链接)
|
|
44
|
+
- [许可证](#许可证)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 功能特性
|
|
49
|
+
|
|
50
|
+
### 三通道提醒,一条不漏
|
|
51
|
+
|
|
52
|
+
| 通道 | 形式 | 说明 |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| 会话内系统消息 | 可折叠提示行 | 每轮结束把结束原因与用时、消耗作为插件来源的系统消息追加进会话日志,随 JSONL 落盘,恢复或回放会话后依然可见。 |
|
|
55
|
+
| 浏览器系统通知 | Web Notification | 原生弹窗。每次完成事件使用独立 `tag`(`dsh-session-notify:<timestamp>`),不与前一次互相替换,也不被折叠成一个分组条目;点击通知聚焦回窗口。 |
|
|
56
|
+
| 页内 toast | 右下角浮动弹窗 | 永远展示的保底通道:系统通知被平台静默、权限拒绝或环境不支持时仍有可见反馈。同屏最多 3 条(超出移除最旧),10 秒自动消失,点击关闭。 |
|
|
57
|
+
|
|
58
|
+
### 后台会话全覆盖
|
|
59
|
+
|
|
60
|
+
- 宿主为所有会话(含后台、未打开窗口的)维护「最近一条通知正文」的会话投影单元(key = `session-complete-notify`),推送正文跨会话一致,不依赖你恰好开着那个窗口。
|
|
61
|
+
- 客户端从会话列表快照观测所有会话的 `running` 位,`true → false` 边沿即触发推送,与官方 sidebar 提醒同策略(首次观测只记录基线,已在 idle 的会话不补发)。
|
|
62
|
+
|
|
63
|
+
### 可定制到每一句话
|
|
64
|
+
|
|
65
|
+
- **5 种语言**:简体中文、繁體中文、English、日本語、한국어 —— 通知文案、时长与用量措辞、设置面板界面全部随语言切换(切换即时重渲染)。
|
|
66
|
+
- **可视化模板编辑器**(Chip 胶囊编辑器):动态信息渲染为内联胶囊(占位符代码不露出),「+ 插入信息」在光标处插入(可插到文字中间),点击胶囊移除,每栏带实时预览(信息以示例值流入正文)。
|
|
67
|
+
- **预设系统**:内置「默认」预设作为基线;当前配置可另存为自定义预设(`localStorage` 持久化),支持自动编号的未命名预设(`未命名`、`未命名 2`…)、「来自:xxx · 已修改」来源指示、删除预设。
|
|
68
|
+
- **推送标题模板**:`{title}` 引用会话标题,留空则直接使用会话标题。
|
|
69
|
+
|
|
70
|
+
### 与官方口径同源
|
|
71
|
+
|
|
72
|
+
- **缓存命中率**取自官方 `tokenUsage` 投影:缓存读 /(未缓存输入 + 缓存读 + 缓存写)。
|
|
73
|
+
- **生成速度**取自官方 `sessionStats` 投影:输出 token ÷ 解码耗时。
|
|
74
|
+
- 两者与 dsh-web-ui 状态栏完全同口径,不含排队、准备、工具时间;投影不可用或数据未就绪时自动退回本地用量聚合估算。
|
|
75
|
+
|
|
76
|
+
> [!NOTE]
|
|
77
|
+
> 缓存命中率与速度只在自定义模板中通过 `{cache}`、`{tps}` 占位符插入时才显示。使用内置默认文案时,正文只含用时与消耗。
|
|
78
|
+
|
|
79
|
+
### 工程质量
|
|
80
|
+
|
|
81
|
+
- **只响应实时事件**:resume、replay 不重放旧通知,加载会话不刷屏。
|
|
82
|
+
- **自免疫循环**:插件追加的消息类型(`user/message`)与自身监听目标(`turn/*`)不相交。
|
|
83
|
+
- **零外部依赖**:宿主平面零裸 import,UserMessage 按 `dsh-llm` 的 `createUserMessage` 契约手工构造;纯逻辑层(`lib/core.js`)零依赖,可独立测试。
|
|
84
|
+
- **Cordis effect 纪律**:重试定时器包装在 `ctx.effect()` 中并返回 `clearTimeout` disposer,注册随 fiber 卸载自动撤销,HMR 热重载安全。
|
|
85
|
+
- **安装即挂载**:声明官方 `dsh.bundle` manifest,`dsh plugin add` 一条命令装完即用,无需手写 patch。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 环境要求
|
|
90
|
+
|
|
91
|
+
| 依赖 | 要求 |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| DSH(DeepSeek Harness) | Web profile 部署。官方 base bundle 默认包含 `@deepseek-ai/dsh-settings`(设置命名空间)与会话投影,无需额外配置 |
|
|
94
|
+
| cordis | `>=4.0.0-rc <5`(peer dependency,由宿主提供) |
|
|
95
|
+
| Node.js | `>=22`(宿主侧) |
|
|
96
|
+
| 浏览器 | 支持 Web Notification 则有系统通知;不支持、权限拒绝或被静默时由 toast 兜底 |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 安装
|
|
101
|
+
|
|
102
|
+
> [!WARNING]
|
|
103
|
+
> 裸 `npm install` 只会把包装进依赖树,**不会注册插件** —— 这是 DSH 官方设计(`npm install only adds the dependency; it does not register the plugin`)。自动挂载的唯一官方途径是 `dsh plugin add`:它读取包内 `dsh.bundle` manifest(本插件自 0.1.3 起声明,指向仓库根 `cordis.patch.yml`)并自动应用。
|
|
104
|
+
|
|
105
|
+
### 方式一:dsh plugin add(推荐)
|
|
106
|
+
|
|
107
|
+
安装包的同时自动应用 `cordis.patch.yml`,把插件挂载进 profile 装配(host 事件订阅 + client 启动图注入)。
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dsh plugin --profile web add @telosmaylx/dsh-session-notify
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 方式二:从 GitHub 仓库安装
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
dsh plugin add github:TelosmaYLX/dsh-session-notify
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
也可以在 DSH Web GUI 会话内执行:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
dev_install_package github=TelosmaYLX/dsh-session-notify
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 方式三:本地目录热装配(开发用)
|
|
126
|
+
|
|
127
|
+
把路径换成你的克隆目录,在 DSH Web GUI 会话内执行:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
dev_install_package dir=/你的/克隆目录/dsh-session-notify
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 方式四:npm 包手动安装
|
|
134
|
+
|
|
135
|
+
先打包:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm pack @telosmaylx/dsh-session-notify
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
解压后指定目录安装(在 DSH Web GUI 会话内执行):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
dev_install_package dir=/解压/目录/package
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 方式五:手动 cordis patch(不依赖安装器)
|
|
148
|
+
|
|
149
|
+
在 `~/.dsh/profiles/web/cordis.patch.yml` 追加:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
- insert:
|
|
153
|
+
- id: dsh-session-notify
|
|
154
|
+
name: '@telosmaylx/dsh-session-notify'
|
|
155
|
+
config: {}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> [!IMPORTANT]
|
|
159
|
+
> 无论用哪种方式,装完都需要**刷新一次浏览器页面** —— 客户端 bundle 通过 `__DSH_BOOT__` 启动图注入。
|
|
160
|
+
|
|
161
|
+
## 卸载
|
|
162
|
+
|
|
163
|
+
一条命令移除插件及其挂载(自动从 `cordis.patch.yml` 移除 insert 条目):
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
dsh plugin --profile web remove @telosmaylx/dsh-session-notify
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
> [!NOTE]
|
|
170
|
+
> 手动安装(方式四/五)的用户,需同步从 `~/.dsh/profiles/web/cordis.patch.yml` 删除对应 insert 条目,再刷新页面。
|
|
171
|
+
|
|
172
|
+
### 卸载时自动清理的内容
|
|
173
|
+
|
|
174
|
+
插件实现了完整的生命周期收尾(Cordis effect 纪律),卸载/禁用/HMR 热重载时:
|
|
175
|
+
|
|
176
|
+
| 平面 | 自动释放的资源 |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
| host | `session/event` 事件订阅、settings 命名空间、会话投影单元、设置注册重试定时器(`ctx.effect` 包装);置卸载标志抑制已调度的微任务追加 |
|
|
179
|
+
| client | 会话列表订阅、完成推送正文的轮询定时器、`window.__dsch_notify_debug` 调试钩子(按引用删除,防闭包泄漏)、页内 toast 容器 DOM |
|
|
180
|
+
|
|
181
|
+
### 卸载后保留的数据
|
|
182
|
+
|
|
183
|
+
- **设置配置**(语言、文案模板)留在 settings 文档,重装后自动恢复;
|
|
184
|
+
- **自定义预设**存于浏览器 `localStorage`(`dsh-scn-custom-presets`),重装后仍在;
|
|
185
|
+
- 历史会话中已追加的系统消息与 JSONL 日志**不会**被回滚(它们是会话数据的一部分,与官方侧边栏提示同语义)。
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 快速开始
|
|
190
|
+
|
|
191
|
+
1. 按上面任一方式安装并刷新页面。
|
|
192
|
+
2. 发起任意一轮对话,等它结束 —— 右下角弹出 toast、浏览器弹系统通知、会话日志里出现可折叠的系统提示行。
|
|
193
|
+
3. 首次收到完成事件时,浏览器会请求通知权限(每页只问一次),允许后后续完成都有系统通知。
|
|
194
|
+
4. 打开 **设置 → 插件 → 会话完成提醒**,切换语言、编辑文案模板、另存预设。保存后点「点击刷新」让宿主与客户端两侧重新读取,新配置即生效。
|
|
195
|
+
|
|
196
|
+
刚装好时,会话日志里会出现这样一行可折叠提示:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
会话已完成(用时 1 分 12 秒,消耗 1,240 输入 / 3,560 输出)。
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 通知行为
|
|
205
|
+
|
|
206
|
+
### 触发条件
|
|
207
|
+
|
|
208
|
+
每轮对话结束(`turn/end`)时按结束原因判断,命中白名单即提醒:
|
|
209
|
+
|
|
210
|
+
| 结束原因 | 含义 | 默认 |
|
|
211
|
+
| --- | --- | --- |
|
|
212
|
+
| `completed` | 会话正常完成 | 提醒 |
|
|
213
|
+
| `aborted` | 会话中止 | 提醒 |
|
|
214
|
+
| `blocked` | 会话被阻塞 | 提醒 |
|
|
215
|
+
| `error` | 会话出错(附错误详情,超长截断) | 提醒 |
|
|
216
|
+
| `max-tokens` | 达到输出 token 上限 | 提醒 |
|
|
217
|
+
| `interrupted` | 中断(崩溃恢复后由持久化后端补写的孤儿轮次关闭标记) | 不提醒(可配置加入) |
|
|
218
|
+
|
|
219
|
+
**子代理会话默认跳过**(`header.origin === 'subagent'` 或 `delegationDepth > 0`)—— 子代理由父会话编排,逐轮提醒是噪音;可在宿主配置关闭跳过。
|
|
220
|
+
|
|
221
|
+
### 推送正文从哪来
|
|
222
|
+
|
|
223
|
+
客户端在会话列表观测到 `running: true → false` 边沿时推送,正文按以下优先级获取(最长轮询 6 秒,400ms 间隔):
|
|
224
|
+
|
|
225
|
+
1. **宿主投影**(key = `session-complete-notify`)—— 每个会话都有,后台会话同样拿到全文;
|
|
226
|
+
2. **会话事件窗口里的 notice 节点**(`kind=context` + `form=notice`)—— 正在查看的会话,落盘后立即可用;
|
|
227
|
+
3. **降级** —— 「详情见会话内系统消息」+ 工作区信息(`cwd` 最后一段)。
|
|
228
|
+
|
|
229
|
+
### 通知示例
|
|
230
|
+
|
|
231
|
+
以下均由 `lib/core.js` 的 `buildNotice` 实际生成。
|
|
232
|
+
|
|
233
|
+
简体中文默认文案:
|
|
234
|
+
|
|
235
|
+
```text
|
|
236
|
+
会话已完成(用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出)。
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
出错时附带错误详情(单行化,超过 40 字符截断):
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
会话出错:connection timeout(用时 12 秒)。
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
English 默认文案:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
Session completed (took 3m25s, used 12,400 in / 35,600 out).
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
自定义模板(在设置面板编辑,本例用到全部信息位):
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
{title} 干完了!用时 {duration},消耗 {usage},缓存命中 {cache},速度 {tps}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
渲染结果:
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
重构登录模块 干完了!用时 3 分 25 秒,消耗 103,600 输入 / 35,600 输出,缓存命中 96.5%,速度 92 tok/s
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
五种语言的同一事件:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
会话已完成(用时 3 分 25 秒,消耗 1,240 输入 / 3,560 输出)。
|
|
267
|
+
會話已完成(用時 3 分 25 秒,消耗 1,240 輸入 / 3,560 輸出)。
|
|
268
|
+
Session completed (took 3m25s, used 1,240 in / 3,560 out).
|
|
269
|
+
セッション完了(所要 3 分 25 秒、消費 1,240 入力 / 3,560 出力)。
|
|
270
|
+
세션 완료(소요 3분 25초, 소모 1,240 입력 / 3,560 출력)。
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 通知权限
|
|
274
|
+
|
|
275
|
+
| 权限状态 | 行为 |
|
|
276
|
+
| --- | --- |
|
|
277
|
+
| `default`(未决定) | 每页加载只发起一次授权请求,后续完成事件不再触发询问 |
|
|
278
|
+
| `granted` | 每次完成都发系统通知(独立 tag,互不覆盖) |
|
|
279
|
+
| `denied` 或环境不支持 | 仅 toast(永远展示,保底可见) |
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 配置
|
|
284
|
+
|
|
285
|
+
绝大多数配置在 **DSH Web UI → 设置 → 插件 → 会话完成提醒** 面板完成(保存后点「点击刷新」生效)。仅「触发原因白名单」与「跳过子代理」两项在宿主 `cordis.patch.yml` 的 `config` 中配置。
|
|
286
|
+
|
|
287
|
+
### 设置面板
|
|
288
|
+
|
|
289
|
+
面板在官方「设置 → 插件」面板中注册(`settings.plugin.item` keyed slot,key = `session-complete-notify`),样式逐值复刻原生插件卡片(12px 圆角、展开收起、旋转 chevron、footer 状态位 + 弃置 ghost + 主色保存按钮):
|
|
290
|
+
|
|
291
|
+
| 区域 | 内容 |
|
|
292
|
+
| --- | --- |
|
|
293
|
+
| 预设 | 下拉选择内置或自定义预设;「新增」把当前配置另存为自定义预设;当前预设可「删除」 |
|
|
294
|
+
| 语言 | 5 种语言单选,切换即时重渲染整个面板 |
|
|
295
|
+
| 推送标题 | `{title}` 引用会话标题,留空则直接用会话标题 |
|
|
296
|
+
| 模板 × 5 | 每条结束原因(完成、出错、中止、阻塞、输出上限)独立一个 Chip 编辑器:文字 + 内联信息胶囊,光标处插入、点击移除、实时预览 |
|
|
297
|
+
| 跳过子代理会话 | 复选框(保存时一并写入设置文档) |
|
|
298
|
+
| 保存 | 写入宿主设置文档(`language` / `templates` / `titleTemplate`);保存后显示「点击刷新」链接 |
|
|
299
|
+
|
|
300
|
+
> [!NOTE]
|
|
301
|
+
> 面板中「跳过子代理会话」保存的是设置文档里的布尔值;宿主 `cordis.patch.yml` 的 `config.skipSubagents` 是其启动默认值,两者任一为真即跳过。
|
|
302
|
+
|
|
303
|
+
### 文案模板与占位符
|
|
304
|
+
|
|
305
|
+
每条结束原因独立一个模板输入框,**标签即开关** —— 在模板里插入对应信息标签,该项数据才会显示:
|
|
306
|
+
|
|
307
|
+
| 占位符 | 含义 | 示例值 |
|
|
308
|
+
| --- | --- | --- |
|
|
309
|
+
| `{title}` | 会话标题(推送标题模板也可用) | `重构登录模块` |
|
|
310
|
+
| `{duration}` | 本轮用时(`turn/start` 起表 → `turn/end` 结束) | `3 分 25 秒` / `3m25s` |
|
|
311
|
+
| `{usage}` | token 消耗(输入 = 未缓存 + 缓存读 + 缓存写) | `1,240 输入 / 3,560 输出` |
|
|
312
|
+
| `{error}` | 错误信息(无错误时显示 `none`;单行化,80 字符截断) | `connection timeout` |
|
|
313
|
+
| `{cache}` | 缓存命中率(官方投影口径,无数据为空) | `96.5%` |
|
|
314
|
+
| `{tps}` | 生成速度(官方投影口径,无数据为空) | `92 tok/s` |
|
|
315
|
+
| `{label}` | 已废弃 —— 渲染时自动剥除,旧模板仍兼容(插入菜单已移除该选项) | — |
|
|
316
|
+
|
|
317
|
+
模板留空即使用内置默认文案(自动带用时与消耗)。折叠行 `summary` 与正文同源(渲染结果截断至 120 字符)—— 只看折叠行的用户也能看到真实标题与用时、消耗。
|
|
318
|
+
|
|
319
|
+
### 预设系统
|
|
320
|
+
|
|
321
|
+
- **内置预设**:仅「默认」,作为基线。
|
|
322
|
+
- **自定义预设**:保存在 `localStorage`(key = `dsh-scn-custom-presets`):
|
|
323
|
+
- 「新增」命名后保存为自定义预设;保存后可「修改」自动同步、「删除」移除;
|
|
324
|
+
- **自动编号的未命名预设**:从「默认 / 空白」直接保存时,自动生成 `未命名`、`未命名 2`、`未命名 3`…(编号取当前最大值 + 1);
|
|
325
|
+
- 表单显示「来自:xxx · 已修改」来源指示(来自预设但内容已改动时)。
|
|
326
|
+
- **保存即同步**:保存时若表单来源是自定义预设则更新该预设,否则新建或继续编号未命名预设。
|
|
327
|
+
|
|
328
|
+
### 宿主配置项
|
|
329
|
+
|
|
330
|
+
```yaml
|
|
331
|
+
- insert:
|
|
332
|
+
- id: dsh-session-notify
|
|
333
|
+
name: '@telosmaylx/dsh-session-notify'
|
|
334
|
+
config:
|
|
335
|
+
reasons: [completed, aborted, blocked, error, max-tokens]
|
|
336
|
+
skipSubagents: true
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
340
|
+
| --- | --- | --- | --- |
|
|
341
|
+
| `reasons` | `string[]` | `[completed, aborted, blocked, error, max-tokens]` | 触发提醒的 `turn/end` 原因白名单 |
|
|
342
|
+
| `skipSubagents` | `boolean` | `true` | 跳过子代理会话(`origin=subagent` 或 `delegationDepth>0`) |
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 工作原理
|
|
347
|
+
|
|
348
|
+
插件分**宿主平面**(Node)与**客户端平面**(浏览器),中间靠会话日志(JSONL)与官方会话投影衔接:
|
|
349
|
+
|
|
350
|
+
```text
|
|
351
|
+
┌─────────────────── 宿主平面(lib/index.js,Node)──────────────────┐
|
|
352
|
+
│ │
|
|
353
|
+
│ session/event 火线 │
|
|
354
|
+
│ ├─ turn/start → tracker 起表(key: sessionId:turn) │
|
|
355
|
+
│ ├─ assistant/message → 累加该轮 token 用量 │
|
|
356
|
+
│ └─ turn/end → reason.kind ∈ reasons ? │
|
|
357
|
+
│ ├─ 子代理会话?跳过 │
|
|
358
|
+
│ ├─ 读官方投影:cache / tps / title │
|
|
359
|
+
│ ├─ 按语言+模板构建通知(summary ≤120 字) │
|
|
360
|
+
│ └─ queueMicrotask 追加系统消息 │
|
|
361
|
+
│ (避开 append 重入窗口) │
|
|
362
|
+
│ │
|
|
363
|
+
│ settings.register → 官方「设置 → 插件」命名空间(失败退避重试) │
|
|
364
|
+
│ sessionProjections → 注册投影单元(key=session-complete-notify) │
|
|
365
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
366
|
+
│ user/message (source: plugin, form: notice)
|
|
367
|
+
▼ JSONL 持久化 + 投影推送
|
|
368
|
+
┌─────────────────── 客户端平面(lib/client.js,浏览器)──────────────┐
|
|
369
|
+
│ │
|
|
370
|
+
│ 会话列表订阅:running true → false 边沿 → pushCompletion │
|
|
371
|
+
│ ├─ 取正文:投影 → 事件窗口 notice → 降级(轮询 ≤6s) │
|
|
372
|
+
│ ├─ Web Notification(独立 tag,点击聚焦) │
|
|
373
|
+
│ └─ 页内 toast(永远展示,≤3 条,10s 自动消失) │
|
|
374
|
+
│ │
|
|
375
|
+
│ slots.inject('settings.plugin.item') → 设置卡片(预设/语言/模板) │
|
|
376
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 关键设计决策
|
|
380
|
+
|
|
381
|
+
- **不重放**:只处理实时事件,resume、replay 不会补发历史通知。
|
|
382
|
+
- **无自我循环**:插件追加 `user/message`,自身只监听 `turn/*`,事件类型不相交。
|
|
383
|
+
- **零外部 import**:插件从仓库目录以 realpath 加载,`@deepseek-ai/*` 无法裸解析 —— 宿主平面用 `createRequire` 锚定 profile 共享依赖枢纽(`.dsh/profiles/node_modules`)取 `schemastery`(设置 schema)与 `zod`(投影 schema);UserMessage 按 `dsh-llm` 契约手工构造(`id = crypto.randomUUID()`,deep-freeze 由 `session.append` 的 adopt 快照阶段完成)。
|
|
384
|
+
- **append 重入规避**:`session/event` 观察者回调运行在 `turn/end` 那次 append 的发布边界之内(dsh-session 在 dispatch 前置 `entry.appending`、`finally` 复位),同步 append 会被拒绝 —— 因此推迟到 `queueMicrotask`(微任务在本次同步栈含 `finally` 复位之后才执行)。
|
|
385
|
+
- **effect 纪律**:设置注册的退避重试定时器包装在 `ctx.effect()` 中并返回 `clearTimeout` disposer —— 插件在重试窗口内被卸载或热重载时定时器随 fiber 拆除,不会对已释放的 ctx 触发注册(极老环境无 `ctx.effect` API 时退化为裸定时器 + ctx 已拆除兜底捕获)。
|
|
386
|
+
- **HMR 安全**:`core.js` 导入带 `?v=1` 缓存破坏(HMR 重载按 URL 键控);设置注册遇到热重载竞态(duplicate)时自动退避重试(最多 8 次,间隔 `400ms × attempts`)。
|
|
387
|
+
- **投影注册双轨**:优先 `ctx.root.get('sessionProjections')`(最靠近宿主根的一份),拿不到时回退注入实例;只注册进注入实例时客户端可能读不到投影单元,推送正文走降级路径 —— 属尽力而为,不影响会话内系统消息。
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 项目结构
|
|
392
|
+
|
|
393
|
+
```text
|
|
394
|
+
dsh-session-notify/
|
|
395
|
+
├── lib/
|
|
396
|
+
│ ├── index.js # 宿主平面(Node):session/event 订阅 → 系统消息落盘;
|
|
397
|
+
│ │ # settings 命名空间注册(schemastery schema,退避重试);
|
|
398
|
+
│ │ # sessionProjections 投影单元(后台会话推送正文)
|
|
399
|
+
│ ├── core.js # 纯逻辑层(零依赖,可独立测试):轮次计时与用量聚合、
|
|
400
|
+
│ │ # 5 语言文案表、时长/用量/缓存/速度格式化、
|
|
401
|
+
│ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps})
|
|
402
|
+
│ └── client.js # 浏览器平面:完成推送(系统通知 + toast)、
|
|
403
|
+
│ # 设置卡片(Chip 模板编辑器 + 预设系统 + 实时预览)
|
|
404
|
+
├── scripts/
|
|
405
|
+
│ ├── build.sh # 零构建:仅 node --check 语法校验
|
|
406
|
+
│ ├── verify-notice.mjs # 校验会话日志落盘证据(zstd 多帧逐帧解压)
|
|
407
|
+
│ ├── probe-client.mjs # 探针:客户端装配
|
|
408
|
+
│ ├── probe-client-e2e.mjs # 探针:客户端端到端
|
|
409
|
+
│ ├── probe-card-render.mjs # 探针:设置卡片渲染
|
|
410
|
+
│ ├── probe-settings-card.mjs # 探针:设置面板卡片
|
|
411
|
+
│ ├── probe-settings-check.mjs# 探针:设置面板检查
|
|
412
|
+
│ └── probe-diag-settings.mjs # 探针:settings 诊断
|
|
413
|
+
├── cordis.patch.yml # dsh.bundle manifest —— dsh plugin add 自动挂载的凭证
|
|
414
|
+
├── package.json # dsh.bundle(patch)+ dsh.client(web 注入)双 manifest;
|
|
415
|
+
│ # exports: "." / "./client" / "./core"
|
|
416
|
+
├── LICENSE # MIT
|
|
417
|
+
└── README.md # 本文档
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## 开发与调试
|
|
423
|
+
|
|
424
|
+
语法校验(零构建,`prepublishOnly` 同款检查):
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
npm run build
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
发布(发布前自动执行 `prepublishOnly` 语法校验):
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npm publish --registry=https://registry.npmjs.org --access public
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
离线校验:解出会话日志中所有 plugin-source 事件与 `turn/end` 尾部序列(不传路径则自动选 `~/.dsh/sessions` 下最新会话):
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
node scripts/verify-notice.mjs <session.jsonl.zstd>
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### 调试入口
|
|
443
|
+
|
|
444
|
+
| 入口 | 内容 |
|
|
445
|
+
| --- | --- |
|
|
446
|
+
| `~/.dsh/session-complete-notify.log` | 宿主诊断日志:设置注册、重试与失败、投影注册、追加失败堆栈 |
|
|
447
|
+
| 浏览器 console `[dsh-session-notify-client]` | 客户端日志:权限状态、通知展示、设置保存 |
|
|
448
|
+
| `window.__dsch_notify_debug.readNotice(id)` | 手动读取指定会话的最新通知正文 |
|
|
449
|
+
| `window.__dsch_notify_debug.snapshotDebug(id)` | 会话尾部节点类型 + notice 数量 + 最近正文(前 200 字) |
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## 常见问题
|
|
454
|
+
|
|
455
|
+
<details>
|
|
456
|
+
<summary><b>npm install 之后为什么不自动挂载?</b></summary>
|
|
457
|
+
|
|
458
|
+
这是 DSH 官方设计:`npm install` 只把包装进依赖树,不注册插件。自动挂载的唯一途径是 `dsh plugin add` —— 它读取包内 `dsh.bundle` manifest(本插件自 0.1.3 起声明)并自动应用 `cordis.patch.yml`。参见[安装](#安装)。
|
|
459
|
+
|
|
460
|
+
</details>
|
|
461
|
+
|
|
462
|
+
<details>
|
|
463
|
+
<summary><b>为什么「中断」(interrupted)不提醒?</b></summary>
|
|
464
|
+
|
|
465
|
+
`interrupted` 是崩溃恢复后由持久化后端补写的孤儿轮次关闭标记,用户视角的「完成」不包含它(否则恢复会话会刷一屏误报)。确有需要可在宿主配置的 `reasons` 中加入。
|
|
466
|
+
|
|
467
|
+
</details>
|
|
468
|
+
|
|
469
|
+
<details>
|
|
470
|
+
<summary><b>后台会话(没打开窗口的)也会推送吗?</b></summary>
|
|
471
|
+
|
|
472
|
+
会。客户端从会话列表快照观测所有会话的 `running` 边沿;正文优先取宿主投影 —— 宿主为所有会话(含后台)维护投影单元,因此推送正文跨会话一致。投影不可用时降级为事件窗口或工作区信息。
|
|
473
|
+
|
|
474
|
+
</details>
|
|
475
|
+
|
|
476
|
+
<details>
|
|
477
|
+
<summary><b>保存设置后为什么提示刷新页面?</b></summary>
|
|
478
|
+
|
|
479
|
+
宿主在注册命名空间时读取一次设置,客户端 bundle 在页面加载时装配。保存后点「点击刷新」让两侧重新读取,新语言、模板即生效。
|
|
480
|
+
|
|
481
|
+
</details>
|
|
482
|
+
|
|
483
|
+
<details>
|
|
484
|
+
<summary><b>缓存命中率、速度数据从哪来?为什么有时是空的?</b></summary>
|
|
485
|
+
|
|
486
|
+
来自官方 `sessionProjections`(`tokenUsage`、`sessionStats`),与 dsh-web-ui 状态栏同口径。宿主读取投影快照失败或数据尚未就绪时,退回本地用量聚合估算,仍无数据则该项留空(标签插了也不显示)。另外,这两项只在自定义模板中通过 `{cache}`、`{tps}` 插入时才出现,默认文案不含。
|
|
487
|
+
|
|
488
|
+
</details>
|
|
489
|
+
|
|
490
|
+
<details>
|
|
491
|
+
<summary><b>通知正文里的错误信息太长、有换行怎么办?</b></summary>
|
|
492
|
+
|
|
493
|
+
摘要行(折叠行)与错误详情都会单行化并截断:摘要 120 字符、模板 `{error}` 80 字符、默认文案的错误详情 40 字符,超长以省略号结尾。
|
|
494
|
+
|
|
495
|
+
</details>
|
|
496
|
+
|
|
497
|
+
<details>
|
|
498
|
+
<summary><b>可以自定义系统通知的图标或声音吗?</b></summary>
|
|
499
|
+
|
|
500
|
+
当前版本使用浏览器默认通知样式,不注入自定义图标或声音,toast 为固定深色卡片。如需这些能力欢迎提 Issue 或 PR。
|
|
501
|
+
|
|
502
|
+
</details>
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## 更新日志
|
|
507
|
+
|
|
508
|
+
| 版本 | 日期 | 变更 |
|
|
509
|
+
| --- | --- | --- |
|
|
510
|
+
| **0.1.4** | 2026-08-28 | 补充完整卸载支持:`dispose` 生命周期收尾(host 置卸载标志抑制待追加微任务;client 清理正文轮询定时器、`__dsch_notify_debug` 钩子、toast 容器);卸载文档与 FAQ 同步 |
|
|
511
|
+
| **0.1.3** | 2026-08-28 | 声明官方 `dsh.bundle` manifest(`dsh plugin add` 一条命令自动挂载);settings 重试定时器改为 `ctx.effect()` 包装(Cordis effect 纪律);安装文档重排 |
|
|
512
|
+
| 0.1.2 | 2026-08-27 | 包更名至 `@telosmaylx` scope(npm 用户名作用域) |
|
|
513
|
+
| 0.1.1 | 2026-08-27 | GitHub、npm 安装方式文档化 |
|
|
514
|
+
| 0.1.0 | 2026-08-26 | 初始版本:会话内系统消息 + 浏览器推送 + 官方设置面板 |
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 贡献
|
|
519
|
+
|
|
520
|
+
欢迎 Issue 与 PR:
|
|
521
|
+
|
|
522
|
+
1. Fork 仓库并新建分支(`feat/xxx`)
|
|
523
|
+
2. 改动后运行 `npm run build` 做语法校验
|
|
524
|
+
3. 提交 PR,说明动机与验证方式
|
|
525
|
+
|
|
526
|
+
提交前请遵守 [Cordis 开发教程](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) 纪律:
|
|
527
|
+
|
|
528
|
+
- Cordis 之外的资源(定时器、订阅、watcher)必须包装在 `ctx.effect()` 中并返回 disposer;
|
|
529
|
+
- 配置项显式 `id` 防止编辑漂移;
|
|
530
|
+
- 插件须声明 `dsh.bundle` manifest 才能被 `dsh plugin add` 识别安装。
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 相关链接
|
|
535
|
+
|
|
536
|
+
- [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) —— DSH 插件精选列表(投稿规范:`dsh.bundle` 是安装唯一凭证)
|
|
537
|
+
- [Cordis 开发教程](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) —— 插件开发全流程(01-07 章)
|
|
538
|
+
- [npm 包主页](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
539
|
+
- [GitHub 仓库](https://github.com/TelosmaYLX/dsh-session-notify)
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## 许可证
|
|
544
|
+
|
|
545
|
+
[MIT](./LICENSE) © dsh-session-notify contributors
|