Track inherited paginated rollout prefixes (#33930)

## What changed

- Add `HistoryPosition` to identify a source thread and an exclusive rollout
  prefix by ordinal and JSONL byte offset.
- Add optional `history_base` metadata to `SessionMeta` so a thread can record
  an inherited prefix of another paginated rollout.
- Default missing `history_base` metadata to `None` and omit it when unset to
  preserve compatibility with existing rollout metadata.

## Testing

- Verify that legacy session metadata without `history_base` deserializes with
  no inherited history position.

GitOrigin-RevId: 3b2b07a2dfee793d3b37b0a60529f856f167dbc1
This commit is contained in:
Owen Lin
2026-07-18 00:06:49 +00:00
committed by copyberry
parent 15d7d2a733
commit bcdc695877
14 changed files with 33 additions and 0 deletions
+15
View File
@@ -3049,6 +3049,16 @@ impl SessionContextWindow {
}
}
/// Exclusive position in another thread's paginated rollout history.
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, JsonSchema, TS)]
pub struct HistoryPosition {
pub thread_id: ThreadId,
/// First rollout ordinal not included from the prefix file.
pub end_ordinal_exclusive: u64,
/// Byte offset immediately after the last included JSONL record from the prefix file.
pub end_byte_offset: u64,
}
/// SessionMeta contains session-level data that doesn't correspond to a specific turn.
///
/// NOTE: There used to be an `instructions` field here, which stored user_instructions, but we
@@ -3098,6 +3108,9 @@ pub struct SessionMeta {
pub memory_mode: Option<String>,
#[serde(default)]
pub history_mode: ThreadHistoryMode,
/// Exclusive prefix of another paginated rollout inherited by this thread.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub history_base: Option<HistoryPosition>,
/// First rollout ordinal that belongs to this subagent's own projected history.
///
/// Earlier rollout records are inherited model context and stay out of child
@@ -3134,6 +3147,7 @@ impl Default for SessionMeta {
selected_capability_roots: Vec::new(),
memory_mode: None,
history_mode: ThreadHistoryMode::default(),
history_base: None,
subagent_history_start_ordinal: None,
multi_agent_version: None,
context_window: None,
@@ -5854,6 +5868,7 @@ mod tests {
}))?;
assert_eq!(session_meta.history_mode, ThreadHistoryMode::Legacy);
assert_eq!(session_meta.history_base, None);
let serialized = serde_json::to_value(&session_meta)?;
assert_eq!(serialized["history_mode"], json!("legacy"));
let mut unknown = serialized;