Preserve model-facing ID allocations in saved history (#60771)

## Why

Restoring model-facing ID sequences requires allocation ordinals alongside the rendered IDs. Responses API payloads still require string IDs.

## What changed

- Add allocation metadata to `ResponseItemId` while keeping equality, hashing, and ordering based on the rendered ID.
- Preserve optional `id_allocation` metadata in rollout items, inter-agent messages, compaction replacement history, and Guardian checkpoints. Continue accepting legacy string IDs.
- Support structured context-window allocations alongside UUIDv7 strings, validate the `window` salt on restoration, and keep request metadata IDs as strings.

## Testing

Add tests for allocation round trips, legacy ID compatibility, string-only raw response item IDs, and context-window allocation validation.

GitOrigin-RevId: ce1fd964bde9a4e7c527432be99aea87ada30169
This commit is contained in:
Ruslan Nigmatullin
2026-11-11 09:58:13 +00:00
committed by copyberry
parent 32234ddac4
commit 340a18ed7c
15 changed files with 450 additions and 85 deletions
+6 -4
View File
@@ -438,9 +438,11 @@ impl CodexResponsesMetadata {
turn_id: self.turn_id.as_deref(),
window_id: has_request_identity.then_some(self.window_id.as_str()),
window_number: has_request_identity.then_some(self.window_number).flatten(),
context_window_id: has_request_identity
.then_some(self.context_window_id.as_ref())
.flatten(),
context_window_id: self
.context_window_id
.as_ref()
.filter(|_| has_request_identity)
.map(ToString::to_string),
request_kind: request_kind_value,
history_initialization: self.history_initialization,
forked_from_thread_id: self.forked_from_thread_id,
@@ -572,7 +574,7 @@ struct CodexTurnMetadataPayload<'a> {
#[serde(default, skip_serializing_if = "Option::is_none")]
window_number: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
context_window_id: Option<&'a ContextWindowId>,
context_window_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
request_kind: Option<&'static str>,
#[serde(default, skip_serializing_if = "Option::is_none")]