diff --git a/README.md b/README.md
index e7ed041..ec3d3fa 100644
--- a/README.md
+++ b/README.md
@@ -72,8 +72,9 @@ agent 循环 + 沙箱 + 工具 + MCP + skills + 线程持久化
agent 循环、沙箱、工具执行、上下文压缩、线程存储,全部是官方
codex 二进制里的,不是我复刻的简化版。
-实测版本:`codex-cli 0.164.0-alpha.9`,协议定义就是从它
-导出的,见 `protocol/`。
+实测版本:`codex-cli 0.166.0`,协议定义就是从它
+导出的,见 `protocol/`(2026-11-05 由 0.166.0 二进制重导,见
+`docs/upstream-166-alignment.md`)。
## 目录
diff --git a/config.example.toml b/config.example.toml
index fae8c8e..9cad6d5 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -36,6 +36,12 @@ daemon_auto_start = false
# Skills / 插件:放 ~/.codex/skills 或用 `codex plugin` 管理,
# app-server 会在 thread 里按需加载,不用在代码里硬编码。
+# 想停用某个技能又不想删文件时,用 [[skills.config]](只写一条
+# 选择器:path 匹配单个 SKILL.md,name 匹配同名全部技能;多条
+# 匹配时最后一条胜出):
+# [[skills.config]]
+# name = "software-engineering"
+# enabled = false
# 项目指令:每个仓库根放 AGENTS.md,thread 启动时自动合并读取,
# 依次是 ~/.codex/AGENTS.md、仓库根、当前子目录,越往下优先级越高。
diff --git a/docs/upstream-166-alignment.md b/docs/upstream-166-alignment.md
new file mode 100644
index 0000000..b86fd15
--- /dev/null
+++ b/docs/upstream-166-alignment.md
@@ -0,0 +1,130 @@
+# 上游对齐:openai/codex 稳定版 0.166.0(核查日 2026-11-05)
+
+结论先说:**已用 0.166.0 官方二进制重导 schema 并验证通过;本批只改
+宿主侧 pin、文档、schema 与映射测试,不动任何生产二进制。**
+
+生产二进制更换(/usr/local/bin/codex、172、170、Docker 镜像内
+二进制)是部署动作,需要用户另外点头 + 窗口,不在本批;本批的
+验证边界是本地 scratch 验证(见下文“验证”),不是生产换装实证。
+
+## 基线与上游位置
+
+- 本项目此前锚定:`codex-cli 0.164.0-alpha.9`(2026-10-25 实测,
+ `protocol/` 的 schema 就是从它用
+ `codex app-server generate-json-schema` 导出的;见
+ `docs/upstream-164-alignment.md`)。
+- 上游稳定版:`0.165.0`(2026-10-27)、`0.166.0`(2026-10-29,
+ 当前最新稳定版)。本批一次跨两个稳定版锚定到 0.166.0,不
+ 在 0.165.0 停留(0.165.0 的内容已全含在 0.166.0 里)。
+- 上游 alpha 线(不作为目标):0.167.0-alpha.5 与 0.168.0-alpha.1
+ (均 2026-11-03),无 0.167/0.168 稳定版。按 164 文档立过的
+ 原则只记录、不跟进;alpha 跟踪办法见批总结,不进本文档。
+- 上游来源:https://github.com/openai/codex/releases(npm 官方包
+ `@openai/codex@0.166.0` + 平台包 `@openai/codex@0.166.0-linux-x64`,
+ npm integrity sha512 已验)。
+
+## 稳定版 0.166.0 改动 → 本项目状态
+
+| 上游改动 | 对宿主层的影响 | 结论 |
+|---|---|---|
+| app-server 线程启动/关闭竞态修复、已完成 runtime 及时释放(#57787、#57788) | 宿主 host.py 是长循环、一个 app-server 进程托管多 thread,直接受益 | 升级收益,无需改代码;重测 thread 生命周期一项(已做,见验证) |
+| resume/fork 时保留已确认的 approval 与权限设置(#57227) | 宿主 resume/fork 流程不再被重置坑绊 | 升级收益,无需改代码 |
+| 独立 executor 随规范 CLI 与 app-server 发布包一起发(#57837) | 实查:不是新文件,是二进制内 `exec-server` 子命令;三件套文件形态不变,宿主只驱动 `app-server` | 不影响安装形态,无需改代码 |
+| GPT-6.1 Terra 进入 bundled、Vertex AI、Bedrock 三个目录(#57293);Vertex AI provider(0.165.0 #56265) | 宿主模型走中转路由,引擎版本不卡 Terra 可用性;内置列表回退路径 0.165+ 才含 Terra | 不相关,无需改代码 |
+| `codex queue --resume`(0.165.0 #57232) | 宿主任务队列自建(host.py 循环 + worker_sweep admin CLI),不依赖上游 queue | 不相关,功能等价但实现无关 |
+| `codex doctor --network-only`(#57606) | 运维诊断工具,不是宿主运行路径 | 观察项,不接 |
+| 键盘 transcript 选择、Markdown 渲染一致化、安全拦截说明 | 都是 TUI 交互面,app-server 协议与宿主层不依赖它们 | 不相关,无需改代码 |
+| memory v2 对新 profile 默认开启 + 外部记忆导入(0.165.0) | 宿主 auto_memory 为自建租户记忆,未对齐上游 memory v2 格式/导入 | 不对齐,见对齐表 A10;本批不动 |
+| 实验性 attached thread forks(0.165.0 #56351) | 只在 `--experimental` schema 里出现(`ThreadForkParams.attachToSource`),默认 schema 无;且 attached fork 在普通 thread/list 里不可见,与宿主“fork 即新建 worktree 的可见线程”模型冲突 | 不接,评估见批总结;本批不写代码 |
+| 构建基线 Node.js 24.22.1、Rust 1.95.0 | 与本机 Node 24.22.1 一致;宿主是 Python,不编译 Rust | 不相关 |
+
+## schema 重导结果(0.166.0 二进制实导,非树对比)
+
+导出方式:`codex app-server generate-json-schema --out
`,
+原样取其中两 bundle 替换 `protocol/`(v1 + v2,与旧形态一致,
+不引入 per-type 散文件)。
+
+| 对比项 | alpha.9 时代 bundle | 0.166.0 重导 bundle | 结论 |
+|---|---|---|---|
+| v1 bundle 定义数 | 83 | 83 | 仅 ClientRequest 变(新增 feedback/save 一个方法) |
+| v2 bundle 定义数 | 665 | 677 | +12 定义、0 删除、16 个定义有改动 |
+| v2 新增定义 | — | AccountSession、AccountSessionWorkspace(Kind)、ApplicationStorageRequirements、FeedbackSaveParams/Response、ModelProviderError、OtelExporter、OtelHttpProtocol、OtelRequirements、OtelTlsConfig、OtelToolResultLogConfig | 均为新能力/新遥测,无 host 用项 |
+| v2 改动定义 | — | Account、AdditionalContextEntry、ApplicationRequirements、ClientRequest、CodexErrorInfo、ConfigRequirements、HookMetadata、HookRunSummary、HooksListParams、NetworkRequirements、ProcessExitedNotification、ReviewDelivery、ReviewStartParams、ReviewStartResponse、ThreadItem、ThreadUsage | 除 ReviewDelivery 外全是新增可选字段或描述改动(见下) |
+| 方法集 | ClientRequest 105 | ClientRequest 106(+feedback/save) | 0 删除;ServerRequest 10→10、通知 85→85 全不变 |
+| host 方法 params 引用 | — | 21 个 host 方法的 params 引用全部未变 | 零漂移 |
+
+唯一语义变化:`ReviewDelivery` 枚举从 `["inline", "detached"]`
+变为 `["inline"]`(detached 已移除),`ReviewStartParams.delivery`
+与 `ReviewStartResponse.reviewThreadId` 的描述同步改写。宿主早已
+不发 detached(`host.py start_review` 注释“不发 detached(已废弃)”、
+`cloud/review.py` 同款说明),故无影响;其余改动如
+`ThreadItem` 新增 `completedAtMs`/`startedAtMs`(可空)、
+`ThreadUsage` 新增 `nativeUsageUsdMicros`(可空)、
+`CodexErrorInfo` 新增 `modelNotFound`/`policyCapacityUnavailable`
+均为向后兼容加法。
+
+方法数勘误:2026-11-05 对齐表写“17 个方法/通知”并不准确。
+实查 host.py 触及 21 个方法字符串:18 个发出请求(含
+initialize 握手)+ 3 个入站通知;对齐表列出的 18 项漏了
+initialize、app/list、mcpServerStatus/list 三个。
+`tests/test_host_protocol_mapping.py` 以实查 21 项为准钉死。
+
+## 三件套核对(0.166.0 linux-x64 平台包实查)
+
+- `bin/codex`(319,346,888 字节,`codex-cli 0.166.0`)、
+ `bin/codex-code-mode-host`(77,997,328 字节)、
+ `codex-resources/bwrap`(529,776 字节)三件齐全,成套。
+- 包内另有 voice bundle(`codex-voice-host` 及 gstreamer 库,
+ 语音为刻意不做项,不理会)与 `codex-path/rg`,均不影响三件套。
+- 0.166.0 发布包不改变安装形态:宿主的 `bin/codex` 优先、
+ `CODEX_BIN` 环境变量、`PATH` 回退三档逻辑(`host.py
+ default_codex_bin`、`start.sh`)无需改动。
+
+## 验证(本地 scratch 验证,无模型调用)
+
+- 沙箱 smoke:scratch 三件套布局下 `codex sandbox echo`
+ 成功返回,exit 0(bwrap 配对工作)。
+- app-server smoke:仓库自带 `client.py --list-threads` 对
+ scratch 0.166.0 二进制跑通:initialize 成功(codexHome 为
+ scratch 目录、platform=linux)、thread/list 返回空数据,exit 0。
+- 技能发现复核:0.166.0 下 `/skills/`(我方播种
+ 位置)与 `$HOME/.agents/skills/`(文档 USER 位置)放的测试
+ 技能均被 `skills/list` 发现(scope=user、enabled=true),
+ 故播种机制无需改动;系统技能(skill-creator 等 5 个)照常出现。
+- 方法映射重测:`tests/test_host_protocol_mapping.py`(21 项
+ 钉死)+ 完整三套件(数见批总结)。
+
+## 本批实际改动(只改宿主侧,不动官方二进制)
+
+1. `protocol/`:两 bundle 替换为 0.166.0 二进制原样导出
+ (749,825 + 646,070 字节)。
+2. `README.md`:实测版本 pin 从 `0.164.0-alpha.9` 改为
+ `0.166.0`(验证通过后才改,见批总结;不是提前改 pin 充数)。
+3. `start.sh`:三件套注释版本号同步为 0.166.0(仅注释)。
+4. `config.example.toml`:补 `[[skills.config]]` 禁用语义的
+ 注释示例(仅注释,不改任何生效配置)。
+5. `tests/test_host_protocol_mapping.py`:新增,21 项钉死。
+6. 本文件:作为 0.166.0 的对齐记录,后续每次上游稳定版发布时
+ 按同一张表复核。
+
+## 明确不做的事(诚实边界)
+
+- **不换任何生产/服务器二进制。** `/usr/local/bin/codex`、
+ 172、170、Docker 镜像内二进制一律不动;本批二进制只活在
+ scratch 目录,批后清理。
+- **不追 alpha。** 0.167/0.168 alpha 只记录、不采用。
+- **不改播种机制。** 0.166.0 下 `/skills/` 仍被
+ 发现,无需改 `skill_seed.py`。
+- **不接 attached forks。** 实验性、仅 `--experimental`
+ schema、与宿主可观测模型冲突;重估触发条件见批总结。
+- **不改阈值、不改 #238 auto_apply、不改 #247 语义、不改
+ v1.1 契约、不改 SKILL.md 第五项。**
+- **不合并、不部署、不推送。** 本批留在隔离 worktree 的
+ `codex-opensource-align2` 分支上;live 仍是 aba06d9,不碰。
+
+## 下一步(需要用户定)
+
+1. 是否把生产二进制从 alpha.9 换到 0.166.0(主机部署动作,
+ 需窗口 + 重跑 resume / goal / 成本实弹)?那一步与控制面
+ 部署是两件分开的决策,不可悄悄混进一次部署。
+2. gapfill2 线是否合并(B1)仍待用户;本批不替用户定。
diff --git a/protocol/codex_app_server_protocol.schemas.json b/protocol/codex_app_server_protocol.schemas.json
index 9381414..afeb276 100644
--- a/protocol/codex_app_server_protocol.schemas.json
+++ b/protocol/codex_app_server_protocol.schemas.json
@@ -2394,6 +2394,30 @@
"title": "Feedback/uploadRequest",
"type": "object"
},
+ {
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/v2/RequestId"
+ },
+ "method": {
+ "enum": [
+ "feedback/save"
+ ],
+ "title": "Feedback/saveRequestMethod",
+ "type": "string"
+ },
+ "params": {
+ "$ref": "#/definitions/v2/FeedbackSaveParams"
+ }
+ },
+ "required": [
+ "id",
+ "method",
+ "params"
+ ],
+ "title": "Feedback/saveRequest",
+ "type": "object"
+ },
{
"description": "Execute a standalone command (argv vector) under the server's sandbox.",
"properties": {
@@ -6781,6 +6805,13 @@
],
"title": "ChatgptAccountType",
"type": "string"
+ },
+ "workspaceName": {
+ "description": "Display name of the selected ChatGPT workspace, when available.",
+ "type": [
+ "string",
+ "null"
+ ]
}
},
"required": [
@@ -6810,6 +6841,23 @@
],
"title": "AmazonBedrockAccount",
"type": "object"
+ },
+ {
+ "description": "Google Cloud Vertex AI with externally managed credentials.",
+ "properties": {
+ "type": {
+ "enum": [
+ "gcp"
+ ],
+ "title": "GcpAccountType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "title": "GcpAccount",
+ "type": "object"
}
]
},
@@ -6871,6 +6919,103 @@
],
"type": "string"
},
+ "AccountSession": {
+ "properties": {
+ "displayName": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "email": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imageUrl": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "isActive": {
+ "type": "boolean"
+ },
+ "lastUsedAt": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "selectedWorkspaceAccountId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "sessionId": {
+ "type": "string"
+ },
+ "userId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "workspaces": {
+ "items": {
+ "$ref": "#/definitions/v2/AccountSessionWorkspace"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "isActive",
+ "lastUsedAt",
+ "sessionId",
+ "workspaces"
+ ],
+ "type": "object"
+ },
+ "AccountSessionWorkspace": {
+ "properties": {
+ "accountId": {
+ "type": "string"
+ },
+ "imageUrl": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/AccountSessionWorkspaceKind"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "accountId"
+ ],
+ "type": "object"
+ },
+ "AccountSessionWorkspaceKind": {
+ "enum": [
+ "personal",
+ "workspace"
+ ],
+ "type": "string"
+ },
"AccountTokenUsageDailyBucket": {
"properties": {
"startDate": {
@@ -6993,6 +7138,13 @@
"kind": {
"$ref": "#/definitions/v2/AdditionalContextKind"
},
+ "name": {
+ "description": "Optional name for untrusted context, used to label its tool output.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"value": {
"type": "string"
}
@@ -7756,6 +7908,42 @@
"type": "null"
}
]
+ },
+ "storage": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/ApplicationStorageRequirements"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "ApplicationStorageRequirements": {
+ "description": "Managed storage settings for new Desktop workspaces, not filesystem access restrictions.",
+ "properties": {
+ "defaultProjectParent": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/LegacyAppPathString"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "projectlessWorkspaceRoot": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/LegacyAppPathString"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object"
@@ -8502,6 +8690,8 @@
"rateLimitExceeded",
"flexUnavailable",
"serverOverloaded",
+ "modelNotFound",
+ "policyCapacityUnavailable",
"cyberPolicy",
"misalignmentPolicyViolation",
"tooManyDenials",
@@ -10216,6 +10406,16 @@
}
]
},
+ "otel": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelRequirements"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"sqliteHome": {
"type": [
"string",
@@ -11807,6 +12007,85 @@
},
"type": "object"
},
+ "FeedbackSaveParams": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "properties": {
+ "classification": {
+ "type": "string"
+ },
+ "extraLogFiles": {
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "includeLogs": {
+ "type": "boolean"
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "savePath": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/v2/LegacyAppPathString"
+ }
+ ],
+ "description": "Absolute, previously unused ZIP path on the app-server host. The caller owns this file, including after a disconnected RPC."
+ },
+ "tags": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "threadId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "classification",
+ "savePath"
+ ],
+ "title": "FeedbackSaveParams",
+ "type": "object"
+ },
+ "FeedbackSaveResponse": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "properties": {
+ "promptHash": {
+ "description": "Same prompt hash as feedback/upload; null when prompt metadata is absent.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "savedPath": {
+ "$ref": "#/definitions/v2/LegacyAppPathString"
+ },
+ "threadId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "savedPath",
+ "threadId"
+ ],
+ "title": "FeedbackSaveResponse",
+ "type": "object"
+ },
"FeedbackUploadParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -13455,6 +13734,13 @@
"enabled": {
"type": "boolean"
},
+ "environmentId": {
+ "description": "The owning execution environment, if this hook came from its requirements.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"eventName": {
"$ref": "#/definitions/v2/HookEventName"
},
@@ -13622,7 +13908,7 @@
"default": "unknown"
},
"sourcePath": {
- "$ref": "#/definitions/v2/AbsolutePathBuf"
+ "$ref": "#/definitions/v2/LegacyAppPathString"
},
"startedAt": {
"format": "int64",
@@ -13743,11 +14029,18 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cwds": {
- "description": "When empty, defaults to the current session working directory.",
+ "description": "Working directories to inspect when no thread is specified. An empty list uses the session cwd.",
"items": {
"type": "string"
},
"type": "array"
+ },
+ "threadId": {
+ "description": "List a loaded thread's hooks, including its selected environments. Omit `cwds` in this mode.",
+ "type": [
+ "string",
+ "null"
+ ]
}
},
"title": "HooksListParams",
@@ -15818,6 +16111,81 @@
"title": "ModelProviderCapabilitiesReadResponse",
"type": "object"
},
+ "ModelProviderError": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Structured JSON-RPC error data. Unknown errors must retain the client's normal fallback.",
+ "oneOf": [
+ {
+ "properties": {
+ "currentModelProvider": {
+ "type": "string"
+ },
+ "modelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderMismatch"
+ ],
+ "title": "ModelProviderMismatchv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "currentModelProvider",
+ "modelProvider",
+ "type"
+ ],
+ "title": "ModelProviderMismatchv2::ModelProviderError",
+ "type": "object"
+ },
+ {
+ "properties": {
+ "modelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderNotConfigured"
+ ],
+ "title": "ModelProviderNotConfiguredv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "modelProvider",
+ "type"
+ ],
+ "title": "ModelProviderNotConfiguredv2::ModelProviderError",
+ "type": "object"
+ },
+ {
+ "properties": {
+ "modelProvider": {
+ "type": "string"
+ },
+ "requiredModelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderPolicyConflict"
+ ],
+ "title": "ModelProviderPolicyConflictv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "modelProvider",
+ "requiredModelProvider",
+ "type"
+ ],
+ "title": "ModelProviderPolicyConflictv2::ModelProviderError",
+ "type": "object"
+ }
+ ],
+ "title": "ModelProviderError"
+ },
"ModelRerouteReason": {
"enum": [
"highRiskCyberActivity"
@@ -16116,7 +16484,7 @@
"additionalProperties": {
"$ref": "#/definitions/v2/NetworkDomainPermission"
},
- "description": "Canonical network permission map for `experimental_network`.",
+ "description": "Canonical managed network permission map.",
"type": [
"object",
"null"
@@ -16143,6 +16511,16 @@
"null"
]
},
+ "reviewDomains": {
+ "description": "Host patterns routed to network approval before ordinary allow rules.",
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
"socksPort": {
"format": "uint16",
"minimum": 0.0,
@@ -16155,7 +16533,7 @@
"additionalProperties": {
"$ref": "#/definitions/v2/NetworkUnixSocketPermission"
},
- "description": "Canonical unix socket permission map for `experimental_network`.",
+ "description": "Canonical managed unix socket permission map.",
"type": [
"object",
"null"
@@ -16229,6 +16607,232 @@
],
"title": "Nullable_GetAccountTokenUsageParams"
},
+ "OtelExporter": {
+ "oneOf": [
+ {
+ "enum": [
+ "none",
+ "statsig"
+ ],
+ "type": "string"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "otlp-http": {
+ "properties": {
+ "endpoint": {
+ "type": "string"
+ },
+ "headers": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Header names are retained; values are redacted by the server.",
+ "type": "object"
+ },
+ "protocol": {
+ "$ref": "#/definitions/v2/OtelHttpProtocol"
+ },
+ "tls": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelTlsConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "required": [
+ "endpoint",
+ "headers",
+ "protocol"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "otlp-http"
+ ],
+ "title": "OtlpHttpOtelExporter",
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "otlp-grpc": {
+ "properties": {
+ "endpoint": {
+ "type": "string"
+ },
+ "headers": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Header names are retained; values are redacted by the server.",
+ "type": "object"
+ },
+ "tls": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelTlsConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "required": [
+ "endpoint",
+ "headers"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "otlp-grpc"
+ ],
+ "title": "OtlpGrpcOtelExporter",
+ "type": "object"
+ }
+ ]
+ },
+ "OtelHttpProtocol": {
+ "enum": [
+ "binary",
+ "json"
+ ],
+ "type": "string"
+ },
+ "OtelRequirements": {
+ "properties": {
+ "environment": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "log_agent_responses": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "log_guardian_assessments": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "log_user_prompt": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "metrics_exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "span_attributes": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "tool_result": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelToolResultLogConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "trace_exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/v2/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "tracestate": {
+ "additionalProperties": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "OtelTlsConfig": {
+ "properties": {
+ "ca-certificate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "client-certificate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "client-private-key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "OtelToolResultLogConfig": {
+ "properties": {
+ "max_bytes": {
+ "format": "uint",
+ "minimum": 0.0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "max_bytes"
+ ],
+ "type": "object"
+ },
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -17824,6 +18428,13 @@
"format": "int32",
"type": "integer"
},
+ "outputDrainTimedOut": {
+ "description": "Whether output collection hit its post-exit deadline before seeing EOF or reaching the requested byte cap on either stream. Older servers omit this field.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
"processHandle": {
"description": "Client-supplied, connection-scoped `processHandle` from `process/spawn`.",
"type": "string"
@@ -19801,8 +20412,7 @@
},
"ReviewDelivery": {
"enum": [
- "inline",
- "detached"
+ "inline"
],
"type": "string"
},
@@ -19819,7 +20429,7 @@
}
],
"default": null,
- "description": "Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`). Detached delivery is deprecated and emits `deprecationNotice`. Use `thread/start` followed by an inline review for a separate review thread."
+ "description": "Reviews run inline on the supplied thread. Omit this field or pass `inline`. To review on a separate thread, start a thread first and use its id."
},
"target": {
"$ref": "#/definitions/v2/ReviewTarget"
@@ -19839,7 +20449,7 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"reviewThreadId": {
- "description": "Identifies the thread where the review runs.\n\nFor inline reviews, this is the original thread id. For detached reviews, this is the id of the new review thread.",
+ "description": "The thread id supplied to `review/start`.",
"type": "string"
},
"turn": {
@@ -22135,6 +22745,7 @@
"type": "object"
},
"ThreadItem": {
+ "description": "A thread item and its recorded lifecycle times. The payload stays flat on the wire.",
"oneOf": [
{
"properties": {
@@ -22917,6 +23528,14 @@
],
"default": null
},
+ "generationId": {
+ "default": null,
+ "description": "Image generation identifier for the generated image, when provided by the image backend.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"id": {
"type": "string"
},
@@ -23037,7 +23656,28 @@
"title": "ContextCompactionThreadItem",
"type": "object"
}
- ]
+ ],
+ "properties": {
+ "completedAtMs": {
+ "default": null,
+ "description": "Unix timestamp in milliseconds when the item completed, if recorded.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "startedAtMs": {
+ "default": null,
+ "description": "Unix timestamp in milliseconds when the item started, if recorded.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
},
"ThreadItemEntry": {
"properties": {
@@ -25689,6 +26329,14 @@
},
"type": "array"
},
+ "nativeUsageUsdMicros": {
+ "description": "Native dollar usage, independent of credit estimates. None means unknown, not zero.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
"threadId": {
"type": "string"
}
diff --git a/protocol/codex_app_server_protocol.v2.schemas.json b/protocol/codex_app_server_protocol.v2.schemas.json
index cf6063c..b6a7622 100644
--- a/protocol/codex_app_server_protocol.v2.schemas.json
+++ b/protocol/codex_app_server_protocol.v2.schemas.json
@@ -40,6 +40,13 @@
],
"title": "ChatgptAccountType",
"type": "string"
+ },
+ "workspaceName": {
+ "description": "Display name of the selected ChatGPT workspace, when available.",
+ "type": [
+ "string",
+ "null"
+ ]
}
},
"required": [
@@ -69,6 +76,23 @@
],
"title": "AmazonBedrockAccount",
"type": "object"
+ },
+ {
+ "description": "Google Cloud Vertex AI with externally managed credentials.",
+ "properties": {
+ "type": {
+ "enum": [
+ "gcp"
+ ],
+ "title": "GcpAccountType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "title": "GcpAccount",
+ "type": "object"
}
]
},
@@ -130,6 +154,103 @@
],
"type": "string"
},
+ "AccountSession": {
+ "properties": {
+ "displayName": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "email": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imageUrl": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "isActive": {
+ "type": "boolean"
+ },
+ "lastUsedAt": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "selectedWorkspaceAccountId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "sessionId": {
+ "type": "string"
+ },
+ "userId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "workspaces": {
+ "items": {
+ "$ref": "#/definitions/AccountSessionWorkspace"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "isActive",
+ "lastUsedAt",
+ "sessionId",
+ "workspaces"
+ ],
+ "type": "object"
+ },
+ "AccountSessionWorkspace": {
+ "properties": {
+ "accountId": {
+ "type": "string"
+ },
+ "imageUrl": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/AccountSessionWorkspaceKind"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "name": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "accountId"
+ ],
+ "type": "object"
+ },
+ "AccountSessionWorkspaceKind": {
+ "enum": [
+ "personal",
+ "workspace"
+ ],
+ "type": "string"
+ },
"AccountTokenUsageDailyBucket": {
"properties": {
"startDate": {
@@ -252,6 +373,13 @@
"kind": {
"$ref": "#/definitions/AdditionalContextKind"
},
+ "name": {
+ "description": "Optional name for untrusted context, used to label its tool output.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"value": {
"type": "string"
}
@@ -1015,6 +1143,42 @@
"type": "null"
}
]
+ },
+ "storage": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/ApplicationStorageRequirements"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "ApplicationStorageRequirements": {
+ "description": "Managed storage settings for new Desktop workspaces, not filesystem access restrictions.",
+ "properties": {
+ "defaultProjectParent": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/LegacyAppPathString"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "projectlessWorkspaceRoot": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/LegacyAppPathString"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object"
@@ -3967,6 +4131,30 @@
"title": "Feedback/uploadRequest",
"type": "object"
},
+ {
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/RequestId"
+ },
+ "method": {
+ "enum": [
+ "feedback/save"
+ ],
+ "title": "Feedback/saveRequestMethod",
+ "type": "string"
+ },
+ "params": {
+ "$ref": "#/definitions/FeedbackSaveParams"
+ }
+ },
+ "required": [
+ "id",
+ "method",
+ "params"
+ ],
+ "title": "Feedback/saveRequest",
+ "type": "object"
+ },
{
"description": "Execute a standalone command (argv vector) under the server's sandbox.",
"properties": {
@@ -4318,6 +4506,8 @@
"rateLimitExceeded",
"flexUnavailable",
"serverOverloaded",
+ "modelNotFound",
+ "policyCapacityUnavailable",
"cyberPolicy",
"misalignmentPolicyViolation",
"tooManyDenials",
@@ -6032,6 +6222,16 @@
}
]
},
+ "otel": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelRequirements"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"sqliteHome": {
"type": [
"string",
@@ -7623,6 +7823,85 @@
},
"type": "object"
},
+ "FeedbackSaveParams": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "properties": {
+ "classification": {
+ "type": "string"
+ },
+ "extraLogFiles": {
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "includeLogs": {
+ "type": "boolean"
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "savePath": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/LegacyAppPathString"
+ }
+ ],
+ "description": "Absolute, previously unused ZIP path on the app-server host. The caller owns this file, including after a disconnected RPC."
+ },
+ "tags": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "threadId": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "classification",
+ "savePath"
+ ],
+ "title": "FeedbackSaveParams",
+ "type": "object"
+ },
+ "FeedbackSaveResponse": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "properties": {
+ "promptHash": {
+ "description": "Same prompt hash as feedback/upload; null when prompt metadata is absent.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "savedPath": {
+ "$ref": "#/definitions/LegacyAppPathString"
+ },
+ "threadId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "savedPath",
+ "threadId"
+ ],
+ "title": "FeedbackSaveResponse",
+ "type": "object"
+ },
"FeedbackUploadParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -9382,6 +9661,13 @@
"enabled": {
"type": "boolean"
},
+ "environmentId": {
+ "description": "The owning execution environment, if this hook came from its requirements.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"eventName": {
"$ref": "#/definitions/HookEventName"
},
@@ -9549,7 +9835,7 @@
"default": "unknown"
},
"sourcePath": {
- "$ref": "#/definitions/AbsolutePathBuf"
+ "$ref": "#/definitions/LegacyAppPathString"
},
"startedAt": {
"format": "int64",
@@ -9670,11 +9956,18 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cwds": {
- "description": "When empty, defaults to the current session working directory.",
+ "description": "Working directories to inspect when no thread is specified. An empty list uses the session cwd.",
"items": {
"type": "string"
},
"type": "array"
+ },
+ "threadId": {
+ "description": "List a loaded thread's hooks, including its selected environments. Omit `cwds` in this mode.",
+ "type": [
+ "string",
+ "null"
+ ]
}
},
"title": "HooksListParams",
@@ -11810,6 +12103,81 @@
"title": "ModelProviderCapabilitiesReadResponse",
"type": "object"
},
+ "ModelProviderError": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Structured JSON-RPC error data. Unknown errors must retain the client's normal fallback.",
+ "oneOf": [
+ {
+ "properties": {
+ "currentModelProvider": {
+ "type": "string"
+ },
+ "modelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderMismatch"
+ ],
+ "title": "ModelProviderMismatchv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "currentModelProvider",
+ "modelProvider",
+ "type"
+ ],
+ "title": "ModelProviderMismatchv2::ModelProviderError",
+ "type": "object"
+ },
+ {
+ "properties": {
+ "modelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderNotConfigured"
+ ],
+ "title": "ModelProviderNotConfiguredv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "modelProvider",
+ "type"
+ ],
+ "title": "ModelProviderNotConfiguredv2::ModelProviderError",
+ "type": "object"
+ },
+ {
+ "properties": {
+ "modelProvider": {
+ "type": "string"
+ },
+ "requiredModelProvider": {
+ "type": "string"
+ },
+ "type": {
+ "enum": [
+ "modelProviderPolicyConflict"
+ ],
+ "title": "ModelProviderPolicyConflictv2::ModelProviderErrorType",
+ "type": "string"
+ }
+ },
+ "required": [
+ "modelProvider",
+ "requiredModelProvider",
+ "type"
+ ],
+ "title": "ModelProviderPolicyConflictv2::ModelProviderError",
+ "type": "object"
+ }
+ ],
+ "title": "ModelProviderError"
+ },
"ModelRerouteReason": {
"enum": [
"highRiskCyberActivity"
@@ -12108,7 +12476,7 @@
"additionalProperties": {
"$ref": "#/definitions/NetworkDomainPermission"
},
- "description": "Canonical network permission map for `experimental_network`.",
+ "description": "Canonical managed network permission map.",
"type": [
"object",
"null"
@@ -12135,6 +12503,16 @@
"null"
]
},
+ "reviewDomains": {
+ "description": "Host patterns routed to network approval before ordinary allow rules.",
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
"socksPort": {
"format": "uint16",
"minimum": 0.0,
@@ -12147,7 +12525,7 @@
"additionalProperties": {
"$ref": "#/definitions/NetworkUnixSocketPermission"
},
- "description": "Canonical unix socket permission map for `experimental_network`.",
+ "description": "Canonical managed unix socket permission map.",
"type": [
"object",
"null"
@@ -12221,6 +12599,232 @@
],
"title": "Nullable_GetAccountTokenUsageParams"
},
+ "OtelExporter": {
+ "oneOf": [
+ {
+ "enum": [
+ "none",
+ "statsig"
+ ],
+ "type": "string"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "otlp-http": {
+ "properties": {
+ "endpoint": {
+ "type": "string"
+ },
+ "headers": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Header names are retained; values are redacted by the server.",
+ "type": "object"
+ },
+ "protocol": {
+ "$ref": "#/definitions/OtelHttpProtocol"
+ },
+ "tls": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelTlsConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "required": [
+ "endpoint",
+ "headers",
+ "protocol"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "otlp-http"
+ ],
+ "title": "OtlpHttpOtelExporter",
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "properties": {
+ "otlp-grpc": {
+ "properties": {
+ "endpoint": {
+ "type": "string"
+ },
+ "headers": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Header names are retained; values are redacted by the server.",
+ "type": "object"
+ },
+ "tls": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelTlsConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "required": [
+ "endpoint",
+ "headers"
+ ],
+ "type": "object"
+ }
+ },
+ "required": [
+ "otlp-grpc"
+ ],
+ "title": "OtlpGrpcOtelExporter",
+ "type": "object"
+ }
+ ]
+ },
+ "OtelHttpProtocol": {
+ "enum": [
+ "binary",
+ "json"
+ ],
+ "type": "string"
+ },
+ "OtelRequirements": {
+ "properties": {
+ "environment": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "log_agent_responses": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "log_guardian_assessments": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "log_user_prompt": {
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "metrics_exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "span_attributes": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "tool_result": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelToolResultLogConfig"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "trace_exporter": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/OtelExporter"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "tracestate": {
+ "additionalProperties": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "OtelTlsConfig": {
+ "properties": {
+ "ca-certificate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "client-certificate": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "client-private-key": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "OtelToolResultLogConfig": {
+ "properties": {
+ "max_bytes": {
+ "format": "uint",
+ "minimum": 0.0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "max_bytes"
+ ],
+ "type": "object"
+ },
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -13816,6 +14420,13 @@
"format": "int32",
"type": "integer"
},
+ "outputDrainTimedOut": {
+ "description": "Whether output collection hit its post-exit deadline before seeing EOF or reaching the requested byte cap on either stream. Older servers omit this field.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
"processHandle": {
"description": "Client-supplied, connection-scoped `processHandle` from `process/spawn`.",
"type": "string"
@@ -15793,8 +16404,7 @@
},
"ReviewDelivery": {
"enum": [
- "inline",
- "detached"
+ "inline"
],
"type": "string"
},
@@ -15811,7 +16421,7 @@
}
],
"default": null,
- "description": "Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`). Detached delivery is deprecated and emits `deprecationNotice`. Use `thread/start` followed by an inline review for a separate review thread."
+ "description": "Reviews run inline on the supplied thread. Omit this field or pass `inline`. To review on a separate thread, start a thread first and use its id."
},
"target": {
"$ref": "#/definitions/ReviewTarget"
@@ -15831,7 +16441,7 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"reviewThreadId": {
- "description": "Identifies the thread where the review runs.\n\nFor inline reviews, this is the original thread id. For detached reviews, this is the id of the new review thread.",
+ "description": "The thread id supplied to `review/start`.",
"type": "string"
},
"turn": {
@@ -19849,6 +20459,7 @@
"type": "object"
},
"ThreadItem": {
+ "description": "A thread item and its recorded lifecycle times. The payload stays flat on the wire.",
"oneOf": [
{
"properties": {
@@ -20631,6 +21242,14 @@
],
"default": null
},
+ "generationId": {
+ "default": null,
+ "description": "Image generation identifier for the generated image, when provided by the image backend.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
"id": {
"type": "string"
},
@@ -20751,7 +21370,28 @@
"title": "ContextCompactionThreadItem",
"type": "object"
}
- ]
+ ],
+ "properties": {
+ "completedAtMs": {
+ "default": null,
+ "description": "Unix timestamp in milliseconds when the item completed, if recorded.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "startedAtMs": {
+ "default": null,
+ "description": "Unix timestamp in milliseconds when the item started, if recorded.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
},
"ThreadItemEntry": {
"properties": {
@@ -23403,6 +24043,14 @@
},
"type": "array"
},
+ "nativeUsageUsdMicros": {
+ "description": "Native dollar usage, independent of credit estimates. None means unknown, not zero.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
"threadId": {
"type": "string"
}
diff --git a/start.sh b/start.sh
index 67205b6..51d9dd7 100755
--- a/start.sh
+++ b/start.sh
@@ -21,7 +21,7 @@ cd "$(dirname "$0")"
# 本机 VM 修复:系统里的 codex 是裸二进制,旁边没有
# codex-code-mode-host 和 codex-resources/bwrap,工具会 fail closed。
-# 本项目 bin/ 里是配对好的官方 0.164.0-alpha.9 三件套
+# 本项目 bin/ 里是配对好的官方 0.166.0 三件套
# (codex + codex-code-mode-host + codex-resources/bwrap),
# 优先用它;Mac 上用 npm 正常安装的 codex 自带这三件套,
# 不需要 bin/,直接用 PATH 里的 codex 即可。
diff --git a/tests/test_host_protocol_mapping.py b/tests/test_host_protocol_mapping.py
new file mode 100644
index 0000000..335afbe
--- /dev/null
+++ b/tests/test_host_protocol_mapping.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+"""host.py 方法 ↔ protocol/ schema 映射钉死测试(引擎升级 0.166.0 批)。
+
+钉死两件事:
+1. host.py 实际触及的方法集合(发出的请求 + 处理的入站通知)必须
+ 等于下表;host.py 新增方法时本测试先红,逼升级者同步更新映射,
+ 而不是悄悄带一个 schema 里没有的方法上线。
+2. 下表每个方法在 protocol/ 两 bundle 里存在,且 params 引用与
+ 0.166.0 重导结果一致;上游删方法或改 params 引用时本测试先红。
+
+方法数勘误:2026-11-05 对齐表写“17 个方法/通知”并不准确——
+实查 host.py 触及 21 个方法字符串:18 个发出请求(含 initialize
+握手)+ 3 个入站通知;对齐表列出的 18 项漏了 initialize、
+app/list、mcpServerStatus/list 三个。本测试以实查 21 项为准。
+其中 17 个应用请求(不含 initialize)与对齐表原意一致,只是
+对齐表把其中 app/list、mcpServerStatus/list 两项漏列了。
+
+本测试只读 host.py 文本与 protocol/ 两 bundle,不 import host,
+不启动任何 app-server 进程,不做模型调用。
+"""
+
+import json
+import os
+import re
+import unittest
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# method -> v2 ClientRequest 的 params 引用;None 表示该请求的
+# params 在 schema 里是 {"type": "null"}(config/mcpServer/reload)。
+EXPECTED_REQUESTS = {
+ "initialize": "InitializeParams",
+ "thread/start": "ThreadStartParams",
+ "thread/resume": "ThreadResumeParams",
+ "thread/fork": "ThreadForkParams",
+ "thread/list": "ThreadListParams",
+ "thread/read": "ThreadReadParams",
+ "thread/name/set": "ThreadSetNameParams",
+ "thread/archive": "ThreadArchiveParams",
+ "thread/unarchive": "ThreadUnarchiveParams",
+ "thread/compact/start": "ThreadCompactStartParams",
+ "turn/start": "TurnStartParams",
+ "turn/interrupt": "TurnInterruptParams",
+ "review/start": "ReviewStartParams",
+ "skills/list": "SkillsListParams",
+ "app/list": "AppsListParams",
+ "mcpServerStatus/list": "ListMcpServerStatusParams",
+ "mcpServer/oauth/login": "McpServerOauthLoginParams",
+ "config/mcpServer/reload": None,
+}
+
+# method -> (bundle, container, params 引用)。turn/completed 与
+# thread/tokenUsage/updated 是服务端发来的通知(v2 ServerNotification);
+# mcpServer/elicitation/request 是服务端发来的请求,活在 v1 bundle
+# 的 ServerRequest 里(v2 bundle 没有 ServerRequest 定义)。
+EXPECTED_INBOUND = {
+ "turn/completed": ("v2", "ServerNotification", "TurnCompletedNotification"),
+ "thread/tokenUsage/updated": (
+ "v2", "ServerNotification", "ThreadTokenUsageUpdatedNotification"),
+ "mcpServer/elicitation/request": (
+ "v1", "ServerRequest", "McpServerElicitationRequestParams"),
+}
+
+
+def _load_bundle(name):
+ path = os.path.join(ROOT, "protocol", name)
+ with open(path, encoding="utf-8") as f:
+ return json.load(f)
+
+
+def _method_map(bundle, container):
+ out = {}
+ for entry in bundle["definitions"].get(container, {}).get("oneOf", []):
+ props = entry.get("properties", {})
+ method = (props.get("method", {}).get("enum") or [None])[0]
+ if not method:
+ continue
+ params = props.get("params", {})
+ ref = params.get("$ref", "")
+ out[method] = ref.split("/")[-1] if ref else params
+ return out
+
+
+class TestHostProtocolMapping(unittest.TestCase):
+ def test_host_method_set_matches_pin(self):
+ with open(os.path.join(ROOT, "host.py"), encoding="utf-8") as f:
+ src = f.read()
+ requests = set(re.findall(r'\.request\(\s*"([^"]+)"', src))
+ inbound = set(
+ re.findall(r'\.get\("method"\)\s*[=!]=\s*"([^"]+)"', src))
+ self.assertEqual(requests, set(EXPECTED_REQUESTS),
+ "host.py 发出的请求集合变了,先更新映射表再合")
+ self.assertEqual(inbound, set(EXPECTED_INBOUND),
+ "host.py 处理的入站通知集合变了,先更新映射表再合")
+
+ def test_requests_in_v2_schema(self):
+ bundle = _load_bundle("codex_app_server_protocol.v2.schemas.json")
+ methods = _method_map(bundle, "ClientRequest")
+ for method, ref in EXPECTED_REQUESTS.items():
+ self.assertIn(method, methods, f"v2 schema 缺少 host 请求 {method}")
+ if ref is None:
+ self.assertEqual(methods[method], {"type": "null"},
+ f"{method} 的 params 应为 null 类型")
+ else:
+ self.assertEqual(methods[method], ref,
+ f"{method} 的 params 引用变了")
+
+ def test_inbound_in_schema(self):
+ bundles = {
+ "v1": _load_bundle("codex_app_server_protocol.schemas.json"),
+ "v2": _load_bundle("codex_app_server_protocol.v2.schemas.json"),
+ }
+ for method, (bundle_name, container, ref) in EXPECTED_INBOUND.items():
+ methods = _method_map(bundles[bundle_name], container)
+ self.assertIn(method, methods,
+ f"{bundle_name} schema {container} 缺少 {method}")
+ self.assertEqual(methods[method], ref,
+ f"{method} 的 params 引用变了")
+
+ def test_bundles_parse(self):
+ for name in ("codex_app_server_protocol.schemas.json",
+ "codex_app_server_protocol.v2.schemas.json"):
+ bundle = _load_bundle(name)
+ self.assertIn("$schema", bundle)
+ self.assertIn("definitions", bundle)
+ self.assertGreater(len(bundle["definitions"]), 0)
+
+
+if __name__ == "__main__":
+ unittest.main()