Skip to content

Commit de2dba8

Browse files
vanzueCopilot
andauthored
Fix agent switch hanging on duplicate retirement events (#883)
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 e90ab68 commit de2dba8

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
@@ -3722,10 +3722,12 @@ impl App {
37223722
}
37233723

37243724
fn begin_pending_agent_reconnect_preflight(&mut self) -> Option<AgentReconnectRequest> {
3725-
let AgentReconnectState::Disconnecting(latest) =
3726-
std::mem::take(&mut self.agent_reconnect_state)
3727-
else {
3728-
return None;
3725+
let latest = match std::mem::take(&mut self.agent_reconnect_state) {
3726+
AgentReconnectState::Disconnecting(latest) => latest,
3727+
state => {
3728+
self.agent_reconnect_state = state;
3729+
return None;
3730+
}
37293731
};
37303732
self.pending_session_load = None;
37313733
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
@@ -4637,6 +4637,62 @@ fn settings_agent_rebind_targets_owner_and_resets_only_agent_state() {
46374637
));
46384638
}
46394639

4640+
#[test]
4641+
fn agent_rebind_duplicate_retirement_notification_preserves_target_preflight() {
4642+
let (mut app, mut restart_rx) = test_app_with_restart_rx();
4643+
app.owner_tab_id = Some("owner-tab".into());
4644+
app.window_id = Some("window-1".into());
4645+
app.tab_id = Some("owner-tab".into());
4646+
app.current_agent_id = "copilot".into();
4647+
app.tab_mut("owner-tab");
4648+
app.set_master_pipe_acp_params(
4649+
"master-pipe".into(),
4650+
"copilot --acp".into(),
4651+
Some("copilot".into()),
4652+
None,
4653+
None,
4654+
crate::agent_source::AgentSource::Host,
4655+
None,
4656+
Some("owner-tab".into()),
4657+
Arc::clone(&app.shell_mgr),
4658+
true,
4659+
);
4660+
4661+
app.handle_event(agent_rebind_event("owner-tab", 1, "opencode"));
4662+
let request = match restart_rx
4663+
.try_recv()
4664+
.expect("agent rebind should retire the current transport")
4665+
{
4666+
AgentLifecycleRequest::RebindAgent(request) => request,
4667+
other => panic!("expected RebindAgent, got {other:?}"),
4668+
};
4669+
4670+
app.handle_event(AppEvent::AgentTransportRetired);
4671+
assert!(matches!(
4672+
&app.agent_reconnect_state,
4673+
AgentReconnectState::Preflighting(pending)
4674+
if pending.agent_id == "opencode" && pending.generation == 1
4675+
));
4676+
4677+
app.handle_event(AppEvent::AgentReconnectReady(request));
4678+
assert!(matches!(
4679+
&app.agent_reconnect_state,
4680+
AgentReconnectState::Preflighting(pending)
4681+
if pending.agent_id == "opencode" && pending.generation == 1
4682+
));
4683+
4684+
app.handle_event(AppEvent::AgentReconnectPreflightComplete {
4685+
operation_id: "op-1".into(),
4686+
generation: 1,
4687+
result: passed_preflight("opencode", "OpenCode"),
4688+
});
4689+
assert!(app.pending_acp_start);
4690+
assert!(matches!(
4691+
&app.agent_reconnect_state,
4692+
AgentReconnectState::Idle
4693+
));
4694+
}
4695+
46404696
#[test]
46414697
fn agent_rebind_accepts_only_the_helpers_current_execution_source() {
46424698
let (mut app, mut restart_rx) = test_app_with_restart_rx();

0 commit comments

Comments
 (0)