dsh-compat-adapter
让 Claude Code / Codex / WorkBuddy / OpenCode / Copilot 的 hooks 与 rules,在 DSH 里真正跑起来。
一个 DSH 前置适配插件:显式声明 → 能力分级 → 路由到真实 seam。
不猜、不假装修好了、不把别家运行时塞进 Host。
🔌 我要用它 — 别的插件如何调用本适配器(消费者指南)
English ·
简体中文
---
## 这是什么
DSH(DeepSeek Harness)是新的 agent 运行时。而你已经在 Claude Code、Codex、WorkBuddy、OpenCode、GitHub Copilot 里积累了成套的 hooks 与 rules —— 那些"写文件前先拦一下""工具调用后补一段上下文""会话结束时跑个脚本"的东西。
把它们搬到 DSH 最省事的做法,是照着名字硬接一遍。那会静默地坏掉:
> Claude 的 `updatedInput` 被解析了但不生效;
> Codex 的 `permissionDecision: "ask"` 没有审批路径,退化成放行;
> Copilot 把 deny 写在**顶层**,而 DSH 的 hook 解析器只读 `hookSpecificOutput` —— 直接复用的话,**每一个 deny 都会被当成"没有决定"而放行**;
> OpenCode 的插件是 TypeScript 回调,"名字"能对上,"回调体"根本过不来。
**这个插件存在的意义,是让每一次适配的"损失"都变成可读的输出,而不是一个看起来能跑的假象。**
它只做三件事:
| | |
|---|---|
| **声明** | 预置作者显式写出 vendor 与 event。从不嗅探文件名、从不猜归属。 |
| **分级** | 每个资产返回冻结的四级结果,损失逐条列出。 |
| **路由** | 有官方桥就复用官方桥,有可拦截 seam 就真翻译,只能观察就明说只能观察,做不到就拒绝。 |
### 两个不做
- **不重造官方桥。** 能映射的事件直接交给 `@deepseek-ai/dsh-hooks-claude-code` / `-codex`,本插件只负责"把声明变成桥的配置"和"没有桥的事件怎么办"。
- **不执行外部代码。** JS 函数 handler、OpenCode 的 TS 回调、Claude 的 `http` / `mcp_tool` / `prompt` / `agent` handler —— 一律 `UNSUPPORTED`。别家插件运行时不嵌进 DSH Host。
---
## 核心设计:分级,而不是布尔
每个注册都返回一个**冻结**的能力结果。没有 `supported: true` 这种把风险抹平的字段。
| 分级 | 含义 | 本版本情况 |
| :--- | :--- | :--- |
| `DIRECT` | DSH 存在 1:1 seam,语义原样保留。 | **零**。见下。 |
| `ADAPTED` | DSH 有 seam,但存在语义损失;`degradations[]` 逐条列明。 | **全部 112 个事件** |
| `CARRY` | 无通用映射,原文逐字节携带、会话开始注入;永不变成可执行策略。 | **全部 5 家 rules** |
| `UNSUPPORTED` | 无忠实映射;拒绝并给出 `reason`。 | 程序性边界(见下) |
### 为什么一个 `DIRECT` 都没有
两个官方桥本身就是"尽力而为"的方言适配。宣称逐字节忠实,就是虚假能力。
所以本插件**任何一行都不标 `DIRECT`** —— 全部 112 行都是 `ADAPTED`,每一行都必须写出自己的损失。这不是悲观,这是诚实:`test/matrix-readme.test.js` 会断言 `DIRECT.length === 0`,并断言每一行 `ADAPTED` 都至少带一条 `degradations`。
> **全覆盖不等于等价。** 112 个事件全部可执行(0 拒绝),但这是"每行都写明损失"的全覆盖,不是"全都一模一样"。其中 **10 个事件在自己的 `degradations` 里明确写着 `Approximate`**(Claude `MessageDisplay`、`StopFailure`;Codex `Interrupt`;WorkBuddy `Elicitation`、`ElicitationResult`;OpenCode `installation.updated`、`server.connected`、`session.deleted`、`session.updated`、`tui.toast.show`),其余 **102 个为真实/精确映射**。`test/doc-counts.test.js` 会把这组数字与代码逐项比对,文档不能比代码多宣称一分能力。
### 四条硬性不变量(测试强制,不是文档承诺)
| 不变量 | 由谁保证 |
| :--- | :--- |
| `ADAPTED` **必须**至少列出一条 `degradations`,否则构造即抛错 | `capabilityResult()` + `test/capability.test.js` |
| `UNSUPPORTED` **必须**带 `reason`,否则构造即抛错 | `capabilityResult()` + `test/capability.test.js` |
| 不认识的 vendor / event **抛错**(`UNKNOWN_VENDOR` / `UNKNOWN_EVENT`),不静默 fallback | `test/service.test.js` |
| `UNSUPPORTED` 的资产**永不挂载**:不写配置、不挂桥、不进 `status()` 的可执行列表 | `test/service.test.js` + `verify:live` |
那么在全部可执行的今天,`UNSUPPORTED` 还剩哪些真实触发路径?
- **JS 函数 handler** —— 不把外部代码执行进 Host。
- **缺 `ctx.shell`** —— 组合里没有 shell executor,命令 hook 跑不了;rules 仍然可用(只读文件、只注入)。
- **缺官方桥包** —— `dsh-hooks-*` 从 profile 解析不到时拒绝,不退回自研实现。
- **非 command handler** —— `http` / `mcp_tool` / `prompt` / `agent` 抛 `INVALID_HANDLER`。
---
## 支持的 vendor
五家互相独立。每家是一个纯数据模块(`src/vendors/