core: add UUIDv7 context window IDs (#28953)

## Why

The token-budget context currently identifies a context window by its
thread-local sequence number. A UUIDv7 gives the model a stable opaque
identity that remains fixed for a window and rotates when compaction or
`new_context` starts the next one.

## What changed

- Preserve the existing monotonic value as `window_number` and add a
UUIDv7 `window_id` to `CompactedItem`.
- Generate and rotate the UUID with auto-compaction window state,
persist it alongside the number, and reconstruct it on resume and
rollback.
- Accept legacy compacted rollout records where the numeric `window_id`
represented the window number.
- Use the UUID only in token-budget context; existing request headers
and metadata continue using `thread_id:window_number`.

## Testing

- `just test -p codex-protocol compacted_item::tests`
- `just test -p codex-core token_budget`
This commit is contained in:
pakrym-oai
2026-06-18 17:00:49 -07:00
committed by GitHub
parent e83b7841b0
commit 5c12034e42
19 changed files with 319 additions and 87 deletions
+94
View File
@@ -0,0 +1,94 @@
use crate::models::ResponseItem;
use crate::protocol::CompactedItem;
use serde::Deserialize;
// Before `window_number` was introduced, the numeric window number was serialized as
// `window_id`. Accept that shape so existing rollouts remain resumable.
impl<'de> Deserialize<'de> for CompactedItem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let serialized = SerializedCompactedItem::deserialize(deserializer)?;
let mut window_number = serialized.window_number;
let window_id = match serialized.window_id {
Some(SerializedWindowId::Id(window_id)) => Some(window_id),
Some(SerializedWindowId::LegacyWindowNumber(legacy_window_number)) => {
window_number.get_or_insert(legacy_window_number);
None
}
None => None,
};
Ok(Self {
message: serialized.message,
replacement_history: serialized.replacement_history,
window_number,
window_id,
})
}
}
#[derive(Deserialize)]
struct SerializedCompactedItem {
message: String,
#[serde(default)]
replacement_history: Option<Vec<ResponseItem>>,
#[serde(default)]
window_number: Option<u64>,
#[serde(default)]
window_id: Option<SerializedWindowId>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum SerializedWindowId {
Id(String),
LegacyWindowNumber(u64),
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn serializes_window_number_and_id() -> Result<()> {
let item = CompactedItem {
message: "summary".to_string(),
replacement_history: None,
window_number: Some(3),
window_id: Some("019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001".to_string()),
};
assert_eq!(
serde_json::to_value(item)?,
json!({
"message": "summary",
"window_number": 3,
"window_id": "019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001",
})
);
Ok(())
}
#[test]
fn migrates_legacy_numeric_window_id() -> Result<()> {
let item = serde_json::from_value::<CompactedItem>(json!({
"message": "summary",
"window_id": 3,
}))?;
assert_eq!(
item,
CompactedItem {
message: "summary".to_string(),
replacement_history: None,
window_number: Some(3),
window_id: None,
}
);
Ok(())
}
}
+1
View File
@@ -10,6 +10,7 @@ pub use thread_id::ThreadId;
pub use tool_name::ToolName;
pub mod approvals;
pub mod capabilities;
mod compacted_item;
pub mod config_types;
pub mod dynamic_tools;
pub mod error;
+6 -2
View File
@@ -2971,13 +2971,17 @@ pub enum RolloutItem {
EventMsg(EventMsg),
}
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, TS)]
#[derive(Serialize, Clone, Debug, PartialEq, JsonSchema, TS)]
pub struct CompactedItem {
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub replacement_history: Option<Vec<ResponseItem>>,
/// Monotonic position of this context window within the thread.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window_id: Option<u64>,
pub window_number: Option<u64>,
/// UUIDv7 identity of this context window.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window_id: Option<String>,
}
impl From<CompactedItem> for ResponseItem {