DSH Hub / 插件 / dsh-tiered-approval Elaina-real/dsh-tiered-approval ↗ ★ 2
dsh-tiered-approval Tiered auto-review for DeepSeek Harness (DSH): static-rule safety net + LLM reviewer + human fallback. Auto-approve safe actions, auto-deny irreversible ones, ask a human for the rest. PURE VIBE CODING - not audited, use at your own risk.
包名 dsh-tiered-approval
版本 0.1.0
许可证 MIT
最近更新 2026年8月13日 GitHub ↗ 文档 ↗ 安装 $ npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Elaina-real/dsh-tiered-approval复制
dsh-tiered-approval
自动放行安全的,拦下不可逆的,拿不准的问人。
🛡️ 静态规则安全网 · 🤖 LLM 审查员 · 🙋 人工兜底
为 DeepSeek Harness (DSH)而写的分级自动审查插件
[!WARNING]
本插件是纯 vibe coding 写的
代码、配置 schema、这份 README 都是 AI agent 生成的,几乎没有人工 review:未经过安全审计 、与 DeepSeek 官方无关 、不提供任何担保 。它把关的是安全决策,请把它当起点,而不是信任边界 ——
风险自负。review、审计、PR 都特别欢迎。
目录
dsh-tiered-approval · DSH Hub
这是什么 DSH 没有内置自动审批器,只有两个极端:每次越界都弹窗 (烦),或全放权什么都不问 (怕)。
这个插件填上中间地带:在每个工具调用真正执行前加一道三层裁决 ——静态规则先拦下不可逆的,LLM 审查员再看一遍拿不准的,剩下真正有疑问的才交回给你。
装完即生效,默认行为就是安全值,不需要你写一条规则。
为什么需要它 原生状态 体验 本插件 ask(审批策略)每个逃出沙箱的操作都弹确认 静态规则 + 审查员替你裁决,只剩真疑问 never / 全放权什么都不问,出事故没兜底 不可逆操作被安全网直接拒绝,不弹窗也不花 token 权限预设(read-only / workspace-write / full-access) 只换沙箱边界,审查强度不变 审查强度自动跟随预设 (见 perMode)
它如何工作 DSH 留了两个官方接缝(tools/pre-execute 门禁 + approval/request 应答者),本插件各占一个:
工具调用
│
▼
tools/pre-execute 门禁 ── 能看到完整参数(命令文本、目标路径、升权模式、理由)
│ 第一层 静态规则(零成本、确定性)
│ 命中 deny 规则 ──► 直接拒绝,不弹窗(内置安全网)
│ 命中 allow 规则 ──► 打 "allow" 印记,放行
│ 第二层 LLM 审查员(策略跟随当前 Access 模式)
│ 裁决 allow ──► 打 "review-allow" 印记,放行
│ 裁决 deny ──► 直接拒绝(理由返回给模型)
│ 裁决 ask ──► 升权调用:放行到工具本体弹一次人工;其余:门禁直接问人工
│ 审查不可用/超时/解析失败 ──► 静默回退,不新增弹窗
│ 其余 ──► 保持默认放行,打 "none" 印记
▼
工具本体(例如 pwsh 升权时)──► 发起 approval/request 审批请求
│
▼
approval/request 应答者 ── 依据印记 + 文本规则自动回答
│ reason 命中 denyJustifications ──► rejected(优先级最高)
│ 印记 deny ──► rejected
│ 升权请求:
│ 印记 allow 且模式 ∈ answerer.allowModes ──► allowed-once
│ 印记 review-allow 且模式 ∈ 当前模式的 review.allowModes ──► allowed-once
│ reason 命中 allowJustifications 且模式 ∈ answerer.allowModes ──► allowed-once
│ 其余 ──► next() → 人工审批 UI(现状不变)
印记(stamp) :approval/request 请求不带工具参数,危险与否只能在门禁里用完整参数判断——门禁把结论打在 callId 上,应答者凭印记兑现。
prepend 注册 :web 部署里 dsh-host-apiproxy 有个挂起等人工的"终端"应答者,本插件必须排它前面,未命中的请求才能流回人工。
一次性授权 :allowed-once 只管这一次调用,没有 allow-always / 记住授权。
高亮特性
静态安全网优先 —— 试图逃出沙箱的破坏性命令(递归删除、格式化磁盘、强推 git、注册表删除、写系统目录)在任何模型调用之前 被拒绝:不弹窗、不花 token;
LLM 审查员(上下文感知) —— 小模型对规则未裁决的调用做全参数审查(allow / deny / ask),并结合会话里最近一条用户消息 判断命令是否对齐用户意图;复用会话自己的模型路由,不开子 agent、不写会话日志;
self-kill 守卫 —— taskkill / Stop-Process / killall / pkill 一类进程终止命令确定性拒绝(防 agent 杀掉自己的宿主;后台任务用 job_kill,kill 保留为逃生口);
L0 双保险 —— 静态 deny 除了瀑布最前的监听器,还注册了单调 tools.guard(),其他插件旁路不掉这条硬底线;
跟随预设 —— 审查强度自动跟随 Access 选择器(Read only / Workspace write / Full access);Full access 下默认全量审查 ,因为沙箱已经不隔离了;
全自主可切换 —— review.onUncertain: 'deny' 开启两态模式(审查不确定/失败一律拒绝,无人参与);默认 'ask' 保留人工兜底;
快捷开关 —— 输入框直接 /auto-review on|off|rules|tiered|auto|status|reset 切档位、看统计,无需改配置(内存态,重启重置);
不加弹窗 —— 未命中的请求行为与原生 DSH 完全一致;
可插拔 —— 标准 cordis bundle,dsh plugin add 安装,卸载自动 dispose 全部监听器。
看它工作 真实日志(进程日志里的 [auto-approval] 行):
[auto-approval] deny pwsh : auto-review: escalated destructive command is refused without prompting (irreversible)
[auto-approval] review-allow pwsh : install dependencies
[auto-approval] allow escalation pwsh -> workspace-write
[auto-approval] reviewer call failed: provider exploded
[auto-approval] review inconclusive for pwsh ; falling back
一条被拦住的命令(Remove-Item -Recurse + 升权,静态层直接拒绝——安装时它拦过我们自己的部署命令 😄):
→ pwsh(
command="Remove-Item C:\Users\x -Recurse -Force",
sandbox_permissions="danger-full-access",
justification="clean up"
)
→ Error: auto-review: escalated destructive command is refused without prompting (irreversible)
快速开始 最小配置就是默认配置 ——装完重启即生效,行为即"安全模型"一节:
# ~/.dsh/profiles/
/cordis.patch.yml(手动挂载时)
- insert:
- id: tiered-approval
name: 'dsh-tiered-approval'
插件清单页出现 tiered-approval;
进程日志出现 [auto-approval] 决策行;
试一次"升权 + Remove-Item -Recurse"——被直接拒绝且不弹窗(静态安全网生效)。
之后按需调 config:(见「配置」),改完重启生效。
快捷开关(/auto-review) 不想改配置也能随时切审查档位——在输入框直接输入斜杠命令(和 /permission 一个玩法):
命令 效果 /auto-review 或 /auto-review status显示当前档位、审查模式、决策计数和最近决策 /auto-review on / /auto-review off打开 / 关闭 LLM 审查(off = 纯静态规则) /auto-review rules纯规则档(等价 off) /auto-review tiered三态档(默认):静态 → LLM → 人工兜底 /auto-review auto两态自主档:审查员不确定 / 失败一律拒绝,无人参与 /auto-review reset回到配置文件里的设置
内存态 :开关只对当前进程生效,重启后重置 回配置;要持久化就改 config:。
优先级 :开关 > 配置 > perMode 默认。
档位映射:rules = review.mode: off;tiered = on + onUncertain: ask;auto = on + onUncertain: deny。
安装 本包是标准 bundle (package.json 声明 dsh.bundle,携带自己的 cordis.patch.yml 层),按官方发布文档(docs/user/develop/basic/publish.md)安装。已发布到 npm (dsh-tiered-approval)。
方式一:一行安装(npm,推荐) dsh plugin --profile web add dsh-tiered-approval
dsh plugin add 会从 npm 安装预构建产物、链接进 profile 的 node_modules,并把本包追加到 dsh.profile.bundles(其 cordis.patch.yml 层自动挂载 tiered-approval 行)。前置条件:profile 目录里需要 pnpm 可用(dsh plugin 转发给 pnpm)。
从本地源码装:dsh plugin --profile web add ./dsh-tiered-approval(在包含包目录的上层执行)。
方式二:从 GitHub 安装 dsh plugin --profile web add github:Elaina-real/dsh-tiered-approval
本包是编译好的 JS (lib/ 已提交在仓库里),没有 TS 源码 + 构建步骤,所以不需要 prepare 脚本,也不需要 allowBuilds 放行 ——比 TS 源码包少一道安全门槛。
方式三:手动拷贝(无 pnpm 时兜底) 把整个目录拷到 ~/.dsh/profiles/ /node_modules/dsh-tiered-approval(profile 用 hoisted pnpm 布局,拷贝即可被 loader 解析),并在 profile 的 cordis.patch.yml 里 - insert: 挂载行。
验证与卸载
验证 :dsh --profile web --dump-config 应出现 # == dsh-tiered-approval 层;或装完重启后看插件清单页 / 日志。
注意 :插件代码在 node_modules 里,HMR 不追踪 node_modules ——改代码必须重启;改 cordis.patch.yml 配置可能 热生效,但别依赖。
卸载 :见下一节。
卸载 / 禁用
临时禁用 :bundle 行加 disabled: true(或在 profile 的 patch 里覆盖 tiered-approval 行),重启。
彻底卸载(bundle 方式) :dsh plugin --profile web remove dsh-tiered-approval,重启。所有监听器随 cordis fiber 自动 dispose。
配置 # ~/.dsh/profiles/
/cordis.patch.yml
- insert:
- id: tiered-approval
name: 'dsh-tiered-approval'
config:
builtinDeny: true # 内置破坏性命令安全网总开关(默认开;建议永远别关)
builtinDenyRules: [] # 内置危险命令规则列表(默认 = 代码内置那组;可在这里增删改,无需改代码)
log: true # 记录每一次自动决策
deny: [] # 追加硬拒绝规则(命中即拒、不弹窗)
# 例如:
# - tool: pwsh
# where:
# command: ['git\\s+push.*(--force|-f\\b)']
# reason: '禁止强推'
allow: [] # 自动放行规则(配合应答者生效)
# 例如(门禁判定安全,且升权模式被允许时自动批准):
# - tool: pwsh
# where:
# command: ['pnpm\\s+install|npm\\s+install']
# escalating: true
answerer:
allowModes: ['workspace-write'] # 规则层:默认不含 danger-full-access
allowJustifications: [] # 理由命中 ⇒ 自动批准
denyJustifications: [] # 理由命中 ⇒ 自动拒绝(优先)
# 例如:
# denyJustifications:
# - 'drop\\s+database'
# - 'DROP\\s+TABLE'
# - '删除.*(数据库|生产|整个)'
review: # LLM 审查层(默认开)
mode: 'on' # 'off' = 纯规则版
# provider: 'deepseek-official' # 显式路由(必须与 model 成对)
# model: 'deepseek-v4-flash'
skipTools: [read, read_image, glob, grep, web_search,
job_output, job_list, job_kill, ask_user_question,
todo_write, list_agents, interrupt_agent]
skipNested: true # 跳过 run_code 子分发
allowModes: ['workspace-write', 'danger-full-access'] # 审查员可批准的模式
onUncertain: 'ask' # 'ask'=不确定交人工(默认);'deny'=全自主两态,直接拒绝
timeoutMs: 20000
maxTokens: 512
maxInputChars: 12000
perMode: {} # 按沙箱模式覆盖(见下)
规则字段(deny / allow 共用) 字段 说明 tool工具名,* 匹配所有工具(pwsh、bash、write、edit、read、glob、grep…) where参数匹配表:键是参数的点路径,值是正则列表;所有 列出的键都命中才匹配 escalatingtrue ⇒ 只匹配带了 sandbox_permissions(升权)的调用reason拒绝时的说明(作为工具错误返回给模型 / 写进日志)
正则大小写不敏感;YAML 里反斜杠要写双份(\\s)。所有正则加载期校验 :写错插件直接拒绝加载,不会静默不匹配。
跟随 Access 选择器(perMode) 审查策略自动跟随会话当前的沙箱模式 ——界面 Access 下拉(Read only / Workspace write / Full access)会把 sandbox/mode 写进会话,插件每次调用都按当前模式重新解析策略:
review:
perMode:
'read-only': # 只读下保持审查(命令仍会执行,只是改不了文件)
mode: 'on'
'workspace-write': # 标准模式,默认即可
mode: 'on'
'danger-full-access':
# 内置安全默认:若从未自定义过全局 skipTools,Full access 下全量审查
# (skipTools: [])——沙箱不隔离了,审查是静态规则之外的最后防线。
# 想放开就在这写:
# skipTools: [read, glob, grep]
mode: 'on'
当前 Access 默认审查行为 Read only 开;跳过只读/搜索/任务控制类工具 Workspace write 开;跳过只读/搜索/任务控制类工具(同全局默认) Full access 开且全量 (什么都不跳过);全局 skipTools 自定义过则跟随全局
[!WARNING]
Full access 预设同时把审批策略设为 never(无人工弹窗)。此时审查员裁决 ask 不会弹窗,而是变成确定性拒绝 (fail-closed)。想要人工兜底,就别把会话切成 Full access。
LLM 审查层说明
上下文感知 :审查输入帧除了工具名 + 完整参数 + 沙箱模式/工作区根,还带会话里最近一条真人消息 (source.kind === 'user',注入的 skill/通知类上下文会跳过),让审查员判断命令是否对齐用户意图。
路由 :默认取第一个已注册 provider 的第一个模型;想指定就配 review.provider + review.model(必须成对)。
成本 :每个规则未裁决的调用 = 一次小模型调用(输出 ≤ maxTokens,输入帧截断到 maxInputChars);skipTools 和 skipNested 防止审查调用被放大。
故障行为 (默认 onUncertain: 'ask'):审查员不可用 / 超时 / 输出畸形 ⇒ 静默回退 (记日志,不新增弹窗);静态安全网照常兜底。只有审查员明确裁决 ask 才弹人工。
全自主两态 (onUncertain: 'deny'):审查员 ask 与审查失败都变成确定性拒绝 (fail-closed),全程无人参与——适合你已经信任模型判断的场景;代价是审查员拿不准的调用会被拒绝而不是问你。
收紧 :把 review.allowModes 改成 ['workspace-write'],danger-full-access 升权就回到人工审查。
安全模型与默认值 情况 默认行为 工作区内正常操作(读写/搜索/构建) 静态规则放行;未裁决的交给 LLM 审查员 升权到 workspace-write(如 read-only 会话) 规则或审查员判定安全 → 自动批准 升权到 danger-full-access 规则层:默认不自动批准;LLM 层:审查员允许则自动批准(review.allowModes 默认含它) 升权 + 破坏性命令(递归删除、格式化、强推、注册表…) 静态层直接拒绝——不弹窗、不调模型 升权写入系统目录(C:\Windows、/etc…) 静态层直接拒绝——不弹窗 进程终止命令(taskkill / Stop-Process / killall / pkill) self-kill 守卫静态拒绝 (后台任务用 job_kill;`kill` 是逃生口)
规则版与 LLM 版自由混用:review.mode: 'off' = 纯规则版;开着 LLM 层时静态安全网仍然先执行(不可逆操作从不消耗审查 token)。
权限与数据 内容 说明 读取 每个工具调用的完整参数 (命令文本、路径、升权理由等)——在门禁内读,仅用于裁决,不落盘 发送给模型 开启 LLM 审查时,把参数帧(工具名、参数、沙箱模式/工作区根、最近一条用户消息 )发给你配置的模型 provider (默认与会话同款路由)——参数和用户消息里可能含敏感文本,请知情 网络 无独立网络访问;只通过 DSH 的 ctx.llm 服务发模型请求 凭据 不读取、不存储任何凭据;~/.dsh/.credentials.yaml 由 DSH 凭据服务管理,本插件不触碰 文件写入 无(仅日志由 dsh 进程统一输出) 会话日志 不写 session 事件;仅通过插件 logger 输出 [auto-approval] 行 卸载 全部监听器随 fiber dispose,无残留状态
兼容性 项 说明 DSH 版本 针对 @deepseek-ai/dsh 0.1.0-rc.6 开发与验证 依赖 @deepseek-ai/cordis ^4.0.1、@deepseek-ai/schemastery ^3.18.1、@deepseek-ai/dsh-llm ^0.1.0-rc.6、@deepseek-ai/dsh-timeout ^0.1.0-rc.6平台 Windows(pwsh 规则集,实测);POSIX 走 bash 规则集,理论上兼容(未实测) 最后验证 2026-08(npm test 全绿;冒烟测试 61 项)
mainline 迭代很快,兼容性结论可能过期;升级 DSH 后请重跑 npm test 并试一次门禁行为再依赖。
边界与限制
本插件是纯 vibe coding 写的——见顶部警告。 未审计、非官方、无担保。
应答者的文本规则(allowJustifications / denyJustifications)只匹配模型写的一句理由——真正的判断在门禁(静态规则 + LLM 审查员),那里才有完整参数。
LLM 审查是概率性的,不是证明;规则是人写的,可能漏掉新姿势;来自文件/网页/工具输出的 prompt injection 可能把 agent 往越界方向带,命令级审查员不一定看得出来。
run_code 子分发经过门禁(印记覆盖),但 LLM 审查默认跳过嵌套调用(skipNested: true),只审查外层 run_code 本身。
委派的子 agent 不受影响:DSH 默认把子 agent 的审批策略钉死为 never。
日志与排查 log: true(默认)时,每次自动决策都会写进 dsh 进程日志:
[auto-approval] deny ... —— 静态或审查拒绝
[auto-approval] review-allow / review-deny / review-ask ... —— 审查员裁决
[auto-approval] allow escalation ... -> —— 自动批准了一次升权
[auto-approval] reviewer call failed ... / review inconclusive ... —— 审查层故障(已静默回退)
排查时 grep [auto-approval],看是哪一层、哪条规则/理由做的决定。
症状 排查 插件清单里没有 tiered-approval 重启服务;确认包在 node_modules、patch/bundle 行正确;看启动日志是否报 "plugin failed to load"(如配置 schema 校验失败) 升权不再自动批准,或弹窗变多 检查当前 Access 预设(Full access 下 ask 是确定性拒绝);确认 review.mode 与 allowModes 规则没生效 配置正则加载期已校验(非法会拒绝加载);确认 tool 名、where 键路径、YAML 转义(\\s) 审查延迟高 加了 skipTools 仍慢的工具;或 review.provider/model 指向慢模型;看 timeoutMs 想彻底回滚 按「卸载 / 禁用」移除 bundle 或手动行 + 重启;删除包目录即可
测试 包内带 test/smoke.mjs —— 61 项断言驱动 apply()(假 ctx:假 llm 服务返回预置裁决、假 sandboxPolicy),覆盖静态层、审查员 allow/deny/ask/故障、skipTools / skipNested、perMode 跟随预设、应答者的升权规则和配置默认值。
独立运行(devDependencies 已声明,不需要 DSH profile):
npm install
npm test # 期望 "ALL PASS"
推送到 GitHub 后,.github/workflows/test.yml 会在每次 push / PR 上自动跑冒烟测试(Node 20 和 22)。
冒烟测试 ≠ 安全审计 ——用之前先为你的工作流补用例。
贡献
Issues :bug、建议、看不懂的报错、文档疑问——开一个 就行;
PRs :欢迎,尤其是 review / 审计 ——一个 vibe coding 产物最缺的就是人眼;
安全相关问题:开 issue 时标注 security,或先私下联系作者。
License MIT —— 但请看顶部的 vibe coding 警告:风险自负。
如果它帮你少点了很多次鼠标,⭐ 一下 就是最好的支持。