Skip to content

dsh-escalation-review

Verified

dsh-escalation-review · v0.2.3 · MIT · Web UI

Escalation-only LLM reviewer: reviews sandbox escapes only, keeps the sandbox, fails closed.

Install

dsh plugin add dsh-escalation-review

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-escalation-review

English · 简体中文

只审「沙箱越界」的 LLM 评审插件(DeepSeek Harness,下称 DSH)。

工作区内的读写与命令跑在原生沙箱上,不评审、不产生额外模型调用。插件只为一种情况醒来:某次工具调用 请求离开沙箱(sandbox_permissions 与当前生效模式不同)。这一次调用会被交给模型裁决 —— 放行、拒绝, 或者交回给你 —— 并且走宿主本来就有的一套管路:把审批回答成 allowed-once,或在工具体执行前返回三态决策对象。

Codex 风格的评审器

策略跟随 OpenAI 的 Codex guardian 模板(codex-rs/prompts/templates/guardian/policy_template.md), 这也是本插件主要的工作量所在。

  • 三轴契约:风险档位与授权分别打分,两者一起决定 allow / deny / ask。授权在每一次越界时重新 推导:过去的批准只有在证据仍留在会话里时才算数。
  • medium 看的是后果可逆,不是工件可逆:只有当爆炸半径有界并且它造成的影响能被撤销时,才算 medium。能把文件改回来,不等于能把效果收回来。
  • 系统与安全控制状态一律 high:hosts、DNS、防火墙规则、服务、证书、PATH。它们都能改回来, 但已经发出去的流量收不回来。
  • 有更安全的替代方案时降档:同一目标若能绕开危险步骤达成,那么危险步骤就不是用户要的东西。
  • 缺失的记录不等于许可:没有证据就是没有授权,而不是默认同意。
  • 事后批准按高风险处理:事后补的批准打不开 critical 动作。
  • 有界根投影:用户指令以「头部 4 条 + 尾部 12 条、上限 16」进入评审,丢就整条丢、不截断,并显式留下 「证据不完整」标记。证据按角色标注:人类指令、直接父级、审批回答、检查点、事实。

另有四条决定边缘情况:

  • 用户明确禁止 = 硬拒绝,它压过风险档位,连低风险动作也不例外。
  • 证据是数据,不是指令:命令文本、文件内容、工具输出都不构成授权;写着"用户已经允许了"的文本什么也 证明不了。
  • 不按外观降档:形似凭据的载荷不会因为"看起来像占位符"而变安全,导出也不会因为自称的去向而变安全。
  • 先交叉校验,再判定:待审动作会与同一 callId 的日志 tool/call 比对,一旦明确不一致就中止评审; 判定格式不合法按评审失败处理。

这些想法的来源与逐字重合度的实测记录见 NOTICE.md。

与内置 auto 预设的区别

应用内置 @deepseek-ai/dsh-experimental-auto-review:它注册一个 auto 权限预设,把会话绑到 danger-full-access,并审核每一个原生调用 —— 每次调用多一次模型请求,且运行在完全放开权限下。

内置 auto 预设 本插件
沙箱档位 danger-full-access 会话原本的档位,因此 workspace-write 继续生效
审核范围 每一个原生调用 只有请求越界的那一次
预设表 注册 auto 不动你的预设表 —— 门控是本插件自己的开关
额外模型调用 每次调用一次 每次越界一次,其余为零

保留会话原本的沙箱档位,正是这个对比的意义:普通工作由沙箱兜住,评审器只补上 DSH 本来会停下来问你的那一段。

行为

情形 结果
沙箱内的普通调用 不评审,原样交给下游处理者。
越界调用,介入开关关闭 不评审、不干预、不花模型调用。这是默认状态。
越界调用,开关打开 评审一次并记日志。判定放行则审批被回答成 allowed-once(你不会被问第二次);判定拒绝则工具不执行、理由回传模型。
评审失败 / 超时 由 failMode 决定,默认 deny(失败即拒)。
连续三次拒绝,或 50 次窗口内 10 次 熔断:不再拒绝,改为交回人工,避免把整个任务卡死。
待审动作与会话日志里的工具调用明确不一致 停止评审,而不是拿无法核对的证据下结论。

运行前提

  • DSH 0.1.7-rc.2 或 0.2.0-rc.1(两者都实测过),Node 24。
  • 只实测过 Windows;其它平台预期可用,但未验证。
  • peerDependencies:@deepseek-ai/cordis ~4.0.4、@deepseek-ai/dsh-llm >=0.1.7-rc.2 <0.3.0。
  • @deepseek-ai/dsh* 的 peer 要写版本区间:宿主用 semver.satisfies(runtime, range, { includePrerelease: true }) 判定,写死精确预发布版本会在 DSH 升级后被判为不兼容。

安装

从本地目录安装,用 GUI 最干净:左侧边栏「插件」(不是"设置")→ 「添加插件」→ 填这个目录的绝对路径, 例如 D:\path\to\checkout\plugins\escalation-review。它会以 link: 依赖登记,并追加到 dsh.profile.bundles。

