From a9f566af7bfb43c126dd6930b5aa8267b19f439c Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Mon, 8 Dec 2025 14:33:00 -0800 Subject: [PATCH] Restore status header after stream recovery (#7660) ## Summary - restore the previous status header when a non-error event arrives after a stream retry - add a regression test to ensure the reconnect banner clears once streaming resumes ## Testing - cargo fmt -- --config imports_granularity=Item - cargo clippy --fix --all-features --tests --allow-dirty -p codex-tui - NO_COLOR=0 cargo test -p codex-tui *(fails: vt100 color assertion tests expect colored cells but the environment returns Default colors even with NO_COLOR cleared and TERM/COLORTERM set)* ------ [Codex Task](https://chatgpt.com/codex/tasks/task_i_69337f8c77508329b3ea85134d4a7ac7) --- codex-rs/tui/src/chatwidget.rs | 13 +++++++++++ codex-rs/tui/src/chatwidget/tests.rs | 33 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index c8f221de6..1302b2343 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -377,6 +377,14 @@ impl ChatWidget { self.bottom_pane.update_status_header(header); } + fn restore_retry_status_header_if_present(&mut self) { + if let Some(header) = self.retry_status_header.take() + && self.current_status_header != header + { + self.set_status_header(header); + } + } + // --- Small event handlers --- fn on_session_configured(&mut self, event: codex_core::protocol::SessionConfiguredEvent) { self.bottom_pane @@ -1771,6 +1779,11 @@ impl ChatWidget { /// `replay_initial_messages()`. Callers should treat `None` as a "fake" id /// that must not be used to correlate follow-up actions. fn dispatch_event_msg(&mut self, id: Option, msg: EventMsg, from_replay: bool) { + let is_stream_error = matches!(&msg, EventMsg::StreamError(_)); + if !is_stream_error { + self.restore_retry_status_header_if_present(); + } + match msg { EventMsg::AgentMessageDelta(_) | EventMsg::AgentReasoningDelta(_) diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 5159d12ce..126c91f9d 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -2905,6 +2905,39 @@ fn warning_event_adds_warning_history_cell() { ); } +#[test] +fn stream_recovery_restores_previous_status_header() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(); + chat.handle_codex_event(Event { + id: "task".into(), + msg: EventMsg::TaskStarted(TaskStartedEvent { + model_context_window: None, + }), + }); + drain_insert_history(&mut rx); + chat.handle_codex_event(Event { + id: "retry".into(), + msg: EventMsg::StreamError(StreamErrorEvent { + message: "Reconnecting... 1/5".to_string(), + codex_error_info: Some(CodexErrorInfo::Other), + }), + }); + drain_insert_history(&mut rx); + chat.handle_codex_event(Event { + id: "delta".into(), + msg: EventMsg::AgentMessageDelta(AgentMessageDeltaEvent { + delta: "hello".to_string(), + }), + }); + + let status = chat + .bottom_pane + .status_widget() + .expect("status indicator should be visible"); + assert_eq!(status.header(), "Working"); + assert!(chat.retry_status_header.is_none()); +} + #[test] fn multiple_agent_messages_in_single_turn_emit_multiple_headers() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();