BYOA 深入报告:Agent 控制面的通用架构、通信协议与 Runtime Adapter

BYOA(Bring Your Own Agent)不是“允许用户填一个 Agent 地址”这么简单。它真正要求系统把持久协调、外部机器连接和异构 Agent 控制拆成三个可独立演化的层。

阅读前先分清

名词在本文中的含义
控制面保存意图、共享事实、资源登记、治理与恢复的持久协调层。
节点协议控制面与 Daemon/Computer 之间的注册、心跳、领取、结果和重连契约。
Runtime Adapter将不同 CLI/Runtime 的事件、权限、取消、恢复和错误语义翻译成稳定内部契约的边界层。
ACPClient 与兼容 Agent 的会话/Prompt/Update/Permission 协议;它不负责机器调度或业务事实。
BYOA让用户自带 Agent 与本地执行环境,但不自动把控制面去中心化。

BYOA 三层总览:持久控制面、通信协议、Daemon 与 Runtime Adapter 共同连接多种 Agent Runtime 和 Workspace。

信息图 A|BYOA 不是一个远程 exec,而是持久协调、可重连节点和可替换适配器的组合。原创分析图;生成记录见 ImageGen Manifest。

研究范围:基于 2026-08-21 固定的 cc-connect、Cumora、Multica 和 Agent Client Protocol 源码与官方文档。完整文件级证据、版本和未验证项见 Source Manifest;写作范围见 Research Spec。

文中使用三种证据措辞:

  • 实现事实:固定提交中的代码或同仓库官方文档直接支持;
  • 跨项目归纳:至少两个实现呈现了相同机制,但名称和细节可能不同;
  • 设计建议:本文据此给出的自建方案,不代表现有项目或 ACP 官方标准。

先给结论

如果只记住一个公式,可以记住:

BYOA = 持久控制面 + 可重连执行节点 + 可替换 Runtime Adapter

这三个部分缺一不可:

  • 只有控制面,没有节点连接:只能控制平台托管的 Agent;
  • 只有 Daemon,没有持久协调:断线后不知道还有什么工作,也不知道是否已经执行;
  • 只有 Adapter,没有控制面:只是另一个本地 CLI wrapper;
  • 只有 ACP,没有节点协议:可以和一个 Agent 会话通信,却还不能管理用户的机器、在线状态、领取与恢复。

本文得到十个核心判断:

  1. BYOA 不是一种单协议,而是一组边界。 在本次样本中,没有项目使用一个统一协议同时解决入口、节点管理、Agent 会话和业务动作。
  2. 控制面的共同本体不是某个产品对象,而是“持久协调”。 消息、任务、对话、运行记录只是不同产品承载协调状态的方式。
  3. Daemon/Computer 是节点代理,不是 Agent。 一台 Daemon 可以承载多个 Agent;一个 Agent 也可以在不同执行节点或 Runtime 之间重新绑定。
  4. 低延迟通知不是事实源。 Cumora 用 SSE 唤醒但以 Inbox 为准;Multica 用 WebSocket 降低延迟但以 PostgreSQL 和领取结果为准。
  5. 恢复的最小单位不是 Session ID。 真正的连续性至少依赖 Agent 类型、原生 Session ID、工作目录、Provider/认证身份和会话配置的组合。
  6. Adapter 的核心工作是语义归一。 启动进程只占很小一部分;事件、权限、取消、Resume、用量、错误和能力降级才决定接入是否可靠。
  7. 逻辑 Session 不必等于一个长期 OS 进程。 Claude Code 可以长期保持 stream-json 进程;一些 CLI 则每回合重新启动,再用原生 Resume ID 延续上下文。
  8. ACP 很重要,但它位于 Adapter 边界。 它标准化 Client 与 Agent 的 Session/Prompt/Update/Permission,不定义机器注册、心跳、队列、租约或重试。
  9. “本地执行”不等于“平台永远看不到 Secret 或上下文”。 Provider 登录可以只留在本机,但平台下发的环境变量、MCP 配置、任务上下文和 Agent 回写内容仍可能经过服务端。
  10. 适合抄作业的架构不是复制某个产品,而是固定三个深模块。 控制面稳定保存意图与事实;节点协议负责可靠连接;Adapter SDK 消化 Runtime 差异。

全景心智模型:三个层面,不是三个进程

Mermaid diagram

图 1|BYOA 三层总体架构。它是本文依据多个公开实现归纳的分析图,不是任何项目的官方架构图。

飞书 Aily 的第三方 Agent、本地 Adapter,以及 Raft 的 Computer/External Agent 都说明产品正在把执行面开放给用户已有 Agent。不过本文不展开它们的产品设计;主要实现证据来自 cc-connect、Cumora 和 Multica。

三者代表的不是相同产品,而是三种不同深度:

实现它首先是什么控制状态的重心本地执行的重心
cc-connect本地消息入口与 Agent 会话桥平台 Session、历史和原生 Agent Session 映射直接管理 Agent CLI/ACP 子进程
Cumora协作控制面 + BYOA Computer持久消息/Inbox、共享动作、Run 和 Agent 身份一台 Computer 承载多个独立 Agent Engine
Multica持久工作协调与异构执行调度队列、执行尝试、状态、结果和恢复记录Daemon 领取工作、准备目录、调用 Backend
ACPClient-Agent 协议,不是控制面不定义业务事实和节点调度定义一个 Client 如何控制兼容 Agent

这张表也给报告划定了边界:我们不是在寻找所有产品都拥有的同名对象,而是在寻找不同产品为了可靠驱动外部 Agent,不得不承担的共同责任。


第一部分:控制面——把外部计算变成可持续的工作

1. 控制面的本质是持久协调,不是聊天 UI

当 Agent 和控制面运行在同一个进程里时,许多问题可以被进程内变量暂时掩盖:当前做什么、使用哪个模型、是否正在运行、刚才输出了什么。但 BYOA 把执行放到用户电脑或服务器后,这些隐含状态会被网络、重启和多机并发撕开。

控制面必须重新回答:

  • 有什么意图仍未完成?
  • 哪个执行节点具备所需 Agent 和资源?
  • 当前是否已经有人或某个节点接手?
  • 执行到哪里,是否仍然存活?
  • 用户新消息应该进入当前回合,还是成为下一次执行?
  • 成功、失败、取消和失联分别意味着什么?
  • 重试会不会重复产生副作用?
  • 最终结果如何回到所有协作者能看见的地方?

因此,本文把控制面的共同职责归纳成六类,而不规定具体表名:

通用职责必须回答的问题可能的具体承载方式
意图入口什么事情需要处理?消息、请求、计划、自动触发
持久协调哪些工作尚未完成,当前处于什么阶段?Inbox、工作记录、状态机、事件日志
执行资源哪些机器、Runtime 和能力当前可用?Computer、Daemon、Runtime registry
执行生命周期谁接手、何时开始、如何停止、结果是什么?Wake、Claim、Run、Attempt、Result
连续性如何延续 Agent 上下文与本地环境?Session binding、Workspace、Memory、配置
治理与观察谁被授权做什么,成本和故障如何追踪?Token、Scope、Approval、Usage、Audit

“消息”“任务”“运行”在某个产品里可能是正式对象,在另一个产品里可能只是内存事件。上表表达的是分析类别,不是建议所有控制面照抄一套数据库 schema。

2. OS 类比如何帮助理解

把控制面想成操作系统,是一个足够好用的直觉模型:

