DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Approval Sound — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
A

dsh-approval-sound

Approval Sound

Audible alert for the dsh web GUI: plays a distinct sound the moment the harness raises a prompt that is waiting for you — approval panels AND ask-user-question panels — with synthesized per-seam voices or your own uploaded audio, pushed through a limiter so it can exceed 100%

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:rongqingwen/dsh-approval-sound#6000a30efed3205131bc4517ab1a8bb31a05f708
READMECompatibilityVersions

Compatibility and provenance

Approval Sound is published as dsh-approval-sound and currently resolves to version 0.4.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/6/2026

Versions

0.4.1stable
10/6/2026

Related plugins

Loading related plugins…

Latest
0.4.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/6/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in integrations-communication.

Acp App@deepseek-ai/dsh-acp-appThe dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-baseAb Publishdsh-ab-publishBrowser-automation publishing tool that logs into and posts to arbitrary websites, in one publishable plugin packageAb Wechat Scrapedsh-ab-wechat-scrape--- description: "The out-of-tree wechat_scrape tool: retrieves WeChat official-account articles, filters promotional blocks, and saves each as a Markdown file, for maintainers of this machine's dsh profiles." kind: "package-reference" ---Remote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and r

README

dsh-approval-sound — 权限提示音

给 dsh Web GUI 的审批提示音插件:当工具请求越权执行、页面弹出「等待审批」面板时,立刻响一声,并把请求按工具类型配成不同的音色(命令行升级、文件写入、网络请求、子代理各不一样),这样你不用一直盯着屏幕也知道有人在等你点「允许一次」。

声音是浏览器端用 Web Audio 实时合成的:不打包任何音频文件、不走网络、不需要额外依赖。

为什么需要它

DSH 的审批面板出现在输入框上方,但默认没有任何声音。如果窗口在后台、或者你正好切到别的程序,一个需要批准的 bash 升权请求会一直静默地卡在那里,直到你回到窗口才看见。

这个插件补的就是这一段:审批被发起的那一刻就出声。

它是怎么知道"弹窗了"的

它是怎么知道"弹窗了"的

弹窗不是页面自己判断出来的,而是宿主侧的瀑布事件。DSH 里有两条"正在等人类决定"的链路,本插件两条都监听:

链路什么时候触发你看到的界面
approval/request某个工具请求越权执行(ctx.approval.request())带「允许一次 / 拒绝」的审批面板
user-questions/request助手要问你问题(ask_user_question),计划审核也走这条带选项按钮、跳过 / 提交 的提问面板

为什么要两条都听:对用户来说这两者没区别——都是"有个对话框在等我点"。如果只监听审批那条,而你的会话把审批策略设成了 never,那么审批服务会在派发瀑布之前就返回 'rejected'(只往会话日志里写一条 approval/asked),根本没有审批弹窗存在,于是插件永远不响——哪怕你屏幕上明明有一个提问面板在等答案。这正是本插件 0.2.0 的真实缺陷。

宿主半区为每条链路注册一个只观察、不干预的监听器:

  • 它先记录这次请求(归属链路 + 一个短标签 + 一个依次递增的编号),然后原样调用 next(),把后续 waterfall 的结果一模一样地还给服务;
  • 请求结束(发起方的 AbortSignal 触发,或应答者返回)时把它从待弹窗集合里移走;
  • 页面每 700ms 拉一次 /api/dsh-approval-sound/pending,看到新的编号就响。

短标签从请求里就地取:审批用工具名(bash / write…),提问优先用问题的 header,计划审核用 plan-review。问题的正文不会送到浏览器——音色只需要一个短标签,而正文是你自己的提问内容。

这样做的结果是:弹窗的判定和行为完全没有被改动。审批策略是 ask 还是 never、失败是否 fail-closed,全和没装这个插件时一致(插件只是把 next() 的结果原样透传)。

为什么响得"刚刚好一次"

页面端用三层去重,专门对付现实里的各种抖动:

情况行为
页面加载时已经有一个待弹窗不响(第一轮成功轮询把已有的静默登记,你已经在看它了)
页面加载时宿主不可达不响,并且不认为已经"登记过";恢复后第一次拿到列表时静默登记
同一个弹窗,后续每次轮询都还在只响一次
中间丢了 1~2 次轮询不重复响(宿主编号是单调递增的,用高水位判断)
短时间内连续弹好几个由「最短间隔」压成一声,避免叠成噪音
标签页在后台照常轮询(有意为之:后台才最需要提醒);只有「仅窗口不在前台时提醒」打开时才响

