dsh-speak 1.2.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/README.md +29 -0
- package/README.zh-CN.md +27 -0
- package/adapters/dsh/speech-hook.js +24 -3
- package/cordis.patch.yml +5 -0
- package/docs/DESIGN.md +8 -6
- package/docs/DESIGN.zh-CN.md +8 -6
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
[](https://awesome-dsh-plugin.com)
|
|
8
|
+
|
|
7
9
|
Let your agent **tell you** when a long task is done — no more staring at the screen.
|
|
8
10
|
|
|
9
11
|
dsh-speak reads the final assistant reply aloud through system speech synthesis —
|
|
@@ -245,6 +247,33 @@ DSH plugin environment variables:
|
|
|
245
247
|
| `DSH_SPEAK_ENGINE` | empty (auto-resolved) | engine path override; otherwise resolved as `<package>/engine/<platform script>` → `~/.dsh/hooks/<platform script>` (Windows `speak.ps1` / macOS `speak.sh`) |
|
|
246
248
|
| `DSH_SPEAK_THROTTLE_MS` | `1500` | merge delay before announcing |
|
|
247
249
|
|
|
250
|
+
## Customizing (survives npm updates)
|
|
251
|
+
|
|
252
|
+
You can tune behavior without forking, and your changes **survive `npm update`**:
|
|
253
|
+
|
|
254
|
+
1. **Copy the engine out and edit it** (recommended — this is where defaults live: volume,
|
|
255
|
+
rate, `MaxChars`, `LongTextMessage`, voice logic):
|
|
256
|
+
|
|
257
|
+
```powershell
|
|
258
|
+
# Windows
|
|
259
|
+
Copy-Item "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-speak\engine\speak.ps1" "$env:USERPROFILE\.dsh\hooks\my-speak.ps1"
|
|
260
|
+
# macOS
|
|
261
|
+
cp ~/.dsh/profiles/web/node_modules/dsh-speak/engine/speak.sh ~/.dsh/hooks/my-speak.sh
|
|
262
|
+
# edit my-speak.ps1 / my-speak.sh to taste, then point the plugin at it:
|
|
263
|
+
setx DSH_SPEAK_ENGINE "$env:USERPROFILE\.dsh\hooks\my-speak.ps1" # Windows
|
|
264
|
+
echo 'export DSH_SPEAK_ENGINE=~/.dsh/hooks/my-speak.sh' >> ~/.zshrc # macOS
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The plugin resolves the engine as `DSH_SPEAK_ENGINE` → package engine → `~/.dsh/hooks/`,
|
|
268
|
+
so your copy wins. `npm update` only touches the package — your engine stays.
|
|
269
|
+
|
|
270
|
+
2. **Environment variables** (no code changes): `DSH_SPEAK_ENGINE` (engine path),
|
|
271
|
+
`DSH_SPEAK_THROTTLE_MS` (announcement merge delay).
|
|
272
|
+
|
|
273
|
+
3. **Edit the file inside `node_modules`** — works, but the next `npm update` overwrites it.
|
|
274
|
+
|
|
275
|
+
4. **Fork the repo** — full control, publish your own package if you want.
|
|
276
|
+
|
|
248
277
|
## Troubleshooting
|
|
249
278
|
|
|
250
279
|
| symptom | cause | fix |
|
package/README.zh-CN.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
[](https://awesome-dsh-plugin.com)
|
|
6
|
+
|
|
5
7
|
让 Agent 在长任务完成时**开口告诉你**——不用再盯着屏幕等。
|
|
6
8
|
|
|
7
9
|
dsh-speak 通过系统语音合成把 Agent 的最终回复朗读出来——Windows 上优先使用自然
|
|
@@ -230,6 +232,31 @@ DSH 插件环境变量:
|
|
|
230
232
|
| `DSH_SPEAK_ENGINE` | 空(自动解析) | 引擎路径覆盖;否则按"包内 `engine/<平台脚本>` → `~/.dsh/hooks/<平台脚本>`"顺序解析(Windows `speak.ps1` / macOS `speak.sh`) |
|
|
231
233
|
| `DSH_SPEAK_THROTTLE_MS` | `1500` | 播报前的合并延迟(毫秒) |
|
|
232
234
|
|
|
235
|
+
## 自定义(升级不丢)
|
|
236
|
+
|
|
237
|
+
想调行为又不想 fork,而且改完**不会被 `npm update` 覆盖**:
|
|
238
|
+
|
|
239
|
+
1. **把引擎复制出来改**(推荐——默认参数都在这:音量、语速、字数上限、超长提示语、音色逻辑):
|
|
240
|
+
|
|
241
|
+
```powershell
|
|
242
|
+
# Windows
|
|
243
|
+
Copy-Item "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-speak\engine\speak.ps1" "$env:USERPROFILE\.dsh\hooks\my-speak.ps1"
|
|
244
|
+
# macOS
|
|
245
|
+
cp ~/.dsh/profiles/web/node_modules/dsh-speak/engine/speak.sh ~/.dsh/hooks/my-speak.sh
|
|
246
|
+
# 按喜好编辑 my-speak.ps1 / my-speak.sh,然后让插件指向它:
|
|
247
|
+
setx DSH_SPEAK_ENGINE "$env:USERPROFILE\.dsh\hooks\my-speak.ps1" # Windows
|
|
248
|
+
echo 'export DSH_SPEAK_ENGINE=~/.dsh/hooks/my-speak.sh' >> ~/.zshrc # macOS
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
插件按 `DSH_SPEAK_ENGINE` → 包内引擎 → `~/.dsh/hooks/` 的顺序解析引擎,所以你的副本
|
|
252
|
+
优先生效;`npm update` 只动包本身,你的引擎安然无恙。
|
|
253
|
+
|
|
254
|
+
2. **环境变量**(零改代码):`DSH_SPEAK_ENGINE`(引擎路径)、`DSH_SPEAK_THROTTLE_MS`(播报合并延迟)。
|
|
255
|
+
|
|
256
|
+
3. **直接改 `node_modules` 里的文件**——能改,但下次 `npm update` 会被覆盖。
|
|
257
|
+
|
|
258
|
+
4. **fork 仓库**——完全掌控,想发自己的包也行。
|
|
259
|
+
|
|
233
260
|
## 排障
|
|
234
261
|
|
|
235
262
|
| 现象 | 原因 | 解决 |
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
// * only events with a `text` block are announced (reasoning / tool_use blocks
|
|
10
10
|
// are skipped)
|
|
11
11
|
// * when a tool/call event arrives, that round's assistant text is treated as
|
|
12
|
-
// process narration, so any pending announcement is cancelled
|
|
12
|
+
// process narration, so any pending announcement is cancelled — EXCEPT a
|
|
13
|
+
// call to `ask_user_question`, which is a question for the user and keeps
|
|
14
|
+
// the pending text so it is announced
|
|
15
|
+
// * `approval/asked` is announced immediately (approval reason, or a fixed
|
|
16
|
+
// prompt) since approvals are time-sensitive
|
|
13
17
|
// * a final reply with no following tool/call is announced after a throttle
|
|
14
18
|
// delay (merges multi-step messages from the same reply)
|
|
15
19
|
//
|
|
@@ -101,12 +105,29 @@ module.exports = {
|
|
|
101
105
|
if (type !== 'assistant/chunk') {
|
|
102
106
|
log('事件 type=', type, 'surfaceOp=', event && event.surfaceOp, 'seq=', event && event.seq)
|
|
103
107
|
}
|
|
104
|
-
// tool-call round:
|
|
105
|
-
// text
|
|
108
|
+
// tool-call round: a call to ask_user_question is a question to the
|
|
109
|
+
// user — keep the pending text so it gets announced (the user should
|
|
110
|
+
// hear the question); any other tool call cancels the pending
|
|
111
|
+
// announcement (that round's assistant text is process narration)
|
|
106
112
|
if (type === 'tool/call') {
|
|
113
|
+
const toolName = event.data && event.data.name
|
|
114
|
+
if (toolName === 'ask_user_question') {
|
|
115
|
+
log('提问工具调用(ask_user_question)— 保留待播报文本')
|
|
116
|
+
return
|
|
117
|
+
}
|
|
107
118
|
cancelPending()
|
|
108
119
|
return
|
|
109
120
|
}
|
|
121
|
+
// approval requested: announce it right away (time-sensitive), using
|
|
122
|
+
// the approval reason if present
|
|
123
|
+
if (type === 'approval/asked') {
|
|
124
|
+
cancelPending()
|
|
125
|
+
const reason = event.data && event.data.reason
|
|
126
|
+
const text = reason && reason.trim() ? reason : '需要你的审批,请查看界面。'
|
|
127
|
+
log('审批请求,播报:', text.slice(0, 60))
|
|
128
|
+
speak(text)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
110
131
|
if (!event || type !== 'assistant/message') return
|
|
111
132
|
if (event.surfaceOp && event.surfaceOp !== 'append') return
|
|
112
133
|
// the message object lives at event.data.message (event.data wraps { turn, step, message })
|
package/cordis.patch.yml
ADDED
package/docs/DESIGN.md
CHANGED
|
@@ -147,12 +147,14 @@ returns immediately. (Async spawning is safe here — the nested-spawn restricti
|
|
|
147
147
|
|
|
148
148
|
## 4. Event-flow truth table (DSH)
|
|
149
149
|
|
|
150
|
-
| assistant round
|
|
151
|
-
|
|
|
152
|
-
| final text reply, no tool call
|
|
153
|
-
| text + tool/call(s)
|
|
154
|
-
|
|
|
155
|
-
|
|
|
150
|
+
| assistant round / event | announced? |
|
|
151
|
+
| -------------------------------- | ----------- |
|
|
152
|
+
| final text reply, no tool call | ✅ after throttle |
|
|
153
|
+
| text + tool/call(s) | ❌ (cancelled — narration) |
|
|
154
|
+
| text + `ask_user_question` call | ✅ the question text is kept and announced |
|
|
155
|
+
| `approval/asked` | ✅ immediately (reason, else a fixed prompt) |
|
|
156
|
+
| reasoning only, no text | ❌ (no text block) |
|
|
157
|
+
| streaming chunks | ❌ (filtered) |
|
|
156
158
|
|
|
157
159
|
## 5. Configuration reference
|
|
158
160
|
|
package/docs/DESIGN.zh-CN.md
CHANGED
|
@@ -134,12 +134,14 @@ Claude Code *确实*有 Stop hook。hook JSON(含 `transcript_path`)从 stdi
|
|
|
134
134
|
|
|
135
135
|
## 4. 事件流真值表(DSH)
|
|
136
136
|
|
|
137
|
-
| assistant
|
|
138
|
-
|
|
|
139
|
-
| 最终文本回复,无工具调用
|
|
140
|
-
| 文本 + tool/call(s)
|
|
141
|
-
|
|
|
142
|
-
|
|
|
137
|
+
| assistant 轮次 / 事件 | 是否播报 |
|
|
138
|
+
| -------------------------------- | -------- |
|
|
139
|
+
| 最终文本回复,无工具调用 | ✅ 节流后播报 |
|
|
140
|
+
| 文本 + tool/call(s) | ❌(取消——旁白) |
|
|
141
|
+
| 文本 + `ask_user_question` 调用 | ✅ 保留提问文本并播报 |
|
|
142
|
+
| `approval/asked`(审批请求) | ✅ 立即播报(审批原因,否则固定提示语) |
|
|
143
|
+
| 只有 reasoning,无文本 | ❌(无 text 块) |
|
|
144
|
+
| 流式分块 | ❌(被过滤) |
|
|
143
145
|
|
|
144
146
|
## 5. 配置参考
|
|
145
147
|
|
package/package.json
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-speak",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Make your AI harness speak — verified voice announcements for DSH and other AI coding harnesses (Windows SAPI5 + macOS system voices)",
|
|
5
5
|
"main": "adapters/dsh/speech-hook.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"adapters/dsh/speech-hook.js",
|
|
8
8
|
"adapters/dsh/install.ps1",
|
|
9
|
+
"cordis.patch.yml",
|
|
9
10
|
"engine/",
|
|
10
11
|
"docs/",
|
|
11
12
|
"README.md",
|
|
12
13
|
"README.zh-CN.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|
|
16
|
+
"dsh": {
|
|
17
|
+
"bundle": {
|
|
18
|
+
"patch": "./cordis.patch.yml"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
15
21
|
"keywords": [
|
|
16
22
|
"dsh",
|
|
17
23
|
"dsh-plugin",
|