操作系统直觉BYOA 中对应的理解
进程需要被创建、调度、停止一次 Agent 执行需要被分派、观察、取消
进程有地址空间和文件Agent 有原生上下文、工作目录和本地记忆
进程间通过协议通信用户、控制面、Daemon、Agent 通过消息和回调通信
崩溃后由持久状态恢复控制面通过工作记录、Session ID 和 Workspace 恢复
内核管理权限与资源控制面/Daemon管理身份、并发、凭据和审批

这个类比的用途是帮助人快速理解“为什么 Agent 外置后需要调度、状态和恢复”,而不是建立严格同构。实际实现中,一个逻辑会话可能对应一个长期进程、多个短进程,甚至一个远端服务;控制面也不一定拥有类似内核的强制隔离能力。

3. Daemon 为什么不能直接叫 Agent

Cumora 和 Multica 都给出了非常明确的分层证据。

Cumora 把 Computer 定义为 Agent 的运行宿主:一台 Computer 可以托管多个 Agent,每个 Agent 有独立 home、token、Engine Session;Engine 才是 Claude Code、Codex、Grok 或 Cursor。EngineAdapter 负责启动和控制具体大脑,而 Daemon 负责 Agent roster、令牌刷新、Wake、并发和上报。

Multica 也区分:

  • Daemon:运行在一台电脑上的后台程序;
  • Runtime:某个工作区可使用的“机器 × Agent 工具或自定义协议配置”;
  • Backend:如何实际控制那种 Agent。官方文档

所以更准确的关系是:

一台机器
  └─ 一个 Daemon
       ├─ Runtime A:Claude Code
       ├─ Runtime B:Codex
       └─ Runtime C:兼容某协议族的自定义命令
 
每个 Runtime 可以被多个逻辑 Agent 配置引用;
每次实际执行再形成独立的 Session 或 Attempt。

这个分层带来三个工程收益:

  1. 机器生命周期与 Agent 生命周期解耦。 Daemon 可以重启,Agent 身份和未完成工作仍留在控制面。
  2. 发现与执行解耦。 Daemon 先报告本机安装了什么,控制面再决定哪些 Agent 可以路由到这里。
  3. Adapter 与调度解耦。 新增 Agent CLI 时,通常不必改动控制面的业务状态模型。

4. 三种控制深度:桥接、唤醒、领取

4.1 cc-connect:平台会话到本地 Agent 会话的桥

cc-connect 的核心不是远端任务调度,而是将消息平台转换成统一 Platform,再将 Agent 转换成统一 Agent/AgentSession。它的 Session 保存平台侧会话、Agent 原生 Session ID、历史 Session ID、当前 Provider、History 和时间字段。

这说明即便是轻量桥接,也必须解决两个身份域的映射:

聊天平台 session key
       ↕
cc-connect Session
       ↕
Claude/Codex/ACP native session id

它没有必要先把每条消息变成重型工作对象;但它仍需要保存“这个聊天应该恢复哪个 Agent 上下文”。这是一种会话连续性控制面。

4.2 Cumora:持久 Inbox 加轻量 Wake

Cumora 的关键不变量是:消息先成为服务端持久事实,Wake 只负责降低响应延迟。BYOA Daemon 每个 Agent 保持一条 /runtime/wake-stream SSE;连接或重新连接时执行 catch-up,另有 20 秒 Inbox poll 作为独立兜底。BYOA lifecycle

因此:

SSE 可以丢;Inbox 不能跟着丢。Wake 告诉 Daemon“值得看一下”,Inbox 才告诉它“到底还有什么没处理”。

Daemon 读取未读快照,做 triage,再启动或恢复 Engine Session。若 Engine 失败,源码选择不推进 seen/ack,让未读内容保留到后续重试。这里的正确性不依赖 SSE exactly-once,而依赖服务端未读边界。

4.3 Multica:持久队列加原子领取

Multica 面向更明确的异步工作。控制面保存待执行记录,Daemon 通过 tasks.claim 领取,再准备工作目录、启动 Backend、上传事件与结果。

最值得借鉴的是 WebSocket 与 HTTP 的关系:WebSocket 用于低延迟通知、心跳和 RPC,但 PostgreSQL 仍是权威状态;Daemon 也保留轮询。更细一层,Multica 区分两种传输失败:

  • 请求确定没有发出:可以安全转向 HTTP;
  • 帧已经发送,但连接在响应前断开:结果未知,不能立刻再领一次,否则可能重复领取。

这个区别在 wsrpc.go 中直接编码为 unavailable 与 uncertain,并为不确定结果保留一个安全窗口。

三种模式的共同点不是“都有 Task”,而是:

控制面必须在易失执行之外,保留足以重新判断下一步的持久状态。

5. 状态应该放在哪里

Mermaid diagram

图 2|状态所有权。控制面保存团队需要共享和故障后需要重建的事实;本地保存执行环境与 Agent 原生状态。真实产品可能让部分状态跨边界流动。

5.1 控制面应保存什么

跨项目归纳后,至少应保存:

  • 尚未完成的意图或可重新拉取的输入;
  • 节点与 Runtime 的身份、能力和最近在线状态;
  • 某次执行是否已经被接手、开始、取消或终结;
  • 当前绑定的原生 Session ID 和工作目录引用;
  • 可审计的关键事件、失败分类和最终结果;
  • 需要团队共享的上下文与产物。

5.2 本地应保存什么

  • Provider 原生登录和全局配置;
  • 仓库、依赖、编译缓存与本地工具链;
  • Agent 原生 Session 数据;
  • 与某个 Agent/项目相关的本地 Memory、Skills、Notes;
  • 运行中进程、临时文件和尚未上报的短期状态。

5.3 状态边界不是天然安全边界

Cumora 明确说明 per-Agent home 是目录级隔离,但同一 OS 用户下的 Agent 仍共享 Claude/Codex 等全局登录;目录分开并不是强沙箱。Cumora BYOA boundaries

Multica 也提醒:本地 Agent 登录和代码目录留在机器上,但自定义环境变量与 MCP 配置可能保存在服务端并在执行时下发。因此“BYOA = 所有秘密只在本地”并不成立;应该逐项做数据流清单。Multica data boundary

6. 一次执行的通用生命周期

Mermaid diagram

图 3|通用 BYOA 生命周期。cc-connect 通常省略远端节点领取;Cumora 使用 Wake + Inbox;Multica 使用持久队列 + Claim。

关键顺序是:

  1. 先持久化意图,再通知执行。 否则通知丢失就等于工作丢失。
  2. 先取得执行权,再启动高成本 Agent。 否则多 Daemon 或并发 poll 会重复执行。
  3. 尽早保存 Session/Workspace 指针。 Agent 可能在长回合中途崩溃,不能只在成功结束时保存。
  4. 终态回报需要幂等。 Daemon 可能已经提交成功,但在收到 HTTP/WS 响应前断线。
  5. 业务完成与进程成功分开。 进程退出码为零只说明一次执行结束,不自动证明用户意图已经验收。

7. 恢复不是一个 Resume 按钮

7.1 五类故障要分开处理

故障位置不能做的错误假设推荐恢复依据
通知丢失没收到 Wake 就没有工作持久 Inbox/队列 + poll/reconcile
Daemon 在接手前离线任务已经发送给这台机器仍未被领取的持久状态
已领取但未启动状态会自动回到队列lease、prepare heartbeat、stale dispatch 回收
Agent 中途退出只要重启同一个命令就能续上原生 Session ID + workdir + provider identity
终态回报响应丢失超时就再提交一份新结果task/run identity + 幂等终态 API/outbox

