claw-code ACP/Zed 与 JSON-RPC 状态契约解析:以 truthful unsupported 契约服务编辑器生态探测
【免费下载链接】claw-codeAn agent-managed museum exhibit, built in Rust with Gajae-Code / LazyCodex — developed and maintained with no human intervention.项目地址: https://gitcode.com/gh_mirrors/claudeco/claw-code
本篇文章基于仓库契约文档 docs/g011-acp-json-rpc-status-contract.md,系统讲解 claw-code 2.0 如何将 ACP/Zed 与 JSON-RPC 能力面收敛为稳定的"状态查询契约":哪些命令被定义为合法状态查询、它们返回什么样的机器可读 JSON 信封、错误调用如何被归类,以及真实协议服务为何被推迟(deferral gate)。读完本文,你将掌握如何用claw acp系列命令在编辑器探针(editor probes)与 CI 检查中正确探测能力状态,并理解这套"诚实的不支持"(truthful unsupported)设计背后的契约纪律。
为什么需要一份"状态契约"而非隐藏的守护进程
在 claw-code 的演进中,ACP(Agent Client Protocol)与 Zed 编辑器集成、JSON-RPC 服务这类能力,容易让外部系统误以为存在一个"隐藏的守护进程"(hidden daemon)。G011 契约的核心立场是:当前公共能力面(public surface)是一个诚实报告"不支持"的状态面,而不是一个未暴露的常驻服务。也就是说,claw acp这一系列命令存在的意义不是假装提供协议服务,而是为桌面端、市场(marketplace)与编辑器集成提供稳定、可探测、不会误导消费者的能力声明。
从仓库实现看,这一设计贯穿 CLI 解析层:rust/crates/rusty-claude-cli/src/main.rs 中parse_acp_args对 ACP 子命令做了白名单式解析,只接受空参数与serve,其余一律拒绝(详见下文"不支持的调用"一节);而状态输出本身则通过CliAction::Acp分发到print_acp_status,以纯文本或 JSON 两种格式打印状态。整条路径没有任何 socket 绑定、没有 daemon 进程,也没有 JSON-RPC endpoint——这与文档声明的"不启动 daemon、不暴露端点"完全一致。
支持的状态查询命令
按照契约文档,以下命令是合法的状态查询(status queries),均以退出码0结束:
claw acp claw acp serve claw --acp claw -acp claw acp --output-format json claw acp serve --output-format json| 命令形式 | 说明 |
|---|---|
claw acp | 基础状态查询,纯文本输出 |
claw acp serve | serve当前刻意作为 status 的别名,不绑定 socket、不启动 daemon、不暴露 JSON-RPC endpoint |
claw --acp/claw -acp | 等价的别名形式,便于不同习惯的调用方 |
claw acp --output-format json | 状态查询 + 机器可读 JSON 信封(见下节) |
claw acp serve --output-format json | serve别名的 JSON 形态 |
源码层面,--acp与-acp两个别名在参数归一化阶段被转换为acp子命令(见 rust/crates/rusty-claude-cli/src/main.rs 中"--acp" | "-acp"的分支处理),因此它们与claw acp走完全相同的分发路径,保证六种写法的行为一致。帮助文本同样只宣传claw acp [serve]这一种可发现形式,并在--help中明确标注"currently unsupported"(见该文件claw acp [serve]的 help 条目),避免用户产生能力幻觉。
JSON 信封:稳定的机器可读状态面
claw acp --output-format json返回一个为编辑器探针与 CI 检查设计的稳定信封,契约文档给出的完整示例为:
{ "schema_version": "1.0", "kind": "acp", "status": "unsupported", "phase": "discoverability_only", "supported": false, "exit_code": 0, "serve_alias_only": true, "protocol": { "name": "ACP/Zed", "json_rpc": false, "daemon": false, "endpoint": null, "serve_starts_daemon": false } }信封字段语义如下:
| 字段 | 类型 | 含义 |
|---|---|---|
schema_version | string | 信封自身的模式版本,当前为"1.0",作为向后兼容的判据 |
kind | string | 固定为"acp",用于标识该信封属于 ACP 状态面 |
status | string | 能力状态;文档以"unsupported"作为契约目标形态 |
phase | string | "discoverability_only",说明当前仅处于"可发现性"阶段 |
supported | boolean | false,明确声明协议未被支持 |
exit_code | number | 0,与状态查询退出码一致,便于 shell 层直接透传 |
serve_alias_only | boolean | true,声明serve仅是指令别名而非真实服务 |
protocol.name | string | "ACP/Zed" |
protocol.json_rpc | boolean | false,明确无 JSON-RPC 能力 |
protocol.daemon | boolean | false,明确无守护进程 |
protocol.endpoint | null | 无监听端点 |
protocol.serve_starts_daemon | boolean | false,serve不会拉起 daemon |
需要指出的是,仓库当前源码 rust/crates/rusty-claude-cli/src/main.rs 中acp_status_json()实现的信封在稳定字段上保持一致(schema_version: "1.0"、kind: "acp"、supported: false、protocol.json_rpc/daemon/endpoint/serve_starts_daemon等),同时额外携带status: "not_implemented"、action、message、launch_command、contracts、aliases等运行时信息,并在contracts.blocking_gates中列出阻塞真实实现的契约门(详见"推迟门(Deferral Gate)"一节)。这意味着契约文档描述的是稳定消费面,而源码实现是这一消费面的运行时投影——两者在核心判据字段上保持一致,这正是下面要强调的防御性消费原则。
消费者应如何校验
文档明确要求:消费方应检查kind == "acp"、supported == false、protocol.json_rpc == false,而不是根据命令是否存在来推断能力支持。这一原则至关重要:
- 命令存在 ≠ 能力可用,
claw acp存在恰恰是为了诚实地声明"暂不支持"; - 只检查
supported可能遗漏协议细节(例如未来supported: true但json_rpc: false的中间态); - 三字段联合判断才能覆盖"命令存在但协议未就绪"的所有组合。
集成方在 CI 或编辑器插件里做能力探测时,应解析该 JSON 而非正则抓取帮助文本,这正是--output-format json信封存在的意义。仓库测试 rust/crates/rusty-claude-cli/tests/output_format_contract.rs 中acp_guidance_emits_json_when_requested对上述核心字段逐一断言(kind、schema_version、supported、protocol.json_rpc、protocol.daemon、endpoint为 null 等),并验证内部追踪 ID(如discoverability_tracking、tracking、recommended_workflows)不会泄漏进公开 JSON——公开面只暴露契约允许的字段。
不支持的调用:错误信封与退出码 1
形如claw acp start的畸形 ACP 调用会被拒绝:以退出码1结束。在--output-format json模式下,stderr 走 CLI 通用错误信封,设置如下判据:
{ "type": "error", "kind": "unsupported_acp_invocation", "exit_code": 1 }这一行为在源码中非常清晰:parse_acp_args(rust/crates/rusty-claude-cli/src/main.rs)只放行[](即claw acp)与["serve"](即claw acp serve),其余参数一律返回Err,错误消息以unsupported_acp_invocation前缀开头并附带\n分隔的修复提示(提示用户改用claw acp或claw acp serve)。该Err最终被顶层错误处理捕获:在--output-format json激活时,rust/crates/rusty-claude-cli/src/main.rs 的main/run错误路径会调用classify_error_kind将消息归类为unsupported_acp_invocation,并组装包含type: "error"、error_kind、hint、exit_code: 1的 JSON 错误信封输出。同一归类逻辑的单元测试也覆盖了"unsupported ACP invocation. Use claw acp."这类消息的分类结果。
契约文档强调这种"白名单拒绝"的价值:畸形调用不静默、不假装成功,而是给出可机器判别的错误类别与可人类阅读的修复提示。集成方在探测脚本中遇到unsupported_acp_invocation时,应当将其视为"调用方式错误"而非"服务故障",并可以据hint字段向用户展示修复建议。
推迟门(Deferral Gate):真实协议服务何时落地
契约文档明确:真实的 ACP/Zed 或 JSON-RPC serve 工作,在路线图中 task packets、session control、event/report schemas 等契约稳定之前保持推迟。源码中这一决定被显式编码进 JSON 信封的contracts.blocking_gates字段:
"contracts": { "blocking_gates": [ "task_packet_schema", "session_control_schema", "event_report_schema" ], "stable_status_surface": "claw acp [serve] --output-format json", "unsupported_invocation_kind": "unsupported_acp_invocation" }这一设计的动机在文档中表述为:防止桌面端、市场与编辑器集成在 CLI/文件/API 契约就绪之前,成为另一种"事实来源"(alternate sources of truth)。换言之,claw-code 不希望编辑器插件先于核心契约锁定行为语义,否则后续契约演进会撕裂生态。推迟门的实质是一种契约纪律——能力面的开放顺序必须与数据面契约(任务包、会话控制、事件/报告)的成熟度对齐。
从仓库路线图 ROADMAP.md 的演进记录看,这一顺序是有据可循的:结构化任务包(typed task packet format)已由 rust/crates/runtime/src/task_packet.rs 实现(TaskPacket结构、校验、序列化与TaskScope解析),会话控制(session_control)在 rust/crates/runtime/src/session_control.rs 中提供SessionStore等实现,事件/报告 schema 则由 rust/crates/runtime/src/report_schema.rs 承担。这三者正是blocking_gates列出的三份契约——它们先稳定,ACP/Zed 的真实服务才有资格开闸。对读者而言,这意味着:在blocking_gates对应的契约在路线图中转为稳定之前,任何依赖claw acp serve提供真实 JSON-RPC 能力的集成都应当把该命令当作状态探针而非服务入口。
实践建议:如何正确消费这套契约
综合文档、源码与测试,面向编辑器插件、CI 或脚本的推荐做法可归纳为:
- 探测状态一律用 JSON 形态:执行
claw acp --output-format json(或claw acp serve --output-format json),解析 stdout 信封,而不是抓取帮助文本或依赖命令存在性。 - 三字段联合判据:以
kind == "acp"、supported == false、protocol.json_rpc == false判定"当前不支持",未来契约演进时再按schema_version做兼容分支。 - 区分两类失败:状态查询成功应退出码为
0;畸形调用(如claw acp start)退出码为1且kind/error_kind为unsupported_acp_invocation,此时应展示hint中的修复建议,而非当作系统故障告警。 - 不要把
serve当服务:serve_alias_only/protocol.serve_starts_daemon为false意味着它永不绑定端口、永不启动 daemon;集成方不应尝试连接任何 ACP/Zed 端点。 - 留意契约演进:文档信封与源码运行时信封的字段集存在演进差异(例如文档的
status: "unsupported"、phase: "discoverability_only"与源码当前的status: "not_implemented"),消费方应以文档强调的稳定判据字段为准,不依赖单字段的精确字符串。
这套"诚实的不支持"契约体现了 claw-code 在能力面管理上的克制:与其让编辑器生态误探测出一个不存在的 daemon,不如用稳定、可校验、可演进的 JSON 状态面把"尚未支持"这一事实说得明明白白,同时为真实协议服务预留清晰的解锁条件。
【免费下载链接】claw-codeAn agent-managed museum exhibit, built in Rust with Gajae-Code / LazyCodex — developed and maintained with no human intervention.项目地址: https://gitcode.com/gh_mirrors/claudeco/claw-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考