dsh-notify-windows 0.7.0 → 0.7.4-beta.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,72 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/)。
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ### 兼容:DSH 0.1.2-alpha.4
8
+
9
+ - **peerDependencies**:`@deepseek-ai/cordis` `^4.0.1` → `^4.0.2`(alpha.4
10
+ 各包一致声明的版本)。
11
+ - **真实-Cordis 探针**:新增 `test/probe.test.mjs`(devDependency
12
+ `@deepseek-ai/cordis ^4.0.2`,`--legacy-peer-deps` 安装),在真实 Context 上
13
+ 挂载插件,按 harness-src @ 0.1.2-alpha.4 核实的会话事件字形合成 session
14
+ stub 并经 `session/event` 火线派发,断言 JSONL 日志(
15
+ `<tmpdir>/dsh-notify/notify.log`)的 notify / approval / ask-user 行与
16
+ 过滤行为:
17
+ - turn/end completed → notify(标题/轮次/截断摘要);原因不在 `reasons`
18
+ 内不通知;
19
+ - 子代理过滤:`delegationDepth ≥ 1` / `header.origin === 'subagent'`
20
+ 过滤,恢复主会话(深度 0)通知;`includeSubagents: true` 放行;
21
+ - approval/asked:`approval/policy` 为 `ask` 时通知、`never` 时抑制;
22
+ - goal 轮静默:`user/message` source.kind `goal` + round > 0 时仅终态
23
+ (`goal/change` operation `complete`)通知,非终态(`edit`)抑制;
24
+ - ask_user_question 直接调用与 run_code 内嵌调用均提取问题文本。
25
+ - **脚本**:`npm test` = `node --test "test/*.test.mjs"`(显式限定 `test/`
26
+ 目录,避免 `node --test` 默认 glob 误拾取面向 Windows 宿主手工运行的
27
+ `scripts/smoke-test.mjs`)。
28
+ - **事件字形核对结论**:`0.1.0-rc.8` → `0.1.2-alpha.4` 中插件消费的所有
29
+ 会话事件字段(turn/end reason kinds、user/message source、goal/change
30
+ operation、approval/asked、approval/policy、tool/call、session.header)
31
+ 无变化;`ctx.sessionTitle.get(session).title` 与 `ctx.webServer.port`
32
+ 亦保留——lib/index.js 无需改码,探针作为兼容契约锚点锁定。
33
+
34
+ ## [0.7.3] - 2026-08-28
35
+
36
+ ### 修复:重启后主会话被误判为子代理,通知全部失效
37
+
38
+ - **根因**:DSH 持久化会话记录时写入 `delegationDepth ?? 0`,重启恢复后主会话的
39
+ header 也带 `delegationDepth: 0`;旧过滤逻辑 `delegationDepth !== undefined`
40
+ 把「带该字段」当作子代理,导致每次 `dsh web` 重启后所有主会话的通知被静默过滤。
41
+ - **修复**:子代理判定改为 `(delegationDepth ?? 0) > 0`(主会话为 0,子代理 ≥ 1),
42
+ 重启后通知恢复正常。
43
+ - 冒烟测试新增回归用例:`delegationDepth: 0` 的恢复主会话必须弹通知,深度 ≥ 1 仍过滤。
44
+
45
+ ## [0.7.2] - 2026-08-28
46
+
47
+ ### 修复:WSL 下通知失败被静默吞掉 + 通知通道自检
48
+
49
+ - **静默失败检测**:PowerShell 5.1 在 `-File` 无法加载脚本时仍以退出码 0 退出(例如路径
50
+ 未转换成功时),旧版只检查退出码,失败完全无痕。现在捕获 stdout/stderr,以
51
+ notify.ps1 输出的 `toast shown` 作为成功标记,失败时记录详细输出并告警。
52
+ - **powershell.exe 解析不再只依赖 PATH**:WSL 下若 DSH 由 systemd / cron / ssh 等
53
+ 最小 PATH 环境启动,`spawn("powershell.exe")` 会 ENOENT 失败。现在优先使用
54
+ `/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`(WSL 标准位置),
55
+ 原生 Windows 回退到 PATH 查找,行为不变。
56
+ - **启动自检**:插件启动时校验 notify.ps1 与 powershell.exe 是否可达,通道不可用时
57
+ 立即告警,不再等任务结束才发现通知发不出去。
58
+ - `wslpath` 转换结果缓存(NOTIFY_SCRIPT 恒定,不再每次 toast 同步转换)。
59
+
60
+ ## [0.7.1] - 2026-08-27
61
+
62
+ ### 修复:支持 DSH 部署在 WSL 中时发送通知
63
+
64
+ - 之前通知通道唯一依赖 `powershell.exe -File <notify.ps1>`,而 DSH 跑在 WSL 里时该脚本路径为
65
+ Linux 形式(如 `/home/.../notify.ps1`),Windows 侧 PowerShell 无法识别,导致通知静默失败。
66
+ - **修复**:新增 `winPath()`,在 WSL 下用 `wslpath -w` 将脚本路径转为 Windows 可读的
67
+ `\\wsl.localhost\...` UNC 路径后传给 `-File`。
68
+ - **原生 Windows 不受影响**:`wslpath` 在原生 Windows 上不存在,`winPath()` 会原样返回
69
+ 原始路径,行为与旧版完全一致。
70
+
5
71
  ## [0.7.0] - 2026-08-20
6
72
 
