dsh-notify-me 1.3.0 → 1.4.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,6 +2,31 @@
2
2
 
3
3
  All notable changes to **dsh-notify-me** are documented here.
4
4
 
5
+ ## [1.4.0] — 2026-10-03
6
+
7
+ ### 新增
8
+ - **子代理会话不提醒**(设置页新开关「子代理会话不提醒」,默认开启):DSH 把每个子代理(subagent)子会话列成独立会话行(`origin: 'subagent'` 加 `parentId`),于是这些子会话的「需要你」与「回复完成」都会照常弹通知——可它们是所属主对话那一轮里的步骤,提醒只是噪音。现在默认静音**后台**子代理会话:列表行评估与 `sessionStatus` 等待通道都跳过「带 `origin: 'subagent'` 且不是当前会话」的行;你正打开着的那个子代理会话不算静音对象,照常提醒(走当前对话的常规规则:页面可见时默认只留标题标记、转后台补发)。主对话自己的提醒不受影响。
9
+ - 判定认 `origin` 加「不是当前会话」两条:只有 `parentId`、没有 `origin` 的分支(fork)会话仍是你自己的对话,照常提醒;静音随你打开的会话实时变化——切进某个后台子代理会话,它积压的等待按当前对话的规则补上,切走后重新回到静音范围(标题标记与排队中的通知一并撤下)。静音开关读的是实时配置,切换后下一条事件即生效,不需要重载页面。
10
+ - `window.__dshNotifyMe` 配置新增 `ignoreSubagent`(默认 `true`,可用 `setConfig` / `resetConfig` 调整),`debug()` 新增同名字段便于核对。
11
+
12
+ ### 变更
13
+ - 版本号 1.3.1 → 1.4.0;`window.__dshNotifyMe.version` 同步。
14
+ - README(中英)补上设置页新开关、提醒时机说明、控制台配置示例与工作原理里的子代理口径。
15
+ - README(中英)新增「致谢与贡献须知 / Credits & how contributions land」:外部 PR 的署名口径与贡献者名单;「只静音后台子代理、当前打开的照常提醒」这一口径来自 [@d0ublecl1ck](https://github.com/d0ublecl1ck) 在 [#8](https://github.com/chromoany/dsh-notify-me/pull/8) 讨论里的建议(收编进 main 的原版是整条静音),本次按该口径改准并由维护者落地。
16
+
17
+ ### 测试
18
+ - `smoke/smoke-test.cjs` 新增子代理用例:`ignoreSubagent` 默认值与 `debug()` 字段、后台子代理完成不提醒而普通后台会话照常、后台子代理等待不提醒也不打标题标记、分支(fork)会话仍提醒、关掉开关后子代理的等待与完成重新提醒,以及「当前打开的子代理会话照常提醒」——等待与完成都提醒;后台积压的等待在切进去时按当前对话的规则排队补发,切走则连标题标记与排队通知一起撤下。
19
+
20
+ ## [1.3.1] — 2026-10-03
21
+
22
+ ### 文档
23
+ - README 中英补齐 1.1.7 / 1.1.8 起就有、但一直没写进文档的两个设置项:「当前对话不弹通知」(`currentHiddenOnly`)与「点击通知回到对应对话」(`autoFocus`),控制台配置示例同步补 `currentHiddenOnly`;「提醒时机」表的默认口径改为「页面可见也提醒(当前对话除外:默认只留标题标记)」——原表述把当前对话的静默规则漏掉了。
24
+ - 措辞去掉 Windows 专属口径:系统通知按「经浏览器弹出,进 Windows 通知中心或 macOS 通知中心」描述,权限提示补 macOS 的 系统设置 → 通知;已知限制新增 macOS 一条——浏览器路径无平台假设,但 DSH Desktop(Electron)的系统通知要求应用已签名(官方 macOS 版是否满足未实测),审批按钮在 macOS 与桌面宿主上的渲染同样未实测,不渲染时自动退回无按钮通知。
25
+ - 已知限制补「当前对话不弹通知」按 `document.hidden` 判定的边界(窗口被别的应用完全盖住仍算可见);开发自检一节补 `cordis-host-test.mjs` 的 cordis 探测路径(含 `~/.dsh/profiles/node_modules` 平铺回退目录)。
26
+
27
+ ### 变更
28
+ - 版本号 1.3.0 → 1.3.1;`window.__dshNotifyMe.version` 同步。功能零改动。
29
+
5
30
  ## [1.3.0] — 2026-10-03
6
31
 
7
32
  ### 新增
package/README.en.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  ---
13
13
 
14
- **Step away from the DeepSeek Harness web UI — and still know what's going on.** When the agent stops and needs your input, or when a reply finishes in the background while you're in another app, dsh-notify-me alerts you with a Windows desktop notification, a sound, and a tab-title marker. Approval notifications even carry Approve / Reject buttons, so you can settle the request without switching back. Toggle it and pick the notification language right from **Settings → Notify me**.
14
+ **Step away from the DeepSeek Harness web UI — and still know what's going on.** When the agent stops and needs your input, or when a reply finishes in the background while you're in another app, dsh-notify-me alerts you with a desktop notification, a sound, and a tab-title marker. Approval notifications even carry Approve / Reject buttons, so you can settle the request without switching back. Toggle it and pick the notification language right from **Settings → Notify me**.
15
15
 
16
16
  ---
17
17
 
@@ -32,7 +32,7 @@ dsh-notify-me decouples **supervision** from **sitting in front of the screen**:
32
32
 
33
33
  ## UI Preview
34
34
 
35
- | Windows system notification |
35
+ | System notification (screenshot from Windows) |
36
36
  | --- |
37
37
  | ![DSH notification](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/notify-toast.png) |
38
38
 
@@ -42,9 +42,11 @@ dsh-notify-me decouples **supervision** from **sitting in front of the screen**:
42
42
 
43
43
  | When | What you get | Default |
44
44
  | --- | --- | --- |
45
- | 🔔 **The agent needs your input** — sandbox approval / plan review / a question (`ask_user_question`) | Toast + sound + `🔔 Action needed ·` title marker; approval toasts also carry Approve / Reject buttons | Alerts even while the page is visible |
45
+ | 🔔 **The agent needs your input** — sandbox approval / plan review / a question (`ask_user_question`) | Toast + sound + `🔔 Action needed ·` title marker; approval toasts also carry Approve / Reject buttons | Alerts even while the page is visible (the conversation on screen is the exception: tab marker only by default) |
46
46
  | ✅ **A reply finishes** — a turn completes; background sessions finishing are also reported | Toast + sound | Alerts only while the page is hidden / backgrounded |
47
47
 
48
+ > **Background** subagent child sessions are **not** alerted about by default: DSH lists each of them as its own session, but they are steps inside the parent conversation's turn, so an alert for them is only noise. The subagent session you have open is not part of this and alerts as usual. Settings → "Ignore subagent sessions" turns the mute off.
49
+
48
50
  ## Configure it from DSH Settings
49
51
 
50
52
  Open **Settings → Notify me** (refresh the page once after installing):
@@ -52,15 +54,18 @@ Open **Settings → Notify me** (refresh the page once after installing):
52
54
  - **Enable reminders** — master switch; when off, no toasts, no sounds and the tab title is never touched;
53
55
  - **System notifications / Sound / Volume** — toast toggle, WebAudio beep toggle and a volume slider;
54
56
  - **"Needs you" alerts while the page is open** (default on) and **"Reply finished" alerts while the page is open** (default off);
57
+ - **Stay quiet for the conversation on screen** (default on): a wait inside the conversation you are looking at keeps only the tab marker — no toast or sound landing on the approval card. Backgrounding the page puts that alert back at once, unless you already handled it. Waits in other background sessions are unaffected;
58
+ - **Ignore subagent sessions** (default on): background subagent child sessions raise neither "needs you" nor "reply finished" — those are steps of the parent conversation's turn, and the parent's own alert already covers them. The subagent session you have open alerts as usual. Turn it off to alert for every session;
59
+ - **Click a toast to open that conversation** (default on): clicking brings DSH to the front and switches to the conversation the alert came from; when off, clicking a toast does nothing;
55
60
  - **Decide approvals from the toast** (default on): approval notifications carry Approve / Reject buttons, and one click settles the request exactly like the Allow once / Reject buttons on the approval card. Clicking the toast body still takes you back to the conversation;
56
61
  - **Notification language** — follow the interface / 简体中文 / English: controls the language of the alert text and the `🔔 …` title marker;
57
62
  - **Test buttons** — send one "needs you", "reply finished" or "approval buttons" test alert with the current settings (**not** limited by the "while the page is open" toggles; the "needs you" test's title marker clears itself after ~6s).
58
63
 
59
- > Notification permission is required: click once on the page → **Allow** (or address-bar lock → Site settings → Notifications → Allow → reload).
64
+ > Notification permission is required: click once on the page → **Allow** (or address-bar lock → Site settings → Notifications → Allow → reload). On macOS, also allow the browser under System Settings → Notifications.
60
65
 
61
66
  ## How it alerts
62
67
 
63
- - **System notification** — native notification-center toast (clicking it brings the DSH window back to front; approval toasts can be decided straight from the buttons)
68
+ - **System notification** — a toast through the browser into the Windows action center / macOS Notification Center (clicking it opens the conversation it came from; approval toasts can be decided straight from the buttons)
64
69
  - **Sound** — WebAudio beeps (distinct patterns for "needs you" vs "done")
65
70
  - **Tab title marker** — while something is waiting on you, the tab title is prefixed with `🔔 Action needed · …` / `🔔 需要你 · …`
66
71
 
@@ -112,11 +117,13 @@ window.__dshNotifyMe.setConfig({
112
117
  enabled: true, // false = disable all reminders (master switch)
113
118
  language: "auto", // 'auto' follow the UI | 'zh' | 'en'
114
119
  attentionHiddenOnly: false, // true = don't alert "needs you" while the page is visible
120
+ currentHiddenOnly: true, // true = waits in the conversation on screen keep only the tab marker (replayed once backgrounded)
115
121
  doneHiddenOnly: true, // false = also alert "reply finished" while visible
122
+ ignoreSubagent: true, // true = never alert for background subagent sessions (the open one is exempt)
116
123
  toast: true, // system-notification toggle
117
124
  sound: true, // sound toggle
118
125
  volume: 0.5, // 0..1
119
- autoFocus: true, // clicking the toast focuses the DSH window
126
+ autoFocus: true, // clicking a toast opens the conversation it came from
120
127
  quickActions: true // Approve / Reject buttons on approval toasts
121
128
  })
122
129
  window.__dshNotifyMe.resetConfig() // restore defaults
@@ -129,7 +136,8 @@ The reminder core picks its interaction source per host generation (see the tabl
129
136
 
130
137
  - the **selected session's** `SessionSnapshot`: a `running: true → false` edge means a reply finished;
131
138
  - **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[]`;
132
- - **every other listed session's** summary: a `running: true → false` edge (or the legacy `completed` flag) alerts you about background work.
139
+ - **every other listed session's** summary: a `running: true → false` edge (or the legacy `completed` flag) alerts you about background work;
140
+ - **subagent sessions**: **background** rows carrying `origin: 'subagent'` (and a `parentId`) stay muted by default. The subagent session you have open is exempt — its alerts follow the normal current-conversation rules (tab marker only while the page is visible, delivered on background), its outstanding waits are picked up when you switch into it, and switching away mutes them again. A fork has a `parentId` but no `origin`, is your own conversation, and keeps alerting throughout.
133
141
 
134
142
  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).
135
143
 
@@ -142,13 +150,15 @@ Every button toast is booked against its interaction key, so it can only decide
142
150
  ## Known limitations
143
151
 
144
152
  - The page must be open for alerts to fire (background tab / minimized is fine; closing the tab stops it — that's inherent to a browser-layer plugin).
145
- - Notifications appear through the browser, so the browser needs "show notifications" permission in Windows settings, and notification-center "Do Not Disturb" must not suppress them.
153
+ - Notifications appear through the browser, so the browser needs "show notifications" permission in the OS notification settings, and notification-center "Do Not Disturb" must not suppress them.
146
154
  - The first sound/toast of each page load needs one user click on the page first (browser autoplay + permission policy).
147
155
  - Toasts are only produced once the notification permission is granted; declining means sound + title marker only.
148
156
  - Quick-decision buttons need a Service Worker: they work on `http://127.0.0.1` and `https`, not on plain `http://192.168.x.x`-style LAN addresses, and some browsers or desktop hosts never render notification buttons. The `bridge` field of `window.__dshNotifyMe.debug()` says why; toasts then fall back to their buttonless form and everything else keeps working.
157
+ - macOS: in the browser, alerts, sounds, the title marker and toast-click navigation carry no OS assumptions and work as on Windows. Two caveats: DSH Desktop (Electron) notifications require the app to be code-signed — unsigned builds simply emit no notification — and whether the official macOS build satisfies that is untested; and notification buttons on macOS / desktop hosts are likewise unrendered in some cases, falling back to the buttonless form above.
149
158
  - Config lives in browser `localStorage`: switching browsers/devices or clearing site data returns to defaults (one-click restore in the Settings page).
150
159
  - 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.
151
160
  - 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.
161
+ - "Stay quiet for the conversation on screen" is judged by `document.hidden`: a window fully covered by other apps still counts as visible, so waits in the current conversation stay quiet there too.
152
162
 
153
163
  ## Development
154
164
 
@@ -160,7 +170,16 @@ node smoke\cordis-host-test.mjs # real-cordis end-to-end (skips when no local D
160
170
  npm pack --dry-run # preview the published tarball
161
171
  ```
162
172
 
163
- `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`.
173
+ `cordis-host-test.mjs` borrows `@deepseek-ai/cordis` from a local DSH installation (it probes the global npm directory, `~/.dsh/profiles/web/node_modules` and the flat fallback `~/.dsh/profiles/node_modules` in turn); in a source checkout with no profile dependencies, point it there with `DSH_CORDIS_PATH=<checkout>/vendor/cordis/lib/index.js`.
174
+
175
+ ## Credits & how contributions land
176
+
177
+ Contributors (by handle; the linked PR is their change):
178
+
179
+ - [@AgMahone](https://github.com/AgMahone) — [#7](https://github.com/chromoany/dsh-notify-me/pull/7): the 0.1.6+ / 0.2.0 host-generation support, plus the double-chime and cordis-lookup reports and retesting;
180
+ - [@d0ublecl1ck](https://github.com/d0ublecl1ck) — [#8](https://github.com/chromoany/dsh-notify-me/pull/8): muting subagent sessions (`ignoreSubagent`) with its tests and docs; the 1.4.0 refinement — mute only background subagents, alert as usual for the one you have open — is his suggestion from the #8 discussion, landed by the maintainer.
181
+
182
+ Attribution policy: external PRs are landed as maintainer commits — the original commit is not reused and no Co-authored-by trailer is added, so GitHub's contributors graph lists the maintainer only and contributors are credited above.
164
183
 
165
184
  ## License
166
185
 
package/README.md CHANGED
@@ -32,7 +32,7 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
32
32
 
33
33
  ## 效果预览
34
34
 
35
- | Windows 系统通知效果 |
35
+ | 系统通知效果(截图来自 Windows) |
36
36
  | --- |
37
37
  | ![DSH 通知效果](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/notify-toast.png) |
38
38
 
@@ -42,9 +42,11 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
42
42
 
43
43
  | 时机 | 提醒内容 | 默认 |
44
44
  | --- | --- | --- |
45
- | 🔔 **模型需要你操作** — 审批请求 / 方案待确认(plan review)/ 提问(`ask_user_question`) | 通知 + 提示音 + `🔔 需要你 ·` 标题标记;审批的通知上直接带「同意 / 拒绝」按钮 | 页面可见也提醒 |
45
+ | 🔔 **模型需要你操作** — 审批请求 / 方案待确认(plan review)/ 提问(`ask_user_question`) | 通知 + 提示音 + `🔔 需要你 ·` 标题标记;审批的通知上直接带「同意 / 拒绝」按钮 | 页面可见也提醒(当前对话除外:默认只留标题标记) |
46
46
  | ✅ **回复完成** — 一轮回复跑完;后台会话完成也会报 | 通知 + 提示音 | 仅页面隐藏/后台时提醒 |
47
47
 
48
+ > **后台**子代理(subagent)子会话的等待与完成默认**不提醒**:DSH 把每个子代理子会话列成独立会话,但它们是所属主对话那一轮里的步骤,提醒只会是噪音。你当前打开的那个子代理会话不算在内,照常提醒。设置页「子代理会话不提醒」可关掉这个静音。
49
+
48
50
  ## 在 DSH 设置里改配置
49
51
 
50
52
  打开 **设置 → 通知提醒**(首次安装后刷新一次页面即可看到),页面提供:
@@ -52,15 +54,18 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
52
54
  - **启用提醒** 主开关:关闭后不再弹系统通知、不播放提示音、也不改标签页标题;
53
55
  - **系统通知 / 提示音 / 音量**:Toast、WebAudio 提示音与音量滑块;
54
56
  - **页面打开时也提醒「需要你」**(默认开)与 **页面打开时也提醒「回复完成」**(默认关);
57
+ - **当前对话不弹通知**(默认开):等待就发生在你正看着的对话里时只留标题标记,不弹通知也不响音(免得盖住审批卡片);页面切到后台立刻补发,若你已经处理掉就作废。其它后台会话不受影响;
58
+ - **子代理会话不提醒**(默认开):后台子代理(subagent)子会话的「需要你」与「回复完成」都不提醒——它们是主对话那一轮的步骤,完成由主对话自己的提醒覆盖;你当前打开的那个子代理会话照常提醒;关掉后恢复逐会话提醒;
59
+ - **点击通知回到对应对话**(默认开):点通知把 DSH 带到前台并切到提醒所属的对话;关掉后点通知不做任何事;
55
60
  - **审批通知上直接裁决**(默认开):权限审批的通知上带「同意 / 拒绝」按钮,点一下就按审批卡片的「允许一次 / 拒绝」直接裁决,不用切回页面;点通知正文仍是原来的「切回对应对话」;
56
61
  - **通知语言**:跟随界面 / 简体中文 / English——决定通知文字与 `🔔 …` 标题标记使用的语言;
57
62
  - **测试按钮**:用当前设置立即发一条「需要你」「回复完成」或「审批按钮」测试提醒(**不受**上面「页面打开时也提醒」开关限制;「需要你」测试的标题标记约 6 秒后自动消失)。
58
63
 
59
- > 需要浏览器通知权限:页面上点一下 → 允许;或地址栏锁 → 站点设置 → 通知 → 允许 → 刷新。
64
+ > 需要浏览器通知权限:页面上点一下 → 允许;或地址栏锁 → 站点设置 → 通知 → 允许 → 刷新。macOS 还要在 系统设置 → 通知 里允许该浏览器。
60
65
 
61
66
  ## 提醒方式
62
67
 
63
- - **系统通知**:Windows 通知中心 Toast(点击可把 DSH 窗口切回前台;审批的通知点按钮直接裁决)
68
+ - **系统通知**:经浏览器弹出的系统通知,进 Windows 通知中心或 macOS 通知中心(点击回到提醒所属的对话;审批的通知点按钮直接裁决)
64
69
  - **提示音**:WebAudio 合成音(「需要你」与「完成」使用不同音型)
65
70
  - **标签页标题标记**:有待处理事项时,标题前出现 `🔔 需要你 · …` / `🔔 Action needed · …`
66
71
 
@@ -108,11 +113,13 @@ window.__dshNotifyMe.setConfig({
108
113
  enabled: true, // false = 关闭所有提醒(主开关)
109
114
  language: "auto", // 'auto' 跟随界面 | 'zh' 简体中文 | 'en' English
110
115
  attentionHiddenOnly: false, // true = 页面可见时「需要你」不提醒
116
+ currentHiddenOnly: true, // true = 当前对话的等待只留标题标记(转后台补发)
111
117
  doneHiddenOnly: true, // false = 页面可见时「完成」也提醒
118
+ ignoreSubagent: true, // true = 后台子代理(subagent)会话不提醒(当前打开的照常)
112
119
  toast: true, // 系统通知开关
113
120
  sound: true, // 提示音开关
114
121
  volume: 0.5, // 音量 0~1
115
- autoFocus: true, // 点通知切回 DSH 窗口
122
+ autoFocus: true, // 点通知回到提醒所属的对话
116
123
  quickActions: true // 审批通知上的「同意 / 拒绝」按钮
117
124
  })
118
125
  window.__dshNotifyMe.resetConfig() // 恢复默认
@@ -126,6 +133,7 @@ window.__dshNotifyMe.decide("approval:3", "allowed-once") // 程序化裁决:
126
133
  - **当前会话** `SessionSnapshot`:`running` true→false = 回复完成;
127
134
  - **待办交互**:`uiSession` 的 `sessionStatus`(`≥ 0.1.6`,逐会话 `pendingInteraction`)或 `pendingInteractions`(`0.1.2 .. 0.1.5`)出现新 key = 模型在等你,容器里带上审批工具名 / 理由、提问文本;旧宿主回落到控制器快照的 `pending[]`;
128
135
  - **其它已列会话**摘要:`running` true→false(或旧宿主的 `completed` 边沿)= 后台工作提醒。
136
+ - **子代理会话**:列表行带 `origin: 'subagent'`(并有 `parentId`)的**后台**会话默认静音;你正打开着的那个子代理会话不算静音对象,它的提醒走当前对话的常规规则(页面可见时默认只留标题标记、转后台补发),切进来时积压的等待同样补上,切走后重新静音。只有 `parentId`、没有 `origin` 的分支(fork)会话属于你自己的对话,始终照常提醒。
129
137
 
130
138
  提醒核心仍然零第三方运行时依赖、完全自包含:通知文字按所选语言(跟随界面 / 中文 / English)即时解析。设置页是**可选**的 React 呈现层——当 DSH web profile 提供 `slots` / `locale` / `react` 时才注册进「设置 → 通知提醒」,缺任一能力时插件自动降级为纯提醒(无设置页),不影响功能。
131
139
 
@@ -142,9 +150,11 @@ window.__dshNotifyMe.decide("approval:3", "allowed-once") // 程序化裁决:
142
150
  - 每次页面加载后第一次出声/弹通知前,需在页面上点击过一次(浏览器自动播放与权限策略)。
143
151
  - 未授权通知权限时只有提示音与标题标记。
144
152
  - 快捷裁决按钮要 Service Worker:`http://127.0.0.1`、`https` 可用,`http://192.168.x.x` 这类局域网地址不行;浏览器或桌面宿主不渲染通知按钮时,`window.__dshNotifyMe.debug()` 的 `bridge` 字段会说明原因,通知自动退回无按钮样式,其余功能不受影响。
153
+ - macOS:浏览器里提醒、提示音、标题标记、点通知跳转都没有平台假设,照常工作;但 DSH Desktop(Electron)的系统通知要求应用已签名,未签名的通知直接发不出去,官方 macOS 版是否满足这一前提未实测;审批按钮在 macOS 与桌面宿主上的渲染同样未实测,不渲染时自动退回无按钮通知。
145
154
  - 配置存在浏览器 `localStorage`:换浏览器/设备或清除站点数据后会回到默认值(设置页可一键恢复默认)。
146
155
  - `≥ 0.1.6`(含 `0.2.0-rc.2`)的「回复完成」提醒正文只有会话名,不再附带回复摘要——宿主快照已不再提供 `nodes`;提醒本身照常触发。
147
156
  - 标题标记由宿主与插件共同写 `document.title`:宿主重算标题时标记可能被覆盖,直到下一次提醒事件重新写成。
157
+ - 「当前对话不弹通知」按 `document.hidden` 判定:窗口被别的应用完全盖住时仍算「页面可见」,此时当前对话的等待照样静默。
148
158
 
149
159
  ## 开发自检
150
160
 
@@ -156,7 +166,16 @@ node smoke\cordis-host-test.mjs # 真 cordis 端到端(找不到本机 DSH
156
166
  npm pack --dry-run # 预览发布包
157
167
  ```
158
168
 
159
- `cordis-host-test.mjs` 会从本机 DSH 安装借 `@deepseek-ai/cordis`;源码 checkout 里没有 profile 依赖时,用 `DSH_CORDIS_PATH=<checkout>/vendor/cordis/lib/index.js` 指定即可。
169
+ `cordis-host-test.mjs` 会从本机 DSH 安装借 `@deepseek-ai/cordis`(依次探测 npm 全局目录、`~/.dsh/profiles/web/node_modules` 与 `~/.dsh/profiles/node_modules` 平铺回退目录);源码 checkout 里没有 profile 依赖时,用 `DSH_CORDIS_PATH=<checkout>/vendor/cordis/lib/index.js` 指定即可。
170
+
171
+ ## 致谢与贡献须知
172
+
173
+ 贡献者(按 handle 列出,对应 PR 就是他们提的改动):
174
+
175
+ - [@AgMahone](https://github.com/AgMahone) — [#7](https://github.com/chromoany/dsh-notify-me/pull/7):0.1.6+ / 0.2.0 宿主代际支持,以及通知双响、cordis 查找路径的报障与复测;
176
+ - [@d0ublecl1ck](https://github.com/d0ublecl1ck) — [#8](https://github.com/chromoany/dsh-notify-me/pull/8):子代理会话静音(`ignoreSubagent`)与配套测试、文档;1.4.0 里「只静音后台子代理、当前打开的照常提醒」的口径同样出自他在 #8 讨论里的建议,落地由维护者完成。
177
+
178
+ 署名口径:本仓库收编外部 PR 时以维护者提交落地——不沿用原 commit、不挂 Co-authored-by,GitHub 的 contributors 图只显示维护者,贡献者以上面的致谢署名。
160
179
 
161
180
  ## License
162
181
 
package/lib/client.js CHANGED
@@ -32,6 +32,12 @@
32
32
  // `current` field where it exists (<= 0.1.5-rc.3) and otherwise derived
33
33
  // from the mainView retention count (0.1.6+, the way the shipped
34
34
  // DocumentTitle / ui-session pick it);
35
+ // - mutes background subagent child sessions by default
36
+ // (config.ignoreSubagent): DSH lists them as ordinary rows carrying
37
+ // `origin: 'subagent'`, but their waits and finish edges are steps of the
38
+ // parent conversation's turn. The subagent session the user has open is not
39
+ // muted — it is on screen like any other conversation and alerts by the
40
+ // normal rules;
35
41
  // - fires a Windows desktop Notification (+ optional sound) when the agent
36
42
  // needs the user's input — pending interaction kinds 'approval',
37
43
  // 'plan-review' or 'question' — and when a reply/turn finishes;
@@ -140,6 +146,13 @@
140
146
  // the page brings that alert back (see flushQuieted).
141
147
  currentHiddenOnly: true, // the visible session's own waits stay silent
142
148
  doneHiddenOnly: true, // reply-finished alerts only when the page is hidden
149
+ // Background subagent child sessions are steps of another conversation's
150
+ // turn: DSH lists each of them as its own row (origin: 'subagent'), so
151
+ // their waits and finish edges would toast noise the user cannot act on.
152
+ // Muted by default; the list row's own origin field is the only thing this
153
+ // keys on, plus "not the session on screen" — an open subagent session is
154
+ // just another conversation to alert for.
155
+ ignoreSubagent: true,
143
156
  toast: true,
144
157
  sound: true,
145
158
  volume: 0.5,
@@ -748,6 +761,28 @@
748
761
  } catch (e) {}
749
762
  return sid;
750
763
  }
764
+ // Should this session's alerts stay muted as a subagent? Every child session
765
+ // a subagent run created is listed as a row carrying `origin: 'subagent'`
766
+ // (plus a parentId), including the rows the host synthesizes from its
767
+ // subagentCatalog. Those alerts describe a step of another conversation's
768
+ // turn, so the ones running in the background are muted by default
769
+ // (config.ignoreSubagent); a forked session has a parentId but no origin and
770
+ // is the user's own conversation, so it keeps alerting. The subagent session
771
+ // the user has open is not background at all: it is on screen like any other
772
+ // conversation, so it alerts by the normal rules (including the
773
+ // current-conversation quiet rule) and is never a mute candidate. Read live
774
+ // rather than cached, so the settings switch and session switches both take
775
+ // effect on the next event without rebinding anything.
776
+ function isSubagentSession(sid) {
777
+ if (!config.ignoreSubagent || !sid) return false;
778
+ if (currentSessionId() === sid) return false;
779
+ try {
780
+ var st = listStore && listStore.getSnapshot();
781
+ var sum = st && st.byId && st.byId[sid];
782
+ if (sum && sum.origin === "subagent") return true;
783
+ } catch (e) {}
784
+ return false;
785
+ }
751
786
  // Returns null once the alert went out, or the copy it would have shown
752
787
  // when only the visible-session rule held it back — the caller queues that
753
788
  // copy by interaction key so it can still fire from the background (the
@@ -888,12 +923,20 @@
888
923
  var pendingNow = row.pending || [];
889
924
  var prevPendingKeys = prev ? prev.pendingKeys : [];
890
925
  var wasRunning = prev ? prev.running : false;
926
+ var muted = isSubagentSession(row.id);
891
927
  var newKeys = [];
892
- for (var i = 0; i < pendingNow.length; i++) {
893
- var pk = pendingNow[i].key;
894
- if (prevPendingKeys.indexOf(pk) === -1 && !attentionKeysFired[pk]) {
895
- newKeys.push(pendingNow[i]);
896
- attentionKeysFired[pk] = true;
928
+ // A muted row still feeds the baseline below, so lifting the mute
929
+ // (turning the switch off, or opening that subagent session) resumes
930
+ // alerting from the next edge on instead of replaying old ones.
931
+ // Its keys are left out of attentionKeysFired on purpose: a wait that was
932
+ // never announced must stay announceable once the mute is lifted.
933
+ if (!muted) {
934
+ for (var i = 0; i < pendingNow.length; i++) {
935
+ var pk = pendingNow[i].key;
936
+ if (prevPendingKeys.indexOf(pk) === -1 && !attentionKeysFired[pk]) {
937
+ newKeys.push(pendingNow[i]);
938
+ attentionKeysFired[pk] = true;
939
+ }
897
940
  }
898
941
  }
899
942
  var hasPendingNow = pendingNow.length > 0;
@@ -905,7 +948,7 @@
905
948
  // On hosts that expose uiSession this is the fallback path only: the
906
949
  // interaction snapshot there is the authoritative source, and it drives
907
950
  // the title marker itself (see the uiSession watcher below).
908
- if (newKeys.length > 0) {
951
+ if (!muted && newKeys.length > 0) {
909
952
  var it = newKeys[0];
910
953
  var quick = quickFor(it.key, it, row.id);
911
954
  var copy = fireAttention(it.kind, sessionLabelText(row), textFromPayload(it.payload), !!row.face, row.id, quick);
@@ -916,13 +959,13 @@
916
959
 
917
960
  // 2) a non-selected session finished while we were away (sidebar "done")
918
961
  var notified = false;
919
- if (!row.face && row.completed && prev && !prev.completed && prev.seeded) {
962
+ if (!muted && !row.face && row.completed && prev && !prev.completed && prev.seeded) {
920
963
  fireDone(sessionLabelText(row), "", row.id);
921
964
  notified = true;
922
965
  }
923
966
 
924
967
  // 3) running true -> false on a session we were watching: a reply finished
925
- if (!notified && prev && prev.seeded && wasRunning && !runningNow && !hasPendingNow) {
968
+ if (!muted && !notified && prev && prev.seeded && wasRunning && !runningNow && !hasPendingNow) {
926
969
  // ignore the edge if it happened in the same tick as an attention alert
927
970
  if (Date.now() - lastAttentionAt < 300) {
928
971
  /* answered by an attention alert above */
@@ -1010,6 +1053,12 @@
1010
1053
  var cur = currentSessionId();
1011
1054
  bindCurrentFace();
1012
1055
  bindUiSession();
1056
+ // The subagent mute keys on "not the session on screen", and switching
1057
+ // sessions only emits on the list store — re-run the interaction pass so
1058
+ // the decision tracks the switch: opening a muted subagent session
1059
+ // announces its outstanding wait (under the current-conversation rules),
1060
+ // leaving it puts the next edge back in the muted bucket.
1061
+ evalUiPending();
1013
1062
  var byId = (st && st.byId) || {};
1014
1063
  for (var sid in byId) {
1015
1064
  if (sid === cur) continue; // current handled by the face watcher
@@ -1129,6 +1178,13 @@
1129
1178
  // A session with no pending interaction is not "seen": leaving it out
1130
1179
  // lets the teardown pass below release any mark its answer replaced.
1131
1180
  if (!it) return;
1181
+ // A background subagent wait is muted and deliberately leaves no
1182
+ // baseline: the row is what isSubagentSession reads, and skipping the
1183
+ // record here also lets the teardown pass drop a mark whose wait was
1184
+ // announced before the mute was switched on (or before the user
1185
+ // switched away from that subagent session). Opening the session later
1186
+ // announces the outstanding wait then, like any first sighting.
1187
+ if (isSubagentSession(sid)) return;
1132
1188
  seen[sid] = true;
1133
1189
  var key = it.key || (it.kind + ":" + sid);
1134
1190
  var prev = uiBaseline[sid];
@@ -1179,7 +1235,7 @@
1179
1235
  });
1180
1236
  } catch (e) {}
1181
1237
  // keep in sync with package.json "version" on every release
1182
- api.version = "1.3.0";
1238
+ api.version = "1.4.0";
1183
1239
  api.setConfig = function (patch) { return setConfigLocal(patch); };
1184
1240
  api.resetConfig = function () {
1185
1241
  try { localStorage.removeItem(LS_CONFIG); } catch (e) {}
@@ -1212,6 +1268,7 @@
1212
1268
  attentionHiddenOnly: config.attentionHiddenOnly,
1213
1269
  currentHiddenOnly: config.currentHiddenOnly,
1214
1270
  doneHiddenOnly: config.doneHiddenOnly,
1271
+ ignoreSubagent: config.ignoreSubagent,
1215
1272
  toast: config.toast,
1216
1273
  sound: config.sound,
1217
1274
  quickActions: config.quickActions,
@@ -1254,6 +1311,8 @@
1254
1311
  currentQuietHint: "默认开启:等待就发生在你正看着的这个对话里时,只留标签页标记,不弹系统通知、不响提示音,免得挡住审批卡片;页面切到后台后照常提醒。",
1255
1312
  doneVisible: "页面打开时也提醒「回复完成」",
1256
1313
  doneVisibleHint: "默认关闭:「完成」只在页面隐藏或切到后台时才提醒,避免打扰。",
1314
+ subagentQuiet: "子代理会话不提醒",
1315
+ subagentQuietHint: "默认开启:后台子代理(subagent)会话的「需要你」与「回复完成」都不提醒——它们是另一个对话的步骤,完成由所属主对话自己的提醒覆盖;你当前打开的那个子代理会话照常提醒。关掉后恢复逐会话提醒。",
1257
1316
  autoFocus: "点击通知回到对应对话",
1258
1317
  autoFocusHint: "默认开启:点系统通知会把 DSH 带到前台,并直接切到这条提醒所属的对话;关掉后点击通知不做任何事。",
1259
1318
  quickActions: "审批通知上直接裁决",
@@ -1294,6 +1353,8 @@
1294
1353
  currentQuietHint: "On by default: a wait inside the conversation you are looking at keeps only the tab marker — no toast or sound landing on the approval card. Backgrounding the page puts the alert back.",
1295
1354
  doneVisible: "Also alert \"reply finished\" while the page is open",
1296
1355
  doneVisibleHint: "Off by default: \"done\" only alerts while the page is hidden or backgrounded, to avoid noise.",
1356
+ subagentQuiet: "Ignore subagent sessions",
1357
+ subagentQuietHint: "On by default: background sessions spawned by a subagent raise neither \"needs you\" nor \"reply finished\" alerts — they are steps of another conversation, already covered by that conversation's own alert. The subagent session you have open alerts as usual. Turn off to alert for every session.",
1297
1358
  autoFocus: "Click a toast to open that conversation",
1298
1359
  autoFocusHint: "On by default: clicking a toast brings DSH forward and switches to the conversation the alert came from. When off, clicking a toast does nothing.",
1299
1360
  quickActions: "Decide approvals from the toast",
@@ -1421,6 +1482,7 @@
1421
1482
  rowSwitch("attentionVisible", t("attentionVisible"), t("attentionVisibleHint"), !cfg.attentionHiddenOnly, function (v) { patch({ attentionHiddenOnly: !v }); }),
1422
1483
  rowSwitch("currentQuiet", t("currentQuiet"), t("currentQuietHint"), cfg.currentHiddenOnly, function (v) { patch({ currentHiddenOnly: v }); }),
1423
1484
  rowSwitch("doneVisible", t("doneVisible"), t("doneVisibleHint"), !cfg.doneHiddenOnly, function (v) { patch({ doneHiddenOnly: !v }); }),
1485
+ rowSwitch("subagentQuiet", t("subagentQuiet"), t("subagentQuietHint"), cfg.ignoreSubagent, function (v) { patch({ ignoreSubagent: v }); }),
1424
1486
  rowSwitch("autoFocus", t("autoFocus"), t("autoFocusHint"), cfg.autoFocus, function (v) { patch({ autoFocus: v }); }),
1425
1487
  rowSwitch("quickActions", t("quickActions"), t("quickActionsHint"), cfg.quickActions, function (v) {
1426
1488
  patch({ quickActions: v });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-notify-me",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "DSH 桌面消息提醒插件:模型需要你操作(审批/方案确认/提问的可操作提醒)或回复在后台完成时,系统通知+提示音+标签页标题提醒你;权限审批的通知上可直接点「同意/拒绝」完成裁决;可在设置页开关并切换通知语言。Desktop notification & message alerts for DeepSeek Harness web — toasts, sounds and a tab-title marker when the agent needs your input (approval / plan review / question) or a reply finishes in the background, Approve/Reject buttons on approval toasts to decide right from the notification; toggleable from Settings with zh/en notification language.",
5
5
  "license": "MIT",
6
6
  "type": "module",