Cumora 把 Engine Session ID 写在 ~/.cumora/sessions/<agentId>.session,并在运行中每两秒尝试提取和落盘;进程死亡后用 ID 重建 Session。Engine 失败时不 ack 未读输入,避免“执行失败但消息已消费”。

Multica 更进一步,将 session_id 和 work_dir 在执行中途钉到服务端记录,并显式处理 stale dispatch、Runtime recovery、Resume 拒绝和已退休 Session。它的统一 Result 专门保留 ResumeRejected,因为“Session ID 非空”也不代表历史仍可被 Provider 接受。

cc-connect 的 SessionIDValidator 则说明另一个风险:一个保存下来的 Session ID 可能属于另一个项目或工作目录。恢复前必须验证所属范围,不能把“格式正确”当成“上下文正确”。

7.2 Session 连续性的真实键

可恢复会话由 Adapter 类型、原生 Session ID、Workspace 或 Workdir、Provider 身份与会话配置共同绑定。

信息图 B|Session ID 只是索引。当 Runtime、工作目录、账户或配置发生变化时,“有 ID”并不等于“能正确恢复”。

**设计建议:**不要只存 native_session_id,而应至少保存或能重新推导下面这个绑定:

SessionBinding = {
  adapter_family,
  native_session_id,
  execution_node_id,
  workspace_identity,
  work_dir,
  provider_or_account_identity,
  standing_prompt_version,
  runtime_config_version
}

为什么需要这么多字段:

  • 同一个 Session ID 在不同 Agent/Provider 的命名空间里没有意义;
  • 工作目录丢失后,会话即使能载入,也可能无法继续修改原来的文件;
  • Provider 登录切换后,原 Session 可能不可见;
  • standing prompt 或 Runtime 配置发生破坏性变化时,盲目续接可能产生行为漂移;
  • 节点换机后,本地 Session store 未必存在。

8. 身份、凭据与信任边界

BYOA 至少涉及三种身份:

身份证明谁应拥有的权限
用户/管理员身份谁有权添加或移除机器配对、授权、查看与撤销
节点身份哪台 Daemon 正在连接注册 Runtime、心跳、为本机 Agent 请求短期凭据
执行身份哪个 Agent/哪次执行正在行动读取限定上下文、回写进度、调用被授权动作

Cumora 的做法是长期可撤销 Device Token 加每 Agent 短期 Runtime JWT;服务端 /runtime/cli 会丢弃客户端自己提交的 --as,重新注入 JWT 绑定的 Agent 身份。Multica 则在领取后向一次执行提供 task-scoped token,并把 Workspace、Agent、Task ID 注入执行环境。

可抄的原则是:

  • 不要把用户网页登录 Cookie 直接交给 Daemon;
  • 不要让一个节点 Token 天然拥有整个租户的业务权限;
  • 每次执行的权限范围应能独立撤销和审计;
  • Runtime 的 Provider 凭据与控制面的业务凭据分开;
  • Daemon 所在 OS 用户仍是本地文件和全局 CLI 凭据的真实边界;
  • “自动审批”是高风险运行策略,不是 Adapter 的技术必需条件。

第二部分:通信协议——BYOA 不是一条连接,而是四段协议

BYOA 的四段通信边界:入口协议、节点控制协议、Agent 控制协议与 Agent 反向调用的动作资源协议;ACP 主要覆盖 Adapter 到 Agent Runtime。

信息图 C|ACP 可以显著收窄 Agent Adapter 的私有协议面,但不会自动提供机器注册、节点心跳、工作领取、持久队列和业务事实。

9. 先拆开四个通信边界

很多讨论把“控制面如何连接 Agent”当成一条协议。真实系统至少有四段:

Mermaid diagram

图 4|四段通信边界。ACP 主要落在第 ③ 段;它不天然替代第 ② 段的节点管理,也不定义第 ① 段的产品入口。

9.1 入口协议

把飞书、Slack、Telegram、Web UI 或 Webhook 的输入转换成控制面可理解的意图。cc-connect 的外部 Bridge Platform Protocol 就位于这一侧:Adapter 用 WebSocket 注册聊天平台能力,并传入 session_key、消息和 reply context。

这与它的 ACP Adapter 是两件事:Bridge 统一“消息从哪里来、回复发到哪里”,ACP 统一“如何控制一个 Agent”。

9.2 节点控制协议

解决用户机器如何加入系统:

  • pairing / registration;
  • 节点和 Runtime capability;
  • heartbeat / online status;
  • wake / available-work hint;
  • pull / claim / lease;
  • progress / run heartbeat;
  • cancel / steer;
  • result / failure / retry;
  • config refresh / revoke / version compatibility。

Cumora 与 Multica 都自行实现了这一层,因为 ACP 不定义它。

9.3 Agent 控制协议

解决一个已经选定的 Runtime 如何开始和继续工作:

  • 初始化与能力协商;
  • 创建或恢复 Session;
  • 发送 Prompt;
  • 接收文本、思考、工具、计划和用量事件;
  • 请求和响应权限;
  • Steering、取消和关闭;
  • 获取原生 Session ID 与最终结果。

ACP、Codex app-server、Claude stream-json 和各类 CLI JSON 输出都属于这一层。

9.4 动作与资源协议

Agent 执行期间还需要“做事”:发消息、改共享对象、访问文件、调用 MCP 或业务 API。Cumora 把 cumora CLI 变成 /runtime/cli 的本地 shim;Multica 给每次执行注入 task-scoped CLI 身份;ACP 允许 Agent 反向请求 Client 的 FS/Terminal,也允许 Client 将 MCP Server 配置交给 Agent。

如果忽略这一层,就会得到一个“可以聊天但不能可靠改变世界”的 Agent。

10. 传输不是协议语义

实现低延迟通道正确性/补偿通道Agent 控制通道
cc-connect各 IM SDK/WebSocket;外部 Platform Bridge 可用 WS本地 Session store 与 Agent 原生 Sessionstdio stream-json、Codex app-server、ACP、单次 CLI
CumoraServer → Daemon:SSE Wake/SteerDaemon → Server:HTTP Runtime API;durable Inbox + 20s pollClaude stream-json、Codex app-server、Grok ACP、Cursor one-shot
MulticaDaemon WebSocket hint/heartbeat/RPCPostgreSQL + HTTP API + polling/recovery统一 Backend 封装多种原生/ACP/CLI 协议
ACP v1本地默认 newline-delimited JSON-RPC over stdio不定义业务持久化、节点重连或队列协议本身

同样使用 WebSocket,不代表协议相同;同样使用 JSON-RPC,也不代表解决相同问题。应该分别问:

  1. 谁发给谁?
  2. 这条消息是事实、命令、提示还是事件?
  3. 需要响应吗?
  4. 响应丢失后是否能安全重试?
  5. 重连后从哪里恢复?

11. 最可靠的组合:Durable Truth + Ephemeral Nudge + Reconcile

控制面的持久事实从 Pending 推进到 Terminal;Wake、WebSocket 或 SSE 只是可能丢失的短暂通知,Daemon 通过主动对账恢复。

信息图 D|通知负责快,持久状态负责对。只有通知没有对账,断线就会变成丢工作或重复执行。

Mermaid diagram

图 5|通知负责快,持久状态负责对。Cumora 和 Multica 都使用了这一组合,只是事实对象和领取方式不同。

这比追求“WebSocket 永不掉线”更现实。需要特别设计三种语义:

11.1 At-least-once 通知

