core: persist initial context window metadata

Record the initial AutoCompactWindowIds window_id in session_meta.context_window so JSONL tailers can observe the first context window before any compaction item exists. Thread creation now requires an initial_window_id string, while rollout JSONL keeps that value under the context_window namespace without writing derived chain fields for the initial window.

Rollout reconstruction derives window 0 state from that initial_window_id when no compaction window metadata exists. Forks intentionally use the same reconstruction path; consumers that need to distinguish copied initial-window metadata can use thread_id. Legacy compactions without window_number continue to use compaction-count fallback accounting.

Verification:
- just test -p codex-protocol
- just test -p codex-rollout recorder_materializes_on_flush_with_pending_items
- just test -p codex-core reconstruct_history_restores_initial_window_from_session_meta
- just test -p codex-core reconstruct_history_prefers_compacted_window_over_session_meta
- just test -p codex-core reconstruct_history_preserves_legacy_compaction_count_with_session_meta_window
- just test -p codex-core reconstruct_history
- just test -p codex-core record_initial_history_reconstructs_forked_transcript
- just test -p codex-thread-store
- just test -p codex-state
- just test -p codex-app-server thread_read_returns_summary_without_turns
- just test -p codex-rollout persistence_metrics
- just fix -p codex-protocol -p codex-rollout -p codex-thread-store -p codex-core -p codex-app-server
- just fix -p codex-core -p codex-rollout
- just fix -p codex-core
This commit is contained in:
Michael Bolin
2026-06-23 14:26:39 -07:00
parent 7b40e3523f
commit 1dfbe151f1
31 changed files with 255 additions and 13 deletions
+3
View File
@@ -10,6 +10,7 @@ use codex_protocol::ThreadId;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::SessionContextWindow;
use codex_protocol::protocol::SessionMeta;
use codex_protocol::protocol::SessionMetaLine;
use codex_protocol::protocol::ThreadMemoryMode;
@@ -120,6 +121,7 @@ mod tests {
base_instructions: BaseInstructions::default(),
dynamic_tools: Vec::new(),
multi_agent_version: None,
initial_window_id: uuid::Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: None,
model_provider: "test-provider".to_string(),
@@ -248,6 +250,7 @@ impl InMemoryThreadStore {
memory_mode: matches!(params.metadata.memory_mode, ThreadMemoryMode::Disabled)
.then_some("disabled".to_string()),
multi_agent_version: params.multi_agent_version,
context_window: Some(SessionContextWindow::new(params.initial_window_id.clone())),
..SessionMeta::default()
};
state
@@ -37,7 +37,8 @@ pub(super) async fn create_thread(
params.dynamic_tools,
)
.with_session_id(params.session_id)
.with_multi_agent_version(params.multi_agent_version),
.with_multi_agent_version(params.multi_agent_version)
.with_initial_window_id(params.initial_window_id),
)
.await
.map_err(|err| ThreadStoreError::Internal {
+1
View File
@@ -1133,6 +1133,7 @@ mod tests {
base_instructions: BaseInstructions::default(),
dynamic_tools: Vec::new(),
multi_agent_version: None,
initial_window_id: uuid::Uuid::now_v7().to_string(),
metadata: thread_metadata(),
}
}
+2
View File
@@ -85,6 +85,8 @@ pub struct CreateThreadParams {
pub dynamic_tools: Vec<DynamicToolSpec>,
/// Multi-agent runtime selected when the thread was created.
pub multi_agent_version: Option<MultiAgentVersion>,
/// Initial context-window identity captured when the thread was created.
pub initial_window_id: String,
/// Metadata captured for the newly created thread.
pub metadata: ThreadPersistenceMetadata,
}