在 DSH 审批环节插入第二个只读 AI 模型审阅请求,让危险工具在执行前先经过独立判断,失败默认拒绝,全程留审计日志。
- 语言
- TypeScript
- License
- Apache-2.0
- 分支
- main
安装
$ dsh plugin --profile web add github:PerryLink/dsh-auto-review在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
对话式安装
帮我安装 DeepSeek Harness 插件 PerryLink/dsh-auto-review:先查看仓库 https://github.com/PerryLink/dsh-auto-review.git 确认安全性,然后执行安装命令并验证插件加载成功。
把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。
一句话定位
在 DeepSeek Harness 的审批链上挂一个第二模型,让沙箱外的工具调用先由一个只读子 agent 读上下文并返回允许/拒绝;失败默认拒绝,全程记录会话日志。配套风险等级策略、拒绝熔断器和 Web 可视面板。
核心能力
- 第二模型审批:在
approval/requestanswerer 链上注册一个钩子,命中ai策略的工具交给只读子 agent 裁决,其它请求一律走next()放行给原有人类审批链。 - 结构化裁决:审查子 agent 通过结构化输出返回
{ decision, reason, riskLevel },只有read/glob/grep这三个只读工具,不会真正执行被审查的动作。 - 失败默认拒绝:审查超时、崩溃、子 agent 拉不起、verdict 结构不对都走
fallbackPolicy(默认rejected),不会被悄悄放行;可改为delegate(交给人类)或allow-once(一次性放行,文档明确标注为危险)。 - 风险等级 + 熔断器:允许裁决附带的
riskLevel超过maxAutoAllow时可配置 delegate 或 deny;同一回合连续拒绝或滑窗拒绝过多时熔断,触发delegate/reject/abort-turn三种动作之一。 - deny 原因回流到模型:被拒的工具结果里会注入
[auto-review]/[auto-review-fallback]/[auto-review-never]标记 + 拒绝理由 + 防绕过提示,模型能看到为什么被拒、为什么不能换个姿势重试。 - 完整审计 + Web 面板:
autoReview/state、autoReview/verdict、autoReview/rejection、autoReview/circuit、autoReview/override五类事件写入会话日志;Web profile 多一个会话头 AI Review 按钮,展示开关、预算、累计统计、最近裁决和一次性批准。
技术实现
- 语言: TypeScript(ESM,
type: module) - 关键依赖:
@deepseek-ai/cordis(插件宿主框架)、@deepseek-ai/schemastery(配置 schema + 默认值)、@deepseek-ai/dsh-session+@deepseek-ai/dsh-session-projection(会话日志 + Web 面板数据通道)、@deepseek-ai/dsh-subagent(审查子 agent 编排)、zod(projection wire schema 校验) - 架构模式: 走 Cordis 函数插件契约(
name+inject+apply),通过ctx.on('approval/request', ...)、ctx.on('tools/post-execute', ...)、ctx.commands.register(...)三个 effect 注册副作用;通过ctx.inject(['sessionProjections'])在 host 支持时挂载autoReview投影单元供 Web 面板读取 - 入口文件:
src/index.ts(插件导出),src/runtime.ts(审批回答器 + 命令),src/review.ts(审查子 agent 编排),src/config.ts(Schemastery schema +resolveConfig)
适用场景
你希望把 DSH 跑在无人值守环境,但又不放心让主模型独断"写文件"或"跑命令"这类有副作用的操作;装上这个插件后,主模型要做的每一步沙箱外动作会先被第二个模型读上下文、给出裁决和理由,命中失败兜底会保守拒绝,整个过程在会话日志里可审计、可回放。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DeepSeek Harness | 0.1.0-rc.7 | 所有 dsh-* peerDependencies 精确锁定为 0.1.0-rc.7,dshWorkshop.compatibility.dshVersions 也只列该版本 |
| Node.js | ^22.19.0 || >=24.0.0 | package.json#engines.node 声明 |
| Cordis | ^4.0.1 | peerDependencies |
| Schemastery | ^3.18.0 | peerDependencies |
| 平台 | 跨平台 | host 半段纯 JS,不依赖原生模块;Web review panel 仅在 web profile 下注册 |
| 原生模块 | 无 | 无 os/cpu 限制,无 node-gyp/node-pty 等依赖 |
安装方式
dsh plugin --profile web add github:PerryLink/dsh-auto-review
配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
enableByDefault | boolean | 新会话是否默认开启 auto-review;/auto-review on|off 写覆盖事件 | true |
toolsPolicy.default | ai | human | never | 工具表里没列的工具走哪条策略 | human(交给人类) |
toolsPolicy.overrides | object | 具体工具 → 策略映射,例如 { bash: ai, write: ai } | {} |
riskRules | array | 命中请求 reason/toolName/参数的正则 + 策略(先于工具表匹配) | [] |
reviewerProvider | string | 审查子 agent 用的 subagent provider | fork(进程内 fork) |
reviewerModel | string | 审查用的模型 ID;不填继承当前会话模型 | 继承 |
reviewerTimeoutMs | number | 审查子 agent 超时毫秒数;超时走 fallbackPolicy | 60000 |
reviewerTools | string[] | 审查子 agent 允许使用的工具(白名单,必须非空) | ["read","glob","grep"] |
fallbackPolicy | rejected | delegate | allow-once | 审查失败(超时/崩溃/结构错误)时怎么落 | rejected(保守拒绝) |
maxReviewsPerTurn | number | 每个回合最多真实 AI 裁决数;超过后请求交给人类 | 10 |
maxFailuresPerTurn | number | 每个回合最多审查失败次数;超过后请求交给人类 | 10 |
reasonMaxChars | number | 审查 reason / 请求 reason / 参数预览的最大字符数 | 2000 |
reviewerGuidance | string | 追加到审查 prompt 的可选建议(advisory,不是硬约束) | 无 |
reviewerPolicyText | string | 像 Codex 那样注入到审查 prompt 的 Markdown 策略模板 | 无(参考 fixtures/config/policy-template.md) |
denyGuidance | string | 每次注入拒绝原因时追加的防绕过提示 | 默认英文 + 默认中文 |
contextBudget.turns | number | 给审查子 agent 看多少回合已展示过的会话内容;0 表示关闭 | 0 |
contextBudget.maxChars | number | 上下文段总字符上限 | 4000 |
riskPolicy.maxAutoAllow | low | medium | high | 允许裁决的 riskLevel 上限;超过则 delegate 或 deny | high |
riskPolicy.onHighRisk | delegate | deny | 超过 maxAutoAllow 时落哪种 | delegate |
circuitBreaker.consecutiveDenies | number | 连续多少次拒绝触发熔断 | 3 |
circuitBreaker.windowDenies | number | 滑窗内多少次拒绝触发熔断 | 6 |
circuitBreaker.windowSize | number | 滑窗大小(按最近 N 个裁决数) | 10 |
circuitBreaker.action | delegate | reject | abort-turn | 熔断后同一回合后续请求怎么处理 | delegate |
overrideTtlMs | number | /auto-review approve 一次性授权的有效毫秒数 | 300000(5 分钟) |
language | en | zh | /auto-review 命令输出语言 | en |
allowUnmarkedAudit | boolean | 是否在 host 不识别 ignorable 标记时仍写审计事件(危险:可能让更严格 harness 无法恢复会话) | false |
常见问题
Q: 默认会自动审查哪些工具?
A: 只审查 bash 和 write,其余工具交给人类审批。edit(原地修改)刻意没列入默认 AI 审查列表——如果你接受原地修改也走自动审查,把 edit: ai 加到 toolsPolicy.overrides 即可。
Q: 审查子 agent 自己也能发起工具调用吗?会不会再次触发审查?
A: 不会。审查子 agent 走的是 fork provider 的子会话,插件按 session id 识别审查子会话并直接走 next() 跳过审查;同时审查只允许 read/glob/grep 三个只读工具,它既不能写、不能跑 shell、也不能调用别的子 agent(maxDepth 限定)。
Q: 审查挂掉了怎么办?
A: 走 fallbackPolicy,默认 rejected——也就是保守拒绝,并把审查失败原因回灌给主模型。如果你想让人类兜底,把 fallbackPolicy 设为 delegate;非要把失败当成功,设为 allow-once(README 明确标注这是无人值守部署才会选的危险选项,grant 是无条件的)。
Q: 我能临时关闭吗?
A: 可以。在会话里跑 /auto-review off 即可,关闭状态写进会话日志,下次回放仍然有效。/auto-review status 看当前状态、当回合预算、累计统计、熔断情况;/auto-review approve [n] 给最近第 n 条拒绝一次性放行。
Q: 它会写入会话日志吗?我能事后审计吗?
A: 会。一条审批请求对应的 approval/asked → autoReview/verdict(或 autoReview/rejection)→ approval/decided 链条都能从 session log 复原。早期的 harness 版本(0.1.0-rc.1 ~ rc.7)会丢掉 ignorable 标记,所以插件会在第一次写入前做能力探测,如果探测不通过就自动降级为内存审计并一次性提醒,必要时用 dsh-permission-rules 仓库里的 scripts/repair-session-logs.mjs 修复已经被污染的历史日志。
Q: 风险等级策略和拒绝熔断器有什么区别?
A: 风险等级策略作用于单次裁决:审查返回 allow 但 riskLevel 超过 maxAutoAllow,该次允许会被升级成 delegate 或 deny;熔断器作用于回合累计:连续 3 次拒绝、或最近 10 次裁决里出现 6 次拒绝,触发后整回合后续请求按预设动作(delegate/reject/abort-turn)落。两者互不冲突。
Q: 数据存哪里?会联网吗?
A: 不写本地文件、不额外发网络请求。审计数据全在内存 + session log(受 MEMORY_DENIES_CAP=200 / MEMORY_OVERRIDES_CAP=50 等有界缓冲约束)。reviewerModel 不填则继承当前会话模型;要换成别的 provider,等于把已展示的会话内容呈现给另一个 provider,自己拿捏一下。
Q: 怎么卸载?
A: dsh plugin --profile web remove dsh-auto-review,或从 profile patch 里删 auto-review 那一行;manifest 标的是 transactional 卸载,不会动 profile 之外的配置。
上手难度
进阶 — 需要理解 DSH 的审批链、risk rule 正则、会话日志 audit 事件这三条核心概念,且要在 cordis.yml 里调风险策略和熔断阈值才能发挥全部作用;只装默认配置直接用也是可以的。
已知问题与限制
- 早期 harness 兼容性:0.1.0-rc.1 ~ rc.7 的
@deepseek-ai/dsh-session不识别ignorable标记,会导致autoReview/*事件落地后让更严格 harness 无法 resume 该会话。插件默认会预检并降级为内存审计(功能照常,但无审计日志),需要持久审计可设allowUnmarkedAudit: true(危险)或用dsh-permission-rules的 repair 脚本修历史日志(CHANGELOG.md:9-17 / src/audit.ts:36-52)。 - 审查模型需要可用的 LLM 路由:审查默认继承主会话模型路由;路由不通时每个审查都走 fallbackPolicy——绝对不会静默放行(README.md:232-241)。
reviewerTools必须非空:空白名单会让审查子 agent 无工具可用,加载时直接报错;且名单里的工具名必须是 profile 已注册的全局工具,否则审查子 agent 启动时失败。/auto-review approve授权的是"下一次同工具审查"而不是历史那一次调用:如果下一次同工具的调用参数、上下文已经不一样,授权照样会被消费,审查仍按当时上下文判定(README.md:236-237)。never是单向锁:命中never策略直接拒绝,不会进入人类链——这是个 lockdown 旋钮,不是日常选项(README.md:230)。- git 渠道安装需要
pnpm.allowBuilds: { esbuild: true }:pnpm 11 忽略package.json#pnpm字段,必须在pnpm-workspace.yaml里写allowBuilds(repo 自带)。typescript+tsdown在常规dependencies里以保证 git 隔离 prepare 环境能装上。 - invariant 配套需要
invariants服务:只在 headless/ACP 这类 agent-spine composition 下能用;普通 web profile 没有该服务,配套 patch 在仓库里默认注释掉。
🤖 dsh-auto-review
Second-model AI approval for DeepSeek Harness — a read-only reviewer subagent decides allow/deny on the approval chain, fail-closed by default.
When an action crosses the sandbox boundary, a second model reads the evidence and returns a verdict with a reason — so humans approve nothing while nothing unsafe slips through.
Compatibility
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness 0.1.0-rc.8 (peers pinned to 0.1.0-rc.8) |
| Node | ^22.19.0 || >=24.0.0 |
| Platforms | All (host answerer; optional Web review panel via the session-projection capability) |
| Model | Any (the reviewer inherits the session agent's route; reviewerModel overrides) |
What you get
dsh-auto-review puts a second model on the approval/request answerer chain:
- Official seam — an answerer that claims only the requests it owns (
aipolicy) and delegates everything else vianext(); the human approval flow is never short-circuited. - Read-only reviewer subagent — a one-shot fork with a
read/glob/greptool allow-list returns a structured verdict{ decision, reason, riskLevel }. Reviewer asks are recognized by identity and delegated;maxDepth+ the allow-list keep the reviewer non-delegating. - Fail closed — reviewer crash, timeout, or schema mismatch resolves through
fallbackPolicy(defaultrejected); a deny verdict feeds its reason back to the calling model. - Config-driven routing — per-tool policies (
ai/human/never) plus regex risk rules, all changeable from cordis.yml. - Deny reasons reach the model — the reviewer's reason is injected into the denied tool result (callId-linked); fallback and
never-policy rejections inject auditable markers too ([auto-review]/[auto-review-fallback]/[auto-review-never]). - Full audit trail — log-only
autoReview/verdict+autoReview/rejectionsession events (envelopeignorable: true) plus an optional invariant companion enforcing marker ⟺ event. - Safety knobs — a rejection circuit breaker (3 consecutive denials, or 6 of the last 10 verdicts, per turn), a risk-level policy, a one-shot
/auto-review approveoverride, and anever-policy hard disable that explains itself to the model. - Optional reviewer context — a bounded compact transcript (
contextBudget) plus a Codex-style Markdown ruling policy (reviewerPolicyText).
Every decision reconstructs from the session log: approval/asked → autoReview/verdict (or autoReview/rejection) → approval/decided.
Why a second model instead of rules?
Pattern-based auto-approvers decide before dispatch, with no evidence. dsh-auto-review gives the decision to a reviewer subagent that reads the actual workspace (through its read-only tool face), the already-streamed tool-call arguments (sensitive values redacted), the request reason, and your risk rules — then returns a structured verdict. A deny verdict feeds its reason back to the calling model, so the agent learns why instead of retrying blindly.
Quick start
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-auto-review#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-auto-review
# 2. restart and verify the row
dsh --profile web --dump-config | grep -A4 'id: auto-review'
Out of the box the shipped patch AI-reviews bash and write; every other tool (including edit — in-place modification) delegates to the human chain. Add edit: ai explicitly if you accept in-place edits without a human in the loop.
Install & uninstall
- git channel (latest
main):dsh plugin --profile web add "github:PerryLink/dsh-auto-review#main"— the isolatedpreparebuild needs the singleallowBuilds: { esbuild: true }key thedshCLI prints fordsh-auto-review. - npm channel (published releases):
dsh plugin --profile web add dsh-auto-review. - tarball channel:
pnpm packin this repo, thendsh plugin --profile web add ./dsh-auto-review-<version>.tgz. - uninstall:
dsh plugin --profile web remove dsh-auto-review(or remove the row from the profile patch).
Configuration
All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need.
| Key | Default | Meaning |
|---|---|---|
enableByDefault | true | Sessions start with auto-review enabled; /auto-review on|off writes a durable override that beats this |
toolsPolicy.default | human | Policy for unlisted tools (delegate to the human answerer) |
toolsPolicy.overrides | {} | Per-tool policy: ai / human / never |
riskRules | [] | {pattern, policy, field?} matched before the tool table; field selects reason (default), toolName, or arguments |
reviewerProvider | fork | Subagent provider for the reviewer (in-process fork backend) |
reviewerModel | (inherit) | Reviewer model id; unset inherits the session agent's route |
reviewerTimeoutMs | 60000 | Verdict deadline; on expiry the fallback policy applies |
reviewerTools | [read, glob, grep] | The reviewer child's tool allow-list (must be non-empty) |
fallbackPolicy | rejected | Reviewer failure: rejected (fail closed) / delegate / allow-once |
maxReviewsPerTurn | 10 | Real AI-verdict budget per open turn; beyond it, requests delegate |
maxFailuresPerTurn | 10 | Reviewer-failure budget per open turn |
reasonMaxChars | 2000 | Cap for reviewer reasons and the redacted argument preview |
reviewerGuidance | (none) | Optional advisory guidance appended to the reviewer prompt |
reviewerPolicyText | (none) | Markdown ruling policy injected into the reviewer prompt (Codex-style) |
denyGuidance | (anti-circumvention text) | Guidance appended to every injected deny reason |
contextBudget | {turns: 0, maxChars: 4000} | Compact transcript budget for the reviewer prompt; turns: 0 disables |
riskPolicy | {maxAutoAllow: high, onHighRisk: delegate} | allow verdicts above maxAutoAllow delegate or deny |
circuitBreaker | {consecutiveDenies: 3, windowDenies: 6, windowSize: 10, action: delegate} | Rejection circuit breaker |
overrideTtlMs | 300000 | How long a /auto-review approve override stays usable |
language | en | UI language of the /auto-review command output (en | zh) |
allowUnmarkedAudit | false | Force session-log audit on hosts that drop the ignorable marker (dangerous: unmarked events make sessions unresumable elsewhere); default is detect-and-degrade |
Example (annotated full form: fixtures/config/config-full.yaml):
- insert:
- id: auto-review
name: dsh-auto-review
config:
toolsPolicy:
overrides: { bash: ai, write: ai }
riskRules:
- pattern: '(?i)(rm\s+(-[a-z]+\s+)*/|git\s+push\s+--force)'
policy: never
- pattern: 'write'
policy: never
field: toolName
reviewerTimeoutMs: 30000
fallbackPolicy: delegate
riskPolicy: { maxAutoAllow: medium, onHighRisk: delegate }
circuitBreaker: { consecutiveDenies: 3, windowDenies: 6, windowSize: 10, action: delegate }
Tools & surfaces
| Surface | Kind | Notes |
|---|---|---|
auto-review | answerer | approval/request waterfall answerer — claims ai-policy requests, delegates the rest via next() |
/auto-review | command | on|off|status|approve [n] — durable per-session override, budgets, and cumulative statistics |
| deny-reason injection | listener | tools/post-execute — verdict / fallback / never reasons fed back to the denied tool result |
autoReview | session projection | Folded from the log-only autoReview/* events |
| Web review panel | client | Session-header action: switch, budgets, statistics, recent verdicts, one-shot approve |
dsh-eval | CLI | YAML-driven agent evaluation engine (bin/dsh-eval.mjs) |
| invariant companion | invariant | dsh-auto-review/invariant (optional; needs the invariants service) |
Session command
/auto-review on|off|status|approve [n]
on/off append the durable autoReview/state override (the fold survives restart/resume — replay IS the state) and inject a switch notice the model sees (logged as a user/message event). status reports the effective state, both per-turn budgets (AI verdicts and reviewer failures), a tripped circuit breaker when one is active, and the session's cumulative statistics (allows/denies/fallbacks/never rejects, mean duration, recent verdicts). approve [n] records a single-use autoReview/override for the n-th most recent denial (1 = most recent): the next same-tool review within overrideTtlMs carries the authorization as reviewer context — the reviewer still decides, and the override is consumed by that review regardless of its outcome.
Web review panel
In the Web GUI (web profile), the package contributes a session-header action (AI Review) that opens a panel with the session's auto-review state: the switch with on/off buttons (they execute /auto-review on|off), both per-turn budgets, cumulative statistics (including hard-disable rejections), the circuit trip, the recent verdicts, and one-shot approve buttons for recent denials (they execute /auto-review approve [n]).
How it is wired:
- The host registers an
autoReviewsession projection (folded from the log-onlyautoReview/*events) and serves it through the session-projection channel. - The browser half is a client module (auto-discovered from the
dsh.clientdeclaration) registered on theconversation.session.header.actionsseat. - No extra patch rows are needed: the panel loads whenever the plugin is installed in a profile whose web build provides the session-projection capability (the web profile does). Without that capability the panel reports itself unavailable; the answerer is unaffected.
The panel reads only whole projection values — it never receives the raw session event stream.
How it works
approval/request waterfall (answerer chain)
│
┌───────────────────────┴──────────────────────┐
│ dsh-auto-review answerer │
│ · session enabled? · policy = ai? │ no ── next() ──▶ human answerer (UI)
│ · risk rules → toolsPolicy → default │
└───────────────────────┬──────────────────────┘
│ yes
▼
┌───────────────────────────────────┐
│ reviewer subagent (fork, one-shot)│
│ · toolFilter: read/glob/grep │
│ · outputSchema: {decision, │
│ reason, riskLevel} │
│ · timeout + req.signal abort │
└───────────────┬───────────────────┘
│ verdict / failure (fail-closed fallback)
▼
allow → allowed-once deny → rejected + reason injected into the
denied tool result (callId-linked)
│ never → rejected + [auto-review-never] feedback
│ (hard disable, no reviewer runs)
▼
audit: approval/asked → autoReview/verdict | autoReview/rejection
→ approval/decided (session events, log-only, invariant-checked)
Composition order. The answerer runs at its registration position in the waterfall: if a human UI answerer is composed BEFORE the auto-review row, humans answer first and the reviewer only sees what is delegated downstream. Verify with dsh --profile <name> --dump-config and place the auto-review row before your human answerer rows when you want ai-policy tools routed to the reviewer first.
dsh-eval — agent evaluation engine
Beyond the approval reviewer, dsh-auto-review ships dsh-eval: a YAML-driven agent evaluation platform that runs real headless DSH sessions (one isolated agent + scratch workspace per case, the official Minimal persona as the baseline system prompt), collects the tool-call trace from the session event log, and evaluates structured assertions plus an optional second-model review — the same reviewer seam as the approval answerer.
# eval/cases/demo.yaml (abridged)
suite:
name: my-suite
cases:
- id: math-output
input: Solve 17 × 24 and reply with only the final number, nothing else.
expect:
output: { contains: "408" }
- id: glob-trace
seedFrom: '.'
input: Use the glob tool with pattern "src/**" to list the source files…
expect:
toolCalls: [{ tool: glob, arguments: { contains: { pattern: "src" } } }]
results: [{ tool: glob, contains: "index.ts" }]
Run it (a DeepSeek API key must be in the environment):
dsh-eval eval/cases --model deepseek-v4-flash --timeout-ms 240000 --out .eval-reports
CI gate: the process exits 0 only when every case of every suite passed — drop it into a GitHub Action step and failing evaluations fail the build. Each case leaves a replayable session JSONL and a trace JSON beside report.md/report.json; assertion results, token usage, and the review verdict are all written into the report files.
Permissions & data
- Permissions: the workshop manifest declares
session:append,approval:answer,subagent:spawn,command:register, andtools:observe. - Data: nothing is stored on disk; the report ring buffer is in-memory and bounded. No network requests of its own.
- Session log:
autoReview/*events carry reviewer identity, verdict, reason, risk, and duration — appended with the envelope'signorable: truemarker so any build loads the log. Hosts whoseSession.appendpredates the marker (every released rc line through0.1.0-rc.8— no release stamps it yet) are detected before the first append (peer-version pre-check, then a probe of the returned envelope) and audit degrades to an in-memory mirror with marker-free feedback, so sessions stay loadable everywhere.
Security boundaries
- The reviewer is a model. Its verdicts are advisory policy, not a security kernel; prefer
human/neverrules for irreversible operations. - Fail closed. Every abnormal path (provider missing, capability gaps, start rejection, timeout, non-
completedstop reason, missing/malformed verdict, audit-correlation failure) resolves throughfallbackPolicy, defaultrejected— and the rejection feeds an auditable reason back to the model.allow-oncegrants unconditionally; it exists only for unattended deployments whose admin accepts that risk. - Read-only reviewer. The reviewer's
toolFilterallow-list (read/glob/grep) cannot write, edit, run bash, fetch the network, or delegate (maxDepth= its own depth). Its session log is persisted and auditable. - Sensitive arguments are redacted (key-name matching:
token,password,api_key,Authorization, credentials, private keys …) before entering the reviewer prompt; the plugin never executes the reviewed arguments. Redaction is key-based, not content-based — do not AI-review tools whose argument values you cannot afford to show a model. - Hard disables explain themselves. A
nevertool or risk rule rejects deterministically AND records a log-onlyautoReview/rejectionevent, then injects a[auto-review-never]marker into the denied tool result — the model learns the action is hard-disabled instead of retrying it (invariant-checked: marker ⟺ event). - Rejection circuit breaker. A run of denials in one turn trips the breaker (
consecutiveDenies/windowDeniesinsidewindowSize), recorded as a log-onlyautoReview/circuitevent; later requests follow itsaction(delegate/reject/abort-turn). - Reviewer context is presented transcript.
contextBudgetfeeds already-presented session content to the reviewer. With the default same-route reviewer model that content stays inside one provider; configurereviewerModelto a different provider only if you accept presenting that transcript to it. neveris one-way at this layer. Anevertool or risk rule rejects before the human chain sees the request — a lockdown knob, not a default.
Known limitations
- The reviewer needs a working LLM route (inherited by default); without one every review falls back per
fallbackPolicy— never a silent grant. reviewerToolsnames must exist as global tools in the profile; an unknown name fails the reviewer child loudly at the earliest point and falls back.- Risk rules match the request
reason, thetoolName, or the redacted callargumentsper theirfield; other conditions belong intoolsPolicy.overrides. - The
/auto-review approveoverride authorizes the next same-tool review, not the exact historical call; a different action on the same tool consumes it. - The verdict events are log-only; the Web review panel reads the folded
autoReviewprojection (the raw event stream never reaches browser plugins). autoReview/stateandautoReview/verdictare appended with the envelope'signorable: truemarker on hosts that honor it, so any harness build loads the log — readers that do not know the out-of-repo types simply skip those records. On released rc hosts (rc.1–rc.8) the runtime detects the dropped marker and never writes these events (the in-memory mirror keeps the command, budgets, breaker, andapproveworking for the session); sessions already polluted by pre-0.5.1 versions can be repaired withscripts/repair-session-logs.mjsfromdsh-permission-rules(its default target set covers all fiveautoReview/*event types).- The git channel needs the single
allowBuildskey thedshCLI prints fordsh-auto-reviewitself. The repo ships its ownpnpm-workspace.yamlwithallowBuilds: { esbuild: true };typescript+tsdownare regulardependencies. - The optional invariant companion needs the
invariantsservice (agent-spine compositions such as headless/ACP); the plain web profile does not provide it, so the row ships commented out in the bundle patch.
Related work
- Andy8647/dsh-auto-approval — two-state allow/deny classifier on the
tools/pre-executewaterfall with file-log audit.dsh-auto-reviewdeliberately differs: official answerer chain, always delegates what it does not own, read-only second model with a structured verdict, deny reasons fed back to the model, session-log audit. - ACP automation bridge — one-shot machine decisions for its own ACP-owned agents.
dsh-auto-reviewis session- and tool-policy-scoped for the interactive harness; it never infers durable grants.
Development
pnpm install # node ^22.19 || >=24
pnpm run typecheck # tsc: src + tests against the local harness checkout
pnpm test # vitest: 202 tests, 16 files
pnpm run build # tsc declarations + tsdown bundles (lib/, incl. the client bundle)
pnpm run verify:self-contained
pnpm pack # the published tarball
Repository layout: src/index.ts (plugin contract) · src/config.ts (Schemastery schema + resolution) · src/runtime.ts (answerer, command, deny-reason injection) · src/review.ts (reviewer orchestration, prompt, sanitization) · src/events.ts (session-event vocabulary + folds) · src/audit.ts (host ignorable-marker capability detection) · src/projection.ts + src/projection-types.ts (the autoReview session projection) · src/invariant.ts (invariant companion) · src/eval/ (the dsh-eval engine) · eval/ (shipped evaluation composition) · bin/dsh-eval.mjs (CLI launcher) · src/client/ (browser half) · test/ · fixtures/.
Topics
deepseek-harness, dsh, dsh-plugin, cordis, approval, auto-review, second-model, ai-safety, sandbox, subagent
Contributors
- @PerryLink — creator and maintainer: the approval answerer, the reviewer subagent, risk policy and circuit breaker, the session-projection review panel, the invariant companion, dsh-eval, and the five-language docs.
PerryLink DSH Plugin Family
This project is one of the DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:
| Plugin | One-liner |
|---|---|
| dsh-mcp-panel | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors |
| dsh-doublecheck | Engineering-discipline guard: requirements grill, test gates, adversary review |
| dsh-background-agents | Durable background child agents with a Web UI sidebar, messaging and interrupt |
| dsh-lsp-actions | LSP diagnostics, formatting, completion, code actions and rename over language servers |
| dsh-output-styles | Claude Code outputStyles-equivalent runtime style switching |
| dsh-checkpoint-rewind | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore |
| dsh-permission-rules | Claude Code-style declarative allow/deny/ask permission rules with audit |
| dsh-auto-review | Second-model auto-review on the approval chain, fail-closed by default |
| dsh-memento | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
| dsh-skill-pack-security | Security-audit skill pack: secret scan, dependency and supply-chain review |
| dsh-session-pin | Pin sessions in the Web sidebar with durable ordering |
| dsh-composer-history | Terminal-style input history for the web composer: arrows, Ctrl+R search |
| dsh-github | GitHub PR/issues integration for DSH, every write gated by approval |
| dsh-plugin-guide | Plugin-development knowledge base as an on-demand agent skill |
| dsh-claude-move | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
License
Apache License 2.0 © 2026 dsh-auto-review contributors
收录徽章
[](https://deepseek-plugin.org/plugins/PerryLink/dsh-auto-review)把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。