Wake 可以重复,因为 Daemon 最终会重新读取 Inbox/队列。去重发生在持久状态和执行权上,而不是要求通知系统 exactly-once。

11.2 原子取得执行权

多个 Daemon、多个 poll 或 WebSocket 与 HTTP fallback 可能同时看到工作。执行权必须由数据库 compare-and-set、行锁、lease 或等价机制决定。

11.3 不确定投递

如果客户端知道请求没有发出,可以换通道重试;如果请求可能已经提交,就不能立即再执行一次。Multica 的 WS RPC 明确建模了这一点。这是所有“WebSocket 优先、HTTP 兜底”方案容易漏掉的细节。

12. 节点协议最小语义

**设计建议:**自建控制面时,不必先发明复杂标准,但应把下面语义做成稳定版本化协议:

类别建议消息/操作关键字段
建连node.pair、node.registernode identity、version、platform、nonce
能力runtime.upsertadapter family、binary version、capabilities、capacity
存活heartbeatnode/runtime、busy slots、running executions、timestamp
通知work.availablework hint、routing key;不承载全部事实
领取execution.claimrequest id、node、runtime、lease、idempotency key
开始execution.startedexecution id、native session/workdir pointer
流式事件execution.eventsequence、event type、payload、redaction
控制execution.cancel、execution.steertarget execution/session、reason、message
续租execution.heartbeatprogress checkpoint、lease extension
终态execution.complete/failresult、error class、session/workdir、usage
对账node.reconcile本地仍在运行的 execution set

协议里应明确:

  • 每条命令的幂等键;
  • Server 与 Daemon 谁拥有状态转换权;
  • 事件序号是否按执行、Session 还是节点递增;
  • 心跳超时意味着 offline、failed 还是 unknown;
  • Cancel 是请求、确认还是强制状态;
  • 断线后由哪一侧发起 reconcile;
  • 版本不兼容时是否拒绝领取新工作。

13. ACP 到底统一了什么

ACP 官方定位是标准化 Code Editor/Client 与 Coding Agent 的通信,类似 LSP 对语言服务器集成的作用。ACP Introduction

13.1 ACP v1 的基本流程

Mermaid diagram

图 6|ACP v1 典型流程。session/load 会向 Client 回放历史;较新的可选 session/resume 只恢复上下文,不回放。

稳定 v1 已覆盖:

  • initialize:协议版本和能力协商;
  • 可选 authenticate;
  • session/new;
  • 可选 session/load、session/resume、session/list、session/delete、session/close;
  • session/prompt 和 session/update;
  • Plan、文本、Tool Call、Usage 等更新;
  • Agent → Client 的 permission request;
  • Client 可提供文件系统、Terminal 和 elicitation 等能力;
  • session/cancel 与通用 $/cancel_request;
  • _meta 与以下划线开头的扩展方法;
  • MCP Server 配置委托。

本地 Agent 默认作为 Client 子进程,使用 newline-delimited JSON-RPC over stdio;Agent 的 stdout 必须只承载协议消息,日志走 stderr。ACP 文档虽然描述 HTTP/WebSocket 远程场景,但截至本次快照仍明确标注完整远程支持在推进中。

13.2 ACP 为什么很适合 Adapter 层

cc-connect 的 ACP Adapter 展示了最直接的映射:

ACP initialize/session/new/session/prompt
              ↓
cc-connect AgentSession.Send
 
ACP session/update
              ↓
cc-connect EventText / EventToolUse / EventPermission / EventResult
 
cc-connect /stop
              ↓
ACP session/cancel

只要 Agent 实现 ACP,cc-connect 就不需要为它重新解析一套专有 JSON。Cumora 用 ACP 控制 Grok,Multica 也有 ACP 协议族 Backend。三者都说明 ACP 可以显著减少“每个控制面 × 每个 Agent”的组合爆炸。

13.3 ACP 没有统一什么

BYOA 问题ACP v1 是否定义谁仍要负责
用户如何把机器加入控制面否控制面 + Daemon 节点协议
一台机器有哪些 Agent CLI否Daemon discovery / runtime registry
节点心跳与在线状态否节点协议
工作排队、领取、lease否控制面调度
断网后有哪些工作未完成否持久 Inbox/队列 + reconcile
Session/Prompt/Update/Permission是ACP Client 与 Agent
业务状态和共享产物否控制面 action surface
跨 Agent 编排否控制面/上层 Agent orchestration
OS 进程、工作目录和凭据隔离部分参数,不负责实现Daemon/Adapter/宿主 OS

因此最准确的结论不是“ACP 是 BYOA 标准”,而是:

ACP 是 BYOA 协议栈中最接近标准化的一段:Daemon/Adapter 与 Agent Runtime 的会话控制边界。

13.4 v2 Draft 暗示的方向

ACP v2 在 2026-07-20 发布 Draft,重点之一是从严格的“一个 Prompt 拥有一个 Turn 生命周期”转向更持续的 Session:更新可在 Session 中持续发生,Prompt response 更接近“消息已被 Agent 接收”,而不是“全部工作已经结束”。ACP v2 Draft

这与 BYOA 长任务、后台执行、Steering 和多客户端观察更加契合。但 v2 仍是 Draft,生产实现应同时支持 v1,并通过版本协商与 feature flag 隔离 v2 行为,不能把 Draft 语义当成既成标准。

14. 有没有可能统一整个 BYOA 协议

从本次样本看,近期更现实的路线是协议组合,而不是一个协议包办所有层:

入口侧:平台/Webhook 自有协议
节点侧:产品自己的 versioned control protocol
Agent 侧:优先 ACP;否则专有 structured adapter
工具侧:MCP + 产品 action API / CLI shim

原因是节点调度与产品业务强相关:聊天桥只需要按会话路由,协作平台需要 Inbox 与共享动作,任务系统需要 claim、lease 和终态事务。强行把这些业务对象塞进 ACP,会让 ACP 从 Agent-Client 协议膨胀为控制面框架。

可以标准化的是:

  • 节点注册和 capability 的基础 envelope;
  • 执行事件的通用类型;
  • trace、idempotency、error category;
  • Adapter conformance;
  • ACP 与节点协议之间的映射约定。

不应过早标准化的是:

  • 产品如何定义工作承诺;
  • 多 Agent 如何分工;
  • 共享状态的具体 schema;
  • 什么结果算业务完成。

第三部分:适配器——真正困难的是语义,不是 exec

15. 三套接口暴露了同一个问题

15.1 cc-connect:小核心 + 大量可选能力

cc-connect 的 Agent 核心接口非常小:启动/恢复 Session、列出 Session、停止;AgentSession 只要求 Send、Permission、Events、Session ID、Alive 和 Close。

模型切换、Provider、Memory、Usage、Context Usage、Cancel、Commands、Skills、WorkDir、Mode 都通过可选接口表达。这是一种典型的 capability-oriented 设计:核心路径保持稳定,Agent 差异不会迫使所有实现伪造能力。

15.2 Cumora:Engine 同时承担大脑与健康检查

Cumora 的 EngineAdapter 包含:

  • seedHome:为 Agent 布置 persona、memory、skills;
  • startSession:若支持,启动长期会话;
  • run:one-shot fallback;
  • classify:小模型 triage;
  • probe/probeWake:验证真实 Wake 路径。

它比纯 Agent Adapter 更厚,因为 Cumora Daemon 还需要判断本地订阅、模型和 Wake path 是否健康。

15.3 Multica:Execution 返回事件流和唯一终态

