From 1bd2d7a659d1e1a4bb987d6d61c2cc1ebf752a40 Mon Sep 17 00:00:00 2001 From: Jeremy Rose <172423086+nornagon-openai@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:50:07 -0800 Subject: [PATCH] tui: fix backtracking past /status (#6335) Fixes https://github.com/openai/codex/issues/4722 Supersedes https://github.com/openai/codex/pull/5058 Ideally we'd have a clearer way of separating history per-session than by detecting a specific history cell type, but this is a fairly minimal fix for now. --- codex-rs/tui/src/app_backtrack.rs | 6 +++--- codex-rs/tui/src/history_cell.rs | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/codex-rs/tui/src/app_backtrack.rs b/codex-rs/tui/src/app_backtrack.rs index e2ded3d3..b161867e 100644 --- a/codex-rs/tui/src/app_backtrack.rs +++ b/codex-rs/tui/src/app_backtrack.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::sync::Arc; use crate::app::App; -use crate::history_cell::CompositeHistoryCell; +use crate::history_cell::SessionInfoCell; use crate::history_cell::UserHistoryCell; use crate::pager_overlay::Overlay; use crate::tui; @@ -394,13 +394,13 @@ fn nth_user_position( fn user_positions_iter( cells: &[Arc], ) -> impl Iterator + '_ { - let header_type = TypeId::of::(); + let session_start_type = TypeId::of::(); let user_type = TypeId::of::(); let type_of = |cell: &Arc| cell.as_any().type_id(); let start = cells .iter() - .rposition(|cell| type_of(cell) == header_type) + .rposition(|cell| type_of(cell) == session_start_type) .map_or(0, |idx| idx + 1); cells diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 27f8220a..875bb9e2 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -559,11 +559,28 @@ pub(crate) fn padded_emoji(emoji: &str) -> String { format!("{emoji}\u{200A}") } +#[derive(Debug)] +pub struct SessionInfoCell(CompositeHistoryCell); + +impl HistoryCell for SessionInfoCell { + fn display_lines(&self, width: u16) -> Vec> { + self.0.display_lines(width) + } + + fn desired_height(&self, width: u16) -> u16 { + self.0.desired_height(width) + } + + fn transcript_lines(&self, width: u16) -> Vec> { + self.0.transcript_lines(width) + } +} + pub(crate) fn new_session_info( config: &Config, event: SessionConfiguredEvent, is_first_event: bool, -) -> CompositeHistoryCell { +) -> SessionInfoCell { let SessionConfiguredEvent { model, reasoning_effort, @@ -573,7 +590,7 @@ pub(crate) fn new_session_info( initial_messages: _, rollout_path: _, } = event; - if is_first_event { + SessionInfoCell(if is_first_event { // Header box rendered as history (so it appears at the very top) let header = SessionHeaderHistoryCell::new( model, @@ -632,7 +649,7 @@ pub(crate) fn new_session_info( CompositeHistoryCell { parts: vec![Box::new(PlainHistoryCell { lines })], } - } + }) } pub(crate) fn new_user_prompt(message: String) -> UserHistoryCell {