7
73
  ### 新增「点击通知跳转」功能
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 SeverusZh
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SeverusZh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md CHANGED
@@ -1,132 +1,132 @@
1
- # dsh-notify-windows
2
-
3
- > A DeepSeek Harness (DSH) plugin that sends Windows toast notifications whenever the agent needs your attention.
4
-
5
- <p align="center">
6
- <img src="https://img.shields.io/npm/v/dsh-notify-windows" alt="npm">
7
- <img src="https://img.shields.io/github/license/SeverusZh/dsh-notify-windows" alt="license">
8
- <img src="https://img.shields.io/badge/platform-Windows%2010%2F11-blue" alt="platform">
9
- <img src="https://img.shields.io/github/stars/SeverusZh/dsh-notify-windows?style=social" alt="stars">
10
- </p>
11
-
12
- **Task finished** ✅ | **Approval pending** 🔐 | **Question pending** ❓ — never miss anything that needs you, even away from the screen.
13
-
14
- ## ✨ Features
15
-
16
- - **Task completion** — listens to `turn/end` session events; toasts on completed / errored / max-tokens turns, with the session title and reason;
17
- - **Approval pending** — listens to `approval/asked`; toasts the moment a permission decision is waiting, and skips sessions whose approval policy is `never` (nothing is waiting there);
18
- - **Question pending** — toasts when the agent calls `ask_user_question`; in this deployment every tool call goes through `run_code`, so the plugin also scans `run_code` source for `tools.ask_user_question(` calls and extracts the question text;
19
- - **Quiet by default** — subagent sessions are ignored unless `includeSubagents` is set;
20
- - **Zero dependencies** — toasts are sent through Windows PowerShell 5.1's WinRT toast API, with the AppUserModelId registered under HKCU on first use (no admin required);
21
- - **Diagnosable** — optional log (`%TEMP%\dsh-notify\notify.log`) and a debug event log.
22
-
23
- ## 🚀 Install
24
-
25
- The plugin installs through the **`dsh.bundle`** mechanism: the npm package ships its own
26
- `cordis.patch.yml`, and after `dsh plugin add` it auto-mounts the `dsh-notify` entry — no
27
- manual `- insert:` needed.
28
-
29
- ```powershell
30
- dsh plugin --profile web add dsh-notify-windows
31
- ```
32
-
33
- Restart DSH and refresh the browser:
34
-
35
- ```powershell
36
- dsh --profile web
37
- ```
38
-
39
- > Note: don't hand-add `- insert:` with `dsh-notify` anymore — DSH would fail to boot with
40
- > `duplicate loader entry id: dsh-notify`. To tweak settings, override the entry by id in the
41
- > profile's `cordis.patch.yml` (see below).
42
-
43
- ### Update / Remove
44
-
45
- ```powershell
46
- dsh plugin --profile web update dsh-notify-windows
47
- dsh plugin --profile web remove dsh-notify-windows
48
- ```
49
-
50
- Restart the DSH host after updating (a newly added bundle layer needs a boot).
51
-
52
- ## ⚙️ Configuration
53
-
54
- All `config` keys are optional and default in the plugin. To override, target the entry by id
55
- in `$DSH_HOME/profiles/web/cordis.patch.yml` (hot-applied by the running host — no restart):
56
-
57
- ```yaml
58
- - id: dsh-notify
59
- name: dsh-notify-windows
60
- config:
61
- enabled: true
62
- reasons: [completed, error, max-tokens]
63
- notifyOnStart: true
64
- notifyOnApproval: true
65
- log: true
66
- ```
67
-
68
- | Key | Default | Meaning |
69
- | --- | --- | --- |
70
- | `enabled` | `true` | master switch |
71
- | `reasons` | `[completed, error, max-tokens]` | turn-end reasons to notify (`aborted` / `interrupted` / `blocked` also available) |
72
- | `includeSubagents` | `false` | also notify subagent sessions |
73
- | `notifyOnStart` | `false` | toast when the plugin loads |
74
- | `notifyOnApproval` | `true` | toast on `approval/asked` (skipped under policy `never`) |
75
- | `notifyOnAskUser` | `true` | toast when the agent asks you a question |
76
- | `notifyOnGoalRounds` | `false` | stay quiet on /goal auto-continuation rounds (except the final complete/block round) |
77
- | `excerpt` | `true` | append an excerpt of the agent's final reply to the toast |
78
- | `excerptMaxChars` | `80` | max characters of the excerpt |
79
- | `appName` | `DeepSeek Harness` | toast source display name and fallback title |
80
- | `aumid` | `DeepSeekHarness.Notify` | toast AppUserModelId |
81
- | `log` | `true` | write `%TEMP%\dsh-notify\notify.log` |
82
- | `debug` | `false` | log every session event (diagnostics, heavy) |
83
- | `openOnClick` | `true` | make completion / approval / question toasts clickable (false falls back to the old non-clickable toast) |
84
- | `preferExisting` | `true` | when true, launcher prefers focusing an open DSH window; when false, just open the URL in the default browser |
85
- | `webUrl` | `''` | override the auto-discovered base URL (e.g. `http://192.168.1.5:4000`); leave empty to auto-discover (defaults to `http://127.0.0.1:3080`) |
86
-
87
- ## 🔔 Triggers
88
-
89
- | Scenario | Session event | Sample toast |
90
- | --- | --- | --- |
91
- | Task finished | `turn/end` | Task completed (turn N) |
92
- | Task errored / capped | `turn/end` | Task errored / Output hit token cap |
93
- | Approval waiting | `approval/asked` | DeepSeek Harness · Needs approval / tool pwsh: … |
94
- | Question waiting | `tool/call` (incl. run_code detection) | DeepSeek Harness · Needs your answer / Continue? |
95
-
96
- ## 🔗 Click to open
97
-
98
- Toasts for task completion / approval pending / question pending are clickable → they open the
99
- DSH Web GUI. The plugin prefers to focus an already-open DSH browser tab or Chrome Application
100
- window; if none is open, it opens a new tab in the default browser and loads the GUI. The target
101
- URL carries a `?session=<id>` parameter (not yet consumed by the rc.8 frontend — reserved for
102
- future deep links).
103
-
104
- **Configuration:**
105
-
106
- - `openOnClick` (default `true`; `false` disables clickability and falls back to the old non-clickable toast);
107
- - `preferExisting` (default `true`; when true the launcher prefers reusing an existing window; when false it opens the URL directly in the default browser);
108
- - `webUrl` (optional, overrides the auto-discovered base URL, e.g. `http://192.168.1.5:4000`; leave empty to auto-discover / default `http://127.0.0.1:3080`).
109
-
110
- **Notes:** the first click auto-registers the `dshnotify://` protocol handler (HKCU, no admin
111
- needed); on failure it silently degrades to "open in default browser".
112
-
113
- > Note: DSH 0.1.0-rc.8 does not yet support deep-linking to a single session by URL; clicking opens the DSH interface (home / session list) and focuses the existing window, and you pick the target session from the list. Once the DSH frontend supports deep links, `?session=<id>` will jump straight to it.
114
-
115
- ## 🧪 Verify
116
-
117
- ```powershell
118
- node scripts\smoke-test.mjs
119
- ```
120
-
121
- Runs the plugin on a bare cordis Context with synthetic events: three test toasts fire (completion, approval, question) and the log assertions verify the filtering (subagent, `never`-policy, unrelated calls).
122
-
123
- ## ❓ FAQ
124
-
125
- - **No toast?** Check Windows notification settings for this app and Focus Assist; the AUMID registers itself on first use.
126
- - **Do /goal rounds toast?** Not by default: auto-continuation rounds stay quiet and only the final round that completes (or blocks) the goal toasts. Set `notifyOnGoalRounds` to `true` to hear every round.
127
- - **Approval toasts missing?** Sessions whose approval policy is `never` auto-reject — nothing waits, so the plugin stays quiet. Only `ask`-policy sessions notify.
128
- - **Hot-update after code changes?** Run `dsh plugin --profile web update dsh-notify-windows`, then restart the DSH host.
129
-
130
- ## 📄 License
131
-
1
+ # dsh-notify-windows
2
+
3
+ > A DeepSeek Harness (DSH) plugin that sends Windows toast notifications whenever the agent needs your attention.
4
+
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/dsh-notify-windows" alt="npm">
7
+ <img src="https://img.shields.io/github/license/SeverusZh/dsh-notify-windows" alt="license">
8
+ <img src="https://img.shields.io/badge/platform-Windows%2010%2F11-blue" alt="platform">
9
+ <img src="https://img.shields.io/github/stars/SeverusZh/dsh-notify-windows?style=social" alt="stars">
10
+ </p>
11
+
12
+ **Task finished** ✅ | **Approval pending** 🔐 | **Question pending** ❓ — never miss anything that needs you, even away from the screen.
13
+
14
+ ## ✨ Features
15
+
16
+ - **Task completion** — listens to `turn/end` session events; toasts on completed / errored / max-tokens turns, with the session title and reason;
17
+ - **Approval pending** — listens to `approval/asked`; toasts the moment a permission decision is waiting, and skips sessions whose approval policy is `never` (nothing is waiting there);
18
+ - **Question pending** — toasts when the agent calls `ask_user_question`; in this deployment every tool call goes through `run_code`, so the plugin also scans `run_code` source for `tools.ask_user_question(` calls and extracts the question text;
19
+ - **Quiet by default** — subagent sessions are ignored unless `includeSubagents` is set;
20
+ - **Zero dependencies** — toasts are sent through Windows PowerShell 5.1's WinRT toast API, with the AppUserModelId registered under HKCU on first use (no admin required);
21
+ - **Diagnosable** — optional log (`%TEMP%\dsh-notify\notify.log`) and a debug event log.
22
+
23
+ ## 🚀 Install
24
+
25
+ The plugin installs through the **`dsh.bundle`** mechanism: the npm package ships its own
26
+ `cordis.patch.yml`, and after `dsh plugin add` it auto-mounts the `dsh-notify` entry — no
27
+ manual `- insert:` needed.
28
+
29
+ ```powershell
30
+ dsh plugin --profile web add dsh-notify-windows
31
+ ```
32
+
33
+ Restart DSH and refresh the browser:
34
+
35
+ ```powershell
36
+ dsh --profile web
37
+ ```
38
+
39
+ > Note: don't hand-add `- insert:` with `dsh-notify` anymore — DSH would fail to boot with
40
+ > `duplicate loader entry id: dsh-notify`. To tweak settings, override the entry by id in the
41
+ > profile's `cordis.patch.yml` (see below).
42
+
43
+ ### Update / Remove
44
+
45
+ ```powershell
46
+ dsh plugin --profile web update dsh-notify-windows
47
+ dsh plugin --profile web remove dsh-notify-windows
48
+ ```
49
+
50
+ Restart the DSH host after updating (a newly added bundle layer needs a boot).
51
+
52
+ ## ⚙️ Configuration
53
+
54
+ All `config` keys are optional and default in the plugin. To override, target the entry by id
55
+ in `$DSH_HOME/profiles/web/cordis.patch.yml` (hot-applied by the running host — no restart):
56
+
57
+ ```yaml
58
+ - id: dsh-notify
59
+ name: dsh-notify-windows
60
+ config:
61
+ enabled: true
62
+ reasons: [completed, error, max-tokens]
63
+ notifyOnStart: true
64
+ notifyOnApproval: true
65
+ log: true
66
+ ```
67
+
68
+ | Key | Default | Meaning |
69
+ | --- | --- | --- |
70
+ | `enabled` | `true` | master switch |
71
+ | `reasons` | `[completed, error, max-tokens]` | turn-end reasons to notify (`aborted` / `interrupted` / `blocked` also available) |
72
+ | `includeSubagents` | `false` | also notify subagent sessions |
73
+ | `notifyOnStart` | `false` | toast when the plugin loads |
74
+ | `notifyOnApproval` | `true` | toast on `approval/asked` (skipped under policy `never`) |
75
+ | `notifyOnAskUser` | `true` | toast when the agent asks you a question |
76
+ | `notifyOnGoalRounds` | `false` | stay quiet on /goal auto-continuation rounds (except the final complete/block round) |
77
+ | `excerpt` | `true` | append an excerpt of the agent's final reply to the toast |
78
+ | `excerptMaxChars` | `80` | max characters of the excerpt |
79
+ | `appName` | `DeepSeek Harness` | toast source display name and fallback title |
80
+ | `aumid` | `DeepSeekHarness.Notify` | toast AppUserModelId |
81
+ | `log` | `true` | write `%TEMP%\dsh-notify\notify.log` |
82
+ | `debug` | `false` | log every session event (diagnostics, heavy) |
83
+ | `openOnClick` | `true` | make completion / approval / question toasts clickable (false falls back to the old non-clickable toast) |
84
+ | `preferExisting` | `true` | when true, launcher prefers focusing an open DSH window; when false, just open the URL in the default browser |
85
+ | `webUrl` | `''` | override the auto-discovered base URL (e.g. `http://192.168.1.5:4000`); leave empty to auto-discover (defaults to `http://127.0.0.1:3080`) |
86
+
87
+ ## 🔔 Triggers
88
+
89
+ | Scenario | Session event | Sample toast |
90
+ | --- | --- | --- |
91
+ | Task finished | `turn/end` | Task completed (turn N) |
92
+ | Task errored / capped | `turn/end` | Task errored / Output hit token cap |
93
+ | Approval waiting | `approval/asked` | DeepSeek Harness · Needs approval / tool pwsh: … |
94
+ | Question waiting | `tool/call` (incl. run_code detection) | DeepSeek Harness · Needs your answer / Continue? |
95
+
96
+ ## 🔗 Click to open
97
+
98
+ Toasts for task completion / approval pending / question pending are clickable → they open the
99
+ DSH Web GUI. The plugin prefers to focus an already-open DSH browser tab or Chrome Application
100
+ window; if none is open, it opens a new tab in the default browser and loads the GUI. The target
101
+ URL carries a `?session=<id>` parameter (not yet consumed by the rc.8 frontend — reserved for
102
+ future deep links).
103
+
104
+ **Configuration:**
105
+
106
+ - `openOnClick` (default `true`; `false` disables clickability and falls back to the old non-clickable toast);
107
+ - `preferExisting` (default `true`; when true the launcher prefers reusing an existing window; when false it opens the URL directly in the default browser);
108
+ - `webUrl` (optional, overrides the auto-discovered base URL, e.g. `http://192.168.1.5:4000`; leave empty to auto-discover / default `http://127.0.0.1:3080`).
109
+
110
+ **Notes:** the first click auto-registers the `dshnotify://` protocol handler (HKCU, no admin
111
+ needed); on failure it silently degrades to "open in default browser".
112
+
113
+ > Note: DSH 0.1.0-rc.8 does not yet support deep-linking to a single session by URL; clicking opens the DSH interface (home / session list) and focuses the existing window, and you pick the target session from the list. Once the DSH frontend supports deep links, `?session=<id>` will jump straight to it.
114
+
115
+ ## 🧪 Verify
116
+
117
+ ```powershell
118
+ node scripts\smoke-test.mjs
119
+ ```
120
+
121
+ Runs the plugin on a bare cordis Context with synthetic events: three test toasts fire (completion, approval, question) and the log assertions verify the filtering (subagent, `never`-policy, unrelated calls).
122
+
123
+ ## ❓ FAQ
124
+
125
+ - **No toast?** Check Windows notification settings for this app and Focus Assist; the AUMID registers itself on first use.
126
+ - **Do /goal rounds toast?** Not by default: auto-continuation rounds stay quiet and only the final round that completes (or blocks) the goal toasts. Set `notifyOnGoalRounds` to `true` to hear every round.
127
+ - **Approval toasts missing?** Sessions whose approval policy is `never` auto-reject — nothing waits, so the plugin stays quiet. Only `ask`-policy sessions notify.
128
+ - **Hot-update after code changes?** Run `dsh plugin --profile web update dsh-notify-windows`, then restart the DSH host.
129
+
130
+ ## 📄 License
131
+
132
132
  [MIT](./LICENSE) © 2026 SeverusZh
