Repository navigation
Releases: denceee/dsh-compat-adapter
Release list
v0.5.0 — 真实能力补齐(102 精确 / 10 近似)
v0.5.0 — 为缺失能力补齐真实实现
核心变化
四条路由,能力分级不变(DIRECT / ADAPTED / CARRY / UNSUPPORTED)
| 路由 | 事件数 | 说明 |
|---|---|---|
official-bridge |
17 | 复用 DSH 官方已发布的 hook 桥 |
native-translate |
80 | 直接翻译到 DSH 原生 seam |
native-seam |
5 | 只读观测(observe-only) |
builtin-bridge |
10 | 本插件自建桥(无官方桥的厂商) |
总计:112 个厂商事件,0 个拒收,29 个原生翻译点。
其中 102 个为真实/精确映射,10 个在自己的 degradations 里明确写着 Approximate。
这 10 个分布在四家 vendor 上,而不是集中在某一家的边角:
| vendor | 近似事件 |
|---|---|
claude |
MessageDisplay、StopFailure |
codex |
Interrupt |
workbuddy |
Elicitation、ElicitationResult |
opencode |
installation.updated、server.connected、session.deleted、session.updated、tui.toast.show |
修正说明: 本条 release 的初版正文写的是"107 个真实 / 5 个近似"。那是错的 —— 它按"只有 5 个
native-seam只读事件"来推算,而没有去读每个事件自己的degradations文本。按代码重新统计后是 102 / 10。这个偏差已被test/doc-counts.test.js覆盖:它从代码反推 badge、汇总表、流程图、翻译点数、用例数与package.json描述,任何一处比代码多宣称一分能力都会让npm test失败;初版数字 107 被按名拒收。
本轮补齐的真实能力
- WorktreeCreate / WorktreeRemove — 真实执行
git worktree list --porcelain轮询(porcelain stanza 解析:worktree/HEAD/branch/detached/locked/prunable),配合looksLikeRepository()与diffWorktrees()做增删差分。 - opencode
lsp.*— 真实调用ctx.lsp.query(变更文件后触发),LSP_QUERY_FILE_LIMIT=10。 - Elicitation —
user-questions/request瀑布真实作答(elicitationAnswerFrom),不再只是观测。 - PreModelSwitch / PostModelSwitch —
request/header对EpochHeader.config做差分;首个 header 只作基线,不触发回调。 - 工具批边界 — 以
step/end作为真实边界(DSH 中一步 = 一次模型调用及其工具执行),当该步未请求任何工具时保持静默。 - Copilot 自建桥 —
builtin-bridge路由 +kind:'builtin',与官方桥明确区分;check-surface断言@deepseek-ai/dsh-hooks-copilot不存在,若 DSH 未来发布则测试响亮失败。
阻塞契约
只用 stdout 的 hookSpecificOutput.permissionDecision(deny/ask)+ exit 0;
不依赖 exit 2。Copilot 方言的决策位于顶层(permissionDecision),
matcher 为锚定正则 ^(?:PATTERN)$,stop 用 decision:"block",另有 8 次失控保护。
验证(本机实测)
npm run check-surface # OK — 缺失元素会响亮命名报错
npm test # 141 pass / 0 fail
node scripts/verify-live.mjs # PASS — 真实 profile 启动
node scripts/verify-installed.mjs --profile <已装 profile> # PASS — 已装 profile 真的生效
verify-live matrix:
claude hooks=33 supported / 0 refused rules=CARRY
codex hooks=12 supported / 0 refused rules=CARRY
workbuddy hooks=24 supported / 0 refused rules=CARRY
opencode hooks=29 supported / 0 refused rules=CARRY
copilot hooks=14 supported / 0 refused rules=CARRY
verify-live 断言:Copilot 顶层 deny 能到达 DSH、锚定 matcher(bash vs edit)生效、1 次拒收路径成立。
兼容性
- DSH
0.2.0-rc.2(peerDependencies 已固定) - Cordis 4.x、
@deepseek-ai/schemastery - 零静态
@deepseek-ai/*导入(src/peer.js延迟加载),因此在 profilelink:目录下也能解析
另有本次修正:verify-installed 的四个假失败
scripts/verify-installed.mjs 回答的是使用者最关心的那个问题——"装到我自己的 profile 里,
它到底活没活"。它此前会在真实 profile 上给出错误结论,本次一并修掉:
- 没有握住 stdin —— profile 挂载的 SDK app 注册了
exitOnStdinEnd,stdin 一到 EOF 就卸载整个组合,
下一次调用抛ADAPTER_DISPOSED,被误报为插件故障。改为父进程持有管道再 re-exec。 - 伪造的 agent 没有
session—— DSH 自己的GoalService在agent/created里做
WeakMap.set(agent.session, ...),直接抛Invalid value used as weak map key。 - 用
ctx.waterfall手动派发agent/created—— DSH 实际是经AgentService.announce()
以ctx.serial在 agent 的 scope carrier 上派发;手动派发会在挂了 scope 插件的 profile 上
假报 "0 injected",而真实产品路径注入正常。改为走真实的ctx.agents.register()。 - 立刻读注入计数 —— 规则注入惰性解析消息构造器,需要等微任务落地。
并加了失败归因:deny 脚本现在无条件写 trace,于是"命令根本没跑"(环境限制,例如
沙箱受限执行器 + 本机无沙箱后端)与"跑了但裁决被忽略"(插件 bug)被区分开。
本机实测:真实
dsh-baseprofile 在DSH_PERMISSION_MODE=danger-full-access下
verify-installed: PASS;非危险模式下给出带归因的失败,而不是误导性的allow。
新增test/verify-scripts.test.js(5 例)把这些不变量钉住。
v0.4.0 — 自建 Copilot 桥 + 真实文件监听
DSH 没有官方 Copilot 桥 —— 这个版本自己造了一条,并补齐了此前只能近似的 seam。
自建 Copilot 桥(新)
与官方桥同一契约,可像官方桥一样挂载:
ctx.plugin(copilotBridge, { configPath: './hooks.json' })- 导出
name/inject/Config/apply,与dsh-hooks-claude-code一致 - 复用
@deepseek-ai/dsh-hook-protocol做匹配、执行、解码,不重造协议 - 写耐久审计事件
hook/invoked/hook/result,Copilot hook 与 Claude/Codex 一样可审计 - 解析 Copilot 自己的配置信封
{version:1, hooks:{...}}与bash/powershell/command/exec字段 - 自己实现 Copilot 方言:顶层
permissionDecision解码、锚定正则 matcher、8 次续跑守卫
Copilot 的 10 个事件因此从「内联翻译」改为「走桥」。
真实文件监听(消除 FileChanged 的损失)
ctx.fs.watch 武装真正的文件系统监听 —— 外部编辑器改文件也会触发,这是「工具调用后近似」永远做不到的。测试用测试进程自己写文件来证明这一点。
新增 5 个真实 seam(共 25 个翻译点)
| seam | 效果 |
|---|---|
fs/watch |
真实文件监听 |
user-questions/request |
Elicitation 真实拦截(可回答结构化问题) |
command/run |
拿到真实命令名与参数 |
agent/instructions-loaded |
真实指令文件发现 |
agent/inbox/inserted |
真实消息到达 |
造桥过程中暴露并修复的 6 个真实缺陷
- 桥内异步 mount 让监听器注册落在 inactive fiber(
INACTIVE_EFFECT)→ 所有监听器静默失效 - 桥未解码顶层
permissionDecision→ 每个 Copilot deny 都会被当作放行 mergeHookOutputs把 context 合并成数组,注入被判非字符串而丢失mergeHookOutputs把decision:"block"归一为deny→ 续跑判断永不命中- 桥用
ctx.agents属性访问未声明的 inject → Cordis 抛错 - 桥漏了 Copilot 的 8 次 runaway guard
测试不再不稳定
「固定 sleep 后读文件」是竞态:文件已创建但内容未写完,高负载下必然失败。
已全部改为轮询可观测结果(含 parseable 判定)—— 修复前 15 轮有 1~2 轮失败,现在 10 轮 0 失败。
验证
npm test # 115 tests passing
npm run check-surface # OK
npm run verify-live # PASS
事件总数仍为 112,0 拒绝,0 个 DIRECT:每条适配仍在矩阵里逐项列出损失。
v0.3.1 — 用精确 seam 减少适配损失
事件数保持 112,但把能精确化的近似映射换成了真实 seam —— 这一步只减少损失,不增加数量。
用精确 seam 替换的近似
| 事件 | 原来 | 现在 |
|---|---|---|
FileChanged(claude / workbuddy) |
任何可能改文件的工具调用后触发 | workspace/changes + workspaceChanges.summary(),拿到该 turn 真实的变更文件列表(paths / total / added / deleted) |
file.edited / file.watcher.updated / session.diff(opencode) |
同上 | 同上 |
InstructionsLoaded(claude / workbuddy) |
session/created 近似,文件列表恒为 null |
调用 dsh-agent-instructions 导出的 discoverBaselineInstructionFiles,报告真实发现的指令文件 |
message.updated 等 4 个(opencode) |
整个 session 事件流近似 | agent/inbox/inserted,携带真实 UserMessage(id + 文本) |
新增 3 个原生翻译点(共 22 个)
workspace/changes— 真实变更文件摘要agent/instructions-loaded— 真实指令文件发现agent/inbox/inserted— 真实消息到达
同时修复一个真实接线 bug(出现过两次)
workspace/changes 与 agent/instructions-loaded 曾只加了 service 分支、却没加入 NATIVE_TRANSLATIONS,导致这些 point 永远解析不到(vendor 表找不到映射,注册会失败)。
现在有两个双向守卫测试:每个已声明 point 必须有 service 分支;每个 service 分支必须对应已声明的 point;且每个 native-translate 事件的 point 必须存在。
确认无法精确化的(保持近似并如实标注)
- LSP:DSH 既无 lsp 包、也无 lsp 事件
- PostToolBatch:工具库没有批次 seam
- worktree / TUI / 通知 / 服务器连接等 DSH 本就没有的生命周期
验证
npm test # 102 tests passing
npm run check-surface # OK
npm run verify-live # PASS
v0.3.0 — 厂商文档事件全覆盖(112 个事件)
逐一对照 4 家 vendor 的官方文档做差集,补上此前会抛 UNKNOWN_EVENT 的事件:85 → 112 个。
总数
| Vendor | 事件 | 变化 |
|---|---|---|
claude |
33 | +1(PostCompact) |
codex |
12 | —(核对无缺口) |
workbuddy |
24 | — |
opencode |
29 | +24(补全事件总线) |
copilot |
14 | +2(preCompact / userPromptTransformed) |
| 合计 | 112 | +27 |
值得一提的两处
- Copilot
userPromptTransformed是真实改写:它能替换发给模型的内容。DSH 的agent/pre-step支持返回替换后的消息列表,因此这不是观察,而是真的生效。 - OpenCode
permission.asked是真实拦截:走 DSH 的approval/requestwaterfall,hook 能决定放行或拒绝。
新增 3 个原生翻译点
compaction/start、compaction/end:压缩前后观察(摘要取自前置的compaction/summary事件)agent/pre-step/transform:模型可见 prompt 的真实改写
验证
npm test # 96 tests passing
npm run check-surface # OK
npm run verify-live # PASS
仍然是 0 拒绝、0 个 DIRECT:112 条全部是 ADAPTED,每条在矩阵里逐项列出损失(OpenCode 事件总线观察多为近似映射)。
v0.2.0 — Copilot 适配器与 85 事件全覆盖
DSH 前置/基座适配插件首个功能完整版本:5 家 vendor、85 个 hook 事件全部可执行。
支持矩阵
| Vendor | 事件 | 官方桥 | 本插件翻译 | rules |
|---|---|---|---|---|
claude |
32 | 7 | 25 | CARRY |
codex |
12 | 5 | 7 | CARRY |
workbuddy |
24 | 5 | 19 | CARRY |
opencode |
5 | — | 5 | CARRY |
copilot |
12 | — | 12 | CARRY |
亮点
- GitHub Copilot:DSH 无官方 Copilot 桥,12 个事件全部由本插件翻译。Copilot 把 deny 写在顶层
permissionDecision,而 DSH 协议只读hookSpecificOutput—— 直接复用会让每一个 deny 被当成「没有决定」而放行。本插件自带解析层修正这一点。 - 真拦截而非旁观:
preToolUse的 deny 真的拦工具;agentStop的decision:"block"真的让 turn 继续(含 8 次 runaway guard);approval/request上的PermissionRequest真的决定放行/拒绝;subagentStart的上下文真的注入子 agent。 - 损失逐项列出:85 条全部是
ADAPTED,没有一条声称DIRECT。近似映射(文件变更、worktree、通知类)在矩阵每一行最后一列写明。
安装
dsh plugin --profile <profile> add github:denceee/dsh-compat-adapter验证
npm test # 87 tests passing
npm run check-surface # OK
npm run verify-live # PASS(真实启动 profile)
注意:全局覆盖不等于语义等价。OpenCode 的 TS 回调只映射「名字」不携带「体」;WorkBuddy 桌面端无公开 hook API。细节见 README 的「已知限制」。