在终端里用 Claude Code 或 Codex 时,插话是很自然的事。它在跑测试,我们看到它跑的是一组用不着的集成测试,就直接敲一句“先别跑集成测试,只修单元测试”,回车。Claude Code 会把这句话放进队列,等手头的工具调用结束后读到;Codex 也能把它 steer 进正在进行的这一轮。
可一旦想让脚本、定时任务或者另一个 agent 来驱动它们,事情就变了。发起还好办,两家都有非交互模式:
claude -p "修复 tests/ 下失败的测试" --output-format stream-json --verbosecodex exec --json "修复 tests/ 下失败的测试"我们希望插话也是一条命令:
harnessctl send fix-tests.jsonl "修复 tests/ 下失败的测试" --cwd ~/repoharnessctl send fix-tests.jsonl "先别跑集成测试,只修单元测试"两条命令一模一样,也都很快返回。agent 说了什么、做了什么,这句话归到了哪一次交互,要不要批准某条命令,全都写进 fix-tests.jsonl。把 Codex 的适配器换成 Claude Code 的,命令一个字不改。要做到这一点,得先回答几个问题:
- 发起一次推理,两家的差别有多大?
- 为什么发起能用一条命令完成,插话却不行?
- 两家各自的插话机制差在哪里,能不能收进同一套语义?
- 市面上有没有现成的协议已经做了这件事?
- 如果没有,这个协议该长什么样,适配器能不能只是一包脚本?
本文顺着这几个问题一层一层往下讲,最后给出我们设计的协议 harnessctl,并在本机验证它的 Codex 适配器。
一、会话的发起
现象
上面两条非交互命令长得差不多:给一段提示词,拿回一串 JSON 事件,最后一条是结果。要接着聊,两家也都有续上的办法12:
# Claude Code:从 JSON 结果里取 session_id,再用 --resume 续上sid=$(claude -p "开始审查这次改动" --output-format json | jq -r '.session_id')claude -p "继续,重点看数据库查询" --resume "$sid"
# Codex:事件流的第一行是 {"type":"thread.started","thread_id":"0199a213-…"}codex exec --json "开始审查这次改动"codex exec resume 0199a213-… "继续,重点看数据库查询"分析:一次性进程,加上磁盘上的会话
这两种用法背后是同一个模型:一个进程只跑一轮。进程启动,读入提示词,跑完这一轮(中间可能有几十次工具调用),把结果写到 stdout,然后退出。会话不在进程里,而是以对话记录的形式存在磁盘上,下一次 --resume 或 exec resume 时再读回来。
所以“发起”和“续上”很容易统一。把本地 Agent 变成平台上的 Bot 里提到过,Multica 为 26 种 agent CLI 做适配时,把差异收敛到一个只有一个方法的接口里3:
type Backend interface { Execute(ctx context.Context, prompt string, opts ExecOptions) (*Session, error)}要续上的会话 ID 放进 ExecOptions 就行。
可这个接口里没有“插话”。一次性进程跑起来以后,还有谁能跟它说话?
二、运行中会话的寻址
现象
最直觉的做法是再 resume 一次:第一条 claude -p 还在跑,另开一个终端执行 claude -p --resume "$sid" "先别跑集成测试"。
这句话插不进去。第二个进程从磁盘上读到的,是会话此刻保存下来的样子;它和第一个进程之间没有任何通道,第一个进程正在跑的那一轮根本不知道有这句话。结果是同一段对话被两个进程各自往下写。(Claude Code 新近的后台会话对这种情况做了特殊处理,第四节再讲。)
原因
会话有 ID,但正在运行的那一轮没有地址。插话的本质,是把一条消息送进一个还活着的进程。命令行参数在进程启动时就读完了,之后能进去的只有 stdin 这一条管道,而这条管道握在启动它的那个脚本手里。
两家其实都留了长连接的入口。Claude Code 加上 --input-format stream-json 之后,claude -p 不再读一次就结束,而是持续从 stdin 读 JSON 消息;Codex 的 app-server 是一个常驻进程,在 stdio 上跑 JSON-RPC。问题只在于,怎么让另一条命令也用上这条连接。
第一次尝试:用命名管道留住 stdin
对 Claude Code,可以把 stdin 接到一个命名管道上,谁想插话,就往管道里写一行:
mkfifo /tmp/fix.intail -f /tmp/fix.in | claude -p --input-format stream-json \ --output-format stream-json --verbose > /tmp/fix.out &
echo '{"type":"user","message":{"role":"user","content":"修复失败的测试"},"parent_tool_use_id":null}' > /tmp/fix.in# 过一会儿,另一个脚本:echo '{"type":"user","message":{"role":"user","content":"先别跑集成测试"},"parent_tool_use_id":null}' > /tmp/fix.in看起来已经是“一条命令插话”了,但很快会碰到三个问题:
- 没有回执。
echo只要把字节写进管道就返回。这句话是插进了当前这一轮,还是排到了下一轮,甚至claude是不是早就退出了,写入方都不知道。 - 输出没人整理。 所有原始输出都流进
/tmp/fix.out,想知道“我刚才那句话之后发生了什么”,只能自己去翻。 - Codex 用不了。 JSON-RPC 是一问一答。
turn/steer必须带上当前这一轮的expectedTurnId,这个 ID 要从 stdout 上读出来;Codex 请求审批时,又是服务器发给客户端的一个请求,必须有人回应。单向的管道做不了这些。
解决:让事件文件充当会话的地址
回头看这三个问题,它们指向同一件事:调用方和 agent 之间,缺一个双方都找得到、又不随进程消失的东西。
最简单的这种东西就是一个文件。于是协议这样约定:调用方给出一个文件路径,对这个会话的每条命令都带上同一个路径;agent 的一切输出,包括回答、工具进度、输入的归属、要不要批准某条命令,都以一行 JSON 追加进这个文件。
三个问题就都有了着落。回执是文件里的一行事件;文件谁都能读,断开了也能从任意一行接着读;Codex 需要的一问一答、Claude Code 需要的一条常开的 stdin,都关在适配器里面。
那个必须一直活着的进程并没有消失,只是成了适配器的内部实现,调用方看不到,协议也不规定。第六节会看到它在实现里的样子。
会话有了地址,接下来的问题是:适配器收到一句“先别跑集成测试”,该怎么交给 agent?两家在这一步的做法相当不同。
三、插话语义的差异
Claude Code:消息上的 priority
--input-format stream-json 模式下,往 stdin 写的每一行都是一条用户消息。如果它在一轮还没结束时到达,默认的优先级是 next:Claude 在同一轮里读到它,时机是手头正在跑的工具调用结束之后;如果这一轮先结束了,它就开启下一轮4。
打断走另一条线:写一行 type 为 control_request、子类型为 interrupt 的消息,Claude Code 回一个 control_response5。
Codex:带前置条件的 turn/steer
Codex 的 app-server 把插话做成了一个独立的 JSON-RPC 方法 turn/steer6。它的 expectedTurnId 参数是必填的,schema 里的说明是:这是一个前置条件,和当前活动的那一轮对不上,请求就失败7。打断用 turn/interrupt,带上 threadId 和 turnId。
我在本机用 codex-cli 0.165.0 实测了这几种情况。没有登录,所以模型请求会失败,但轮次的开始、插话和打断都照常发生:
→ {"method":"turn/steer","id":"in_11","params":{…,"expectedTurnId":"01a19675-9014-…",…}}← {"id":"in_11","result":{"turnId":"01a19675-9014-71e2-b5f6-b6d450d616b6"}}→ {"method":"turn/steer","id":"in_12","params":{…,"expectedTurnId":"wrong-turn",…}}← {"error":{"code":-32600,"message":"expected active turn id `wrong-turn` but found `01a19675-9014-71e2-b5f6-b6d450d616b6`"},"id":"in_12"}(这一轮结束之后再 steer)← {"error":{"code":-32600,"message":"no active turn to steer"},"id":"in_6"}同一句话在两条线上
把图 2 两边对照,差别集中在两处。
回执。 Codex 当场告诉你这句话进了哪一轮;Claude Code 对这条消息不作任何直接回应。--replay-user-messages 会把收到的消息回显到 stdout,能确认“收到了”,但不说明落在哪一轮。
竞态。 插话的那一刻,这一轮恰好结束了怎么办?Claude Code 的 next 会自动顺延成下一轮;Codex 则直接报错 no active turn to steer,由客户端决定怎么办。
这两处差异,都得在协议里有个交代。动手设计之前,先看看是不是已经有人做了。
四、现有方案的调研
ACP:协议有了,插话还没进标准
最接近的是 ACP(Agent Client Protocol),Zed 牵头的编辑器与 agent 之间的协议。它基于 JSON-RPC,agent 通常作为子进程跑在 stdio 上:session/new 建会话,session/prompt 发起一轮,session/cancel 打断8。Claude Code 和 Codex 都有 ACP 适配器,codex-acp 内部启动的就是 Codex 的 app-server。
但 ACP v1 里没有往正在运行的一轮追加输入的请求。两家的适配器先各自实现了一个预标准扩展 _session/steering9,把它写进标准的提案还没有合并10。10 月底合并的 PR #2627 规定,agent 忙的时候也要接受 session/prompt,但收到后是插进当前一轮还是排到下一轮,由 agent 自己决定11。
ACP 本身也不是“一条命令”的形态,它需要一个持续的连接。
acpx:ACP 的命令行客户端
acpx 补的正是这一层。它是 ACP 的无头命令行客户端,内置了 claude、codex 等几十个 agent 的配置12:
acpx codex sessions new --name backendacpx codex -s backend "修复 API 分页的 bug"acpx codex -s backend --no-wait "顺便把测试补上"acpx codex cancel -s backend只是 --no-wait 的语义是排到下一轮,不是插进当前这一轮。文档说得很直接:“ACP has no request that adds input to a prompt that is already running”13。给 acpx 加 steer 子命令的 issue 还开着14。
审批方面,acpx 在非交互模式下会直接拒绝需要人工批准的请求15,没有事后用另一条命令批准的办法。
厂商自己的守护进程
两家厂商也各自做了后台托管,但都不是给脚本插话用的。
Claude Code 的后台会话。 claude --bg "任务" 把会话交给一个 supervisor 进程托管,claude stop <id> 停止16。对运行中的后台会话执行 claude --resume <id> "提示词",提示词会作为下一轮发过去;但只要带了管道或重定向,它就什么都不发,以状态码 1 退出17。
Codex 的常驻 app-server。 本机的 codex-cli 0.165.0 里,codex app-server 多了 daemon 和 proxy 两个子命令。一次性的命令可以经 proxy 连上常驻服务,但连上之后说的仍是完整的 JSON-RPC。
其他方案
coder/agentapi 用一个内存里的终端模拟器,把 Claude Code、Codex 等包成 HTTP 接口,靠发按键模拟插话,效果取决于各家 TUI 的行为,项目也已不再维护18。A2A 是服务之间的 agent 通信协议19,AG-UI 是 agent 和前端界面之间的事件协议20,MCP 是 agent 调用工具的方向21,都不是“驱动一个编码 agent 会话”的形态。
| 方案 | 覆盖两家 | 一条命令发起 | 一条命令打断 | 一条命令插进当前一轮 |
|---|---|---|---|---|
| ACP 协议本身 | 是,经适配器 | 否,需要常驻连接 | 适配器扩展 _session/steering,未标准化 | |
| acpx | 是 | 是 | 是,cancel | 否,--no-wait 只排到下一轮 |
| Claude Code 后台会话 | 只有 Claude Code | 是,--bg | 只有 stop,停掉整个会话 | 否,--resume 发的是下一轮,且拒绝脚本调用 |
| Codex app-server daemon | 只有 Codex | 否,要经 proxy 说完整的 JSON-RPC | 协议里有 turn/steer,但不是一条命令 | |
| coder/agentapi | 是 | 是,HTTP | 靠发按键,取决于各家 TUI;已停止维护 | |
| A2A / AG-UI / MCP | 不是驱动编码 agent 会话的形态 | |||
核实于 2026-11-01。“一条命令”指一次性的 shell 调用,不需要调用方自己维持连接。
结论是:没有现成的协议同时满足“跨 Claude Code 和 Codex、一条命令发起、一条命令在进行中插话”。最接近的是 acpx,它缺三样东西:插进当前一轮的命令、插话的回执,以及脱离终端的异步审批。
那就自己设计一个。
五、协议的设计
我们设计的这个协议叫 harnessctl,实现开源在 AgentUse 仓库22。设计时只守一条原则:调用方只说自己要什么,怎么做到都是适配器的事。 落下来就是三样东西:四条命令、一个事件文件、一个独立的适配器包。
四条命令
harnessctl send <events.jsonl> [<prompt>] [--cwd DIR] [--model MODEL]harnessctl cancel <events.jsonl>harnessctl status <events.jsonl>harnessctl reply <events.jsonl> <request_id> [<answer>]send、cancel、reply 受理后很快返回,成功时 stdout 为空,结果都看事件文件。status 输出一行 JSON,讲的是这段对话的状态(idle、running、awaiting_reply、cancelling、failed),不是进程的状态。正文可以从 stdin 传,多行提示词不用再操心 shell 转义:
printf '%s' "$prompt" | "$A" send fix-tests.jsonl --cwd ~/repo发起和插话是同一条 send,调用方不用先判断会话在不在、这一轮结束了没有。这些判断它也做不好,因为状态在它读到的下一刻就可能变了。
退出码 0 只表示已受理,不代表模型读过这句话,更不代表任务成功。命令如果超时,这句话可能已经被受理了,所以不能盲目重发,要先读事件文件。
事件文件的路径就是会话
规范化之后的绝对路径是会话的唯一身份,协议里没有另外的 session ID。第一次 send 自动创建事件文件,并把适配器、cwd、model 绑定到这个地址上。之后可以省略它们;传了不同的值就报错。想换工作区,就换一个事件文件。
send:由适配器决定投递
第三节那些差异,全部在这里消化。两个适配器收到同样的两条命令,做法不同,但都遵守同一条规则:只公布确认过的归属。
Codex 适配器在内部发 turn/steer,拿到回执后才写 accepted,这句话归入当前这次交互。如果原生回的是这一轮刚结束,就分配新的 run_id,开启下一轮23。
Claude Code 适配器没有可靠的轮次回执,忙的时候就先留在自己的队列里,这一轮结束后再提交24。代价很直接:在 Claude Code 上,send 目前做不到“工具调用结束就读到”的插话,要等这一轮结束。我们宁可晚一点,也不在文件里写一个证明不了的归属。
所以这个协议不保证一句话落在哪里,只保证事后告诉你它落在了哪里。想立刻换方向,就先 cancel 再 send。cancel 打断当前执行,清空还没处理的输入,但保留对话历史;已经发生的工具副作用撤销不了。
事件:能不能丢
事件只有五种,按能不能丢分成两层25:
| 类型 | 内容 | 可靠性 |
|---|---|---|
| accepted | 完整输入、input_id、确认的 run_id | 校验、加锁、fsync 之后,send 才返回成功 |
| message | 完整正文快照,同一 message_id 整条替换 | 关键,校验、加锁、fsync,不能靠丢日志丢弃 |
| request | 审批或提问的 pending、replied、expired 快照 | |
| result | 一个 run 的终态(completed、cancelled、failed)、input_ids、输出和计量 | |
| log | 流式片段、工具进度、原生回执、进程诊断 | 可丢、可合并、可限流,写失败不影响工作结果 |
分层的标准是:把所有 log 都删掉,业务状态能不能完整重建? 所以流式片段只能放进 log,正文一定要有一条完整的 message;致命错误一定要进 result,不能只写一条日志。原生报文也只允许出现在 log 里,换一个 agent,调用方读到的关键事件结构不变。
一句话的结局也不用自己拼。run_id 由适配器分配,result.input_ids 直接列出这次结果覆盖了哪几句话。下面三行来自本机实测,第二行就是插话,它和第一句共用一个 run_id:
{"type":"accepted","run_id":"fd923206-…","input_id":"bb5fd1b9-…","text":"修复 tests/ 下失败的测试"}{"type":"accepted","run_id":"fd923206-…","input_id":"97b7e1dd-…","text":"先别跑集成测试,只修单元测试"}{"type":"result","run_id":"fd923206-…","input_ids":["bb5fd1b9-…","97b7e1dd-…"],"status":"failed","output":"","usage":null,"cost_usd":null,"error":"unexpected status 401 Unauthorized…"}审批:也是一行事件加一条命令
agent 需要外部决定时,文件里出现一条 pending 的 request:
{"type":"request","ts":"…","run_id":"550e8400-…","request_id":"req-1","state":"pending","kind":"permission","message":"执行 npm test?"}调用方可以过一会儿再用另一条命令回答,提问类请求也一样:
"$A" reply fix-tests.jsonl req-1 denyprintf '%s' '{"q1":"Go"}' | "$A" reply fix-tests.jsonl req-2到两家原生调用的映射
| harnessctl | Claude Code · stream-json | Codex · app-server |
|---|---|---|
| send | ||
| 首次 | claude -p --input-format stream-json --output-format stream-json --verbose --replay-user-messages --permission-prompt-tool stdio,写一条用户消息 | codex app-server --listen stdio://,initialize、thread/start、turn/start |
| 回收后续上 | 同上,加 --resume <session> | thread/resume、turn/start |
| 正在忙 | 适配器内部排队,这一轮结束后再写用户消息 | turn/steer 带 expectedTurnId;这一轮刚结束就改为下一轮 |
| 其他 | ||
| cancel | control_request · interrupt | turn/interrupt;还没拿到 turn id 时先记下,拿到后再发 |
| reply · 审批 | control_response,behavior: allow / deny | JSON-RPC 响应,decision: accept / decline |
| reply · 提问 | AskUserQuestion 的 updatedInput.answers | item/tool/requestUserInput 的 answers |
status 只查适配器自己的状态,不需要映射。
协议里已经没有进程了。可第二节说过,必须有一个活着的进程握着原生连接。它去哪了?
六、参考实现:运行进程
按需启动,空闲回收
答案是每个地址一个运行进程,由 send 按需拉起。它持有原生连接、队列和待回复请求26:
命令和运行进程之间走本机 socket,但 socket 只出现在实现里,换掉它不影响任何调用方。这正是把“每个动作一条命令”作为协议边界的好处。
回收。 没有活动执行、待处理输入和待回复请求时,运行进程空闲约 60 秒后退出,下一次 send 接着原来的上下文继续。所以协议不需要“关闭”这个动作。
失联。 运行进程被 kill -9 之后,下一条命令能通过文件锁发现它没了。这时适配器把还没有结果的输入记成 failed,但不重放:它们可能已经执行了一半,再跑一遍可能重复产生副作用。
适配器还是“一包脚本”。Codex 的包打出来约 24 KB,只依赖 Linux 和 Python 3.9 以上的标准库,以及用户自己装好并登录的 codex。代价是代码量约 1100 行,大头是运行进程;目前也只支持 Linux。
实测
我们从打好的 tar.gz 解压,用本机 codex-cli 0.165.0 跑了一遍。没有登录,所以模型请求会以 401 失败,但轮次的开始、插话、打断、回收和恢复都照常发生:
$ A=./codex/harnessctl$ "$A" send fix.jsonl "修复 tests/ 下失败的测试" --cwd repo # 168 ms,含拉起运行进程$ "$A" status fix.jsonl{"state": "running", "run_id": "fd923206-…", "queued": 0, "requests": [], "cwd": "/tmp/hc-run4/repo", "model": null}$ "$A" send fix.jsonl "先别跑集成测试,只修单元测试" # 134 ms,已拿到原生回执接着试了几种边角情况:
- 换一个
--cwd再send:退出码 2,cwd is bound to this event path。 - 空闲 72 秒后:运行进程已退出。再
send,175 ms 返回,还是同一个 thread。 - 运行中
kill -9运行进程:下一次status补写一条failed的result(Runtime lost; input outcome is uncertain)。再send恢复同一个 thread,旧输入没有重放。 - 运行中
cancel:这句话得到一条cancelled的result,状态回到idle。
仓库自带的 29 个测试也全部通过,它们用一个假的后端驱动两个适配器。
还没有验证的:真实的审批路径(未登录时模型不会去执行命令,request 和 reply 只在假后端上跑过),以及真实的 Claude Code(本机没有安装,这一侧来自读代码和假后端测试)。
拿来用
git clone --depth 1 https://github.com/memory-co/agentuse.gitpython3 agentuse/harnessctl/package.py --adapter codex --output-dir ./distmkdir -p ~/.harness && tar xzf dist/harnessctl-codex-*-linux.tar.gz -C ~/.harness~/.harness/codex/harnessctl status /tmp/none.jsonl # 退出码 4:地址不存在,说明入口可用七、回到起点
回到开头那两条命令:
A=~/.harness/codex/harnessctl"$A" send fix-tests.jsonl "修复 tests/ 下失败的测试" --cwd ~/repo"$A" send fix-tests.jsonl "先别跑集成测试,只修单元测试"两条命令都在零点几秒内返回。第二句话在 Codex 上并入同一个 run_id,在 Claude Code 上排到下一个 run_id,事件文件都如实记下了。把 A 换成 Claude Code 适配器的路径,其余一个字不改。
小结
- 发起一次推理,两家差别大吗? 不大。两家都是一个进程跑一轮,会话存在磁盘上。
- 为什么插话做不到一条命令? 正在运行的那一轮没有地址,而插话要把消息送进一个活着的进程。
- 怎么给运行中的会话一个地址? 用一个事件文件的路径,一切输出都追加进这个文件。
- 两家的插话差在哪? Claude Code 没有轮次回执;Codex 当场回执,这一轮结束了就报错。
- 有现成的协议吗? 没有完全满足的。发起和打断都有人做了,一条命令插进当前一轮还没有。
- harnessctl 怎么设计? 四条命令
send、cancel、status、reply,由适配器决定投递,只公布确认过的归属;四种关键事件足够重建业务状态,log可以丢。 - 那个常驻进程去哪了? 变成每个地址一个按需启动的运行进程,空闲回收,失联时不重放。它只是实现细节,协议里没有它。
参考资料
Footnotes
-
Run Claude Code programmaticallycode.claude.com ↗
--output-format json取session_id再--resumehttps://code.claude.com/docs/en/headless ↩ -
Codex non-interactive modedevelopers.openai.com ↗
codex exec --json的事件类型,codex exec resumehttps://developers.openai.com/codex/noninteractive ↩ -
server/pkg/agent/agent.go收藏github.comhttps://github.com/multica-ai/multica/blob/main/server/pkg/agent/agent.go ↩
-
Agent SDK reference - TypeScriptcode.claude.com ↗
SDKUserMessage.priorityhttps://code.claude.com/docs/en/agent-sdk/typescript ↩ -
claude-agent-sdk-python · query.pygithub.com ↗
control_request/control_response信封,interrupt与can_use_tool两种子类型https://github.com/anthropics/claude-agent-sdk-python/blob/main/src/claude_agent_sdk/_internal/query.py ↩ -
Codex App Serverdevelopers.openai.com ↗JSON-RPC 2.0,线上省略
"jsonrpc":"2.0";turn/steer、turn/interrupthttps://developers.openai.com/codex/app-server ↩ -
codex app-server generate-json-schema 的输出本机 codex-cli 0.165.0 生成。
TurnSteerParams.expectedTurnId:“Required active turn id precondition. The request fails when it does not match the currently active turn.”;CommandExecutionApprovalDecision取值accept/decline等 ↩ -
ACP · Prompt Turnagentclientprotocol.com ↗https://agentclientprotocol.com/protocol/v1/prompt-turn ↩
-
codex-acp · Session steeringgithub.com ↗预标准扩展
_session/steering,claude-agent-acp 也实现了同名扩展https://github.com/agentclientprotocol/codex-acp/blob/main/docs/steering.md ↩ -
RFD: steer and queue prompts sent while the Agent is workinggithub.com ↗截至 2026-11-01 仍为 openhttps://github.com/agentclientprotocol/agent-client-protocol/pull/1261 ↩
-
docs(protocol): define prompts sent during active workgithub.com ↗2026-10-29 合并https://github.com/agentclientprotocol/agent-client-protocol/pull/2627 ↩
-
acpx · Promptinggithub.com ↗“ACP has no request that adds input to a prompt that is already running”https://github.com/openclaw/acpx/blob/main/docs/prompting.md ↩
-
acpx #836: Support the adapters' `_session/steering` extensiongithub.com ↗截至 2026-11-01 仍为 openhttps://github.com/openclaw/acpx/issues/836 ↩
-
acpx · Permissionsgithub.com ↗https://github.com/openclaw/acpx/blob/main/docs/permissions.md ↩
-
Manage multiple agents with agent viewcode.claude.com ↗
claude --bg、claude agents --json、claude stop,以及 supervisor 进程https://code.claude.com/docs/en/agent-view ↩ -
Resume a running background sessioncode.claude.com ↗带管道、重定向或
--output-format json时什么都不发,以状态码 1 退出https://code.claude.com/docs/en/sessions ↩ -
coder/agentapigithub.com ↗https://github.com/coder/agentapi ↩
-
A2A Protocol Specificationa2a-protocol.org ↗https://a2a-protocol.org/latest/specification/ ↩
-
ag-ui-protocol/ag-uigithub.com ↗https://github.com/ag-ui-protocol/ag-ui ↩
-
Model Context Protocol Specificationmodelcontextprotocol.io ↗https://modelcontextprotocol.io/specification/2025-06-18 ↩
-
harnessctl/2:本机 Harness CLI 协议github.com ↗协议规范,提交
91bb42dhttps://github.com/memory-co/agentuse/blob/91bb42d82160d226c7f5337565879ffcf384685b/harnessctl/README.md ↩ -
harness/codex/lib/codex.pygithub.com ↗
STEER_WHILE_BUSY = True,只把no active turn和expected active turn当作竞态https://github.com/memory-co/agentuse/blob/91bb42d82160d226c7f5337565879ffcf384685b/harnessctl/harness/codex/lib/codex.py ↩ -
harness/claudecode/lib/claudecode.pygithub.com ↗
STEER_WHILE_BUSY = False;“replay is receipt, and result/echo ordering alone does not prove model consumption or turn”https://github.com/memory-co/agentuse/blob/91bb42d82160d226c7f5337565879ffcf384685b/harnessctl/harness/claudecode/lib/claudecode.py ↩ -
harnessctl/2:五种公共 JSONL 事件github.com ↗https://github.com/memory-co/agentuse/blob/91bb42d82160d226c7f5337565879ffcf384685b/harnessctl/EVENT.md ↩
-
harness/codex/lib/runtime.pygithub.com ↗运行进程、队列、失联恢复https://github.com/memory-co/agentuse/blob/91bb42d82160d226c7f5337565879ffcf384685b/harnessctl/harness/codex/lib/runtime.py ↩