package/README.md CHANGED
@@ -1,129 +1,130 @@
1
- # dsh-notify-windows
2
-
3
- > DeepSeek Harness(DSH)插件:当 Agent 需要你关注时,向 Windows 发送系统桌面通知。
4
-
5
- <p align="center">
6
- <img src="https://img.shields.io/npm/v/dsh-notify-windows" alt="npm">
7
- <img src="https://img.shields.io/github/license/SeverusZh/dsh-notify-windows" alt="license">
8
- <img src="https://img.shields.io/badge/platform-Windows%2010%2F11-blue" alt="platform">
9
- <img src="https://img.shields.io/github/stars/SeverusZh/dsh-notify-windows?style=social" alt="stars">
10
- </p>
11
-
12
- **任务完成** ✅ | **等待审批** 🔐 | **等待回答** ❓ —— 离开屏幕也不错过任何需要你处理的事。
13
-
14
- ## ✨ 功能
15
-
16
- - **任务完成提醒**:监听会话 `turn/end` 事件,任务完成 / 出错 / 输出超限时立即弹窗,标题自动使用会话标题,正文显示原因与轮次;
17
- - **权限审批提醒**:监听 `approval/asked` 事件,有操作等待你的审批时立即提醒;会话审批策略为 `never` 时自动跳过(此时没有东西在等你);
18
- - **提问确认提醒**:Agent 调用 `ask_user_question` 向你提问时提醒;本部署下所有工具都经 `run_code` 调用,插件会扫描 `run_code` 程序源码中的 `tools.ask_user_question(` 调用并提取问题文本;
19
- - **防打扰**:默认忽略子代理(subagent)会话,只提醒主会话;
20
- - **零依赖**:通知通过 Windows PowerShell 5.1 的 WinRT Toast API 发送,自动注册 HKCU 的 AppUserModelId(无需管理员权限);
21
- - **可诊断**:可选日志(`%TEMP%\dsh-notify\notify.log`)与 debug 事件日志。
22
-
23
- ## 🚀 安装
24
-
25
- 项目通过 **`dsh.bundle`** 机制安装:npm 包自带的 `cordis.patch.yml` 会在
26
- `dsh plugin add` 后自动挂载 `dsh-notify` 入口,**不需要**再手动 `- insert:`。
27
-
28
- ```powershell
29
- dsh plugin --profile web add dsh-notify-windows
30
- ```
31
-
32
- 重启 DSH 并刷新浏览器后生效:
33
-
34
- ```powershell
35
- dsh --profile web
36
- ```
37
-
38
- > 注意:不要再用 `- insert:` 手动添加 `dsh-notify`,否则启动会报
39
- > `duplicate loader entry id: dsh-notify`。想调整配置,在 profile 的
40
- > `cordis.patch.yml` 里按 id 覆盖即可(见下节)。
41
-
42
- ### 更新 / 卸载
43
-
44
- ```powershell
45
- dsh plugin --profile web update dsh-notify-windows
46
- dsh plugin --profile web remove dsh-notify-windows
47
- ```
48
-
49
- 更新后需重启 DSH 宿主(新增 bundle 层需要重新启动)。
50
-
51
- ## ⚙️ 配置项
52
-
53
- 插件行 `config` 全字段可选,未填按默认值。需要调整时,在
54
- `$DSH_HOME/profiles/web/cordis.patch.yml` 里按 id 覆盖主条目即可
55
- (该文件被运行中的 DSH 热监视,改动立即生效,无需重启):
56
-
57
- ```yaml
58
- - id: dsh-notify
59
- name: dsh-notify-windows
60
- config:
61
- enabled: true
62
- reasons: [completed, error, max-tokens]
63
- notifyOnStart: true
64
- notifyOnApproval: true
65
- log: true
66
- ```
67
-
68
- | 配置 | 默认值 | 说明 |
69
- | --- | --- | --- |
70
- | `enabled` | `true` | 总开关 |
71
- | `reasons` | `[completed, error, max-tokens]` | 需要提醒的回合结束原因(可选 `aborted` / `interrupted` / `blocked`) |
72
- | `includeSubagents` | `false` | 是否也提醒子代理会话 |
73
- | `notifyOnStart` | `false` | 插件加载时发一条「已激活」通知 |
74
- | `notifyOnApproval` | `true` | 审批请求提醒(策略 `never` 时自动跳过) |
75
- | `notifyOnAskUser` | `true` | Agent 提问等待回答时提醒 |
76
- | `notifyOnGoalRounds` | `false` | /goal 自动推进回合不提醒(目标完成/阻塞的最终回合除外) |
77
- | `excerpt` | `true` | 通知正文附带 Agent 最后回复的摘要 |
78
- | `excerptMaxChars` | `80` | 摘要最大字符数 |
79
- | `appName` | `DeepSeek Harness` | 通知来源显示名与兜底标题 |
80
- | `aumid` | `DeepSeekHarness.Notify` | 通知 AppUserModelId |
81
- | `log` | `true` | 写日志到 `%TEMP%\dsh-notify\notify.log` |
82
- | `debug` | `false` | 把所有会话事件写入日志(排查用,量大) |
83
- | `openOnClick` | `true` | 任务完成 / 审批 / 提问 toast 是否可点击(false 回退为旧的无点击 toast) |
84
- | `preferExisting` | `true` | true 走 launcher 优先聚焦已开 DSH 窗口;false 直接用默认浏览器打开 URL |
85
- | `webUrl` | `''` | 覆盖自动发现的基地址(如 `http://192.168.1.5:4000`),留空自动发现(默认 `http://127.0.0.1:3080`) |
86
-
87
- ## 🔔 触发场景
88
-
89
- | 场景 | 会话事件 | 通知示例 |
90
- | --- | --- | --- |
91
- | 任务完成 | `turn/end` | 「任务已完成(第 N 轮)」 |
92
- | 任务出错 / 超限 | `turn/end` | 「任务出错」/「输出达到 token 上限」 |
93
- | 等待审批 | `approval/asked` | 「DeepSeek Harness · 需要审批 / 工具 pwsh:…」 |
94
- | 等待回答 | `tool/call`(含 run_code 检测) | 「DeepSeek Harness · 需要回答 / 是否继续?」 |
95
-
96
- ## 🔗 点击通知跳转
97
-
98
- 任务完成 / 等待审批 / 等待回答的 toast 可点击 → 打开 DSH Web GUI。优先聚焦已打开的
99
- DSH 浏览器标签或 Chrome Application 窗口;没有已开窗口时在默认浏览器新开标签并打开 GUI。
100
- 目标 URL 带 `?session=<id>` 参数(rc.8 前端暂不消费,为未来深链预留)。
101
-
102
- **配置:**
103
-
104
- - `openOnClick`(默认 `true`,`false` 则关闭可点击,回退为旧的无点击 toast);
105
- - `preferExisting`(默认 `true`,`true` 走 launcher 优先复用已开窗口;`false` 直接用默认浏览器打开 URL);
106
- - `webUrl`(可选,覆盖自动发现的基地址,如 `http://192.168.1.5:4000`,留空自动发现 / 默认 `http://127.0.0.1:3080`)。
107
-
108
- **注意事项:** 首次点击会自动注册 `dshnotify://` 协议处理器(HKCU,无需管理员);故障会静默降级为「默认浏览器打开」。
109
-
110
- > 说明:DSH 0.1.0-rc.8 前端暂不支持按 URL 直达单个会话;点击后打开的是 DSH 界面(首页/会话列表)并聚焦已有窗口,目标会话需在列表中选择。未来 DSH 前端支持深链后 `?session=<id>` 将直接定位。
111
-
112
- ## 🧪 验证
113
-
114
- ```powershell
115
- node scripts\smoke-test.mjs
116
- ```
117
-
118
- 独立进程内用 cordis Context 模拟会话事件:应弹出任务完成 / 审批 / 提问三条测试通知,并断言日志记录与过滤行为(子代理、`never` 策略、无关调用均被过滤)。
119
-
120
- ## ❓ 常见问题
121
-
122
- - **收不到通知?** 检查 Windows「通知与操作」设置是否允许该应用显示通知,以及「专注助手」是否开启;首次发送会自动注册 AUMID
123
- - **/goal 模式会提醒吗?** 默认不会:自动推进的中间回合保持静默,只有目标完成(或阻塞)的最终回合才提醒;如需每个回合都提醒,把 `notifyOnGoalRounds` 设为 `true`。
124
- - **为什么审批提醒有时不弹?** 会话审批策略为 `never` 时审批会被自动拒绝、不会等待,插件会跳过提醒;策略为 `ask` 时才提醒。
125
- - **更新插件代码后如何生效?** 执行 `dsh plugin --profile web update dsh-notify-windows` 后重启 DSH 宿主。
126
-
127
- ## 📄 许可证
128
-
1
+ # dsh-notify-windows
2
+
3
+ > DeepSeek Harness(DSH)插件:当 Agent 需要你关注时,向 Windows 发送系统桌面通知。
4
+
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/dsh-notify-windows" alt="npm">
7
+ <img src="https://img.shields.io/github/license/SeverusZh/dsh-notify-windows" alt="license">
8
+ <img src="https://img.shields.io/badge/platform-Windows%2010%2F11-blue" alt="platform">
9
+ <img src="https://img.shields.io/github/stars/SeverusZh/dsh-notify-windows?style=social" alt="stars">
10
+ </p>
11
+
12
+ **任务完成** ✅ | **等待审批** 🔐 | **等待回答** ❓ —— 离开屏幕也不错过任何需要你处理的事。
13
+
14
+ ## ✨ 功能
15
+
16
+ - **任务完成提醒**:监听会话 `turn/end` 事件,任务完成 / 出错 / 输出超限时立即弹窗,标题自动使用会话标题,正文显示原因与轮次;
17
+ - **权限审批提醒**:监听 `approval/asked` 事件,有操作等待你的审批时立即提醒;会话审批策略为 `never` 时自动跳过(此时没有东西在等你);
18
+ - **提问确认提醒**:Agent 调用 `ask_user_question` 向你提问时提醒;本部署下所有工具都经 `run_code` 调用,插件会扫描 `run_code` 程序源码中的 `tools.ask_user_question(` 调用并提取问题文本;
19
+ - **防打扰**:默认忽略子代理(subagent)会话,只提醒主会话;
20
+ - **零依赖**:通知通过 Windows PowerShell 5.1 的 WinRT Toast API 发送,自动注册 HKCU 的 AppUserModelId(无需管理员权限);
21
+ - **可诊断**:可选日志(`%TEMP%\dsh-notify\notify.log`)与 debug 事件日志。
22
+
23
+ ## 🚀 安装
24
+
25
+ 项目通过 **`dsh.bundle`** 机制安装:npm 包自带的 `cordis.patch.yml` 会在
26
+ `dsh plugin add` 后自动挂载 `dsh-notify` 入口,**不需要**再手动 `- insert:`。
27
+
28
+ ```powershell
29
+ dsh plugin --profile web add dsh-notify-windows
30
+ ```
31
+
32
+ 重启 DSH 并刷新浏览器后生效:
33
+
34
+ ```powershell
35
+ dsh --profile web
36
+ ```
37
+
38
+ > 注意:不要再用 `- insert:` 手动添加 `dsh-notify`,否则启动会报
39
+ > `duplicate loader entry id: dsh-notify`。想调整配置,在 profile 的
40
+ > `cordis.patch.yml` 里按 id 覆盖即可(见下节)。
41
+
42
+ ### 更新 / 卸载
43
+
44
+ ```powershell
45
+ dsh plugin --profile web update dsh-notify-windows
46
+ dsh plugin --profile web remove dsh-notify-windows
47
+ ```
48
+
49
+ 更新后需重启 DSH 宿主(新增 bundle 层需要重新启动)。
50
+
51
+ ## ⚙️ 配置项
52
+
53
+ 插件行 `config` 全字段可选,未填按默认值。需要调整时,在
54
+ `$DSH_HOME/profiles/web/cordis.patch.yml` 里按 id 覆盖主条目即可
55
+ (该文件被运行中的 DSH 热监视,改动立即生效,无需重启):
56
+
57
+ ```yaml
58
+ - id: dsh-notify
59
+ name: dsh-notify-windows
60
+ config:
61
+ enabled: true
62
+ reasons: [completed, error, max-tokens]
63
+ notifyOnStart: true
64
+ notifyOnApproval: true
65
+ log: true
66
+ ```
67
+
68
+ | 配置 | 默认值 | 说明 |
69
+ | --- | --- | --- |
70
+ | `enabled` | `true` | 总开关 |
71
+ | `reasons` | `[completed, error, max-tokens]` | 需要提醒的回合结束原因(可选 `aborted` / `interrupted` / `blocked`) |
72
+ | `includeSubagents` | `false` | 是否也提醒子代理会话 |
73
+ | `notifyOnStart` | `false` | 插件加载时发一条「已激活」通知 |
74
+ | `notifyOnApproval` | `true` | 审批请求提醒(策略 `never` 时自动跳过) |
75
+ | `notifyOnAskUser` | `true` | Agent 提问等待回答时提醒 |
76
+ | `notifyOnGoalRounds` | `false` | /goal 自动推进回合不提醒(目标完成/阻塞的最终回合除外) |
77
+ | `excerpt` | `true` | 通知正文附带 Agent 最后回复的摘要 |
78
+ | `excerptMaxChars` | `80` | 摘要最大字符数 |
79
+ | `appName` | `DeepSeek Harness` | 通知来源显示名与兜底标题 |
80
+ | `aumid` | `DeepSeekHarness.Notify` | 通知 AppUserModelId |
81
+ | `log` | `true` | 写日志到 `%TEMP%\dsh-notify\notify.log` |
82
+ | `debug` | `false` | 把所有会话事件写入日志(排查用,量大) |
83
+ | `openOnClick` | `true` | 任务完成 / 审批 / 提问 toast 是否可点击(false 回退为旧的无点击 toast) |
84
+ | `preferExisting` | `true` | true 走 launcher 优先聚焦已开 DSH 窗口;false 直接用默认浏览器打开 URL |
85
+ | `webUrl` | `''` | 覆盖自动发现的基地址(如 `http://192.168.1.5:4000`),留空自动发现(默认 `http://127.0.0.1:3080`) |
86
+
87
+ ## 🔔 触发场景
88
+
89
+ | 场景 | 会话事件 | 通知示例 |
90
+ | --- | --- | --- |
91
+ | 任务完成 | `turn/end` | 「任务已完成(第 N 轮)」 |
92
+ | 任务出错 / 超限 | `turn/end` | 「任务出错」/「输出达到 token 上限」 |
93
+ | 等待审批 | `approval/asked` | 「DeepSeek Harness · 需要审批 / 工具 pwsh:…」 |
94
+ | 等待回答 | `tool/call`(含 run_code 检测) | 「DeepSeek Harness · 需要回答 / 是否继续?」 |
95
+
96
+ ## 🔗 点击通知跳转
97
+
98
+ 任务完成 / 等待审批 / 等待回答的 toast 可点击 → 打开 DSH Web GUI。优先聚焦已打开的
99
+ DSH 浏览器标签或 Chrome Application 窗口;没有已开窗口时在默认浏览器新开标签并打开 GUI。
100
+ 目标 URL 带 `?session=<id>` 参数(rc.8 前端暂不消费,为未来深链预留)。
101
+
102
+ **配置:**
103
+
104
+ - `openOnClick`(默认 `true`,`false` 则关闭可点击,回退为旧的无点击 toast);
105
+ - `preferExisting`(默认 `true`,`true` 走 launcher 优先复用已开窗口;`false` 直接用默认浏览器打开 URL);
106
+ - `webUrl`(可选,覆盖自动发现的基地址,如 `http://192.168.1.5:4000`,留空自动发现 / 默认 `http://127.0.0.1:3080`)。
107
+
108
+ **注意事项:** 首次点击会自动注册 `dshnotify://` 协议处理器(HKCU,无需管理员);故障会静默降级为「默认浏览器打开」。
109
+
110
+ > 说明:DSH 0.1.0-rc.8 前端暂不支持按 URL 直达单个会话;点击后打开的是 DSH 界面(首页/会话列表)并聚焦已有窗口,目标会话需在列表中选择。未来 DSH 前端支持深链后 `?session=<id>` 将直接定位。
111
+
112
+ ## 🧪 验证
113
+
114
+ ```powershell
115
+ node scripts\smoke-test.mjs
116
+ ```
117
+
118
+ 独立进程内用 cordis Context 模拟会话事件:应弹出任务完成 / 审批 / 提问三条测试通知,并断言日志记录与过滤行为(子代理、`never` 策略、无关调用均被过滤)。
119
+
120
+ ## ❓ 常见问题
121
+
122
+ - **收不到通知?** 检查 Windows「通知与操作」设置是否允许该应用显示通知,以及「专注助手」是否开启;首次发送会自动注册 AUMID。插件启动时会自检通知通道(`powershell.exe` 与 `notify.ps1` 是否可达),通道不可用会在 DSH 日志中立即告警;每次发送失败也会记录详细原因到 `%TEMP%\dsh-notify\notify.log`。
123
+ - **WSL 下收不到通知?** 插件会自动用 `wslpath -w` 把脚本路径转成 `\\wsl.localhost\...` UNC 路径,并优先通过 `/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` 调用 PowerShell(不依赖 PATH,systemd / cron 等最小环境也能工作)。若仍失败,查看 `%TEMP%\dsh-notify\notify.log` 中的 `error` 条目。
124
+ - **/goal 模式会提醒吗?** 默认不会:自动推进的中间回合保持静默,只有目标完成(或阻塞)的最终回合才提醒;如需每个回合都提醒,把 `notifyOnGoalRounds` 设为 `true`。
125
+ - **为什么审批提醒有时不弹?** 会话审批策略为 `never` 时审批会被自动拒绝、不会等待,插件会跳过提醒;策略为 `ask` 时才提醒。
126
+ - **更新插件代码后如何生效?** 执行 `dsh plugin --profile web update dsh-notify-windows` 后重启 DSH 宿主。
127
+
128
+ ## 📄 许可证
129
+
129
130
  [MIT](./LICENSE) © 2026 SeverusZh
