diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 2617665bf6..f789c21474 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -4534,6 +4534,8 @@ impl ChatWidget { self.on_interrupted_turn(ev.reason); } TurnAbortReason::Replaced => { + self.pending_steers.clear(); + self.refresh_pending_input_preview(); self.on_error("Turn aborted: replaced by a new task".to_owned()) } TurnAbortReason::ReviewEnded => { diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 44656013b1..e4b97c52bb 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -4132,6 +4132,59 @@ queued draft" assert_no_submit_op(&mut op_rx); } +#[tokio::test] +async fn replaced_turn_clears_pending_steers_but_keeps_queued_drafts() { + let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await; + chat.thread_id = Some(ThreadId::new()); + chat.on_task_started(); + chat.on_agent_message_delta( + "Final answer line +" + .to_string(), + ); + + chat.bottom_pane + .set_composer_text("pending steer".to_string(), Vec::new(), Vec::new()); + chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + chat.queued_user_messages + .push_back(UserMessage::from("queued draft".to_string())); + chat.refresh_pending_input_preview(); + + match next_submit_op(&mut op_rx) { + Op::UserTurn { items, .. } => assert_eq!( + items, + vec![UserInput::Text { + text: "pending steer".to_string(), + text_elements: Vec::new(), + }] + ), + other => panic!("expected Op::UserTurn, got {other:?}"), + } + assert!(drain_insert_history(&mut rx).is_empty()); + + chat.handle_codex_event(Event { + id: "replaced".into(), + msg: EventMsg::TurnAborted(codex_protocol::protocol::TurnAbortedEvent { + turn_id: Some("turn-1".to_string()), + reason: TurnAbortReason::Replaced, + }), + }); + + assert!(chat.pending_steers.is_empty()); + assert!(chat.queued_user_messages.is_empty()); + assert_eq!(chat.bottom_pane.composer_text(), ""); + match next_submit_op(&mut op_rx) { + Op::UserTurn { items, .. } => assert_eq!( + items, + vec![UserInput::Text { + text: "queued draft".to_string(), + text_elements: Vec::new(), + }] + ), + other => panic!("expected queued draft Op::UserTurn, got {other:?}"), + } +} + #[tokio::test] async fn enter_submits_when_plan_stream_is_not_active() { let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await;