⚠️ 测不出声音时先看这一条:「仅窗口不在前台时提醒」如果开着,你盯着窗口等弹窗时是不会响的(因为你就在前台)。想边看边测,把它关掉。

音色

先按链路分family,再按标签细分——"助手在问我问题"和"某个工具想要越权"是两种不同性质的打断,听感不该一样:

音色触发音高听感
提问面板user-questions/request 的普通提问0.80×明显偏低,最"温和"
计划审核同上,且标签是 plan-review0.80×同上
命令行升级bash、pwsh、cmd、shell、terminal_*、run_code、job_*1.00×急促双响,最"扎耳"
文件写入write、edit、read、glob、grep、present1.04×同样的双响,音高略高
网络请求web_search、web_fetch1.08×再高一点
子代理 / 团队subagent、subagent_fork、send_message、workflow1.12×再高一点
通用其它一切1.16×最高

提问一族刻意压在审批一族下面,所以哪怕你没看屏幕,也能从音高听出"这次是要我回答"还是"这次是要我授权"。

音色本身也可以在设置里整体换掉:急促双响 / 清脆铃声 / 钟声 / 水滴 / 自定义音频 / 静音。

自定义音频(上传自己的铃声)

设置页里有独立的 「自定义音频」 卡片:

  • 上传音频文件——支持 wav / mp3 / ogg / m4a / aac / flac / webm,最大 8 MiB;
  • 上传成功后音色自动切到「自定义音频」(你刚挑的文件,显然就是想听它);
  • 可以随时更换或移除;
  • 文件只保存在本机:浏览器读到字节后直接 POST 给插件自己的 loopback 路由,落在 $DSH_HOME/dsh-approval-sound/custom.<扩展名>,不会上传到任何地方。

两个刻意的处理,让"随便一个音频文件"真的能当警报用:

  1. 按自身峰值归一化:导出音量很小的音频会被拉到接近满幅,不会变成"自定义铃声听起来比原来还轻";
  2. 上传时校验扩展名与文件头:两者必须一致(并单独校验 MP4 的 ftyp 品牌),所以改名骗过的文件在保存前就被拒绝,而不是变成以后一句"怎么没声音"。

只换扩展名(比如把 .wav 改名成 .mp3)会被拒绝,并给出明确原因。这是有意的——否则得到的是一段解不出来的字节。

宿主重启时会以磁盘上的文件为准重建元数据,所以手改 config.json 无法凭空"声明"一个不存在的铃声;真的丢了文件就会回落到内置音色。

音量:可以开过 100%

原来的上限是 100%,实测在"机器正在放别的声音"时不够醒目。现在:

  • 音量范围 0 – 150%,默认 100%;
  • 输出链上串了一个限幅器(DynamicsCompressor,阈值 −6 dB、比率 20:1),所以超过 100% 时是把响度推上去、而不是把波形削平——尖峰被压住,听起来是"更响"而不是"更破";
  • 合成的音色本身也抬了基频增益,配合限幅器大约比原来高 6–8 dB。

想要更响,除了拉满 150%,还可以把「重复次数」设成 2、间隔设短一点,让双响变成连续催。

前置条件

  • DSH 0.1.5 或更高(需要 webServer 服务,也就是 Web 端 / 桌面端)。无头(headless)、ACP、SDK profile 没有 Web 层,本插件无事可做。
  • 浏览器支持 Web Audio(所有现代浏览器都支持;不支持时设置页会明确说明,而不是静默失败)。

安装

# 从本地目录安装(开发/本地)
dsh plugin --profile desktop add link:E:/AI/DSH/插件/dsh-approval-sound

# CLI 的 web profile 用 web
dsh plugin --profile web add link:E:/AI/DSH/插件/dsh-approval-sound

也可以直接在桌面端的「插件」管理界面里装本目录。

装完必须重启 dsh web / 桌面端。

宿主半区(host.js)只在启动时加载一次;改完插件、或者刚 add 进来之后,运行中的进程里跑的还是旧代码,只能靠重启换掉。客户端半区(client.js)在刷新页面时重新拉取,但宿主半区的路由没换,页面也拿不到数据。