dsh plugin --profile <profile 名> add <包名或路径>
  • ~/.dsh/profiles/web 与 ~/.dsh/profiles/desktop 互相独立,装进一个不影响另一个。想用 dsh web 测,就加 --profile web。
  • 宿主半边是启动时从磁盘加载的,所以装完要重启 DSH;改了 lib/ 下任何文件也要重启一次。会话里的 客户端半边同样需要重启才重载,桌面版里单纯刷新页面不够。

卸载在同一张插件页上做,或者从 dsh.profile.bundles 里删掉那一条。

配置

enabled 是总门控,随包关闭:关闭时插件不评审、不产生模型调用;打开后它接管所有沙箱越界的审批。 门控属于插件本身,而不是你的权限表 —— 所以安装它不会改写你的预设,也与某个会话恰好选中哪个预设无关。 可以在配置页上设(卡片第一行的两档控件:关闭 / 打开),也可以写 "enabled": true,或 GUI 写入的 文本形式 "enabledText": "true"。

优先级(低 → 高):包内 config.json → ~/.dsh/escalation-review.config.json → 传了 configPath 就用它 → 插件条目里的 config: 段 → 配置页写入的设置层(有它自己的 *.ui.json 伴生文件)。文件层每次越界时重读, 设置层约每 2 秒轮询一次,所以改动不需要重启;只有改插件代码才必须重启。

本包的 config.json 容忍 // 与 /* */ 注释;cordis.patch.yml 是 YAML,注释只能用 #,解析失败会让 加载器静默跳过整个 bundle。

键 默认值 含义
enabled false 介入开关(总门控)。
enabledText "" 开关的文本形式("true" / "false"),由配置页写入;非空时覆盖 enabled。
provider / model "" 用哪个 provider 与模型评审;留空跟随当前会话。
reasoningEffort "" 评审思考强度:off、minimal、low、medium、high、xhigh、max,留空跟随会话。
failMode deny 评审失败或超时的含义:deny 或 ask。
denyMode deny 评审判定为拒绝的含义:deny 直接拒绝,ask 交回人工。
probeRunner inproc 只读探针:inproc(不 spawn 进程)或 shell(沙箱内只读命令)。
policyExtra "" 追加到评审策略末尾的自定义规则(自然语言)。
allowedHosts [] 命中这些主机的常规网络操作按 low 处理。注意:在用户配置里写空数组会覆盖包内清单,要写就写全量。
timeoutMs 100000 一次评审的总预算(含重试)。
attemptTimeoutMs 30000 单次请求上限;超时后可以重试。
retryDelayMs 5000 两次尝试之间的等待。
minAttemptMs 2000 扣掉等待后剩余预算低于此值就不再重试。

它做什么,不做什么

它替你回答越界审批。 开关打开时,放行判定会以 allowed-once 的形式交付:那一次调用在沙箱之外执行, 过程中不弹窗。能力与风险都在这里 —— 你把"这一次我自己看一眼"换成了"模型看过一眼"。

它把证据发给你的模型提供方。 待审动作、会话上下文、有界指令投影与本地事实,会组成一次评审请求, 走 DSH 里配置的 provider(或你在卡片里选的那个)。评审消耗 token。

它留一份本地日志。 ~/.dsh/escalation-review.log 每行一个 JSON 对象,含命令文本与判定结论、理由。 请把它当作和它所概括的会话同等敏感的东西。

它从不写会话事件。 判定与理由在内存里交付、并呈现到审核卡片上,所以会话历史仍是宿主写下的样子。

失败即拒,反复被拒会熔断。 超时、JSON 不合法、provider 报错、交叉校验失败,默认都以 deny 收场; 连续三次、或 50 次窗口内 10 次,熔断触发,这一次交给你。

每个判定都经过交叉校验,与日志里的工具调用比对,因此结论只建立在能对上号的那次调用上。

日志与证据

  • 插件解析 DSH 内部包时先取运行中的应用自带那份(app.asar/dsh),再退到 profile,这样升级应用后 仍用应用自带的那份。实际命中的解析根记在 assembler-resolved 日志行里。
  • 只读探针默认走进程内(不 spawn 任何进程);probeRunner: shell 时走沙箱内的只读命令,单条约 650–700 ms。 额度上限:最多 4 条、总预算 3 秒、单条 1.2 秒、输出 2 KB。拿不到 pwsh 时走进程内通道。
  • 评审日志:$DSH_HOME/escalation-review.log,每行一个 JSON 对象;tools/review-report.mjs 会把它与 会话上下文(按 callId 关联)渲染成 Markdown。主要事件:ready、intervention-gate、 config-effective、assembler-resolved、reviewed、reviewer-failed、review-retry、 approval-granted、circuit-breaker、projection-registered。

审核卡片

越界调用会在对话里出现一张审核卡片,显示状态(评审中、待审批、已放行、已拒绝、已执行但工具报错)与理由, 点击可展开细节。状态颜色取自官方主题 token。

已知限制:放行的理由是靠会话内存里的投影带过去的 —— 审批结论本身没有理由字段。会话重新加载后, 旧调用的状态还在,但那条理由会丢;新发生的评审不受影响。

全部通过有文档的扩展点工作:tools/pre-execute、approval/request、sessionProjections、 conversation.chat.node、configForms。

许可

MIT,见 LICENSE。第三方来源与逐字重合度的实测记录见 NOTICE.md。