mirror of
https://github.com/openai/codex.git
synced 2026-05-02 18:37:01 +00:00
Preserve background terminals on interrupt and rename cleanup command to /stop (#14602)
### Motivation - Interrupting a running turn (Ctrl+C / Esc) currently also terminates long‑running background shells, which is surprising for workflows like local dev servers or file watchers. - The existing cleanup command name was confusing; callers expect an explicit command to stop background terminals rather than a UI clear action. - Make background‑shell termination explicit and surface a clearer command name while preserving backward compatibility. ### Description - Renamed the background‑terminal cleanup slash command from `Clean` (`/clean`) to `Stop` (`/stop`) and kept `clean` as an alias in the command parsing/visibility layer, updated the user descriptions and command popup wiring accordingly. - Updated the unified‑exec footer text and snapshots to point to `/stop` (and trimmed corresponding snapshot output to match the new label). - Changed interrupt behavior so `Op::Interrupt` (Ctrl+C / Esc interrupt) no longer closes or clears tracked unified exec / background terminal processes in the TUI or core cleanup path; background shells are now preserved after an interrupt. - Updated protocol/docs to clarify that `turn/interrupt` (or `Op::Interrupt`) interrupts the active turn but does not terminate background terminals, and that `thread/backgroundTerminals/clean` is the explicit API to stop those shells. - Updated unit/integration tests and insta snapshots in the TUI and core unified‑exec suites to reflect the new semantics and command name. ### Testing - Ran formatting with `just fmt` in `codex-rs` (succeeded). - Ran `cargo test -p codex-protocol` (succeeded). - Attempted `cargo test -p codex-tui` but the build could not complete in this environment due to a native build dependency that requires `libcap` development headers (the `codex-linux-sandbox` vendored build step); install `libcap-dev` / make `libcap.pc` available in `PKG_CONFIG_PATH` to run the TUI test suite locally. - Updated and accepted the affected `insta` snapshots for the TUI changes so visual diffs reflect the new `/stop` wording and preserved interrupt behavior. ------ [Codex Task](https://chatgpt.com/codex/tasks/task_i_69b39c44b6dc8323bd133ae206310fae)
This commit is contained in:
@@ -3820,7 +3820,7 @@ async fn enqueueing_history_prompt_multiple_times_is_stable() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn streaming_final_answer_keeps_task_running_state() {
|
||||
async fn streaming_final_answer_ctrl_c_interrupt_preserves_background_shells() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
|
||||
@@ -3843,12 +3843,16 @@ async fn streaming_final_answer_keeps_task_running_state() {
|
||||
);
|
||||
assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty));
|
||||
|
||||
begin_unified_exec_startup(&mut chat, "call-1", "process-1", "npm run dev");
|
||||
assert_eq!(chat.unified_exec_processes.len(), 1);
|
||||
|
||||
chat.handle_key_event(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL));
|
||||
match op_rx.try_recv() {
|
||||
Ok(Op::Interrupt) => {}
|
||||
other => panic!("expected Op::Interrupt, got {other:?}"),
|
||||
}
|
||||
assert!(!chat.bottom_pane.quit_shortcut_hint_visible());
|
||||
assert_eq!(chat.unified_exec_processes.len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -6025,10 +6029,10 @@ async fn slash_exit_requests_exit() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_clean_submits_background_terminal_cleanup() {
|
||||
async fn slash_stop_submits_background_terminal_cleanup() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.dispatch_command(SlashCommand::Clean);
|
||||
chat.dispatch_command(SlashCommand::Stop);
|
||||
|
||||
assert_matches!(op_rx.try_recv(), Ok(Op::CleanBackgroundTerminals));
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
@@ -9061,7 +9065,7 @@ async fn interrupt_prepends_queued_messages_before_existing_composer_text() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interrupt_clears_unified_exec_processes() {
|
||||
async fn interrupt_keeps_unified_exec_processes() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
begin_unified_exec_startup(&mut chat, "call-1", "process-1", "sleep 5");
|
||||
@@ -9076,7 +9080,7 @@ async fn interrupt_clears_unified_exec_processes() {
|
||||
}),
|
||||
});
|
||||
|
||||
assert!(chat.unified_exec_processes.is_empty());
|
||||
assert_eq!(chat.unified_exec_processes.len(), 2);
|
||||
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
}
|
||||
@@ -9119,7 +9123,7 @@ async fn review_ended_keeps_unified_exec_processes() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interrupt_clears_unified_exec_wait_streak_snapshot() {
|
||||
async fn interrupt_preserves_unified_exec_wait_streak_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -9150,7 +9154,7 @@ async fn interrupt_clears_unified_exec_wait_streak_snapshot() {
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let snapshot = format!("cells={}\n{combined}", cells.len());
|
||||
assert_snapshot!("interrupt_clears_unified_exec_wait_streak", snapshot);
|
||||
assert_snapshot!("interrupt_preserves_unified_exec_wait_streak", snapshot);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user