Codex 协议参考
这一页列出 Codex app-server 适配器在线上读写的字段,以及它们映射成 ChatKit 的什么。怎么接入见 Codex;本页没写的字段以 Codex app-server 文档为准。
传输
字段按 codex-cli 0.162.1 的 codex app-server generate-json-schema 与实测事件核对。app-server 协议仍在演进,追问依赖 experimentalApi;升级 Codex 后以包里的 src/wire.types.ts 为准重新核对。
发出的请求
transformInput 收到的 defaultInput 是 { input },必须返回完整请求体;其余字段原样带进 turn/start。
turn/start
读结果的 turn.id。非图片附件、resource 来源的附件在编码时抛错(cause.code: 'UNSUPPORTED_ATTACHMENT')。
其它请求
其它方法(模型列表、线程改名、归档、fork…)用 connection.request(method, params) 自行调用,后端返回错误时抛 CodexRpcError。
读取的事件
turn/completed 的 items 不全(实测只带最后一条回答),回答一律从 item/started / item/completed 与增量拼。
条目类型与片段:
工具状态:inProgress → running,completed → completed,failed → failed,declined → cancelled。片段按条目第一次出现的顺序排,一轮只有一条回答消息。
没有内置处理的事件
下面这些上游内容适配器不转成聊天内容,接入方可以自己取:
刷新加载历史时,适配器把历史回放成与实时同形的通知逐条交给 transformOutput(context.source 为 history,带这一轮的 runId 与 messageId):每个条目一条 item/completed,结束的轮次一条 turn/completed。item/started、增量、服务端请求和其它通知只在实时里有。业务内容从回放里有的通知生成,刷新后就能还原。
属于某一轮的通知
参数带 turnId,本轮的 transformOutput 与 listen 都能收到。
不属于某一轮的通知
参数只带 threadId,用 listen 拿。
不带 threadId 的通知
连接按 thread 分发,这些通知不转交给 listen;需要时由接入方另开一条 app-server 连接处理。
服务端请求
只有 item/commandExecution/requestApproval、item/fileChange/requestApproval、item/tool/requestUserInput(且带 threadId)交给协议。其余服务端请求连接收到即回 -32601,不让这一轮干等;它们不进 listen 与 transformOutput,连接的 onError 收到 kind: 'request' 与 { method }。
listen 断线重连
连接断开(或这个会话的订阅单独结束)时,有 listen 监听的会话:
- 监听先收到
{ kind: 'connection', status: 'disconnected' }; - 连接按退避自动重连并重新订阅(重新握手并
thread/resume):第一次等reconnect.minDelayMs(缺省 1000),之后翻倍,最多等reconnect.maxDelayMs(缺省 30000); - 订阅恢复后收到
{ kind: 'connection', status: 'reconnected' }。断线期间的事件不会补发,需要时自行补拉状态(如thread/read)。
每次重新订阅失败经 onError('listen', error) 报告,之后继续重试。取消全部监听或调用 close() 后停止;连接选项 reconnect: false 关闭自动重连(仍会收到 disconnected)。正在进行的一轮不续接,仍由会话层经历史确认终态。
一轮都没跑过的 thread 不落盘,连接断开后 thread/resume 会失败(no rollout found),重连会一直重试并报告错误。
示例
turn/plan/updated 不在历史里,这张计划卡片刷新后不会还原;要还原就从 item/completed 里能拿到的内容生成。
context.messageId 是本轮的回答消息,回答还没有内容时为空。
审批与追问
收到请求后阶段为 awaiting-external。可选回答只取 availableDecisions 里属于 accept、acceptForSession、decline、cancel 的字符串,带执行策略修订的对象形式不提供;一个都没有时给全部四个。
交互请求在工具片段的 metadata['chatkit.interaction'],用 getCodexInteraction(part) 读:
connection.respond({ sessionId, requestId, response }) 按原请求 id 发回:
请求已处理、或连接断开后(待回答请求已清空)再调用会抛错;respond 发出即返回,不等 serverRequest/resolved。
历史
thread/turns/list 返回 data(轮次,从新到旧)与 nextCursor;连接整理成 { turns, nextCursor },解码时反转成时间顺序。每个轮次读 id、status、items、error、startedAt、completedAt(Unix 秒)。
Codex 历史不保留:审批记录(刷新后工具只有最终状态,没有交互请求)、追问(卡片不再出现)、被插话打断的那段回答、停止时已输出的部分回答。插话的用户消息实时显示在回答之后,刷新后排在回答之前。
编号
结束与错误
断线时这一轮由 Core 标记连接中断,不认定结束;适配器不支持续接,由会话层经历史确认终态。runId → turnId 留到读到 turn/completed(或 turn/start 失败)才删,所以断线后仍能停止、插话:连接先在新 WebSocket 上 thread/resume,再发 turn/interrupt / turn/steer。
示例
一轮文本回答(一行一条报文,→ 为发出,← 为收到):
得到消息 assistant:run-1,文本片段 item:msg-1,终态 completed。
命令审批:
工具片段 tool:call_1 上的交互请求依次为 pending(decisions: ['accept', 'acceptForSession', 'cancel'])→ answering → answered(decision: 'accept')。