T3 Code 源码拆解
Provider 与执行

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 和厂商扩展。

ACP 共享核心与厂商扩展
effect-acp transport + schemas AcpSessionRuntime AcpCoreRuntimeEvents CursorAcpSupport GrokAcpSupport CursorAcpExtension XAiAcpExtension CursorAdapter GrokAdapter Canonical events

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 应明确区分:

  1. CLI 未安装;
  2. 已安装但不支持所需 ACP 能力;
  3. 已安装但未认证;
  4. 可创建 session;
  5. 部分扩展不可用但核心可用。

将所有情况压成 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,编排层无需关心。

不同 transport 在 canonical event 汇合
Cursor ACP CLI effect-acp Grok ACP CLI Cursor / Grok Adapters OpenCode SDK / local runtime OpenCode Adapter Canonical Provider Events ProviderRuntimeIngestion Orchestration domain

共享协议不等于共享实例

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,只约束最终控制与事件语义。

On this page