No description
  • Shell 51.2%
  • JavaScript 38.6%
  • Python 10.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Secaumene 2c448240c5 v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only
- 新增参考适配器 deepseek-dsh:经 dsh --profile headless --json 驱动 DeepSeek Harness;
  overlay 按环境变量选模型与 effort(off|low|high|max),锁定 sandbox/approval/permission,
  只加载 ~/.dsh/skills 与 AGENTS.md;cancel.sh 杀整棵进程树;resume 拒绝改变权限(需 --fresh)。
- 新增宿主绑定 hosts/dsh:install.py --host dsh --role peer 把 model-wire-peer 装进 ~/.dsh/skills。
- route.mjs resolve 新增通用开关 --native-only 与第 12 个输出键 native_only。
- profile secaumene 接入 deepseek-flash:新 slot extract(TC14)与 review.of_deepseek(TC26),
  quick、implement.mechanical、implement.token_dense 按 gpt -> ds -> claude 插入 ds-flash,
  wire_defaults.ask 改为 ds-flash;policy.md D/E1、pact.md 措辞同步。
- 通用前端 resume.sh 的 --job 模式在包 write=true 时也传 --write。

评审:Opus 降级代评三轮(Codex 冻结),全部发现已修;真机冒烟通过(dsh 0.2.0-rc.2)。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 17:06:57 +09:00
examples/minimal v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
hosts v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
profiles v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
skills v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
tests v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
tools v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
.gitattributes 加 .gitattributes:工作区强制 LF 2026-10-03 17:28:55 +09:00
.gitignore v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
CHANGELOG.md v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
install.ps1 v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
install.py v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
install.sh v3.0.0:与厂商、宿主解耦的通用模型路由 skill 2026-10-03 17:28:42 +09:00
manifest.json v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
README.md v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00
VERSION v3.1.0:接入 DeepSeek(dsh)参考适配器与 resolve --native-only 2026-10-04 17:06:57 +09:00

model-routing packet 3.1.0

三个通用 skill,让任意命令行主会话(Claude Code、Codex CLI 或其他)按一份机读路由表,把活分给任意厂商的模型,并用省 token 的线协议通信。核心 skill 不含任何厂商、模型、宿主名;差异全部放在数据里:宿主绑定(hosts/<id>/)、适配器(skills/model-wire/adapters/<id>/)、profile(profiles/<name>/,个人数据)。

Quick install (English). Requires a bash (Git Bash on Windows is fine), Node >= 20, Python >= 3.8.

cd <this packet directory>
python tools/make-manifest.py                     # regenerate manifest.json after editing any packet file
python install.py --host claude-code --dry-run    # show what would change
python install.py --host claude-code --profile <name>   # install; pick your own profile (without --profile it uses the one in the local config, else the fictional "example" demo profile, which has no usable channel)
python install.py --verify --host claude-code     # hash-check the installation (--host is required, also with --verify)
node skills/model-routing/routing/route.mjs doctor --host claude-code   # list vendors that have no channel on this host (exit 1 = gaps)
bash tests/run-tests.sh                           # offline wire tests; also: tests/install-test.sh, tests/negotiation-test.sh
node tests/route/route-test.mjs && node tests/lint-generic.mjs

1. 分层

层 位置(packet) 内容 能否出现厂商 / 宿主名
核心 skill skills/model-routing/、skills/model-wire/(不含 adapters/)、skills/model-wire-peer/ 判定器 route.mjs、线协议前端、对端协议、通用文档、协商工具 不能(tests/lint-generic.mjs 强制)
安装与构建 install.py、tools/make-manifest.py 按宿主数据安装 不能(同上)
宿主绑定 参考实现 hosts/<host-id>/;本机生成物 <home>/hosts/<host-id>/ host.json、notes.md、assets/ 能(只写本宿主)
适配器 参考实现 skills/model-wire/adapters/<adapter-id>/(由同目录 REFERENCE.json 的哈希清单认定);本机生成物 <home>/adapters/<adapter-id>/ 参考实现:adapter.json、六个脚本、README.md、PITFALLS.md、tests/;本机生成物只有哈希集里的文件、*.md 与 .registered.json,不含 tests/ 和点文件 能(只写本厂商与传输方式)
profile profiles/<name>/ profile.json、policy.md、可选 pact.md、可选 history/ 能(用户数据)
本机目录 $MODEL_ROUTING_HOME(默认 ~/.model-routing) config.json、profiles/、hosts/、adapters/(本机生成并经 register.sh 登记的适配器)、keys/registry.key(登记签名密钥)、state.json、backup/ 安装与登记生成

