diff --git a/src-tauri/src/acp/background_watch.rs b/src-tauri/src/acp/background_watch.rs index 82a16057f6..a09c35e6ed 100644 --- a/src-tauri/src/acp/background_watch.rs +++ b/src-tauri/src/acp/background_watch.rs @@ -39,13 +39,19 @@ //! //! * **Session title** — Claude Code's generated name arrives as a dedicated //! `ai-title` transcript record whenever the background summarizer finishes, -//! routinely AFTER the turn that triggered it ended. The ACP adapter only -//! pulls the name at turn-end (`maybeUpdateSessionTitle`, claude-agent-acp -//! 0.69.0), so on a short session there is nothing to read yet and no wire -//! event ever follows. These bytes are already being tailed, so the records -//! are folded here and handed to [`publish_native_title`] — the same path a -//! live ACP title takes. Not activity: it rides alongside the activity event -//! rather than inside it (see `run_watch`). +//! routinely AFTER the turn that triggered it ended. Through claude-agent-acp +//! 0.70.0 the adapter only pulled the name at turn-end +//! (`maybeUpdateSessionTitle`), so on a short session there was nothing to +//! read yet and no wire event ever followed. 0.71.0 (#984) asks the CLI to +//! generate one at turn-end instead (`SessionTitles`, a background +//! `generate_session_title` control request), so a wire title now usually +//! does follow — but generation needs ten characters of collected context +//! and can return null, so this fold stays the backstop. These bytes are +//! already being tailed, so the records are folded here and handed to +//! [`publish_native_title`] — the same skip-cached path a live ACP title +//! takes, which also absorbs the overlap when both fire. Not activity: it +//! rides alongside the activity event rather than inside it (see +//! `run_watch`). //! //! The watcher is connection-scoped on purpose: background work cannot outlive //! the agent CLI process, whose lifetime IS the connection's. Poll ticks are diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index 581e190642..aace8f6526 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -3737,18 +3737,22 @@ fn build_client_capabilities( // // codex-acp 1.7.0 added a third, "nativeSubagentSessions" (the draft ACP // subagent RFD; the canonical gate is a `clientCapabilities.subagents: {}` - // field, with this AIR key as the fallback for SDKs that strip it). It must + // field, with this AIR key as the fallback for SDKs that strip it); + // claude-agent-acp 0.71.0 speaks it too, plus a fourth, "asyncTasks" + // (`async_task_spawned` / `async_task_progress` / + // `async_task_state_update`), gated the same way. They must // stay out for a harder reason than cost: `agent-client-protocol-schema` - // 0.11.7 cannot RECEIVE the result. Its `SessionUpdate` is an + // 0.11.7 cannot RECEIVE the results. Its `SessionUpdate` is an // internally-tagged enum with no catch-all arm, so the `subagent_spawned` / // `subagent_state_update` notifications would fail to deserialize — and - // since the adapter switches child messages, thoughts, tools and + // since the adapters switch child messages, thoughts, tools and // permissions onto a child session id announced only in that first // notification, opting in would make subagent work vanish from the timeline // rather than render better. Without the advertisement the lifecycle stays - // the legacy `subAgentActivity` tool call codeg already renders, whose - // shape is unchanged from 1.4.0. Revisit when the schema crate ships both - // the capability field and the update variants. + // what codeg already renders — codex's legacy `subAgentActivity` tool call + // (unchanged from 1.4.0) and claude's `subagent-transcript` streaming + // (verified intact through 0.73.0). Revisit when the schema crate ships + // both the capability field and the update variants. if matches!(agent_type, AgentType::ClaudeCode | AgentType::Codex) { meta.insert( "jetbrains".to_string(), @@ -10224,8 +10228,10 @@ fn is_codex_plan_review( /// `_meta.permission = {version: 1, title, description?}`. The title is now one /// of four fixed strings and the reason lives only in `description`, so a card /// built from the tool call alone would read "Edit files" where it used to -/// explain WHY the edit needs approval. claude-agent-acp does not send this -/// block; nothing changes for it. +/// explain WHY the edit needs approval. claude-agent-acp joined with its +/// 0.71.0 permission rebuild — same `{version: 1, title, description?}`, the +/// SDK's decisionReason riding in `description` as "Reason: …" — so the hoist +/// serves both built-ins. /// /// Hoisting rather than adding an event field is deliberate: the tool call is /// already the card's payload end-to-end (`PendingPermissionState.tool_call`, @@ -13323,14 +13329,17 @@ mod tests { .iter() .any(|v| v.as_str() == Some("sessionFailure"))); // And nothing else. Adding a capability here is not free — it is - // what turns the corresponding behavior on, and neither of the two + // what turns the corresponding behavior on, and none of the three // that exist is wanted: "agentFileChangeReport" // (claude-agent-acp 0.69.0 / codex-acp 1.4.0) buys an extra model // round-trip per turn for a clamped, self-reported subset of what - // the `workspace_state` watcher already sees, and - // "nativeSubagentSessions" (codex-acp 1.7.0) would move subagent + // the `workspace_state` watcher already sees; + // "nativeSubagentSessions" (codex-acp 1.7.0 / claude-agent-acp + // 0.71.0) would move subagent // output onto child session ids carried by `SessionUpdate` variants - // `agent-client-protocol-schema` 0.11.7 cannot deserialize at all. + // `agent-client-protocol-schema` 0.11.7 cannot deserialize at all; + // and "asyncTasks" (claude-agent-acp 0.71.0) rides the same + // undeliverable `async_task_*` variants. // See the reasoning at the advertisement site before relaxing this. assert_eq!( capabilities, diff --git a/src-tauri/src/acp/preflight.rs b/src-tauri/src/acp/preflight.rs index 3a85bff46f..819e2d17e0 100644 --- a/src-tauri/src/acp/preflight.rs +++ b/src-tauri/src/acp/preflight.rs @@ -51,7 +51,7 @@ pub struct CheckItem { /// `None` on [`PreflightResult`] for every non-adapter agent. #[derive(Debug, Clone, Serialize)] pub struct AdapterInfo { - /// npm spec codeg installs, e.g. "@agentclientprotocol/claude-agent-acp@0.69.0". + /// npm spec codeg installs, e.g. "@agentclientprotocol/claude-agent-acp@0.73.0". pub adapter_package: String, /// Command the launch gate resolves, e.g. "claude-agent-acp". pub adapter_cmd: String, @@ -724,7 +724,7 @@ mod adapter_tests { ); assert_eq!( info.adapter_package, - "@agentclientprotocol/claude-agent-acp@0.69.0" + "@agentclientprotocol/claude-agent-acp@0.73.0" ); assert_eq!(info.adapter_cmd, "claude-agent-acp"); assert!(!info.adapter_installed); diff --git a/src-tauri/src/acp/registry.rs b/src-tauri/src/acp/registry.rs index bab516976d..9ba3a8793c 100644 --- a/src-tauri/src/acp/registry.rs +++ b/src-tauri/src/acp/registry.rs @@ -577,9 +577,112 @@ pub fn get_agent_meta(agent_type: AgentType) -> AcpAgentMeta { // can advertise `["sessionFailure", "agentFileChangeReport"]` — an // ADDITIVE element in an array codeg only ever membership-tests, // so the session-failure gate is unaffected. + // 0.70.0 (#1002) is provider plumbing only: the + // `providers/list|set|disable` methods and the `providers: {}` + // advertisement (both already in 0.69.0) now also cover LOADED + // sessions — a switch respawns the SDK behind the live session + // with a rebuilt routing env (base-URL/Bedrock/Vertex vars, + // local credential checks bypassed via "acp-proxy" sentinels). + // All of it sits inside client-called requests codeg never + // sends; dependencies, the session_update surface and the Node + // floor are unchanged (tarball-diffed — the ambient additions + // are a "Claude ACP started" boot line and provider-resolution + // logging). + // 0.71.0 (nine PRs; claude-agent-sdk 0.3.232→0.3.238, which + // vendors runtime 2.1.238; zod floor raised to ^4, #1057; dist + // split into modules) adds five session_update KINDS, every one + // gated on a client advertisement codeg does not make: + // `subagent_spawned`/`subagent_state_update` (#1017) ride only + // for a client advertising `clientCapabilities.subagents` + // (inexpressible in `agent-client-protocol-schema` 0.11.7) or + // AIR `nativeSubagentSessions`, and `async_task_spawned`/ + // `_progress`/`_state_update` only behind AIR `asyncTasks` — + // codeg's AIR array is pinned to exactly ["sessionFailure"] + // (tested), which the no-catch-all `SessionUpdate` argument in + // codex-acp 1.7.0's NOT-adopted paragraph makes load-bearing: + // the same "would fail to deserialize" logic applies verbatim. + // Without the advertisements the legacy shape is intact + // (tarball-verified): `subagent-transcript` streaming, + // `_meta.claudeCode.parentToolUseId`, permissions on the root + // session. The advertisement array grows to [sessionFailure, + // agentFileChangeReport, nativeSubagentSessions, asyncTasks] and + // `sessionCapabilities` gains `subagents: {}` — additive shapes + // codeg membership-tests or ignores. What codeg's wire DOES + // gain: (a) real session titles (#984) — the CLI's own + // auto-titler never arms under the Agent SDK, so the old + // turn-end pull mostly republished the raw first prompt; the + // adapter now asks for a generated title (a background + // `generate_session_title` control request, once per session, + // needs ≥10 chars of context, a user /rename always wins) over + // the SAME `session_info_update {title, updatedAt}` wire the + // SessionInfoUpdate arm already feeds to `publish_native_title`; + // the transcript-tail fallback in background_watch.rs stays as + // the backstop. (b) The permission layer is rebuilt + // (`src/permissions/*`): the option-level `_meta.permission = + // {version, changes[]}` contract from 0.64.1/#930 is GONE, and + // claude joins codex-acp 1.7.0's REQUEST-level + // `_meta.permission = {version: 1, title, description?}` + // (description = "Reason: ") — exactly what + // `hoist_request_permission_meta` forwards, so the card's reason + // line lights up for claude with no code change while + // `parsePermissionOptionChanges` simply no longer fires for it. + // ExitPlanMode approval gains clear-context lanes (#1004): new + // opaque option ids under standard kinds, and accepting one + // swaps the underlying SDK session while the SAME ACP turn stays + // in flight (`current_mode_update` + `config_option_update` + // mid-turn, then the plan continuation streams on) — transparent + // to codeg's turn-scoped runtime by construction. (c) Modes + // (#1025): the advertised list is now default/acceptEdits/plan/ + // auto (+ bypassPermissions behind the adapter's ALLOW_BYPASS), + // `dontAsk` is still parsed but no longer advertised, and each + // mode/option carries `_meta.kind` (standard/plan/auto_review/ + // full_access) — additive, unread. (d) The prompt response gains + // `_meta.quota = {token_count, model_usage[]}` (#1037), + // deliberately codex-shaped — unread here, input for a future + // usage surface. (e) `session/fork` (registered since 0.69.0) + // honors an AIR fork point (`_meta.jetbrains.air.fork.messageId`, + // #1046) — client-called; codeg doesn't. (f) #1045: a + // `_session/steering` arriving while a permission/elicitation + // card is open is delivered at non-interrupting priority instead + // of aborting the running cycle into the open prompt; the + // `promptRequired` contract is unchanged (tarball-verified), so + // the 0.65.0 floor keeps holding, just safer. The Windows + // `PowerShell` shell tool is now recognized alongside Bash (same + // description-title treatment). + // 0.72.0 moves both SDKs (@agentclientprotocol/sdk 1.3.0→1.4.0, + // claude-agent-sdk 0.3.238→0.3.252 = runtime 2.1.252) and rides + // the new runtime in #1065: a PostModelSwitch hook (fires on CLI + // 2.1.251+ only) mirrors switches the adapter didn't drive — a + // `/model x` typed as a prompt — back into the picker through + // the EXISTING `config_option_update`/`current_mode_update` + // surfaces; PreModelSwitch hooks can veto (`setModel` then fails + // with "Model switch blocked by a PreModelSwitch hook"); effort + // now seeds from the CLI's persisted per-model settings unless + // the user pinned it via the picker this session; and results + // echo the triggering send's `user_message_uuid` (SDK 0.3.246+), + // upgrading the adapter's internal orphan-result heuristics to + // an exact join. The ACP SDK bump is API-surface only: + // `unstable_createElicitation`/`unstable_completeElicitation` + // lose their prefixes while the wire methods stay + // `elicitation/create`/`elicitation/complete` (schema method + // tables diffed byte-identical) — moot here anyway, codeg + // advertises elicitation for codex and deepseek only. + // 0.73.0 (#1066) is the runtime and nothing else: dist/ is + // byte-identical to 0.72.0's and the only manifest change is + // claude-agent-sdk 0.3.252→0.3.257 (runtime 2.1.257). That is + // the point of this pin: model availability ships in the + // runtime — the API refuses new model ids on old ones ("Claude + // Code 2.1.247 does not support this model; version 2.1.251 or + // newer is required" for `claude-fable-5-1`, and the old pin's + // SDK 0.3.232 vendors 2.1.232) — so from 0.72.0 on the dynamic + // model config option lists new Anthropic models with no codeg + // change (verified live over stdio: 0.73.0 initializes and + // session/new's model option offers Default/Opus 1M/Fable/ + // Sonnet/Haiku). `engines.node` stays ">=22" across 0.70–0.73, + // so `node_required` holds. distribution: AgentDistribution::Npx { - version: "0.69.0", - package: "@agentclientprotocol/claude-agent-acp@0.69.0", + version: "0.73.0", + package: "@agentclientprotocol/claude-agent-acp@0.73.0", cmd: "claude-agent-acp", args: &[], env: &[], @@ -734,8 +837,11 @@ pub fn get_agent_meta(agent_type: AgentType) -> AcpAgentMeta { // standard ACP fields `parse_permission_tool_call` already reads // (`rawInput.command/cwd/url/additionalPermissions`, `locations`, // `content`). `handle_permission_request` hoists the request meta - // onto the card so the reason survives; claude-agent-acp 0.69.0 - // still emits `changes[]`, so that parser stays. Codex's option + // onto the card so the reason survives; claude-agent-acp joined + // this request-level contract in 0.71.0 (its `changes[]` ended at + // 0.70.0), so the option-level parser now serves codex's + // description-only MCP-approval options and any custom agent + // still speaking `changes[]`. Codex's option // IDs were also renamed (`allow_for_session`, // `accept_execpolicy_amendment`, `apply_network_policy_amendment:N` // …) — inert here, codeg only echoes back the selected id — and @@ -1616,8 +1722,8 @@ mod tests { fn registry_pins_current_acp_agent_versions() { assert_npx_version( AgentType::ClaudeCode, - "0.69.0", - "@agentclientprotocol/claude-agent-acp@0.69.0", + "0.73.0", + "@agentclientprotocol/claude-agent-acp@0.73.0", Some("22.0.0"), ); assert_npx_version(