# dsh-auto-review

> 在 DSH 审批环节插入第二个只读 AI 模型审阅请求，让危险工具在执行前先经过独立判断，失败默认拒绝，全程留审计日志。

## Metadata

- Author: [@PerryLink](https://github.com/PerryLink)
- Repo: <https://github.com/PerryLink/dsh-auto-review.git>
- GitHub: [PerryLink/dsh-auto-review](https://github.com/PerryLink/dsh-auto-review)
- Stars: 58
- Language: TypeScript
- License: [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html)
- Homepage: <https://www.npmjs.com/package/dsh-auto-review>
- Topics: `ai-safety`, `approval`, `auto-review`, `cordis`, `deepseek`, `deepseek-harness`, `dsh`, `dsh-plugin`, `llm`, `sandbox`, `second-model`, `subagent`
- Forks: 1
- Open Issues: 1
- Last push: 2026-08-19T06:45:11.000Z
- Added: 2026-08-17T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:PerryLink/dsh-auto-review
```

## Wiki

## 一句话定位
在 DeepSeek Harness 的审批链上挂一个第二模型，让沙箱外的工具调用先由一个只读子 agent 读上下文并返回允许/拒绝；失败默认拒绝，全程记录会话日志。配套风险等级策略、拒绝熔断器和 Web 可视面板。

## 核心能力
- **第二模型审批**：在 `approval/request` answerer 链上注册一个钩子，命中 `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` 等依赖 |

## 安装方式
```bash
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 在仓库里默认注释掉。

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-auto-review](https://deepseek-plugin.org/plugins/PerryLink/dsh-auto-review)
Wiki generated by AI (model: `MiniMax-M3`)