package/lib/index.js CHANGED
@@ -18,14 +18,41 @@
18
18
  // Zero runtime dependencies: notifications go through the toast sender
19
19
  // script (./notify.ps1) executed by Windows PowerShell 5.1, whose .NET
20
20
  // Framework runtime supports the WinRT toast API.
21
- import { spawn } from "node:child_process";
22
- import { appendFileSync, mkdirSync } from "node:fs";
21
+ import { spawn, spawnSync } from "node:child_process";
22
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
23
23
  import { tmpdir } from "node:os";
24
24
  import { join } from "node:path";
25
25
  import { fileURLToPath } from "node:url";
26
26
 
27
27
  const NOTIFY_SCRIPT = fileURLToPath(new URL("./notify.ps1", import.meta.url));
28
- const VERSION = "0.7.0";
28
+ const VERSION = "0.7.3";
29
+
30
+ // Convert a script path so powershell.exe can read it.
31
+ // - On WSL: powershell.exe is a Windows process and can't read Linux paths
32
+ // (e.g. /home/.../notify.ps1). wslpath -w rewrites it to a UNC path
33
+ // (\\wsl.localhost\...) that Windows can access.
34
+ // - On native Windows: wslpath does not exist, spawnSync fails with a
35
+ // non-zero status, and we fall back to the original path unchanged, so
36
+ // the native-Windows behaviour is preserved exactly.
37
+ const winPath = (p) => {
38
+ const r = spawnSync("wslpath", ["-w", p], { encoding: "utf8" });
39
+ return r.status === 0 && r.stdout ? r.stdout.trim() : p;
40
+ };
41
+
42
+ // NOTIFY_SCRIPT never changes, so the (synchronous) wslpath conversion is
43
+ // done once and cached instead of on every toast.
44
+ const NOTIFY_SCRIPT_WIN = winPath(NOTIFY_SCRIPT);
45
+
46
+ // Resolve powershell.exe without relying on PATH alone.
47
+ // - WSL: the standard Windows location is always readable via /mnt/c, even
48
+ // when DSH runs under systemd/cron/ssh with a minimal PATH that lacks the
49
+ // Windows directories (spawn("powershell.exe") would then fail with ENOENT
50
+ // and every notification would be lost).
51
+ // - Native Windows: /mnt/c does not exist, so we fall back to PATH lookup
52
+ // ("powershell.exe"), which is the standard behaviour.
53
+ const WSL_POWERSHELL = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe";
54
+ const resolvePowerShell = () => (existsSync(WSL_POWERSHELL) ? WSL_POWERSHELL : "powershell.exe");
55
+ const POWERSHELL = resolvePowerShell();
29
56
 
