From 8cb569e284e5eefaf82f8ec27c993f6f4d781e30 Mon Sep 17 00:00:00 2001 From: Adam Dalloul <47503782+Adam-Dalloul@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:27:47 -0700 Subject: [PATCH] feat(acp): move codex-acp to 1.10.0 The pin is what selects the codex core. 1.8.0 depends on @openai/codex ^0.152.0, and caret on a 0.x pins the minor, so it resolves no higher than 0.152.1 and can never reach a 0.153 core. 1.10.0 depends on ^0.153.3, which is the one that lists gpt-6-astra. The adapter bundle itself names a single model, gpt-5.6-luna for TitleGenerator; every id the picker offers comes from the core underneath. Neither 1.9.0 nor 1.10.0 moves a surface codeg reads. sessionCapabilities is byte-identical to 1.8.0's, so supports_fork and the rest are unchanged, and the whole initialize delta across the two releases is one new agent capability (_meta.authStatus) plus a fourth name in the AIR array. 1.9.0 pushes _auth/status_update with no client capability gating it. codeg drops it on the unrecognized-ext-notification arm of maybe_emit_ext_notification, which is the tolerance that arm exists for. 1.10.0 emits async_task_spawned and async_task_state_update, neither of which can deserialize into agent-client-protocol-schema 0.11.7's SessionUpdate. They stay off the wire because the adapter gates its whole async-task manager on the AIR asyncTasks advertisement, and codeg sends Codex ["sessionFailure"] only. Also correct two comments asserting codex has no async-task code. It has one now, narrower than Claude's, and enabling it is a separate change. --- src-tauri/src/acp/connection.rs | 50 ++++++++++------- src-tauri/src/acp/preflight.rs | 2 +- src-tauri/src/acp/registry.rs | 95 +++++++++++++++++++++++++++++++-- 3 files changed, 124 insertions(+), 23 deletions(-) diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index f3eafef071..55c67dded9 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -3832,20 +3832,31 @@ fn build_client_capabilities( meta.insert("subagent-transcript".to_string(), serde_json::Value::Bool(true)); } // claude-agent-acp 0.73.0 added "asyncTasks", and it is advertised — to - // claude ONLY, because codex-acp 1.8.0 does not implement the channel - // (its bundle contains no `asyncTasks` string at all). It publishes the - // lifecycle of Claude's NON-AGENT background work (background shells, - // workflows, monitors) as `async_task_spawned` / `_progress` / - // `_state_update`, all on the parent session id. Unlike the two capabilities - // below, this one adds something codeg cannot get anywhere else: the - // transcript watcher (`background_watch`) can see that a task was launched - // but explicitly CANNOT tell a still-running task from one whose CLI died, - // it never sees workflow/monitor tasks at all (they produce no tool call), - // and there is no way to stop a task from outside. This channel carries a - // real terminal edge, a liveness boundary, an output file path, and the - // `_session/async_task/stop` control. Sub-agent tasks stay out of it by the - // adapter's own filter (`taskType: "local_agent"` is marked ignored), so - // advertising this does not disturb the sub-agent surfaces. + // claude ONLY. It publishes the lifecycle of Claude's NON-AGENT background + // work (background shells, workflows, monitors) as `async_task_spawned` / + // `_progress` / `_state_update`, all on the parent session id. Unlike the + // two capabilities below, this one adds something codeg cannot get + // anywhere else: the transcript watcher (`background_watch`) can see that a + // task was launched but explicitly CANNOT tell a still-running task from + // one whose CLI died, it never sees workflow/monitor tasks at all (they + // produce no tool call), and there is no way to stop a task from outside. + // This channel carries a real terminal edge, a liveness boundary, an output + // file path, and the `_session/async_task/stop` control. Sub-agent tasks + // stay out of it by the adapter's own filter (`taskType: "local_agent"` is + // marked ignored), so advertising this does not disturb the sub-agent + // surfaces. + // + // codex-acp 1.10.0 implements the same capability name (it was absent + // through 1.8.0), so "codex cannot answer" is no longer why it is withheld + // there. The reason is now that it buys less: codex publishes background + // SHELLS only, with no `async_task_progress`, no `outputFilePath` and no + // `usage`, and it sets `showInTranscript: false` because the terminal + // already has a tool call — so the frames add a terminal-state edge and a + // stop control to a card codeg draws anyway, rather than revealing work it + // otherwise cannot see. Turning it on is a behavior change that should + // carry its own reasoning and verification, not ride a pin bump; + // `air_async_task_delta` is already agent-agnostic and reads both variants + // codex emits, so only this advertisement stands in the way. // // The remaining two AIR capabilities are deliberately still out. // claude-agent-acp 0.69.0 and codex-acp 1.4.0 added @@ -14174,10 +14185,13 @@ mod tests { // And exactly this much. Adding a capability here is not free — it // is what turns the corresponding behavior on. // - // "asyncTasks" (claude-agent-acp 0.73.0) IS wanted, and only claude - // has it: codex-acp 1.8.0 contains no async-task code at all, so - // advertising it there would be a promise about a channel that - // cannot answer. + // "asyncTasks" (claude-agent-acp 0.73.0) IS wanted, and stays + // claude-only. codex-acp 1.10.0 does implement the name, so this is + // no longer a promise codex cannot answer — its channel is just + // narrower (background shells only, no progress frames, and + // `showInTranscript: false` because the terminal already has a tool + // call). Enabling it for codex is its own change; see the + // advertisement site. // // The other two stay out. "agentFileChangeReport" // (claude-agent-acp 0.69.0 / codex-acp 1.4.0) buys an extra model diff --git a/src-tauri/src/acp/preflight.rs b/src-tauri/src/acp/preflight.rs index 215f4f366f..4fcf0fb879 100644 --- a/src-tauri/src/acp/preflight.rs +++ b/src-tauri/src/acp/preflight.rs @@ -738,7 +738,7 @@ mod adapter_tests { #[test] fn codex_adapter_info_uses_codex_home() { let info = info_for(AgentType::Codex, None, true); - assert_eq!(info.adapter_package, "@agentclientprotocol/codex-acp@1.8.0"); + assert_eq!(info.adapter_package, "@agentclientprotocol/codex-acp@1.10.0"); assert_eq!(info.adapter_cmd, "codex-acp"); assert!(info.adapter_installed); assert_eq!(info.native_cmd, "codex"); diff --git a/src-tauri/src/acp/registry.rs b/src-tauri/src/acp/registry.rs index fc8b870648..dae9ff96fa 100644 --- a/src-tauri/src/acp/registry.rs +++ b/src-tauri/src/acp/registry.rs @@ -895,9 +895,96 @@ pub fn get_agent_meta(agent_type: AgentType) -> AcpAgentMeta { // three names). Steering still ships no `promptRequired` opt-in // (tarball grep: zero hits), and there is still no `engines.node`, // so the 20.0.0 floor is retained. + // + // 1.9.0 and 1.10.0 are both small, and neither moves a surface + // codeg reads today: `sessionCapabilities` is byte-identical to + // 1.8.0's, so `supports_fork` and the rest are unchanged, and the + // entire `initialize` delta across the two releases is one new + // agent capability (`_meta.authStatus`) plus a fourth name in the + // AIR array. The reason to take them is `@openai/codex`. + // + // (a) 1.9.0 reports the agent's auth identity (#467). A new + // presence-only `agentCapabilities._meta.authStatus = {}` means + // "this agent pushes `_auth/status_update`", and it then pushes + // `{authStatus: {kind, label, detail?, account?}}` — `kind` one of + // `account` / `api_key` / `external` / `gateway` / `none`, `label` + // already presentable ("ChatGPT Pro", "OpenAI API key", "AWS + // Bedrock", "Not logged in"). Deliberately NOT bilateral: there is + // no client capability to advertise and no request to make, and it + // pushes on every connection, deduped by payload equality with the + // first push always sent. It reports the AGENT-owned login only — + // `applyGatewayConfig` now records whether routing came from the + // `gateway` auth method or from ACP `providers/set`, and the latter + // is excluded on purpose. + // + // codeg drops it. `_auth/status_update` reaches + // `maybe_emit_ext_notification`, no mapper claims it, and + // `is_known_ext_method` is false, so it leaves on the + // unrecognized-ext-notification `debug!`. That is the correct + // no-op and exactly the tolerance that arm exists for. Worth a + // second look only if the version card ever grows an identity + // line: this is the one frame either adapter sends that names the + // account paying for the session. + // + // (b) 1.9.0 also completes `/status` usage and limits (#463) via + // `src/RateLimitsMap.ts` and the app-server `account/rateLimits/read` + // + `account/rateLimits/updated`. Not a wire shape at all — the map + // is rendered by `buildStatusMessage` into the `/status` markdown + // and arrives as an ordinary `agent_message_chunk`. + // + // (c) 1.10.0 exposes background terminals as async tasks (#460), + // and this is the one that could have bitten. It adds + // `src/async-tasks/{AsyncTaskExtension,CodexBackgroundTerminalTasks}.ts` + // and `src/AcpSessionExtensions.ts`, whose whole body is a cast + // that lets a notification OUTSIDE the SDK schema typecheck — + // which is precisely what `async_task_spawned` and + // `async_task_state_update` are. Neither variant can deserialize + // into `agent-client-protocol-schema` 0.11.7's `SessionUpdate`, + // the same wall the subagent RFD hit. + // + // They stay off the wire, and not by luck. `createAsyncTasks` + // passes `clientSupportsAirCapability(caps, "asyncTasks")` into the + // manager as `enabled`, `isActive()` is `enabled && !disposed`, and + // every entry point (`handleNotification`, `observeCommandStarted`, + // `observeCommandCompleted`, `sync`, `stop`) returns early on it. + // `build_client_capabilities` sends Codex `["sessionFailure"]`, so + // `enabled` is false for the whole connection and neither variant — + // nor the paired `tool_call_update` carrying + // `_meta.jetbrains.air.asyncTasks.backgrounded` — is ever emitted. + // `_session/async_task/stop` is client-initiated, so it costs + // nothing either. + // + // Two comments asserting codex has no async-task code are corrected + // with this bump, because the channel is real now. Advertising it + // to Codex is a separate change, not a pin bump, and it buys less + // than Claude's did: the vocabulary is background SHELLS only, + // there is no `async_task_progress`, no `outputFilePath` and no + // `usage`, and codex sets `showInTranscript: false` because the + // terminal ALREADY has a tool call — the frame adds a terminal-state + // edge and a stop control to a card codeg draws anyway, where for + // Claude it was the only sight of workflow and monitor tasks at all. + // `air_async_task_delta` is not gated on `agent_type` and already + // reads both variants and every field codex populates, so the + // consumer side is done whenever that case gets made. + // + // (d) `@openai/codex` moves ^0.152.0 → ^0.153.3 across the two + // releases (#469, #476), and this is the actual reason to bump. The + // adapter bundle names exactly one model, `gpt-5.6-luna` for + // `TitleGenerator`; every id the picker offers comes from the + // pinned core. Caret on a `0.x` pins the MINOR, so 1.8.0's + // ^0.152.0 resolves no higher than 0.152.1 and cannot reach a 0.153 + // core however long it sits. That is why 1.8.0 cannot list + // `gpt-6-astra` and 1.10.0 can. + // + // Re-checked against both tarballs and unchanged: native subagent + // sessions and `agentFileChangeReport` still not adopted, the + // `SessionFork` fork-point resolution (`messageId` / + // `messageFingerprint` / `messageOccurrence`) untouched, still no + // `promptRequired` steering opt-in (tarball grep: zero hits), and + // still no `engines.node`, so the 20.0.0 floor is retained. distribution: AgentDistribution::Npx { - version: "1.8.0", - package: "@agentclientprotocol/codex-acp@1.8.0", + version: "1.10.0", + package: "@agentclientprotocol/codex-acp@1.10.0", cmd: "codex-acp", args: &[], env: &[], @@ -1809,8 +1896,8 @@ mod tests { ); assert_npx_version( AgentType::Codex, - "1.8.0", - "@agentclientprotocol/codex-acp@1.8.0", + "1.10.0", + "@agentclientprotocol/codex-acp@1.10.0", Some("20.0.0"), ); assert_npx_version(AgentType::Pi, "0.0.33", "pi-acp@0.0.33", Some("22.0.0"));