dsh-notify-me 1.1.5 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -2,11 +2,72 @@
2
2
 
3
3
  All notable changes to **dsh-notify-me** are documented here.
4
4
 
5
- ## [未发布]
5
+ ## [1.2.0] — 2026-09-30
6
+
7
+ ### 修复
8
+ - **`0.1.6-alpha.2` 及以后(含 `0.2.0-rc.2`)宿主上「模型需要你操作」整条通道再次静默失效**:上游把待办交互从 `uiSession.pendingInteractions`(`Map<sessionId, interaction>`)搬进了 `uiSession.sessionStatus`(`Map<sessionId, {running, pendingInteraction, completionUnread}>`),旧 store 在新宿主上根本不存在——插件读不到就静默不提醒(审批卡片出现、模型停下,系统通知/提示音/标题标记全无;而「回复完成」与设置页测试按钮照常,现象与 1.1.5 那次一模一样)。现按宿主代际自动选源:`≥ 0.1.6-alpha.2` 绑定 `sessionStatus`,`0.1.2 .. 0.1.5` 仍用 `pendingInteractions`,`≤ 0.1.1` 仍走控制器快照;`window.__dshNotifyMe.debug()` 新增 `uiSessionSource`,直接显示这条通道绑的是哪个来源。
9
+ - **`0.1.6` 及以后的宿主上认不出「当前会话」**:会话列表快照不再有 `current`(`SessionListState` 只剩 `ids / byId / phase / projectionsBySession`),于是当前会话面(face)订阅从未建立、`onListChanged()` 还把当前会话当成后台会话评估——后果是 1.1.7 的「当前对话不弹通知」规则失效(审批卡片就在屏幕上,Toast 仍会盖上去),标签页标记的归属也随之错位。现在按宿主自己的口径推导:有 `current` 用它,没有就取 `retainedBy.mainView > 0` 的那一行(与内置 `DocumentTitle` / `ui-workspace` / `ui-session` 的判定一致)。
10
+ - **`0.1.6` 及以后点通知切不回对应会话**:`sessions.open(id)` 已被移除,导航改由 `uiWorkspace.openSession(id)` 承担;插件现在两个都试(`open` 优先,旧宿主行为不变),会话已不在列表时依旧不调用。`debug()` 新增 `currentSession` 便于核对。
11
+
12
+ ### 新增
13
+ - `sessionStatus` 适配完整:同一会话里等待被替换成新 key 时旧的标题标记随之释放,等待消失(`pendingInteraction` 变 `null`)时按 key 释放标记,重复通知不重复提醒。
14
+
15
+ ### 变更
16
+ - `peerDependencies` 放宽为 `^0.1.0-rc.6 || ^0.1.1-rc.2 || ^0.1.2-rc.1 || ^0.1.5-rc.1 || >=0.1.6-alpha.2 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0`:原范围在 `0.2.0-rc.2` 上被 `evaluatePluginCompatibility` 判为不兼容,`dsh plugin add` 会拒绝并回滚安装(`0.3` 线仍被拒)。`dsh.compatibility.dshReleases` 增加 `0.2.0-rc.2: compatible`。
17
+ - 版本号 1.1.8 → 1.2.0(新宿主代际);`window.__dshNotifyMe.version` 同步。
18
+ - 设置页字典注册改为把 `locale.register()` 返回的 disposer 交给 `ctx.effect`(与官方 `ui-approval` 写法一致),客户端插件 HMR 重载时不会因「同名 namespace 已注册」而丢掉设置页。
19
+ - `uiSession` 绑定的诊断降噪:真实 `0.2.0-rc.2` 页面上每次加载都会出现的那条 `uiSession is provided but not reachable` 警告其实是**启动竞态**(本插件的客户端条目常早于 `ui-session` 条目激活,此时 `ctx.get()` 按 cordis 的严格语义拿不到尚未激活的服务),并非缺陷。现在只有**能拿到服务实例、但它既没有 `sessionStatus` 也没有 `pendingInteractions`** 时才打一条警告(并提示附上 `debug()` 输出);「还没可见」保持安静,由 1.5s 重试定时器接管,`debug().uiSessionNote` 仍可查。
20
+
21
+ ### 测试
22
+ - `smoke/smoke-test.cjs` 增加第三代宿主用例(`uiSession.sessionStatus`、列表无 `current`、无 `sessions.open`):审批/提问/方案确认文案与详情、去重、等待消失释放标记、挂载前已存在的等待提醒一次、当前对话静默 + 转后台补发、后台会话前台提醒、主开关、`retainedBy.mainView` 推导当前会话、点通知走 `uiWorkspace.openSession`、会话已消失不打开。
23
+ - `smoke/cordis-host-test.mjs` 增加真 cordis 的 `sessionStatus` 宿主(兄弟插件提供 `sessionStatus` 与 `uiWorkspace`,`sessions` 不带 `open`):断言订阅绑到 `sessionStatus`、审批只提醒一次且正文带工具名 + 理由、标题标记设置与释放、点通知调用 `uiWorkspace.openSession`。原有 `pendingInteractions` 宿主与无 `uiSession` 的旧宿主用例保留。
24
+ - 自检命令补 `DSH_CORDIS_PATH`:源码 checkout 里没有 profile 依赖时,用它把 `cordis-host-test.mjs` 指向 checkout 内的 `vendor/cordis/lib/index.js`(否则该用例按设计跳过)。
25
+
26
+ ### 已知限制
27
+ - `≥ 0.1.6`(含 `0.2.0-rc.2`)的「回复完成」提醒正文不再附带回复摘要:宿主快照不再提供 `nodes`,摘要取不到;提醒本身照常触发。
28
+
29
+ ## [1.1.8] — 2026-09-24
30
+
31
+ ### 新增
32
+ - **点通知直接回到那条对话**:系统通知现在带着它所属的会话 id,点一下就用 `sessions.open(id)` 把那个会话切成当前会话——以前只把 DSH 窗口带到前台,还得自己找是哪条在等。若该会话已经不在列表里则不调用(`open()` 对未知 id 会直接抛错),点击仍会把窗口带到前台。
33
+ - `onEvent` 回调的 payload 增加 `sessionId`。
34
+
35
+ ### 变更
36
+ - 设置项「点击通知把 DSH 切回前台」改为「点击通知回到对应对话」并补上说明;关掉后点击通知依然不做任何事。
37
+ - 顺手加固 `canToast()`:某些策略下 `Notification` 存在但读 `permission` 会抛错,原来会把整条投递链(连 `onEvent` 一起)打断,现在只在确认不了权限时按「不能弹」处理。
38
+ - 测试:两个套件各补「点击通知 → 宿主收到 `open(该会话)`」与「会话已消失 → 不调用 `open`」的用例。
39
+
40
+ ## [1.1.7] — 2026-09-24
41
+
42
+ ### 新增
43
+ - **当前对话的「需要你」提醒不再弹通知**(设置页新开关,默认开启):当等待就发生在你正看着的那个对话里、且页面在前台时,只保留标签页标记,不弹系统通知、不响提示音——审批卡片本来就在屏幕上,Toast 只会盖住它。页面一转到后台,被压下的那条提醒立刻补发;若在补发之前你已经处理掉了,它随之作废。
44
+ - `window.__dshNotifyMe.debug()` 增加 `currentHiddenOnly` 与 `quietedKeys`(当前被压下、等着补发的提醒 key)。
45
+
46
+ ### 变更
47
+ - 页面可见时**其它会话**(后台会话)的等待照常提醒,不受这条规则影响;把开关关掉即恢复 1.1.6 的行为。
48
+ - 测试:`smoke/smoke-test.cjs` 与 `smoke/cordis-host-test.mjs` 各补用例——「当前会话 + 前台 → 静默并排队」「转后台 → 补发一次」「当前会话 + 前台 + 已处理 → 不补发」「后台会话 + 前台 → 照常提醒」,并在两代宿主(`uiSession` 路径与 controller 路径)上都覆盖。
49
+
50
+ ## [1.1.6] — 2026-09-24
51
+
52
+ ### 修复
53
+ - **1.1.5 的「模型在等你操作」修复在真机上从未生效**(现象与 1.1.5 条目描述的一模一样:审批卡片出现、模型停下等待时,没有系统通知、没有提示音、标签页标题也不变;而「回复完成」提醒和设置页的测试按钮一切正常)。根因是**取服务的方式**:`bindUiSession()` 用 `rootCtx.uiSession` 属性读取,而 **cordis 4 只解析写在插件 `inject` 映射里的服务名**,其余名字一律抛 `cannot get property "uiSession" without inject`——同一 fiber 的兄弟条目(`dsh-client-ui-session`)提供的服务不会出现在祖先 store 里,属性查找必然落空;外层那个 `try/catch` 把这个异常吞成 `svc = null`,订阅永不建立,而 `0.1.2+` 的旧快照路径已经没有 `pending` 可用,待办提醒因此全程哑火。
54
+ - 改用 **`ctx.get("uiSession")`**:这是 cordis 明文的「无需 inject 读取服务」通道;`ctx.reflect.get()` 与属性读取保留为该 API 之前的兜底。服务实例被换掉时(插件 HMR)也会重新绑定,而不是抱着旧 store 的失效订阅。
55
+ - **仍然不把 `uiSession` 写进 `inject`**:那会让本插件在所有 `0.1.2-alpha.2` 之前的宿主上永远停在 `pending`(1.1.4 踩过的坑),而 cordis 的 `inject` 只有「必需」一种语义,没有可选依赖。
56
+
57
+ ### 新增
58
+ - **`window.__dshNotifyMe.debug()`**:输出当前绑定状态(`uiSession: bound|unbound`、失败原因 `uiSessionNote`、会话基线与待办基线数量、已提醒 key、通知权限、页面可见性、最后一次提醒时刻)。这条链路失败时原本没有任何可见信号,排查只能翻宿主源码——这正是本次要补上的东西。
59
+ - 服务「已被某个条目提供、却拿不到」时打一条 `console.warn`;旧宿主根本没有该服务属于正常情况,保持安静。
60
+
61
+ ### 变更
62
+ - 新增 `smoke/cordis-host-test.mjs`:用**真正的 cordis 4** 起根上下文、由兄弟插件提供 `uiSession`,把 `lib/client.js` 真身挂上去端到端验证。先断言宿主语义本身(属性读取必抛 `without inject`、`ctx.get()` 必能解析),再驱动一次 `approval` 交互,断言弹出提醒(标题「DSH · 审批请求」、正文带 `toolName · reason`)、标题标记、同 key 不重复、交互消失后释放标记;最后再跑一遍无 `uiSession` 的旧宿主路径。找不到本机 DSH 安装时自动跳过。
63
+ - `npm test` 现在依次跑 `smoke/smoke-test.cjs` 与 `smoke/cordis-host-test.mjs`。
64
+
65
+ ### 更正
66
+ - 1.1.5 条目里「本版改为订阅该 store」的结论不成立:订阅从未建立。1.1.5 的测试之所以通过,是因为 `smoke/smoke-test.cjs` 传入的是**手写假 ctx(普通对象)**,`ctx.uiSession` 属性读取不经过 cordis 的 inject 门控——假 ctx 上的成功掩盖了真宿主上的失败。教训:用真宿主语义的 ctx 跑插件真身,比手写桩更能说明问题。
6
67
 
