diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 4866d7bad37..cb27e0a7a89 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -745,6 +745,7 @@ impl ChatWidget { self.suppressed_exec_calls.clear(); self.last_unified_wait = None; self.unified_exec_wait_streak = None; + self.clear_unified_exec_processes(); self.request_redraw(); // If there is a queued user message, send exactly one now to begin the next turn. @@ -881,6 +882,7 @@ impl ChatWidget { self.running_commands.clear(); self.suppressed_exec_calls.clear(); self.last_unified_wait = None; + self.clear_unified_exec_processes(); self.stream_controller = None; self.maybe_show_pending_rate_limit_prompt(); } @@ -973,8 +975,6 @@ impl ChatWidget { fn on_interrupted_turn(&mut self, reason: TurnAbortReason) { // Finalize, log a gentle prompt, and clear running state. self.finalize_turn(); - self.unified_exec_processes.clear(); - self.sync_unified_exec_footer(); if reason != TurnAbortReason::ReviewEnded { self.add_to_history(history_cell::new_error_event( @@ -1195,6 +1195,14 @@ impl ChatWidget { self.bottom_pane.set_unified_exec_processes(processes); } + fn clear_unified_exec_processes(&mut self) { + if self.unified_exec_processes.is_empty() { + return; + } + self.unified_exec_processes.clear(); + self.sync_unified_exec_footer(); + } + fn on_mcp_tool_call_begin(&mut self, ev: McpToolCallBeginEvent) { let ev2 = ev.clone(); self.defer_or_handle(|q| q.push_mcp_begin(ev), |s| s.handle_mcp_begin_now(ev2)); diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 2d9d808ed1d..e0490e24650 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -2795,6 +2795,26 @@ async fn interrupt_clears_unified_exec_processes() { let _ = drain_insert_history(&mut rx); } +#[tokio::test] +async fn turn_complete_clears_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"); + begin_unified_exec_startup(&mut chat, "call-2", "process-2", "sleep 6"); + assert_eq!(chat.unified_exec_processes.len(), 2); + + chat.handle_codex_event(Event { + id: "turn-1".into(), + msg: EventMsg::TurnComplete(TurnCompleteEvent { + last_agent_message: None, + }), + }); + + assert!(chat.unified_exec_processes.is_empty()); + + let _ = drain_insert_history(&mut rx); +} + // Snapshot test: ChatWidget at very small heights (idle) // Ensures overall layout behaves when terminal height is extremely constrained. #[tokio::test]