Multica 的 Backend 只有一个 Execute,返回:

  • Messages:text、thinking、tool-use、tool-result、status、error、log;
  • Result:只产生一次的 completed/failed/aborted/timeout/cancelled、输出、Session ID、Usage 和 ResumeRejected。

它更适合任务执行器:外部 Daemon 管理目录和执行生命周期,Backend 只把一次 Prompt 运行归一为流式消息加终态。

15.4 共同契约

通用能力cc-connectCumoraMultica
检测与健康Agent factory/各实现probe、probeWakeRuntime discovery/version check
创建或恢复StartSession(sessionID)startSession / run(resumeSessionId)Execute(ResumeSessionID)
输入Sendsend / run(prompt)Execute(prompt)
流式事件Eventslog/hop callbacks + run resultSession.Messages
权限RespondPermission多为 headless policy/engine native按 Backend 和 daemon policy
取消可选 CancelTurnAbort/stop/steercontext cancellation + process cleanup
原生 SessionCurrentSessionIDsessionIdResult.SessionID / status event
能力差异大量 optional interface是否有 persistent session/standing promptProvider-specific option support + matrix
终态EventResultEngineRunResult唯一 Result

16. 四种 Runtime 控制模式

长期流式 CLI、JSON-RPC Server、标准 ACP 和 One-shot 加 Resume ID 四种 Runtime 控制模式,最终由 Adapter 归一到统一事件接口。

信息图 E|逻辑 Session 可以由长期进程承载,也可以每回合重启进程后用原生 ID 续接;控制面不应依赖这些进程形态。

16.1 长期结构化进程:Claude Code stream-json

cc-connect 对 Claude Code 启动一个长期进程,并使用:

--input-format stream-json
--output-format stream-json
--permission-prompt-tool stdio
--resume <native-session-id>

它可以跨多个回合保持 stdin/stdout、接收权限请求、持续输出结构化事件。Cumora 也优先使用类似持久 Session,并将 standing prompt 通过 --append-system-prompt-file 在会话启动时注入一次。

优点:

  • 冷启动和 MCP/Skills 初始化只付一次;
  • 支持更自然的同回合交互;
  • 事件和 Session ID 可尽早获取。

风险:

  • 需要严格监管 stdout、stderr、stdin 并发和进程树退出;
  • CLI 版本改变事件 schema 或 flag 时容易漂移;
  • 长期进程更容易出现半死连接、背压和资源泄漏。

16.2 长期 JSON-RPC Server:Codex app-server

cc-connect 和 Cumora 都直接控制 Codex app-server:

spawn codex app-server
  → initialize
  → thread/start 或 thread/resume
  → turn/start
  ← item/started、item/completed、turn/completed
  ↔ command/file/permissions approval、requestUserInput

与“解析 stdout 文本”相比,app-server 暴露清晰的 Thread、Turn、Item、Approval 和 Usage 语义。代价是 Adapter 必须实现完整 JSON-RPC client:请求 ID、并发 pending map、notification、server-to-client request、write timeout、断线和进程清理。

16.3 标准协议:ACP Agent

ACP 的价值是 Adapter 不再理解每个 Agent 的私有事件,只需要实现一次通用 Session/Prompt/Update/Permission 映射。

但“实现 ACP Adapter”仍然不是零成本:

  • 不同 Agent 广告的 capabilities 不同;
  • session/load、resume、list、delete、close 都可能缺失;
  • 扩展字段和方法需要保守转发;
  • Client 是否提供 FS/Terminal/MCP 会改变 Agent 的工具边界;
  • 日志必须与 stdout 协议严格分离;
  • ACP v1/v2 需要版本协商。

16.4 One-shot CLI + Resume ID

并非每个 Runtime 都有长期控制协议。cc-connect 的 Codex exec 变体每次 Send 启动 codex exec 或 codex exec resume;Cumora 的 Cursor 在该快照下也是每次 Wake 启动一次命令。

这种模式仍可提供逻辑 Session,但有明显能力上限:

  • 不能真正向运行中的回合 Steering;
  • 权限通常只能预先通过 flag 决定;
  • 每回合支付进程和配置冷启动;
  • Cancel 更接近杀进程;
  • Resume 质量完全依赖 CLI 的原生持久化。

这再次说明:Session 是产品/协议对象,不等同于 OS 进程。

17. Adapter 能力阶梯

Adapter 从启动进程、会话连续、结构化事件、权限与取消、故障恢复到 Steering 的六级能力阶梯。

信息图 F|“支持某个 Agent”必须说明支持到哪一层。Capability Discovery 与 Graceful Degradation 比一个含混的绿色勾更可靠。

Mermaid diagram

图 7|Adapter 能力阶梯。“支持一个 Agent”至少应说明支持到哪一层,而不是只给一个绿色勾。

这也是 Multica “自定义 Runtime Profile 不能创造新协议”的原因:用户可以替换命令或 wrapper,但仍必须选择一个系统已经理解的协议族;否则 Daemon 不知道该加什么 flag、如何解析事件、怎样恢复 Session。Custom runtime profiles

18. 不要用一个巨大接口伪装一致性

比较稳妥的 Adapter 设计是:

小型必需核心
  + 显式 Capability 描述
  + 可选子接口或 capability-gated methods
  + 每种能力的降级规则

例如:

能力原生支持合理降级不可伪造的边界
Resume恢复原生 Session新 Session + 连续性提示 + 重建上下文不能悄悄当成成功 Resume
Steering向当前回合注入保存为下一回合高优先级输入不能声称已影响当前决策
Permission结构化请求/响应预配置只读/自动模式无交互通道时不能挂起等人
Tool events结构化 tool use/result降级为 log/text不能从任意文本猜测高风险审批
UsageRuntime 原生 usage只报 duration/unknown不能用字符数伪造精确 token
Session list/delete原生能力本地只管理已知绑定不能删除不属于当前 Adapter 的状态

19. 一个可用的统一事件模型

**设计建议:**Adapter 对上不要直接暴露每个 CLI 的原始 JSON,而应转换为有限但可扩展的事件:

type RuntimeEvent =
  | { type: 'session'; nativeSessionId: string }
  | { type: 'text'; channel: 'final' | 'commentary'; text: string }
  | { type: 'thinking'; text: string }
  | { type: 'tool.start'; callId: string; name: string; input: unknown }
  | { type: 'tool.finish'; callId: string; output: unknown; status: string }
  | { type: 'permission'; requestId: string; options: PermissionOption[] }
  | { type: 'usage'; model?: string; tokens?: TokenUsage; cost?: Money }
  | { type: 'checkpoint'; resumable: boolean; metadata?: unknown }
  | { type: 'warning'; code: string; message: string }
  | { type: 'log'; level: string; message: string }

事件还需要共同 envelope:

  • execution_id:属于哪次物理执行;
  • session_binding_id:属于哪个逻辑连续会话;
  • sequence:同一执行内单调递增;
  • timestamp;
  • adapter_family 和 runtime_version;
  • sensitivity / redaction;
  • raw_ref:调试时指向被安全保存的原始事件,而不是直接把全部原始内容推到控制面。

为什么不直接上传 raw JSON:

  • 版本升级会把 Provider schema 传播到整个系统;
  • Prompt、工具参数和环境可能包含 Secret;
  • UI 与控制面会被迫理解每个 Agent;
  • 无法跨 Agent 统计错误、用量和工具行为。

20. Permission、Cancel、Close 是三件事

  • Permission:某个潜在动作是否允许发生;
  • Cancel Turn:停止当前计算,但尽量保留可继续的 Session;
  • Close Session:释放长期会话和其进程/资源。

