DeepSeek Harness 插件

Alan2Z/dsh-speak

Star 数 ★ 11 下载量(近 30 天) 1,731 分类 语音与音频 收录于 2026-08-16 npm dsh-speak

零外部依赖、事件驱动、无需额外模型、不消耗任何 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 条目自己导出的 Config schema,把其中的 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。
  • 宿主要求声明在 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-speak

macOS(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 的 修改写回它读到的那一行,彼此同步):

  1. Web UI(1.7.0,推荐):设置 → dsh-speak 设置独立设置页。所有配置项都能直接改并 保存(dsh --dump-config 可见、按 profile 隔离、升级不丢)。
  2. 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 安全上限)。

超出上表范围的取值(volume 0-100、Windows rate -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 覆盖:

  1. 把引擎复制出来改(推荐——默认参数都在这:音量、语速、字数上限、超长提示语、音色逻辑):

    # 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 只动包本身,你的引擎安然无恙。

  2. 直接改 node_modules 里的文件——能改,但下次 npm update 会被覆盖。

  3. 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。

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。