不想用 CLI 的话,手工安装等价于:

  1. 把本目录整体拷进 profile 的 node_modules/dsh-approval-sound/;
  2. 在 ~/.dsh/profiles/<profile>/package.json 的 dsh.profile.bundles 里加上 dsh-approval-sound;
  3. 重启 dsh web / 桌面端。

本包零构建、零运行时依赖:host.js 与 client.js 就是可直接加载的产物,不需要 pnpm build,也不 import 任何第三方包(宿主只用 Node 内置模块;浏览器的 React 由页面的模块表提供)。

这一点是刻意的:插件以 link: 安装时 pnpm 不会安装它自己的依赖,所以任何 import 'some-package' 都可能在某些 profile 里解析失败——而宿主半区 import 失败不会报错给你看,loader 只会把条目停在 inactive,表现为"插件装了但什么都没发生"。

配置

设置页 权限提示音 里可直接改,改动即时生效并持久化:

键默认含义
enabledtrue总开关;关掉后不再出声(仍继续登记,方便排错)
soundalert音色:alert 急促双响 / chime 清脆铃声 / bell 钟声 / drop 水滴 / custom 自定义音频 / off 静音
volume1音量 0–1.5(150%)。0 是合法的"先接好线、之后再开声"
repeat1重复次数:1 只响一次,2 连响两次……最多 6
repeatIntervalMs900重复之间的间隔(120–5000ms)
cooldownMs600两次提醒之间的强制静默(0–10000ms),防止连续弹窗叠成噪音
onlyWhenUnfocusedfalse只在窗口不在前台时才提醒
customName''只读。已上传的文件名,由宿主按磁盘现状回填
customBytes0只读。已上传文件的字节数
customAt0只读。上传时间戳

三个 custom* 字段不接受客户端写入:磁盘上的文件是它们唯一的真源,否则一个页面就能"声明"一个并不存在的铃声,而表现只是静默无声。

设置页里还有:

  • 试听 按钮——用它确认声音、音量、音色,同时解除浏览器的自动播放限制;
  • 音频状态提示——只在需要时出现:不支持 Web Audio、音频被浏览器拦截、或自定义音频正在加载;
  • 上传/解码失败提示——点了上传却没成功时给出一行原因,不会静默。

设置页只有设置:早期版本的「监听状态」和「诊断」两张卡片已按要求移除。宿主侧的诊断数据仍然保留在下面的 /diagnostics 接口里(供排错时用命令行查看),只是不再占用面板。

关于"音频被浏览器拦截"

浏览器不允许页面在用户没有任何交互前出声。所以:

  • 插件在首次点击 / 按键时会自动解锁音频(监听器一直挂着,直到真正变成 running);
  • 如果设置页显示「音频被浏览器拦截」,**点一下「试听」**即可,之后就一直正常;
  • 正常使用中你总会点过页面(比如把审批面板点出来),所以这条几乎不会成为问题。

接口

全部为同源 JSON,且都带 loopback 围栏(非本机请求一律 403)。写入使用 POST,避免误触浏览导致改配置。

方法路径用途
GET/api/dsh-approval-sound/pending当前待审批集合、生效配置、编号水位(页面每 700ms 轮询)
GET/api/dsh-approval-sound/diagnostics上面全部 + 监听器错误 + 最近 40 条事件(设置页按需拉取)
POST/api/dsh-approval-sound/config局部更新配置并持久化
POST/api/dsh-approval-sound/sound上传自定义音频(原始 application/octet-stream 正文,文件名放在 x-dsh-sound-name 头里,encodeURIComponent 编码)
GET/api/dsh-approval-sound/sound取回已安装的音频(无则 404),带正确的 content-type 与 nosniff
DELETE/api/dsh-approval-sound/sound移除音频,并把音色回落到内置音色

实现备注(给改这个插件的人):这些路由必须注册成 kind: 'exact',不能用 prefix。组合出来的 DSH Web 应用自己也挂了一个 /api 前缀路由(浏览器会话鉴权 + RPC),它会用 401 抢先应答任何没被精确匹配的 /api/... 路径——插件的 /api/<名字>/ 前缀路由根本轮不到。精确路由在所有前缀之前匹配,所以这条路是稳的。

