11 · ACP、Cursor、Grok 与 OpenCode
一个标准协议怎样承载两家扩展,独立 SDK Provider 又为何仍能进入同一 canonical stream。
ACP 提供共通底座,不消灭扩展
Cursor 与 Grok 都通过 Agent Client Protocol (ACP) 接入。独立包
packages/effect-acp
封装 stdio、JSON-RPC、Schema、client/agent 双向方法与 terminal 能力。Server 的 provider/acp/ 再实现会话 runtime 和厂商扩展。
AcpCoreRuntimeEvents.ts 负责标准 session update 到核心 runtime event 的映射;CursorAcpExtension.ts 与 XAiAcpExtension.ts
处理协议外或实验性能力。这样标准升级和厂商特性不会混成一组条件分支。
stdio JSON-RPC 的资源边界
effect-acp/src/_internal/stdio.ts 负责 child process 字节流与 JSON 消息 framing。一个健壮实现至少要处理:
- stdout 可能一次包含半条、多条或跨 chunk 的消息;
- stderr 不能混入协议 stdout;
- request ID 关联 response;
- peer 既可能发送 notification,也可能反向 request;
- EOF/exit 时关闭 stream 并失败所有等待者;
- cancel/interrupt 与 Effect scope 释放 child process。
这些问题和 Codex JSON-RPC 相似,但 Schema/方法集不同,因此共享的是实现模式,不应错误复用 wire types。
Cursor 与 Grok 的启动探测
两者都有 CLI probe 测试和 support 模块,说明“支持 ACP”仍要解决本机可执行发现、版本兼容和启动参数。 Driver snapshot 应明确区分:
- CLI 未安装;
- 已安装但不支持所需 ACP 能力;
- 已安装但未认证;
- 可创建 session;
- 部分扩展不可用但核心可用。
将所有情况压成 boolean available 会让用户难以修复,也让上层无法做能力降级。
ACP terminal/update 如何进入产品模型
ACP 可能用 session updates 表示 agent message、tool call、plan、terminal output 等。T3 Code 不把 update 名直接作为 UI component key,
而是先进入 AcpCoreRuntimeEvents/runtime model,再映射 canonical event。这样 Web/Mobile 不必 import ACP Schema,
也可以把相似的 Codex/Claude tool activity 放在同一 Activity UI。
厂商新增未知 update 时,adapter 应记录诊断并保守忽略/降级,而不是因为一个新字段使整个 decode stream 终止。 真正影响生命周期的未知事件则应显式报协议兼容错误,避免悄悄错判完成。
OpenCode:不经过 ACP 的另一条路
OpenCode 使用独立 SDK/runtime,入口集中在:
opencodeRuntime.ts 还包含 CLI/parser 和本地 server 相关支持。它说明窄腰的价值:Provider 底层可以不是 stdio JSON-RPC,
只要最终提供 adapter 控制方法与 canonical event stream,编排层无需关心。
共享协议不等于共享实例
Cursor 与 Grok 都用 ACP,但它们必须有不同 provider ID、driver config、capability probe、模型目录与扩展处理。 协议 transport 可以共享,产品身份和 session namespace 不能共享。否则 session ID 碰撞或配置串用会在多实例场景中出现。
测试分层
这组实现的测试命名展示了合理分层:
effect-acp:protocol、Schema、stdio framing、client/agent request;AcpCoreRuntimeEvents.test.ts:标准 update 映射;CursorAcpExtension.test.ts/XAiAcpExtension.test.ts:扩展;CursorAdapter.test.ts/GrokAdapter.test.ts:统一 adapter 契约;OpenCodeAdapter.test.ts/opencodeRuntime.cliParsers.test.ts:独立 runtime。
这样 wire 回归不会必须启动完整 Server,产品事件回归也不依赖真实账号。
可迁移经验
- 标准协议值得封装成独立包,但扩展层必须显式存在;不要在 core parser 堆 vendor
if。 - “支持某协议”包含 transport、版本探测、认证、capability 和生命周期五个层次。
- 两个 Provider 共用协议仍需独立 identity 与实例 namespace。
- Provider 接入点应允许完全不同的 SDK/transport,只约束最终控制与事件语义。