opensource-align2: engine 0.164.0-alpha.9 -> 0.166.0 (schema re-derived, method map pinned)

- protocol/ bundles re-derived from official 0.166.0 binary
  (generate-json-schema); v1 83 defs, v2 665 -> 677 defs,
  0 removals; all 21 host methods present, params refs unchanged
- README pin + start.sh comment follow the verified binary
- docs/upstream-166-alignment.md: 0.166.0 alignment record
- tests/test_host_protocol_mapping.py: 21 host methods pinned
  against protocol/ bundles (corrects alignment table's 17-count:
  18 requests incl. initialize + 3 inbound notifications)
- config.example.toml: [[skills.config]] disable semantics doc
  comment only (no effective config change)
- No production binary touched; no push, merge, or deploy.
This commit is contained in:
host committed 2026-11-04 21:04:07 +00:00
1 parent 6ba822daa2
commit ce98aaa448
7 files changed
+1584 -21

No files matched your search

+3 -2
View File
@@ -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`)。
## 目录
+6
View File
@@ -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、仓库根、当前子目录,越往下优先级越高。
+130
View File
@@ -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 <dir>`,
原样取其中两 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 下 `<codex-home>/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 下 `<codex-home>/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)仍待用户;本批不替用户定。
+657 -9
View File
@@ -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"
}
@@ -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"
}
+1 -1
View File
@@ -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 即可。
+130
View File
@@ -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()