跳到主要内容

本文为非官方中文翻译,内容以 OpenAI 官方英文文档为准。
官方来源:https://developers.openai.com/codex/app-server

Codex App Server

使用 app-server 协议将 Codex 嵌入到你的产品中

Codex app-server 是 Codex 用于驱动富客户端的接口(例如 Codex VS Code 扩展)。当你希望在自己的产品内部进行深度集成时使用它:身份验证、会话历史、审批以及流式 agent 事件。app-server 实现已在 Codex GitHub 仓库中开源(openai/codex/codex-rs/app-server)。有关开源 Codex 组件的完整列表,请参阅 Open Source 页面。

如果你是在自动化任务或在 CI 中运行 Codex,请改用 Codex SDK。

协议

MCP 类似,codex app-server 支持使用 JSON-RPC 2.0 消息进行双向通信(在线路上传输时省略 "jsonrpc":"2.0" 头)。

支持的传输方式:

  • stdio--listen stdio://,默认):以换行分隔的 JSON(JSONL)。
  • websocket--listen ws://IP:PORT,实验性且不受支持):每个 WebSocket 文本帧一条 JSON-RPC 消息。
  • Unix socket(--listen unix://--listen unix://PATH):通过 Codex 默认的 app-server 控制 socket 或自定义 Unix socket 路径上的 WebSocket 连接,使用标准 HTTP Upgrade 握手。
  • off--listen off):不暴露本地传输端点。

当你使用 --listen ws://IP:PORT 运行时,同一个监听器还会提供基础的 HTTP 健康探测:

  • GET /readyz 会在监听器开始接受新连接后返回 200 OK
  • 当请求不包含 Origin 头时,GET /healthz 返回 200 OK
  • 带有 Origin 头的请求会被拒绝,并返回 403 Forbidden

WebSocket 传输是实验性的且不受支持。像 ws://127.0.0.1:PORT 这样的本地监听器适用于 localhost 和 SSH 端口转发工作流。在当前发布过程中,非 loopback 的 WebSocket 监听器默认允许未认证连接,因此在远程暴露 WebSocket 监听器之前,请先配置 WebSocket 认证。

支持的 WebSocket 认证标志:

  • --ws-auth capability-token --ws-token-file /absolute/path
  • --ws-auth capability-token --ws-token-sha256 HEX
  • --ws-auth signed-bearer-token --ws-shared-secret-file /absolute/path

对于 signed bearer token,你还可以设置 --ws-issuer--ws-audience--ws-max-clock-skew-seconds。客户端在 WebSocket 握手期间以 Authorization: Bearer 的形式提供凭证,而 app-server 会在 JSON-RPC initialize 之前强制执行认证。

相比在命令行中传递原始 bearer token,更推荐使用 --ws-token-file。仅当客户端将原始高熵 token 保存在单独的本地 secret store 中时才使用 --ws-token-sha256;该哈希仅是一个校验值,客户端仍然需要原始 token。

在 WebSocket 模式下,app-server 使用有界队列。当请求入口已满时,服务器会使用 JSON-RPC 错误码 -32001 和消息 "Server overloaded; retry later." 拒绝新请求。客户端应使用指数递增的延迟并加入抖动进行重试。

消息 schema

请求包含 methodparamsid

{ "method": "thread/start", "id": 10, "params": { "model": "gpt-5.4" } }

响应会回显 id,并包含 resulterror

{ "id": 10, "result": { "thread": { "id": "thr_123" } } }
{ "id": 10, "error": { "code": 123, "message": "Something went wrong" } }

通知省略 id,只使用 methodparams

{ "method": "turn/started", "params": { "turn": { "id": "turn_456" } } }

你可以通过 CLI 生成 TypeScript schema 或 JSON Schema bundle。每个输出都对应于你运行时所使用的 Codex 版本,因此生成的工件会与该版本精确匹配:

codex app-server generate-ts --out ./schemas
codex app-server generate-json-schema --out ./schemas

快速开始

  1. 使用 codex app-server(默认 stdio 传输)、 codex app-server --listen ws://127.0.0.1:4500(TCP WebSocket)或 codex app-server --listen unix://(默认 Unix socket)启动服务器。
  2. 通过所选传输连接一个客户端,然后发送 initialize,接着发送 initialized 通知。
  3. 启动一个 thread 和一个 turn,然后持续从活动传输流中读取通知。

示例(Node.js / TypeScript):


const proc = spawn("codex", ["app-server"], {
stdio: ["pipe", "pipe", "inherit"],
});
const rl = readline.createInterface({ input: proc.stdout });

const send = (message: unknown) => {
proc.stdin.write(`${JSON.stringify(message)}\n`);
};

let threadId: string | null = null;

rl.on("line", (line) => {
const msg = JSON.parse(line) as any;
console.log("server:", msg);

if (msg.id === 1 && msg.result?.thread?.id && !threadId) {
threadId = msg.result.thread.id;
send({
method: "turn/start",
id: 2,
params: {
threadId,
input: [{ type: "text", text: "Summarize this repo." }],
},
});
}
});

send({
method: "initialize",
id: 0,
params: {
clientInfo: {
name: "my_product",
title: "My Product",
version: "0.1.0",
},
},
});
send({ method: "initialized", params: {} });
send({ method: "thread/start", id: 1, params: { model: "gpt-5.4" } });

核心原语

  • Thread:用户与 Codex agent 之间的一次会话。Thread 包含多个 turn。
  • Turn:单次用户请求及随后发生的 agent 工作。Turn 包含多个 item,并流式传递增量更新。
  • Item:输入或输出的一个单元(用户消息、agent 消息、命令运行、文件变更、工具调用等)。

使用 thread API 来创建、列出或归档会话。使用 turn API 驱动会话,并通过 turn 通知流式获取进度。

生命周期概览

  • 每个连接初始化一次:在打开传输连接后,立即发送一个包含客户端元数据的 initialize 请求,然后发出 initialized。服务器会拒绝该连接上在此握手之前的任何请求。
  • 启动(或恢复)一个 thread:调用 thread/start 创建新会话,调用 thread/resume 继续现有会话,或调用 thread/fork 将历史分支到一个新的 thread id。
  • 开始一个 turn:调用 turn/start,提供目标 threadId 和用户输入。可选字段可覆盖模型、personality、cwd、sandbox policy 等。
  • 引导一个活动中的 turn:调用 turn/steer,将用户输入附加到当前正在进行的 turn,而无需创建新的 turn。
  • 流式接收事件:在 turn/start 之后,持续从 stdout 读取通知:thread/archivedthread/unarchiveditem/starteditem/completeditem/agentMessage/delta、工具进度及其他更新。
  • 结束 turn:当模型完成时,或在 turn/interrupt 取消之后,服务器会发出带有最终状态的 turn/completed

初始化

客户端必须在每个传输连接上先发送一次且仅一次 initialize 请求,然后才能在该连接上调用任何其他方法,之后再通过 initialized 通知进行确认。在初始化之前发送的请求会收到 Not initialized 错误,而在同一连接上重复调用 initialize 会返回 Already initialized

服务器会返回它将向上游服务提供的 user agent string,以及描述运行时目标的 platformFamilyplatformOs 值。请设置 clientInfo 以标识你的集成。

initialize.params.capabilities 还支持通过 optOutNotificationMethods 按连接选择退出通知,这是一组要在该连接上抑制的精确方法名列表。匹配是精确的(不支持通配符/前缀)。未知的方法名会被接受并忽略。

重要:使用 clientInfo.name 在 OpenAI Compliance Logs Platform 中标识你的客户端。如果你正在开发一个面向企业使用的新 Codex 集成,请联系 OpenAI 以将其添加到已知客户端列表中。更多背景信息,请参阅 Codex logs reference

示例(来自 Codex VS Code 扩展):

{
"method": "initialize",
"id": 0,
"params": {
"clientInfo": {
"name": "codex_vscode",
"title": "Codex VS Code Extension",
"version": "0.1.0"
}
}
}

带通知退出的示例:

{
"method": "initialize",
"id": 1,
"params": {
"clientInfo": {
"name": "my_client",
"title": "My Client",
"version": "0.1.0"
},
"capabilities": {
"experimentalApi": true,
"optOutNotificationMethods": ["thread/started", "item/agentMessage/delta"]
}
}
}

Experimental API 选择加入

某些 app-server 方法和字段会被有意地置于 experimentalApi capability 的控制之下。

  • 省略 capabilities(或将 experimentalApi 设为 false)即可停留在稳定 API 表面,服务器会拒绝实验性的方法/字段。
  • capabilities.experimentalApi 设为 true 以启用实验性的方法和字段。
{
"method": "initialize",
"id": 1,
"params": {
"clientInfo": {
"name": "my_client",
"title": "My Client",
"version": "0.1.0"
},
"capabilities": {
"experimentalApi": true
}
}
}

如果客户端在未选择启用的情况下发送实验性方法或字段,app-server 会拒绝,并提示:

requires experimentalApi capability