cc-connect 特意增加可选 CancelTurn,避免 /stop 直接 Close() 后丢失会话。ACP 也区分 session/cancel 与 session/close。Codex app-server 的 approval 是 server-to-client JSON-RPC request,Adapter 必须将请求挂起、展示给用户,再把结果回送给原请求 ID。

常见错误是把三者都实现成 kill -9。这样虽然能停止进程,却无法回答:

  • Session 是否还能继续?
  • 待审批请求如何结算?
  • 已启动的工具子进程是否一并退出?
  • 控制面应标记 cancelled、failed 还是 unknown?

21. System Prompt、Memory、Skills、MCP 不应混成一块

载体更适合保存什么生命周期
Standing/System Prompt稳定运行规则、身份、动作边界Session 创建时注入,版本化
每回合 Prompt当前未处理输入、triage、任务增量单回合
Project memory file项目约束和可复用知识随 Workspace 持久化
Agent local memory某个 Agent 的长期经验随 Agent identity 持久化
Skill可触发、可分发的程序化能力包独立版本与安装
MCP configAgent 可连接的工具服务Session/Execution 配置

Cumora 刻意把 standing prompt 通过 Claude system-prompt file、Codex developerInstructions 或 ACP _meta 只注入一次,把每回合输入保持为增量;cc-connect 则针对不同 Agent 暴露 Memory/Skill/SystemPromptSupporter;Multica 把大多数运行 brief 写进工作目录的 CLAUDE.md、AGENTS.md 等文件,只为不能从磁盘读取的 Provider 使用 inline system prompt。

可抄的结论是:Adapter 不只是“转发用户 Prompt”,还必须决定稳定规则通过哪个原生载体进入 Agent,并避免每回合重复塞入导致上下文膨胀。

22. 错误必须分类,否则无法恢复

**设计建议:**至少区分:

Error category例子默认处理
binary_unavailableCLI 未安装、路径失效Runtime offline,不领取新工作
auth_required未登录、凭据撤销暂停并提示节点所有者
rate_limitedProvider 限流保留输入,退避重试
transport_loststdio/WS 中断判断请求是否已发送,再恢复
protocol_incompatibleschema/方法/版本不支持降级能力或拒绝 Runtime
permission_denied用户拒绝工具作为受控结果返回 Agent
cancelled用户停止保留 Session,终结当前执行
resume_rejectedSession 不存在或历史不可重放retire 旧绑定,fresh fallback
context_exhausted超出上下文窗口compact 或新 Session + 摘要
workspace_unavailable工作目录消失/被占用等待、重新准备或人工处理
agent_failedRuntime 内部错误保存证据,按幂等策略重试
result_delivery_unknown终态提交响应丢失查询 execution 后再决定重发

只有错误分类稳定,控制面才能把“重试同一次”“新建一次执行”“换节点”“换 Session”“要求人介入”分开。

23. 自建 Adapter SDK 蓝图

自建 BYOA 的三个深模块:Control Plane Core、Node Gateway 和 Adapter SDK;Conformance Suite 作为跨模块底座,外部 Agent 只通过 Adapter SDK 接入。

信息图 G|适合“抄作业”的不是某个产品的表名,而是三个深模块及其稳定契约:核心稳定,边缘可替换。

下面不是照抄任何项目,而是综合三者后的最小接口建议:

interface RuntimeAdapter {
  descriptor(): AdapterDescriptor
  probe(ctx: ProbeContext): Promise<ProbeResult>
  open(ctx: OpenContext): Promise<RuntimeSession>
}
 
interface RuntimeSession {
  readonly nativeSessionId?: string
  readonly capabilities: SessionCapabilities
 
  send(input: PromptInput): Promise<TurnHandle>
  respondPermission?(requestId: string, decision: PermissionDecision): Promise<void>
  steer?(input: PromptInput): Promise<SteerResult>
  cancel?(turnId: string): Promise<void>
  close(): Promise<void>
}
 
interface TurnHandle {
  events: AsyncIterable<RuntimeEvent>
  result: Promise<TurnResult>
}

OpenContext 至少包含:

  • adapter/runtime version;
  • cwd 和允许的额外目录;
  • resume binding;
  • model/reasoning/mode;
  • standing prompt version;
  • MCP、Skills 和环境变量引用;
  • credential scope;
  • timeout 与 cancellation signal;
  • execution identity 和日志脱敏策略。

AdapterDescriptor 应公开能力,而不是让上层通过 Agent 名称猜:

type SessionCapabilities = {
  persistentProcess: boolean
  resume: 'none' | 'id' | 'load-with-replay' | 'resume-without-replay'
  structuredEvents: boolean
  permissions: boolean
  cancelTurn: boolean
  steer: boolean
  mcp: Array<'stdio' | 'http' | 'sse'>
  images: boolean
  files: boolean
  usage: boolean
  modelSwitch: 'none' | 'next-session' | 'live'
}

24. 新增一种 Agent 的正确步骤

  1. 识别原生控制面。 优先找 app-server、ACP、JSON-RPC 或稳定 stream-json,不要先解析人类可读终端文本。
  2. 固定版本与样本。 保存 help/version、正常事件、工具事件、失败事件、Resume 和 Cancel fixture。
  3. 实现 probe。 不只检测 binary 存在,还要验证版本、登录和真实 Wake path。
  4. 实现新建会话。 明确 cwd、standing prompt、MCP 和权限模式。
  5. 实现统一事件。 保留 call ID、Session ID、stop reason 和 usage;未知事件可观测但不能让主流程崩溃。
  6. 实现 Resume。 验证 Session 所属 workspace/provider;区分拒绝、暂时故障和不支持。
  7. 实现取消与进程监管。 处理子进程树、stdin close、graceful wait 和 force kill。
  8. 实现安全边界。 参数顺序、shell escaping、env 脱敏、stdout 协议纯净、附件路径 traversal。
  9. 编写 Conformance Tests。 同一套测试跑所有 Adapter,能力缺失按 descriptor 跳过,不伪造通过。
  10. 再接入控制面。 Adapter 本身可靠后,才注册成可调度 Runtime。

25. Conformance Test 最小清单

Test验证点
probe_missing_binary不可执行时不会注册在线 Runtime
new_session_emits_idSession ID 可尽早观察并保存
multi_turn_continuity第二回合能引用第一回合上下文
resume_after_process_restart杀掉进程后能用 binding 继续
resume_wrong_workspace_rejected不跨项目串会话
resume_rejected_fallback明确告知连续性丢失,不静默重开
permission_roundtrip请求、用户决策、Agent 继续完整闭环
cancel_keeps_session当前回合停止但下一回合可继续
close_reaps_process_tree长期进程和子进程不泄漏
unknown_event_forward_compatible新字段/未知事件不会击穿 Adapter
stdout_protocol_purityJSON-RPC/stream-json 不被日志污染
redactionToken、env、Prompt 敏感字段不进入普通日志
attachment_path_safety文件名不能目录穿越或覆盖旧文件
result_exactly_once_to_caller内部重复终态不会产生多个 Result
transport_uncertainty已发送但响应丢失时不会立即重复副作用

结语:真正可复用的是边界,不是产品名词

BYOA 看起来像“把本地 Agent 接到云端 UI”,但工程上更接近一次系统分层:

  • 控制面从“调用模型”升级为“持久协调外部计算”;
  • Daemon 从“后台进程”升级为“可注册、可撤销、可对账的执行节点”;
  • Adapter 从“拼命令行参数”升级为“Runtime 语义兼容层”;
  • ACP 从“又一个 Agent 协议”变成 Adapter 层最有潜力的公共边界。

