Skip to content

Commit 6c0c9ed

Browse files
DDKingerCopilot
andcommitted
Keep chat available while hook status reconnects
The WT event listener is a session-status enhancement, not an availability gate for the agent stack. Do not make agent-pane chat, Autofix, history listing or resume wait for or fail with COM hook subscription readiness. Master now accepts helpers immediately and starts the listener readiness/retry loop in the background. A missing WT_COM_CLSID or a listener that remains unready is logged as "live session status may be stale" while the rest of the agent experience continues unchanged. Helpers likewise launch their listener without holding ACP startup behind the 15-second readiness window. Once the listener reconnects, direct master-owned hook routing resumes. No special unavailable state is introduced in /sessions: it continues to list and resume known history, with the same existing limitation that live status can be stale while hooks are unavailable. Validation: 1882 WTA tests passed, full solution built with 0 errors, the SessionHookRouting ItE2E suite passed 3/3 after deployment, and settings.json was unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c6ef3e46-71af-4e5a-82af-a172700ca3ce
1 parent 80c81b3 commit 6c0c9ed

2 files changed

Lines changed: 49 additions & 24 deletions

File tree

tools/wta/src/helper/runtime.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,23 @@ async fn run_acp_app(
488488
}
489489
});
490490

491-
// Start the background protocol reader and trigger lazy event registration.
492-
// start_reader() claims stdout/stderr streams and must complete before any requests.
491+
// Start the protocol listener without gating helper/ACP startup on
492+
// its readiness. If COM is temporarily unavailable, chat and
493+
// Autofix still work; the reader retries in the background and
494+
// only helper-local pane/session status may lag.
493495
// get_capabilities triggers _ensurePageEventsRegistered() on the WT server.
494496
if let Some(ref protocol_ch) = wt_protocol_channel {
495497
tracing::info!("start_reader: starting...");
496-
protocol_ch.start_reader().await;
497-
tracing::info!("start_reader: done, sending get_capabilities...");
498+
let reader = Arc::clone(protocol_ch);
499+
tokio::spawn(async move {
500+
if !reader.start_reader().await {
501+
tracing::warn!(
502+
target: "wtcli",
503+
"helper WT event listener is still reconnecting; local session status may be stale"
504+
);
505+
}
506+
});
507+
tracing::info!("start_reader: launched, sending get_capabilities...");
498508
match protocol_ch
499509
.request("get_capabilities", serde_json::json!({}))
500510
.await

tools/wta/src/master/mod.rs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4185,27 +4185,42 @@ async fn run_master_loop(config: MasterConfig, pipe_name: String) -> Result<()>
41854185
// `WtChannel` trait surface doesn't expose event subscription, so bind the
41864186
// concrete channel first, subscribe, then wrap as `dyn WtChannel`.
41874187
//
4188-
// This is now load-bearing: master is the sole authoritative consumer of
4189-
// agent hook broadcasts. Starting without WT_COM_CLSID / a COM channel
4190-
// would look healthy while silently losing every lifecycle event, so fail
4191-
// startup instead of degrading to a hook-blind master.
4192-
let wt_cli = Arc::new(
4193-
crate::shell::wt_channel::CliChannel::connect()
4194-
.await
4195-
.context("connect master WT event channel")?,
4196-
);
4197-
// Subscribe to WT events + start the reader BEFORE wrapping as
4198-
// `dyn WtChannel` (the trait surface doesn't expose subscription).
4199-
// Single-consumer: focus_session uses the same channel via request/
4200-
// response, which doesn't touch the event sender.
4201-
let wt_event_rx = Some(wt_cli.subscribe_events());
4202-
if !wt_cli.start_reader().await {
4203-
anyhow::bail!(
4204-
"master WT event listener did not subscribe; refusing to start without the authoritative hook path"
4205-
);
4188+
// Hook delivery is deliberately NOT a master-startup gate. If COM is
4189+
// temporarily unavailable, agent-pane chat, Autofix, history listing and
4190+
// resume still work; only real-time shell-session status can lag until the
4191+
// listener reconnects. This matches the preexisting product degradation
4192+
// mode instead of turning a session-management enhancement into a global
4193+
// AI-feature outage.
4194+
let wt_cli: Option<Arc<crate::shell::wt_channel::CliChannel>> =
4195+
match crate::shell::wt_channel::CliChannel::connect().await {
4196+
Ok(channel) => Some(Arc::new(channel)),
4197+
Err(error) => {
4198+
tracing::warn!(
4199+
target: "master",
4200+
%error,
4201+
"master WT event channel unavailable; chat remains available but live session status may be stale"
4202+
);
4203+
None
4204+
}
4205+
};
4206+
// Start subscription readiness in the background. `start_reader` keeps
4207+
// retrying after its initial readiness timeout, so master must retain the
4208+
// channel but must not wait up to 15 seconds before accepting helpers.
4209+
let wt_event_rx = wt_cli.as_ref().map(|channel| channel.subscribe_events());
4210+
if let Some(channel) = wt_cli.as_ref() {
4211+
let channel = Arc::clone(channel);
4212+
tokio::task::spawn_local(async move {
4213+
if !channel.start_reader().await {
4214+
tracing::warn!(
4215+
target: "master",
4216+
"master WT event listener is still reconnecting; live session status may be stale"
4217+
);
4218+
}
4219+
});
42064220
}
4207-
let wt: Option<Arc<dyn crate::shell::wt_channel::WtChannel>> =
4208-
Some(wt_cli as Arc<dyn crate::shell::wt_channel::WtChannel>);
4221+
let wt: Option<Arc<dyn crate::shell::wt_channel::WtChannel>> = wt_cli
4222+
.clone()
4223+
.map(|channel| channel as Arc<dyn crate::shell::wt_channel::WtChannel>);
42094224

42104225
// Agent CLIs are spawned LAZILY by `get_or_spawn_agent` the first time
42114226
// a helper declares an agent in its `initialize` handshake — the master

0 commit comments

Comments
 (0)