dsh-review-hub · 明镜
给 DSH 加一层审批大脑:能自动放的自动放,该问你的才问你,事后还能查、能撤回。
AI 是灯下献策的幕僚,镜子只负责把每道审批照得清清楚楚。
中文 · English | 🤖 你是 AI?先读 README-AI.md;要自己改代码,动手前再读 AGENT.md。
它是什么
DSH(DeepSeek Harness)在工具需要提权时会停下来问人:approval/request 是一条瀑布链,链末端没人应答就等于拒绝。本插件注册成唯一的裁决者,在这一刻做三件事:
- L0 硬风险直接给你 —— 删目录、丢改动(
git reset --hard这类)、碰凭据、远程执行、改系统、批量破坏,一律不问 AI、不进记忆。 - 重复操作学会放行 —— 同一条无害命令你亲手批满 3 次,之后它自己放(按会话隔离,换会话重新学)。
- 其余交给 AI 预审 —— 一次小模型调用(≈300–500 tokens),只允许两个结论:
allow(放)或ask(拿不准,交给你)。AI 没有否决权。
因为裁决者唯一,每条记录的"谁放的"是事实而不是猜测:ai / memory / human / external / fallback。
AI 预审默认覆盖 ops 插件的命令:dsh-ops 发布的
ops_bash与只读的ops_read_files/ops_grep/ops_glob/ops_inspect_local_file/ops_status、以及它的后台任务工具job_output/job_list都在默认 AI 名单里——不用额外配置,装好就会预审它们。其它工具(write/edit等)走记忆档,plugin_manager这类默认直接问你;要增减都在插件页「高级设置 → 先交给 AI 的工具 / 不交给 AI 的工具」里改。
决策链
一次 approval/request 进来,按固定顺序过四道关:谁先命中谁负责,后面的层不再参与。
① L0 硬风险(确定性,不花钱)
├ 命中 → 直接问你;不进 AI、不计入记忆、永不自动放行
└ 未命中 ↓
② 记忆(确定性)
├ 本会话里这条操作你亲手批满阈值(默认 3 次)→ 自动放行
└ 未命中 ↓
③ 文件级(确定性,需你显式开开关)
├ 这个文件的人工批准次数 ≥ 阈值 → 自动放行
└ 未命中 ↓
④ AI 预审(一次小模型调用,≈300–500 tokens)
├ allow → 放行(decidedBy = ai)
└ ask → 交给你;AI 没有否决权,只能放行或提问
↓
⑤ 人工
顺序不是随意的:确定性在概率之前。凡是规则能判的(硬风险、记忆、文件级)一律不交给模型;模型只处理"剩下那些无害、但还没见过"的请求。
每条记录都写明是哪一层做的决定:ai / memory / human / external / fallback,并附带 agentKind(请求方)、tier(档位)、aiSource(AI 是否真的审过)。事后想回答"这条为什么没让 AI 看",看这三列就够了;被 AI 打回、再由你决定的那些,行内会标 AI 打回 · 人工通过/拒绝,日期左边写着真正做判断的评审模型。
你会看到什么
- 输入框上方一张淡色卡片:AI 通过(淡绿)/ 记忆通过(淡蓝)/ 待审批(淡黄),右侧有复制和关闭按钮;没有审批时整行不出现,不占地方。
- 别的会话有审批时只出一张灰色聚合卡:
会话名 · AI通过 0 · 记忆通过 1,不和当前对话混在一起,只能手动关。 - 对话顶部「审批」标签:完整时间线(工具、归属、理由、参数、耗时、记忆进度),放大镜找历史;有新审批时标签出现 🔴 红点,列表用
—— 以上为新增审批 ——把"看过的"和"新的"隔开。 - 右侧栏「改动」:点某行的「改动」看那一次调用的 diff(双行号),一键撤销(连点两次确认,3 秒不点作废)。
- 需要你决策时,提示卡和系统通知直接写着 AI 的意见:
AI 建议人工 · high · 理由说读文件但命令会删目录—— 批之前你就知道 AI 看到了什么。 - 需要你决策时还有一张悬浮卡(右上角),内容与输入框上方那张一致——宿主的人工审批面板会盖住输入框上方那条,这张浮在窗口层。
- 台账导出:CSV / JSON,按会话或全部。
和同类插件的关系
DSH 的审批生态里已经有不少插件,思路各不相同。本插件把这些思路里能共存的那些收进一个实现:确定性优先的硬风险层(不需要模型)、AI 预审层(只能放行或转人工)、按会话的学习层,以及完整的面板/diff/撤销。
如果只记四条差异,是这四条(其余都是实现细节):
- AI 只有
allow/ask,没有否决权——deny被强制降级成「转人工」。多数同类插件让模型直接拒(fail-closed),一次误判就变成拒绝执行。 - 记忆只数「人工批准」——AI 或记忆自己放行的次数不推进计数器,所以「学满自动放行」的每一分证据都是人给的,可审计。
- 模型只看脱敏摘要——送审的是压缩后的参数摘要(路径/命令裁剪过),不是原文;凭据类路径连摘要都不给。
- 有完整的审批 UI——时间线、diff、撤销、CSV/JSON 台账导出,而不是「弹一下就没了」。
下表版本核对于 2026-11-03 / 09(registry.npmjs.org/<包名>/latest 与 GitHub 页面复核)。本表只作历史参考——这些包都在更新,读的时候以当天的 npm / GitHub 为准:
| 插件 | 版本 | 它的做法 | 与本插件的关系 |
|---|---|---|---|
@deepseek-ai/dsh-experimental-auto-review |
0.1.6-alpha.1(next = 0.2.2-rc.1) |
官方随宿主的实验性基线:用当前会话模型审每个调用,无确定性危险清单,风险/理由不落盘 | 本插件的对比基线:确定性层(不花钱、可预测)、记忆、审计 UI 是它没有的 |
dsh-auto-approve |
0.7.1 | 保留沙箱、只裁 escalation;确定性危险清单先于分类器;会话记忆(同工具 + 同参数原文哈希,TTL 30 分钟、仅在内存);分类器失败回落人工 | 功能面最接近的一个。差异:本插件的记忆是持久 + 阈值 + 只计人工批准,且危险清单命中永不进记忆(这点两家一致) |
dsh-plugin-auto-review |
0.1.0 | 移植 Codex Guardian / Grok escalation 的审查提示,模型可拒绝,无人值守时直接拒 | 反面参照:机器可拒绝正是本项目的红线(人在环里才有最终决定权) |
dsh-auto-review |
0.15.0 | 链首第二模型预审;reviewer 不可用时 fail-closed(直接拒绝,不给人机会) | AI 层(L3)取了它的分层、每轮预算与熔断;去掉否决权,并修掉 PTC 折叠参数看不到就盲拒 |
dsh-perm-gate |
5.4.4 | 确定性阶梯 P0 硬拒绝 → P1 会话授权 → P2 静态规则 → P3 可选 LLM → P4 人工 | 取它"确定性优先":本插件的 L0/L1 就是不花模型的确定性层 |
moon09300731/dsh-approval-gate |
0.5.2(2026-08 停更) | Flash 判 SAFE/RISKY,五类硬风险永不学习;绿条 + 时间线 + diff/撤销 | 产品形态(学习循环、输入框上方提示、diff/撤销)的主要参考;但它是拦截式门(自己就是整条链),本插件是分层 + 永不阻断 |
dsh-approval-gate-safe |
0.2.4 | 上面那个的兼容加固 fork,含更宽的 Windows/PowerShell 命令词表 | 命令词表的参考 |
@goodandready/dsh-approval-gate |
0.1.31 | 纯规则:认得出的危险直接拦,认不出的交给人 | 与本插件 L0 同一纪律:"看不懂就交给人" |
dsh-approval-ledger |
1.2.2 | 批量审批 + 风险门 + 可导出台账 | 台账导出的参考 |
dsh-approval-hotkeys |
0.2.0 | Enter 确认 / Esc 取消 | 未采用:原生审批弹窗不在插件手里 |
@aiwayds/dsh-approval-policy |
0.2.1 | 给子代理 / cron 的审批请求有界答复 | 与"按请求方分档"同源,本插件默认对子代理/定时/看板更严 |
dsh-approval-toast |
1.0.3 | Windows 原生 toast 里直接批 | 功能互补(触达),但它也能作答,同时启用要确认不抢答 |
表里的包名都能点开(指向 npm 页面;moon09300731/dsh-approval-gate 那个是 GitHub 仓库,它没有发布到 npm)。
和两个最接近的插件的横向对比
真正会被拿来和明镜比的是两个:官方 auto-review 决定「AI 能不能替你决定」,dsh-auto-approve 决定「学到的记忆该怎么用」。三者不冲突、可以同时装——因为挂载点不同,这也正是必须说清的地方。
- 明镜 = 审批链上的 answerer(
approval/request):不改任何预设,任何档位走到审批都过同一套链。 - 官方 auto-review = 工具执行前的闸门(
tools/pre-execute),只有选了AUTO预设的会话才动作。 - dsh-auto-approve = 权限预设(新增
sandboxed-auto),只有选了这档的会话才动作。
官方那列按 next = 0.2.2-rc.1(与本机宿主同版)的 lib/index.js 逐处核对;auto-approve 那列按 0.7.1 的 npm 包与 README 核对(2026-11-09)。
| 维度 | 明镜(本插件) | 官方 @deepseek-ai/dsh-experimental-auto-review |
dsh-auto-approve |
|---|---|---|---|
| 挂载点 | approval/request answerer,全预设、按工具分档(AI / 记忆 / 直接人工) |
tools/pre-execute 闸门,仅 AUTO 预设会话 |
权限预设 sandboxed-auto,仅选择该档的会话 |
| 审查范围 | 所有走到审批的请求 | 该会话里每一次工具调用 | 只裁沙箱 escalation(作者估算约 2%) |
顶层 run_code |
照样过 L0 硬风险与记忆学习,只是不进 AI(tier=memory)——run_code 里写 rm -rf 会被拦下来问你 |
豁免审查:exec.parent === undefined 时直接放行(它怕审外层会与子调用重复计费) |
不在其审查面内(只裁沙箱 escalation) |
| AI 能否否决 | 不能:deny 强制降级成 ask,人永远终审 |
能:deny 且会话 override=never → 直接拒绝执行 |
有限:危险清单命中或分类器拿不准就转人工,never 档可硬拒 |
| 审查失败 | 转人工,aiSource 记明原因 |
fail-closed:拒绝执行 | 回落人工 |
| 送模型什么 | 脱敏摘要(≤240 字符,sk- / Bearer / 密钥字段名全 redact);凭据类路径连摘要都不给 |
整个会话可见历史 + 原始参数 JSON + 工具 schema,无脱敏 | 命令 + justification + 沙箱目标 + 用户原话(≤2000 字符,超长转人工) |
| 学习 / 记忆 | 持久落盘 + 阈值(默认 3 次人工批准)+ 按会话隔离 + 旋钮归一化;可逐条查看与删除 | 无:同一条命令每次都重审、重新付历史 token | 内存 + TTL 30 分钟,重启即失;分类器批过的也计入 |
| 用哪个模型 | 可钉任意 provider/model(全局默认 + 按会话覆盖) |
固定 = 会话自己的车道 | 可配便宜的分类模型 |
| 成本护栏 | 60 秒窗口预算、连续失败熔断 5 分钟、指纹级裁决缓存(TTL 可调) | 无预算 / 无熔断 / 无缓存 | 无(每次 escalation 一次分类调用) |
| 审计与 UI | 自有台账(decidedBy / agentKind / tier / aiSource / 冻结阈值)+ 审批页 / 记忆页 / 统计 / diff·撤销 + CSV·JSON 导出 |
无自有 UI,只留审批事件与错误码 | 无 UI,进程内报告(作者自述不是审计账本) |
| 卸载时 | 只摘 answerer,预设与数据不动 | 处于 AUTO 的会话被切换成 danger-full-access |
撤掉该预设 |
| 规模 / 已知敞口 | 12 个服务端模块 + 约 4.3k 行客户端,97 条测试;凭据类路径落 L0 硬拦,快照不存其正文 | 单文件 617 行;顶层 run_code 调用豁免审查 |
11 文件约 180KB;README 自述默认会放行写 ~/.dsh/profiles/(运行时自改供应链路径) |
怎么选:三者不是零和,真要比就比三件事——① 否决权在谁手里(官方能让 AI 拒你,明镜的 AI 只会说「建议放行」或「交给你」);② 学习能不能审计(30 分钟内存哈希 vs 跨重启、只计人工、逐条可删);③ 数据边界(全量历史与原始参数 vs ≤240 字符脱敏摘要)。
安装
写进 profile(~/.dsh/profiles/desktop),而不是跑 npm install:
package.json的dependencies加"dsh-review-hub": "link:<本仓库路径>";bundles数组加dsh-review-hub,并移除其它会作答approval/request的审批插件(见上一节;纯通知类可留,但要确认不抢答);- 用 DSH 自带的 pnpm 执行
install --lockfile-only(不要全量 install,会清掉原生模块构建); - 建 junction:
node_modules/dsh-review-hub→ 本仓库目录; - 检查
cordis.patch.yml:任何带name:的旧补丁行都会在 bundle 行删掉后继续把插件挂回来; - 重启宿主(服务端半身加载时绑定;客户端半身刷新页面即可)。
详细步骤与踩坑见 README-AI.md 与 AGENT.md。
设置在哪
- 日常设置 → 对话顶部「审批」标签页里的「设置」:AI 审查、审查模型、记忆自动通过、放行阈值、提示卡、三档卡片时长、系统通知。开关是主机同款滑块,每项下面一句人话。
- 审查模型是两级下拉(先选 provider、再选模型),名单来自宿主自己的模型目录(
llm.listProviders()+ 每个 provider 的listModels()),默认跟随会话;下面一行小字「实际使用」告诉你此刻真正在用哪个车道,尾部刷新图标可手动重拉目录。目录为空(宿主没接 llm)时才退回手填。 - 高级设置(不要乱改) → 插件页的插件卡片:兜底轮询间隔、保留条数、审查超时、每轮上限、熔断次数、按请求方分档、先交给 AI 的工具(只填增量,内置基线写死在代码里)与不交给 AI 的工具黑名单(黑名单优先,能盖掉内置基线;write / edit 属记忆档,两边都动不了它)、卡片预览(常驻:就地渲染五种卡片 + 本会话实时 + 两个悬浮卡自测按钮),以及启用「清空历史」(默认关;开启后仍需连点三次,服务端也校验这个开关)。
常见问题
Q:统计页里各模型的「缓存命中」为什么这么低(甚至一直是 0)?
这不是插件没统计到,而是目前的提示词太短:一次评审的 system + user 只有约 900~1000 字符(约 500 token),而主流提供商的 prompt cache 都有最低可缓存长度(常见 1024 token 起)。低于门槛就不会缓存,命中率自然是 0;字段本身仍然照实记录(输入 / 输出 / 缓存读取三个数都能看到)。
各家的缓存策略也不一样:有的按前缀逐字节比对、有的按 128 token 分块、有的要求 cache_control 显式标注,还有的只对「同一会话、连续追加的前缀」生效。所以同一个插件换一条线路,命中率就可能完全不同。真要提高只能让上下文变长(见 1.3.x 的上下文增强审批),而不是在这个长度上去优化前缀。
Q:为什么 AI 没出现就直接问我?
硬风险(6 类)根本不进 AI;或者没有可用模型服务、请求超时、熔断中。记录会写明 aiSource。
Q:AI 会不会把危险操作放过去?
会——但具体要看对应模型的能力和你是否信任 TA,这也是这套设计唯一真正的风险点。 AI 判 allow 时,这一次审批就不再问你了,所以这个问题的答案取决于两件事:那条车道上的模型到底能不能看懂这段命令,以及你是否愿意把自己的一部分判断交出去。
但能约束的部分已经约束住了:① 6 类硬风险(删除、丢改动、凭据、远程执行、系统级、批量破坏)根本不送 AI,直接找你;② AI 只看到脱敏后的参数摘要与理由,判定标准写死为"可逆、范围明确、与理由一致",拿不准就必须 ask;③ 你可以随时关掉 AI 审查,也可以在高级设置里把工具拉黑、或把某个请求方档位调严。剩下的那一小块——模型的判断力——不是这套设计能替你保证的;时间线就是给你复核用的。
Q:为什么批了很多次它还没学会? 记忆只统计人工批准(AI 通过、记忆通过都不计数,否则会自我强化),硬风险永不学习;进度显示为"记忆 n/3 次"。
Q:AI 用哪条线路?花多少 token? 默认跟随当前会话的模型,也可以在设置里钉一个便宜车道。它只看到脱敏后的参数摘要(≤240 字符)与理由,一次约 300–500 tokens;裁决结果落盘缓存,重启后不重复花钱。
Q:推荐使用什么模型? 够用就好,贵不等于好,但参数越小越容易出错。
Q:想让某个会话无人值守、不再被 AI 预审打扰? 可以只停这一个会话:在该会话里打开「不与其他会话同步」,再关掉 AI 审查。其它会话的 AI 预审照旧——这是按会话关闭,不是全局开关。
Q:能多开窗口吗? 能,但两个窗口各自记"已看/基线",红点可能不同步。
安全边界
写清楚这里能防什么、不能防什么,比含糊其辞有用。
谁能读到这些数据:插件的 HTTP 面(
/api/review-hub/*)只监听宿主自己的 loopback 端口,并校验Host是回环地址、Origin/Referer与之同源(桌面壳的dsh-app:协议单独放行)。没有额外的 token,也没有「请求必须来自浏览器」这层校验——实测桌面壳的请求既不发送Origin也不发送Sec-Fetch-Site,加这层校验会把面板整个打成 403(2026-11-09 真机验证)。所以本机上的任何进程,只要能连到那个端口,就能读写台账与设置;宿主端口每次启动随机分配(--port 0)是目前唯一的实际屏障。想收紧有两条现成的路,都在插件配置(不是面板设置)里:strictOrigin: true—— 要求Sec-Fetch-Site。默认关,因为桌面壳不发这个头,开了面板会全 403。writeProofCookie: "<cookie 名>"或writeProofHeader: "<头名>"—— 只拦写方法(POST),读与 SSE 不受影响。默认空 = 关。这个值不该靠猜:先用debugHeaders: true把每个请求的 header 名与 cookie 名记到~/.dsh/storages/review-hub/http-probe.log(只记名字,不记值;最多 400 行),对比面板与curl的差异,再把面板独有、curl没有的那一个填进来。磁盘上有什么:
~/.dsh/storages/review-hub/下有台账(每条审批的工具名、参数、理由、判定者)、记忆表、设置、AI 裁决缓存,以及snapshots/—— 后者保存write/edit目标的改前全文(单文件上限 256KB,每会话最多 64 条)。也就是说:你批准过的文件内容,会以明文留在这台机器上,包括你编辑过的.env、配置文件。怎么清掉:删除整个
~/.dsh/storages/review-hub/目录即可(插件下次启动会重建)。审批页的「清空历史」只清台账与记忆,不动snapshots/。AI 层看到了什么:只送脱敏后的参数摘要(≤240 字符)与理由,不送完整文件内容;裁决结果落盘缓存,重启不重复计费。
AI 没有否决权:它只能说
allow或ask。deny会被降级成ask——这条是硬约束,见 README-AI.md。
性能
空闲时零请求(一条本地 SSE 挂着,30 秒一次心跳);审批事件一条一帧(实测单条记录 ≈630 字节,本机约 0.5 次/小时)。断流自动退到 3 秒轮询;窗口最小化时断流,不唤醒渲染进程。
已知限制
- 深绑 DSH 内部结构(席位名、右侧栏 API、
--dsh-composer-*CSS 变量),宿主大版本升级可能要适配; - 客户端是一个约 4100 行的单文件 bundle(没有构建步骤);
- 审批页按会话,没有"所有会话汇总"视图;
- 撤销是"投递指令给会话",需要该会话还有可用 agent;
- 写前快照有上限(单文件 256KB / 每会话 64 条),超限只能看元数据。
- AI 裁决缓存跨会话共享(键 = 请求方档位 + 参数指纹),默认保留 10 分钟:同一个指纹只要任意会话付过一次模型钱,其它会话直接复用那条裁决(行上记
aiSource=cache)。所以「本会话 AI 调用 0 次」不代表 AI 没参与——既可能是档位把它压成了人工,也可能是命中了别人付过钱的缓存;统计页的「审批缓存 N 条」是整个池子,不是本会话的。
文档导航
| 文件 | 给谁看 |
|---|---|
| README-EN.md | 英文读者(人类) |
| README-AI.md | AI 代理:红线、结构、数据格式、SSE 协议、失败模式 |
| AGENT.md | 想自己改的人/AI:改设置、加策略、加席位、加测试的逐条配方 |
| CHANGELOG.md | 版本变更 |
| UX.md | 界面取舍记录:每处 UI 为什么长这样,含被否掉的方案和原因 |
| RESEARCH.md | 竞品调研与设计笔记(历史,带采集日期) |
docs/PLAN.md、docs/FEEDBACK.md、docs/ISSUES.md |
仅本地留档,不随仓库 / npm 发布(含开发过程与私域细节) |
No comments yet. Be the first to write one.