diff --git a/codex-rs/tui/src/app/backend_sessions.rs b/codex-rs/tui/src/app/backend_sessions.rs index 5112df4530..2795b4824e 100644 --- a/codex-rs/tui/src/app/backend_sessions.rs +++ b/codex-rs/tui/src/app/backend_sessions.rs @@ -118,6 +118,34 @@ impl BackendSessions { .collect() } + #[expect(dead_code, reason = "used by the next stack stage")] + pub(super) fn displayed_name(&self) -> Option { + let slot = self.displayed.0; + self.entries[slot].listening.then(|| match slot { + 0 => "Local".to_string(), + index => format!("Backend {}", index + 1), + }) + } + + #[expect(dead_code, reason = "used by the next stack stage")] + pub(super) fn first_listening_backend(&self) -> Option { + self.entries + .iter() + .find(|entry| entry.listening) + .map(|entry| entry.session.backend_id()) + } + + /// Cycle through listening connections in registration order, excluding the current one. + #[cfg_attr(not(test), expect(dead_code, reason = "used by the next stack stage"))] + pub(super) fn next_listening_backend(&self, current: BackendId) -> Option { + let index = self.slot(current)?.0; + self.entries[index + 1..] + .iter() + .chain(&self.entries[..index]) + .find(|entry| entry.listening) + .map(|entry| entry.session.backend_id()) + } + pub(super) fn session_at_mut(&mut self, slot: SessionSlot) -> &mut AppServerSession { &mut self.entries[slot.0].session } diff --git a/codex-rs/tui/src/app/tests/backend_sessions_tests.rs b/codex-rs/tui/src/app/tests/backend_sessions_tests.rs index 9dcf00d9a7..23a01e84ab 100644 --- a/codex-rs/tui/src/app/tests/backend_sessions_tests.rs +++ b/codex-rs/tui/src/app/tests/backend_sessions_tests.rs @@ -43,7 +43,10 @@ async fn backend_sessions_parked_request_does_not_hide_disconnect() -> Result<() ) }); let background_id = background.backend_id(); - let mut backends = BackendSessions::from_initial(displayed, vec![background]); + let displayed_id = displayed.backend_id(); + let mut backends = BackendSessions::new(displayed); + assert_eq!(backends.next_listening_backend(displayed_id), None); + backends.insert(background); let initial = backends.displayed; let thread_id = ThreadId::new(); @@ -185,6 +188,13 @@ async fn backend_sessions_poll_three_sources_and_isolate_closed_or_paused_stream } let mut sessions = sessions.into_iter(); let mut backends = BackendSessions::from_initial(sessions.next().unwrap(), sessions.collect()); + assert_eq!( + owners + .iter() + .map(|owner| backends.next_listening_backend(*owner)) + .collect::>(), + [Some(owners[1]), Some(owners[2]), Some(owners[0])] + ); let mut unpaused = HashSet::new(); for _ in 0..2 { let (owner, event) = @@ -221,6 +231,8 @@ async fn backend_sessions_poll_three_sources_and_isolate_closed_or_paused_stream } } assert!(!backends.is_listening(owners[0])); + assert_eq!(backends.next_listening_backend(owners[2]), Some(owners[1])); + assert_eq!(backends.next_listening_backend(owners[0]), Some(owners[1])); for index in 1..3 { assert!(backends.is_listening(owners[index])); let _: ThreadSettingsUpdateResponse = backends