零外部依赖、事件驱动、无需额外模型、不消耗任何 token的语音播报插件。使用系统自带自然语音进行播报,支持win/mac双平台;最终回复、审批与提问提醒、可选事件播报(回合结束/命令完成/目标变更/工具出错/待办更新)、最终回复可重播、双语言可视化设置。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-speak
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:Alan2Z/dsh-speak
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
中文 · English

让 Agent 在长任务完成时开口告诉你——不用再盯着屏幕等。
dsh-speak 通过系统语音合成把 Agent 的最终回复朗读出来——Windows 上优先使用自然
语音(Windows 11 内置,或 Windows 10 上经
NaturalVoiceSAPIAdapter 注册,如晓晓),macOS 上使用系统自带的 say
(可跟随 Siri 自然音色);没有时优雅回退到系统自带中文语音。本项目为
DeepSeek Harness 而生,但结构上
任何 harness 都能接入。
特性
- 全自动:DSH web 插件监听会话事件流,自动播报最终回复 (跳过 reasoning/工具调用旁白,合并同一回复的多步消息)。
- 提醒你:审批请求(Agent 等你操作时会播"需要你的审批")和 Agent 通过
ask_user_question提出的问题都会播报。 - 最终回复重播(1.7.0):每条最终回复(回合尾部)操作栏有 🔊 按钮——点击 重播该条回复、再点停止、点另一条切换。语音执行完全由 DSH host 拥有(浏览器 关掉也继续读)。
- host 语音队列(1.7.0):同一时间只运行一个语音进程,队列自动串行; WebSocket 实时同步"正在读哪条、队列长度"到 UI。
- 多事件可选播报(1.6.0):回合结束、命令完成、目标变更、工具出错、 待办更新等事件都可选播报,各自独立开/关(默认关)。
- 可视化配置(1.7.0):设置 → dsh-speak 设置独立设置页,所有配置项(总开关、 自动朗读、Markdown 清洗、代码块、事件开关、固定提示语…)直接改,无需手写 YAML。
- 总开关(1.6.0):一键静音所有播报。
- Bundle 自动注册(1.3.0):把包声明进
dsh.profile.bundles,插件通过包内 自带的cordis.patch.yml自动注册,无需手动写 patch 条目。 - 尽力而为:绝不抛错、绝不阻塞 harness、绝不破坏会话。
- 自然语音:Windows 优先使用自然语音——Windows 11 内置语音包,或 Windows 10 上经 NaturalVoiceSAPIAdapter 注册的语音(如晓晓);macOS 使用系统朗读声音 (新版可跟随 Siri 自然音色)。均回退到任意已安装语音。
- 健壮的文本清洗:去掉会让语音合成静默失败的 markdown/URL/emoji, 并守卫适配器单次朗读的字数上限。
- 引擎可移植:任意进程一行即可朗读:
Windows
powershell -File speak.ps1 -Text "你好"/ macOS./speak.sh -t "你好"。
工作原理
harness 事件(DSH 会话事件 / Claude Code Stop hook / 任意方式)
│
▼ adapters/… (harness 专属触发器:过滤、节流、取消)
▼ engine/speak.ps1 / speak.sh (与 harness 无关:清洗文本 → SAPI5 / say)
▼ 🔊 你听到最终回复
适配器负责把 harness 专属事件转成引擎调用;引擎负责清洗文本并朗读, 与 harness 完全解耦。完整设计见 docs/DESIGN.zh-CN.md。
前置条件
Windows
- Windows 10 或 11,任意较新的 PowerShell。
- 自然语音:
- Windows 11(21H2–23H2):系统已内置自然语音包,无需额外安装——在 设置 → 辅助功能 → 讲述人 或 设置 → 时间和语言 → 语音 中启用/切换即可。
- Windows 11 24H2/25H2:自然语音已改为 MSIX 应用包,
System.Speech可能 枚举不到(SpeechSynthesizer找不到自然语音、回退到机械音)——与 Windows 10 相同,需安装 NaturalVoiceSAPIAdapter 桥接。 - Windows 10:需要安装 NaturalVoiceSAPIAdapter, 并用它的 VoiceDownloader 手动下载你需要的中文或其他语言的自然语音包。
- 没有自然语音时,引擎回退到系统自带语音(如 Huihui)。
macOS 要求
- macOS(Apple Silicon / Intel 均可),系统自带
say命令,无需安装任何软件。 - 中文音色与 Siri 音色的选择入口/坑见 macOS 一节。
DSH 版本
- 已在 DSH 0.1.7-rc.2 上验证。0.1.1 之后有两轮 host/客户端 API 变更,本插件均已适配:
- 0.1.7 把 settings provider API 换成了 Config 投影:settings 服务改为读取每个
已激活 Loader 条目自己导出的
Configschema,把其中的 volatile 字段投影成设置 界面(宿主侧ctx.settings.describe(),浏览器侧ctx.configForms)。1.6.0–1.8.x 使用的settings.register(namespace, schema, { base })已删除,浏览器服务settingsScope也被configForms取代。因此本插件把 schema 作为Config导出, 而设置 namespace 就是它的条目 id(dsh-speak;1.8.x 遗留的speech-hook条目 id 依然可以绑定,见下)。 - 0.1.2 起 Session snapshot 不再携带会话视图(Conversation target)数据——
🔊 按钮改为通过 Chat 目标的 hook
useChat取被点击消息的文本;同一版本还删除了@deepseek-ai/dsh-settings的installSettingsSection/settingsNamespace。
- 0.1.7 把 settings provider API 换成了 Config 投影:settings 服务改为读取每个
已激活 Loader 条目自己导出的
- 宿主要求声明在 dsh-market 实际读取的位置:
package.json的engines.dsh(>=0.1.7-rc.2)。市场卡片与「适配当前 DSH」筛选读的正是这个字段,因此只有在 某个宿主版本上实测通过后,这个下限才会移动。 - 1.8.2 依赖 0.1.7 引入的设置模型:在更老的宿主上浏览器半侧会一直等待
configForms服务。DSH ≤ 0.1.6 请用 1.8.1。
安装与快速开始
DSH — 方式 A:npm 插件(推荐)
# 1. 把插件装进你的 web profile(会写入 ~/.dsh/profiles/web/package.json 的
# dependencies **和** dsh.profile.bundles)
dsh plugin --profile web add dsh-speak
# 2. 到此为止——包自带 bundle patch,条目(id: dsh-speak)由它注册。
# 不要再手写一行 insert:同一个条目注册两次会让 id 不唯一,DSH 的 config-editor
# 随后会拒绝每一次设置写入:
# settings/rejected: Configuration for "dsh-speak" is overridden by a home patch or command-line overlay
# (语音照常工作,只有设置页静默弹回——所以特别难查。)
#
# 如果仍想在 YAML 里固定选项,只加这一行**顶层行**(设置页就地改写它;
# config 放顶层行才是编辑器支持的形状,见「DSH 插件配置」):
# - id: dsh-speak
# name: 'dsh-speak'
# config: {}
#
# 0.1.7 起条目 id 就是设置 namespace:你的选项会以这个 key 存在同一个文件里。
# 1.8.x 遗留的 id(speech-hook)继续可用——浏览器半侧两个 id 都认。
# 3. 重启 DSH web 应用 — 之后回复会被自动播报
在应用内插件市场安装是同一条路径(会把包加进
dsh.profile.bundles); 只有下面两种手动安装才需要手写行。一个 profile 只能有一种注册方式,绝不能两种都有。
没有 pnpm?
dsh plugin内部转发给 pnpm,并非所有机器都装了。可以用 npm 直接完成同样的安装:npm install --prefix "$env:USERPROFILE\.dsh\profiles\web" dsh-speakmacOS(bash):
npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak单纯
npm install不会注册插件:要么把包名加进~/.dsh/profiles/web/package.json的dsh.profile.bundles,要么用文件安装那两行 ——不要两者都做。
引擎随包分发(node_modules/dsh-speak/engine/),无需额外拷贝。
想让 Agent 帮你装? 把本仓库地址(
https://github.com/Alan2Z/dsh-speak) 丢给你的 DSH 会话,让它照着这份 README 安装即可——它读的就是你正在看的这份文档。 只需要同意它对~/.dsh(工作区外)的写入审批。
DSH — 方式 B:文件安装(不需要 npm)
# 1. 克隆
git clone https://github.com/Alan2Z/dsh-speak.git
cd dsh-speak
# 2. 一键安装:拷贝引擎 + 插件,并注册到 cordis.patch.yml
powershell.exe -NoProfile -ExecutionPolicy Bypass -File adapters\dsh\install.ps1
# 3. 验证引擎能出声
powershell -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.dsh\hooks\speak.ps1" -Text "你好,语音播报已就绪。"
# 4. 重启 DSH web 应用 — 之后回复会被自动播报
文件安装脚本做了这些事:
| 文件 | 目标位置 |
|---|---|
engine/*.ps1 |
%USERPROFILE%\.dsh\hooks\ |
adapters/dsh/speech-hook.js |
%USERPROFILE%\.dsh\profiles\web\plugins\ |
| 注册条目 | 追加到 %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml(先备份) |
macOS
同一套适配层原生支持 macOS——插件自动检测平台,改调 engine/speak.sh
(系统自带的 say 命令)而不是 speak.ps1。自 1.2.0 起 macOS 引擎随 npm 包
正式分发,无需安装任何额外软件。
# 1. 装进你的 web profile(没有 pnpm 也能装——dsh plugin 才依赖 pnpm)
npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak
# 2. 在 ~/.dsh/profiles/web/cordis.patch.yml 末尾注册(裸包名即可,无需 file:/// URL):
# - insert:
# - id: dsh-speak
# name: 'dsh-speak'
# - id: dsh-speak
# name: 'dsh-speak'
# config: {}
# 3. 无需重启——patch 监视器会热更新;回复在节流后(约 1.5 秒)自动播报;
# 带工具调用的回复会在回合结束时补播最终回复
装过 pnpm 也可以
dsh plugin --profile web add dsh-speak,效果相同。
音色(重要,有两个坑)
- 默认跟随系统朗读声音(系统设置 → 辅助功能 → 朗读内容 → 系统朗读声音)。 macOS 26 上该选择框旁有个 ⓘ 圆圈图标,点开才是完整音色列表——普通 下拉框里没有 Siri 自然音色;可在 ⓘ 列表里选"普通话 Siri 声音1(男声)"等。
- Siri 声音(设置 → Siri → 声音)与系统朗读声音是两个独立设置;Siri
音色不暴露给
say -v '?',无法按名选择,只能作为系统默认生效。 - ⚠️ 坑 1(实测复现):打开"朗读内容 / Siri 声音"设置面板(哪怕不改任何 选项)会把系统朗读声音漂移/重置成经典音色"婷婷(Tingting)"——音色突然变了 就回到 ⓘ 入口重新选择。
- ⚠️ 坑 2:日志在
$TMPDIR/dsh-speech-hook.log(os.tmpdir(),不是/tmp)。 - 想强制指定音色用
-v Eddy|Flo|Tingting(say -v '?'列出可用音色)。 say没有音量参数——音量跟随系统输出音量。
单独测试引擎(不装 DSH 也行)
curl -sfL -o ~/speak.sh "https://cdn.jsdelivr.net/gh/Alan2Z/dsh-speak@main/engine/speak.sh"
chmod +x ~/speak.sh
~/speak.sh -t "你好,Mac 版语音播报测试"
~/speak.sh -t "测试" -v Eddy -r 200 # 指定音色 + 语速
Claude Code
在 ~/.claude/settings.json 注册 Stop hook:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\\path\\to\\dsh-speak\\adapters\\claude-code\\stop-hook.ps1"
}
]
}
]
}
}
其他任何 harness
直接从你的 Agent / 包装脚本 / 工具里调用引擎:
# 播报一句话
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speak.ps1 -Text "构建完成"
# 播报较长总结(阻塞,读完才返回)
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-summary.ps1 -Text "…"
# 需要用户注意时(阻塞,适合提问/授权场景)
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-prompt.ps1 -Text "请做出选择"
配置
引擎参数
详见 docs/DESIGN.zh-CN.md §5 配置参考:
speak.ps1 -Text "…" -Volume 50 -Rate 1 -MaxChars 300 -LongTextMessage "本次播报内容较长,请自行阅读。"
DSH 插件配置
两种改法,任选其一(改 UI 或改 YAML 都落进同一份 profile patch——设置服务把 UI 的 修改写回它读到的那一行,彼此同步):
- Web UI(1.7.0,推荐):设置 → dsh-speak 设置独立设置页。所有配置项都能直接改并
保存(
dsh --dump-config可见、按 profile 隔离、升级不丢)。 - profile patch 的
config块(等效):
# ~/.dsh/profiles/web/cordis.patch.yml
# 必须写成两行:insert 行负责“提供条目”,顶层行承载设置页要改写的 config。
# DSH 的 config-editor 只会就地改写顶层行;config 嵌在 insert 里时,UI 会接受修改、
# 运行中的插件也会立刻生效,但这次写入随后会被回滚——下次启动 dsh 时值就悄悄变回去了。
- insert:
- id: dsh-speak # 0.1.7 起条目 id 就是设置 namespace
name: 'dsh-speak'
- id: dsh-speak
name: 'dsh-speak'
config:
enabled: true # 总开关:false 时完全不播报
automaticSpeech: true # 自动朗读最终回复
queueAllMessages: false # true = 所有 assistant 消息立即入队朗读(中间消息也读)
replayFullRead: false # true = 手动重播跳过超长文本截断,完整朗读
cleanMarkdownFormatting: true # Markdown 转自然语音
readInlineCode: true # 朗读行内代码(去掉反引号)
codeBlocks: smart # all | smart | replace(围栏代码块)
codeBlockMaxChars: 300 # smart 模式下的代码块字数上限
codeBlockReplacementText: 'You can see the code in our history.' # replace 时的替代文本
throttleMs: 1500 # 播报前的合并延迟(毫秒)
engine: '' # 引擎路径覆盖;'' = 自动解析
announceApprovals: true # 播报审批请求
announceQuestions: true # 播报 ask_user_question 提问内容
stripApprovalPrefix: true # 剥离审批原因里的 "escalate sandbox to ...: " 前缀
questionGapMs: 2000 # 多个提问播报之间的停顿(毫秒)
longTextMode: message # message | heading(念最大字号 markdown 标题)
longTextMessage: '本次播报内容较长,请自行阅读。' # message 模式下的固定提示语
maxChars: 300 # 引擎单次朗读字数上限(macOS 默认 0 = 不限)
volume: 50 # 仅 Windows
rate: 0 # 0 = 引擎默认(Windows SAPI 刻度 / macOS wpm)
# —— 可选事件播报(1.6.0,默认全关)——
announceTurnEnd: false # 回合结束("第 N 轮对话完成")
announceCommandDone: false # 命令完成/失败(command/done)
announceGoalChange: false # 目标创建/更新/完成(goal/change)
announceToolErrors: false # 工具调用出错时播报(英文详情截掉,tool/result)
announceTodoWrite: false # 待办列表更新(todo/write)
解析顺序:schema 默认值 → patch
config→ UI 用户设置。写进 YAML 的字段 同样出现在 UI 中。平台差异:maxChars在 macOS 默认 0(say无上限), Windows 默认 300(SAPI 安全上限)。超出上表范围的取值(
volume0-100、Windowsrate-10..10)会在送进引擎前被钳制 ——SAPI 遇到越界的Volume/Rate会抛异常,表现就是"没声音且没有任何提示"。被钳制 时会记一行日志:settings 值超出范围,已钳制: <字段> <给出值> -> <实际值>。 显式写0一律尊重原意(volume: 0静音、maxChars: 0不限长、throttleMs: 0不合并)。
config必须写在顶层行上。 这不是风格问题:如果把config:嵌在insert行里,设置页照样能显示、改完运行中的插件也立刻生效,但 DSH 的 config-editor 无法就地改写那一行——它会追加一个顶层行再把写入回滚,于是下次启动 dsh 时值会 悄悄变回去。可以用python scripts/settings-ui-check.py验证(它会断言写入真的 落进了 profile patch)。
从 1.8.x 升级: 直接在设置页里改,或把旧的
config:块挪到新的顶层行上(形状见上)。 0.1.7 之前选项存在~/.dsh/settings.yaml的dsh-speak:段里;该文件已被 DSH 迁移为settings.yaml.imported,而段名对不上任何 Loader 条目的段(1.8.x 是在代码里注册 namespace,所以dsh-speak:谁也匹配不到)会被留在原地。如果你改过默认值,把那些值 抄进上面那个config:块即可——现在 DSH 找的 key 就是条目 id(dsh-speak)。
选项说明
| 选项 | 默认值 | 效果 |
|---|---|---|
enabled |
true |
总开关:关闭后所有播报都不触发(最终回复/审批/提问/可选事件/重播) |
automaticSpeech |
true |
自动朗读最终回复;手动重播始终可用 |
queueAllMessages |
false |
true 时每条 assistant 消息立即入队朗读(中间消息也读,FIFO);默认只读节流后的最终回复 |
replayFullRead |
false |
true 时手动重播跳过超长文本的标题截断(longTextMode: heading),完整分段朗读 |
cleanMarkdownFormatting |
true |
把 Markdown 转成自然语音文本(链接保留文字去 URL、标题/强调符号清理) |
readInlineCode |
true |
朗读行内代码(去掉反引号标记) |
codeBlocks |
smart |
围栏代码块处理:all 全读 / smart(≤codeBlockMaxChars 才读)/ replace 用替代文本 |
codeBlockMaxChars |
300 |
smart 模式下的代码块字数上限 |
codeBlockReplacementText |
You can see the code in our history. |
replace 模式(或超限的 smart)下朗读的替代文本 |
throttleMs |
1500 |
回复文本等待多久才播报(合并同一回复的多步消息) |
engine |
'' |
显式引擎脚本路径;'' 自动解析:包内 engine/<平台> → ~/.dsh/hooks/<平台> |
announceApprovals |
true |
播报 approval/asked 事件(审批原因,或固定提示语) |
announceQuestions |
true |
播报 ask_user_question:每个问题单独朗读,带"问题N"序号(多问题时)与"选项N"序号(与 UI 编号一致);多个问题之间停顿 questionGapMs |
questionGapMs |
2000 |
多个提问播报之间的停顿(毫秒),0 = 不停顿 |
stripApprovalPrefix |
true |
剥离审批原因里的固定英文模板前缀(escalate sandbox to danger-full-access: ),保留中文说明 |
longTextMode |
message |
message = 超长念固定提示语;heading = 改念最大字号 markdown 标题(规则见下) |
longTextMessage |
本次播报内容较长,请自行阅读。 |
message 模式下超长文本改念的固定提示语(UI 可编辑) |
maxChars |
平台相关 | 引擎单次朗读上限。macOS 默认 0(say 无上限);Windows 默认 300(SAPI 超过约 375-470 字会静默失败) |
volume |
50 |
仅 Windows(0-100);macOS 音量跟随系统 |
rate |
0 |
语速:Windows SAPI 刻度(-10 到 10,0 = 正常,推荐 0 / 稍快 1-3);macOS words-per-minute(默认 175,稍快 200) |
announceTurnEnd |
false |
回合结束时播报"第 N 轮对话完成/中断/异常结束"(turn/end) |
announceCommandDone |
false |
命令执行完成/失败时播报(command/done) |
announceGoalChange |
false |
目标创建/更新/完成/暂停/恢复时播报(goal/change,含目标标题前 40 字) |
announceToolErrors |
false |
工具调用返回错误时播报"工具调用出错"(英文错误详情/技术 code 截掉,只保留中文详情)。触发条件:tool/result 带 error(结构化失败身份)或结果块 isError === true。注意 shell 命令非零退出不算——pwsh/bash 把 exit code: N 当结果数据上报(dsh 明文如此设计),只有基础设施失败(spawn 错误、abort)和 fs 这类结构化失败才置 isError |
announceTodoWrite |
false |
agent 更新待办列表时播报"待办已更新:n/m 完成"(todo/write) |
超长文本模式
清洗后文本超过 maxChars 时:
message(默认):念longTextMessage(本次播报内容较长,请自行阅读。, 可在 UI 或 YAML 里编辑)。heading:在原始文本里挑最大字号的 markdown 标题——#数量最少者优先, 并列取第一个。整段没有任何标题时改念"有头有尾的开头":取开头maxChars长度的窗口并回退到窗口内最后一个句末标点;若这样会砍掉半个窗口以上则保留整窗。 句末标点中英双语识别:全角。!?;与…无条件算;半角.!?;只在后面跟 空白、右引号/右括号时才算,落在窗口最后一位时会多读一位判断——所以英文 「句号+空格」在边缘照样算,而Version 0.1.这种小数点不算。(1.8.0 之前这里只念 第一个非空行,听感上就是"从第二行开始不念了"。)选中的候选仍会清洗并受maxChars上限约束,若其本身仍超长则回退提示语。
完整架构与设计取舍见 docs/DESIGN.zh-CN.md。
自定义(升级不丢)
想调行为又不想 fork,而且改完不会被 npm update 覆盖:
把引擎复制出来改(推荐——默认参数都在这:音量、语速、字数上限、超长提示语、音色逻辑):
# Windows Copy-Item "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-speak\engine\speak.ps1" "$env:USERPROFILE\.dsh\hooks\my-speak.ps1" # macOS cp ~/.dsh/profiles/web/node_modules/dsh-speak/engine/speak.sh ~/.dsh/hooks/my-speak.sh然后在 config 块里指向你的副本:
Windows:务必保住文件的 UTF-8 BOM。
speak.ps1是 UTF-8 脚本,而 Windows PowerShell 5.1 只能靠开头那三个字节EF BB BF知道这一点;编辑器保存时若把它 丢掉,系统会改用 ANSI 代码页解码,脚本里的中文会变乱码——症状是静默无声或 修剪错乱,且不报错。为此仓库里的脚本已把逻辑部分全部写成纯 ASCII,所以 丢 BOM 只会让中文注释和默认提示语变乱码。改完可以用Get-Content -Encoding Byte -TotalCount 3 你的-speak.ps1检查(应为239 187 191), 或跑node scripts/test-engine-static.js。# 改的是顶层行(不是 insert 行——见「DSH 插件配置」) - id: dsh-speak name: 'dsh-speak' config: engine: 'C:/Users/<你>/.dsh/hooks/my-speak.ps1' # macOS 用 ~/.dsh/hooks/my-speak.sh插件按
config.engine→ 包内引擎 →~/.dsh/hooks/的顺序解析引擎,所以你的副本 优先生效;npm update只动包本身,你的引擎安然无恙。直接改
node_modules里的文件——能改,但下次npm update会被覆盖。fork 仓库——完全掌控,想发自己的包也行。
排障
| 现象 | 原因 | 解决 |
|---|---|---|
| 完全没有声音、无报错 | 未启用/安装自然语音 | Win11:在 设置 → 讲述人/语音 中启用自然语音;Win10:安装 NaturalVoiceSAPIAdapter 并下载语音包。直接测 speak.ps1 |
| 长回复从不播报 | 适配器单次 Speak 有字数上限 |
已默认在 300 字处守卫——必要时调低 -MaxChars |
| 念到第二行就停/像是被切断 | longTextMode: heading 下,文本超过 maxChars 且整段没有 markdown 标题时,旧版引擎只念第一个非空行(1.8.0 之前) |
1.8.0 已修(改念"有头有尾的开头");想换策略可用 message 模式或调高 maxChars |
听到 工具调用出错:Error: cannot read … |
"是否中文"的详情判据只检查"含有汉字",英文报错里夹着中文目录名就能骗过它(1.8.0 引入的回归) | 1.8.0 已修——详情需满足"汉字数量多于拉丁字母数量" |
| 含大量 emoji 的文本静默 | SAPI 遇到 emoji 会静默失败 | 引擎已自动剥离 |
| 插件加载失败 | 插件名用了 Windows 原始路径 | 改用 file:///C:/… URL 形式(安装脚本会自动处理) |
| 升级到 1.8.2 后设置页不出现 | profile patch 那行写着 disabled: true,或条目 id 既不是 dsh-speak 也不是旧 id speech-hook |
去掉 disabled,并把 id 改成两者之一 |
| 设置页能开、但每次改动都被弹回 | DSH < 0.1.7(configForms 已取代 settingsScope) |
升级 DSH,或继续用 dsh-speak 1.8.1 |
DSH 0.1.7+ 上每次改动都被弹回,且插件日志出现 已有实例在运行 |
条目被注册了两次(例如包既在 dsh.profile.bundles 里、又手写了一行 insert:):id 不再唯一,config-editor 会拒绝每次写入(settings/rejected: Configuration for "dsh-speak" is overridden by a home patch or command-line overlay);语音照常工作,所以特别难查 |
只保留一种注册方式(见「方式 A」),然后重启 |
| 改完立刻生效,但重启后变回旧值 | config: 块嵌在 profile patch 的 insert: 行里——DSH 的 config-editor 只会就地改写顶层行,嵌套写入会被静默回滚(仍然回 ok: true) |
按「DSH 插件配置」把 config 放到顶层行;python scripts/settings-ui-check.py 会断言写入真的落盘 |
| macOS:音色突然变成"婷婷" | 打开过"朗读内容 / Siri 声音"设置面板导致系统朗读声音漂移 | 系统设置 → 辅助功能 → 阅读与朗读 → 系统声音 → ⓘ 入口重新选择 |
macOS:在 /tmp 找不到日志 |
os.tmpdir() 是 /var/folders/.../T,不是 /tmp |
日志在 $TMPDIR/dsh-speech-hook.log |
插件诊断日志:Windows %TEMP%\dsh-speech-hook.log;macOS $TMPDIR/dsh-speech-hook.log
仓库结构
engine/ 与 harness 无关的语音引擎(PowerShell + SAPI5 / bash + say)
speak.ps1 / speak.sh 清洗 + 朗读(适配层唯一需要打交道的接口)
speech-prompt.ps1 阻塞式短提示播报
speech-summary.ps1 阻塞式回复总结播报
adapters/
dsh/ DSH web 插件 + 一键安装脚本
speech-hook.js 会话事件触发器(节流/取消 + 可选事件 + FIFO 语音队列 + WebSocket + Config 投影的设置表单)
install.ps1 拷贝 + 注册 + 备份
claude-code/
stop-hook.ps1 Claude Code Stop hook 触发器
client/
client.js DSH 浏览器端 bundle:回合尾部 Speak/Stop 按钮 + 设置 → dsh-speak 设置页
docs/
DESIGN.zh-CN.md 完整设计文档:设计取舍、踩坑记录、扩展指南
scripts/ 测试 + 手动开发辅助脚本(不随 npm 包发布)
test-engine-static.js 引擎静态不变量:.ps1 的 BOM + PowerShell 语法解析、.sh 的 LF(prepublishOnly 也会跑)
test-engine-longtext.js 两个引擎的长文守卫契约(speak.ps1 -DryRun / speak.sh 的 perl)
test-speech-hook.js 宿主插件:事件触发、队列、工具出错详情过滤
test-client-bundle.js 浏览器 bundle:slot 注册 + 组件渲染
test-settings-integration.js Config 投影接线(volatile 引用 + 实时写入)+ 已删除 API 回归守卫
session-log-dump.js 读取 DSH 会话日志(手动:看引擎究竟收到了什么文本)
settings-ui-check.py Playwright UI 检查(手动:需要运行中且已鉴权的 dsh)
dsh-events-check.py Playwright 折叠行检查(手动)
编写新适配器
三种参考模式:事件流(DSH)、Stop hook(Claude Code)、Agent 自调用
(在 shell 里调 speech-summary.ps1)。无论哪种,适配器只需做一件事:
拿到最终回复文本 → 调用引擎。详见
docs/DESIGN.zh-CN.md §7 扩展。
License
MIT — 见 LICENSE。
链接
同类插件
PolinniZhong/dsh-omi-voice★ 74
DeepSeek Harness 对话内朗读:点一下即可朗读、暂停、继续 AI 回复,豆包 TTS 自然音色(BYOK),只读最终回答并过滤代码、表格与图形,本地引擎,插件零 Key。
PensiveFei/dsh-voice-scribe★ 34
面向 Web UI 的语音输入插件:点按 Alt(或 Alt+空格)开始/停止听写,支持浏览器内置 Web Speech(零配置)或 OpenAI 兼容云端 ASR,可选经 DSH 已配置模型润色,带设置页。
1624318455/dsh-plugin-tts★ 21
用免费 Edge TTS 或你自己的 RVC 音色朗读 AI 回复:消息朗读与自动朗读、长文自适应分块渐进播放(无缝衔接)、音色包仓库一键安装、便携 RVC 运行时。
WizisCool/dsh-ears★ 21
面向 DeepSeek Harness (dsh) 的语音输入插件:输入框的麦克风按钮把语音转成草稿文本,支持多种语音识别后端,可选经 dsh 自有 LLM 路由润色,并带原生设置页。
PerryLink/dsh-talk★ 15
DeepSeek Harness 的语音输入输出:麦克风语音转文字与文字转语音。
qishuilalala/dsh-voice-mode#dsh-voice-mode★ 15
DeepSeek Harness Web UI 全双工语音对话:按钮或 Ctrl+Shift+V 进入,持续聆听(停顿自动发送)或按住说话,zipformer2 流式识别入可编辑草稿、可选唤醒词;回复按句 Edge TTS 朗读并显示实时字幕,开口即打断播放与回合(真 barge-in);识别模型本地推理、Edge TTS 在线合成,无需 API Key。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。