WTA ACP 0.10→1.0 migration#367
Conversation
…h moves - tools/wta/Cargo.toml: agent-client-protocol 0.10 -> 1.0; drop stabilized features (unstable_session_list/model). No agent-client-protocol-tokio: AcpAgent ships in core 1.0; the helper crate is stuck at 0.11.1. - Mechanical: ~538 acp::<T> / agent_client_protocol::<T> -> ::schema::v1::<T>. - Adds doc/specs/acp-1.0-conductor-migration.md. - Crate does NOT compile yet: 47 structural errors (connection ctor + 5 trait impls + call sites) are chunk 2-4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… 2-4 TODO) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ld + 1017 tests green Confine 1.0 builder/dispatch to shim protocol/acp/conn.rs (ClientLink/AgentLink wrap ConnectionTo; spawn_client/agent return link+handle_io). impl Client/Agent -> on_receive_* enum dispatch. set_session_model removed in schema 1.1 -> local type, models via config-option. ext_* only enum-falls-through for _-prefixed, so intellterm.wta/* -> _intellterm.wta/*. N:1 multiplexer stays bespoke. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…entLink::new, helper set_session_model, record_channel_legacy) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
check-spelling found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Pull request overview
This PR performs Phase 0 of the WTA ACP 0.10 → 1.0 migration by updating the WTA helper/master ACP plumbing to the agent-client-protocol 1.0 builder/dispatch model while aiming to preserve existing behavior. It also adds a compat layer (protocol::acp::conn) to keep the rest of the WTA code using a “connection handle + handle_io future” shape similar to the pre-1.0 API.
Changes:
- Upgrades
agent-client-protocolto 1.0 and rewrites ACP message/type usage toacp::schema::v1::*. - Introduces
tools/wta/src/protocol/acp/conn.rsas a compatibility shim around 1.0ConnectionTo<>, and migrates helper/probe/CLI/test paths to use it. - Regenerates third-party notices (
cgmanifest.json,NOTICE.md) and adds a new migration design spec.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/shell/wt_channel/mod.rs | Adjusts public re-exports for wtcli channel helpers. |
| tools/wta/src/protocol/acp/soft_stop.rs | Updates stop-reason typing to acp::schema::v1 and aligns tests. |
| tools/wta/src/protocol/acp/probe.rs | Migrates probe-models ACP path to the 1.0 connection shim and v1 schema types. |
| tools/wta/src/protocol/acp/model_select.rs | Reworks model extraction/switching to schema v1 config-options + legacy set-model compatibility path. |
| tools/wta/src/protocol/acp/mod.rs | Exposes the new conn module in the ACP protocol tree. |
| tools/wta/src/protocol/acp/mock_agent_tests.rs | Updates in-process ACP mock harness to builder/dispatch model using the connection shim. |
| tools/wta/src/protocol/acp/conn.rs | Adds a 1.0 “compat shim” (ClientLink/AgentLink, spawn helpers, and legacy session/set_model request). |
| tools/wta/src/protocol/acp/client.rs | Migrates helper ACP client wiring and dispatcher to builder/dispatch and the connection shim. |
| tools/wta/src/main.rs | Updates CLI entrypoints (probe-models, sessions list, delegate registration) to use builder/dispatch + LocalSet + shim. |
| tools/wta/src/cli_tests.rs | Updates tests for schema v1 SessionId path changes. |
| tools/wta/src/app.rs | Updates app state/event types and ACP call sites to schema v1 ids and requests. |
| tools/wta/src/agent_hooks_installer.rs | Minor signature cleanup to silence unused parameter warnings. |
| tools/wta/src/agent_check.rs | Removes now-unused imports. |
| tools/wta/cgmanifest.json | Updates CG manifest entries for the new ACP crate versions (and new derive crate). |
| tools/wta/Cargo.toml | Bumps agent-client-protocol dependency to 1.0 and drops unstable feature flags. |
| tools/wta/Cargo.lock | Updates lockfile to ACP 1.0 dependency graph and new transitive crates. |
| NOTICE.md | Regenerates third-party attributions to match the updated Rust dependency graph. |
| doc/specs/acp-1.0-conductor-migration.md | Adds a detailed spec/plan for the broader conductor/proxy migration beyond Phase 0. |
Comments suppressed due to low confidence (1)
tools/wta/src/protocol/acp/model_select.rs:63
models_from_new_sessionno longer updatesMODEL_SWITCHwhen the agent does not advertise a model selector inconfig_options. BecauseMODEL_SWITCHis process-global, this can leave a staleConfig { config_id }from a previous session and later routeapply_session_modeltosession/set_config_optionwith the wrongconfig_id(or when the agent only supports legacy switching). The doc comment above the function also still claims we prefer the legacymodelsfield, but the schema 1.1 type no longer exposes it.
Reset the channel to Legacy when no config-option model selector is found, and update the doc comment to match the new behavior.
/// Extract the model list and current model id from a `new_session` response,
/// preferring the legacy `models` field and falling back to a `config_options`
/// `Select` with `category == Model`. Records the switch channel as a side
/// effect so [`apply_session_model`] later dispatches correctly.
pub(crate) fn models_from_new_session(
resp: &acp::schema::v1::NewSessionResponse,
) -> (Vec<AcpModelInfo>, Option<String>) {
if let Some(opts) = &resp.config_options {
if let Some((config_id, models, current)) = model_option_from_config(opts) {
record_channel_config(&config_id);
return (models, current);
}
}
(Vec::new(), None)
}
This comment has been minimized.
This comment has been minimized.
…drop dev username from spec cx() awaited a OnceLock via yield_now spin; if connect_with failed pre-ready it would spin forever. Replace with a Notify + failed flag and make cx() fallible so setup errors surface as acp::Error. Reword spec branch ref to dev/<alias>. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…AHP, sacp, reorderable, workstream, etc) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…/list + MCP-inject + session_list/probe-sessions to 1.0 main reworked session-title sourcing from on-disk to host ACP session/list (host_session_list_raw, try_refresh_title_via_acp, new session_list.rs/ session_history.rs, MCP server inject in session/new). Resolved all conflicts taking main's evolved code and porting it to the 1.0 builder/shim: ClientLink agent_conn, schema::v1 paths, StubClient/ProbeClient -> Client.builder(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 24 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
tools/wta/src/protocol/acp/model_select.rs:59
models_from_new_sessiononly records the switch channel when a modelconfig_optionsentry is found, which can leave a staleModelSwitchChannel::Configvalue across agent restarts or acrossnew_sessionresponses that don't carry the model selector. This contradicts the module comment that the channel is refreshed on every parse, and can causeapply_session_modelto sendsession/set_config_optionto an agent that only supports legacysession/set_model. Reset the channel toLegacyat the start of each parse, then override toConfigwhen a model selector is found.
if let Some(opts) = &resp.config_options {
if let Some((config_id, models, current)) = model_option_from_config(opts) {
record_channel_config(&config_id);
return (models, current);
}
…tale ACP 0.10/!Send note in session_list Addresses Copilot review on PR #367. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…re diagrams Phase 0/2 had detail sections; add matching Phase 1 (master->Conductor) and Phase 3 (WT control via MCP-over-ACP) details, plus an 'after this phase' mermaid diagram for each of Phase 0/1/2/3. Chinese spec updated in parallel (maintained outside the repo). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hase 2 tables - Enrich Today/Target diagrams: contrast today's app.rs monolith vs target's composable proxies; add the session-proxy family (list-union / enrichment / activity) plus a residual non-proxy core. - Correct topology and facts: N x agent-side + 1 x client-side connections, Class A/B session sources, helper-side alive-mirror, and session/list history sourcing. - Phase 1 diagram now uses dashed edges for what is new vs Phase 0 (mirrors the Phase 2 diagram). - Merge the four Phase 2 tables into two (app.rs responsibility + extraction; proxy criterion + viability); drop bold on session labels. - Retitle 'Phase 2 detail' to focus on proxy extraction rather than app.rs; update the anchor link. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update the stale ClientSideConnection / AgentSideConnection names in the module header to the conn.rs shim (ConnectionTo<Client> / ConnectionTo<Agent> via ClientLink / AgentLink / spawn_client / spawn_agent). Comment-only; no behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 24 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
tools/wta/src/protocol/acp/model_select.rs:59
models_from_new_sessiononly records the switch channel when a model config option is present. If it previously saw a config-option model selector, then later parses anew_sessionresponse with no model config option,MODEL_SWITCHremains stuck inConfig, andapply_session_modelwill incorrectly trysession/set_config_optionagainst legacy agents (Copilot/Gemini), causing model switching to fail. Reset the channel back toLegacywhen no model option is parsed so the channel is refreshed on every parse as the module docs describe.
if let Some(opts) = &resp.config_options {
if let Some((config_id, models, current)) = model_option_from_config(opts) {
record_channel_config(&config_id);
return (models, current);
}
This comment has been minimized.
This comment has been minimized.
…o the right - Target & Phase 1 diagrams: route the N helper transports into the N:1 bridge (helper -> BR) before it feeds the conductor, so the bridge is no longer drawn without an upstream; keep BR --- AC. In Phase 1 the new bridge->conductor link is dashed (new vs Phase 0) while the helper->bridge transport stays solid. - Legend now notes the N:1 bridge stays hand-rolled because the library's linear 1:1 chain cannot express N clients sharing one agent, pointing to The topology caveat. - Target diagram: declare the proxies subgraph before master so the composable proxies render to the right of the conductor spine (layout-only; verified by rendering to SVG). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Resolve the single content conflict in tools/wta/src/agent_check.rs: main's #370 (WTA login-flow cleanup) removed check_all_agents() and all its callers, so the KNOWN_AGENTS import is now unused — take main's 'use crate::agent_registry;'. All other WTA files auto-merged; wta crate builds clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…ane text The 'View switch preserves the draft input' case read AgentLabelText's UIA value (winapp get-value) to detect the chat/session view, but that value comes back empty here (the label surfaces text via Name, not a Value pattern, and the per-tab pre-warm can target another pane), so the setup hard-failed. It also clicked the chat toggle unconditionally, stashing an already-chat pane. Detect the view from the pane text via Get-SessionViewRenderRegex (the same locale-robust footer-hint signal Open-SessionList / Test-SessionListShown use) and only click a toggle when a switch is actually needed. Export Get-SessionViewRenderRegex for test use. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…race The "selection moves with arrow keys" test read the selected row once, immediately after sending Down. Under load the TUI re-render + capture-pane read can lag the keypress, so a single read occasionally caught the pre-move frame (flaky: 12/1/1 in a full-batch run vs 4/4 passing in isolation -- not an ACP 1.0 regression). Poll (re-pressing Down) until the selected row differs from the start, and skip cleanly on a single-row list. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
ACP 1.0's `connect_with(transport, main_fn)` surfaces peer death only by returning, and `run_until` returns early only when the internal background I/O *errors*. Killing wta-master produces a clean EOF (not an error), so with a `pending` main_fn `connect_with` hung forever, `handle_io` never resolved, and the helper never emitted `TransportLost` — the pane stayed `Connected` (AgentMasterDeath #329). ACP 0.10's `handle_io` was the direct I/O future and resolved on EOF, so this is a regression from the rewrite. Watch the incoming reader directly: `DeathWatchRead` trips a `TransportDeath` latch on EOF/read-error, and `main_fn` now awaits it (`death.wait()`) instead of `pending`, so `connect_with` returns and `handle_io` fires on peer death. Wrapping lives in `byte_streams`/`WatchedTransport`, so all call sites are unchanged. Adds regression tests for both client and agent sides. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
The two existing handle_io integration tests (#329) prove the end-to-end "peer death resolves handle_io" contract but exercise only the clean-EOF path. Add focused unit tests pinning the primitives the fix is built on so a future refactor of either can't silently reintroduce the hang (or tear down a live connection): - DeathWatchRead classification: real EOF (0-byte read into a non-empty buffer) and read errors signal death; a benign empty-buffer 0-byte read and a normal non-empty read do NOT (guards the !buf.is_empty() check). - TransportDeath latch: wait() returns when already signaled, wakes on a signal that races in after it starts waiting, and signal() is idempotent. Full suite: 996 passed / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
- conn.rs: spawn_client/spawn_agent now surface a cancelled `done_tx` (connection task dropped/panicked) as an `internal_error` instead of masking it as a clean `Ok(())` shutdown (Copilot review). - check-spelling: rename the `dwr` test variable to `reader` (unrecognized word) and replace `non-existent` with `nonexistent` in coordinator.rs / main.rs (forbidden-pattern rule). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…unimplemented Addresses a PR #367 review comment on the model-switch path. Since schema 1.1 removed NewSessionResponse.models, models_from_new_session records the Config channel whenever config_options carries a model selector, and apply_session_model then used session/set_config_option unconditionally. An agent that advertises a config-option model selector for discovery but only implements the legacy session/set_model would fail model switching with MethodNotFound (-32601). apply_session_model now falls back to session/set_model on a MethodNotFound from set_config_option, and records the Legacy channel so later switches skip the dead config channel. Other errors still propagate unchanged. Adds unit tests for both the fallback (config MethodNotFound -> set_model + channel flip) and the no-fallback path (a non-MethodNotFound error propagates), driven over real ACP via a typed session/set_model mock handler. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
check-spelling forbids the noun 'fallback' immediately before 'to'; the test assertion message must use the verb form 'fall back to'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve client.rs import conflict with #385 (Copilot auth flow): keep the ACP 1.0 `use super::conn;` shim import and add `HandshakeStage` to the `failure` import (both are used). app.rs / main.rs / Cargo.toml auto-merged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Integrates the WTA ACP 0.10->1.0 migration (#367) and other main changes. Conflict resolution: kept the workspace-command re-exports in wt_channel/mod.rs (annotated the now-orphaned spawn_wtcli_focus_pane with #[allow(dead_code)] to match its sibling, since main's dispatch_focus_pane moved to dispatch_session_focus_rpc); took main's Cargo.lock + NOTICE.md then regenerated NOTICE.md + cgmanifest.json for the merged graph (ACP 1.0 + chrono). Verified: wta cargo test 1016 passed/0 failed (all workspace feature tests intact), full C++ bcz no_clean 0 errors.
WTA ACP 0.10 → 1.0 migration (behavior-preserving)
Migrates the WTA helper/master ACP plane from
agent-client-protocol0.10 (the removedClientSideConnection/AgentSideConnectiontrait-impl model) onto 1.0's builder/dispatch model. The migration itself is behavior-preserving; this PR also bundles the fixes that surfaced while doing it.Migration
agent-client-protocol0.10 → 1.0; dropped the now-stabilizedunstable_session_list/unstable_session_modelfeatures. Regeneratedcgmanifest.json+NOTICE.md. (Noagent-client-protocol-tokio— it's stuck at 0.11.1 needing core ^0.11;Client/Agent/ByteStreamsship in core 1.0.)conn.rscompat shim: re-exposes the old "stash a connection, drive I/O on the side, call typed methods later" shape on top of the 1.0 builder model —ClientLink/AgentLink/spawn_client/spawn_agent/byte_streams, each returning ahandle_ioliveness future. Re-declares the droppedsession/set_modelrequest locally so Copilot/Gemini model switching keeps working (Claude switches via the still-standardsession/set_config_option).ClientSideConnection/AgentSideConnection→Client/Agent.builder().on_receive_*().connect_with(...);conn.method().await→cx.send_request().block_task().await; the 5impl acp::Client/Agentblocks → builder callbacks.acp::<T>→acp::schema::v1::<T>path moves.Fixes bundled in
wta-mastermust drop the pane out ofConnectedinto the/restart-only state (no split-brain). The 1.0 rewrite regressed this — a clean pipe EOF no longer resolvedhandle_io— so the helper now watches the transport reader directly (DeathWatchRead→TransportDeath) to restore it.MethodNotFoundfromsession/set_config_optionnow falls back to legacysession/set_model(and recordsLegacy), so an agent that advertises a config-option model selector but only implementsset_modelstill switches. Before schema 1.1 droppedNewSessionResponse.models, the legacy field took priority and this couldn't arise.request_permissionmid-prompt no longer wedges the master dispatch loop — 1.0 dispatches handlers serially inline, so the handler is now spawned instead of inline-awaited._intellterm.wta/*local methods are matched instead of falling through to the agent CLI.Tests
TransportDeath/DeathWatchReadand forhandle_ioresolving on peer death (both client and agent sides).MethodNotFoundfalls back toset_modeland flips the channel toLegacy; a non-MethodNotFounderror propagates without falling back.cargo buildclean.