diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 2c6a54f287..9e6306238e 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -6489,6 +6489,9 @@ impl ChatWidget { if feature == Feature::RealtimeConversation { self.sync_realtime_command_enabled(); if !enabled { + if self.realtime_conversation_state != RealtimeConversationUiState::Stopped { + self.submit_op(Op::RealtimeConversationClose); + } if let Some(a) = self.realtime_audio_controller.take() { RealtimeAudioController::shutdown(a) } diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 8b4c1631a1..614aa8f8b2 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -4507,6 +4507,32 @@ async fn slash_realtime_toggle_sends_start_then_close_ops() { assert_matches!(op_rx.try_recv(), Ok(Op::RealtimeConversationClose)); } +#[tokio::test] +async fn disabling_realtime_feature_closes_active_session() { + let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await; + chat.set_feature_enabled(Feature::RealtimeConversation, true); + + chat.dispatch_command(SlashCommand::Realtime); + assert_matches!( + op_rx.try_recv(), + Ok(Op::RealtimeConversationStart( + ConversationStartParams { .. } + )) + ); + assert_eq!( + chat.realtime_conversation_state, + RealtimeConversationUiState::Working + ); + + chat.set_feature_enabled(Feature::RealtimeConversation, false); + + assert_eq!( + chat.realtime_conversation_state, + RealtimeConversationUiState::Stopped + ); + assert_matches!(op_rx.try_recv(), Ok(Op::RealtimeConversationClose)); +} + #[tokio::test] async fn realtime_events_update_lifecycle_state() { let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await;