dsh-notify-windows 0.4.0 → 0.5.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/README.en.md CHANGED
@@ -1,35 +1,39 @@
1
- # dsh-notify
1
+ # dsh-notify-windows
2
2
 
3
- A DeepSeek Harness (DSH) plugin that sends Windows toast notifications whenever
4
- the agent needs your attention:
3
+ > A DeepSeek Harness (DSH) plugin that sends Windows toast notifications whenever the agent needs your attention.
5
4
 
6
- 1. Task finished (turn/end) — a turn completed, errored, or hit the token cap;
7
- 2. Approval pending (approval/asked) — a permission decision is waiting for you
8
- (skipped when the session's approval policy is never, since nothing waits);
9
- 3. Question pending — the agent asked you a question via ask_user_question,
10
- including calls nested inside run_code programs (in this deployment every
11
- tool call goes through run_code, so the plugin scans run_code source for
12
- tools.ask_user_question( calls).
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>
13
11
 
14
- Zero runtime dependencies: toasts are sent through Windows PowerShell 5.1's
15
- WinRT toast API (lib/notify.ps1), with the AppUserModelId registered under HKCU
16
- on first use (no admin required).
12
+ **Task finished** **Approval pending** 🔐 **Question pending** ❓ — never miss anything that needs you, even away from the screen.
17
13
 
18
- ## Install
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
+ ### Option A: npm (recommended)
19
26
 
20
27
  ```powershell
21
- pwsh scripts\install-profile.ps1 -Profile web # or: dsh plugin --profile web add dsh-notify-windows
28
+ dsh plugin --profile web add dsh-notify-windows
22
29
  ```
23
30
 
24
- The script deploys the package into the profile's hoisted node_modules as
25
- dsh-notify-<version> (the versioned directory lets a running host hot-load a
26
- redeploy without a restart) and adds this entry to the profile's
27
- cordis.patch.yml, which the running host hot-applies:
31
+ Then add this entry to the profile's `cordis.patch.yml` (the running host hot-applies it — no restart needed):
28
32
 
29
33
  ```yaml
30
34
  - insert:
31
35
  - id: dsh-notify
32
- name: 'dsh-notify-0.4.0'
36
+ name: 'dsh-notify-windows'
33
37
  config:
34
38
  enabled: true
35
39
  reasons: [completed, error, max-tokens]
@@ -43,33 +47,58 @@ cordis.patch.yml, which the running host hot-applies:
43
47
  debug: false
44
48
  ```
45
49
 
46
- ## Configuration
50
+ ### Option B: from source
51
+
52
+ ```powershell
53
+ git clone git@github.com:SeverusZh/dsh-notify-windows.git
54
+ cd dsh-notify-windows
55
+ pwsh scripts\install-profile.ps1 -Profile web
56
+ ```
57
+
58
+ The script deploys the package into the profile's hoisted `node_modules` as `dsh-notify-windows-<version>` — the versioned directory lets a running host hot-load a redeploy without a restart — and writes the patch entry above idempotently.
59
+
60
+ ## ⚙️ Configuration
47
61
 
48
62
  | Key | Default | Meaning |
49
63
  | --- | --- | --- |
50
- | enabled | true | master switch |
51
- | reasons | [completed, error, max-tokens] | turn-end reasons to notify |
52
- | includeSubagents | false | also notify subagent sessions |
53
- | notifyOnStart | false | toast when the plugin loads |
54
- | notifyOnApproval | true | toast on approval/asked (policy-aware) |
55
- | notifyOnAskUser | true | toast when the agent asks a question |
56
- | appName | DeepSeek Harness | toast source display name |
57
- | aumid | DeepSeekHarness.Notify | toast AppUserModelId |
58
- | log | true | write %TEMP%\dsh-notify\notify.log |
59
- | debug | false | log every session event (diagnostics, heavy) |
60
-
61
- ## Verify
64
+ | `enabled` | `true` | master switch |
65
+ | `reasons` | `[completed, error, max-tokens]` | turn-end reasons to notify (`aborted` / `interrupted` / `blocked` also available) |
66
+ | `includeSubagents` | `false` | also notify subagent sessions |
67
+ | `notifyOnStart` | `false` | toast when the plugin loads |
68
+ | `notifyOnApproval` | `true` | toast on `approval/asked` (skipped under policy `never`) |
69
+ | `notifyOnAskUser` | `true` | toast when the agent asks you a question |
70
+ | `notifyOnGoalRounds` | `false` | stay quiet on /goal auto-continuation rounds (except the final complete/block round) |
71
+ | `excerpt` | `true` | append an excerpt of the agent's final reply to the toast |
72
+ | `excerptMaxChars` | `80` | max characters of the excerpt |
73
+ | `appName` | `DeepSeek Harness` | toast source display name and fallback title |
74
+ | `aumid` | `DeepSeekHarness.Notify` | toast AppUserModelId |
75
+ | `log` | `true` | write `%TEMP%\dsh-notify\notify.log` |
76
+ | `debug` | `false` | log every session event (diagnostics, heavy) |
77
+
78
+ ## 🔔 Triggers
79
+
80
+ | Scenario | Session event | Sample toast |
81
+ | --- | --- | --- |
82
+ | Task finished | `turn/end` | Task completed (turn N) |
83
+ | Task errored / capped | `turn/end` | Task errored / Output hit token cap |
84
+ | Approval waiting | `approval/asked` | DeepSeek Harness · Needs approval / tool pwsh: … |
85
+ | Question waiting | `tool/call` (incl. run_code detection) | DeepSeek Harness · Needs your answer / Continue? |
86
+
87
+ ## 🧪 Verify
62
88
 
63
89
  ```powershell
64
90
  node scripts\smoke-test.mjs
65
91
  ```
66
92
 
67
- Runs the plugin on a bare cordis Context with synthetic events and asserts the
68
- log output (task, approval, and question toasts fire; subagent / never-policy /
69
- unrelated events are filtered).
93
+ 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).
94
+
95
+ ## FAQ
96
+
97
+ - **No toast?** Check Windows notification settings for this app and Focus Assist; the AUMID registers itself on first use.
98
+ - **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.
99
+ - **Approval toasts missing?** Sessions whose approval policy is `never` auto-reject — nothing waits, so the plugin stays quiet. Only `ask`-policy sessions notify.
100
+ - **Hot-update after code changes?** Re-run the install script (the versioned directory name busts the module cache); for npm installs run `dsh plugin --profile web update dsh-notify-windows` and restart the host.
70
101
 
71
- ## Limitations
102
+ ## 📄 License
72
103
 
73
- - Windows only (requires Windows PowerShell 5.1, shipped with Windows 10/11);
74
- - toast delivery depends on the system notification / Focus Assist settings;
75
- - the run_code question detection is heuristic and may miss or misreport.
104
+ [MIT](./LICENSE) © 2026 SeverusZh
package/README.md CHANGED
@@ -1,51 +1,39 @@
1
- # dsh-notify
1
+ # dsh-notify-windows
2
2
 
3
- DeepSeek Harness(DSH)插件:Agent 需要你关注时,向 Windows 发送系统桌面通知。
3
+ > DeepSeek Harness(DSH)插件:当 Agent 需要你关注时,向 Windows 发送系统桌面通知。
4
4
 
5
- 触发时机(三类,均可在配置中开关):
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>
6
11
 
7
- 1. 任务完成(turn/end):Agent 每完成一轮回复,或任务出错/输出超限时弹窗提醒;
8
- 2. 权限审批(approval/asked):有操作在等待你的审批时立即提醒(会话审批策略为
9
- never 时自动跳过,因为那时没有东西在等你);
10
- 3. 提问确认(ask_user_question):Agent 向你提问、等待回答时提醒——包括通过
11
- run_code 内部调用 ask_user_question 的情形(本部署下所有工具都经 run_code
12
- 调用,插件会扫描 run_code 程序源码里的 tools.ask_user_question 调用)。
12
+ **任务完成** | **等待审批** 🔐 | **等待回答** ❓ —— 离开屏幕也不错过任何需要你处理的事。
13
13
 
14
- 让你无需盯着网页界面也能知道任务结束、需要审批或等待你回答。
14
+ ## ✨ 功能
15
15
 
16
- ## 功能
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 事件日志。
17
22
 
18
- - 监听会话事件流:turn/end、approval/asked、tool/call(含 run_code 源码启发式检测);
19
- - turn/end 支持按结束原因过滤:completed、error、max-tokens、aborted、interrupted、blocked;
20
- - 审批提醒按会话生效策略折叠:策略为 never 时不提醒;
21
- - 默认忽略子代理(subagent)会话,只提醒主会话,避免通知轰炸;
22
- - 通知标题自动使用会话标题(session/title),正文显示原因与轮次;
23
- - 零运行时依赖:通知通过 Windows PowerShell 5.1 的 WinRT Toast API 发送,并自动注册通知用的 AppUserModelId;
24
- - 可选的日志文件(%TEMP%\dsh-notify\notify.log)与 debug 事件日志便于排查。
23
+ ## 🚀 安装
25
24
 
26
- ## 工作原理
25
+ ### 方式一:npm(推荐)
27
26
 
28
- turn/end、approval/asked、tool/call (session/event) -> lib/index.js -> spawn powershell.exe -> lib/notify.ps1 -> WinRT Toast
27
+ ```powershell
28
+ dsh plugin --profile web add dsh-notify-windows
29
+ ```
29
30
 
30
- 1. 插件在根上下文监听 session/event;
31
- 2. 匹配到关注的结束原因/审批请求/提问调用后生成标题与正文;
32
- 3. 启动 powershell.exe 执行 lib/notify.ps1:先按需在 HKCU\SOFTWARE\Classes\AppUserModelId 下注册 AUMID(无需管理员权限),再发送 Toast。
33
-
34
- ## 安装
35
-
36
- 把本包放进 DSH profile 可解析的位置,然后在 profile 的 cordis.patch.yml 里追加条目。
37
-
38
- ### 方式一:自动安装脚本(Windows)
39
-
40
- pwsh scripts\install-profile.ps1 -Profile web
41
-
42
- 脚本会把插件复制到 profile 的 hoisted node_modules(目录名为 dsh-notify-版本号,
43
- 重新部署时目录名变化可使运行中的宿主热加载新代码而无需重启),并幂等地写入/更新
44
- $HOME\.dsh\profiles\web\cordis.patch.yml(该文件被运行中的 DSH 热监视):
31
+ 然后在 profile 的 `cordis.patch.yml` 中追加条目(该文件被运行中的 DSH 热监视,改动立即生效,无需重启):
45
32
 
33
+ ```yaml
46
34
  - insert:
47
35
  - id: dsh-notify
48
- name: 'dsh-notify-0.4.0'
36
+ name: 'dsh-notify-windows'
49
37
  config:
50
38
  enabled: true
51
39
  reasons: [completed, error, max-tokens]
@@ -57,37 +45,60 @@ $HOME\.dsh\profiles\web\cordis.patch.yml(该文件被运行中的 DSH 热监
57
45
  aumid: 'DeepSeekHarness.Notify'
58
46
  log: true
59
47
  debug: false
48
+ ```
60
49
 
61
- ### 方式二:dsh plugin 命令
50
+ ### 方式二:源码安装
62
51
 
63
- dsh plugin --profile web add 本仓库路径
64
-
65
- 再手动把上面的 insert 条目加入 cordis.patch.yml(注意:修改插件代码后若想被运行中的宿主立即加载,请用安装脚本,它会更换部署目录名以绕过模块缓存)。
66
-
67
- ## 配置项
68
-
69
- enabled true 总开关
70
- reasons [completed, error, max-tokens] 需要提醒的回合结束原因
71
- includeSubagents false 是否也提醒子代理会话
72
- notifyOnStart false 插件加载时发一条「已激活」通知
73
- notifyOnApproval true 审批请求提醒(策略 never 时自动跳过)
74
- notifyOnAskUser true Agent 提问等待回答时提醒
75
- appName DeepSeek Harness 通知来源显示名与兜底标题
76
- aumid DeepSeekHarness.Notify 通知 AppUserModelId
77
- log true 写日志到 %TEMP%\dsh-notify\notify.log
78
- debug false 把所有收到的会话事件写入日志(排查用,量大会产生大量磁盘写入)
52
+ ```powershell
53
+ git clone git@github.com:SeverusZh/dsh-notify-windows.git
54
+ cd dsh-notify-windows
55
+ pwsh scripts\install-profile.ps1 -Profile web
56
+ ```
57
+
58
+ 脚本会把插件部署到 profile 的 hoisted `node_modules`(目录名 `dsh-notify-windows-<版本>`,重新部署时更换目录名,让运行中的宿主热加载新代码而无需重启),并幂等地写入上面的 patch 条目。
59
+
60
+ ## ⚙️ 配置项
61
+
62
+ | 配置 | 默认值 | 说明 |
63
+ | --- | --- | --- |
64
+ | `enabled` | `true` | 总开关 |
65
+ | `reasons` | `[completed, error, max-tokens]` | 需要提醒的回合结束原因(可选 `aborted` / `interrupted` / `blocked`) |
66
+ | `includeSubagents` | `false` | 是否也提醒子代理会话 |
67
+ | `notifyOnStart` | `false` | 插件加载时发一条「已激活」通知 |
68
+ | `notifyOnApproval` | `true` | 审批请求提醒(策略 `never` 时自动跳过) |
69
+ | `notifyOnAskUser` | `true` | Agent 提问等待回答时提醒 |
70
+ | `notifyOnGoalRounds` | `false` | /goal 自动推进回合不提醒(目标完成/阻塞的最终回合除外) |
71
+ | `excerpt` | `true` | 通知正文附带 Agent 最后回复的摘要 |
72
+ | `excerptMaxChars` | `80` | 摘要最大字符数 |
73
+ | `appName` | `DeepSeek Harness` | 通知来源显示名与兜底标题 |
74
+ | `aumid` | `DeepSeekHarness.Notify` | 通知 AppUserModelId |
75
+ | `log` | `true` | 写日志到 `%TEMP%\dsh-notify\notify.log` |
76
+ | `debug` | `false` | 把所有会话事件写入日志(排查用,量大) |
77
+
78
+ ## 🔔 触发场景
79
+
80
+ | 场景 | 会话事件 | 通知示例 |
81
+ | --- | --- | --- |
82
+ | 任务完成 | `turn/end` | 「任务已完成(第 N 轮)」 |
83
+ | 任务出错 / 超限 | `turn/end` | 「任务出错」/「输出达到 token 上限」 |
84
+ | 等待审批 | `approval/asked` | 「DeepSeek Harness · 需要审批 / 工具 pwsh:…」 |
85
+ | 等待回答 | `tool/call`(含 run_code 检测) | 「DeepSeek Harness · 需要回答 / 是否继续?」 |
86
+
87
+ ## 🧪 验证
88
+
89
+ ```powershell
90
+ node scripts\smoke-test.mjs
91
+ ```
79
92
 
80
- ## 验证
93
+ 独立进程内用 cordis Context 模拟会话事件:应弹出任务完成 / 审批 / 提问三条测试通知,并断言日志记录与过滤行为(子代理、`never` 策略、无关调用均被过滤)。
81
94
 
82
- node scripts\smoke-test.mjs
95
+ ## ❓ 常见问题
83
96
 
84
- 独立进程内用 cordis Context 模拟会话事件,通过后会弹出多条测试通知(任务完成、
85
- 审批、提问),并断言日志记录与过滤行为。
97
+ - **收不到通知?** 检查 Windows「通知与操作」设置是否允许该应用显示通知,以及「专注助手」是否开启;首次发送会自动注册 AUMID。
98
+ - **/goal 模式会提醒吗?** 默认不会:自动推进的中间回合保持静默,只有目标完成(或阻塞)的最终回合才提醒;如需每个回合都提醒,把 `notifyOnGoalRounds` 设为 `true`。
99
+ - **为什么审批提醒有时不弹?** 会话审批策略为 `never` 时审批会被自动拒绝、不会等待,插件会跳过提醒;策略为 `ask` 时才提醒。
100
+ - **更新插件代码后如何热更新?** 重新执行安装脚本(版本目录名变化即热加载);用 npm 安装时执行 `dsh plugin --profile web update dsh-notify-windows` 后重启宿主。
86
101
 
87
- ## 限制与说明
102
+ ## 📄 许可证
88
103
 
89
- - 仅支持 Windows(依赖 Windows PowerShell 5.1,Windows 10/11 自带);
90
- - 通知是否弹出受系统「专注助手 / 通知中心」设置影响;
91
- - Toast 显示为 appName 名称;首次发送会自动注册 AUMID(HKCU,无需管理员);
92
- - 审批提醒只在会话审批策略为 ask(默认)时有意义;策略为 never 的会话不提醒;
93
- - 提问检测依赖 run_code 程序源码中出现 tools.ask_user_question( 调用,属启发式,可能漏检或误报。
104
+ [MIT](./LICENSE) © 2026 SeverusZh
package/lib/index.js CHANGED
@@ -1,10 +1,14 @@
1
- // dsh-notify — DeepSeek Harness plugin: send a Windows toast notification
1
+ // dsh-notify-windows — DeepSeek Harness plugin: send a Windows toast notification
2
2
  // whenever the agent needs the user's attention:
3
- // - turn/end — a task completed, failed, or was cut off;
3
+ // - turn/end — a task completed, failed, or was cut off (with an
4
+ // optional excerpt of the agent's final reply);
4
5
  // - approval/asked — a permission approval is waiting (only when the
5
6
  // session's effective approval policy is "ask");
6
- // - tool/call — the agent called the ask_user_question tool and is
7
- // waiting for an answer.
7
+ // - tool/call — the agent called the ask_user_question tool (directly
8
+ // or inside a run_code program) and is waiting.
9
+ //
10
+ // Goal rounds (/goal auto-continuation) are quiet by default: only the final
11
+ // round that completes or blocks the goal produces a toast.
8
12
  //
9
13
  // Zero runtime dependencies: notifications go through the toast sender
10
14
  // script (./notify.ps1) executed by Windows PowerShell 5.1, whose .NET
@@ -16,7 +20,7 @@ import { join } from "node:path";
16
20
  import { fileURLToPath } from "node:url";
17
21
 
18
22
  const NOTIFY_SCRIPT = fileURLToPath(new URL("./notify.ps1", import.meta.url));
19
- const VERSION = "0.4.0";
23
+ const VERSION = "0.5.0";
20
24
 
21
25
  const REASON_TEXT = {
22
26
  completed: "任务已完成",
@@ -27,7 +31,7 @@ const REASON_TEXT = {
27
31
  blocked: "任务未开始",
28
32
  };
29
33
 
30
- export const name = "dsh-notify";
34
+ export const name = "dsh-notify-windows";
31
35
 
32
36
  function truncate(text, max) {
33
37
  if (typeof text !== "string") return "";
@@ -65,6 +69,48 @@ function extractQuestionFromCode(argumentsJson) {
65
69
  }
66
70
  }
67
71
 
72
+ /** First non-empty text block of a message, trimmed. */
73
+ function textOfMessage(message) {
74
+ const blocks = message?.content;
75
+ if (!Array.isArray(blocks)) return undefined;
76
+ for (const block of blocks) {
77
+ if (block?.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) return block.text.trim();
78
+ }
79
+ return undefined;
80
+ }
81
+
82
+ /**
83
+ * Scan the session log from the turn/end event back to its turn/start and
84
+ * collect what the notification needs: the driving user message's source,
85
+ * every goal/change operation in the window, and the last assistant reply
86
+ * text (for the excerpt).
87
+ */
88
+ function turnWindow(session, turnEnd) {
89
+ const events = session?.events ?? [];
90
+ const turn = turnEnd?.data?.turn;
91
+ let endIndex = events.length - 1;
92
+ for (let i = events.length - 1; i >= 0; i -= 1) {
93
+ if (events[i]?.seq === turnEnd?.seq) { endIndex = i; break; }
94
+ }
95
+ let userSource;
96
+ const goalOps = [];
97
+ let lastAssistantText;
98
+ for (let i = endIndex; i >= 0; i -= 1) {
99
+ const event = events[i];
100
+ if (!event) continue;
101
+ if (event.type === "turn/start" && event.data?.turn === turn) break;
102
+ // Every user/message in the window overwrites: walking backwards, the
103
+ // final value is the FIRST message of the turn — the driving input.
104
+ if (event.type === "user/message" && event.data?.source) userSource = event.data.source;
105
+ if (event.type === "goal/change" && event.data?.operation) goalOps.push(event.data.operation);
106
+ if (event.type === "assistant/message" && event.data?.turn === turn && lastAssistantText === undefined) {
107
+ const text = textOfMessage(event.data.message);
108
+ if (text) lastAssistantText = text;
109
+ }
110
+ }
111
+ return { userSource, goalOps, lastAssistantText };
112
+ }
113
+
68
114
  export default function dshNotify(ctx, config = {}) {
69
115
  const cfg = {
70
116
  enabled: true,
@@ -73,6 +119,9 @@ export default function dshNotify(ctx, config = {}) {
73
119
  notifyOnStart: false,
74
120
  notifyOnApproval: true,
75
121
  notifyOnAskUser: true,
122
+ notifyOnGoalRounds: false,
123
+ excerpt: true,
124
+ excerptMaxChars: 80,
76
125
  appName: "DeepSeek Harness",
77
126
  aumid: "DeepSeekHarness.Notify",
78
127
  log: true,
@@ -159,9 +208,22 @@ export default function dshNotify(ctx, config = {}) {
159
208
  if (event.type === "turn/end") {
160
209
  const reason = event.data?.reason;
161
210
  if (typeof reason?.kind !== "string" || !reasons.has(reason.kind)) return;
211
+ const window = turnWindow(session, event);
212
+
213
+ // /goal 自动推进回合默认静默:仅在目标完成/阻塞的最终回合提醒。
214
+ if (!cfg.notifyOnGoalRounds) {
215
+ const isGoalRound = window.userSource?.kind === "goal" && window.userSource.round > 0;
216
+ const terminal = window.goalOps.some((op) => op === "complete" || op === "block");
217
+ if (isGoalRound && !terminal) return;
218
+ }
219
+
162
220
  const title = titleFor(session) || cfg.appName;
163
- const body = REASON_TEXT[reason.kind] ?? "任务已结束";
164
- sendToast(title, body + (Number.isInteger(event.data.turn) ? "(第 " + event.data.turn + " 轮)" : ""));
221
+ let body = REASON_TEXT[reason.kind] ?? "任务已结束";
222
+ if (Number.isInteger(event.data.turn)) body += "(第 " + event.data.turn + " 轮)";
223
+ if (cfg.excerpt && window.lastAssistantText) {
224
+ body += "\n" + truncate(window.lastAssistantText, cfg.excerptMaxChars);
225
+ }
226
+ sendToast(title, body);
165
227
  log({
166
228
  event: "notify",
167
229
  sessionId: session?.id,
@@ -208,4 +270,4 @@ export default function dshNotify(ctx, config = {}) {
208
270
  if (cfg.enabled && cfg.notifyOnStart) {
209
271
  sendToast(cfg.appName, "任务完成提醒已激活(v" + VERSION + ")");
210
272
  }
211
- }
273
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-notify-windows",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "DeepSeek Harness 插件:任务完成时发送 Windows 系统通知",
5
5
  "repository": {
6
6
  "type": "git",