30
57
  const REASON_TEXT = {
31
58
  completed: "任务已完成",
@@ -179,7 +206,7 @@ export const apply = (ctx, config = {}) => {
179
206
  "-ExecutionPolicy",
180
207
  "Bypass",
181
208
  "-File",
182
- NOTIFY_SCRIPT,
209
+ NOTIFY_SCRIPT_WIN,
183
210
  "-Title",
184
211
  title,
185
212
  "-Body",
@@ -194,15 +221,30 @@ export const apply = (ctx, config = {}) => {
194
221
  const url = conversationUrl(sessionId);
195
222
  args.push("-Url", url, "-LaunchProtocol", cfg.preferExisting ? 1 : 0);
196
223
  }
197
- const child = spawn("powershell.exe", args, { stdio: "ignore", windowsHide: true });
224
+ // Capture output: PowerShell 5.1 exits 0 even when -File cannot load the
225
+ // script (e.g. a Linux path that was never converted), so the exit code
226
+ // alone cannot tell success from failure. notify.ps1 prints "toast shown"
227
+ // only after the WinRT Show() call returns, which is our success marker.
228
+ const child = spawn(POWERSHELL, args, { stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
229
+ let stdout = "";
230
+ let stderr = "";
231
+ child.stdout?.on("data", (chunk) => { stdout += chunk; });
232
+ child.stderr?.on("data", (chunk) => { stderr += chunk; });
198
233
  child.on("error", (error) => {
199
234
  log({ event: "error", message: String(error?.message ?? error) });
200
235
  ctx.logger?.warn?.("dsh-notify: 启动通知进程失败", error);
201
236
  });
202
237
  child.on("exit", (code) => {
203
- if (code !== 0) {
204
- log({ event: "error", code });
205
- ctx.logger?.warn?.("dsh-notify: 通知进程退出码 " + code);
238
+ const shown = /toast shown/.test(stdout);
239
+ if (code !== 0 || !shown) {
240
+ log({
241
+ event: "error",
242
+ code,
243
+ shown,
244
+ stdout: stdout.slice(0, 500),
245
+ stderr: stderr.slice(0, 500),
246
+ });
247
+ ctx.logger?.warn?.("dsh-notify: 通知进程未成功显示 toast" + (code !== null ? "(退出码 " + code + ")" : ""));
206
248
  }
207
249
  });
208
250
  };
@@ -215,10 +257,19 @@ export const apply = (ctx, config = {}) => {
215
257
  }
216
258
  };
217
259
 
218
- /** True for subagent sessions when subagents are excluded. */
260
+ /**
261
+ * True for subagent sessions when subagents are excluded.
262
+ *
263
+ * DSH stamps `delegationDepth` on the session header: main sessions carry 0
264
+ * (and, after a restart, the RESTORED header always includes it because the
265
+ * persistence layer writes `delegationDepth ?? 0`), while subagent sessions
266
+ * carry >= 1. Testing `!== undefined` therefore misclassifies every restored
267
+ * main session as a subagent and silently kills all notifications after a
268
+ * dsh web restart. Only depths >= 1 are real subagents.
269
+ */
219
270
  const isFilteredSession = (session) => {
220
271
  if (cfg.includeSubagents) return false;
221
- return session?.header?.origin === "subagent" || session?.header?.delegationDepth !== undefined;
272
+ return session?.header?.origin === "subagent" || (session?.header?.delegationDepth ?? 0) > 0;
222
273
  };
223
274
 
224
275
  /** Last approval/policy event of the session, or undefined (defaults to ask). */
@@ -315,7 +366,23 @@ export const apply = (ctx, config = {}) => {
315
366
  }
316
367
  });
317
368
 
318
- log({ event: "start", version: VERSION, enabled: cfg.enabled, reasons: [...reasons], sessionId: null });
369
+ // Startup self-check: if the notification channel is broken (powershell.exe
370
+ // unreachable or notify.ps1 missing), say so immediately instead of letting
371
+ // every toast fail silently later.
372
+ const channelOk = existsSync(NOTIFY_SCRIPT) && (POWERSHELL === "powershell.exe" || existsSync(POWERSHELL));
373
+ log({
374
+ event: "start",
375
+ version: VERSION,
376
+ enabled: cfg.enabled,
377
+ reasons: [...reasons],
378
+ powershell: POWERSHELL,
379
+ notifyScript: NOTIFY_SCRIPT_WIN,
380
+ channelOk,
381
+ sessionId: null,
382
+ });
383
+ if (cfg.enabled && !channelOk) {
384
+ ctx.logger?.warn?.("dsh-notify: 通知通道不可用(powershell.exe 或 notify.ps1 缺失),桌面通知将不会发送");
385
+ }
319
386
  if (cfg.enabled && cfg.notifyOnStart) {
320
387
  // Activation toast has no session: leave non-clickable (legacy behavior).
321
388
  sendToast(cfg.appName, "任务完成提醒已激活(v" + VERSION + ")");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-notify-windows",
3
- "version": "0.7.0",
3
+ "version": "0.7.4-beta.0",
4
4
  "description": "DeepSeek Harness (DSH) 插件:任务完成 / 等待审批 / 等待回答时发送 Windows 系统桌面通知",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,13 @@
25
25
  "node": ">=20"
26
26
  },
27
27
  "peerDependencies": {
28
- "@deepseek-ai/cordis": "^4.0.1"
28
+ "@deepseek-ai/cordis": "^4.0.2"
29
+ },
30
+ "devDependencies": {
31
+ "@deepseek-ai/cordis": "^4.0.2"
32
+ },
33
+ "scripts": {
34
+ "test": "node --test \"test/*.test.mjs\""
29
35
  },
30
36
  "dsh": {
31
37
  "bundle": {