协议规范速查
线协议: 0.4 · 文档修订: 1.10 · 状态: 稳定
完整规范维护在仓库的 spec/protocol.zh.md。本页是快速查阅版。
Profile YAML
command: python3 # argv[0] — 始终单个 token,永不切分
args: ["{{PROFILE_DIR}}/my_agent.py"] # argv[1..];省略时默认为 []
# {{PROFILE_DIR}} = profile 所在目录
# cwd 可选。省略时默认为 bridge 进程的 cwd。
# 若为相对路径,相对 {{PROFILE_DIR}}(profile 所在目录)解析。
cwd: /path/to/workspace
env:
MY_API_KEY: "${MY_API_KEY}"
env_allowlist: [MY_API_KEY] # 可选:限制 ${VAR} 展开
timeout_secs: 600 # 每轮挂钟超时(秒),默认 1800
budget_secs: 900 # 可选:每轮挂钟时间预算(秒);无默认值
deadline: "2025-01-01T12:00:00+00:00" # 可选:ISO-8601 绝对轮次截止时刻(须带显式时区偏移)
kill_grace_secs: 5 # SIGTERM → SIGKILL 宽限期,默认 5
streaming: true # 实时转发 {"type":"partial"} 事件
permission: false # 可选工具授权(保持 stdin 打开;见完整规范)timeout_secs 是每轮默认的挂钟限额。另有两个可选字段可进一步收紧:budget_secs(由 bridge 测量的每轮时间预算)与 deadline(带显式时区偏移的 ISO-8601 绝对时刻——naive 值属 profile 校验错误)。三者中最早到期者生效,且到期遵循与普通超时相同的 kill 序列与退出码。三个字段都是 bridge 侧 profile 字段,均不出现在线格式中。通过 executor 在进程内运行 agent 的 bridge 不要求支持 budget_secs / deadline。
占位符不经 shell 替换。argv 由两个字段构成:
command— argv[0]。单个 token,永不切分,即使含空格。args— argv[1..] 的 YAML 列表。省略时默认为[]。
最终 argv([command, *args])直接传给 execve,允许 command 携带含空格的路径:
command: "/path with spaces/my agent"
args: [](0.2 曾有「args 缺省 + command 含空格 = 切分 command」的简写。0.3 移除——command 始终是单个 token。)
输入 — stdin turn 对象
在 agent 读取 stdin 第一个字节之前,bridge 写入恰好一行 NDJSON:turn 对象。
{"type":"turn","message":"hello","session_id":"","session_name":"default",
"attachments":[],"permission":false,"protocol_version":"0.4"}必填字段
| 字段 | 说明 |
|---|---|
type | 字面量 "turn"。 |
message | 用户消息文本。可为 ""(见完整规范「空 turn」)。 |
session_id | 上一轮的 session ID("" = 新会话)。 |
protocol_version | 协议版本字符串,例如 "0.4"。不透明且不可比较——agent MUST NOT 对它排序或范围检查。 |
可选字段(出现即相关)
| 字段 | 说明 |
|---|---|
session_name | 人类可读的会话名(默认 "default")。 |
from_user | 平台侧发送者标识。平台已知发送者时 MAY 携带;缺省或 "" = 未知。不需要的 agent MUST 忽略它。 |
attachments | {kind, url, ...} 数组(如 {"kind":"image","url":"https://..."})。唯一的附件通道——没有单附件便捷变量。缺省/[] = 无。 |
permission | profile 开启 permission: true 时为 true;否则缺省/false。 |
密钥和 per-CLI 配置走环境变量(profile 的 env 块),不走 turn 对象。0.4 中单轮请求不走环境变量。
stdin / EOF
- 当
permission缺省/false 时,bridge 写入 turn 行后关闭 stdin(EOF)。agent 读完 turn 后 MUST NOT 再阻塞等待 stdin。 - 当
permission: true时,bridge 保持 stdin 打开,以接收轮中的{"type":"permission_response"}行(见完整规范)。
输出 — stdout NDJSON 事件
stdout 每一行都是一个以 \n 结尾的 JSON 对象,带 type 字段。词汇表是封闭的:partial、result、error,以及(开启权限时的)permission_request。
{"type":"partial","text":"..."} ← 流式分块;streaming: true 时转发
{"type":"result","text":"..."} ← 终端回复正文(至多一条);可选 usage
{"type":"error","message":"..."} ← 用户可读错误;任意模式;使本轮失败
{"type":"permission_request",...} ← 可选工具授权(permission: true)这些事件上都可以带可选的 session_id。0.4 没有 {"type":"session"} 或 {"type":"text"} 事件。
不是合法 JSON、不是对象、或 type 不被识别的行会被忽略(记到 stderr)——不作为回复正文。回复正文由 result(以及流式时已转发的 partial)承载。
事件上的 session_id — 第一个非空值
会话连续性是事件上的字段,不是独立事件类型。
- bridge 持久化本轮观察到的第一个非空
session_id。 - 早期事件 MAY 省略该字段;一旦已知,agent SHOULD 在后续事件上带上。
- 无状态 agent 完全省略
session_id(且 MUST NOT 铸造底层工具无法 resume 的 id)。 - 之后出现不同的非空值是违规(保留第一个;警告)。
{"type":"partial","text":"回答中..."}
{"type":"partial","text":"答案","session_id":"cli-sess-9f3a2c1e-4b8d-4a2f-b6c1-2e8d4f5a7b9c"}
{"type":"result","text":"","session_id":"cli-sess-9f3a2c1e-4b8d-4a2f-b6c1-2e8d4f5a7b9c"}若同一轮既有 session_id 又有 error,bridge MUST 仍持久化 session id 供下一轮使用,即使本轮被报告为失败。
Partial 事件
流式分块。streaming: true 时立即转发;否则 runner 忽略。可选 role("output" | "thinking")区分助手输出与推理。
{"type":"partial","text":"这是第一部分。"}
{"type":"partial","role":"thinking","text":"让我想想..."}Result 事件 — 终端回复正文
每轮至多一条 result。可选 usage 用于 token/费用统计。若流式已通过 partial 送达正文,result.text MAY 为 ""。
{"type":"result","text":"这是完整答案。","session_id":"cli-sess-…"}Error 事件
用户可读错误。无论 streaming 模式如何都生效。bridge 转发给用户、抑制后续 partial,且即使退出码为 0 也 MUST 将本轮视为失败。error 之后的 result 会被丢弃。
{"type":"error","message":"上游 API 限流。请 60 秒后重试。"}可选工具权限
通过 profile permission: true 开启。不是通用 HIL——仅工具授权。在 in-process executor 路径上,只有声明了审批通道的 executor(今仅 claude-code)才接受 permission: true;其余 executor 一律拒绝运行——error 事件 + 非零退出码,永不静默回落到 --dangerously-skip-permissions / --yolo。未请求权限的 profile 仍带这些自动批准 flag;AGENTPROC_AUTO_APPROVE=0 / false 会让它们也变成同样的拒绝。
{"type":"permission_request","request_id":"1","tool_name":"Bash","input":{"command":"echo ok > f.txt"}}用户批准后 bridge 写入 stdin:
{"type":"permission_response","request_id":"1","behavior":"allow"}stdin 保持打开规则、超时与字段定义见完整规范。
In-process executor 的 argv
在 spawn 路径上消息只走 stdin,永不进 argv。in-process executor 驱动的是第三方 CLI,因此只有当该 CLI 能从 stdin 读 prompt 时才能遵守此规则:这时 executor 必须让消息留在 argv 之外,并通过可选的 buildInitialStdin(message, sessionId) 钩子返回 CLI 的 stdin 载荷,由 runner 写入 CLI 的 stdin。claude-code 的两种模式都这样做(claude --print --input-format stream-json,一行 stream-json 用户消息走 stdin)。
只接受把 prompt 作为命令行参数传入的 CLI 属于明文豁免:buildArgs 可以把消息放进目标 CLI 的 argv。该 argv 可被同用户的任意进程经 ps(1) 读取,受 OS 参数长度上限约束(Linux ≈128 KiB,Windows ~32 K 字符),且当消息以 - 开头时可能被误当作 flag。当消息是位置参数时,executor 必须在它前面紧邻放置 --,并把所有 flag 放在 -- 之前。这类 executor 请跑在独占宿主、容器或 VM 上。细节见完整规范。
退出码
| 码 | 含义 |
|---|---|
0 | 成功 |
1 | 通用 agent 错误 |
124 | 超时(bridge 施加;与 GNU timeout 一致) |
130 | 被 SIGINT 中断 |
143 | 被 SIGTERM 终止 |
多种失败信号同时出现时的优先级:超时 (124) > error 事件 (1) > 进程退出码。
进程非零退出且未发出 error 事件时,bridge SHOULD 向用户发送通用错误消息。
超时处理
到达该轮的有效限额时——timeout_secs,或 budget_secs / deadline 中最早到期者:
- bridge 向 agent 的整个进程组发送
SIGTERM。 - bridge 等待
kill_grace_secs(默认 5)让进程退出。 - 若仍在运行,bridge 向同一进程组发送
SIGKILL。
已收到的 partial 事件仍会转发。agent SHOULD 在收到 SIGTERM 时冲刷缓冲的 partial 并尽快退出。
杀进程树很关键:agent 会派生子进程(shell 工具调用、构建、sub-agent),它们继承 agent 的凭据与 workspace 写权限,只对直接子进程发信号会让它们继续运行且无人回收。同一套组信号也适用于协作式取消、Ctrl-C,以及先于 bridge 自身限额到期的 bridge 内部 CLI 超时;CLI 拥有自己进程组的 bridge 必须把收到的终止信号转发给该组。Windows 无进程组,退化为终止直接进程。
直接子进程退出后,bridge MUST NOT 无界等待 agent 继承来的 stdio:那些管道只有在每一个持有者都消失后才到 EOF,存活的助手进程会让该轮永远挂起。bridge 只 drain 一个短暂有界的窗口(参考 runner 取 1 秒),随后放弃管道并以已收到的输出结束该轮。
可观测性
bridge MAY 记录 bridge 侧计时与事件日志(opt-in、默认关闭、只写文件——绝不写 stdout;关闭时 stdout/stderr 字节一致):每条事件的 seq + 毫秒精度 ISO-8601 UTC ts,以及运行结果携带 bridge 实测的 started_at/duration(区别于 agent 自报的 usage.duration_ms)。详见完整规范的「事件可追溯性(可选)」。
设计原则
- 进程边界是唯一契约。 任何从 stdin 读 turn、向 stdout 写 NDJSON 事件的进程都是合法 agent。
- Agent 不感知平台。 投递、限流等平台事务是 bridge 的职责。
- Session ID 不透明。 bridge 存储并转发;含义由 agent 拥有。
- 每进程一轮。 长会话与轮中取消按设计不在范围内。
type:不属于本规范。 内置快捷方式是平台扩展,不是 P0。
设计动机以及与 MCP、ACP、NDJSON、SSE、LSP、Unix filter 的对比,见 GitHub 上的完整规范。