上传用 .tgz 之外的单文件报文而不是 multipart:一个文件就是全部负载,multipart 解析器只会带来额外攻击面。

实现备注 2(这条最坑,别改回去):监听这两条 waterfall 必须用 { prepend: true, global: true }。

  • prepend:waterfall 是一条链,监听器返回结果就等于"认领"这次请求并终止整条链(不再调用 next())。官方应答者(把请求转发到浏览器面板的那个桥)在启动时就注册了,位置在链首;它一认领,链就断在那里,排在它后面的监听器永远不会被调用。所以"只观察、不干预"的监听器必须排在认领者前面——否则它注册得好好的、ctx.on 不报错、事件总线也正常,但就是一次都不触发(observed 永远 0)。这正是本插件 0.1.0–0.3.0 始终不出声的真正原因,而且两条链路都中招(所以当初即使审批策略是 ask,审批面板也一样不会响)。
  • global:跳过总线按监听器上下文做的作用域过滤。本插件要观察进程里所有会话/agent 的弹窗,没有任何理由被过滤;被过滤掉的监听器同样是静默失效。

这两种失效方式都不报错,所以宿主侧内置了自检:/diagnostics 的 eventBus.attached 会直接从事件总线的监听器表里读回 {count, position}——position 必须是 0,不是 0 就说明又被排到认领者后面了。这条排错时用命令行看一眼即可(设置页不再显示它):

curl.exe -sS http://127.0.0.1:19387/api/dsh-approval-sound/diagnostics

数据

配置持久化在 $DSH_HOME/dsh-approval-sound/config.json(原子写入:临时文件 + fsync + rename);上传的音频是同目录下的 custom.<扩展名>(同样是临时文件 + fsync + rename,所以中途崩溃不会留下半截文件被当成铃声播出去)。

插件只读审批元数据:工具名和一个自增编号。审批的 reason 文本不会进浏览器;它也不写任何会话数据、不改审批策略、不影响任何决定。

已知限制

  • 只在页面打开时出声。页面关掉就没人听了——这是所有浏览器端提醒的固有限制。想让"关掉页面也能提醒"变成系统级通知,需要走桌面端原生通知通道,不在本插件范围内。
  • 只在 ask 策略下有意义。策略是 never 时审批根本不弹窗,插件自然也不会响(这正是 ask/never 该有的区别)。
  • 轮询而不是推送。最坏情况有约 700ms 延迟(实际体验是"几乎同时")。宿主侧没有向页面推送事件的公开通道,轮询是唯一不依赖私有 API 的做法。
  • 编号水位是进程级的。宿主重启后编号从 1 重新开始,页面此时也一起刷新了,所以不会互相干扰;但如果你用同一页面跨宿主重启(页面不刷新而宿主重启),水位会挡住重启后的头几个提醒,直到编号重新超过旧水位——刷新页面即可。
  • 自定义音频只在页面能解码它时有效。浏览器不支持该编码时,设置页会给出错误、并且响铃会退回到内置音色(而不是静默);如果关掉窗口前刚上传、解码还没完成,那一次提醒会等解码完成后补响。

开发自测

两个 harness 覆盖了宿主和浏览器两半,都不需要启动 DSH:

node scripts/dev-harness.mjs         # 宿主:94 项断言(两条链路、监听器位置、关闭边界、编号、围栏、配置、上传/取回/删除/重启对账、释放)
node scripts/dev-client-harness.mjs  # 浏览器:74 项断言(静音登记、去重、音色、响度/限幅、自定义音频、提问面板、上传、焦点、降级)

想看到页面内部的决策过程,加上环境变量即可(输出 absorb / alert 之类的行):

DSH_HARNESS_TRACE=1 node scripts/dev-client-harness.mjs

scripts/ 不在 package.json 的 files 里,不会被发布。

宿主 harness 会给自己一个临时 $DSH_HOME,所以跑测试不会读到你真实配置(它有一条断言专门验证"手改 config.json 不能凭空声明铃声");浏览器 harness 用 node:vm 加一层最小页面桩(__ModuleLoader__、document、fetch、定时器、可录音的 AudioContext、可驱动的 input[type=file]),直接驱动真正的 client.js。

许可证

MIT