Files
codex/codex-rs/tui/src/thread_transcript/activity_pages.rs
T
Eric Traut f43faf132d Align persisted TUI activity groups and reasoning with live output (#46711)
## Why

Persisted transcripts rendered tool calls individually and displayed reasoning differently from live replay. Enabling raw reasoning also replaced the summary instead of retaining it.

## What changed

- Group computer activity and compatible exploration commands across intervening reasoning, retaining details for the expanded transcript. Respect visible content, command failure boundaries, and turn boundaries.
- Separate paginated history at every turn boundary before filtering hidden items, while adding completion metadata only for successful turns.
- Keep persisted reasoning out of the compact display, preserve hyperlinks in detailed rendering, and append enabled raw reasoning to the summary. Preserve standalone reasoning Markdown in transcript exports.
- Add helpers to rejoin activity groups and attach reasoning across page boundaries using persisted item identities and turn adjacency.

## Testing

Add regression coverage for reasoning replay parity, computer groups split across history pages, turn boundaries, and grouped terminal input. Update export and raw-reasoning tests to verify retained summaries and Markdown.

GitOrigin-RevId: a716db571440f891cc2c6894ae93fbe0e149aed4
2026-09-19 20:27:28 +00:00

66 lines
2.6 KiB
Rust

//! Attach transparent page-boundary details to a freshly rebuilt activity group.
use crate::exec_cell::ExecCell;
use crate::history_cell::ComputerActivityCell;
use crate::history_cell::HistoryCell;
use crate::history_cell::ReasoningSummaryCell;
use std::sync::Arc;
#[allow(dead_code, reason = "Used by later layers of the TUI refresh stack.")]
pub(crate) fn is_hidden_activity_detail(cell: &Arc<dyn HistoryCell>) -> bool {
cell.as_any()
.downcast_ref::<ReasoningSummaryCell>()
.is_some_and(ReasoningSummaryCell::is_transcript_only)
}
/// Fold standalone reasoning using exact persisted identities from one turn.
///
/// An older page can arrive after the final reasoning page even with no newer group or live tail.
/// Rebuild only known completed calls and share their immutable details; never infer adjacency
/// from identical reasoning text or attach a detail from the next turn.
#[allow(dead_code, reason = "Used by later layers of the TUI refresh stack.")]
pub(crate) fn fold_trailing_activity_details(
older: &Arc<dyn HistoryCell>,
details: &[Arc<dyn HistoryCell>],
turns: &[codex_app_server_protocol::Turn],
) -> Option<Arc<dyn HistoryCell>> {
let (mut ids, retained) =
if let Some(group) = older.as_any().downcast_ref::<ComputerActivityCell>() {
(group.call_ids().collect::<Vec<_>>(), &group.group.details)
} else {
let group = older.as_any().downcast_ref::<ExecCell>()?;
if !group.is_exploring_cell() || group.should_flush() {
return None;
}
(
group
.iter_calls()
.map(|call| call.call_id.as_str())
.collect::<Vec<_>>(),
&group.group.details,
)
};
let count = ids.len();
for detail in details {
let detail = detail.as_any().downcast_ref::<ReasoningSummaryCell>()?;
if !detail.is_transcript_only() {
return None;
}
ids.push(detail.source_item_id()?);
}
let items = super::computer_groups::adjacent_activity_items(&ids, turns)?;
let mut retained = retained.clone();
for detail in details {
retained.push(count, Arc::clone(detail));
}
if older.as_any().is::<ComputerActivityCell>() {
let mut group = super::computer_groups::completed_group(items)?;
group.group.details = retained;
Some(Arc::new(group))
} else {
let mut group = super::exploration_groups::completed_group(items)?;
group.group.details = retained;
Some(Arc::new(group))
}
}