diff --git a/codex-rs/core/src/mcp/mod.rs b/codex-rs/core/src/mcp/mod.rs index c3513bb061..1bb08a75be 100644 --- a/codex-rs/core/src/mcp/mod.rs +++ b/codex-rs/core/src/mcp/mod.rs @@ -97,6 +97,7 @@ fn codex_apps_mcp_server_config(config: &Config, auth: Option<&CodexAuth>) -> Mc tool_timeout_sec: None, enabled_tools: None, disabled_tools: None, + scopes: None, } } diff --git a/codex-rs/tui/src/tui/event_stream.rs b/codex-rs/tui/src/tui/event_stream.rs index 2ce0aa7d2c..1e9f845181 100644 --- a/codex-rs/tui/src/tui/event_stream.rs +++ b/codex-rs/tui/src/tui/event_stream.rs @@ -248,7 +248,12 @@ impl TuiEventStream { Event::Paste(pasted) => Some(TuiEvent::Paste(pasted)), Event::FocusGained => { self.terminal_focused.store(true, Ordering::Relaxed); + // crossterm queries use the same global stdin reader that EventStream relies on. + // Drop/recreate the EventStream around any such query to avoid contention that can + // stall the UI on focus changes (e.g. after alt-tabbing back to the terminal). + self.broker.pause_events(); crate::terminal_palette::requery_default_colors(); + self.broker.resume_events(); Some(TuiEvent::Draw) } Event::FocusLost => { @@ -508,4 +513,35 @@ mod tests { other => panic!("expected key event, got {other:?}"), } } + + #[tokio::test(flavor = "current_thread")] + async fn focus_gained_recreates_event_source() { + let (broker, handle, _draw_tx, draw_rx, terminal_focused) = setup(); + terminal_focused.store(false, Ordering::Relaxed); + let mut stream = make_stream(broker.clone(), draw_rx, terminal_focused.clone()); + + handle.send(Ok(Event::FocusGained)); + + let first = stream.next().await; + assert!(matches!(first, Some(TuiEvent::Draw))); + assert!(terminal_focused.load(Ordering::Relaxed)); + + { + let state = broker + .state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + assert!(matches!(&*state, EventBrokerState::Start)); + } + + // Ensure the stream is still usable after the event source is recreated. + let expected_key = KeyEvent::new(KeyCode::Char('n'), KeyModifiers::NONE); + handle.send(Ok(Event::Key(expected_key))); + + let second = stream.next().await; + match second { + Some(TuiEvent::Key(key)) => assert_eq!(key, expected_key), + other => panic!("expected key event, got {other:?}"), + } + } }