7
68
  ### 文档
8
69
  - 新增 `docs/listing.md`:记录各插件目录/市场的收录方式(提交物、合并方式),以及本插件当前的收录状态;并记下 `awesome-dsh-plugin` 站点构建失败的排查入口(`build-site.yml` 最近一次运行 / issue #4731)。
9
- - issue #1 / #2 报告的「等待审批 / 提问 / 方案确认不提醒」查清并修复(见 1.1.5)。
70
+ - issue #1 / #2 报告的「等待审批 / 提问 / 方案确认不提醒」:1.1.5 判对了病因、开错了药(见上),1.1.6 才是真正生效的修复。
10
71
 
11
72
  ## [1.1.5] — 2026-09-14
12
73
 
package/README.en.md CHANGED
@@ -74,7 +74,15 @@ dsh plugin --profile web add dsh-notify-me
74
74
 
75
75
  Restart `dsh web`, then hard-refresh the page (Ctrl+Shift+R).
76
76
 
77
- **Compatibility** — verified to actually activate on `0.1.2-rc.1` and `0.1.5-rc.2` (each booted in its own profile: the Settings → Notifications section registers, i.e. the plugin's `apply` really runs), and previously on `0.1.1-rc.2`. Per-release declarations live in `package.json` under `dsh.compatibility.dshReleases`. If a plugin's client entry lists a package that a newer runtime no longer ships in `dsh.client.inject`, the entry parks at `pending (waiting for services: …)` and never runs — which is what broke 1.1.3 on `0.1.2-rc.1` and later.
77
+ **Compatibility** — the plugin picks its interaction source per host generation; per-release declarations live in `package.json` under `dsh.compatibility.dshReleases`:
78
+
79
+ | Host generation | "Needs you" interaction source | Current session | Toast-click navigation |
80
+ | --- | --- | --- | --- |
81
+ | `<= 0.1.1-rc.2` | controller snapshot `pending[]` | snapshot `current` | `sessions.open()` |
82
+ | `0.1.2-alpha.2 .. 0.1.5-rc.3` | `uiSession.pendingInteractions` | snapshot `current` | `sessions.open()` |
83
+ | `>= 0.1.6-alpha.2` (incl. `0.2.0-rc.2`) | `uiSession.sessionStatus` | `retainedBy.mainView` | `uiWorkspace.openSession()` |
84
+
85
+ Verified to actually activate on `0.1.2-rc.1`, `0.1.5-rc.2` and `0.2.0-rc.2` (each booted in its own profile: the Settings → Notifications section registers, i.e. the plugin's `apply` really runs), and previously on `0.1.1-rc.2`. Two traps are worth remembering: a package listed in `dsh.client.inject` that a newer runtime no longer ships parks the client entry at `pending (waiting for services: …)` forever — which is what broke 1.1.3 on `0.1.2-rc.1` and later; and when the host renames a store or a field nothing throws, alerts just go **silent** — `0.2.0-rc.2` moved `pendingInteractions`→`sessionStatus`, `list.current`→`retainedBy.mainView` and `sessions.open()`→`uiWorkspace.openSession()`. So every adaptation must confirm `window.__dshNotifyMe.debug()` reports `bound` and which source answered.
78
86
 
79
87
  **Verify** — open the DevTools console and run:
80
88
 
@@ -110,10 +118,11 @@ window.__dshNotifyMe.resetConfig() // restore defaults
110
118
 
111
119
  ## How it works
112
120
 
113
- The browser half subscribes to the client `sessions` service — the same source the UI itself renders from:
121
+ The reminder core picks its interaction source per host generation (see the table above); the current session always comes from the client `sessions` service:
114
122
 
115
- - the **selected session's** `ConversationSnapshot`: a `running: true → false` edge means a reply finished; a new entry in `pending[]` of kind `approval` / `plan-review` / `question` means the agent is waiting on you (payload text is shown in the alert when available);
116
- - **every other listed session's** summary: a new `pendingInteraction`, or the `completed` edge (finished while not selected), alerts you about background work.
123
+ - the **selected session's** `SessionSnapshot`: a `running: true → false` edge means a reply finished;
124
+ - **pending interactions**: a new key in `uiSession.sessionStatus` (`>= 0.1.6`, per-session `pendingInteraction`) or in `pendingInteractions` (`0.1.2 .. 0.1.5`) means the agent is waiting on you — approval requests carry the tool name and reason, questions their text; older hosts fall back to the controller snapshot's `pending[]`;
125
+ - **every other listed session's** summary: a `running: true → false` edge (or the legacy `completed` flag) alerts you about background work.
117
126
 
118
127
  The reminder core stays dependency-free and self-contained: notification copy resolves at alert time from the chosen language (follow-interface / Simplified Chinese / English). The Settings page is an **optional** React surface — it only registers into Settings when the web profile provides the `slots` / `locale` services and `react`; without them the plugin degrades gracefully to alerts-only (no Settings page).
119
128
 
@@ -124,16 +133,21 @@ The reminder core stays dependency-free and self-contained: notification copy re
124
133
  - The first sound/toast of each page load needs one user click on the page first (browser autoplay + permission policy).
125
134
  - Toasts are only produced once the notification permission is granted; declining means sound + title marker only.
126
135
  - Config lives in browser `localStorage`: switching browsers/devices or clearing site data returns to defaults (one-click restore in the Settings page).
136
+ - On `>= 0.1.6` (including `0.2.0-rc.2`) the "reply finished" toast body carries only the session label, no assistant snippet: host snapshots no longer expose `nodes`. The alert itself still fires.
137
+ - The title marker is written into `document.title` alongside the host: when the host recomputes the title it can overwrite the marker until the next alert event rewrites it.
127
138
 
128
139
  ## Development
129
140
 
130
141
  ```powershell
131
142
  node --check lib\client.js
132
143
  node --check lib\index.js
133
- node smoke\smoke-test.cjs # offline state-machine smoke test (master switch + zh/en language cases)
134
- npm pack --dry-run # preview the published tarball
144
+ node smoke\smoke-test.cjs # offline state-machine test: three host generations + master switch + zh/en cases
145
+ node smoke\cordis-host-test.mjs # real-cordis end-to-end (skips when no local DSH installation is found)
146
+ npm pack --dry-run # preview the published tarball
135
147
  ```
136
148
 
149
+ `cordis-host-test.mjs` borrows `@deepseek-ai/cordis` from a local DSH installation; in a source checkout with no profile dependencies, point it there with `DSH_CORDIS_PATH=<checkout>/vendor/cordis/lib/index.js`.
150
+
137
151
  ## License
138
152
 
139
153
  MIT — see [LICENSE](LICENSE).
package/README.md CHANGED
@@ -74,7 +74,15 @@ dsh plugin --profile web add dsh-notify-me
74
74
 
75
75
  重启 `dsh web` 后硬刷新页面(Ctrl+Shift+R)。
76
76
 
77
- **兼容性** — 实测**真正激活**(各自独立 profile 启动:设置里出现「通知提醒」分区,即插件的 `apply` 确实执行)于 `0.1.2-rc.1` 与 `0.1.5-rc.2`;更早的 `0.1.1-rc.2` 亦验证过。逐版本声明见 `package.json` 的 `dsh.compatibility.dshReleases`。注意一个坑:若 `dsh.client.inject` 里列了新版运行时已不再提供的包,客户端条目会停在 `pending (waiting for services: …)` 而永不执行——1.1.3 在 `0.1.2-rc.1` 及以后正是这样失效的。
77
+ **兼容性** — 插件按宿主代际自动选源,逐版本声明见 `package.json` 的 `dsh.compatibility.dshReleases`:
78
+
79
+ | 宿主代际 | 「需要你」交互来源 | 判断当前会话 | 点通知切会话 |
80
+ | --- | --- | --- | --- |
81
+ | `≤ 0.1.1-rc.2` | 控制器快照的 `pending[]` | 快照 `current` | `sessions.open()` |
82
+ | `0.1.2-alpha.2 .. 0.1.5-rc.3` | `uiSession.pendingInteractions` | 快照 `current` | `sessions.open()` |
83
+ | `≥ 0.1.6-alpha.2`(含 `0.2.0-rc.2`) | `uiSession.sessionStatus` | `retainedBy.mainView` | `uiWorkspace.openSession()` |
84
+
85
+ 实测**真正激活**(各自独立 profile 启动:设置里出现「通知提醒」分区,即插件的 `apply` 确实执行)于 `0.1.2-rc.1`、`0.1.5-rc.2` 与 `0.2.0-rc.2`;更早的 `0.1.1-rc.2` 亦验证过。两个坑值得记住:一是 `dsh.client.inject` 里列了新版运行时已不再提供的包,客户端条目会停在 `pending (waiting for services: …)` 而永不执行——1.1.3 在 `0.1.2-rc.1` 及以后正是这样失效的;二是宿主换存储/字段名时**不会报错**,提醒只会静默失效——`0.2.0-rc.2` 上 `pendingInteractions`→`sessionStatus`、`list.current`→`retainedBy.mainView`、`sessions.open()`→`uiWorkspace.openSession()` 都是这一类,所以每次适配都要用 `window.__dshNotifyMe.debug()` 确认真的是 `bound`、以及用的是哪个来源。
78
86
 
79
87
  **验证** — F12 控制台执行:
80
88
 
@@ -106,10 +114,11 @@ window.__dshNotifyMe.resetConfig() // 恢复默认
106
114
 
107
115
  ## 工作原理
108
116
 
109
- 浏览器半身订阅客户端 `sessions` 服务(与 UI 同一数据源):
117
+ 提醒核心按宿主代际取源(见上表),当前会话一律来自客户端 `sessions` 服务:
110
118
 
111
- - **当前会话** `ConversationSnapshot`:`running` true→false = 回复完成;`pending[]` 新增 `approval` / `plan-review` / `question` = 模型在等你(可行时在提醒里展示提问/审批内容);
112
- - **其它已列会话**摘要:出现新 `pendingInteraction`,或 `completed` 边沿(非选中状态下跑完)→ 后台工作提醒。
119
+ - **当前会话** `SessionSnapshot`:`running` true→false = 回复完成;
120
+ - **待办交互**:`uiSession` 的 `sessionStatus`(`≥ 0.1.6`,逐会话 `pendingInteraction`)或 `pendingInteractions`(`0.1.2 .. 0.1.5`)出现新 key = 模型在等你,容器里带上审批工具名 / 理由、提问文本;旧宿主回落到控制器快照的 `pending[]`;
121
+ - **其它已列会话**摘要:`running` true→false(或旧宿主的 `completed` 边沿)= 后台工作提醒。
113
122
 
114
123
  提醒核心仍然零第三方运行时依赖、完全自包含:通知文字按所选语言(跟随界面 / 中文 / English)即时解析。设置页是**可选**的 React 呈现层——当 DSH web profile 提供 `slots` / `locale` / `react` 时才注册进「设置 → 通知提醒」,缺任一能力时插件自动降级为纯提醒(无设置页),不影响功能。
115
124
 
@@ -120,16 +129,21 @@ window.__dshNotifyMe.resetConfig() // 恢复默认
120
129
  - 每次页面加载后第一次出声/弹通知前,需在页面上点击过一次(浏览器自动播放与权限策略)。
121
130
  - 未授权通知权限时只有提示音与标题标记。
122
131
  - 配置存在浏览器 `localStorage`:换浏览器/设备或清除站点数据后会回到默认值(设置页可一键恢复默认)。
132
+ - `≥ 0.1.6`(含 `0.2.0-rc.2`)的「回复完成」提醒正文只有会话名,不再附带回复摘要——宿主快照已不再提供 `nodes`;提醒本身照常触发。
133
+ - 标题标记由宿主与插件共同写 `document.title`:宿主重算标题时标记可能被覆盖,直到下一次提醒事件重新写成。
123
134
 
124
135
  ## 开发自检
125
136
 
126
137
  ```powershell
127
138
  node --check lib\client.js
128
139
  node --check lib\index.js
129
- node smoke\smoke-test.cjs # 离线状态机冒烟测试(含主开关与中英语言用例)
130
- npm pack --dry-run # 预览发布包
140
+ node smoke\smoke-test.cjs # 离线状态机冒烟测试:三代宿主 + 主开关 + 中英语言用例
141
+ node smoke\cordis-host-test.mjs # 真 cordis 端到端(找不到本机 DSH 时自动跳过)
142
+ npm pack --dry-run # 预览发布包
131
143
  ```
132
144
 
145
+ `cordis-host-test.mjs` 会从本机 DSH 安装借 `@deepseek-ai/cordis`;源码 checkout 里没有 profile 依赖时,用 `DSH_CORDIS_PATH=<checkout>/vendor/cordis/lib/index.js` 指定即可。
146
+
133
147
  ## License
134
148
 
135
149
  MIT — 见 [LICENSE](LICENSE)。