dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
每个 turn 结束自动播报完成、出错时播失败提示(用户中断默认不播,子代理会话默认跳过,均可配置)。三种提醒方式:语音、音效、关闭。默认「音效」内置 20 个音效(提醒 10 + 大自然 10),每个音效可指定给完成/失败/审批事件并单独试听,零配置、不需要任何 API Key 与音频文件;语音走火山 TTS,支持克隆音色与预设音色、批量导入音色 ID,并按文案/音色/语速/音调/音量指纹复用已生成的 mp3,未变则不重复合成。第三个「审批」槽位没有自动触发源,只能手动播放。走 winmm/waveOut 原样播放,绝不改动系统音量或静音状态,播放进程独立拉起、DSH 会话销毁也杀不到声音。仅 Windows。
DSH(DeepSeek Harness)对话语音播报插件:一轮对话结束自动播报「完成」语音, 出现错误 / 工具失败时播报「失败」语音。播放绝不改动系统音量或静音状态。
·
设置页实拍(v0.4.18,DSH 原生设置分区「语音播报」):左 = 语音播报模式(三条文案 + 音色库,含 7 个免费内置音色),右 = 音效模式(每条事件一个音效下拉)。配色跟随宿主主题(浅色 / 深色都清晰),窄面板下控件自动换行不溢出。高级设置里存 Key 的输入框、克隆音色 ID 与本机路径已脱敏。
让 DSH 干完活会开口说话 —— 装完即用,零配置:内置 20 个音效、三条播报文案(完成 / 失败 / 审批, 其中审批只能手动触发), 想用自己的声音再按 §4.1 一键克隆。支持三种提醒方式:🗣 语音播报 / 🎵 音效 / 🔕 关闭。
v0.4.18 修四类界面缺陷:音色库不再重载就消失(React 重挂载自愈)+ 音色库不再时隐时现(槽位出生看门狗)+ 「我的音色库」终于看得见删除按钮(任何行都可删、点完即存;删掉正在使用的音色会自动切到库里第一个)+ 试听图标的悬浮气泡永不出框(浮出瞬间实测矩形再夹回可见区)。另更新两张脱敏效果图。v0.4.17 的两件事(界面更紧凑 + 内嵌免费音色)见下。
free = 微软 edge-tts(不要任何 API Key)。音色库顶部一组「🆓 免费内置音色 · 无需 Key」共 7 个试听音色(台湾腔甜美女声打头), 点「设为当前」立即生效、点「▶ 试听」当场合成并播放,「🎙 生成语音」按钮随之直接可用 (旧版只看火山 Key,选了免费音色按钮永远是灰的)。 前提:本机要有 Python 且装过 edge-tts,首次合成需联网 —— 缺什么界面就明说(edge-python-missing / edge-tts-not-installed / edge-offline),不会崩。产物 mp3 缓存后可离线重放。 这组是服务端虚拟目录:不写进 config.json 的 voices、不混进 /voices 的 list、 也不进 DEFAULT_VOICES,所以开源版「不内置任何音色」的决定与你自己的音色库一行都没变, 切回自己的 Key 音色随时可行。
删掉「🔔 播报开关」卡顶部那排 ⬇下载 / ⬆导入 按钮(「✍️ 播报内容」每条播报本来就各带一对), 并把 25+ 处样式压缩(卡片内边距 16/18 → 11/13、外层间距 12 → 8、标题 17 → 15px、 正文/说明 → 11.5px、主按钮 15 → 13px 等)与 5 段长说明精简;一屏能看全的内容明显变多。
自检规模随版本增长:当前 v0.4.18 的--no-sound实测 472 过 / 0 失败(另有 6 项跳过:都会真出声,--no-sound下直接不执行,不计入通过/失败)。
构成:§1–§16(0.4.16 及更早)共 360 条 + §17「免费内置音色」41 条 + §18「端到端复核缺陷回归」21 条 + §19「第三轮复核缺陷回归」8 条 + §20「客户端界面静态一致性」42 条 = 472。§18 覆盖第二轮实测报回的 6 个缺陷(D1~D6),§19 覆盖第三轮报回的 2 个缺陷(D7 /generate 缺畸形 free id 闸门、D8 free 名长度无上限),均已修复并有断言锁死。v0.4.10 / v0.4.9 界面两处修复:
深色背景下正常,浅色背景下白字落在白底上=完全看不见。现改用宿主的 --dsw-alias-* 设计令牌,随主题自动反转,两种背景都可读;并修掉两处同源问题 (宿主 state-success/warn 静态色不随主题反转、在白底上对比度不足)。
min-width 锁死(最小 494px),面板比这窄时第三个输入框溢出卡片右边缘。现改为可换行 + 解除宽度锁 + 容器可压缩。
v0.4.16 关键修复:三个「界面亮了但没生效」的坑(用户实测反馈):
onChange,React 立刻按旧值回写,表现就是「点了没反应」;而且改完还得再按一次「保存设置」。现改为点完即存。
宿主 alertMode 仍是 voice → 回合结束照旧合成并播放人声。现在三个按钮 (语音 / 音效 / 关闭)统一走 persistPatch({ alertMode }),点完即存。
0.2 下限(前端校验 / 宿主归一化 / 输入框 min),填 0 被静默丢弃或悄悄抬回 0.2。现在音量下限单独为 0,0 = 静音:语音与音效一起哑, 试听也哑,界面显示「🔕 当前音量 = 0(静音)」;语速 / 音调下限仍是 0.2。 注意空框 ≠ 0:清空输入框是「不改」,不会变成静音。
旧界面无论宿主收没收都写「已保存」。现在保存后拿回包里的 applied.prosody 与提交值逐项比对, 不一致就在状态行点名,例如「⚠ 未生效:音量 9(超出允许范围,已忽略)」。
v0.4.15 关键修复:预设音色 Key 不会再被「留空保存」清掉。此前在设置页点「保存音色库」时, 若「预设音色 Key」输入框是空的,会把 config.json 里已存好的 presetApiKey 覆盖成空串(无提示、无备份)。 现在三层守卫:前端留空时根本不提交该字段、宿主只在非空时写入、落盘函数同样只接受非空值 (真要清空必须显式传 clearPresetApiKey: true)。同时新增「已配置 / 未配置」状态回显,留空是否安全一眼可辨。
v0.4.3 关键修复:默认不再做「静音预热」 —— 实测发现预热会在播报前多开关一次音频端点, 打断蓝牙耳机上正在播放的音乐(v0.3.5 引入的副作用)。现在 prewarmMs: 0(默认不预热), 需要时设回 350 可恢复旧行为;同时把 §5.1 的两个实战坑(默认设备登记 / 预热副作用)写清楚。
v0.4.1 / v0.4.2:waveOut 设为默认播放内核;python 解释器自动探测(跳过 WindowsApps 存根); 自测真实播放用例之间加等待、不再叠响。
v0.4.0 开源准备(可直接分发给别人,零配置开箱即用):
playerEngine: "wav"):实测在蓝牙耳机下 不吞第一声、不打断正在播的音乐;由 ffmpeg 首次播放时自动转 WAV 并缓存(~60ms / 200~300KB)。旧内核 "mci"(DirectShow)保留为可选。originalsDir 默认在插件自己的数据目录内、ffmpegPath 默认走 PATH 探测、playerScript/volumeProbeScript 默认留空(只用自带播放器)。使用者无需改代码。.gitignore(排除 __pycache__ / config.json / 音频产物)。发布前已核查:无密钥、无用户名、无内网信息。v0.3.9 备选播放内核 waveOut / WAV(默认仍是 MCI,行为零变化,config.playerEngine = "wav" 一键切换):
type mpegvideo 走 DirectShow,会创建播放图并打开音频端点;在蓝牙耳机上可能触发链路重协商,打断其他正在播放的音乐(用户 2026-09-16 反复反馈"播报/音效后音乐显示在播但没声音")。waveOut(winsound)是 Windows 最基础的播放路径:不启 DirectShow 图、不枚举设备、不改端点格式,对链路和其他播放器冲击最小。lib/play_wav_out.py(waveOut 播放器,含 350ms 静音预热 + 毫秒级阶段日志);host 侧新增 ensureWavCache() 用 ffmpeg 一次性把 mp3 转成 WAV 并缓存(实测单文件 64ms / 236KB,源文件更新会自动重转),playKind/playSfx 在 wav 引擎下优先走它,任何失败自动回退 MCI,绝不因为换内核而静音。v0.3.8 延迟优化(用户要求:对话结束到出声更快)+ 延迟可测量:
open(实测 open 只要 38ms,现在完全被预热窗口吞掉)——唤醒效果不变。play launched 与 turn-end 同毫秒)。[时间.毫秒] sfx-player: +610ms play issued rc=0,以后任何延迟疑问都能直接对着日志算,不用再猜。自测新增两条断言锁住「进程启动→出声 <900ms」和「open 发生在预热窗口内」。v0.3.7 「强制重新生成」改成一次性(用户 2026-09-16 确认):勾上后本次把三条语音全部重新合成(真实调用 TTS),提交成功后自动取消勾选;提交失败则保留勾选便于直接重试。标签改为「强制重新生成(仅本次)」并加了说明气泡。原来的坑:勾选框不会复位,勾一次后每次生成都强制重做三条、白耗额度。
v0.3.6 把「没声音怎么办」写进界面:总控卡「▶ 试听一下」正下方新增常驻提示(dva-audio-tip)——「点了试听没声音?把系统音量拖一下再点一次就有声了 —— 蓝牙耳机 / 虚拟声卡空闲挂起时,系统会把第一次播放吞掉,调音量正好把它唤醒」。
v0.3.5 修「刚生成语音后点试听没声音,调一下系统音量就有声音」(用户实测报告,根因探针实测非推测):
open/play 返回码全 0、status position 正常推进(229→482→733→983 ms)、mode=playing、系统音量 35% 非静音——音频确实在"播放";而同一时刻 Core Audio 探针(IMMDeviceEnumerator)读出默认输出设备 = 耳机 (EDIFIER Lolli Pro 5),即蓝牙耳机(三个 role 一致)。蓝牙 A2DP 链路空闲后会挂起,这段时间 Windows 仍把音频"成功"送进驱动、耳机端不出声;调节系统音量会立刻激活链路——这正是"调一下音量之后就有声音"的来源。虚拟声卡端点未唤醒时同理。play 的返回码此前被完全忽略,播放失败会被当成"播完了"(静默误报成功)。现在检查返回码,失败重试一次。--log-file),含 prewarm ok / play_rc / positions —— 以后遇到无声可查 \voice-alert.log 直接判断是"没播起来"还是"播了但端点没出声"。v0.3.4 依用户反馈再修(界面不再出现任何具体人名,插件可直接分发给别人用):
x/y 进度,结束后明确显示「✅ 语音更新成功!已重新合成 N 条,复用 M 条」,失败显示「⚠️ 语音更新失败:… 合成出错」。/preview / /sfx/play)串行化——等上一个播完再开下一个,避免两个 python+MCI 进程抢声卡导致后一个静默失败。v0.3.3 新增音效提醒:内置 20 个音效(提醒 10:叮咚/电话铃/消息提示/闹钟/滴滴/清脆叮/钟声/成功号角/欢快音/木鱼;大自然 10:鸟鸣/蝉鸣/蛙鸣/雨声/海浪/溪流/风声/篝火/雷鸣/森林),设置页一个全局「提醒方式」三选:🗣 语音播报 / 🎵 音效 / 🔕 关闭。音效模式完全不调用 TTS、不需要 API Key;选音效时展开「完成 / 失败 / 审批」三个勾选(默认 成功号角 / 木鱼 / 叮咚),每行可一键试听。详见 §4.2。
v0.3.3 同版修正:「🎙 我的音色 · 语音合成」卡常驻页面(不再藏进折叠区)——三条播报文案 + 试听 + 一键生成主按钮一目了然。
v0.3.2 设置页 UI 傻瓜化重做(lib/client.js):中文优先字体(微软雅黑,标题 16px/正文 14px/行距 1.6)、卡片式分区(语音播报总控大卡 → 常用设置卡 → 「高级设置」折叠卡,默认收起)、主次分明的按钮布局(「▶ 试听一下」大按钮 + 强调色主按钮,hover/active 反馈);高级项(文案/API Key/音色库/系统音量/日志)收进折叠面板,折叠只影响可见性、DOM 元素全部保留。
v0.3.1 在 DSH 原生设置页新增音色库能力(lib/client.js 渲染的「语音播报」分区,见 §4.1:克隆/预设双路由、批量导入、试合验证;v0.3.1 已移除 v0.3.0 的朗读面板)。
A DSH plugin that deterministically plays a fixed voice alert when a conversation turn finishes and a different one when something failed, plus a settings-section voice library.
中文 · English 每个 turn 结束自动播报「完成」语音 · Plays a "done" voice when any conversation turn ends turn 内出现错误(agent/error 或工具 isError)播报「失败」 · Plays a "fail" voice when the turn had errors (agent/error or tool isError) 同 turn 只播一次(去重)、错误优先(不叠加完成)、错误播报节流(默认 5s) · Per-turn dedup, error-first (never stacks "done"), error throttling (5s default) 「失败」后 3s 内抑制「完成」防连声,可关闭 · Suppresses "done" for 3s after a "fail" — no back-to-back sounds; disable with 0 用户中断(aborted)默认不播;子代理会话默认跳过,均可配置 · User aborts silent by default; subagent sessions skipped — both configurable
中文 · English 🗣 语音:用当前选中音色朗读文案 —— 可选 §4.3 的免费内置音色(零 Key)、自己的克隆音色或预设音色(自动播报只有完成 / 失败两种,详见下方说明) · 🗣 Voice: the selected voice reads your lines — a free built-in voice (§4.3, no key at all), or your own clone / preset voice (only done / fail fire automatically — see note below) 🎵 音效:内置 20 个音效(提醒 10 + 大自然 10),零配置、无需 API Key 开箱即用 · 🎵 SFX: 20 built-in sounds (10 alerts + 10 nature), zero config, no API key needed 🔕 关闭:完成/失败/审批都不提醒 · 🔕 Off: no alerts at all 每条事件(完成/失败/审批)各有一个下拉选音效,选中即生效,旁边带「▶ 试听 / ⬇ 导出 / ⬆ 导入」 · Each event (done/fail/approval) has its own dropdown; picking one applies immediately, with "▶ preview / ⬇ export / ⬆ import" next to it
⚠️ 关于「审批」事件:插件只监听了三个 DSH 事件 —— session/event(turn 结束 → 完成)、agent/error(→ 失败)、tools/result。审批没有自动触发源,engine.js里也没有任何
权限/审批事件监听。approval只能手动触发:GET /dsh-voice-alert/play?kind=approval、
GET /dsh-voice-alert/preview?kind=approval,或在控制文件里写kind=approval。
换成一句话:自动播报 = 完成 / 失败;审批是手动播放的第三个音效槽位。
中文 · English 🆓 免费内置音色(v0.4.17):第三条路线 free = 微软 edge-tts,零 API Key,7 个试听音色(台湾腔甜美女声打头),点「设为当前」立即生效、「▶ 试听」当场合成 · 🆓 Free built-in voices (v0.4.17): a third route free = Microsoft edge-tts, no API key at all, 7 audition voices (Taiwan-accent sweet female first), "set current" applies instantly, "▶ audition" synthesises on the spot 免费音色是服务端虚拟目录:不进 config.json 的 voices、不混进 /voices 列表、不进 DEFAULT_VOICES,绝不影响你自己的 Key 音色 · The free set is a server-side virtual catalogue: never written into config.json voices, never merged into the /voices list, not in DEFAULT_VOICES — your own key voices stay untouched 火山克隆音色 / 预设音色双路由(seed-icl-2.0 / seed-tts-2.0),自动识别类型 · Clone & preset TTS routing (seed-icl-2.0 / seed-tts-2.0), type auto-detected 批量导入(多行 ID,备注 或 JSON)、自动去重;POST /voices/probe 可逐条试合验证(界面不再放「试合」按钮,免费音色同样自动分流到 edge 合成) · Batch import (ID,note lines or JSON) with dedup; POST /voices/probe verifies one voice with a real trial synthesis (no "probe" button in the UI; free voices route to edge synthesis) 生成指纹复用:文案/音色/语速/音调/音量没变就复用既有 mp3,零 TTS 消耗 · Fingerprint reuse: unchanged text/voice/params reuse existing mp3s, zero TTS calls 默认音色库为空,音色名由使用者自填——不含任何具体人名 · Empty default voice library, names filled in by the user — no personal data shipped
中文 · English 默认 waveOut/WAV 播放内核(不吞第一声、不打断蓝牙音乐),MCI 可选,失败自动回退 · waveOut/WAV player by default (no lost first sound, doesn't cut Bluetooth music), MCI optional, auto-fallback 绝不改动系统音量或静音状态(winmm 原样播放) · Never touches system volume or mute state (winmm plays as-is) 播放进程经隐藏 PowerShell → Start-Process 独立拉起,DSH 会话销毁杀不到声音 · Player spawned detached via hidden PowerShell → Start-Process, immune to session teardown 播放失败全线静默降级(缺文件→蜂鸣、异常→仅日志),绝不影响 DSH 运行 · Silent fallback everywhere (missing file → beep, exceptions → log only), never affects DSH 蓝牙耳机专项适配:默认不预热(prewarmMs: 0)不再打断音乐,实测文档齐全 · Bluetooth headset tuning: prewarm off by default (no music cuts), documented with measurements
中文 · English 试听旁一键下载 mp3(每条播报独立下载,可选增益版/原件) · One-click download next to every preview (per-kind, gain or original) 试听旁一键导入 mp3(用你自己的音频替换某条播报,导入后自动试听一次) · One-click import next to every preview (replace any kind with your own audio, auto-previewed once) 导入前自动备份旧文件、同时写 origin 与 loud 两份、清掉过期 wav 缓存 · Auto-backup before overwrite, writes both origin and loud copies, drops the stale wav cache 导入限 10 MB,前后端双重校验,非 mp3 直接拒绝(不会误覆盖语音文件) · 10 MB cap enforced on both sides; non-mp3 rejected so voice files are never clobbered 导入的 mp3 不会被「生成语音」冲掉:改别的文案时该条自动跳过合成(P0 修复) · An imported mp3 survives later synthesis runs: the kind is skipped while a text edit regenerates the others (P0 fix) 每行显示「已导入」状态,可一键撤销导入回到自动生成的语音 · Each row shows its imported state and offers one-click undo back to the synthesised clip 后台错误原因翻成中文人话(不是把 not an mp3 甩给用户) · Host error reasons are translated into plain Chinese instead of raw English
中文 · English DSH 原生设置页「语音播报」分区:总控卡 + 常用设置 + 高级折叠,中文优先字体 · Native DSH settings section "语音播报": master card + common settings + collapsible advanced, CJK-first typography 紧凑排版(v0.4.17):卡片内边距 16/18 → 11/13px、外层间距 12 → 8px、卡标题 17 → 15px、正文/说明 → 11.5px、分节标题/输入框/次按钮 → 12.5px、主按钮 15 → 13px、行内动作按钮/音效行 → 11.5px;并删掉「🔔 播报开关」卡顶部多余的 ⬇下载 / ⬆导入 一排(每条播报本来就各带一对) · Compact layout (v0.4.17): card padding 16/18 → 11/13px, outer gap 12 → 8px, card title 17 → 15px, body/desc → 11.5px, section titles/inputs/secondary buttons → 12.5px, primary button 15 → 13px, row action buttons & sfx rows → 11.5px; plus the redundant ⬇download / ⬆import strip on the master card removed (each cue row already carries its own pair) 说明文字一律悬浮展示(v0.4.17):常驻段落收进 ⓘ / 🔈 图标后的气泡,悬停(或键盘聚焦)才显示,平时不占版面;音色库每行还改成「一行放下」——行容器不换行、文本列可收缩到省略号、按钮不缩不折,窄面板下按钮不再被挤出卡片 · Hover tooltips everywhere (v0.4.17): standing paragraphs moved into bubbles behind ⓘ / 🔈 icons, shown on hover (or keyboard focus) and taking no room otherwise; voice-library rows now stay on one line — no wrap, shrinkable text columns, non-shrinking buttons, so buttons no longer get pushed off the card on narrow panels 毫秒级阶段日志(\voice-alert.log),任何延迟/无声可对着日志定位 · Millisecond stage logs (voice-alert.log) to pinpoint latency or silence 免凭据控制通道:写 control.txt 即触发真播,status.json 快照验收 · Credential-free control: write control.txt to trigger a real play, read status.json to verify 配色跟随宿主主题:浅色/深色背景下都清晰(走 --dsw-alias-* 设计令牌,不写死色值) · Theme-aware colors: legible on both light and dark backgrounds (host design tokens, no hard-coded colors) 472 项自测断言(--no-sound 实测 472 过 / 0 失败,另有 6 项跳过:都会真出声;构成 = §1–§16 共 360 条 + §17 41 条 + §18 21 条 + §19 8 条 + §20 42 条),HTTP 路由仅回环 · 472 self-check assertions (--no-sound: 472 pass / 0 fail; 6 skipped — all would really play audio; 360 in §1–§16 + 41 in §17 + 21 in §18 + 8 in §19 + 42 in §20); HTTP routes loopback-only
事件 · 语音 · 文件(默认优先播放增益版) 任意一个 turn 结束 · 完成 · voice-alert-complete-poetic-loud.mp3 该 turn 内出现过错误(agent/error 或工具 isError) · 失败 · voice-alert-fail-poetic-loud.mp3 立即错误播报(工具失败/agent 报错当下,节流 5s) · 失败 · 同上 手动验证(HTTP 路由)仅手动 · 完成/失败/审批 · voice-alert-approval-poetic-loud.mp3
自动播报只有「完成 / 失败」两种。 上表最后一行是手动触发的验证入口(/play?kind=approval),不是自动事件 —— 插件没有监听任何权限/审批类 DSH 事件。详见 §3 末尾的说明。
音频永不改动系统音量与静音状态:播放器用 winmm/MCI「原样播放」(系统多大就多大,系统静音就无声), 不读写任何音频控制接口。响度问题通过音频文件本身解决(见 §4)。
用途 · 事件 · 出处(已装官方包) 判「一个 turn 结束」 · session/event 且 event.type === "turn/end" · @deepseek-ai/dsh-agent-loop/lib/index.js:994(finally 里 append turn/end) 识别 turn 开始(用于归属错误) · session/event 且 event.type === "turn/start" · 同上 :926 判「出现错误」 · agent/error,payload {agent, turn, step, error} · 同上 :863(throwError),且在 turn/end 之前发出(:991 → :994) 判「工具失败」 · tools/result 的 result.isError · @deepseek-ai/dsh-tools/lib/types/index.js:3287(以 exec.agent 为 scope 派发)
关键结论:
turn/end 每个 turn 只发一次(不是每个 step),所以含大量工具调用的长任务只会按 turn 播报;含工具的一轮会话不会因为 step 多而重复播报。agent/status(running→idle)只在「整个 agent 彻底空闲」时变化,不是每 turn 信号,因此没有采用。session/event 的历史回放不发事件(构造函数种子不 emit,见 dsh-session/lib/index.js 注释),所以 DSH 重启 / 恢复会话不会误报。sessionId#turn 为键登记,重复的 turn/end 直接忽略(缓存上限由 dedupeCacheSize 控制)。turn/start 打开该会话的 turn 记录;该 turn 内出现 agent/error 或 tools/result.isError 即把该 turn 标记为 errored;turn/end 时若已 errored → 只播失败,不叠加播完成。
errorMinIntervalMs(默认 5000ms)约束,同一时间窗内只播一次。suppressCompleteAfterFailMs(默认 3000ms)之内,turn/end 的 complete 一律抑制并记日志(suppressedComplete 计数)。两个开关彼此独立:errorMinIntervalMs 管 fail 自身节流, suppressCompleteAfterFailMs 只管「fail 之后是否还允许立刻播 complete」;设 0 关闭本闸门。 实测场景(宿主日志 2026-09-15 11:28:23):agent/error 播 fail 后 0.26s,同 turn 的 turn/end 又播 complete → 本闸门消除。