Skip to content

Commit efc56bb

Browse files
vanzueCopilot
andcommitted
Fix agent switch hanging on duplicate retirement events
Preserve the in-flight preflight state when transport retirement and reconnect-ready notifications arrive for the same agent rebind. Add regression coverage for Copilot to OpenCode switching without changing cancellation retirement barriers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: df937457-6f9d-4a02-a8bf-fca6697e63a5
1 parent f67c04e commit efc56bb

2 files changed

Lines changed: 62 additions & 4 deletions

File tree

tools/wta/src/app.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3674,10 +3674,12 @@ impl App {
36743674
}
36753675

36763676
fn begin_pending_agent_reconnect_preflight(&mut self) -> Option<AgentReconnectRequest> {
3677-
let AgentReconnectState::Disconnecting(latest) =
3678-
std::mem::take(&mut self.agent_reconnect_state)
3679-
else {
3680-
return None;
3677+
let latest = match std::mem::take(&mut self.agent_reconnect_state) {
3678+
AgentReconnectState::Disconnecting(latest) => latest,
3679+
state => {
3680+
self.agent_reconnect_state = state;
3681+
return None;
3682+
}
36813683
};
36823684
self.pending_session_load = None;
36833685
self.reset_agent_scoped_state();

tools/wta/src/app_tests.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4524,6 +4524,62 @@ fn settings_agent_rebind_targets_owner_and_resets_only_agent_state() {
45244524
));
45254525
}
45264526

4527+
#[test]
4528+
fn agent_rebind_duplicate_retirement_notification_preserves_target_preflight() {
4529+
let (mut app, mut restart_rx) = test_app_with_restart_rx();
4530+
app.owner_tab_id = Some("owner-tab".into());
4531+
app.window_id = Some("window-1".into());
4532+
app.tab_id = Some("owner-tab".into());
4533+
app.current_agent_id = "copilot".into();
4534+
app.tab_mut("owner-tab");
4535+
app.set_master_pipe_acp_params(
4536+
"master-pipe".into(),
4537+
"copilot --acp".into(),
4538+
Some("copilot".into()),
4539+
None,
4540+
None,
4541+
crate::agent_source::AgentSource::Host,
4542+
None,
4543+
Some("owner-tab".into()),
4544+
Arc::clone(&app.shell_mgr),
4545+
true,
4546+
);
4547+
4548+
app.handle_event(agent_rebind_event("owner-tab", 1, "opencode"));
4549+
let request = match restart_rx
4550+
.try_recv()
4551+
.expect("agent rebind should retire the current transport")
4552+
{
4553+
AgentLifecycleRequest::RebindAgent(request) => request,
4554+
other => panic!("expected RebindAgent, got {other:?}"),
4555+
};
4556+
4557+
app.handle_event(AppEvent::AgentTransportRetired);
4558+
assert!(matches!(
4559+
&app.agent_reconnect_state,
4560+
AgentReconnectState::Preflighting(pending)
4561+
if pending.agent_id == "opencode" && pending.generation == 1
4562+
));
4563+
4564+
app.handle_event(AppEvent::AgentReconnectReady(request));
4565+
assert!(matches!(
4566+
&app.agent_reconnect_state,
4567+
AgentReconnectState::Preflighting(pending)
4568+
if pending.agent_id == "opencode" && pending.generation == 1
4569+
));
4570+
4571+
app.handle_event(AppEvent::AgentReconnectPreflightComplete {
4572+
operation_id: "op-1".into(),
4573+
generation: 1,
4574+
result: passed_preflight("opencode", "OpenCode"),
4575+
});
4576+
assert!(app.pending_acp_start);
4577+
assert!(matches!(
4578+
&app.agent_reconnect_state,
4579+
AgentReconnectState::Idle
4580+
));
4581+
}
4582+
45274583
#[test]
45284584
fn agent_rebind_accepts_only_the_helpers_current_execution_source() {
45294585
let (mut app, mut restart_rx) = test_app_with_restart_rx();

0 commit comments

Comments
 (0)