Skip to content

Feature: provider CLI 契约守卫扩展到 kimi 等 provider(目前只有 Codex 有) #1336

Description

@TERRYYYC

What problem does this solve?

我们依赖的 provider CLI(kimi-code / claude / codex / gemini / opencode)都是会自动升级的外部依赖,但我们对它们「接受什么参数、参数之间什么关系」的假设,目前只对着 mock 断言

结果就是:单元测试全绿,和线上 100% 失效,可以同时成立 —— 因为两者根本没在验证同一件事。

刚发生的实例(#1324 / 修复 PR #1323):

  • kimi-code CLI 从 0.29.1 升到 0.34.0,把 --agent-file--session 从「静默忽略」改成「硬报错」
  • kimi 猫在任何 thread 里第一轮之后完全不可用
  • 9 天运行时日志普查显示 76 次失败,分两种面孔:
    • 48x unknown option '--agent-file'(更早的版本根本没这个 flag —— 这段时间 L0 身份注入是静默失效的)
    • 28x Cannot combine --agent/--agent-file with --session/--continue
  • 整个期间 kimi-agent-service.test.js 39/39 全绿
  • 最终是**人类 operator 察觉「kimi 最近老报错」**才被发现的,不是系统报出来的

mock 测试在结构上看不见这条裂缝。修完之后我做了真机验证,但那是手动一次性操作,不在任何自动流程里 —— 下一次同样保护不了我们。

Proposed solution

这个能力我们已经有了,只是只装在一个 provider 上。

packages/api/scripts/check-codex-app-server-protocol-census.mjs

  • 挂在 packages/apibuild script 里,每次 build(含 CI)都跑
  • 真实执行装着的 codex 二进制,导出它的实际协议 schema
  • 与钉住的 fixture(test/fixtures/codex-app-server-thread-item-types.json)比对,漂移即失败
  • 降级设计成熟:
try   { census = loadInstalledCensus() }              // 装了 → 查真实二进制
catch { if (ENOENT) census = loadPinnedCensus() }     // 没装 → 退回 pinned fixture

装了 CLI 的机器查真实契约,没装的 CI 用 pinned —— 所以真正的保护发生在开发/运行机上,也正是事故发生的地方。

现状对比:

Codex kimi / claude / gemini / opencode
build 期契约守卫
pinned fixture
真机契约验证 ✅ 自动 ❌ 无(仅临时手动)

packages/api/test/fixtures/ 下目前只有 codex 一个 census fixture。

提议:把这个模式扩展到其他 provider CLI,从 kimi 开始。

kimi 的契约守卫应当断言三件事(都能用真实 CLI 在临时会话里跑):

  1. 新建会话带 --agent-file → 成功,且 agent 绑定生效
  2. 续接会话不带 --agent-file → 成功,且绑定的 agent 仍然生效(这是 fix(kimi): drop --agent-file on session resume (kimi-code >=0.30 rejects the combination) #1323 修复所依赖的核心假设)
  3. 续接会话 --agent-file → 被拒绝(把「我们知道它互斥」这件事本身钉住)

第 3 条是关键:它把我们的假设本身变成可执行断言。哪天 CLI 又改语义(比如反过来允许了、或换成别的约束),这一步会红 —— 在 build 时红,不是在用户面前红。

这三条任意一条变化,都能覆盖本次事故的两种面孔。

家里已有「真机 fixture」这个品类可复用(f254-codex-native-freshness-live-fixturef210-agy-profile-smokewindows-smoke.yml 里装 pinned opencode-ai@1.18.9),所以这不是新范式,是把已验证的做法补齐。

Alternatives considered

  • 补更多单元测试 —— 无效。mock spawn 在原理上不可能发现「真实 CLI 拒绝这串 argv」,这次 39/39 全绿就是证明。
  • 钉死 CLI 版本 —— 做不到。用户自己安装、CLI 自动更新,我们不控制运行机上的版本。而且这只会把问题从「悄悄坏掉」变成「永远用不上新版」。
  • 解析 --help 文本 —— 比 mock 强,但依赖散文措辞,CLI 改一句话就失效。直接跑真实命令更稳,也正是 Codex census 的做法(它导出 schema 而不是读 help)。
  • 维持现状,坏了再修 —— 就是这次的代价:两天、76 次失败、一段 L0 静默失效期,最后靠人类察觉。

Additional context

@co-creator 建议立项;事故调查与证据来自 #1324

[宪宪/opus5🐾]

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedMaintainer accepted: ready for implementation/mergeenhancementNew feature or requesttriagedMaintainer reviewed, replied, and made an initial triage decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions