diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml index 4673ffaab7..d81ee98e02 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md -2026-08-04-claude-code-and-codex-subagent-backends.md: e81d1fb14f719331c503dba539d6a5ec0f1eed4f -2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 3c78e8d5a4ca1a86942971721ad05c631901f231 +2026-08-04-claude-code-and-codex-subagent-backends.md: 2063b99a7b0ca34f434b3e56628f3ce765d90d81 +2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 735ad459c8b1f32051c366140faff7b57e763854 diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md index e81d1fb14f..2063b99a7b 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md @@ -39,11 +39,11 @@ Before publication, the provider validates a non-empty text-only task, starts th `turn/completed` is the authoritative remote terminal fact. The latest `agentMessage` with `phase: "final_answer"` wins, and that selected message must contain nonblank text. When the product emits no explicit final phase, the latest message with `phase: null` is the compatibility fallback and must likewise be nonblank; commentary never replaces either answer. A failed turn with `error.codexErrorInfo: "contextWindowExceeded"` becomes `max-tokens`. A completed turn without an answer, every other failed or interrupted remote turn, malformed required fields in a recognized app-server frame, protocol closure, early process exit, or unknown server request becomes `error`; this version has no native refusal terminal and therefore produces no `refusal`. Local cancellation wins its race and remains `aborted`. -For command and file approvals, the unattended wire selects a non-approval decision offered by the request, preferring `cancel`; the stable 0.146.0 request shape without an offered-decision list falls back to `decline`. It grants no requested permissions for the turn, answers user-input requests with no answers, and declines MCP elicitation. A request with no legal unattended response, or any unknown server request, fails the run instead of waiting for a user interface the provider does not supply. +For command and file approvals, the unattended wire selects a non-approval decision offered by the request, preferring `cancel`; the stable 0.147.0 request shape without an offered-decision list falls back to `decline`. It grants no requested permissions for the turn, answers user-input requests with no answers, and declines MCP elicitation. A request with no legal unattended response, or any unknown server request, fails the run instead of waiting for a user interface the provider does not supply. An unpublished startup failure closes the wire, terminates the acquired process tree, waits for exit, and then rejects `start()`. Published disposal best-effort interrupts a known turn, closes the wire, ends stdin, invokes the shared termination escalation, and waits for whole-tree exit. Result failure and teardown failure stay independently observable. -Codex 0.146.0 speaks the Responses protocol, while DeepSeek's public OpenAI-compatible endpoint speaks Chat Completions. The credentialed Codex e2e therefore uses a loopback-only, test-private bridge for one no-tool nonce request: real Codex sends Responses to the bridge, the bridge forwards the received bearer credential and extracted task to the fixed official DeepSeek endpoint, and it wraps the real text in the minimal Responses SSE lifecycle. The bridge is neither a production proxy nor evidence that Codex connects to DeepSeek Chat Completions natively. +Codex 0.147.0 speaks the Responses protocol, while DeepSeek's public OpenAI-compatible endpoint speaks Chat Completions. The credentialed Codex e2e therefore uses a loopback-only, test-private bridge for one no-tool nonce request: real Codex sends Responses to the bridge, the bridge forwards the received bearer credential and extracted task to the fixed official DeepSeek endpoint, and it wraps the real text in the minimal Responses SSE lifecycle. The bridge is neither a production proxy nor evidence that Codex connects to DeepSeek Chat Completions natively. ## Claude Code provider @@ -61,7 +61,7 @@ The credentialed Claude Code e2e uses the official DeepSeek Claude Code contract Each product owns branch-complete package tests, a required keyless real-product spec, a Loader composition e2e, and a credentialed DeepSeek e2e. The keyless product tier uses the exact official distribution under test, a non-empty fake product key, an isolated temporary workspace and product home, and a loopback fixed-answer model. Missing product requests, wrong authentication, altered task text, a non-exact answer, a skipped real product, or a surviving managed handle fails the required test. The Loader tier boots the README-shaped user configuration, verifies both fixed foreground-only tools in one context, and starts neither product process. The credentialed tier starts the same production provider and real product with a runtime-only key, requires a unique nonce from the fixed official DeepSeek service, and proves quiescence again; it self-skips only when a local operator supplied no key, while trusted CI preflights the secret. -The Codex evidence pins `@openai/codex@0.146.0` and `codex-cli 0.146.0`. Its real-product spec observes the exact Bearer key, original task, byte-exact final answer, unattended command rejection with no file side effect, local cancellation, and whole-tree exit. Production still supplies `codex` on `PATH`. +The Codex evidence pins `@openai/codex@0.147.0` and `codex-cli 0.147.0`. Its real-product spec observes the exact Bearer key, original task, byte-exact final answer, unattended command rejection with no file side effect, local cancellation, and whole-tree exit. Production still supplies `codex` on `PATH`. The Codex credentialed e2e registers the production provider, starts the same real app-server, and requests one random nonce through the test-private bridge described above. It fixes the external endpoint and model, stores no credential or request payload, requires exactly one completed upstream response, compares the trimmed product answer byte-for-byte with the nonce, and waits for every managed handle to exit. diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md index 3c78e8d5a4..735ad459c8 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md @@ -39,11 +39,11 @@ fixed tool → shared subagent service → product provider → official product `turn/completed` 是权威的远端终止事实。以最后一条带有 `phase: "final_answer"` 的 `agentMessage` 为准,且选中的消息必须包含非空白文本。若产品没有发出明确的最终阶段,则以最后一条 `phase: null` 的消息作为兼容性回退,该消息也必须包含非空白文本;过程说明绝不会取代上述任一答案。带有 `error.codexErrorInfo: "contextWindowExceeded"` 的失败轮次会成为 `max-tokens`。轮次完成却没有答案、其他任何远端失败或中断轮次、已识别的 app-server 帧中必需字段格式错误、协议关闭、进程提前退出或未知的服务器请求,都会产生 `error`;本版本没有原生的拒绝终止状态,因此不会产生 `refusal`。本地取消在竞态中胜出并保持为 `aborted`。 -对于命令与文件审批,无人值守的协议连接会从请求给出的决策选项中选择一项不予批准的决策,并优先选择 `cancel`;稳定的 0.146.0 请求形态没有决策选项列表,因此回退到 `decline`。它不授予该轮次请求的任何权限,不向用户输入请求提供任何答案,并拒绝 MCP elicitation。若请求在无人值守模式下没有合法响应,或是未知服务器请求,此次运行就会失败,而不会等待本提供方没有提供的用户界面。 +对于命令与文件审批,无人值守的协议连接会从请求给出的决策选项中选择一项不予批准的决策,并优先选择 `cancel`;稳定的 0.147.0 请求形态没有决策选项列表,因此回退到 `decline`。它不授予该轮次请求的任何权限,不向用户输入请求提供任何答案,并拒绝 MCP elicitation。若请求在无人值守模式下没有合法响应,或是未知服务器请求,此次运行就会失败,而不会等待本提供方没有提供的用户界面。 若启动在发布前失败,提供方会关闭协议连接、终止已获取的进程树并等待其退出,然后拒绝 `start()`。对已发布的运行执行资源释放时,提供方会尽力中断已知轮次、关闭协议连接、结束标准输入、调用共享的逐级终止机制,并等待整棵进程树退出。结果失败与清理失败仍可彼此独立地观察。 -Codex 0.146.0 使用 Responses 协议,而 DeepSeek 的公开 OpenAI 兼容端点使用 Chat Completions。因此,带密钥 Codex e2e 会采用一个仅限回环、仅供测试内部使用的桥接层来处理一次不使用工具的随机数请求:真实 Codex 将 Responses 发送到桥接层,桥接层把收到的 Bearer 凭据与提取出的任务转发到固定的 DeepSeek 官方端点,再将真实文本包装进最小化的 Responses SSE(Server-Sent Events)生命周期。该桥接层既不是生产代理,也不能作为 Codex 原生连接 DeepSeek Chat Completions 的证据。 +Codex 0.147.0 使用 Responses 协议,而 DeepSeek 的公开 OpenAI 兼容端点使用 Chat Completions。因此,带密钥 Codex e2e 会采用一个仅限回环、仅供测试内部使用的桥接层来处理一次不使用工具的随机数请求:真实 Codex 将 Responses 发送到桥接层,桥接层把收到的 Bearer 凭据与提取出的任务转发到固定的 DeepSeek 官方端点,再将真实文本包装进最小化的 Responses SSE(Server-Sent Events)生命周期。该桥接层既不是生产代理,也不能作为 Codex 原生连接 DeepSeek Chat Completions 的证据。 ## Claude Code 提供方 @@ -61,7 +61,7 @@ Codex 0.146.0 使用 Responses 协议,而 DeepSeek 的公开 OpenAI 兼容端 每个产品都负责覆盖所有分支的包测试、一项必跑的无密钥真实产品测试、一项 Loader 组合 e2e 和一项带密钥 DeepSeek e2e。无密钥产品层级使用被测的确切官方发行版、非空的伪产品密钥、隔离的临时工作区与产品主目录,以及能返回固定答案的回环模型。产品请求缺失、身份验证错误、任务文本被改动、答案不完全一致、真实产品被跳过或受管句柄仍存活,都会使这项必跑测试失败。Loader 层级会启动 README 所示形态的用户配置,在同一个上下文中验证两个固定且只支持前台执行的工具,并且不会启动任何产品进程。带密钥层级会使用仅在运行时提供的密钥启动同一生产提供方与真实产品,要求从固定的 DeepSeek 官方服务取得唯一随机数,并再次证明完全停稳;仅当本地操作者未提供密钥时才会自行跳过,而受信任的 CI 会预检该 secret。 -Codex 证据锁定 `@openai/codex@0.146.0` 与 `codex-cli 0.146.0`。其真实产品测试会观测确切的 Bearer 密钥、原始任务、逐字节完全一致的最终回答、不会产生文件副作用的无人值守命令拒绝、本地取消以及整棵进程树退出。生产环境仍提供 `codex`,并通过 `PATH` 解析。 +Codex 证据锁定 `@openai/codex@0.147.0` 与 `codex-cli 0.147.0`。其真实产品测试会观测确切的 Bearer 密钥、原始任务、逐字节完全一致的最终回答、不会产生文件副作用的无人值守命令拒绝、本地取消以及整棵进程树退出。生产环境仍提供 `codex`,并通过 `PATH` 解析。 带密钥 Codex e2e 会注册生产提供方,启动同样的真实 app-server,并通过上述测试专用桥接层请求一个随机数。该测试固定外部端点与模型,不存储任何凭据或请求载荷,要求上游恰好完成一次响应,将去除首尾空白后的产品答案与该随机数逐字节比较,并等待所有受管句柄退出。 diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml index b69ebc94ae..05a73c2053 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md -2026-08-08-native-windows-pull-request-ci.md: a540a73e5979c436fb8a085516554f5ae7249939 -2026-08-08-native-windows-pull-request-ci.zh.md: 34ca87f21bcd5b72b2ecbc43e66043caee299e48 +2026-08-08-native-windows-pull-request-ci.md: 5aafb8b9c7557157135dfa6ecedffed42eb76183 +2026-08-08-native-windows-pull-request-ci.zh.md: 4c71f3eeda86fdc537c22629962b0eccbb994b65 diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md index a540a73e59..5aafb8b9c7 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md @@ -30,6 +30,10 @@ Running the complete instrumented graph instead of the earlier reduced inventory The final root-probe failure came from applying the extended-length namespace to the drive root as well as long descendants. Node rejected the bare root probe as `EISDIR`, cascading through every JSONL fixture and assembled binary that materialized a session. The Windows durable-directory helper now probes the short drive root in its native spelling and namespaces only descendants; an injected Win32-path unit test locks both spellings while native coverage exercises the real filesystem. +The next complete coverage run reached six independent late failures rather than one shared cascade. React queue-action coverage now resolves its mocked request inside an awaited `act()` before observing the settled render. The unclosed-Markdown workload keeps all 6,400 candidates under an explicit three-second coverage budget, and the asynchronous workspace-projection warning test gives its outer case a twenty-second budget larger than its ten-second poll. Real Claude Code teardown uses asynchronous recursive removal with ten bounded retries after every managed handle reports exit, accommodating Windows' delayed handle release without weakening the quiescence assertions. + +Two product boundaries required foundation repairs. Include's debounced config persistence previously launched an unobserved promise from a timer; a transient Windows `EPERM` while replacing `cordis.yml` could therefore lose the disabled row and escape as an unhandled rejection. The vendored writer now serializes writes, retries only transient access/busy failures with bounded backoff, observes every rejection, and drains the latest write at teardown; the real Loader composition injects one `EPERM` and proves the durable retry. Codex 0.146 advertised `exec_command` to the loopback Responses model on Windows but rejected the returned call in its own router, the same upstream failure class tracked in [openai/codex#31665](https://github.com/openai/codex/issues/31665). Development evidence is pinned to the current stable 0.147.0 release: regenerated upstream schemas preserve the provider-owned handshake, thread/turn, approval, user-input, and elicitation contract, while the real-product suite again proves command rejection without a side effect and whole-tree exit. + POSIX mode bits, chmod-based unreadability, and chmod-based writer-lock refusal do not exist as equivalent Windows facilities. Those acceptance cases remain enforced on POSIX and are skipped on Windows; content, atomic replacement, symlink safety, rollback and recovery through platform-independent filesystem conflicts, and native Windows long-path behavior remain covered. No supported product source is excluded from Windows coverage to accommodate these differences. Wine-only infrastructure is absent from the supported workflow: there is no apt-cache producer, compatibility script, hoisted snapshot install, Windows Node download, or local `check:windows-wine` command. The [archived Wine experiment](../../archived/process/2026-07-27-wine-windows-gates-experiment.md) remains historical evidence for its measured latency and fidelity trade-offs, not a current execution path. diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md index 34ca87f21b..4c71f3eeda 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md @@ -30,6 +30,10 @@ Status: implemented 最后一个根路径探测失败源于扩展长度命名空间既应用到长后代路径,也应用到了驱动器根目录。Node 将裸根目录探测拒绝为 `EISDIR`,从而连锁影响所有会物化会话的 JSONL fixture 和组装后二进制。Windows 持久目录辅助函数现在以原生写法探测本来就很短的驱动器根目录,仅对后代路径添加命名空间;注入 Win32 路径语义的单元测试固定两种写法,原生覆盖率则验证真实文件系统。 +下一次完整覆盖率运行触及的是 6 项相互独立的末端故障,不再是同一问题的连锁结果。React 队列动作覆盖现在会在 awaited `act()` 中解析模拟请求,再观察渲染完成后的状态。未闭合 Markdown 工作负载保留全部 6,400 个候选项,并采用显式的 3 秒覆盖率预算;异步工作区投影告警测试则为外层用例设置 20 秒预算,大于其 10 秒轮询预算。真实 Claude Code 拆卸会在所有受管句柄均报告退出后,采用带 10 次有界重试的异步递归删除,以容纳 Windows 延迟释放句柄的行为,同时不削弱完全停稳断言。 + +另有两个产品边界需要基础性修复。Include 的防抖配置持久化此前会从计时器启动一个无人观察的 Promise;Windows 在替换 `cordis.yml` 时若瞬时返回 `EPERM`,既可能丢失已禁用行,也会让 rejection 以未处理形式逸出。现在,vendored writer 会串行化写入,只对瞬时的访问/忙碌错误执行有界退避重试,观察每个 rejection,并在拆卸时排空最新写入;真实 Loader 组合测试会注入一次 `EPERM` 并证明持久化重试。Codex 0.146 在 Windows 上会把 `exec_command` 提供给回环 Responses 模型,却在自身路由器中拒绝模型返回的调用;这与 [openai/codex#31665](https://github.com/openai/codex/issues/31665) 跟踪的上游故障属于同一类。开发证据现锁定当前稳定版 0.147.0:重新生成的上游 schema 保留了提供方拥有的握手、线程/轮次、审批、用户输入和 elicitation 契约;真实产品测试则再次证明命令会在不产生副作用的情况下被拒绝,且整棵进程树退出。 + POSIX 模式位、基于 chmod 的不可读状态和基于 chmod 的 writer lock 拒绝在 Windows 上没有等价机制。这些验收场景继续在 POSIX 上强制执行,并在 Windows 上跳过;内容、原子替换、符号链接安全、通过平台无关文件系统冲突验证的回滚与恢复,以及原生 Windows 长路径行为仍保有覆盖。没有任何受支持的产品源码为适应这些差异而从 Windows 覆盖率中排除。 受支持的工作流不含 Wine 专属基础设施:不存在 apt 缓存生产者、兼容性脚本、对仓库快照执行的 hoisted 安装、Windows Node 下载或本地 `check:windows-wine` 命令。[已归档的 Wine 实验](../../archived/process/2026-07-27-wine-windows-gates-experiment.md)仍作为其实测延迟与保真度取舍的历史证据,而非当前执行路径。 diff --git a/packages/subagent/subagent-codex/README.i18n.yaml b/packages/subagent/subagent-codex/README.i18n.yaml index 97c2b9f705..6bb735b3e5 100644 --- a/packages/subagent/subagent-codex/README.i18n.yaml +++ b/packages/subagent/subagent-codex/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-codex/README.md -README.md: c25ee90edf8972da66448fe84cb659b0aec79e6f -README.zh.md: 10c8fcc47a9ab04bca983857bd44ede265c23435 +README.md: b0f0548ed7dd61c81111eb8a141b7a994066ed5b +README.zh.md: 8fe84e1428a77d9842a58c230185afb004345d58 diff --git a/packages/subagent/subagent-codex/README.md b/packages/subagent/subagent-codex/README.md index c25ee90edf..b0f0548ed7 100644 --- a/packages/subagent/subagent-codex/README.md +++ b/packages/subagent/subagent-codex/README.md @@ -10,7 +10,7 @@ This package registers the fixed `codex` subagent provider. Each accepted run st The published `run.result` starts exactly one turn. It accepts only notifications for that run's thread and turn, then waits for the authoritative `turn/completed` terminal notification. The latest `agentMessage` with `phase: "final_answer"` wins; when Codex emits no explicit final phase, the latest message with `phase: null` is the compatibility fallback. Commentary never replaces either answer, and a successful turn with no nonblank answer settles as an error. -For command and file approvals, the unattended provider selects a non-approval decision offered by the request, preferring `cancel`; the stable 0.146.0 request shape without an offered-decision list falls back to `decline`. It answers permission requests with an empty turn-scoped permission set, answers user-input requests with no answers, and declines MCP elicitation. A request with no legal unattended response, or any unknown server request, fails the run. +For command and file approvals, the unattended provider selects a non-approval decision offered by the request, preferring `cancel`; the stable 0.147.0 request shape without an offered-decision list falls back to `decline`. It answers permission requests with an empty turn-scoped permission set, answers user-input requests with no answers, and declines MCP elicitation. A request with no legal unattended response, or any unknown server request, fails the run. Local cancellation wins the result race and maps to `aborted`. A failed turn whose `codexErrorInfo` is `contextWindowExceeded` maps to `max-tokens`; every other remote interrupted or failed turn maps to `error`, and this version produces no `refusal`. `dispose()` is idempotent: it requests a best-effort `turn/interrupt` with both current ids when they are known, closes the JSON-RPC wire, ends stdin, invokes the shared process-tree termination escalation, and waits for whole-tree exit. Result failure and independent teardown failure remain separate. @@ -47,7 +47,7 @@ Install this package and add the following rows to your own `cordis.yml`. Shippe ## Product compatibility and evidence -The production wire intentionally implements only the app-server methods required by this one-shot contract. Development evidence is pinned to `@openai/codex@0.146.0` / `codex-cli 0.146.0`: the keyless real-product spec drives the official binary against a loopback Responses service with a non-empty fake key and proves the task, authentication, exact answer, cancellation, approvals, and process-tree exit. A separate Loader composition e2e boots the README-shaped user configuration with no `codex` command available, verifies the fixed provider and foreground-only tool schema, and records zero child starts. A credentialed e2e starts the production provider and real Codex, then obtains a unique answer from the fixed official DeepSeek service through a loopback-only test bridge from Responses to Chat Completions; that bridge is not production functionality or native Codex support for DeepSeek's Chat Completions API. The npm package is a test-only dependency; deployments still supply `codex` on `PATH`. +The production wire intentionally implements only the app-server methods required by this one-shot contract. Development evidence is pinned to `@openai/codex@0.147.0` / `codex-cli 0.147.0`: the keyless real-product spec drives the official binary against a loopback Responses service with a non-empty fake key and proves the task, authentication, exact answer, cancellation, approvals, and process-tree exit. A separate Loader composition e2e boots the README-shaped user configuration with no `codex` command available, verifies the fixed provider and foreground-only tool schema, and records zero child starts. A credentialed e2e starts the production provider and real Codex, then obtains a unique answer from the fixed official DeepSeek service through a loopback-only test bridge from Responses to Chat Completions; that bridge is not production functionality or native Codex support for DeepSeek's Chat Completions API. The npm package is a test-only dependency; deployments still supply `codex` on `PATH`. ## Model Experience @@ -83,7 +83,7 @@ Append-only: the new tool result follows the reusable parent request prefix. - **One fresh process, thread, and turn per run** — there is no continuation, resume, pooling, progress stream, or product-session persistence. - **Host-managed product installation and account state** — a missing or incompatible `codex`, configuration error, or authentication failure is surfaced as a startup or run error; the plugin provides no installer, login flow, or runtime version gate. -- **Compatibility is pinned by development evidence** — upgrading from the verified 0.146.0 protocol baseline requires regenerating upstream schema evidence and rerunning handshake, answer-selection, approval, cancellation, keyless real-product, and credentialed DeepSeek nonce tests. +- **Compatibility is pinned by development evidence** — upgrading from the verified 0.147.0 protocol baseline requires regenerating upstream schema evidence and rerunning handshake, answer-selection, approval, cancellation, keyless real-product, and credentialed DeepSeek nonce tests. - **No human approval path** — known unattended approval requests are denied and unknown server requests fail closed; deployments cannot configure an allow policy through this package. - **Final text only** — reasoning, commentary, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local. - **No optional shared capabilities** — output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider. diff --git a/packages/subagent/subagent-codex/README.zh.md b/packages/subagent/subagent-codex/README.zh.md index 10c8fcc47a..8fe84e1428 100644 --- a/packages/subagent/subagent-codex/README.zh.md +++ b/packages/subagent/subagent-codex/README.zh.md @@ -10,7 +10,7 @@ 已发布的 `run.result` 恰好启动一个轮次。它只接受与此次运行的线程和轮次匹配的通知,随后等待权威的终止通知 `turn/completed`。以最后一条 `phase: "final_answer"` 的 `agentMessage` 为准;若 Codex 没有发出明确的最终阶段,则以最后一条 `phase: null` 的消息作为兼容性回退。过程说明绝不会取代上述任一答案;成功完成的轮次若没有非空白答案,结果也会判为错误。 -对于命令与文件审批,无人值守的提供方会从请求给出的决策选项中选择一项不予批准的决策,并优先选择 `cancel`;稳定的 0.146.0 请求形态没有决策选项列表,因此回退到 `decline`。它对权限请求返回作用域限于当前轮次的空权限集,不向用户输入请求提供任何答案,并拒绝 MCP elicitation。若请求在无人值守模式下没有合法响应,或是未知服务器请求,此次运行就会失败。 +对于命令与文件审批,无人值守的提供方会从请求给出的决策选项中选择一项不予批准的决策,并优先选择 `cancel`;稳定的 0.147.0 请求形态没有决策选项列表,因此回退到 `decline`。它对权限请求返回作用域限于当前轮次的空权限集,不向用户输入请求提供任何答案,并拒绝 MCP elicitation。若请求在无人值守模式下没有合法响应,或是未知服务器请求,此次运行就会失败。 本地取消会在结果竞态中胜出并映射为 `aborted`。失败轮次的 `codexErrorInfo` 若为 `contextWindowExceeded`,则映射为 `max-tokens`;其他任何远端中断或失败轮次都映射为 `error`,且本版本不会产生 `refusal`。`dispose()` 具有幂等性:如果当前的两个标识符均已知,它会尽力请求 `turn/interrupt`,关闭 JSON-RPC 通信链路,结束标准输入,调用共享的进程树逐级终止机制,并等待整棵进程树退出。结果失败与独立的清理失败仍彼此分离。 @@ -47,7 +47,7 @@ ## 产品兼容性与证据 -生产环境的协议层有意只实现这一单次执行契约所需的 app-server 方法。开发证据锁定在 `@openai/codex@0.146.0` / `codex-cli 0.146.0`:无密钥真实产品测试使用非空的伪密钥,驱动官方二进制程序连接回环 Responses 服务,并证明任务、身份验证、精确回答、取消、审批与进程树退出。独立的 Loader 装配 e2e 会在没有可用 `codex` 命令时启动与 README 同形的用户配置,验证固定提供方与只支持前台执行的工具 schema,并记录零次子级启动。带密钥 e2e 会启动生产提供方和真实 Codex,再通过一个仅限回环、将 Responses 转为 Chat Completions 的测试桥接层,从固定的 DeepSeek 官方服务获得唯一答案;该桥接层既不属于生产功能,也不代表 Codex 原生支持 DeepSeek 的 Chat Completions API。该 NPM 包仅作为测试依赖;部署环境仍需通过 `PATH` 提供 `codex`。 +生产环境的协议层有意只实现这一单次执行契约所需的 app-server 方法。开发证据锁定在 `@openai/codex@0.147.0` / `codex-cli 0.147.0`:无密钥真实产品测试使用非空的伪密钥,驱动官方二进制程序连接回环 Responses 服务,并证明任务、身份验证、精确回答、取消、审批与进程树退出。独立的 Loader 装配 e2e 会在没有可用的 `codex` 命令时启动与 README 同形的用户配置,验证固定提供方与只支持前台执行的工具 schema,并记录零次子级启动。带密钥 e2e 会启动生产提供方和真实 Codex,再通过一个仅限回环、将 Responses 转为 Chat Completions 的测试桥接层,从固定的 DeepSeek 官方服务获得唯一答案;该桥接层既不属于生产功能,也不代表 Codex 原生支持 DeepSeek 的 Chat Completions API。该 NPM 包仅作为测试依赖;部署环境仍需通过 `PATH` 提供 `codex`。 ## 模型体验 @@ -83,7 +83,7 @@ Codex 子任务会在一个全新的临时线程中,以单个轮次接收这 - **每次运行均新建一个进程、一个线程和一个轮次**:不支持续接、恢复、池化、进度流或产品会话持久化。 - **产品安装和账户状态由宿主管理**:`codex` 缺失或不兼容、配置错误或身份验证失败,都会呈现为启动错误或运行错误;本插件不提供安装程序、登录流程或运行时版本门禁。 -- **兼容性由开发证据锁定**:若要从已验证的 0.146.0 协议基线升级,必须重新生成上游 schema 证据,并重新运行握手、答案选择、审批、取消、无密钥真实产品以及带密钥的 DeepSeek 随机数测试。 +- **兼容性由开发证据锁定**:若要从已验证的 0.147.0 协议基线升级,必须重新生成上游 schema 证据,并重新运行握手、答案选择、审批、取消、无密钥真实产品以及带密钥的 DeepSeek 随机数测试。 - **没有人工审批路径**:已知的无人值守审批请求会被拒绝,未知服务器请求会以默认拒绝方式使运行失败;部署方无法通过本包配置允许策略。 - **仅返回最终文本**:推理、过程说明、中间消息、工具通信、用量信息、stderr 和工作区差异仍只保留在产品内部。 - **没有可选的共享能力**:对于本提供方,共享服务会拒绝输出 schema、子任务角色设定、工具筛选和 harness 深度强制约束。 diff --git a/packages/subagent/subagent-codex/package.json b/packages/subagent/subagent-codex/package.json index 873cf1d77c..30788aa2e5 100644 --- a/packages/subagent/subagent-codex/package.json +++ b/packages/subagent/subagent-codex/package.json @@ -49,7 +49,7 @@ "@deepseek-ai/dsh-subprocess": "workspace:^", "@deepseek-ai/dsh-subprocess-local": "workspace:^", "@deepseek-ai/dsh-timeout": "workspace:^", - "@openai/codex": "0.146.0", + "@openai/codex": "0.147.0", "cordis": "^4.0.0-rc.7" } } diff --git a/packages/subagent/subagent-codex/src/wire.ts b/packages/subagent/subagent-codex/src/wire.ts index 51be212841..bc00ff0acf 100644 --- a/packages/subagent/subagent-codex/src/wire.ts +++ b/packages/subagent/subagent-codex/src/wire.ts @@ -1,5 +1,5 @@ /** - * Minimal Codex app-server 0.146.0 protocol adapter. The shared JSON-RPC + * Minimal Codex app-server 0.147.0 protocol adapter. The shared JSON-RPC * transport owns framing and request correlation; this module owns only the * product methods, current thread/turn association, unattended approval * responses, and terminal-answer selection. diff --git a/packages/subagent/subagent-codex/tests/real-deepseek.e2e.ts b/packages/subagent/subagent-codex/tests/real-deepseek.e2e.ts index 29c5536bc0..5c39ebfb3d 100644 --- a/packages/subagent/subagent-codex/tests/real-deepseek.e2e.ts +++ b/packages/subagent/subagent-codex/tests/real-deepseek.e2e.ts @@ -109,8 +109,8 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)( const version = await execFileAsync(join(codexBinDir, 'codex'), ['--version'], { env: { ...process.env, ...env }, }) - expect(codexPackage.version).toBe('0.146.0') - expect(version.stdout.trim()).toBe('codex-cli 0.146.0') + expect(codexPackage.version).toBe('0.147.0') + expect(version.stdout.trim()).toBe('codex-cli 0.147.0') const parent = { id: 'deepseek-e2e-parent', diff --git a/packages/subagent/subagent-codex/tests/real-product.spec.ts b/packages/subagent/subagent-codex/tests/real-product.spec.ts index badab946e6..9c3de65229 100644 --- a/packages/subagent/subagent-codex/tests/real-product.spec.ts +++ b/packages/subagent/subagent-codex/tests/real-product.spec.ts @@ -140,18 +140,18 @@ function responseInputTexts(body: Record): string[] { }) } -describe('real @openai/codex 0.146.0 product', () => { +describe('real @openai/codex 0.147.0 product', () => { it('passes the exact task and fake authentication to local Responses and returns exact text', async () => { - const sentinel = 'REAL_CODEX_SENTINEL_0_146_0' + const sentinel = 'REAL_CODEX_SENTINEL_0_147_0' const task = 'Return the fixture sentinel exactly.' const { harness, fixture } = await realHarness([ { kind: 'complete', text: sentinel }, ]) - expect(codexPackage.version).toBe('0.146.0') + expect(codexPackage.version).toBe('0.147.0') const version = await execFileAsync(process.execPath, [codexEntry, '--version'], { env: { ...process.env, ...harness.env }, }) - expect(version.stdout.trim()).toBe('codex-cli 0.146.0') + expect(version.stdout.trim()).toBe('codex-cli 0.147.0') const run = await harness.ctx.subagents.start('codex', { prompt: [{ type: 'text', text: task }], diff --git a/packages/subagent/subagent-codex/tests/responses-fixture.ts b/packages/subagent/subagent-codex/tests/responses-fixture.ts index cac49b9158..2c7e5534d0 100644 --- a/packages/subagent/subagent-codex/tests/responses-fixture.ts +++ b/packages/subagent/subagent-codex/tests/responses-fixture.ts @@ -86,7 +86,7 @@ function responseObject(text: string): Record { } /** - * Build the minimal Responses SSE event sequence consumed by Codex 0.146.0. + * Build the minimal Responses SSE event sequence consumed by Codex 0.147.0. * @param text - exact assistant answer. * @returns ordered response lifecycle events. */ diff --git a/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts b/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts index de89aa4854..9318c59787 100644 --- a/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts +++ b/packages/subagent/subagent-codex/tests/subagent-codex.spec.ts @@ -199,7 +199,7 @@ async function initializeWire(): Promise<{ wire.start() const initializing = wire.initialize(new AbortController().signal) const initialize = await child.peer.nextMethod('initialize') - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await initializing expect(await child.peer.nextMethod('initialized')).toEqual({ jsonrpc: '2.0', @@ -219,7 +219,7 @@ async function publishRun( ) { const starting = startCodexRun(request(undefined, signal), runSpec(child, specOverrides)) const initialize = await child.peer.nextMethod('initialize') - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await child.peer.nextMethod('initialized') const threadStart = await child.peer.nextMethod('thread/start') child.peer.respond(threadStart, { thread: { id: 'thread-1', ephemeral: true } }) @@ -380,7 +380,7 @@ describe('CodexAppServerWire', () => { requestAttestation: false, }, }) - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await initializing await child.peer.nextMethod('initialized') @@ -855,7 +855,7 @@ describe('run lifecycle and quiescence', () => { void starting.then(() => { published = true }) const initialize = await child.peer.nextMethod('initialize') expect(published).toBe(false) - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await child.peer.nextMethod('initialized') const threadStart = await child.peer.nextMethod('thread/start') expect(published).toBe(false) @@ -962,7 +962,7 @@ describe('run lifecycle and quiescence', () => { runSpec(child), ) const initialize = await child.peer.nextMethod('initialize') - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await child.peer.nextMethod('initialized') const threadStart = await child.peer.nextMethod('thread/start') child.peer.respond(threadStart, { thread: { id: 'thread-1', ephemeral: true } }) @@ -1032,7 +1032,7 @@ describe('run lifecycle and quiescence', () => { signal: new AbortController().signal, }) const initialize = await child.peer.nextMethod('initialize') - child.peer.respond(initialize, { userAgent: 'codex-cli 0.146.0' }) + child.peer.respond(initialize, { userAgent: 'codex-cli 0.147.0' }) await child.peer.nextMethod('initialized') const threadStart = await child.peer.nextMethod('thread/start') child.peer.respond(threadStart, { thread: { id: 'thread-1', ephemeral: true } }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccbc67c764..b11aa900cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5604,8 +5604,8 @@ importers: specifier: workspace:^ version: link:../../util/timeout '@openai/codex': - specifier: 0.146.0 - version: 0.146.0 + specifier: 0.147.0 + version: 0.147.0 cordis: specifier: ^4.0.0-rc.7 version: link:../../../vendor/cordis @@ -8594,43 +8594,43 @@ packages: '@nodable/entities@2.2.0': resolution: {integrity: sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==} - '@openai/codex@0.146.0': - resolution: {integrity: sha512-yG3sPWNda/2YAIQIDq9MrrjoCTIQ7rxYM5IasrG3VBcuhCLTkgeg/JzqmJq1V98RE4MJ5jCxDXXQlOjrditFRw==} + '@openai/codex@0.147.0': + resolution: {integrity: sha512-EQLEXecAG2ptxI7UpBMo2TR/ga5596/c/OsYF/0LoUDh5JANZ7IoGqlzBEWbuEVQ76JePIbtTW/ihCkp1a7Z3w==} engines: {node: '>=16'} hasBin: true - '@openai/codex@0.146.0-darwin-arm64': - resolution: {integrity: sha512-nb61yX4r5L6Z0dlC4o3u0GAK1YCd4TUvjaB382bajDoh84V+uv2hTBIVZ++fgXWV9yoeuNrNnNcn7GoTGOe2Tg==} + '@openai/codex@0.147.0-darwin-arm64': + resolution: {integrity: sha512-BEUVkiOW7kLcRyrMLfAr/h9wF8sRVJyZDy6OHtVn6QGDXiv3BvAZVTY1Pu9xF7KdIdkYXbp4uayN0aDQQaAUJw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@openai/codex@0.146.0-darwin-x64': - resolution: {integrity: sha512-hTQR5jy/ObfTf1MDnuJCZJAe+SljKE8DDwQWN6lDFgjsPhMQz852U2tILt8Ei+G5GkQSzemHYKl2AYPwW0Y5xw==} + '@openai/codex@0.147.0-darwin-x64': + resolution: {integrity: sha512-Tb8McE5SvJIH0Vs5R6sq7u+quiC931yan2KOOl6km1OdZ82+Wi7eF5XrSFPs5CF7xCgoIK4Vs+byMbT5hN+ZUw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@openai/codex@0.146.0-linux-arm64': - resolution: {integrity: sha512-qiYDxkkEFnXG7joadJW6Q+XcgyDXCpGdpa9nk/c+i0gEomur1j7bHvx12NfWWCF/y8Tqri6ay+FLuC2MjdehtA==} + '@openai/codex@0.147.0-linux-arm64': + resolution: {integrity: sha512-SLC1JXw2TYfr/c3HhrJubyyLelq7vTOLWVmiThFA+z0+WgzCPmaseJ/kzDD3Gge/TO7fCnnj7UcPmC0d2c8XAg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@openai/codex@0.146.0-linux-x64': - resolution: {integrity: sha512-fswvyGprAPCMiOEue/7MKMk7pCjh9kZIJfJX5i9atmfnmGYbYCcUhZsEH9LEP0+0t5xyPqDbfNXY7NSxIVuXxA==} + '@openai/codex@0.147.0-linux-x64': + resolution: {integrity: sha512-0W9MBxPpWW0cSkNqrTDN2jR7rzzT7oNMhQY5446lT2Lw5cz5yhDTck4Va9rjkQEm+HlFzP/dmEMSZbXfJsINmw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@openai/codex@0.146.0-win32-arm64': - resolution: {integrity: sha512-EW6zdjDe+SLX2Iw+xymJ5+Pz2+DGexdstfFHXh4Ub+TfJsQPiMjGfZfNaoWgdJ2FsqSIzVKu2+G0KCMGYz2W8g==} + '@openai/codex@0.147.0-win32-arm64': + resolution: {integrity: sha512-e2ZstJ8zT8Rm1nvR7CUVO+Gr3cTChE41+VfOzGhynzDXEoW0wfbjUQbc2bWbh1arG94LMm4y3dqBtUIbSrfeGA==} engines: {node: '>=16'} cpu: [arm64] os: [win32] - '@openai/codex@0.146.0-win32-x64': - resolution: {integrity: sha512-b3lxMYeR0+IhstNo4JjX1P9cPc1xwVcCVkPd1lD1wpWPJ0SBhpIkPczwbu3ZRkJcdyl342+rgyf4DUrbZLdrGA==} + '@openai/codex@0.147.0-win32-x64': + resolution: {integrity: sha512-oT7Ss5fAPf2fiWE9QNURqZcQGAAawSVxmIUdgPzckq4KFZAM+pRz9JbM4Rr498CjtbNgTOjWvDJ+DXvIBSfOPA==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -13889,31 +13889,31 @@ snapshots: '@nodable/entities@2.2.0': {} - '@openai/codex@0.146.0': + '@openai/codex@0.147.0': optionalDependencies: - '@openai/codex-darwin-arm64': '@openai/codex@0.146.0-darwin-arm64' - '@openai/codex-darwin-x64': '@openai/codex@0.146.0-darwin-x64' - '@openai/codex-linux-arm64': '@openai/codex@0.146.0-linux-arm64' - '@openai/codex-linux-x64': '@openai/codex@0.146.0-linux-x64' - '@openai/codex-win32-arm64': '@openai/codex@0.146.0-win32-arm64' - '@openai/codex-win32-x64': '@openai/codex@0.146.0-win32-x64' + '@openai/codex-darwin-arm64': '@openai/codex@0.147.0-darwin-arm64' + '@openai/codex-darwin-x64': '@openai/codex@0.147.0-darwin-x64' + '@openai/codex-linux-arm64': '@openai/codex@0.147.0-linux-arm64' + '@openai/codex-linux-x64': '@openai/codex@0.147.0-linux-x64' + '@openai/codex-win32-arm64': '@openai/codex@0.147.0-win32-arm64' + '@openai/codex-win32-x64': '@openai/codex@0.147.0-win32-x64' - '@openai/codex@0.146.0-darwin-arm64': + '@openai/codex@0.147.0-darwin-arm64': optional: true - '@openai/codex@0.146.0-darwin-x64': + '@openai/codex@0.147.0-darwin-x64': optional: true - '@openai/codex@0.146.0-linux-arm64': + '@openai/codex@0.147.0-linux-arm64': optional: true - '@openai/codex@0.146.0-linux-x64': + '@openai/codex@0.147.0-linux-x64': optional: true - '@openai/codex@0.146.0-win32-arm64': + '@openai/codex@0.147.0-win32-arm64': optional: true - '@openai/codex@0.146.0-win32-x64': + '@openai/codex@0.147.0-win32-x64': optional: true '@opentelemetry/api-logs@0.220.0':