三个 skill:

skill 装在主会话宿主(--role main)还是对端(--role peer) 作用
model-routing main 谁干什么:route.mjs resolve 按 profile 的 slot 候选链、本机 state 和宿主渠道选出模型、effort 与调用方式;resolve --native-only 对本次判定施加 side=dispatcher,输出多一个键 native_only(共 12 个);加原则、额度状态机、评审流程、契约检查与账本工具、协商工具
model-wire main 怎么说、怎么收:线协议 MODEL-WIRE v2 的调度端 pack → dispatch → wait → unpack,续派 resume,速问 ask;前端不含厂商命令,厂商差异在适配器
model-wire-peer peer 对端:识别首行 MODEL-WIRE v2,只读任务包,证据落盘,按固定标签加 REPORT_JSON 回复并自检;附同一份 contract-check.mjs

适配器与宿主绑定是“标准 + 现场生成”:随包只带参考实现(三个适配器、四个宿主);遇到没有的厂商或宿主,主会话的 AI 按标准现场生成(model-wire/adapter-kit/、references/adapter-playbook.md、references/host-binding-playbook.md),适配器生成后必须真机冒烟通过才登记:唯一入口是 register.sh(route.mjs registry-commit),它亲自跑 conformance 与经 shim 核实真 CLI 的冒烟,再用本机密钥签名写记录;记录不能手写,也没有任何命令接受外部提供的验证结果。登记后才被路由采用。信任由固定位置加哈希清单决定(REFERENCE.json),不由 adapter.json 里的 origin 自称决定。流程概览见第 5 节。

渠道(chosen.channel):候选模型所属厂商在本宿主有原生派发机制(host.json 的 native.kinds 非空,且厂商声明了该宿主)→ native;否则有可在本宿主运行的适配器 → wire;都没有 → 跳过(no-channel)。

2. 目录

packet/
  VERSION  README.md  CHANGELOG.md  manifest.json  install.py  install.sh  install.ps1
  tools/make-manifest.py
  skills/
    model-routing/    SKILL.md  routing/route.mjs  references/{host.md,host-schema.md,host-binding-playbook.md,profile-schema.md,...}  tools/{ledger.sh,contract-check.mjs}  negotiation/
    model-wire/       SKILL.md  scripts/  references/{adapter-contract.md,adapter-playbook.md,...}  adapter-kit/{template,conformance.sh,smoke.sh,register.sh}  adapters/{REFERENCE.json,openai-codex,anthropic-claude-cli,deepseek-dsh}/
    model-wire-peer/  SKILL.md  instructions-block.md  scripts/  references/
  hosts/{claude-code,codex-cli,dsh,generic}/   host.json  notes.md  assets/
  hosts/template/                          host.json  notes.md(带 TODO(host) 占位,给现场生成宿主绑定用)
  profiles/{example,secaumene}/            profile.json  policy.md  [pact.md]  [history/]
  tests/            route/  run-tests.sh  install-test.sh  negotiation-test.sh  lint-generic.mjs  fixtures/
  examples/minimal/ 最小任务包样例(仅测试夹具用:task_class 与 model 取自 tests/fixtures 的 profile,在 example / secaumene 下 pack 会失败;格式可参考,值要换成自己 profile 里的)

3. 安装

