Bundle
dsh-host-compliance-check
DSH 宿主插件:模仿 Trae stop-hook —— 主会话轮次结束时若本轮修改过文件(信源:tools/result 实时精确记录,覆盖 write/edit 与 run_code 子派发),注入 notice 形态需求合规提醒,由主模型自行判断
- Source
- liuyun847
- License
- MIT
- Updated
- Updated 10 days ago
Readme
# dsh-host-compliance-check
[](LICENSE)
[](package.json)
[](https://github.com/topics/dsh-plugin)
DSH(DeepSeek Harness)宿主插件:模仿 Trae 的 stop-hook 配置(hooks.json 的
PostToolUse + Stop → decision 移交),**不自动派发检查子智能体**;在主会话
轮次正常结束时,若本轮修改过文件,向主会话注入一段 **notice 形态的合规提醒
提示词**(`source.kind='plugin:compliance-check'` + `source.form='notice'` +
一句话 summary,正文带文件清单),由主模型当场自行判断:直接收尾 / 简要自查 /
派 subagent 深入检查(按其结算通知收活)。
> (v0.4.0 变更) 与 0.3.x 的差异:
> - **收集信源从"会话事件日志 + run_code 源码启发式解析"改为 `tools/result`
> 实时精确记录** —— 顶层 write/edit 与 run_code 内 `tools.write/edit`
> 子派发都经工具管道 emit `tools/result`(`exec.name`/`exec.arguments` 为真实
> 调用,`exec.agent` 携带会话),`file_path` 精确,删除正则猜源码的脆弱逻辑;
> - 注入消息由裸 plugin 文本改为 **notice 形态**(`form:'notice'` + `summary`
> ≤120,`boundContextSummary` 语义),不再以 `[AUTO]` 文本冒充用户输入;
> - diag 日志路径改为基于 `DSH_HOME`/用户主目录推导,不再硬编码。
> (v0.4.1 变更) 注入正文去掉"阻塞等待"措辞:是否前台等待由会话自身的人格纪律决定,
> 插件只负责提醒与给出手续 —— 与「永不阻塞」型预设(coordinator)的指令冲突由此消除。
> (v0.4.2 起) **会话格式 v4 兼容**(DSH **0.1.7-rc.1** 起):v4 的准入规则
> 要求 `user/message` 的 `source.kind` 是**非空字符串**且**不得等于 `'plugin'`**
> (旧版"插件包装"写法已退役),违反时该消息落盘被拒、**整轮以
> `format v4 message requires a producer-owned source kind` 失败**(错误码 UNKNOWN)。
> 第三方插件注入消息的正确写法是 `kind: 'plugin:<插件名>'`,且 source 里**不再有**
> `plugin` 字段 ⇒ 本插件注入消息由 `{ kind:'plugin', plugin:'compliance-check' }`
> 改为 `{ kind:'plugin:compliance-check' }`;`form` / `summary` 语义与 UI 折叠展示不变
> (UI 按 `source.kind !== 'user'` 判为注入行、按 `source.form` 折叠)。
## 特性
- 轮末提醒:主会话轮次正常结束时,若本轮修改过文件且本用户输入尚未提醒过,
注入 notice 提醒(进 next-step 队列 → 本 turn 继续,主模型当场看到并响应,
响应完 turn 才真正结束 —— 等价 Trae 的 block)
- **精确收集 = `tools/result` 实时记录**(对顶层 write/edit 与 run_code 内子派发
均触发;scope-filtered 按 agent 分发):
- `exec.name` ∈ {write, edit} 且成功 → 提取真实 `file_path`;
- 事件日志仅作兜底(顶层 `tool/call` 的 write/edit),不再解析 run_code 源码
- **只支持标准模式写入**;PTC 裸写(`await import('node:fs')` + 直接文件 API,
绕过 `tools` 绑定)不经过工具管道、不产生 `tools/result`,本插件检测不到
(已知限制;合规做法是程序内用 `tools.write/edit`)
- 每用户输入最多提醒一次(新用户消息经 `agent/pre-step` 复位记录与标记)
- 只对主会话触发:**GUI 恢复/续写历史会话创建的 fork 会话(带 parentSession、无 origin)视为主会话**,照常提醒;仅排除子代理会话(header `origin: 'subagent'`),避免嵌套提醒
- 提示词携带**全部用户输入历史**(按时间顺序拼接、跳过插件注入,超长截断保留最新)供对照,
不只取最近一条;内置自查清单与处理方式引导(可直接结束 / 派 subagent 深查,按其结算通知收活)
- 提醒以 **notice** 消息呈现(`source.kind='plugin:compliance-check'` + `source.form='notice'`,summary 一句话 ≤120),UI 折叠展示、不冒充用户输入
## 配置(cordis.patch.yml 的 entry config)
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `skipPaths` | `[]` | 跳过匹配的路径(RegExp/字符串数组),命中则不记录 |
| `diag` | `false` | 写诊断日志到 `~/.dsh/compliance-diag.log`(DSH_HOME 优先;排查用) |
(0.2.x 的 `providerName`/`promptTemplate`/`diffMaxChars`/`diffMaxLinesPerFile`/`watchTools`
与 0.3.x 的源码解析逻辑均已移除。)
## 安装
本包是**组合包(bundle)**:注册行(`id: compliance-check`)随包发布在包内 `cordis.patch.yml` 里,
宿主按 profile `package.json` 的 `dsh.profile.bundles` 加载 —— **不需要**再往 profile 的
`cordis.patch.yml` 里贴任何 `- insert:` 行。
在 profile 目录(如 `~/.dsh/profiles/desktop/`)下执行:
```powershell
dshpm add file:./plugins/dsh-host-compliance-check --profile desktop
```
`dshpm` 是替代 `dsh plugin add` 的装卸 CLI,装包的同时会把包名写进 `dsh.profile.bundles`;
装完重启 dsh 生效。
- **启停**:插件页 →「已安装」区里本卡的总开关(写 `dsh.profile.bundles`);
点开卡片后每一行还有行级开关(向 profile 的 `cordis.patch.yml` 写 `disabled` 覆盖 ——
profile 层在包层之后应用,所以覆写优先)。
⚠ 包内注册行当前是 `disabled: true`(随迁移保持停用,与迁移前一致)⇒ 装好默认**不生效**。
要启用它,拨**这一行**(`compliance-check`)的**行级开关**:它向 profile 的 `cordis.patch.yml`
写 `disabled` 覆写,而 profile 层在包层之后应用 ⇒ 能盖掉包内那一行;或者直接删掉包内
`cordis.patch.yml` 里的 `disabled: true`(改完记得同步副本,并按上面口径触发一次重组合)。
⚠ **不要用卡片总开关**:那只决定本包在不在 `dsh.profile.bundles` 里,而它本来就是开着的
(随迁移进入 bundles) —— 总开关关掉只会整个卸载本包,不会让这一行生效。
- **卸载**:`dshpm remove dsh-host-compliance-check --profile desktop`。
⚠ 若 profile 的 pnpm 带供应链策略,`pnpm remove` 会报
`ERR_PNPM_RESOLUTION_POLICY_VIOLATIONS_UNHANDLED`;绕过办法是进 profile 目录直接跑
`pnpm remove dsh-host-compliance-check --config.minimum-release-age=0`
(dshpm 的 `--fast` 只对 `add` 有效 —— `pnpm remove` 不接受 `--minimum-release-age` 这类参数)。
> 注意:本仓库以 `plugins/dsh-host-compliance-check/` 为**活源**,运行时加载
> `node_modules/` 下的 **file: 拷贝**(二者独立)。改动活源后需把
> `lib/index.js`、`package.json`、`cordis.patch.yml` 同步复制到
> `node_modules/dsh-host-compliance-check/`,再重启 dsh 才生效。
> (本包 7/7 共享文件 2026-09-26 实测**均为独立拷贝**,两侧 `fileId` 不同属正常 ——
> **不要**去重建硬链接。全机机制见 `dsh-plugin-manager\README.md`「本地 `file:` 插件的落盘形态」。测试与 `tests/` 不进运行副本(`files` 白名单不含它,副本里那份是旧安装残留),`node tests/…` 从源码目录跑。)
> 包内 `cordis.patch.yml` 属**包层 patch**,改它不需要重启,但它**不会自己触发重组合** —— dsh-hmr
> 只监视 profile 的 `cordis.patch.yml`、home 层 `cordis.patch.yml` 与 profile 的 `package.json` 三个输入
> (`dsh-hmr/lib/index.js:353-376`),包内文件不在其中;重组合时会重读全部 bundle 层,所以改完要有一次触发
> 才被读入:在插件页点一下本卡(或任意行级)开关,或保存 profile patch 的任意一处改动。
> `lib/*.js` 是模块代码,loader 按 URL 缓存已 import 的模块实例,改完必须重启 dsh。
## 工作原理
- 监听 `agent/pre-step`:检测到用户新消息(`source.kind === 'user'`)时复位
该会话的"已提醒"标记与本轮写入记录;
- 监听 `tools/result`:每次工具成功结束(顶层或 run_code 子派发),若是
write/edit 则把精确 `file_path` 记入该会话本轮表(应用 skipPaths 过滤);
- 监听 `agent/turn-stopping`(≈ Trae Stop):仅主会话且未提醒过、本轮表非空时,
`agent.inject` 注入 notice 提醒 —— 注入 next-step 使本 turn 继续,主模型当场响应;
- 事件日志兜底:仅当实时记录缺失时,从会话事件取顶层 `tool/call` 的 write/edit;
- 提示词正文带文件清单、本轮用户输入摘录、自查清单与处理方式引导(可直接结束或
派 subagent/subagent_fork 深查(按其结算通知收活));
- **已知边界**:run_code 程序内 PTC 裸写(`await import('node:fs')` 等直接文件
API,绕过 `tools` 绑定)不产生 `tools/result`,本插件不检测;如需要覆盖请改用
`tools.write/edit`;
- 无子代理派发、无 `ctx.get('subagents')` 依赖。
## 开发
```bash
pnpm install
# 修改活源 plugins/dsh-host-compliance-check/ 后,同步 lib/ 与 package.json 到
# node_modules/dsh-host-compliance-check/(file: 拷贝,不自动同步;本包 7/7 共享文件实测均为
# 独立拷贝,两侧 fileId 不同属正常,不要去重建硬链接),再重启 dsh
# 纯函数单测(提取/记录/收集/提示词组装):
node plugins/dsh-host-compliance-check/tests/diff.test.mjs
```
## License
MIT
Install
dsh plugin --profile web add github:liuyun847/dsh-host-compliance-check
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-host-compliance-check from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.