Preserve verified answers across history compaction (#42293)

## Why

Compaction replaces the model history window, but host-verified user answers may
still be needed for the rest of the owning thread.

## What changed

- Add bounded, model-invisible retained context for verified
  `request_user_input` answers.
- Persist retained context in rollout events and compaction checkpoints, and
  restore it when a thread resumes.
- Remove answers when their source turns are rolled back, and prevent
  parent-local answers from being inherited by child forks.

## Testing

Add lifecycle coverage for deduplication and size limits, compaction and resume,
rollback, rollout migration, and child forks.

GitOrigin-RevId: c32900d3e8120a9899b71f0576683acd67a4b898
This commit is contained in:
felixxia-oai authored and copyberry committed 2026-09-02 15:07:11 +00:00
1 parent 389dd56459
commit 5971d42847
45 files changed
+805 -9

No files matched your search

+4
View File
@@ -1540,6 +1540,7 @@ impl Session {
) -> Option<PreviousTurnSettings> {
let rollout_reconstruction::RolloutReconstruction {
mut history,
retained_context,
guardian_history,
previous_turn_settings,
reference_context_item,
@@ -1586,6 +1587,7 @@ impl Session {
state
.history
.restore_guardian_history(guardian_history.as_ref());
state.history.restore_retained_context(&retained_context);
if let Some(world_state) = world_state_baseline {
state.history.set_world_state_baseline(world_state);
}
@@ -3760,6 +3762,7 @@ impl Session {
let mut compacted_item = CompactedItem {
message: metadata.message,
replacement_history: Some(items.clone()),
retained_context: None,
guardian_history: None,
mcp_resource_origins: self.services.mcp_runtime.resource_origin_checkpoint(),
window_number: Some(metadata.window_number),
@@ -3785,6 +3788,7 @@ impl Session {
HistoryReplacement::Compaction,
);
compacted_item.guardian_history = state.history.guardian_history_checkpoint();
compacted_item.retained_context = Some(state.history.retained_context().clone());
if let Some(world_state) = world_state_baseline {
let snapshot = world_state.snapshot();
world_state_item = Some(WorldStateItem::full(snapshot.clone().into_object()));