Release superseded assistant text from live paginated history (#58751)

## Why

Live paginated history retains completed assistant text even when summaries only need the latest message. Release older text for threads with persisted history while preserving full resume responses.

## What changed

- Evict superseded assistant text from live turn state while preserving corrected items and the latest assistant message.
- Restore evicted text for full resume responses from prepared history pages, fetching remaining items by ID in bounded batches. Return an error if saved text is missing.
- Only load the active turn when it belongs in the requested initial page, and use the item ID index for targeted history reads.

## Testing

Add reducer coverage for text eviction and corrected messages. Extend running-thread resume coverage to compare full and summary items with live notifications, exercise restoration when the prepared page lacks the active turn, and check missing-text errors.

GitOrigin-RevId: 3229c0cea820f117dc6331312f1e6056a8d1d52c
This commit is contained in:
Brent Traut
2026-11-01 20:10:33 +00:00
committed by copyberry
parent 4b4a5cf25a
commit fe16bc0a97
6 changed files with 252 additions and 65 deletions
+9 -2
View File
@@ -139,6 +139,7 @@ pub(crate) struct ThreadState {
listener_command_tx: Option<mpsc::UnboundedSender<ThreadListenerCommand>>,
current_turn_history: ThreadHistoryBuilder,
history_mode: ThreadHistoryMode,
evict_superseded_agent_text: bool,
listener_thread: Option<Weak<CodexThread>>,
watch_registration: WatchRegistration,
}
@@ -181,6 +182,8 @@ impl ThreadState {
self.listener_generation = self.listener_generation.wrapping_add(1);
self.last_thread_settings = Some(thread_settings_baseline);
self.history_mode = history_mode;
self.evict_superseded_agent_text =
history_mode == ThreadHistoryMode::Paginated && conversation.rollout_path().is_some();
let (listener_command_tx, listener_command_rx) = mpsc::unbounded_channel();
self.listener_command_tx = Some(listener_command_tx);
self.listener_thread = Some(Arc::downgrade(conversation));
@@ -243,8 +246,12 @@ impl ThreadState {
{
self.turn_summary.last_agent_message = Some(ThreadItem::from(payload));
}
self.current_turn_history
.handle_event_for_history_mode(event, self.history_mode);
if self.evict_superseded_agent_text {
self.current_turn_history.handle_live_paginated_event(event);
} else {
self.current_turn_history
.handle_event_for_history_mode(event, self.history_mode);
}
if matches!(event, EventMsg::TurnAborted(_) | EventMsg::TurnComplete(_)) {
self.last_terminal_turn_id = Some(event_turn_id.to_string());
if !self.current_turn_history.has_active_turn() {