install.py --host <id>[,<id>…] [--profile <name>] [--role main|peer|both] [--home <dir>]
           [--routing-home <dir>] [--dry-run] [--force] [--verify] [--uninstall] [--uninstall-pact]
           [--no-instructions] [--reference-adapters all|none] [--quiet]
  • --host 必填(包括 --verify、--uninstall 时),可逗号分隔多个,也可以是一个含 host.json 的目录;--home 只在单个宿主时允许,覆盖 host.json 的 home;generic 必须给 --home。宿主 id 先在 packet 的 hosts/ 找,再在 <routing-home>/hosts/<id>/(本机生成的绑定)找;host.json 的 id 必须等于它所在的目录名;两处同 id 时用随包的参考宿主,并把本机那份整目录移到 <routing-home>/backup/<时间戳>/hosts/<id>/ 再写入参考绑定,打 WARN(所以生成的宿主别与参考宿主同名);两处都没有就退出 2,并打印四步:建目录 → 复制 hosts/template/ → route.mjs check-host <目录>/host.json → install.py --host <目录>(详见 references/host-binding-playbook.md)。
  • --profile 缺省取本机 config.json 里的;本机也没有就用 example(虚构厂商的演示 profile,装完多半没有可用渠道,真要用请自选 profile)。--role 缺省 both。
  • --reference-adapters all|none(缺省 all):none 时不装随包的参考适配器(纯生成模式,.installed.json 记 reference_adapters,重装缺省沿用上次的值,--verify 据此跳过 model-wire/adapters/**)。本机 <routing-home>/adapters/ 与 hosts/ 里的生成物,重装不清(同 id 的宿主绑定见上,移入 backup)。
  • 安装末尾对每个宿主跑 route.mjs doctor --quiet,有厂商缺渠道时只打 NOTE(不算失败)并指向 adapter-playbook.md;安装时的渲染与 doctor 都用刚写入的 routing/host.json,与运行时一致。--routing-home 不是缺省值($MODEL_ROUTING_HOME 或 ~/.model-routing)时安装末尾打 NOTE:运行时要设同样的 MODEL_ROUTING_HOME,--verify 在不设该变量时发现不一致会报 FAIL。
  • 路径限制:宿主绑定的 skills_dir、instructions_file、native.subagent.dir、legacy_cleanup.migrate_state_from 展开后必须在展开后的 {home} 之内(check-host 与安装器都拒绝跳出,安装器写文件、改名前再断言);legacy_cleanup.remove_skill_dirs 不得含 model-routing、model-wire、model-wire-peer。
  • 每个宿主:备份将被覆盖的目录到 <routing-home>/backup/<时间戳>/<host>/ → 清空目标 skill 目录 → 复制核心 skill → 复制 host.json 到 model-routing/routing/host.json → 把 notes.md 装成 model-routing/references/host.md 与 model-wire/references/host.md → 把 profile 的 policy.md 渲染进 SKILL.md 的 profile-policy 标记块(frontmatter 的 skill_description_append 追加到 description 末尾)→ 按 profile 的 subagent_types 生成子代理文件(仅声明了 native.subagent 的宿主)→ skill_extras → 指令文件标记块(对端协议块;profile 带 pact.md 时再写策略块)→ legacy_cleanup(旧 skill 目录先备份再删,旧块剥除,旧 state 迁移)→ 写 .installed.json。
  • 本机目录:复制 profile(不含 history/)与全部宿主绑定(整份:host.json、notes.md、assets/、子代理模板,本机那份也能单独通过 check-host),写 config.json,对每个安装的宿主生成 models-and-task-classes.<host-id>.md,state.json 不存在时迁移旧 state 或写空 state。本机 profile 受保护:<routing-home>/profiles/<name>/ 里与 packet 不同的文件,只有仍与安装器上次复制的内容逐字节一致(记在同目录的 .profile-sync.json,值是 sha256)或给了 --force 才会被覆盖;否则保留本机版并打 NOTE。本机删除同样受保护:你删掉的文件(例如不要 pact.md)重装时不会被恢复,只打 NOTE。所以改本机 profile(例如给厂商加适配器 id)不会被重装冲掉,packet 里的 profile 有更新时要自己合并,或 --force(覆盖前备份)。
  • --verify --host <id>:按 manifest 校验 sha256,渲染块与 profile 的 policy.md 逐字一致,无多余文件,旧目录与旧块已清,对端协议首行为 MODEL-WIRE v2。--uninstall 只删 .installed.json 记录过、且属于本次 --role 的 skill 目录,以及子代理文件和指令块(策略块需 --uninstall-pact),不动本机 state;不是本 packet 装的同名目录保留。
  • 改过 packet 里任何文件后先 python tools/make-manifest.py(--check 只校验)。install.sh / install.ps1 只是找到 Python 后转调 install.py。

本机目录与环境变量

变量 含义 缺省
MODEL_ROUTING_HOME 本机目录 ~/.model-routing
MODEL_ROUTING_PROFILE 指定 profile(名字或路径) config.json 的 profile
MODEL_ROUTING_HOST 指定宿主(id 或路径) routing/host.json
MODEL_ROUTING_STATE 指定 state 文件 <home>/state.json
MODEL_WIRE_ADAPTERS 适配器目录(只给测试用:设了就只用它;该目录若是可信参考位置,按 REFERENCE.json 认定,否则其中每个适配器都必须有效登记,没有“旁路目录免登记”;旁路下 doctor、adapter-dir、wire-target 都输出 bypass:true,每条命令向 stderr 打 WARN,--quiet 不静音;目录就是 <home>/adapters 时不算旁路) 依次 <home>/adapters/、同级 model-wire/adapters
MODEL_ROUTING_BASH 前端使用的 bash 宿主的 bash_env 变量,再自动探测

文档里的路径一律用 node <model-routing>/routing/route.mjs where 查(一行 JSON,含 adapters_dirs:[本机, 参考];适配器目录再用 route.mjs adapter-dir <id> 取)。测试时把 MODEL_ROUTING_HOME 设为临时目录并清掉前三个变量,不要读写真实目录。

4. 写一个 profile

profiles/<name>/profile.json(schema 4.0.0):

  • vendors:每个厂商 {native_hosts, adapters, reserved}——哪些宿主能原生派它的模型、有哪些线协议适配器(按偏好排序)、是否保留不用。
  • models:alias、id、vendor、tier、effort{default,critical,allowed}、allow_null、requires_authorization,可选 native_name(宿主原生派发时用的名字)。
  • slots:slot 名 → 候选链 [{model, effort?, flags?}],顺序即优先级;main-session 是保留 slot 名。
  • task_classes:{id, name, role, slot, consult?, capability_fallback?, critical_effort?, effort_override?, binding?};binding 只有 requires(能力)、vendor、side(dispatcher|peer)。
  • profile secaumene 接入 deepseek:新增 extract 与 review.of_deepseek slot、TC26,TC14 改走 extract,quick、implement.mechanical、implement.token_dense 插入 ds-flash,wire_defaults.ask 为 ds-flash。
  • 可选:reviewer_by_vendor、hosts.<id>(覆盖能力、subagent_types)、adapters.<id>(覆盖能力)、wire_defaults.ask、negotiation.parties。
  • policy.md:策略正文(硬约束、纪律),渲染进 SKILL.md;frontmatter 可有 skill_description_append。pact.md:对端行为准则正文(不含标记行,由安装器加)。
  • 字段逐项表与从零写 profile 的例子见 skills/model-routing/references/profile-schema.md(随 skill 安装)。先从 profiles/example/ 抄起;写完跑 route.mjs check --profile <名> --host <id>(宿主相关项只 WARN)。binding.side:"peer" 的任务类只选 wire 渠道,"dispatcher" 只选 native;字符集:适配器 id 与宿主 id 只允许 [a-z0-9][a-z0-9-]*;profile 名与 subagent_types[].name 允许 [A-Za-z0-9_.-]+(不能是 .、..);check 对 profile 里引用的适配器 id 与宿主 id 按前者校验。能力词(binding.requires、capabilities、spawn_limits)是 profile 与宿主 / 适配器约定的自由字符串。
  • schema 在哪看:profile.json 见本节与 skills/model-routing/references/profile-schema.md;host.json 见 skills/model-routing/references/host-schema.md(含占位符、spawn_limits);adapter.json(schema 1.1.0,必填 test、origin;可选 test.cli(真实 CLI 可执行名,origin:"generated" 的适配器必填)、test.help_cmds、generated_by、generated_at、hash_files;touched 取不到时 fetch 填 null)见 skills/model-wire/references/adapter-contract.md 第 1 节。这三处是包内自带的权威说明,不依赖作者仓库里的设计文档。

5. 加宿主、适配器、厂商(标准 + 现场生成)

主会话的 AI 在安装 skill 时或遇到缺口时,按标准自己生成,生成物放本机目录,不改包。

流程概览

  1. route.mjs doctor [--host <id>] 找缺口:输出 {host,profile,gaps,unverified,ok},有 gap 退出 1。gaps 是没有渠道的厂商,unverified 是找到但不可用的适配器,reason 取值:not-registered、not-reference、registered-invalid、bad-signature、not-committed、conformance-failed、smoke-missing、smoke-failed、smoke-fake、smoke-not-via-cli、bad-hash-files、hash-mismatch、undeclared-file、vendor-mismatch、shadowed、invalid-adapter-json(含义与处置见 SKILL.md 第 7 节;以 route.mjs 实际输出为准)。登记记录里的 smoke.cli_bin 与 smoke.cli_version 记的是冒烟时实际调用的真二进制与版本;真实 CLI 版本变了 doctor 会打 WARN,提示重跑冒烟。
  2. 缺适配器(model-wire 的 references/adapter-playbook.md):读目标 CLI 的 --help → cp -r skills/model-wire/adapter-kit/template 到暂存目录并按 adapter-contract.md 填写(模板的 adapter.json、env.sh、common.sh 留有 TODO(adapter) 占位,清单在模板 README.md;check-adapter 要求 adapter.json 里不再有占位;模板 tests/test.sh 依次从 MODEL_WIRE_KIT、<适配器>/../../adapter-kit、<适配器>/.. 找 conformance.sh)→ 写假 CLI → adapter-kit/conformance.sh <目录> 直到通过 → adapter-kit/register.sh <暂存目录> [--model <id|alias>] [--effort e] [--force](即 route.mjs registry-commit:先复制一份快照,自己跑 conformance 与冒烟并核对结果,都在快照上;冒烟用真 CLI 经 dispatch→wait→fetch 做一次随机 N 的“加 1”挑战,supports.ask 为 true 时 ask 也问一次,花少量额度,先告诉用户;都通过才把去掉 tests/ 的快照装到 <home>/adapters/<id>/ 并写带签名的 .registered.json,运行的目录就是冒烟的目录)→ 把 id 加进本机 profile 的 vendors.<v>.adapters(改前先 route.mjs check --host,给用户看 diff)→ 再跑 doctor。
  3. 缺宿主绑定(skills/model-routing/references/host-binding-playbook.md):install.py --host <id> 退出 2 并给出四步 → 复制 hosts/template/ 到 <home>/hosts/<id>/ 并填写(消掉所有 TODO(host),id 等于目录名,路径字段都在 {home} 之内)→ route.mjs check-host <host.json> → install.py --host <id>。
  4. 校验命令:route.mjs check-host <host.json 或其目录>、route.mjs check-adapter <适配器目录>(退出 0 通过、1 不通过、2 用法错误);route.mjs adapter-dir <id> 输出 {id,dir,origin,verified,reason,bypass},找到(无论是否已验证)退出 0,id 未知退出 1,id 非法或用法错误退出 2,verified:false 的目录不能用(线协议前端见到会拒绝并退出 2)。
  5. 登记不能造假,必须冒烟才登记,信任由固定位置加哈希清单决定:
    • 免登记的只有一种:适配器在可信参考位置(skills/model-wire/adapters/,以及仅在 packet 布局下才有的测试夹具目录 tests/fixtures/adapters、tests/route/fixtures/adapters),被该位置的 REFERENCE.json 列出,且文件哈希全部相符。REFERENCE.json 由 python tools/make-manifest.py 生成、安装器随适配器复制;哈希集 = adapter.json + 六个脚本 + hash_files + tests/ 外的其余非 *.md 文件。install.py --verify 另按 manifest 校验这些文件。adapter.json 的 origin 只作说明,写什么都不改变信任结论。
    • 其余适配器(本机目录、旁路目录,或可信位置里没列出的)都必须有效登记,否则 adapter-unverified(reason 见第 1 步)。适配器 id 不能与任何可信参考位置的 id 相同(register.sh 拒绝)。
    • 只有 register.sh 能写 .registered.json。没有 registry-sign;registry-commit 不接受外部提供的 conformance 或冒烟结果,一律自己跑:快照(不含点文件)→ conformance.sh --out → smoke.sh --out → 核对两份固定格式的结果 → 在快照上算哈希 → 本机密钥(<home>/keys/registry.key,route.mjs registry-key [--ensure] 查或生成)签名 → 去掉 tests/ 装到本机目录(先备份旧的,加锁)。--force 只覆盖同 id 的本机生成物,不能跳过冒烟。
    • 冒烟经 shim 核实真 CLI:临时目录放一个与 test.cli 同名的 shim,记录调用并原样转调从剔除适配器目录后的 PATH 解析出的真二进制,test.bin_env 指向 shim;dispatch 与(支持时)ask 都要经过 shim,且回复里的 SMOKE-<N+1> 必须出现在 shim 记下的真二进制输出里(回显问句原文判失败)。origin:"generated" 的适配器必须填 test.cli;test.bin_env 指向适配器目录内的拒绝。
    • 记录(schema_version:"2")含 committed_by:"registry-commit"、files(sha256,小写十六进制,无前缀)、conformance、smoke(ok、fake:false、via_shim:true、cli_bin、cli_version、nonces、steps)、sig。校验时除签名与各项结果外,目录里不得有哈希集、.registered.json、*.md 之外的文件(reason undeclared-file),所以运行树就是冒烟树:登记后不能再补文件,需要的运行时文件都要写进 hash_files。
    • 禁止手写、拷贝或改写 .registered.json,不要往已登记目录里补文件,不要用测试变量绕过冒烟。适配器运行时只写 $WIRE_DIR,目录只读。威胁模型与残余风险见下面“适配器登记的安全边界”。

手工方式(参考实现的写法)

  • 加宿主:建 hosts/<id>/{host.json,notes.md}(字段见 references/host-schema.md)。有原生派发就在 native.kinds 声明各 kind 的参数模板;没有就留空数组,所有非主会话任务走线协议。notes.md 写怎么调 invoke。需要子代理文件就加 native.subagent 与 assets/agent-template.md。宿主沙箱会被子进程继承时设 spawn_limits。
  • 加适配器:建 skills/model-wire/adapters/<id>/,含 adapter.json、dispatch.sh、wait.sh、fetch.sh、resume.sh、ask.sh、env.sh、README.md、PITFALLS.md、带假二进制的 tests/test.sh。接口与步骤见 skills/model-wire/references/adapter-contract.md。随包新增参考实现后要跑 python tools/make-manifest.py,它会把该适配器写进 skills/model-wire/adapters/REFERENCE.json;不在清单里的适配器不算参考实现,会被要求登记。
  • 加厂商:在 profile 的 vendors 写一项(native_hosts / adapters),在 models 里加模型,在 slots 与 task_classes 里引用;厂商若走线协议,需要有对应适配器。核心代码不用改。
  • 通用性由 node tests/lint-generic.mjs 把关:核心范围内出现任何厂商、模型、宿主名都会失败(含新增的 skills/model-wire/adapter-kit/**)。

6. 验证

python install.py --verify --host claude-code   # 安装文件 hash、宿主绑定、渲染块、指令块(--host 必填)
node tests/route/route-test.mjs             # 路由用例(独立 oracle 写的 cases/*.json)
node tests/lint-generic.mjs                 # 核心范围零厂商词
bash tests/run-tests.sh                     # 线协议离线全链路(假适配器)+ 各参考适配器的 conformance + register 正反例
bash tests/install-test.sh                  # 临时 home 下装各个宿主、旧物清理、卸载
bash tests/negotiation-test.sh              # 3 方合成数据的协商工具

附录 A 宿主:claude-code

  • 安装:python install.py --host claude-code --profile <name>;home 取 CLAUDE_HOME,缺省 ~/.claude;skill 装到 {home}/skills/,子代理文件装到 {home}/agents/(由 profile 的 hosts.claude-code.subagent_types 生成,只写这些文件)。不写指令文件。
  • 原生派发 kind:workflow(agent({model, effort}))、agent_tool(Agent 工具,只能靠子代理类型表达 effort)。用法见 hosts/claude-code/notes.md。
  • 旧物清理:删旧的 codex-offload、codex-wire skill 目录(先备份),从 {home}/routing-state.json 迁移 state。
  • 派发线协议要在 Bash 工具里进行,wait 用 run_in_background。

附录 B 宿主:codex-cli

  • 安装:python install.py --host codex-cli --profile <name> --role peer(作对端)或 both;home 取 CODEX_HOME,缺省 ~/.codex。指令文件 {home}/AGENTS.md 里写对端协议块与(profile 带 pact.md 时的)策略块;每个 skill 带 agents/openai.yaml。
  • 没有原生派发:全部走线协议,需要派发方身份的任务类退化为主会话自己做(evidence:"no-native-kinds")。
  • 旧物清理:删旧的 claude-wire 目录,剥除旧的 claude-wire 与 codex-offload:pact 块。

附录 C 宿主:generic

  • 安装:python install.py --host generic --home <dir>(必须给 --home),不写指令文件,无原生派发;对端协议块要手工放进宿主自己的指令机制。

附录 C2 宿主:dsh

  • DeepSeek Harness CLI(dsh)。安装:python install.py --host dsh --profile <name> --role peer;home 取 DSH_HOME,缺省 ~/.dsh,skill 装到 {home}/skills,不写指令文件,无原生派发(native.kinds 为空)。用法见 hosts/dsh/notes.md。

附录 D 适配器(随包参考实现;本机生成的适配器在 <home>/adapters/,不在此表)

适配器 厂商 可运行的宿主 对端宿主 说明
openai-codex openai claude-code(依赖其 codex 插件的 companion) codex-cli 用 companion 起后台作业;命令参考见其 README.md,坑见 PITFALLS.md;账本会话 id 由 env.sh 导出
anthropic-claude-cli anthropic 任何有 bash 的宿主 claude-code 直接调 claude -p,作业记录在 $WIRE_DIR/jobs/<job>/
deepseek-dsh deepseek 任何有 bash 的宿主 dsh 调 dsh --profile headless;overlay 选模型与 effort,锁定权限(sandbox-policy、approval、permission)与 skill 根;带 cancel.sh;resume 拒绝权限变更(要改用 --fresh)。细节见其 README.md,坑见 PITFALLS.md

openai-codex-exec(直接调 codex exec,不依赖插件)已设计,推迟到 openai 订阅恢复后再做。

适配器登记的安全边界

威胁模型:登记闸门防的是宿主 AI 偷懒或犯错(手写记录、拿假 CLI 冒烟、事后改文件、把自己写的适配器放进参考目录冒充),不防有本机权限的蓄意者。原则:签名者必须亲自跑验证,任何命令都不接受外部提供的“证据”;可信与否由固定位置加哈希清单决定,不由文件自称。

已生效的防护

  • route.mjs registry-commit 是 .registered.json 的唯一写入者,由它亲自跑 conformance 与冒烟,再签名安装;没有公开的签名命令。
  • 冒烟用 N+1 挑战加 shim:shim 记录真二进制的实际输出,SMOKE-<N+1> 必须出现在其中,才算经过真 CLI(via_shim:true,记录里留 cli_bin、cli_version)。
  • 登记记录带本机密钥的签名,并含文件哈希、conformance 与冒烟结果;校验时逐项核对。
  • 可信参考位置只有固定目录,且各带 REFERENCE.json 哈希清单;列在清单里且哈希相符才免登记,origin 自称不算数。
  • 运行树等于冒烟树:登记安装的目录不含 tests/ 与点文件,目录里出现清单外的文件即判 undeclared-file。
  • 前端(resume 等)拒绝 unverified 适配器,并报出 reason。

残余风险

没有工具能完全堵上,如实列出:

  1. PATH 上、适配器目录外有能正确计算 N+1 的替身程序,shim 分辨不出它是不是真 CLI。登记记录里的 cli_bin、cli_version 可人工核对。
  2. 签名密钥 <home>/keys/registry.key 可读,有本机权限者可自行计算 HMAC 签名。
  3. 运行时改 bin_env 或 PATH,可绕开登记时核实过的那个二进制。
  4. 校验与执行之间存在 TOCTOU:目录只读、每进程缓存一次哈希能缩小窗口,不能消除。
  5. 适配器蓄意定位并改写 shim 日志。
  6. PATH 上被替换的 bash 或 node。
  7. 在 packet 布局里修改适配器后重跑 make-manifest。这属于对 packet 的代码改动,靠 git 审查。

另外:冒烟会花少量额度,登记前要先告诉用户;MODEL_WIRE_SMOKE_ALLOW_FAKE=1 与 MODEL_WIRE_ADAPTERS 只给测试用,它们产生的结果在真环境里会被判 smoke-fake 或带 bypass:true。

已知问题(3.0.0 未修,计划下一版;3.1.0 仍未修)

下面 6 项是 QA 负向验收发现的,规格 docs/routing-v4-design.md §22 已拟定修法,3.0.0 与 3.1.0 都没有实施。在它们修好之前,不要把 verified:true 当成对适配器的完整担保。

  • A(严重度:高)冒烟没覆盖生产路径。 现象:smoke 总是把 test.bin_env 改指 shim,适配器 env.sh 里 bin_env 未设时的默认分支从未被冒烟。影响:默认分支指向适配器目录内的替身,也能拿到 verified:true。计划:只在调用方原本设了 bin_env 时才改指 shim,否则只靠 PATH 最前的同名 shim;默认值指向目录内替身时 shim 不被调用,判 via_shim:false。
  • B(严重度:中)输出可被“洗”。 现象:shim 把真二进制输出与适配器自己写的内容不加区分。影响:适配器可以自己拿到 N+1 并写进输出,伪装成真二进制产生。计划:只把真二进制在本次调用期间写出的 stdout/stderr 及新建或变化的输出文件计为其输出;argv 或 stdin 里已含 SMOKE-<N+1> 则该步判失败。
  • C(属蓄意行为)shim 日志对适配器可见。 现象:shim 日志目录通过环境变量暴露给适配器。影响:适配器可以直接改写日志。计划:不再导出日志目录,日志目录与 shim 目录分开且不在 PATH 上;蓄意定位并改写日志仍列为残余风险。
  • D(属蓄意行为)kit 子进程环境未净化。 现象:registry-commit 调用 conformance/smoke 时继承原环境。影响:BASH_ENV、BASH_FUNC_* 等变量可注入代码;MODEL_ROUTING_BASH 也不要求绝对路径。计划:去掉 BASH_ENV、ENV、BASH_FUNC_*、SHELLOPTS、BASHOPTS、CDPATH、GLOBIGNORE,bash 用存在的绝对路径;PATH 上替换 bash/node 仍列为残余风险。
  • E(严重度:中)REFERENCE.json 本身未防改。 现象:参考清单与它所列的适配器可以一起被改。影响:改一份清单就能让改过的适配器免登记。计划:安装器在 .installed.json 记 reference_sha256,运行时比对,不符则整个参考目录不可信(reference-list-modified);安装布局下夹具目录永不可信;packet 布局要求 manifest.json 记录并匹配该清单的哈希。
  • F(严重度:中低)哈希集不含 *.md 与子目录。 现象:登记哈希与 undeclared 检查放过 *.md。影响:登记后可往适配器目录补 md 文件,而不被发现。计划:生成物的哈希集与 undeclared 检查改为安装树内除 .registered.json 外的全部文件;参考适配器的 REFERENCE.json 改为除 tests/** 外的全部文件。

已知限制

  • 3.1.0 新增已知问题(deepseek-dsh):假 CLI 接受 conf- 前缀的未知会话(为配合 conformance);resume --thread 在 $WIRE_DIR/jobs 里没有作业记录时不校验权限变更,调用方须自己确认;cancel.sh 没有在真实 dsh 上验证过;目标 dsh 是 0.2.0-rc 版本,参数与配置键可能变动。

  • openai-codex 的派发、轮询、取结果只能在 Claude Code 的 Bash 工具里、同一个项目目录下进行;会话结束时插件钩子会杀掉本会话的后台作业,跨会话只认 checkpoint.json 里的线程 id。

  • 对端沙箱能否访问某执行环境因适配器和宿主而异(Codex 沙箱进不去 WSL;宿主 codex-cli 的 spawn_limits 让从它拉起的 wire 子进程也进不去);packet.json 里 "deferred": true 时对端不跑验收,只把命令写进 peer/deferred.txt(报告字段 deferred_commands,digest 行 DEFERRED:),由派发方回到自己的环境执行。wsl 现在只是 profile、宿主、适配器里的一个能力词,线协议里不再出现。

  • unpack 的 digest 里 touched: n/a 表示适配器取不到对端实际改动的文件列表(adapter 的 fetch 输出 touched:null),不等于“没改动”,不能判为与报告一致;对端写的证据路径必须是项目相对路径,绝对路径一律被 report_check 拒绝,验收只比对条数不比对命令原文。

  • 真机冒烟(适配器登记)会花少量额度,每次登记前要先告诉用户;MODEL_WIRE_SMOKE_ALLOW_FAKE=1 只给测试用,用它登记出来的适配器会被判为 adapter-unverified(smoke-fake)。防护对象是“宿主 AI 偷懒或犯错而绕过流程”,不是拿到本机权限的恶意者,详见“适配器登记的安全边界”。

  • 对端会加载所有 skill 的描述,描述过长会被截断;model-wire-peer 的描述刻意写短。

  • packet.json 和含正则的 JSON 要用编辑器 / 文件写入工具写,不用 shell heredoc。

  • 本包 3.0.0 不兼容旧名:codex-offload、codex-wire、claude-wire、.claude-codex/、CLAUDE-WIRE v1、MODEL-WIRE v1 都不再被识别,旧的本机物只由安装器一次性清理或迁移。