Skip to content

Commit f0a85de

Browse files
authored
fix: ctrl c sub agent (openai#12911)
1 parent 739d4b5 commit f0a85de

1 file changed

Lines changed: 52 additions & 13 deletions

File tree

codex-rs/tui/src/app.rs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,19 +1925,9 @@ impl App {
19251925
AppEvent::CodexEvent(event) => {
19261926
self.enqueue_primary_event(event).await?;
19271927
}
1928-
AppEvent::Exit(mode) => match mode {
1929-
ExitMode::ShutdownFirst => {
1930-
// Mark the thread we are explicitly shutting down for exit so
1931-
// its shutdown completion does not trigger agent failover.
1932-
self.pending_shutdown_exit_thread_id =
1933-
self.active_thread_id.or(self.chat_widget.thread_id());
1934-
self.chat_widget.submit_op(Op::Shutdown);
1935-
}
1936-
ExitMode::Immediate => {
1937-
self.pending_shutdown_exit_thread_id = None;
1938-
return Ok(AppRunControl::Exit(ExitReason::UserRequested));
1939-
}
1940-
},
1928+
AppEvent::Exit(mode) => {
1929+
return Ok(self.handle_exit_mode(mode));
1930+
}
19411931
AppEvent::FatalExitRequest(message) => {
19421932
return Ok(AppRunControl::Exit(ExitReason::Fatal(message)));
19431933
}
@@ -2914,6 +2904,27 @@ impl App {
29142904
Ok(AppRunControl::Continue)
29152905
}
29162906

2907+
fn handle_exit_mode(&mut self, mode: ExitMode) -> AppRunControl {
2908+
match mode {
2909+
ExitMode::ShutdownFirst => {
2910+
// Mark the thread we are explicitly shutting down for exit so
2911+
// its shutdown completion does not trigger agent failover.
2912+
self.pending_shutdown_exit_thread_id =
2913+
self.active_thread_id.or(self.chat_widget.thread_id());
2914+
if self.chat_widget.submit_op(Op::Shutdown) {
2915+
AppRunControl::Continue
2916+
} else {
2917+
self.pending_shutdown_exit_thread_id = None;
2918+
AppRunControl::Exit(ExitReason::UserRequested)
2919+
}
2920+
}
2921+
ExitMode::Immediate => {
2922+
self.pending_shutdown_exit_thread_id = None;
2923+
AppRunControl::Exit(ExitReason::UserRequested)
2924+
}
2925+
}
2926+
}
2927+
29172928
fn handle_codex_event_now(&mut self, event: Event) {
29182929
let needs_refresh = matches!(
29192930
event.msg,
@@ -4703,6 +4714,34 @@ mod tests {
47034714
}
47044715
}
47054716

4717+
#[tokio::test]
4718+
async fn shutdown_first_exit_returns_immediate_exit_when_shutdown_submit_fails() {
4719+
let mut app = make_test_app().await;
4720+
let thread_id = ThreadId::new();
4721+
app.active_thread_id = Some(thread_id);
4722+
4723+
let control = app.handle_exit_mode(ExitMode::ShutdownFirst);
4724+
4725+
assert_eq!(app.pending_shutdown_exit_thread_id, None);
4726+
assert!(matches!(
4727+
control,
4728+
AppRunControl::Exit(ExitReason::UserRequested)
4729+
));
4730+
}
4731+
4732+
#[tokio::test]
4733+
async fn shutdown_first_exit_waits_for_shutdown_when_submit_succeeds() {
4734+
let (mut app, _app_event_rx, mut op_rx) = make_test_app_with_channels().await;
4735+
let thread_id = ThreadId::new();
4736+
app.active_thread_id = Some(thread_id);
4737+
4738+
let control = app.handle_exit_mode(ExitMode::ShutdownFirst);
4739+
4740+
assert_eq!(app.pending_shutdown_exit_thread_id, Some(thread_id));
4741+
assert!(matches!(control, AppRunControl::Continue));
4742+
assert_eq!(op_rx.try_recv(), Ok(Op::Shutdown));
4743+
}
4744+
47064745
#[tokio::test]
47074746
async fn clear_only_ui_reset_preserves_chat_session_state() {
47084747
let mut app = make_test_app().await;

0 commit comments

Comments
 (0)