最值得抄的并不是 Cumora 的 Computer、Multica 的 Runtime,或 cc-connect 的 Session 名称,而是它们反复证明的六个不变量:

  1. 意图先持久化,通知只负责加速;
  2. 执行节点与 Agent Runtime 分离;
  3. 执行权必须原子取得;
  4. Session 必须绑定 Workspace 和身份;
  5. Adapter 能力必须显式协商与降级;
  6. 失败、取消、Resume 拒绝和未知投递必须有不同恢复语义。

如果后续要自己实现,建议按下面的最小路径推进:

阶段 1:单机 Daemon + 一个 Agent + 结构化事件
阶段 2:持久输入 + execution identity + 重连 poll
阶段 3:native session/workdir binding + restart resume
阶段 4:permission/cancel + error taxonomy + idempotent result
阶段 5:Runtime registry + capability negotiation + 多节点调度
阶段 6:ACP Adapter + 自定义 Adapter SDK + conformance suite

这条路径先建立最小完整闭环,再逐步增加协议和调度深度。不要一开始复制某个产品完整的业务对象,也不要把“成功启动 CLI”当成 BYOA 已经完成。

附录:主流 Agent 在现有控制面中的实际对接方式

下面不是根据各 Agent 官网功能页拼出的“理论能力表”,而是回到本文三个主要控制面样本的固定源码:cc-connect 3727b74、Cumora 12d19ad、Multica fbbe697。表中的“已接入控制面”只在仓库中存在具体 Adapter/Backend 时才计入;“会话与控制语义”描述的是这些 Adapter 已经实现的路径,不代表 Agent 的全部原生能力,也不等于真实账号下已经完成生产 E2E。

