@telosmaylx/dsh-session-notify 0.1.8 → 0.1.9
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/README.md +583 -577
- package/lib/client.js +153 -46
- package/lib/core.js +72 -31
- package/lib/index.js +46 -10
- package/package.json +70 -70
package/README.md
CHANGED
|
@@ -1,577 +1,583 @@
|
|
|
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
|
-
-
|
|
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
|
-
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
|
284
|
-
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
>
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
| `{
|
|
329
|
-
| `{
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
│
|
|
372
|
-
│
|
|
373
|
-
│
|
|
374
|
-
│
|
|
375
|
-
│
|
|
376
|
-
│
|
|
377
|
-
│
|
|
378
|
-
│
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
│
|
|
384
|
-
|
|
385
|
-
│
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
│ │
|
|
389
|
-
│
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
│
|
|
416
|
-
│
|
|
417
|
-
│
|
|
418
|
-
├──
|
|
419
|
-
│
|
|
420
|
-
│
|
|
421
|
-
│
|
|
422
|
-
│
|
|
423
|
-
|
|
424
|
-
│ ├──
|
|
425
|
-
│ ├──
|
|
426
|
-
│
|
|
427
|
-
├──
|
|
428
|
-
├──
|
|
429
|
-
│
|
|
430
|
-
├──
|
|
431
|
-
└──
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
|
542
|
-
|
|
|
543
|
-
| **0.1.
|
|
544
|
-
| 0.1.
|
|
545
|
-
| 0.1.
|
|
546
|
-
| 0.1.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
[
|
|
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
|
+
> 默认文案在「会话」后内嵌会话标题标签(`{title}`);会话无标题时自动退回「会话已完成」。
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 通知行为
|
|
207
|
+
|
|
208
|
+
### 触发条件
|
|
209
|
+
|
|
210
|
+
每轮对话结束(`turn/end`)时按结束原因判断,命中白名单即提醒:
|
|
211
|
+
|
|
212
|
+
| 结束原因 | 含义 | 默认 |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| `completed` | 会话正常完成 | 提醒 |
|
|
215
|
+
| `aborted` | 会话中止 | 提醒 |
|
|
216
|
+
| `blocked` | 会话被阻塞 | 提醒 |
|
|
217
|
+
| `error` | 会话出错(附错误详情,超长截断) | 提醒 |
|
|
218
|
+
| `max-tokens` | 达到输出 token 上限 | 提醒 |
|
|
219
|
+
| `interrupted` | 中断(崩溃恢复后由持久化后端补写的孤儿轮次关闭标记) | 不提醒(可配置加入) |
|
|
220
|
+
|
|
221
|
+
**子代理会话默认跳过**(`header.origin === 'subagent'` 或 `delegationDepth > 0`)—— 子代理由父会话编排,逐轮提醒是噪音;可在宿主配置关闭跳过。
|
|
222
|
+
|
|
223
|
+
### 推送正文从哪来
|
|
224
|
+
|
|
225
|
+
客户端在会话列表观测到 `running: true → false` 边沿时推送,正文按以下优先级获取(最长轮询 6 秒,400ms 间隔):
|
|
226
|
+
|
|
227
|
+
1. **宿主投影**(key = `session-complete-notify`)—— 每个会话都有,后台会话同样拿到全文;
|
|
228
|
+
2. **会话事件窗口里的 notice 节点**(`kind=context` + `form=notice`)—— 正在查看的会话,落盘后立即可用;
|
|
229
|
+
3. **降级** —— 「详情见会话内系统消息」+ 工作区信息(`cwd` 最后一段)。
|
|
230
|
+
|
|
231
|
+
### 通知示例
|
|
232
|
+
|
|
233
|
+
以下均由 `lib/core.js` 的 `buildNotice` 实际生成。默认文案按结束原因**差异化表达**(非清一色句式):
|
|
234
|
+
|
|
235
|
+
简体中文默认文案:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
会话「重构登录模块」已完成(用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出)。 ← 完成:括号紧凑式 + 内嵌会话标题
|
|
239
|
+
会话「重构登录模块」已中止。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出。 ← 中止:句号拆句
|
|
240
|
+
会话「重构登录模块」被阻塞。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出。 ← 阻塞:句号拆句
|
|
241
|
+
会话「重构登录模块」达到输出上限。用时 3 分 25 秒,消耗 12,400 输入 / 35,600 输出,建议拆分任务后重试。 ← 上限:附建议
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
> 会话无标题(`titleValue` 为空)时自动退回不带标题的句式,如「会话已完成(用时 …)」。
|
|
245
|
+
|
|
246
|
+
出错时错误详情前置(单行化,超过 40 字符截断):
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
会话「重构登录模块」出错:connection timeout(用时 12 秒)。
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
English 默认文案(会话标题用双引号):
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
Session "重构登录模块" completed (took 3m25s, used 12,400 in / 35,600 out).
|
|
256
|
+
Session "重构登录模块" hit the output-token cap. Took 3m25s, used 12,400 in / 35,600 out — consider splitting the task.
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
自定义模板(在设置面板编辑,本例用到全部信息位):
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
{title} 干完了!用时 {duration},消耗 {usage},缓存命中 {cache},速度 {tps}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
渲染结果:
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
重构登录模块 干完了!用时 3 分 25 秒,消耗 103,600 输入 / 35,600 输出,缓存命中 96.5%,速度 92 tok/s
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
五种语言的同一事件:
|
|
272
|
+
|
|
273
|
+
```text
|
|
274
|
+
会话「重构登录模块」已完成(用时 3 分 25 秒,消耗 1,240 输入 / 3,560 输出)。
|
|
275
|
+
會話「重構登入模組」已完成(用時 3 分 25 秒,消耗 1,240 輸入 / 3,560 輸出)。
|
|
276
|
+
Session "重构登录模块" completed (took 3m25s, used 1,240 in / 3,560 out).
|
|
277
|
+
セッション「重构登录模块」完了(所要 3 分 25 秒、消費 1,240 入力 / 3,560 出力)。
|
|
278
|
+
세션「重构登录模块」 완료(소요 3분 25초, 소모 1,240 입력 / 3,560 출력)。
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 通知权限
|
|
282
|
+
|
|
283
|
+
| 权限状态 | 行为 |
|
|
284
|
+
| --- | --- |
|
|
285
|
+
| `default`(未决定) | 完成事件只发 toast;设置面板「通知权限」区提供「请求授权」按钮(**用户手势内请求**——Chromium 会忽略非手势的自动请求,因此插件不再自动请求) |
|
|
286
|
+
| `granted` | 按「推送方式」发系统通知(独立 tag,互不覆盖) |
|
|
287
|
+
| `denied`(被浏览器屏蔽) | 仅 toast;设置面板显示地址栏操作指引(权限图标 → 网站设置 → 通知 → 允许) |
|
|
288
|
+
| `undefined`(非安全上下文 / 不支持) | 仅 toast;建议改用「仅页内提示」 |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 配置
|
|
293
|
+
|
|
294
|
+
绝大多数配置在 **DSH Web UI → 设置 → 插件 → 会话完成提醒** 面板完成(保存后点「点击刷新」生效)。仅「触发原因白名单」与「跳过子代理」两项在宿主 `cordis.patch.yml` 的 `config` 中配置。
|
|
295
|
+
|
|
296
|
+
### 设置面板
|
|
297
|
+
|
|
298
|
+
面板在官方「设置 → 插件」面板中注册(`settings.plugin.item` keyed slot,key = `session-complete-notify`),样式逐值复刻原生插件卡片(12px 圆角、展开收起、旋转 chevron、footer 状态位 + 弃置 ghost + 主色保存按钮):
|
|
299
|
+
|
|
300
|
+
| 区域 | 内容 |
|
|
301
|
+
| --- | --- |
|
|
302
|
+
| 预设 | 下拉选择内置或自定义预设;「新增」把当前配置另存为自定义预设;当前预设可「删除」 |
|
|
303
|
+
| 语言 | 5 种语言单选,切换即时重渲染整个面板 |
|
|
304
|
+
| 推送方式 | 三选一:双通道(系统通知 + 页内提示,默认)/ 仅系统通知 / 仅页内提示 |
|
|
305
|
+
| 推送标题 | 所有原因共用的标题模板;留空时各原因用默认标题(完成=任务已完成、出错=任务出错、中止=任务已中止、阻塞=任务被阻塞、上限=任务达到输出上限);`{title}` 引用会话标题 |
|
|
306
|
+
| 模板 × 5 | 每条结束原因(完成、出错、中止、阻塞、输出上限)独立一个 Chip 编辑器:文字 + 内联信息胶囊,光标处插入、点击移除、实时预览 |
|
|
307
|
+
| 跳过子代理会话 | 复选框(保存时一并写入设置文档) |
|
|
308
|
+
| 通知权限 | 状态实时显示:已授权(绿)/ 尚未授权(附「请求授权」按钮)/ 已被浏览器屏蔽(附地址栏操作指引)/ 环境不支持 |
|
|
309
|
+
| 按原因定制标题 | 折叠区(默认收起):每个结束原因一个独立标题输入框,留空 = 用全局模板或语言默认标题 |
|
|
310
|
+
| 保存 | 写入宿主设置文档(`language` / `templates` / `titleTemplate` / `titleTemplates` / `pushMode`);保存后显示「点击刷新」链接 |
|
|
311
|
+
| 重置 | 一键还原默认值(**语言保留当前选择**,标题/模板/推送方式恢复默认)并立即保存 |
|
|
312
|
+
|
|
313
|
+
> [!NOTE]
|
|
314
|
+
> 「推送方式」的取舍:`dual`(默认)同时弹 Windows 系统通知与页内 toast,toast 是保底通道,防止系统通知被平台静默(专注助手、通知横幅关闭)。但 QQ 浏览器等 Chromium 壳浏览器会把 `Notification` 渲染成页内横幅而非 Windows 通知中心——此时 `dual` 会造成页内两个提示。这类浏览器请选「仅页内提示」(不再调用 `Notification`,页内只有一个小 toast)。
|
|
315
|
+
|
|
316
|
+
> [!NOTE]
|
|
317
|
+
> 系统通知(`Notification` API)能否弹出由**浏览器与站点访问方式**共同决定:Edge/Chrome 对"不熟悉"的站点会**自动屏蔽通知**(地址栏出现「通知已屏蔽」)——点击地址栏左侧权限图标 → 网站设置 → 通知 → 允许即可恢复;`http://IP` 这类非安全上下文访问时 `Notification` 根本不存在,请改用「仅页内提示」。设置面板「通知权限」区域会实时显示当前状态并给出对应操作指引(可一键请求授权)。Firefox 窗口聚焦时通知显示为页内横幅、失焦才进系统通知中心。
|
|
318
|
+
|
|
319
|
+
> [!NOTE]
|
|
320
|
+
> 面板中「跳过子代理会话」保存的是设置文档里的布尔值;宿主 `cordis.patch.yml` 的 `config.skipSubagents` 是其启动默认值,两者任一为真即跳过。
|
|
321
|
+
|
|
322
|
+
### 文案模板与占位符
|
|
323
|
+
|
|
324
|
+
每条结束原因独立一个模板输入框,**标签即开关** —— 在模板里插入对应信息标签,该项数据才会显示:
|
|
325
|
+
|
|
326
|
+
| 占位符 | 含义 | 示例值 |
|
|
327
|
+
| --- | --- | --- |
|
|
328
|
+
| `{title}` | 会话标题(推送标题模板也可用) | `重构登录模块` |
|
|
329
|
+
| `{duration}` | 本轮用时(`turn/start` 起表 → `turn/end` 结束) | `3 分 25 秒` / `3m25s` |
|
|
330
|
+
| `{usage}` | token 消耗(输入 = 未缓存 + 缓存读 + 缓存写) | `1,240 输入 / 3,560 输出` |
|
|
331
|
+
| `{error}` | 错误信息(无错误时显示 `none`;单行化,80 字符截断) | `connection timeout` |
|
|
332
|
+
| `{cache}` | 缓存命中率(官方投影口径,无数据为空) | `96.5%` |
|
|
333
|
+
| `{tps}` | 生成速度(官方投影口径,无数据为空) | `92 tok/s` |
|
|
334
|
+
| `{label}` | 已废弃 —— 渲染时自动剥除,旧模板仍兼容(插入菜单已移除该选项) | — |
|
|
335
|
+
|
|
336
|
+
模板留空即使用内置默认文案(自动带用时与消耗)。折叠行 `summary` 与正文同源(渲染结果截断至 120 字符)—— 只看折叠行的用户也能看到真实标题与用时、消耗。
|
|
337
|
+
|
|
338
|
+
### 预设系统
|
|
339
|
+
|
|
340
|
+
- **内置预设**:仅「默认」,作为基线。
|
|
341
|
+
- **自定义预设**:保存在 `localStorage`(key = `dsh-scn-custom-presets`):
|
|
342
|
+
- 「新增」命名后保存为自定义预设;保存后可「修改」自动同步、「删除」移除;
|
|
343
|
+
- **自动编号的未命名预设**:从「默认 / 空白」直接保存时,自动生成 `未命名`、`未命名 2`、`未命名 3`…(编号取当前最大值 + 1);
|
|
344
|
+
- 表单显示「来自:xxx · 已修改」来源指示(来自预设但内容已改动时)。
|
|
345
|
+
- **保存即同步**:保存时若表单来源是自定义预设则更新该预设,否则新建或继续编号未命名预设。
|
|
346
|
+
|
|
347
|
+
### 宿主配置项
|
|
348
|
+
|
|
349
|
+
```yaml
|
|
350
|
+
- insert:
|
|
351
|
+
- id: dsh-session-notify
|
|
352
|
+
name: '@telosmaylx/dsh-session-notify'
|
|
353
|
+
config:
|
|
354
|
+
reasons: [completed, aborted, blocked, error, max-tokens]
|
|
355
|
+
skipSubagents: true
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
359
|
+
| --- | --- | --- | --- |
|
|
360
|
+
| `reasons` | `string[]` | `[completed, aborted, blocked, error, max-tokens]` | 触发提醒的 `turn/end` 原因白名单 |
|
|
361
|
+
| `skipSubagents` | `boolean` | `true` | 跳过子代理会话(`origin=subagent` 或 `delegationDepth>0`) |
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## 工作原理
|
|
366
|
+
|
|
367
|
+
插件分**宿主平面**(Node)与**客户端平面**(浏览器),中间靠会话日志(JSONL)与官方会话投影衔接:
|
|
368
|
+
|
|
369
|
+
```text
|
|
370
|
+
┌─────────────────── 宿主平面(lib/index.js,Node)──────────────────┐
|
|
371
|
+
│ │
|
|
372
|
+
│ session/event 火线 │
|
|
373
|
+
│ ├─ turn/start → tracker 起表(key: sessionId:turn) │
|
|
374
|
+
│ ├─ assistant/message → 累加该轮 token 用量 │
|
|
375
|
+
│ └─ turn/end → reason.kind ∈ reasons ? │
|
|
376
|
+
│ ├─ 子代理会话?跳过 │
|
|
377
|
+
│ ├─ 读官方投影:cache / tps / title │
|
|
378
|
+
│ ├─ 按语言+模板构建通知(summary ≤120 字) │
|
|
379
|
+
│ └─ queueMicrotask 追加系统消息 │
|
|
380
|
+
│ (避开 append 重入窗口) │
|
|
381
|
+
│ │
|
|
382
|
+
│ settings.register → 官方「设置 → 插件」命名空间(失败退避重试) │
|
|
383
|
+
│ sessionProjections → 注册投影单元(key=session-complete-notify) │
|
|
384
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
385
|
+
│ user/message (source: plugin, form: notice)
|
|
386
|
+
▼ JSONL 持久化 + 投影推送
|
|
387
|
+
┌─────────────────── 客户端平面(lib/client.js,浏览器)──────────────┐
|
|
388
|
+
│ │
|
|
389
|
+
│ 会话列表订阅:running true → false 边沿 → pushCompletion │
|
|
390
|
+
│ ├─ 取正文:投影 → 事件窗口 notice → 降级(轮询 ≤6s) │
|
|
391
|
+
│ ├─ Web Notification(独立 tag,点击聚焦) │
|
|
392
|
+
│ └─ 页内 toast(永远展示,≤3 条,10s 自动消失) │
|
|
393
|
+
│ │
|
|
394
|
+
│ slots.inject('settings.plugin.item') → 设置卡片(预设/语言/模板) │
|
|
395
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 关键设计决策
|
|
399
|
+
|
|
400
|
+
- **不重放**:只处理实时事件,resume、replay 不会补发历史通知。
|
|
401
|
+
- **无自我循环**:插件追加 `user/message`,自身只监听 `turn/*`,事件类型不相交。
|
|
402
|
+
- **零外部 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 快照阶段完成)。
|
|
403
|
+
- **append 重入规避**:`session/event` 观察者回调运行在 `turn/end` 那次 append 的发布边界之内(dsh-session 在 dispatch 前置 `entry.appending`、`finally` 复位),同步 append 会被拒绝 —— 因此推迟到 `queueMicrotask`(微任务在本次同步栈含 `finally` 复位之后才执行)。
|
|
404
|
+
- **effect 纪律**:设置注册的退避重试定时器包装在 `ctx.effect()` 中并返回 `clearTimeout` disposer —— 插件在重试窗口内被卸载或热重载时定时器随 fiber 拆除,不会对已释放的 ctx 触发注册(极老环境无 `ctx.effect` API 时退化为裸定时器 + ctx 已拆除兜底捕获)。
|
|
405
|
+
- **HMR 安全**:`core.js` 导入带 `?v=1` 缓存破坏(HMR 重载按 URL 键控);设置注册遇到热重载竞态(duplicate)时自动退避重试(最多 8 次,间隔 `400ms × attempts`)。
|
|
406
|
+
- **投影注册双轨**:优先 `ctx.root.get('sessionProjections')`(最靠近宿主根的一份),拿不到时回退注入实例;只注册进注入实例时客户端可能读不到投影单元,推送正文走降级路径 —— 属尽力而为,不影响会话内系统消息。
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## 项目结构
|
|
411
|
+
|
|
412
|
+
```text
|
|
413
|
+
dsh-session-notify/
|
|
414
|
+
├── lib/
|
|
415
|
+
│ ├── index.js # 宿主平面(Node):session/event 订阅 → 系统消息落盘;
|
|
416
|
+
│ │ # settings 命名空间注册(schemastery schema,退避重试);
|
|
417
|
+
│ │ # sessionProjections 投影单元(后台会话推送正文)
|
|
418
|
+
│ ├── core.js # 纯逻辑层(零依赖,可独立测试):轮次计时与用量聚合、
|
|
419
|
+
│ │ # 5 语言文案表、时长/用量/缓存/速度格式化、
|
|
420
|
+
│ │ # 模板渲染({title}{duration}{usage}{error}{cache}{tps})
|
|
421
|
+
│ └── client.js # 浏览器平面:完成推送(系统通知 + toast)、
|
|
422
|
+
│ # 设置卡片(Chip 模板编辑器 + 预设系统 + 实时预览)
|
|
423
|
+
├── scripts/
|
|
424
|
+
│ ├── build.sh # 零构建:仅 node --check 语法校验
|
|
425
|
+
│ ├── verify-notice.mjs # 校验会话日志落盘证据(zstd 多帧逐帧解压)
|
|
426
|
+
│ ├── probe-client.mjs # 探针:客户端装配
|
|
427
|
+
│ ├── probe-client-e2e.mjs # 探针:客户端端到端
|
|
428
|
+
│ ├── probe-card-render.mjs # 探针:设置卡片渲染
|
|
429
|
+
│ ├── probe-settings-card.mjs # 探针:设置面板卡片
|
|
430
|
+
│ ├── probe-settings-check.mjs# 探针:设置面板检查
|
|
431
|
+
│ └── probe-diag-settings.mjs # 探针:settings 诊断
|
|
432
|
+
├── cordis.patch.yml # dsh.bundle manifest —— dsh plugin add 自动挂载的凭证
|
|
433
|
+
├── package.json # dsh.bundle(patch)+ dsh.client(web 注入)双 manifest;
|
|
434
|
+
│ # exports: "." / "./client" / "./core"
|
|
435
|
+
├── LICENSE # MIT
|
|
436
|
+
└── README.md # 本文档
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 开发与调试
|
|
442
|
+
|
|
443
|
+
语法校验(零构建,`prepublishOnly` 同款检查):
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
npm run build
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
发布(发布前自动执行 `prepublishOnly` 语法校验):
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
npm publish --registry=https://registry.npmjs.org --access public
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
离线校验:解出会话日志中所有 plugin-source 事件与 `turn/end` 尾部序列(不传路径则自动选 `~/.dsh/sessions` 下最新会话):
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
node scripts/verify-notice.mjs <session.jsonl.zstd>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### 调试入口
|
|
462
|
+
|
|
463
|
+
| 入口 | 内容 |
|
|
464
|
+
| --- | --- |
|
|
465
|
+
| `~/.dsh/session-complete-notify.log` | 宿主诊断日志:设置注册、重试与失败、投影注册、追加失败堆栈 |
|
|
466
|
+
| 浏览器 console `[dsh-session-notify-client]` | 客户端日志:权限状态、通知展示、设置保存 |
|
|
467
|
+
| `window.__dsch_notify_debug.readNotice(id)` | 手动读取指定会话的最新通知正文 |
|
|
468
|
+
| `window.__dsch_notify_debug.snapshotDebug(id)` | 会话尾部节点类型 + notice 数量 + 最近正文(前 200 字) |
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 常见问题
|
|
473
|
+
|
|
474
|
+
<details>
|
|
475
|
+
<summary><b>npm install 之后为什么不自动挂载?</b></summary>
|
|
476
|
+
|
|
477
|
+
这是 DSH 官方设计:`npm install` 只把包装进依赖树,不注册插件。自动挂载的唯一途径是 `dsh plugin add` —— 它读取包内 `dsh.bundle` manifest(本插件自 0.1.3 起声明)并自动应用 `cordis.patch.yml`。参见[安装](#安装)。
|
|
478
|
+
|
|
479
|
+
</details>
|
|
480
|
+
|
|
481
|
+
<details>
|
|
482
|
+
<summary><b>为什么「中断」(interrupted)不提醒?</b></summary>
|
|
483
|
+
|
|
484
|
+
`interrupted` 是崩溃恢复后由持久化后端补写的孤儿轮次关闭标记,用户视角的「完成」不包含它(否则恢复会话会刷一屏误报)。确有需要可在宿主配置的 `reasons` 中加入。
|
|
485
|
+
|
|
486
|
+
</details>
|
|
487
|
+
|
|
488
|
+
<details>
|
|
489
|
+
<summary><b>后台会话(没打开窗口的)也会推送吗?</b></summary>
|
|
490
|
+
|
|
491
|
+
会。客户端从会话列表快照观测所有会话的 `running` 边沿;正文优先取宿主投影 —— 宿主为所有会话(含后台)维护投影单元,因此推送正文跨会话一致。投影不可用时降级为事件窗口或工作区信息。
|
|
492
|
+
|
|
493
|
+
</details>
|
|
494
|
+
|
|
495
|
+
<details>
|
|
496
|
+
<summary><b>保存设置后为什么提示刷新页面?</b></summary>
|
|
497
|
+
|
|
498
|
+
宿主在注册命名空间时读取一次设置,客户端 bundle 在页面加载时装配。保存后点「点击刷新」让两侧重新读取,新语言、模板即生效。
|
|
499
|
+
|
|
500
|
+
</details>
|
|
501
|
+
|
|
502
|
+
<details>
|
|
503
|
+
<summary><b>缓存命中率、速度数据从哪来?为什么有时是空的?</b></summary>
|
|
504
|
+
|
|
505
|
+
来自官方 `sessionProjections`(`tokenUsage`、`sessionStats`),与 dsh-web-ui 状态栏同口径。宿主读取投影快照失败或数据尚未就绪时,退回本地用量聚合估算,仍无数据则该项留空(标签插了也不显示)。另外,这两项只在自定义模板中通过 `{cache}`、`{tps}` 插入时才出现,默认文案不含。
|
|
506
|
+
|
|
507
|
+
</details>
|
|
508
|
+
|
|
509
|
+
<details>
|
|
510
|
+
<summary><b>通知正文里的错误信息太长、有换行怎么办?</b></summary>
|
|
511
|
+
|
|
512
|
+
摘要行(折叠行)与错误详情都会单行化并截断:摘要 120 字符、模板 `{error}` 80 字符、默认文案的错误详情 40 字符,超长以省略号结尾。
|
|
513
|
+
|
|
514
|
+
</details>
|
|
515
|
+
|
|
516
|
+
<details>
|
|
517
|
+
<summary><b>可以自定义系统通知的图标或声音吗?</b></summary>
|
|
518
|
+
|
|
519
|
+
当前版本使用浏览器默认通知样式,不注入自定义图标或声音,toast 为固定深色卡片。如需这些能力欢迎提 Issue 或 PR。
|
|
520
|
+
|
|
521
|
+
</details>
|
|
522
|
+
|
|
523
|
+
<details>
|
|
524
|
+
<summary><b>为什么 Edge 推不了系统通知?QQ 浏览器为什么只有页内横幅?</b></summary>
|
|
525
|
+
|
|
526
|
+
两者都是浏览器行为,插件无法强制:
|
|
527
|
+
|
|
528
|
+
- **Edge / Chrome**:对"不熟悉"的站点会**自动屏蔽通知**(地址栏出现「通知已屏蔽」)。点击地址栏左侧权限图标 → 网站设置 → 通知 → 允许即可恢复,之后正常弹 Windows 通知中心。也可在浏览器通知设置中关闭「自动屏蔽」。
|
|
529
|
+
- **QQ 浏览器等国产 Chromium 壳**:把 `Notification` 固定渲染为页内横幅,不经过 Windows 通知中心,且无系统通知选项——请用「推送方式 = 仅页内提示」,页内只有一个插件自带的小 toast。
|
|
530
|
+
- **Firefox**:窗口聚焦时通知显示为页内横幅,失焦/最小化才进系统通知中心;权限需在地址栏手动允许。
|
|
531
|
+
- 另注意:`http://IP` 访问(非安全上下文)时 `Notification` 不存在,任何浏览器都弹不了系统通知。
|
|
532
|
+
|
|
533
|
+
设置面板「通知权限」区域会实时显示当前状态与对应操作指引。
|
|
534
|
+
|
|
535
|
+
</details>
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## 更新日志
|
|
540
|
+
|
|
541
|
+
| 版本 | 日期 | 变更 |
|
|
542
|
+
| --- | --- | --- |
|
|
543
|
+
| **0.1.9** | 2026-08-29 | 推送标题支持**按原因定制**(折叠区 UI,默认收起不臃肿;留空时各原因用差异化默认标题:任务已完成/任务出错/任务已中止/任务被阻塞/任务达到输出上限,5 语言);投影升级为对象(kind/text/title)承载 host 渲染好的标题;重置按钮**保留当前语言**;默认文案内嵌「会话标题」标签(会话「{title}」已完成,无标题自动回退);设置面板模板预览同步;「+插入信息」插入标签后不再自动折叠;删除当前使用的自定义预设自动切回默认;模板预览修复(点击不消失、输入才隐藏、清空恢复);每个原因新增「发送」按钮(一键发当前模板渲染的测试通知) |
|
|
544
|
+
| **0.1.8** | 2026-08-29 | 默认推送标题改为「任务已完成」(`{title}` 仍可引用会话标题);默认文案按结束原因差异化表达(完成紧凑括号式 / 中止·阻塞拆句 / 出错错误前置 / 上限附建议,5 语言);设置面板新增「重置」按钮一键还原默认 |
|
|
545
|
+
| **0.1.7** | 2026-08-29 | 修复 0.1.6 的设置卡片崩溃:`notificationPermissionRow`/`requestPermissionNow` 曾引用 Card 组件内 state(作用域外)导致渲染 ReferenceError、整个设置卡片消失;改为自包含 + 回调传参 |
|
|
546
|
+
| **0.1.6** | 2026-08-29 | 设置面板新增「通知权限」状态区(授权状态实时显示 + 一键请求授权按钮 + 被屏蔽时的地址栏操作指引);授权改为**用户手势内请求**(Chromium 忽略非手势自动请求,Edge 对不熟悉站点自动屏蔽通知的典型场景得以解决);FAQ 新增浏览器差异说明 |
|
|
547
|
+
| **0.1.5** | 2026-08-29 | 新增「推送方式」设置(双通道 / 仅系统通知 / 仅页内提示):解决 QQ 浏览器等 Chromium 壳把 `Notification` 渲染成页内横幅导致的双提示;`pushMode` 加入设置 schema 与设置面板 |
|
|
548
|
+
| **0.1.4** | 2026-08-28 | 补充完整卸载支持:`dispose` 生命周期收尾(host 置卸载标志抑制待追加微任务;client 清理正文轮询定时器、`__dsch_notify_debug` 钩子、toast 容器);卸载文档与 FAQ 同步 |
|
|
549
|
+
| **0.1.3** | 2026-08-28 | 声明官方 `dsh.bundle` manifest(`dsh plugin add` 一条命令自动挂载);settings 重试定时器改为 `ctx.effect()` 包装(Cordis effect 纪律);安装文档重排 |
|
|
550
|
+
| 0.1.2 | 2026-08-27 | 包更名至 `@telosmaylx` scope(npm 用户名作用域) |
|
|
551
|
+
| 0.1.1 | 2026-08-27 | GitHub、npm 安装方式文档化 |
|
|
552
|
+
| 0.1.0 | 2026-08-26 | 初始版本:会话内系统消息 + 浏览器推送 + 官方设置面板 |
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## 贡献
|
|
557
|
+
|
|
558
|
+
欢迎 Issue 与 PR:
|
|
559
|
+
|
|
560
|
+
1. Fork 仓库并新建分支(`feat/xxx`)
|
|
561
|
+
2. 改动后运行 `npm run build` 做语法校验
|
|
562
|
+
3. 提交 PR,说明动机与验证方式
|
|
563
|
+
|
|
564
|
+
提交前请遵守 [Cordis 开发教程](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) 纪律:
|
|
565
|
+
|
|
566
|
+
- Cordis 之外的资源(定时器、订阅、watcher)必须包装在 `ctx.effect()` 中并返回 disposer;
|
|
567
|
+
- 配置项显式 `id` 防止编辑漂移;
|
|
568
|
+
- 插件须声明 `dsh.bundle` manifest 才能被 `dsh plugin add` 识别安装。
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## 相关链接
|
|
573
|
+
|
|
574
|
+
- [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) —— DSH 插件精选列表(投稿规范:`dsh.bundle` 是安装唯一凭证)
|
|
575
|
+
- [Cordis 开发教程](https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial) —— 插件开发全流程(01-07 章)
|
|
576
|
+
- [npm 包主页](https://www.npmjs.com/package/@telosmaylx/dsh-session-notify)
|
|
577
|
+
- [GitHub 仓库](https://github.com/TelosmaYLX/dsh-session-notify)
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## 许可证
|
|
582
|
+
|
|
583
|
+
[MIT](./LICENSE) © dsh-session-notify contributors
|