diff --git a/codex-rs/app-server/src/external_agent_migration/session_importer.rs b/codex-rs/app-server/src/external_agent_migration/session_importer.rs index dabce8a39f..47292d1635 100644 --- a/codex-rs/app-server/src/external_agent_migration/session_importer.rs +++ b/codex-rs/app-server/src/external_agent_migration/session_importer.rs @@ -490,7 +490,7 @@ impl ExternalAgentSessionImporter { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), metadata: ThreadPersistenceMetadata { cwd: Some(cwd.clone()), model_provider: model_provider.clone(), diff --git a/codex-rs/app-server/tests/suite/conversation_summary.rs b/codex-rs/app-server/tests/suite/conversation_summary.rs index cf5bd8ea3a..a1b6ee7bab 100644 --- a/codex-rs/app-server/tests/suite/conversation_summary.rs +++ b/codex-rs/app-server/tests/suite/conversation_summary.rs @@ -140,7 +140,7 @@ async fn get_conversation_summary_by_thread_id_reads_pathless_store_thread() -> history_mode: Default::default(), history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: None, diff --git a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs index f4f72c5e1a..11496ca85a 100644 --- a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs +++ b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs @@ -302,7 +302,7 @@ async fn thread_list_repeated_cursor_returns_internal_error() -> Result<()> { history_mode: Default::default(), history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(codex_home.path().to_path_buf()), @@ -552,7 +552,7 @@ async fn thread_delete_with_non_local_thread_store_does_not_create_local_persist history_mode: Default::default(), history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(codex_home.path().to_path_buf()), diff --git a/codex-rs/app-server/tests/suite/v2/thread_read.rs b/codex-rs/app-server/tests/suite/v2/thread_read.rs index d12af7854c..24222b867d 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_read.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_read.rs @@ -658,7 +658,7 @@ async fn thread_search_occurrences_reads_paginated_projection() -> Result<()> { history_mode: codex_protocol::protocol::ThreadHistoryMode::Paginated, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(codex_home.path().to_path_buf()), @@ -3068,7 +3068,7 @@ async fn create_paginated_thread( history_mode: codex_protocol::protocol::ThreadHistoryMode::Paginated, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(codex_home.to_path_buf()), @@ -3186,7 +3186,7 @@ async fn seed_pathless_store_thread( history_mode: Default::default(), history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: None, diff --git a/codex-rs/app-server/tests/suite/v2/thread_timeline.rs b/codex-rs/app-server/tests/suite/v2/thread_timeline.rs index 9f9e62e171..87286c055c 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_timeline.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_timeline.rs @@ -76,7 +76,7 @@ async fn timeline_pages_mix_items_and_resolve_the_opening_realtime_session() -> history_mode: ThreadHistoryMode::Paginated, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(codex_home.path().to_path_buf()), diff --git a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs index fff83e6fe5..a45211dcf2 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs @@ -265,7 +265,7 @@ async fn thread_unarchive_preserves_pathless_store_metadata() -> Result<()> { history_mode: Default::default(), history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: None, diff --git a/codex-rs/core/src/session/compaction_replay_tests.rs b/codex-rs/core/src/session/compaction_replay_tests.rs index 86ac0a17fd..0749e065d3 100644 --- a/codex-rs/core/src/session/compaction_replay_tests.rs +++ b/codex-rs/core/src/session/compaction_replay_tests.rs @@ -70,9 +70,9 @@ async fn reconstruct_history_uses_replacement_history_verbatim() { guardian_history: None, mcp_resource_origins: None, window_number: Some(42), - first_window_id: Some(first_window_id.to_string()), - previous_window_id: Some(previous_window_id.to_string()), - window_id: Some(window_id.to_string()), + first_window_id: Some(first_window_id.clone()), + previous_window_id: Some(previous_window_id.clone()), + window_id: Some(window_id.clone()), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: None, diff --git a/codex-rs/core/src/session/guardian_checkpoint.rs b/codex-rs/core/src/session/guardian_checkpoint.rs index 6df9b2669e..1d5498369f 100644 --- a/codex-rs/core/src/session/guardian_checkpoint.rs +++ b/codex-rs/core/src/session/guardian_checkpoint.rs @@ -19,12 +19,9 @@ impl Session { retained_context: Some(history.retained_context().clone()), mcp_resource_origins: self.services.mcp_runtime.resource_origin_checkpoint(), window_number: Some(state.auto_compact_window_number()), - first_window_id: Some(window_ids.first_window_id.to_string()), - previous_window_id: window_ids - .previous_window_id - .as_ref() - .map(ToString::to_string), - window_id: Some(window_ids.window_id.to_string()), + first_window_id: Some(window_ids.first_window_id.clone()), + previous_window_id: window_ids.previous_window_id.clone(), + window_id: Some(window_ids.window_id.clone()), compaction_response_id: None, latest_token_usage_record: state.latest_token_usage_record.clone(), resume_metadata: None, diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index b477663de7..5bde090b99 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -4952,12 +4952,9 @@ impl Session { retained_context: Some(state.history.retained_context().clone()), mcp_resource_origins: self.services.mcp_runtime.resource_origin_checkpoint(), window_number: Some(metadata.window_number), - first_window_id: Some(metadata.window_ids.first_window_id.to_string()), - previous_window_id: metadata - .window_ids - .previous_window_id - .map(|id| id.to_string()), - window_id: Some(metadata.window_ids.window_id.to_string()), + first_window_id: Some(metadata.window_ids.first_window_id), + previous_window_id: metadata.window_ids.previous_window_id, + window_id: Some(metadata.window_ids.window_id), compaction_response_id: metadata.compaction_response_id, latest_token_usage_record: state.latest_token_usage_record.clone(), resume_metadata: Some(CompactionResumeMetadata { diff --git a/codex-rs/core/src/session/rollout_reconstruction.rs b/codex-rs/core/src/session/rollout_reconstruction.rs index 930d410a6e..1df79fc656 100644 --- a/codex-rs/core/src/session/rollout_reconstruction.rs +++ b/codex-rs/core/src/session/rollout_reconstruction.rs @@ -659,23 +659,23 @@ fn reconstructed_window_from_compaction(compacted: &CompactedItem) -> Option Option { - let id = ContextWindowId::parse(&context_window.window_id)?; + let id = context_window.window_id.validated()?; Some(ReconstructedWindow { number: 0, first_id: Some(id.clone()), diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index f4b312503a..37683e4a08 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -1461,7 +1461,7 @@ async fn reconstruct_history_restores_initial_window_from_session_meta() { session_id: thread_id.into(), id: thread_id, context_window: Some(SessionContextWindow { - window_id: initial_window_id.to_string(), + window_id: initial_window_id.clone(), }), ..SessionMeta::default() }, @@ -1495,7 +1495,7 @@ async fn reconstruct_history_prefers_compacted_window_over_session_meta() { session_id: thread_id.into(), id: thread_id, context_window: Some(SessionContextWindow { - window_id: initial_window_id.to_string(), + window_id: initial_window_id.clone(), }), ..SessionMeta::default() }, @@ -1508,9 +1508,9 @@ async fn reconstruct_history_prefers_compacted_window_over_session_meta() { guardian_history: None, mcp_resource_origins: None, window_number: Some(2), - first_window_id: Some(compacted_first_window_id.to_string()), - previous_window_id: Some(compacted_previous_window_id.to_string()), - window_id: Some(compacted_window_id.to_string()), + first_window_id: Some(compacted_first_window_id.clone()), + previous_window_id: Some(compacted_previous_window_id.clone()), + window_id: Some(compacted_window_id.clone()), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: None, @@ -1636,9 +1636,9 @@ async fn bounded_replay_matches_full_replay_after_empty_turn_compactions( ])), mcp_resource_origins: None, window_number: Some(window_number as u64), - first_window_id: Some(window_ids[0].to_string()), - previous_window_id: Some(window_ids[window_number - 1].to_string()), - window_id: Some(window_ids[window_number].to_string()), + first_window_id: Some(window_ids[0].clone()), + previous_window_id: Some(window_ids[window_number - 1].clone()), + window_id: Some(window_ids[window_number].clone()), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: current.then(|| codex_history::CompactionResumeMetadata { @@ -1993,7 +1993,7 @@ async fn reconstruct_history_preserves_legacy_compaction_count_with_session_meta session_id: thread_id.into(), id: thread_id, context_window: Some(SessionContextWindow { - window_id: initial_window_id.to_string(), + window_id: initial_window_id.clone(), }), ..SessionMeta::default() }, diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 0c73c8e303..fba4ce19cc 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -1104,7 +1104,7 @@ impl Session { && session_configuration.session_source.is_non_root_agent() && config.features.enabled(Feature::TokenBudget); if restore_child_window && let InitialHistory::Forked(items) = &mut initial_history { - let child_window_id = initial_auto_compact_window_ids.window_id.to_string(); + let child_window_id = initial_auto_compact_window_ids.window_id.clone(); for item in items { if let RolloutItem::Compacted(checkpoint) = item { checkpoint.window_number = Some(0); @@ -1241,9 +1241,7 @@ impl Session { ForkPersistence::Referenced { history_base, .. } => *history_base, }, subagent_history_start_ordinal: None, - initial_window_id: initial_auto_compact_window_ids - .window_id - .to_string(), + initial_window_id: initial_auto_compact_window_ids.window_id.clone(), runtime_workspace_roots: Some(config.workspace_roots.clone()), metadata: ThreadPersistenceMetadata { cwd: Some(config.cwd.to_path_buf()), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 204f672121..39dcd3cd54 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -4922,7 +4922,7 @@ async fn open_thread_persistence(session: &mut Session) -> PathBuf { history_mode: Default::default(), subagent_history_start_ordinal: None, history_base: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(config.cwd.to_path_buf()), @@ -8716,7 +8716,7 @@ async fn shutdown_complete_does_not_append_to_thread_store_after_shutdown() { history_mode: Default::default(), subagent_history_start_ordinal: None, history_base: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(config.cwd.to_path_buf()), @@ -8855,7 +8855,7 @@ async fn submission_loop_channel_close_runs_full_thread_teardown() -> anyhow::Re history_mode: Default::default(), subagent_history_start_ordinal: None, history_base: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(config.cwd.to_path_buf()), @@ -11839,7 +11839,7 @@ async fn attach_in_memory_thread_store( history_mode: Default::default(), subagent_history_start_ordinal: None, history_base: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(config.cwd.to_path_buf()), @@ -13395,9 +13395,9 @@ async fn sample_rollout( guardian_history: None, mcp_resource_origins: None, window_number: Some(window_number), - first_window_id: Some(window_ids.first_window_id.to_string()), - previous_window_id: window_ids.previous_window_id.map(|id| id.to_string()), - window_id: Some(window_ids.window_id.to_string()), + first_window_id: Some(window_ids.first_window_id), + previous_window_id: window_ids.previous_window_id, + window_id: Some(window_ids.window_id), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: None, @@ -13430,9 +13430,9 @@ async fn sample_rollout( guardian_history: None, mcp_resource_origins: None, window_number: Some(window_number), - first_window_id: Some(window_ids.first_window_id.to_string()), - previous_window_id: window_ids.previous_window_id.map(|id| id.to_string()), - window_id: Some(window_ids.window_id.to_string()), + first_window_id: Some(window_ids.first_window_id), + previous_window_id: window_ids.previous_window_id, + window_id: Some(window_ids.window_id), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: None, diff --git a/codex-rs/core/tests/suite/subagent_notifications.rs b/codex-rs/core/tests/suite/subagent_notifications.rs index 44c26e15ec..d5634b3be0 100644 --- a/codex-rs/core/tests/suite/subagent_notifications.rs +++ b/codex-rs/core/tests/suite/subagent_notifications.rs @@ -2006,14 +2006,21 @@ async fn spawned_full_history_v2_child_uses_model_precedence_without_dropping_co .as_ref() .is_some_and(|history| !history.is_empty()) ); + let expected_window = + codex_protocol::ContextWindowId::parse(window_ids[1]).expect("UUIDv7 child window"); assert_eq!( ( checkpoint.window_number, - checkpoint.first_window_id.as_deref(), - checkpoint.previous_window_id.as_deref(), - checkpoint.window_id.as_deref(), + checkpoint.first_window_id.as_ref(), + checkpoint.previous_window_id.as_ref(), + checkpoint.window_id.as_ref(), ), - (Some(0), Some(window_ids[1]), None, Some(window_ids[1])) + ( + Some(0), + Some(&expected_window), + None, + Some(&expected_window) + ) ); assert!( child_request.has_message_with_input_texts("developer", |message| { diff --git a/codex-rs/ext/guardian-v3/tests/evidence.rs b/codex-rs/ext/guardian-v3/tests/evidence.rs index d612659429..0ca967a661 100644 --- a/codex-rs/ext/guardian-v3/tests/evidence.rs +++ b/codex-rs/ext/guardian-v3/tests/evidence.rs @@ -67,7 +67,7 @@ async fn captures_originals_without_generated_context_or_later_appends() -> anyh history_mode: ThreadHistoryMode::Paginated, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: "window-1".into(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(dir.path().to_path_buf()), diff --git a/codex-rs/history/src/lib.rs b/codex-rs/history/src/lib.rs index 27c81a3cec..28ad371cde 100644 --- a/codex-rs/history/src/lib.rs +++ b/codex-rs/history/src/lib.rs @@ -322,9 +322,9 @@ pub struct CompactedItem { pub retained_context: Option, pub mcp_resource_origins: Option, pub window_number: Option, - pub first_window_id: Option, - pub previous_window_id: Option, - pub window_id: Option, + pub first_window_id: Option, + pub previous_window_id: Option, + pub window_id: Option, /// Responses API ID for the model-backed compaction request, when one exists. pub compaction_response_id: Option, /// Snapshot of the latest reachable token usage record when this compaction was written. diff --git a/codex-rs/history/src/rollout_payload.rs b/codex-rs/history/src/rollout_payload.rs index 5b32febdbb..e7bf15f0fd 100644 --- a/codex-rs/history/src/rollout_payload.rs +++ b/codex-rs/history/src/rollout_payload.rs @@ -16,6 +16,7 @@ use super::TurnContextItem; use super::WorldStateItem; use crate::RetainedContextEvent; use crate::RetainedUserMessage; +use codex_protocol::ContextWindowId; use codex_protocol::ResponseItemId; use codex_protocol::models::ContentItem; use codex_protocol::models::InternalChatMessageMetadataPassthrough; @@ -337,11 +338,11 @@ pub(super) struct CompactedItemWire<'a> { #[serde(default, skip_serializing_if = "Option::is_none")] window_number: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - first_window_id: Option>, + first_window_id: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - previous_window_id: Option>, + previous_window_id: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] - #[schemars(with = "Option")] + #[schemars(with = "Option")] window_id: Option>, #[serde(default)] compaction_response_id: Option>, @@ -380,11 +381,11 @@ impl<'a> From<&'a CompactedItem> for CompactedItemWire<'a> { retained_context: item.retained_context.as_ref().map(Cow::Borrowed), mcp_resource_origins: item.mcp_resource_origins.as_ref().map(Cow::Borrowed), window_number: item.window_number, - first_window_id: item.first_window_id.as_deref().map(Cow::Borrowed), - previous_window_id: item.previous_window_id.as_deref().map(Cow::Borrowed), + first_window_id: item.first_window_id.as_ref().map(Cow::Borrowed), + previous_window_id: item.previous_window_id.as_ref().map(Cow::Borrowed), window_id: item .window_id - .as_deref() + .as_ref() .map(|window_id| WindowIdWire::Id(Cow::Borrowed(window_id))), compaction_response_id: item.compaction_response_id.as_deref().map(Cow::Borrowed), latest_token_usage_record: item.latest_token_usage_record.as_ref().map(Cow::Borrowed), @@ -463,6 +464,6 @@ impl TryFrom> for CompactedItem { #[derive(Serialize, Deserialize)] #[serde(untagged)] enum WindowIdWire<'a> { - Id(Cow<'a, str>), + Id(Cow<'a, ContextWindowId>), LegacyWindowNumber(u64), } diff --git a/codex-rs/history/src/tests.rs b/codex-rs/history/src/tests.rs index 4820e54e6e..3e929d3926 100644 --- a/codex-rs/history/src/tests.rs +++ b/codex-rs/history/src/tests.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use codex_protocol::ContextWindowId; use codex_protocol::models::AudioReference; use codex_protocol::models::ConfigurationReasoning; use codex_protocol::models::PermissionProfile; @@ -1047,9 +1048,9 @@ fn compacted_item_serializes_window_number_and_id() -> Result<()> { guardian_history: None, mcp_resource_origins: None, window_number: Some(3), - first_window_id: Some("019b3f6e-0000-7000-8000-000000000001".to_string()), - previous_window_id: Some("019b3f6e-0000-7000-8000-000000000002".to_string()), - window_id: Some("019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001".to_string()), + first_window_id: ContextWindowId::parse("019b3f6e-0000-7000-8000-000000000001"), + previous_window_id: ContextWindowId::parse("019b3f6e-0000-7000-8000-000000000002"), + window_id: ContextWindowId::parse("019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001"), compaction_response_id: None, latest_token_usage_record: None, resume_metadata: None, diff --git a/codex-rs/protocol/src/context_window_id.rs b/codex-rs/protocol/src/context_window_id.rs index 38e77f4a94..115439ccd7 100644 --- a/codex-rs/protocol/src/context_window_id.rs +++ b/codex-rs/protocol/src/context_window_id.rs @@ -1,10 +1,12 @@ //! Context window identity used for compaction, rollout restoration, and request metadata. +use schemars::JsonSchema; +use serde::Deserialize; use serde::Serialize; use std::fmt; use uuid::Uuid; -#[derive(derive_more::Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(derive_more::Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[debug("{_0}")] #[serde(transparent)] pub struct ContextWindowId(String); @@ -21,6 +23,11 @@ impl ContextWindowId { .filter(|uuid| uuid.get_version_num() == 7) .map(|uuid| Self(uuid.to_string())) } + + /// Validate a deserialized window ID before restoring it. + pub fn validated(&self) -> Option { + Self::parse(&self.0) + } } impl fmt::Display for ContextWindowId { diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 45f795a3d2..dedf79e3bc 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -3448,14 +3448,14 @@ pub enum MultiAgentVersion { V2, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct SessionContextWindow { /// UUIDv7 identity of this context window. - pub window_id: String, + pub window_id: crate::ContextWindowId, } impl SessionContextWindow { - pub fn new(window_id: String) -> Self { + pub fn new(window_id: crate::ContextWindowId) -> Self { Self { window_id } } } @@ -3481,7 +3481,7 @@ pub struct HistoryPosition { /// NOTE: There used to be an `instructions` field here, which stored user_instructions, but we /// now save that on TurnContext. base_instructions stores the base instructions for the session, /// and should be used when there is no config override. -#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, TS)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] pub struct SessionMeta { /// Scope discovery to the creator. Absent on threads created before account filtering. #[serde(default, skip_serializing_if = "std::ops::Not::not")] @@ -3513,7 +3513,6 @@ pub struct SessionMeta { /// An absent value means unknown; an empty list means no roots. /// Keep native paths parseable across hosts; validate them when restoring settings. #[serde(default, skip_serializing_if = "Option::is_none")] - #[ts(optional)] pub runtime_workspace_roots: Option>, pub originator: String, pub cli_version: String, @@ -3602,7 +3601,7 @@ impl Default for SessionMeta { } } -#[derive(Serialize, Debug, Clone, JsonSchema, TS)] +#[derive(Serialize, Debug, Clone, JsonSchema)] pub struct SessionMetaLine { #[serde(flatten)] pub meta: SessionMeta, diff --git a/codex-rs/rollout/src/recorder.rs b/codex-rs/rollout/src/recorder.rs index b2e815388c..abee7efcfa 100644 --- a/codex-rs/rollout/src/recorder.rs +++ b/codex-rs/rollout/src/recorder.rs @@ -117,7 +117,7 @@ pub enum RolloutRecorderParams { history_mode: ThreadHistoryMode, history_base: Option, subagent_history_start_ordinal: Option, - initial_window_id: Option, + initial_window_id: Option, }, Resume { path: PathBuf, @@ -373,7 +373,10 @@ impl RolloutRecorderParams { self } - pub fn with_initial_window_id(mut self, initial_window_id: String) -> Self { + pub fn with_initial_window_id( + mut self, + initial_window_id: codex_protocol::ContextWindowId, + ) -> Self { if let Self::Create { initial_window_id: window_id, .. diff --git a/codex-rs/rollout/src/recorder_tests.rs b/codex-rs/rollout/src/recorder_tests.rs index 0f88cb986f..a0968d3cdd 100644 --- a/codex-rs/rollout/src/recorder_tests.rs +++ b/codex-rs/rollout/src/recorder_tests.rs @@ -699,7 +699,7 @@ async fn recorder_materializes_on_flush_with_pending_items() -> std::io::Result< let config = test_config(home.path()); let session_id = SessionId::default(); let thread_id = ThreadId::new(); - let initial_window_id = codex_protocol::ContextWindowId::new_uuid_v7().to_string(); + let initial_window_id = codex_protocol::ContextWindowId::new_uuid_v7(); let recorder = RolloutRecorder::new( &config, RolloutRecorderParams::new( diff --git a/codex-rs/thread-store/src/in_memory.rs b/codex-rs/thread-store/src/in_memory.rs index 7303edc797..6939111b81 100644 --- a/codex-rs/thread-store/src/in_memory.rs +++ b/codex-rs/thread-store/src/in_memory.rs @@ -186,7 +186,7 @@ mod tests { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: None, @@ -463,7 +463,7 @@ mod tests { history_mode, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: thread_metadata(), } diff --git a/codex-rs/thread-store/src/local/mod.rs b/codex-rs/thread-store/src/local/mod.rs index f779f97cd0..680a9de531 100644 --- a/codex-rs/thread-store/src/local/mod.rs +++ b/codex-rs/thread-store/src/local/mod.rs @@ -2406,7 +2406,7 @@ mod tests { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: thread_metadata(), } diff --git a/codex-rs/thread-store/src/local/pending_thread_metadata_tests.rs b/codex-rs/thread-store/src/local/pending_thread_metadata_tests.rs index 1721596062..156ecc8cb5 100644 --- a/codex-rs/thread-store/src/local/pending_thread_metadata_tests.rs +++ b/codex-rs/thread-store/src/local/pending_thread_metadata_tests.rs @@ -657,7 +657,7 @@ fn create_thread_params(thread_id: ThreadId) -> CreateThreadParams { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(std::env::current_dir().expect("cwd")), diff --git a/codex-rs/thread-store/src/local/revert_thread_tests.rs b/codex-rs/thread-store/src/local/revert_thread_tests.rs index 3a95d73575..8bfeb04322 100644 --- a/codex-rs/thread-store/src/local/revert_thread_tests.rs +++ b/codex-rs/thread-store/src/local/revert_thread_tests.rs @@ -368,7 +368,7 @@ async fn create_paginated_thread(store: &LocalThreadStore, thread_id: ThreadId) history_mode: ThreadHistoryMode::Paginated, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: "window-1".to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: Some(vec![store.config.codex_home.join("workspace").abs()]), metadata: ThreadPersistenceMetadata { cwd: Some(std::env::current_dir().expect("cwd")), diff --git a/codex-rs/thread-store/src/local/thread_history_materialization_tests.rs b/codex-rs/thread-store/src/local/thread_history_materialization_tests.rs index 7cebac97e3..a12bb69fa1 100644 --- a/codex-rs/thread-store/src/local/thread_history_materialization_tests.rs +++ b/codex-rs/thread-store/src/local/thread_history_materialization_tests.rs @@ -138,7 +138,7 @@ async fn split_homes_support_backfill_listing_and_paginated_history() { Vec::new(), ) .with_history_mode(ThreadHistoryMode::Paginated) - .with_initial_window_id("window-1".to_string()), + .with_initial_window_id(codex_protocol::ContextWindowId::new_uuid_v7()), ) .await .expect("create paginated rollout"); @@ -775,7 +775,7 @@ async fn paginated_realtime_items_materialize_separately_in_rollout_order() { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: "window-1".to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(home.path().to_path_buf()), @@ -3097,7 +3097,7 @@ async fn create_paginated_subagent_thread( history_mode: ThreadHistoryMode::Paginated, history_base, subagent_history_start_ordinal, - initial_window_id: "window-1".to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: Some(std::env::current_dir().expect("cwd")), diff --git a/codex-rs/thread-store/src/thread_metadata_sync.rs b/codex-rs/thread-store/src/thread_metadata_sync.rs index 0c80650162..c972769cec 100644 --- a/codex-rs/thread-store/src/thread_metadata_sync.rs +++ b/codex-rs/thread-store/src/thread_metadata_sync.rs @@ -508,7 +508,7 @@ mod tests { history_mode: ThreadHistoryMode::Legacy, history_base: None, subagent_history_start_ordinal: None, - initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7().to_string(), + initial_window_id: codex_protocol::ContextWindowId::new_uuid_v7(), runtime_workspace_roots: None, metadata: ThreadPersistenceMetadata { cwd: None, diff --git a/codex-rs/thread-store/src/types.rs b/codex-rs/thread-store/src/types.rs index 02979d0f68..3a3bad01e1 100644 --- a/codex-rs/thread-store/src/types.rs +++ b/codex-rs/thread-store/src/types.rs @@ -113,7 +113,7 @@ pub struct CreateThreadParams { /// First rollout ordinal that belongs to this subagent's projected history. pub subagent_history_start_ordinal: Option, /// Initial context-window identity captured when the thread was created. - pub initial_window_id: String, + pub initial_window_id: codex_protocol::ContextWindowId, /// Runtime workspace roots at creation, excluding permission-profile roots. #[serde(default, skip_serializing_if = "Option::is_none")] pub runtime_workspace_roots: Option>,