API 概览

  • thread/start - 创建新线程;会发出 thread/started,并自动为你订阅该线程的 turn/item 事件。
  • thread/resume - 通过 id 重新打开现有线程,以便后续的 turn/start 调用可追加到该线程。
  • thread/fork - 通过复制已存储历史,将线程 fork 为一个新的线程 id;会为新线程发出 thread/started。返回的线程在可用时会包含 forkedFromId
  • thread/read - 通过 id 读取已存储线程而不恢复它;设置 includeTurns 可返回完整 turn 历史。返回的 thread 对象包含运行时 status
  • thread/list - 分页遍历已存储线程日志;支持基于游标的分页,以及 modelProviderssourceKindsarchivedcwdsearchTerm 过滤器。返回的 thread 对象包含运行时 status
  • thread/turns/list - 在不恢复线程的情况下,分页遍历已存储线程的 turn 历史。itemsView 控制是省略 turn item、返回摘要,还是完整加载。
  • thread/turns/items/list - 为分页加载 turn-item 预留;当前返回 unsupported。
  • thread/loaded/list - 列出当前已加载到内存中的线程 id。
  • thread/name/set - 为已加载线程或持久化 rollout 设置或更新面向用户的名称;会发出 thread/name/updated
  • thread/goal/set - 为已加载线程设置目标(实验性;需要 capabilities.experimentalApi);会发出 thread/goal/updated
  • thread/goal/get - 读取已加载线程的当前目标(实验性;需要 capabilities.experimentalApi)。
  • thread/goal/clear - 清除已加载线程的目标(实验性;需要 capabilities.experimentalApi);会发出 thread/goal/cleared
  • thread/metadata/update - 修补由 SQLite 支持的已存储线程元数据;当前支持持久化的 gitInfo
  • thread/archive - 将线程日志文件移动到归档目录;成功时返回 {} 并发出 thread/archived
  • thread/unsubscribe - 取消此连接对线程 turn/item 事件的订阅。如果这是最后一个订阅者,服务器会在无订阅者的空闲宽限期后卸载该线程,并发出 thread/closed
  • thread/unarchive - 将已归档的线程 rollout 恢复回活动会话目录;返回恢复后的 thread 并发出 thread/unarchived
  • thread/status/changed - 当已加载线程的运行时 status 变化时发出的通知。
  • thread/compact/start - 触发线程的会话历史压缩;立即返回 {},同时通过 turn/*item/* 通知流式传输进度。
  • thread/shellCommand - 对线程运行用户发起的 shell 命令。此操作在沙箱外运行,具有完整访问权限,并且不继承线程沙箱策略。
  • thread/backgroundTerminals/clean - 停止线程的所有正在运行的后台终端(实验性;需要 capabilities.experimentalApi)。
  • thread/rollback - 从内存上下文中删除最近 N 个 turn,并持久化一个回滚标记;返回更新后的 thread
  • turn/start - 向线程添加用户输入并开始 Codex 生成;响应初始 turn 并流式传输事件。对于 collaborationModesettings.developer_instructions: null 表示“对所选模式使用内置指令”。
  • thread/inject_items - 将原始 Responses API items 追加到已加载线程的模型可见历史中,而不启动用户 turn。
  • turn/steer - 将用户输入追加到线程当前正在进行中的 turn;返回已接受的 turnId
  • turn/interrupt - 请求取消正在进行中的 turn;成功时返回 {},且该 turn 将以 status: "interrupted" 结束。
  • review/start - 为线程启动 Codex reviewer;会发出 enteredReviewModeexitedReviewMode items。
  • command/exec - 在服务器沙箱下运行单个命令,而不启动线程/turn。
  • command/exec/write - 向正在运行的 command/exec 会话写入 stdin 字节,或关闭 stdin
  • command/exec/resize - 调整正在运行的基于 PTY 的 command/exec 会话大小。
  • command/exec/terminate - 停止正在运行的 command/exec 会话。
  • command/exec/outputDelta (notify) - 为流式 command/exec 会话发出的 base64 编码 stdout/stderr 分块通知。
  • process/spawn - 在 Codex 的沙箱之外启动显式进程会话(实验性;需要 capabilities.experimentalApi)。
  • process/writeStdin - 向正在运行的 process/spawn 会话写入 stdin 字节,或关闭 stdin(实验性)。
  • process/resizePty - 调整正在运行的基于 PTY 的进程会话大小(实验性)。
  • process/kill - 终止正在运行的进程会话(实验性)。
  • process/outputDeltaprocess/exited (notify) - 为流式进程输出和进程退出状态发出的通知(实验性)。
  • model/list - 列出可用模型(设置 includeHidden: true 可包含带有 hidden: true 的条目),并附带 effort 选项、可选的 upgrade 以及 inputModalities
  • modelProvider/capabilities/read - 读取模型/提供方组合的提供方能力边界(实验性;需要 capabilities.experimentalApi)。
  • experimentalFeature/list - 列出功能标志及其生命周期阶段元数据,并支持游标分页。
  • experimentalFeature/enablement/set - 修补受支持功能键(如 appsplugins)的内存中运行时设置。
  • collaborationMode/list - 列出协作模式预设(实验性,无分页)。
  • skills/list - 列出一个或多个 cwd 值的 Skills(支持 forceReload 和可选的 perCwdExtraUserRoots)。
  • skills/changed (notify) - 当被监视的本地 skill 文件发生变化时发出的通知。
  • marketplace/add - 添加远程插件市场,并将其持久化到用户的 marketplace 配置中。
  • marketplace/upgrade - 刷新已配置的 Git marketplace;如果省略 marketplace 名称,则刷新所有已配置的 Git marketplaces。
  • plugin/list - 列出已发现的插件市场和插件状态,包括 install/auth 策略元数据、marketplace 加载错误、featured plugin id,以及本地、Git 或远程插件源元数据。
  • plugin/read - 通过 marketplace 路径或远程 marketplace 名称和插件名称读取单个插件;在这些详细信息可用时,包括捆绑的 skills、apps 和 MCP server 名称。
  • plugin/install - 从 marketplace 路径或远程 marketplace 名称安装插件。
  • plugin/uninstall - 卸载已安装的插件。
  • app/list - 列出可用 apps(connectors),支持分页以及 accessibility/enabled 元数据。
  • skills/config/write - 按路径启用或禁用 skills。
  • mcpServer/oauth/login - 为已配置的 MCP server 启动 OAuth 登录;返回授权 URL,并在完成时发出 mcpServer/oauthLogin/completed
  • tool/requestUserInput - 为工具调用提示用户回答 1-3 个简短问题(实验性);问题可设置 isOther 以提供自由输入选项。
  • config/mcpServer/reload - 从磁盘重新加载 MCP server 配置,并为已加载线程排队刷新。
  • mcpServerStatus/list - 列出 MCP servers、tools、resources 和 auth 状态(游标 + limit 分页)。使用 detail: "full" 获取完整数据,或使用 detail: "toolsAndAuthOnly" 以省略 resources。
  • mcpServer/resource/read - 通过已初始化的 MCP server 读取单个 MCP resource。
  • mcpServer/tool/call - 调用线程已配置 MCP server 上的工具。
  • mcpServer/startupStatus/updated (notify) - 当已加载线程中某个已配置 MCP server 的启动状态发生变化时发出的通知。
  • windowsSandbox/setupStart - 为 elevatedunelevated 模式启动 Windows 沙箱设置;会快速返回,并随后发出 windowsSandbox/setupCompleted
  • feedback/upload - 提交反馈报告(分类 + 可选原因/日志 + 会话 id,以及可选的 extraLogFiles 附件)。
  • config/read - 在解析配置分层后,获取磁盘上的生效配置。
  • externalAgentConfig/detect - 检测可通过 includeHome 和可选 cwds 迁移的 external-agent 工件;每个检测到的项都包含 cwd(home 时为 null)。
  • externalAgentConfig/import - 通过传递带有 cwd(home 时为 null)的显式 migrationItems,应用选定的 external-agent 迁移项。支持的项类型包括 config、skills、AGENTS.md、plugins、MCP server config、subagents、hooks、commands 和 sessions;插件导入会发出 externalAgentConfig/import/completed
  • config/value/write - 将单个配置键/值写入磁盘上的用户 config.toml
  • config/batchWrite - 以原子方式将配置编辑应用到磁盘上的用户 config.toml
  • configRequirements/read - 从 requirements.toml 和/或 MDM 获取要求,包括 allow-lists、固定的 featureRequirements 以及驻留/网络要求(如果尚未设置则为 null)。
  • fs/readFilefs/writeFilefs/createDirectoryfs/getMetadatafs/readDirectoryfs/removefs/copyfs/watchfs/unwatchfs/changed (notify) - 通过 app-server v2 文件系统 API 对绝对文件系统路径进行操作。

插件摘要包含一个 `source` 联合类型。本地插件返回
`{ "type": "local", "path": ... }`,由 Git 支持的 marketplace 条目返回
`{ "type": "git", "url": ..., "path": ..., "refName": ..., "sha": ... }`,
远程 catalog 条目返回 `{ "type": "remote" }`。对于仅远程的
catalog 条目,`PluginMarketplaceEntry.path` 可以为 `null`;在读取或安装
这些插件时,请传入 `remoteMarketplaceName` 而不是 `marketplacePath`。

## 模型

### 列出模型(`model/list`)

在渲染模型或 personality 选择器之前,调用 `model/list` 以发现可用模型及其能力。

```json
{ "method": "model/list", "id": 6, "params": { "limit": 20, "includeHidden": false } }
{ "id": 6, "result": {
"data": [{
"id": "gpt-5.4",
"model": "gpt-5.4",
"displayName": "GPT-5.4",
"hidden": false,
"defaultReasoningEffort": "medium",
"supportedReasoningEfforts": [{
"reasoningEffort": "low",
"description": "Lower latency"
}],
"inputModalities": ["text", "image"],
"supportsPersonality": true,
"isDefault": true
}],
"nextCursor": null
} }

每个模型条目可包含:

  • supportedReasoningEfforts - 该模型支持的 effort 选项。
  • defaultReasoningEffort - 为客户端建议的默认 effort。
  • upgrade - 可选的推荐升级模型 id,用于客户端中的迁移提示。
  • upgradeInfo - 可选的升级元数据,用于客户端中的迁移提示。
  • hidden - 该模型是否在默认选择器列表中隐藏。
  • inputModalities - 该模型支持的输入类型(例如 textimage)。
  • supportsPersonality - 该模型是否支持特定 personality 的指令,例如 /personality
  • isDefault - 该模型是否为推荐默认值。

默认情况下,model/list 只返回在选择器中可见的模型。如果你需要完整列表并希望在客户端使用 hidden 进行过滤,请设置 includeHidden: true

当缺少 inputModalities 时(较旧的模型 catalog),为保持向后兼容,请将其视为 ["text", "image"]

列出实验性功能(experimentalFeature/list

使用此端点发现带有元数据和生命周期阶段的功能标志:

{ "method": "experimentalFeature/list", "id": 7, "params": { "limit": 20 } }
{ "id": 7, "result": {
"data": [{
"name": "unified_exec",
"stage": "beta",
"displayName": "Unified exec",
"description": "Use the unified PTY-backed execution tool.",
"announcement": "Beta rollout for improved command execution reliability.",
"enabled": false,
"defaultEnabled": false
}],
"nextCursor": null
} }

stage 可以是 betaunderDevelopmentstabledeprecatedremoved。对于非 beta 标志,displayNamedescriptionannouncement 可以为 null

线程

  • thread/read 读取已存储的线程而不订阅它;设置 includeTurns 以包含 turn。
  • thread/turns/list 对已存储线程的 turn 历史进行分页,而不恢复该线程。使用 itemsView 选择省略 turn 项、返回摘要,或完整加载。
  • thread/list 支持游标分页,以及 modelProviderssourceKindsarchivedcwdsearchTerm 过滤。
  • thread/loaded/list 返回当前在内存中的线程 ID。
  • thread/archive 将线程持久化的 JSONL 日志移动到 archived 目录。
  • thread/metadata/update 补丁更新已存储的线程元数据,目前包括持久化的 gitInfo
  • thread/unsubscribe 取消当前连接对已加载线程的订阅,并且在一段非活动宽限期后可以触发 thread/closed
  • thread/unarchive 将已归档线程的 rollout 恢复到活动 sessions 目录。
  • thread/compact/start 触发压缩并立即返回 {}
  • thread/rollback 从内存上下文中丢弃最后 N 个 turn,并在线程持久化的 JSONL 日志中记录一个 rollback 标记。
  • thread/inject_items 将原始 Responses API 项追加到已加载线程的、模型可见的历史中,而不启动用户 turn。

启动或恢复线程

当你需要一个新的 Codex 对话时,启动一个新的线程。

{ "method": "thread/start", "id": 10, "params": {
"model": "gpt-5.4",
"cwd": "/Users/me/project",
"approvalPolicy": "never",
"sandbox": "workspaceWrite",
"personality": "friendly",
"serviceName": "my_app_server_client"
} }
{ "id": 10, "result": {
"thread": {
"id": "thr_123",
"sessionId": "thr_123",
"preview": "",
"ephemeral": false,
"modelProvider": "openai",
"createdAt": 1730910000
}
} }
{ "method": "thread/started", "params": { "thread": { "id": "thr_123" } } }

serviceName 是可选的。当你希望 app-server 使用你的集成服务名为线程级指标打标签时,请设置它。

thread.sessionId 标识当前活动 session 树的根。根线程 使用其自身的线程 id 作为 session id;fork 出来的线程保留其来源根线程的 session id。 客户端应从 thread.sessionId 读取 session id, 而不是从线程 id 推导它。

要继续一个已存储的 session,请使用你之前记录的 thread.id 调用 thread/resume。响应结构与 thread/start 相同。你也可以传入 thread/start 支持的相同配置覆盖项,例如 personality

{ "method": "thread/resume", "id": 11, "params": {
"threadId": "thr_123",
"personality": "friendly"
} }
{ "id": 11, "result": { "thread": { "id": "thr_123", "name": "Bug bash notes", "ephemeral": false } } }

恢复线程本身不会更新 thread.updatedAt(或 rollout 文件的修改时间)。该时间戳会在你启动一个 turn 时更新。

如果你在配置中将一个已启用的 MCP server 标记为 required,而该 server 初始化失败,则 thread/startthread/resume 会失败,而不是在没有它的情况下继续。

thread/start 上的 dynamicTools 是一个实验性字段(需要 capabilities.experimentalApi = true)。Codex 会将这些动态工具持久化在线程 rollout 元数据中,并在你未提供新的动态工具时,于 thread/resume 时恢复它们。

如果你恢复线程时使用的模型与 rollout 中记录的模型不同,Codex 会发出警告,并在下一次 turn 时应用一次性模型切换指令。

管理线程目标

thread/goal/setthread/goal/getthread/goal/clear 是实验性的, 需要 capabilities.experimentalApi = true 以及 goals 功能。可将它们 用于与 TUI 中 /goal 暴露的相同持久化目标状态。

{ "method": "thread/goal/set", "id": 13, "params": {
"threadId": "thr_123",
"objective": "Finish the migration and keep tests green",
"status": "active",
"tokenBudget": 40000
} }
{ "id": 13, "result": { "goal": {
"threadId": "thr_123",
"objective": "Finish the migration and keep tests green",
"status": "active",
"tokenBudget": 40000,
"tokensUsed": 0,
"timeUsedSeconds": 0
} } }
{ "method": "thread/goal/updated", "params": {
"threadId": "thr_123",
"goal": {
"threadId": "thr_123",
"objective": "Finish the migration and keep tests green",
"status": "active",
"tokenBudget": 40000,
"tokensUsed": 0,
"timeUsedSeconds": 0
}
} }

目标 objective 必须非空,且最多 4,000 个字符。提供新的 objective 会替换该目标并重置用量统计。提供当前的 非终止 objective,或省略 objective,则会更新状态或 token 预算, 同时保留用量历史。

要从已存储的 session 分支,请使用 thread.id 调用 thread/fork。这会创建一个新的线程 id,并为其发出 thread/started 通知:

{ "method": "thread/fork", "id": 12, "params": { "threadId": "thr_123" } }
{ "id": 12, "result": { "thread": { "id": "thr_456", "sessionId": "thr_123", "forkedFromId": "thr_123" } } }
{ "method": "thread/started", "params": { "thread": { "id": "thr_456" } } }

当已设置面向用户的线程标题时,app-server 会在 thread/listthread/readthread/resumethread/unarchivethread/rollback 的响应中填充 thread.namethread/startthread/fork 可能会省略 name(或返回 null),直到稍后设置标题。

读取已存储的线程(不恢复)

当你想获取已存储的线程数据,但不想恢复该线程或订阅其事件时,请使用 thread/read

  • includeTurns - 当为 true 时,响应包含线程的 turn;当为 false 或省略时,只返回线程摘要。
  • 返回的 thread 对象包含运行时 statusnotLoadedidlesystemError,或带有 activeFlagsactive)。
{ "method": "thread/read", "id": 19, "params": { "threadId": "thr_123", "includeTurns": true } }
{ "id": 19, "result": { "thread": { "id": "thr_123", "name": "Bug bash notes", "ephemeral": false, "status": { "type": "notLoaded" }, "turns": [] } } }

thread/resume 不同,thread/read 不会将线程加载到内存中,也不会发出 thread/started

列出线程回合

使用 thread/turns/list 可对已存储线程的回合历史进行分页,而无需恢复该线程。结果默认按最新优先返回,因此客户端可以使用 nextCursor 获取更早的回合。响应还包含 backwardsCursor;将其作为 cursor 并配合 sortDirection: "asc" 传入,即可获取比前一页中第一项更新的回合。

itemsView 控制响应中包含多少回合项数据:

  • notLoaded 省略项。
  • summary 返回汇总后的项数据,且在省略时为默认值。
  • full 返回完整项数据。
{ "method": "thread/turns/list", "id": 20, "params": {
"threadId": "thr_123",
"limit": 50,
"sortDirection": "desc",
"itemsView": "summary"
} }
{ "id": 20, "result": {
"data": [],
"nextCursor": "older-turns-cursor-or-null",
"backwardsCursor": "newer-turns-cursor-or-null"
} }

thread/turns/items/list 预留用于分页加载回合项,但当前服务器会返回 unsupported-method 错误。

列出线程(带分页与筛选)

thread/list 可用于渲染历史记录 UI。结果默认按 createdAt 最新优先返回。筛选会在分页之前应用。可传入以下任意组合:

  • cursor - 来自先前响应的不透明字符串;第一页可省略。
  • limit - 若未设置,服务器会默认使用合理的页面大小。
  • sortKey - created_at(默认)或 updated_at
  • modelProviders - 将结果限制为特定 provider;未设置、为 null 或空数组时包含所有 provider。
  • sourceKinds - 将结果限制为特定线程来源。省略或传入 [] 时,服务器默认仅包含交互式来源:clivscode
  • archived - 为 true 时,仅列出已归档线程。为 false 或省略时,列出未归档线程(默认)。
  • cwd - 将结果限制为会话当前工作目录与此路径完全匹配的线程。
  • searchTerm - 在分页前搜索已存储线程的摘要和元数据。

sourceKinds 接受以下值:

  • cli
  • vscode
  • exec
  • appServer
  • subAgent
  • subAgentReview
  • subAgentCompact
  • subAgentThreadSpawn
  • subAgentOther
  • unknown

示例:

{ "method": "thread/list", "id": 20, "params": {
"cursor": null,
"limit": 25,
"sortKey": "created_at"
} }
{ "id": 20, "result": {
"data": [
{ "id": "thr_a", "preview": "Create a TUI", "ephemeral": false, "modelProvider": "openai", "createdAt": 1730831111, "updatedAt": 1730831111, "name": "TUI prototype", "status": { "type": "notLoaded" } },
{ "id": "thr_b", "preview": "Fix tests", "ephemeral": true, "modelProvider": "openai", "createdAt": 1730750000, "updatedAt": 1730750000, "status": { "type": "notLoaded" } }
],
"nextCursor": "opaque-token-or-null"
} }

nextCursornull 时,表示你已到达最后一页。

更新已存储线程元数据

使用 thread/metadata/update 可在不恢复线程的情况下修补已存储线程的元数据。目前这支持持久化的 gitInfo;省略的字段保持不变,而显式传入 null 会清除已存储的值。

{ "method": "thread/metadata/update", "id": 21, "params": {
"threadId": "thr_123",
"gitInfo": { "branch": "feature/sidebar-pr" }
} }
{ "id": 21, "result": {
"thread": {
"id": "thr_123",
"gitInfo": { "sha": null, "branch": "feature/sidebar-pr", "originUrl": null }
}
} }

跟踪线程状态变更

每当已加载线程的运行时状态发生变化时,都会发出 thread/status/changed。其负载包含 threadId 和新的 status

{
"method": "thread/status/changed",
"params": {
"threadId": "thr_123",
"status": { "type": "active", "activeFlags": ["waitingOnApproval"] }
}
}

列出已加载线程

thread/loaded/list 返回当前已加载到内存中的线程 ID。

{ "method": "thread/loaded/list", "id": 21 }
{ "id": 21, "result": { "data": ["thr_123", "thr_456"] } }

取消订阅已加载线程

thread/unsubscribe 会移除当前连接对某个线程的订阅。响应状态为以下之一:

  • 当该连接原本已订阅且现已移除时,返回 unsubscribed
  • 当该连接并未订阅该线程时,返回 notSubscribed
  • 当线程未加载时,返回 notLoaded

如果这是最后一个订阅者,服务器会继续保持线程已加载状态,直到其既无订阅者且 30 分钟内无线程活动为止。当宽限期到期时,app-server 会卸载该线程,并发出到 notLoadedthread/status/changed 状态转换以及 thread/closed

{ "method": "thread/unsubscribe", "id": 22, "params": { "threadId": "thr_123" } }
{ "id": 22, "result": { "status": "unsubscribed" } }

如果线程之后过期:

{ "method": "thread/status/changed", "params": {
"threadId": "thr_123",
"status": { "type": "notLoaded" }
} }
{ "method": "thread/closed", "params": { "threadId": "thr_123" } }

归档线程

使用 thread/archive 可将持久化线程日志(以磁盘上的 JSONL 文件形式存储)移动到已归档会话目录中。

{ "method": "thread/archive", "id": 22, "params": { "threadId": "thr_b" } }
{ "id": 22, "result": {} }
{ "method": "thread/archived", "params": { "threadId": "thr_b" } }

除非传入 archived: true,否则已归档线程不会出现在后续对 thread/list 的调用中。

取消归档线程

使用 thread/unarchive 可将已归档线程的 rollout 移回活动会话目录。

{ "method": "thread/unarchive", "id": 24, "params": { "threadId": "thr_b" } }
{ "id": 24, "result": { "thread": { "id": "thr_b", "name": "Bug bash notes" } } }
{ "method": "thread/unarchived", "params": { "threadId": "thr_b" } }

触发线程压缩

使用 thread/compact/start 可触发某个线程的手动历史压缩。请求会立即返回 {}

App-server 会在同一 threadId 上通过标准 turn/*item/* 通知发出进度,包括一个 contextCompaction 项生命周期(item/started 然后 item/completed)。

{ "method": "thread/compact/start", "id": 25, "params": { "threadId": "thr_b" } }
{ "id": 25, "result": {} }

运行线程 shell 命令

对属于某个线程的、由用户发起的 shell 命令,请使用 thread/shellCommand。该请求会立即返回 {},同时进度会通过标准 turn/*item/* 通知流式传输。

此 API 在沙箱外运行,具有完全访问权限,并且不会继承线程的沙箱策略。客户端应仅将其暴露给明确由用户发起的命令。

如果线程已有一个活动回合,该命令会作为该回合的辅助操作运行,其格式化输出会注入到该回合的消息流中。如果线程处于空闲状态,app-server 会为该 shell 命令启动一个独立回合。

{ "method": "thread/shellCommand", "id": 26, "params": { "threadId": "thr_b", "command": "git status --short" } }
{ "id": 26, "result": {} }

清理后台终端

使用 thread/backgroundTerminals/clean 可停止与某个线程关联的所有正在运行的后台终端。此方法是实验性的,并要求 capabilities.experimentalApi = true

{ "method": "thread/backgroundTerminals/clean", "id": 27, "params": { "threadId": "thr_b" } }
{ "id": 27, "result": {} }

回滚最近的回合

使用 thread/rollback 可从内存上下文中移除最后 numTurns 条记录,并在 rollout 日志中持久化一个回滚标记。返回的 thread 包含回滚后填充的 turns

{ "method": "thread/rollback", "id": 28, "params": { "threadId": "thr_b", "numTurns": 1 } }
{ "id": 28, "result": { "thread": { "id": "thr_b", "name": "Bug bash notes", "ephemeral": false } } }

回合

input 字段接受一个项列表:

  • { "type": "text", "text": "Explain this diff" }
  • { "type": "image", "url": "https://.../design.png" }
  • { "type": "localImage", "path": "/tmp/screenshot.png" }

你可以按回合覆盖配置设置(model、effort、personality、cwd、沙箱策略、摘要)。指定后,这些设置会成为同一线程后续回合的默认值。outputSchema 仅适用于当前回合。对于 sandboxPolicy.type = "externalSandbox",请将 networkAccess 设置为 restrictedenabled;对于 workspaceWritenetworkAccess 仍为布尔值。

对于 turn/start.collaborationModesettings.developer_instructions: null 表示“对所选模式使用内置说明”,而不是清除模式说明。

沙箱读取访问(ReadOnlyAccess

sandboxPolicy 支持显式的读取访问控制:

  • readOnly:可选 access(默认为 { "type": "fullAccess" },或受限根目录)。
  • workspaceWrite:可选 readOnlyAccess(默认为 { "type": "fullAccess" },或受限根目录)。

受限读取访问的结构:

{
"type": "restricted",
"includePlatformDefaults": true,
"readableRoots": ["/Users/me/shared-read-only"]
}

在 macOS 上,includePlatformDefaults: true 会为 restricted-read 会话附加一组精心挑选的平台默认 Seatbelt 策略。这样可以提升工具兼容性,而不会宽泛地允许整个 /System

示例:

{ "type": "readOnly", "access": { "type": "fullAccess" } }
{
"type": "workspaceWrite",
"writableRoots": ["/Users/me/project"],
"readOnlyAccess": {
"type": "restricted",
"includePlatformDefaults": true,
"readableRoots": ["/Users/me/shared-read-only"]
},
"networkAccess": false
}

启动一个 turn

{ "method": "turn/start", "id": 30, "params": {
"threadId": "thr_123",
"input": [ { "type": "text", "text": "Run tests" } ],
"cwd": "/Users/me/project",
"approvalPolicy": "unlessTrusted",
"sandboxPolicy": {
"type": "workspaceWrite",
"writableRoots": ["/Users/me/project"],
"networkAccess": true
},
"model": "gpt-5.4",
"effort": "medium",
"summary": "concise",
"personality": "friendly",
"outputSchema": {
"type": "object",
"properties": { "answer": { "type": "string" } },
"required": ["answer"],
"additionalProperties": false
}
} }
{ "id": 30, "result": { "turn": { "id": "turn_456", "status": "inProgress", "items": [], "error": null } } }

向线程中注入条目

使用 thread/inject_items 将预先构建好的 Responses API 条目追加到已加载线程的 prompt 历史中,而无需启动用户 turn。这些条目会持久化到 rollout 中,并包含在后续的模型请求里。

{ "method": "thread/inject_items", "id": 31, "params": {
"threadId": "thr_123",
"items": [
{
"type": "message",
"role": "assistant",
"content": [{ "type": "output_text", "text": "Previously computed context." }]
}
]
} }
{ "id": 31, "result": {} }

引导一个活动中的 turn

使用 turn/steer 将更多用户输入追加到当前正在进行中的活动 turn。

  • 包含 expectedTurnId;它必须与活动 turn id 匹配。
  • 如果线程上没有活动 turn,请求会失败。
  • turn/steer 不会发出新的 turn/started 通知。
  • turn/steer 不接受 turn 级别的覆盖项(modelcwdsandboxPolicyoutputSchema)。
{ "method": "turn/steer", "id": 32, "params": {
"threadId": "thr_123",
"input": [ { "type": "text", "text": "Actually focus on failing tests first." } ],
"expectedTurnId": "turn_456"
} }
{ "id": 32, "result": { "turnId": "turn_456" } }

启动一个 turn(调用一个 skill)

通过在文本输入中包含 $,并同时添加一个 skill 输入条目来显式调用某个 skill。

{ "method": "turn/start", "id": 33, "params": {
"threadId": "thr_123",
"input": [
{ "type": "text", "text": "$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage." },
{ "type": "skill", "name": "skill-creator", "path": "/Users/me/.codex/skills/skill-creator/SKILL.md" }
]
} }
{ "id": 33, "result": { "turn": { "id": "turn_457", "status": "inProgress", "items": [], "error": null } } }

中断一个 turn

{ "method": "turn/interrupt", "id": 31, "params": { "threadId": "thr_123", "turnId": "turn_456" } }
{ "id": 31, "result": {} }

成功时,该 turn 会以 status: "interrupted" 结束。

Review

review/start 会为一个线程运行 Codex reviewer,并流式传输 review 条目。目标包括:

  • uncommittedChanges
  • baseBranch(与某个分支做 diff)
  • commit(review 某个特定 commit)
  • custom(自由格式说明)

使用 delivery: "inline"(默认)可在现有线程上运行 review,或使用 delivery: "detached" 分叉出一个新的 review 线程。

请求/响应示例:

{ "method": "review/start", "id": 40, "params": {
"threadId": "thr_123",
"delivery": "inline",
"target": { "type": "commit", "sha": "1234567deadbeef", "title": "Polish tui colors" }
} }
{ "id": 40, "result": {
"turn": {
"id": "turn_900",
"status": "inProgress",
"items": [
{ "type": "userMessage", "id": "turn_900", "content": [ { "type": "text", "text": "Review commit 1234567: Polish tui colors" } ] }
],
"error": null
},
"reviewThreadId": "thr_123"
} }

对于 detached review,使用 "delivery": "detached"。响应具有相同的结构,但 reviewThreadId 将是新 review 线程的 id(不同于原始的 threadId)。在流式传输 review turn 之前,服务器还会先为该新线程发出一个 thread/started 通知。

Codex 会像往常一样先流式发送 turn/started 通知,随后发送一个带有 enteredReviewMode 条目的 item/started

{
"method": "item/started",
"params": {
"item": {
"type": "enteredReviewMode",
"id": "turn_900",
"review": "current changes"
}
}
}

当 reviewer 完成时,服务器会发送 item/starteditem/completed,其中包含一个带有最终 review 文本的 exitedReviewMode 条目:

{
"method": "item/completed",
"params": {
"item": {
"type": "exitedReviewMode",
"id": "turn_900",
"review": "Looks solid overall..."
}
}
}

使用此通知在你的客户端中渲染 reviewer 输出。

进程执行

process/* 是一个实验性的显式进程控制 API。它要求 capabilities.experimentalApi = true,并且在 Codex 的 sandbox 之外运行。仅在你的客户端有意暴露无 sandbox 的本地进程控制时使用它。

使用 process/spawn 启动一个进程,并提供一个 processHandle,然后在 stdin、resize 和 kill 请求中使用该 handle。输出通过 process/outputDelta 通知流式传输,完成状态通过 process/exited 传输。

{ "method": "process/spawn", "id": 48, "params": {
"command": ["python3", "-m", "pytest", "-q"],
"processHandle": "pytest-1",
"cwd": "/Users/me/project",
"tty": true
} }
{ "id": 48, "result": {} }
{ "method": "process/outputDelta", "params": {
"processHandle": "pytest-1",
"stream": "stdout",
"deltaBase64": "Li4u"
} }
{ "method": "process/exited", "params": {
"processHandle": "pytest-1",
"exitCode": 0
} }

使用带有 deltaBase64closeStdin 或两者同时使用的 process/writeStdin 来发送输入。使用 process/resizePty 处理 PTY 大小调整事件,使用 process/kill 终止正在运行的进程。

命令执行

command/exec 会在服务器 sandbox 下运行单条命令(argv 数组),而无需创建线程。

{ "method": "command/exec", "id": 50, "params": {
"command": ["ls", "-la"],
"cwd": "/Users/me/project",
"sandboxPolicy": { "type": "workspaceWrite" },
"timeoutMs": 10000
} }
{ "id": 50, "result": { "exitCode": 0, "stdout": "...", "stderr": "" } }

如果你已经对服务器进程进行了 sandbox 处理,并希望 Codex 跳过其自身的 sandbox 强制措施,请使用 sandboxPolicy.type = "externalSandbox"。对于 external sandbox 模式,将 networkAccess 设为 restricted(默认)或 enabled。对于 readOnlyworkspaceWrite,使用与上文所示相同的可选 access / readOnlyAccess 结构。

注意:

  • 服务器会拒绝空的 command 数组。
  • sandboxPolicy 接受与 turn/start 相同的结构(例如 dangerFullAccessreadOnlyworkspaceWriteexternalSandbox)。
  • 省略时,timeoutMs 会回退到服务器默认值。
  • 为基于 PTY 的会话设置 tty: true,并在计划后续调用 command/exec/writecommand/exec/resizecommand/exec/terminate 时使用 processId
  • 设置 streamStdoutStderr: true 可在命令运行期间接收 command/exec/outputDelta 通知。

读取管理员要求(configRequirements/read

使用 configRequirements/read 检查从 requirements.toml 和/或 MDM 加载的生效管理员要求。

{ "method": "configRequirements/read", "id": 52, "params": {} }
{ "id": 52, "result": {
"requirements": {
"allowedApprovalPolicies": ["onRequest", "unlessTrusted"],
"allowedSandboxModes": ["readOnly", "workspaceWrite"],
"featureRequirements": {
"personality": true,
"unified_exec": false
},
"network": {
"enabled": true,
"allowedDomains": ["api.openai.com"],
"allowUnixSockets": ["/tmp/example.sock"],
"dangerouslyAllowAllUnixSockets": false
}
}
} }

当未配置任何要求时,result.requirementsnull。有关支持的键和值的详细信息,请参阅 requirements.toml 文档。

Windows sandbox 设置(windowsSandbox/setupStart

自定义 Windows 客户端可以异步触发 sandbox 设置,而不是在启动检查时阻塞。

{ "method": "windowsSandbox/setupStart", "id": 53, "params": { "mode": "elevated" } }
{ "id": 53, "result": { "started": true } }

App-server 会在后台启动设置,并在之后发出完成通知:

{
"method": "windowsSandbox/setupCompleted",
"params": { "mode": "elevated", "success": true, "error": null }
}

模式:

  • elevated - 运行提升权限的 Windows 沙箱设置路径。
  • unelevated - 运行旧版设置/预检路径。

文件系统

v2 文件系统 API 基于绝对路径运行。当客户端需要在文件或目录变更后使 UI 状态失效时,请使用 fs/watch

{ "method": "fs/watch", "id": 54, "params": {
"watchId": "0195ec6b-1d6f-7c2e-8c7a-56f2c4a8b9d1",
"path": "/Users/me/project/.git/HEAD"
} }
{ "id": 54, "result": { "path": "/Users/me/project/.git/HEAD" } }
{ "method": "fs/changed", "params": {
"watchId": "0195ec6b-1d6f-7c2e-8c7a-56f2c4a8b9d1",
"changedPaths": ["/Users/me/project/.git/HEAD"]
} }
{ "method": "fs/unwatch", "id": 55, "params": {
"watchId": "0195ec6b-1d6f-7c2e-8c7a-56f2c4a8b9d1"
} }
{ "id": 55, "result": {} }

监听某个文件时,会针对该文件路径发出 fs/changed,包括通过替换或重命名操作传递的更新。

事件

事件通知是由服务器发起的流,用于表示线程生命周期、turn 生命周期以及其中的条目。在你启动或恢复某个线程后,请持续读取当前传输流中的 thread/startedthread/archivedthread/unarchivedthread/closedthread/status/changedturn/*item/*serverRequest/resolved 通知。

选择退出通知

客户端可以通过在 initialize.params.capabilities.optOutNotificationMethods 中发送精确的方法名,按连接抑制特定通知。

  • 仅支持精确匹配:item/agentMessage/delta 只会抑制该方法本身。
  • 未知的方法名会被忽略。
  • 适用于当前的 thread/*turn/*item/* 和相关的 v2 通知。
  • 不适用于请求、响应或错误。

模糊文件搜索事件(实验性)

模糊文件搜索会话 API 会为每个查询发出通知:

  • fuzzyFileSearch/sessionUpdated - { sessionId, query, files },包含当前活动查询的当前匹配结果。
  • fuzzyFileSearch/sessionCompleted - { sessionId },在该查询的索引和匹配完成后发出一次。

Windows 沙箱设置事件

  • windowsSandbox/setupCompleted - { mode, success, error },在 windowsSandbox/setupStart 请求完成后发出。

Turn 事件

  • turn/started - { turn },包含 turn id、空的 items 以及 status: "inProgress"
  • turn/completed - { turn },其中 turn.statuscompletedinterruptedfailed;失败时会携带 { error: { message, codexErrorInfo?, additionalDetails? } }
  • turn/diff/updated - { threadId, turnId, diff },包含该 turn 中所有文件更改聚合后的最新 unified diff。
  • turn/plan/updated - { turnId, explanation?, plan },当 agent 分享或更改其计划时发出;每个 plan 条目都是 { step, status },其中 statuspendinginProgresscompleted
  • thread/tokenUsage/updated - 当前活动线程的用量更新。

turn/diff/updatedturn/plan/updated 当前即使在条目事件流存在时也会包含空的 items 数组。请使用 item/* 通知作为 turn 条目的权威来源。

条目

ThreadItem 是在 turn 响应和 item/* 通知中携带的带标签联合类型。常见条目类型包括:

  • userMessage - {id, content},其中 content 是用户输入列表(textimagelocalImage)。
  • agentMessage - {id, text, phase?},包含累积的 agent 回复。存在时,phase 使用 Responses API wire 值(commentaryfinal_answer)。
  • plan - {id, text},包含计划模式下提出的计划文本。请将 item/completed 中最终的 plan 条目视为权威结果。
  • reasoning - {id, summary, content},其中 summary 保存流式推理摘要,content 保存原始推理块。
  • commandExecution - {id, command, cwd, status, commandActions, aggregatedOutput?, exitCode?, durationMs?}
  • fileChange - {id, changes, status},描述建议的编辑;changes 列表中的每项为 {path, kind, diff}
  • mcpToolCall - {id, server, tool, status, arguments, result?, error?}
  • dynamicToolCall - {id, tool, arguments, status, contentItems?, success?, durationMs?},用于客户端执行的动态工具调用。
  • collabToolCall - {id, tool, status, senderThreadId, receiverThreadId?, newThreadId?, prompt?, agentStatus?}
  • webSearch - {id, query, action?},用于 agent 发出的网页搜索请求。
  • imageView - {id, path},在 agent 调用图像查看器工具时发出。
  • enteredReviewMode - {id, review},在审查者启动时发送。
  • exitedReviewMode - {id, review},在审查者完成时发出。
  • contextCompaction - {id},在 Codex 压缩会话历史时发出。

对于 webSearch.action,其 action type 可以是 searchquery?queries?)、openPageurl?)或 findInPageurl?pattern?)。

app server 已弃用旧版 thread/compacted 通知;请改用 contextCompaction 条目。

所有条目都会发出两个共享的生命周期事件:

  • item/started - 当新的工作单元开始时发出完整的 itemitem.id 与 delta 使用的 itemId 一致。
  • item/completed - 工作完成后发送最终的 item;请将其视为权威状态。

条目增量

  • item/agentMessage/delta - 追加 agent 消息的流式文本。
  • item/plan/delta - 流式传输提出的计划文本。最终的 plan 条目可能与拼接后的所有 delta 不完全一致。
  • item/reasoning/summaryTextDelta - 流式传输可读的推理摘要;当新的摘要段落开始时,summaryIndex 会递增。
  • item/reasoning/summaryPartAdded - 标记推理摘要各段之间的边界。
  • item/reasoning/textDelta - 流式传输原始推理文本(当模型支持时)。
  • item/commandExecution/outputDelta - 流式传输某个命令的 stdout/stderr;请按顺序追加这些 delta。
  • item/fileChange/outputDelta - 已弃用的兼容性通知,用于旧版 apply_patch 文本输出。当前 app-server 版本已不再发出此通知;请改用 fileChange 条目和 turn/diff/updated

错误

如果某个 turn 失败,服务器会发出一个 error 事件,其中包含 { error: { message, codexErrorInfo?, additionalDetails? } },然后以 status: "failed" 结束该 turn。当存在上游 HTTP 状态时,它会出现在 codexErrorInfo.httpStatusCode 中。

常见的 codexErrorInfo 值包括:

  • ContextWindowExceeded
  • UsageLimitExceeded
  • HttpConnectionFailed(上游 4xx/5xx 错误)
  • ResponseStreamConnectionFailed
  • ResponseStreamDisconnected
  • ResponseTooManyFailedAttempts
  • BadRequestUnauthorizedSandboxErrorInternalServerErrorOther

当存在上游 HTTP 状态时,服务器会在相关的 codexErrorInfo 变体上的 httpStatusCode 中转发它。

审批

根据用户的 Codex 设置,命令执行和文件更改可能需要审批。app-server 会向客户端发送由服务器发起的 JSON-RPC 请求,客户端则使用决策负载进行响应。

  • 命令执行决策:acceptacceptForSessiondeclinecancel,或 { "acceptWithExecpolicyAmendment": { "execpolicy_amendment": ["cmd", "..."] } }

  • 文件更改决策:acceptacceptForSessiondeclinecancel

  • 请求中包含 threadIdturnId —— 请使用它们将 UI 状态限定在当前活动会话范围内。

  • 服务器会恢复或拒绝该工作,并通过 item/completed 结束该条目。

命令执行审批

消息顺序:

  1. item/started 显示待处理的 commandExecution 条目,其中包含 commandcwd 和其他字段。
  2. item/commandExecution/requestApproval 包含 itemIdthreadIdturnId、可选的 reason、可选的 command、可选的 cwd、可选的 commandActions、可选的 proposedExecpolicyAmendment、可选的 networkApprovalContext 以及可选的 availableDecisions。当 initialize.params.capabilities.experimentalApi = true 时,负载还可以包含实验性的 additionalPermissions,用于描述请求的按命令粒度的沙箱访问权限。additionalPermissions 中的任何文件系统路径在 wire 上都是绝对路径。
  3. 客户端使用上述命令执行审批决策之一进行响应。
  4. serverRequest/resolved 确认待处理请求已被应答或清除。
  5. item/completed 返回最终的 commandExecution 条目,其中 status: completed | failed | declined

当存在 networkApprovalContext 时,提示针对的是受管控的网络访问(而不是一般的 shell 命令审批)。当前 v2 schema 会公开目标 hostprotocol;客户端应呈现网络专用提示,而不应依赖 command 是用户可理解的 shell 命令预览。

Codex 会按目标(host、protocol 和 port)对并发网络审批提示进行分组。因此,app-server 可能会发送一个提示来解除对同一目标的多个排队请求的阻塞,而同一 host 上的不同 port 会被分别处理。

文件更改审批

消息顺序:

  1. item/started 发出一个 fileChange 项,包含提议的 changesstatus: "inProgress"
  2. item/fileChange/requestApproval 包含 itemIdthreadIdturnId、可选的 reason 和可选的 grantRoot
  3. 客户端使用上述某一种文件更改审批决定进行响应。
  4. serverRequest/resolved 确认待处理请求已被响应或清除。
  5. item/completed 返回最终的 fileChange 项,其中 status: completed | failed | declined

tool/requestUserInput

当客户端响应 item/tool/requestUserInput 时,app-server 会发出带有 { threadId, requestId }serverRequest/resolved。如果在客户端响应之前,待处理请求因 turn 开始、turn 完成或 turn 中断而被清除,服务器也会为该清理发出相同的通知。

动态工具调用(实验性)

thread/start 上的 dynamicTools 以及对应的 item/tool/call 请求或响应流程是实验性 API。

动态工具名称和命名空间名称必须遵循 Responses API 的命名约束。避免使用内置 Codex 工具保留的命名空间名称。

当某个动态工具在一次 turn 期间被调用时,app-server 会发出:

  1. item/started,其中 item.type = "dynamicToolCall"status = "inProgress",以及 toolarguments
  2. 作为服务器请求发给客户端的 item/tool/call
  3. 客户端响应负载,其中包含返回的内容项。
  4. item/completed,其中 item.type = "dynamicToolCall"、最终 status,以及任意返回的 contentItemssuccess 值。

MCP 工具调用审批(apps)

App(连接器)工具调用也可以要求审批。当某个 app 工具调用具有副作用时,服务器可能会通过 tool/requestUserInput 请求审批,并提供诸如 AcceptDeclineCancel 之类的选项。破坏性工具注解始终会触发审批,即使该工具同时声明了权限更低的提示也是如此。如果用户拒绝或取消,则相关的 mcpToolCall 项会以错误完成,而不是运行该工具。

Skills

通过在用户文本输入中包含 $ 来调用一个 skill。添加一个 skill 输入项(推荐),这样服务器会注入完整的 skill 指令,而不是依赖模型去解析名称。

{
"method": "turn/start",
"id": 101,
"params": {
"threadId": "thread-1",
"input": [
{
"type": "text",
"text": "$skill-creator Add a new skill for triaging flaky CI."
},
{
"type": "skill",
"name": "skill-creator",
"path": "/Users/me/.codex/skills/skill-creator/SKILL.md"
}
]
}
}

如果省略 skill 项,模型仍会解析 $ 标记并尝试定位该 skill,这可能会增加延迟。

示例:

$skill-creator Add a new skill for triaging flaky CI and include step-by-step usage.

使用 skills/list 获取可用的 skills(可选地按 cwds 限定范围,并可带上 forceReload)。你也可以包含 perCwdExtraUserRoots,以便为特定 cwd 值将额外的绝对路径作为 user 范围进行扫描。App-server 会忽略其中 cwd 未出现在 cwds 中的条目。skills/list 可能会按每个 cwd 复用缓存结果;设置 forceReload: true 可从磁盘刷新。存在 SKILL.json 时,服务器会从中读取 interfacedependencies

{ "method": "skills/list", "id": 25, "params": {
"cwds": ["/Users/me/project", "/Users/me/other-project"],
"forceReload": true,
"perCwdExtraUserRoots": [
{
"cwd": "/Users/me/project",
"extraUserRoots": ["/Users/me/shared-skills"]
}
]
} }
{ "id": 25, "result": {
"data": [{
"cwd": "/Users/me/project",
"skills": [
{
"name": "skill-creator",
"description": "Create or update a Codex skill",
"enabled": true,
"interface": {
"displayName": "Skill Creator",
"shortDescription": "Create or update a Codex skill"
},
"dependencies": {
"tools": [
{
"type": "env_var",
"value": "GITHUB_TOKEN",
"description": "GitHub API token"
},
{
"type": "mcp",
"value": "github",
"transport": "streamable_http",
"url": "https://example.com/mcp"
}
]
}
}
],
"errors": []
}]
} }

当被监视的本地 skill 文件发生变化时,服务器还会发出 skills/changed 通知。将其视为失效信号,并在需要时使用当前参数重新运行 skills/list

按路径启用或禁用某个 skill:

{
"method": "skills/config/write",
"id": 26,
"params": {
"path": "/Users/me/.codex/skills/skill-creator/SKILL.md",
"enabled": false
}
}

Apps(连接器)

使用 app/list 获取可用的 apps。在 CLI/TUI 中,/apps 是面向用户的选择器;在自定义客户端中,直接调用 app/list。每个条目同时包含 isAccessible(用户可访问)和 isEnabled(在 config.toml 中启用),以便客户端区分安装/访问状态和本地启用状态。App 条目还可以包含可选的 brandingappMetadatalabels 字段。

{ "method": "app/list", "id": 50, "params": {
"cursor": null,
"limit": 50,
"threadId": "thread-1",
"forceRefetch": false
} }
{ "id": 50, "result": {
"data": [
{
"id": "demo-app",
"name": "Demo App",
"description": "Example connector for documentation.",
"logoUrl": "https://example.com/demo-app.png",
"logoUrlDark": null,
"distributionChannel": null,
"branding": null,
"appMetadata": null,
"labels": null,
"installUrl": "https://chatgpt.com/apps/demo-app/demo-app",
"isAccessible": true,
"isEnabled": true
}
],
"nextCursor": null
} }

如果提供了 threadId,app 功能门控(features.apps)会使用该 thread 的配置快照。若省略,则 app-server 使用最新的全局配置。

app/list 会在可访问 apps 和目录 apps 都加载完成后返回。设置 forceRefetch: true 可绕过 app 缓存并获取最新数据。只有当刷新成功时,缓存条目才会被替换。

每当任一来源(可访问 apps 或目录 apps)完成加载时,服务器还会发出 app/list/updated 通知。每个通知都包含最新合并后的 app 列表。

{
"method": "app/list/updated",
"params": {
"data": [
{
"id": "demo-app",
"name": "Demo App",
"description": "Example connector for documentation.",
"logoUrl": "https://example.com/demo-app.png",
"logoUrlDark": null,
"distributionChannel": null,
"branding": null,
"appMetadata": null,
"labels": null,
"installUrl": "https://chatgpt.com/apps/demo-app/demo-app",
"isAccessible": true,
"isEnabled": true
}
]
}
}

通过在文本输入中插入 $ 并添加带有 app:// 路径的 mention 输入项来调用一个 app(推荐)。

{
"method": "turn/start",
"id": 51,
"params": {
"threadId": "thread-1",
"input": [
{
"type": "text",
"text": "$demo-app Pull the latest updates from the team."
},
{
"type": "mention",
"name": "Demo App",
"path": "app://demo-app"
}
]
}
}

app 设置的配置 RPC 示例

使用 config/readconfig/value/writeconfig/batchWrite 查看或更新 config.toml 中的 app 控件。

读取生效的 app 配置结构(包括 _default 和每个工具的覆盖项):

{ "method": "config/read", "id": 60, "params": { "includeLayers": false } }
{ "id": 60, "result": {
"config": {
"apps": {
"_default": {
"enabled": true,
"destructive_enabled": true,
"open_world_enabled": true
},
"google_drive": {
"enabled": true,
"destructive_enabled": false,
"default_tools_approval_mode": "prompt",
"tools": {
"files/delete": { "enabled": false, "approval_mode": "approve" }
}
}
}
}
} }

更新单个 app 设置:

{
"method": "config/value/write",
"id": 61,
"params": {
"keyPath": "apps.google_drive.default_tools_approval_mode",
"value": "prompt",
"mergeStrategy": "replace"
}
}

以原子方式应用多个 app 编辑:

{
"method": "config/batchWrite",
"id": 62,
"params": {
"edits": [
{
"keyPath": "apps._default.destructive_enabled",
"value": false,
"mergeStrategy": "upsert"
},
{
"keyPath": "apps.google_drive.tools.files/delete.approval_mode",
"value": "approve",
"mergeStrategy": "upsert"
}
]
}
}

检测并导入外部 agent 配置

使用 externalAgentConfig/detect 发现可迁移的外部 agent 工件,然后将选定条目传给 externalAgentConfig/import

检测示例:

{ "method": "externalAgentConfig/detect", "id": 63, "params": {
"includeHome": true,
"cwds": ["/Users/me/project"]
} }
{ "id": 63, "result": {
"items": [
{
"itemType": "AGENTS_MD",
"description": "将 /Users/me/project/CLAUDE.md 导入到 /Users/me/project/AGENTS.md。",
"cwd": "/Users/me/project"
},
{
"itemType": "SKILLS",
"description": "将技能文件夹从 /Users/me/.claude/skills 复制到 /Users/me/.agents/skills。",
"cwd": null
}
]
} }

导入示例:

{ "method": "externalAgentConfig/import", "id": 64, "params": {
"migrationItems": [
{
"itemType": "AGENTS_MD",
"description": "将 /Users/me/project/CLAUDE.md 导入到 /Users/me/project/AGENTS.md。",
"cwd": "/Users/me/project"
}
]
} }
{ "id": 64, "result": {} }

当请求包含插件导入时,服务器会在导入完成后发出 externalAgentConfig/import/completed。该通知可能会在响应之后立即到达,也可能会在后台远程导入完成后到达。

支持的 itemType 值为 AGENTS_MDCONFIGSKILLSPLUGINS, 以及 MCP_SERVER_CONFIG。对于 PLUGINS 项,details.plugins 会列出每个 marketplaceName 以及 Codex 可尝试迁移的 pluginNames。检测仅返回仍有工作需要完成的项。例如,当 AGENTS.md 已存在且非空时,Codex 会跳过 AGENTS 迁移,而技能导入不会覆盖已有的技能目录。

.claude/settings.json 检测插件时,Codex 会从 extraKnownMarketplaces 读取已配置的 marketplace 源。如果 enabledPlugins 包含来自 claude-plugins-official 的插件,但缺少该 marketplace 源, Codex 会推断 anthropics/claude-plugins-official 为其源。

认证端点

JSON-RPC 的 auth/account 接口公开了请求/响应方法以及服务器主动发起的通知(无 id)。使用这些接口可确定认证状态、开始或取消登录、登出、检查 ChatGPT 速率限制,以及通知工作区所有者关于额度耗尽或使用限制的情况。

认证模式

Codex 支持以下认证模式。account/updated.authMode 显示当前激活的模式,并在可用时包含当前 ChatGPT 的 planTypeaccount/read 也会报告账户和套餐详情。

  • API key (apikey) - 调用方提供一个 OpenAI API key,使用 type: "apiKey",Codex 会将其存储用于 API 请求。
  • ChatGPT 托管(chatgpt - Codex 负责 ChatGPT OAuth 流程、持久化 token,并自动刷新。浏览器流程使用 type: "chatgpt" 启动,设备码流程使用 type: "chatgptDeviceCode" 启动。
  • ChatGPT 外部 token(chatgptAuthTokens - 实验性功能,面向已自行管理用户 ChatGPT 认证生命周期的宿主应用。宿主应用直接提供 accessTokenchatgptAccountId 和可选的 chatgptPlanType,并且必须在被请求时刷新 token。

API 概览

  • account/read - 获取当前账户信息;可选择刷新 token。
  • account/login/start - 开始登录(apiKeychatgptchatgptDeviceCode 或实验性的 chatgptAuthTokens)。
  • account/login/completed(通知)- 在一次登录尝试结束时发出(成功或错误)。
  • account/login/cancel - 通过 loginId 取消一个待处理的托管 ChatGPT 登录。
  • account/logout - 登出;会触发 account/updated
  • account/updated(通知)- 每当认证模式变化时发出(authModeapikeychatgptchatgptAuthTokensnull),并在可用时包含 planType
  • account/chatgptAuthTokens/refresh(服务器请求)- 在授权错误后,请求新的外部管理 ChatGPT token。
  • account/rateLimits/read - 获取 ChatGPT 速率限制。
  • account/rateLimits/updated(通知)- 每当用户的 ChatGPT 速率限制发生变化时发出。
  • account/sendAddCreditsNudgeEmail - 请求 ChatGPT 向工作区所有者发送邮件,提示额度已耗尽或已达到使用限制。
  • mcpServer/oauthLogin/completed(通知)- 在 mcpServer/oauth/login 流程结束后发出;负载包含 { name, success, error? }
  • mcpServer/startupStatus/updated(通知)- 当已加载线程中已配置的 MCP 服务器启动状态发生变化时发出;负载包含 { name, status, error }

1) 检查认证状态

请求:

{ "method": "account/read", "id": 1, "params": { "refreshToken": false } }

响应示例:

{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": false } }
{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": true } }
{
"id": 1,
"result": { "account": { "type": "apiKey" }, "requiresOpenaiAuth": true }
}
{
"id": 1,
"result": {
"account": {
"type": "chatgpt",
"email": "user@example.com",
"planType": "pro"
},
"requiresOpenaiAuth": true
}
}

字段说明:

  • refreshToken(boolean):设为 true 可在托管 ChatGPT 模式下强制刷新 token。在外部 token 模式(chatgptAuthTokens)下,app-server 会忽略此标志。
  • requiresOpenaiAuth 反映当前激活的提供方;当其为 false 时,Codex 可在没有 OpenAI 凭证的情况下运行。

2) 使用 API key 登录

  1. 发送:

    {
    "method": "account/login/start",
    "id": 2,
    "params": { "type": "apiKey", "apiKey": "sk-..." }
    }
  2. 预期:

    { "id": 2, "result": { "type": "apiKey" } }
  3. 通知:

    {
    "method": "account/login/completed",
    "params": { "loginId": null, "success": true, "error": null }
    }
    {
    "method": "account/updated",
    "params": { "authMode": "apikey", "planType": null }
    }

3) 使用 ChatGPT 登录(浏览器流程)

  1. 启动:

    { "method": "account/login/start", "id": 3, "params": { "type": "chatgpt" } }
    {
    "id": 3,
    "result": {
    "type": "chatgpt",
    "loginId": "<uuid>",
    "authUrl": "https://chatgpt.com/...&redirect_uri=http%3A%2F%2Flocalhost%3A<port>%2Fauth%2Fcallback"
    }
    }
  2. 在浏览器中打开 authUrl;app-server 托管本地回调。

  3. 等待通知:

    {
    "method": "account/login/completed",
    "params": { "loginId": "<uuid>", "success": true, "error": null }
    }
    {
    "method": "account/updated",
    "params": { "authMode": "chatgpt", "planType": "plus" }
    }

3b) 使用 ChatGPT 登录(设备码流程)

当你的客户端负责登录流程,或浏览器回调不稳定时,使用此流程。

  1. 启动:

    {
    "method": "account/login/start",
    "id": 4,
    "params": { "type": "chatgptDeviceCode" }
    }
    {
    "id": 4,
    "result": {
    "type": "chatgptDeviceCode",
    "loginId": "<uuid>",
    "verificationUrl": "https://auth.openai.com/codex/device",
    "userCode": "ABCD-1234"
    }
    }
  2. 向用户显示 verificationUrluserCode;前端负责 UX。

  3. 等待通知:

    {
    "method": "account/login/completed",
    "params": { "loginId": "<uuid>", "success": true, "error": null }
    }
    {
    "method": "account/updated",
    "params": { "authMode": "chatgpt", "planType": "plus" }
    }

3c) 使用外部管理的 ChatGPT token(chatgptAuthTokens)登录

仅当宿主应用负责用户 ChatGPT 认证生命周期并直接提供 token 时,才使用此实验性模式。客户端在使用此登录类型前,必须在 initialize 期间设置 capabilities.experimentalApi = true

  1. 发送:

    {
    "method": "account/login/start",
    "id": 7,
    "params": {
    "type": "chatgptAuthTokens",
    "accessToken": "<jwt>",
    "chatgptAccountId": "org-123",
    "chatgptPlanType": "business"
    }
    }
  2. 预期:

    { "id": 7, "result": { "type": "chatgptAuthTokens" } }
  3. 通知:

    {
    "method": "account/login/completed",
    "params": { "loginId": null, "success": true, "error": null }
    }
    {
    "method": "account/updated",
    "params": { "authMode": "chatgptAuthTokens", "planType": "business" }
    }

当服务器收到 401 Unauthorized 时,可能会向宿主应用请求刷新后的 token:

{
"method": "account/chatgptAuthTokens/refresh",
"id": 8,
"params": { "reason": "unauthorized", "previousAccountId": "org-123" }
}
{ "id": 8, "result": { "accessToken": "<jwt>", "chatgptAccountId": "org-123", "chatgptPlanType": "business" } }

服务器会在成功收到刷新响应后重试原始请求。请求大约会在 10 秒后超时。

4) 取消一次 ChatGPT 登录

{ "method": "account/login/cancel", "id": 4, "params": { "loginId": "<uuid>" } }
{ "method": "account/login/completed", "params": { "loginId": "<uuid>", "success": false, "error": "..." } }

5) 登出

{ "method": "account/logout", "id": 5 }
{ "id": 5, "result": {} }
{ "method": "account/updated", "params": { "authMode": null, "planType": null } }

6) 速率限制(ChatGPT)

{ "method": "account/rateLimits/read", "id": 6 }
{ "id": 6, "result": {
"rateLimits": {
"limitId": "codex",
"limitName": null,
"primary": { "usedPercent": 25, "windowDurationMins": 15, "resetsAt": 1730947200 },
"secondary": null,
"rateLimitReachedType": null
},
"rateLimitsByLimitId": {
"codex": {
"limitId": "codex",
"limitName": null,
"primary": { "usedPercent": 25, "windowDurationMins": 15, "resetsAt": 1730947200 },
"secondary": null,
"rateLimitReachedType": null
},
"codex_other": {
"limitId": "codex_other",
"limitName": "codex_other",
"primary": { "usedPercent": 42, "windowDurationMins": 60, "resetsAt": 1730950800 },
"secondary": null,
"rateLimitReachedType": null
}
}
} }
{ "method": "account/rateLimits/updated", "params": {
"rateLimits": {
"limitId": "codex",
"primary": { "usedPercent": 31, "windowDurationMins": 15, "resetsAt": 1730948100 }
}
} }

字段说明:

  • rateLimits 是向后兼容的单桶视图。
  • rateLimitsByLimitId(如果存在)是多桶视图,按计量的 limit_id 作为键(例如 codex)。
  • limitId 是计量桶标识符。
  • limitName 是桶的可选用户可见标签。
  • usedPercent 是配额窗口内的当前使用百分比。
  • windowDurationMins 是配额窗口长度。
  • resetsAt 是下一次重置的 Unix 时间戳(秒)。
  • 当服务器返回与某个桶关联的 ChatGPT 套餐时,会包含 planType
  • 当服务器返回剩余工作区额度详情时,会包含 credits
  • rateLimitReachedType 用于标识服务器分类的限制状态(当某项限制已被触发时)。

7) 将限制情况通知工作区所有者

使用 account/sendAddCreditsNudgeEmail 在额度耗尽或已达到使用限制时,请求 ChatGPT 向工作区所有者发送电子邮件。

{ "method": "account/sendAddCreditsNudgeEmail", "id": 7, "params": { "creditType": "credits" } }
{ "id": 7, "result": { "status": "sent" } }

当工作区额度耗尽时使用 creditType: "credits",当工作区已达到使用限制时使用 creditType: "usage_limit"。如果最近已经通知过所有者,响应状态将为 cooldown_active