Agent已接入控制面与源码Adapter 实际使用的原生入口会话与恢复权限、取消与事件抄作业时的判断
Claude Codecc-connect・Cumora・Multica--input-format stream-json + --output-format stream-json 的 stdin/stdout 长期或单次进程;三者都解析结构化消息、工具和 Result。从事件提取原生 session_id,后续以 --resume 恢复;控制面还要同时钉住 cwd、Agent home 和 Provider 身份。cc-connect/Cumora 保留长期会话;cc-connect 通过 stdio permission tool 做审批往返;Multica 处理 control_request、ResumeRejected,并对整棵进程组做取消清理。结构化 CLI Adapter 的首选模板。 不要只解析最终文本;应保留 session、usage、tool call、permission 和终态。
OpenAI Codexcc-connect app-server・Cumora・Multicacodex app-server / --listen stdio://,newline JSON-RPC;cc-connect 与 Cumora 还保留 codex exec one-shot fallback。thread/start 创建 Thread,thread/resume 恢复,turn/start 执行一回合;Thread ID 是原生恢复指针。Item/Turn notification 映射成统一事件;审批和 requestUserInput 是 server-to-client request;取消必须同时处理当前 Turn、stdin 和 app-server 进程树。深 Adapter 的最佳样本。 适合直接学习双向 RPC、pending request map、审批回调、Resume 拒绝和优雅关闭。
Cursor Agentcc-connect・Cumora・Multica每回合启动 cursor-agent -p --output-format stream-json;固定快照中不是长期 Agent 进程。从 init/result 事件保存 chat/session ID;下一回合通过 --resume 启动新进程继续。cc-connect 保持 stdin 打开以响应 interaction_query;Cumora/Multica 的无人值守路径使用 force/trust 类策略;取消主要依赖终止当次进程。One-shot + Resume 的代表。 逻辑 Session 可以跨多个 OS 进程,但必须在 Result 前尽早保存原生 ID。
Gemini CLIcc-connect每回合启动 gemini -p - --output-format stream-json,Prompt 从 stdin 输入,Adapter 解析 init/message/tool/result/error。从事件提取 Session ID,后续使用 --resume;会话连续性依赖同一项目目录仍能访问本地记录。权限通过 -y / --approval-mode 启动策略决定;该 Adapter 没有独立的交互式 permission response;取消是 turn context + 子进程终止。中等深度 Adapter。 接入容易,但审批只能策略化,不能伪装成已有双向权限协议。
GitHub Copilot CLIcc-connect・Multicacc-connect 使用 --headless --stdio 的 Content-Length framed JSON-RPC 长期进程;Multica 使用 -p --output-format json 的 JSONL one-shot。前者调用 session.resume;后者把 --resume <id> 传给每次运行。cc-connect 映射 permission.request / permission.requested 并回传决策;Multica 走 --allow-all --no-ask-user,取消当次进程。同一 Agent 两种接法的典型。 需要交互审批和多轮控制时选 JSON-RPC;批处理才选 JSONL one-shot。
OpenCodecc-connect・Multicaopencode run --format json,每个 JSON line 表示 text、tool、step、error 或终态。Adapter 保存 sessionID,下一回合传 --session;Multica 明确标记当前无法可靠区分 Resume 拒绝与一般启动失败。两个实现都在无人值守路径使用跳过权限提示的策略;取消需要清理 OpenCode 及其工具子进程。事件流可用,但恢复错误语义偏弱。 上层要把“未知是否 Resume 失败”与明确拒绝区分开。
OpenClawMultica Backend・Provider Matrix每回合启动 openclaw agent --json --session-id ... --message ...;默认追加 --local,Gateway 模式则省略它并连接预配置 Gateway。模型不是运行时 --model,而是先绑定到 OpenClaw Agent,再由 Multica 传 --agent <id> 选择。新会话由 Daemon 生成 multica-<timestamp> ID,后续复用为 --session-id;状态仍在 OpenClaw 的本地 state dir / Gateway 一侧,单独复制 ID 不等于可迁移恢复。JSON/NDJSON 从 stdout 解析,stderr 只作日志;该 Backend 没有客户端权限问答,取消靠 context 与进程监管。Multica 在运行前合成 MCP/工作区配置,Skill 放在任务目录 skills/;还以 2026.5.5 为最低版本,防止旧版 JSON 输出落到 stderr。“one-shot 外壳 + 可切换远端 Gateway”的代表。 值得抄的是版本门禁、配置隔离、local/remote 路由和“终态已到但子进程不退出”时以协议结果为边界。
Hermes AgentMultica Backend・Provider Matrix启动长期 hermes acp 子进程,以 newline JSON-RPC 依次执行 initialize、Session 建立、配置和 session/prompt;Prompt/Update/Permission 都走 ACP。新建调用 session/new,恢复调用 Hermes 实现的 session/resume。Hermes 在找不到旧 Session 时可能静默创建新会话,因此 Adapter 比较请求 ID 与响应 provenance:不一致时改用实际 ID,并给 Prompt 加连续性提示;若随后无活动地拒绝回合或明确 session-not-found,才清空 ID 并返回 ResumeRejected。MCP 从 Multica 的 canonical 配置转换为 ACP mcpServers,再按握手 capability 过滤;session/request_permission 只能选择 Agent 实际提供的安全选项。绑定 Skill 时才创建单次 HERMES_HOME,隔离 Skill 与会话数据;未绑定时保留用户原 profile。“标准协议仍需要 Provider 专属语义补丁”的代表。 ACP 统一消息形状,却没有替你解决 silent resume fallback、profile 选择、MCP transport 兼容和 headless 权限政策。
Kimi CLIcc-connect・Multicacc-connect 兼容 Kimi 的 stream-json print 模式;Multica 直接启动 kimi acp,通过 ACP JSON-RPC 控制。CLI 路径解析 session.resume_hint 或 stderr 中的恢复提示,再用 --resume / -r;Multica 的 ACP 路径新建时调用 session/new,恢复时调用 Kimi 扩展的 session/resume,并校正返回的 Session ID。CLI 路径把权限交给 auto/AFK 策略;ACP 路径可以统一 Prompt、Update、Permission、Cancel,但实际 capability 必须握手发现。优先抄 ACP 路径。 兼容旧版 CLI 时再保留 stream-json Adapter,并通过 probe 选择参数方言;不要假设所有 ACP Agent 都使用同一个恢复方法。
Qwen CodeMulticaqwen -p <prompt> --output-format stream-json;解析 system、assistant、user、result 和 error 事件。使用 --resume;Adapter 对“无此会话”等错误做 ResumeRejected 分类,失败后由 Daemon 决定是否新建重试。MCP 配置写入权限受限的临时文件后传给 CLI;取消依赖 context 与进程监管。适合复用 Claude 风格的 stream-json 骨架, 但事件 schema 和错误文本必须独立建 fixture,不能假设完全兼容。
Picc-connect・Multicacc-connect 同时实现每回合启动的 pi --mode json -p <prompt> 与常驻 pi --mode rpc JSONL;Multica 使用 pi -p --mode json --session <path>,并把 Prompt 送入 stdin,避免 shell 重新分词。cc-connect RPC 启动后主动发 get_state 才取得 Session ID,恢复传 --session-id;Multica 则把本地 Session 文件路径直接当 opaque Session ID,先创建文件再以 --session <path> 追加事件。因此后者强绑定原电脑、路径和文件生命周期。RPC 可跨多个 Send 复用同一进程,并双向处理 extension_ui_request/response 的 confirm、input、select;当前 cc-connect Pi Adapter 没有单独暴露 turn-level abort,关闭会话仍取消 context 并结束进程。Multica 不注入 MCP,Skill 使用 .pi/skills/,但保留 Pi 完整工具注册表而不强塞 --tools allowlist。Pi 同时给出了浅、深两种 Adapter。 只需批处理可用 JSON mode;要远程审批与真正常驻会话应选 RPC,但还需自行补齐 turn-level cancel。无论哪种,都要把 Session 文件、工作目录和扩展安装视为节点状态。
Antigravitycc-connect・Multicaagy -p one-shot;stdout 不稳定时,Adapter 还需读取本地 conversation transcript 和日志。从日志的 conversation=<uuid> 提取 ID,后续传 --conversation;进程在写出 ID 前失败时无法可靠判断恢复拒绝。cc-connect 额外实现本地 permission bridge;Multica 使用自动放行并从 transcript 补最终输出。反例价值很高。 当原生结构化协议不足时,Adapter 会被迫理解磁盘布局和日志格式,维护成本显著上升。
Qoder CLIcc-connect・Multicacc-connect 使用 qodercli -p ... -f stream-json -q;Multica 使用 <binary> --yolo --acp 的 ACP stdio。CLI 路径用 -r <sessionID>;ACP 路径用 session/load / session/new,并区分 session-not-found 与暂时连接故障。CLI 路径主要靠启动权限模式;ACP 路径映射 session/update、usage、stop reason 和取消。又一个“能用 CLI,但 ACP 更深”的样本。 新实现优先复用通用 ACP client。
Kiro CLIMulticakiro-cli acp --trust-all-tools,ACP over stdio。initialize 后按 capability 使用 session/load 或 session/new;恢复时保留/校正 Session ID。ACP Update、usage、stop reason、MCP capability 和取消进入统一 Backend;权限在该无人值守实现中被 trust-all 策略折叠。标准 ACP Backend 的直接模板。 需要把 capability filtering 做对,不能给只支持 stdio 的 Agent 下发 HTTP/SSE MCP。
Grok BuildCumora・Multicagrok agent --always-approve stdio 的 ACP 长期子进程;Cumora 无法使用持久 ACP 时退回 grok -p。initialize → authenticate → session/load / session/new;Session ID 在首回合尽早落盘。通过 ACP 接收 tool/text/update、usage 和 stop reason;同回合 steering 在 Cumora 的该 stdio 实现中不支持,只能等下一次 wake。展示了 ACP 之外仍需处理认证和产品限制。 标准协议统一方法名,不会自动统一能力集合。
Trae CLIMulticatraecli acp serve --yolo,ACP stdio。支持 session/load 与 session/new,并从响应钉住会话。ACP 事件、取消、usage 和 MCP capability 归一;权限在 daemon 路径中采用 yolo。可直接复用 Kiro/Qoder 类 ACP 骨架, 差异主要留在启动参数、能力握手和错误分类。
CodeBuddyMulticaClaude Code fork 风格的双向 stream-json,启动时强制结构化输入输出。事件返回 Session ID,后续以 --resume 继续。解析 control request、tool、usage 和 Result;即使 bypassPermissions,AskUserQuestion 等仍需要 permission bridge 回答。说明“协议族”比产品名更适合作为 Adapter 抽象。 可复用 Claude 解析框架,但必须有独立兼容测试。
其他 ACP Agentcc-connect 通用 ACP Adapter控制面只需要配置可执行命令与参数,Adapter 统一完成 ACP stdio JSON-RPC。initialize → 可选 authenticate → session/load 或 session/new → session/prompt。session/update 映射统一事件;session/request_permission 做审批往返;session/cancel 与进程关闭分离。新增 Agent 的首选逃生舱。 只要 Runtime 真正兼容 ACP,就先走通用 Adapter,不应立即复制一份 Provider 专属解析器。

这张表还暴露出一个很实用的实现顺序:先检查 Agent 是否提供 ACP、app-server 或 RPC;其次选择稳定的 stream-json/JSONL;最后才考虑 transcript、日志或人类可读文本。现有控制面的源码并没有证明所有 Agent 可以被同一种协议完全控制,反而证明了 Adapter SDK 应按协议族复用骨架,再把启动参数、能力与错误分类留给 Agent descriptor。

OpenClaw、Hermes 与 Pi 的补充尤其能说明:判断一个 Agent “能否 BYOA”不能只看它是否有 CLI。OpenClaw 的控制深度取决于结构化输出、会话 ID 和 local/Gateway 路由;Hermes 的 ACP 仍需要控制面修补 Resume 与 profile 隔离;Pi 则直接用 JSON 与 RPC 两种模式展示了“可调用”和“可双向控制”的差距。对自建 Adapter SDK 来说,应该把 协议模式、会话载体、权限往返、执行位置和运行时目录策略 都声明成独立 capability,而不是用一个 supports_byoa=true 概括。

相关知识

  • Agent 控制面
  • Agent 业务状态与执行状态分离
  • 多 Agent 协作协议
  • Agent 产品的控制面与入口竞争
  • Cumora 专题报告
  • Multica 专题报告

来源与边界

  • 完整来源:Source Manifest
  • 生成配图:BYOA 深入报告 ImageGen Manifest
  • 写作 Spec:Research Spec
  • 本文所有 Mermaid 与 ImageGen 架构图、时序图和信息图均为基于公开源码与文档的分析重建,不是厂商官方架构图、产品截图或线上观测结果。
  • 本次没有使用真实 Provider 账户执行跨网络、重启和审批 E2E;因此报告说明代码机制,不声称生产 SLA、性能或安全性已经独立验证。