//! Session-wide mutable state. use codex_protocol::models::ResponseItem; use crate::codex::SessionConfiguration; use crate::context_manager::ContextManager; use crate::protocol::RateLimitSnapshot; use crate::protocol::TokenUsage; use crate::protocol::TokenUsageInfo; use crate::truncate::TruncationPolicy; /// Persistent, session-scoped state previously stored directly on `Session`. pub(crate) struct SessionState { pub(crate) session_configuration: SessionConfiguration, pub(crate) history: ContextManager, pub(crate) latest_rate_limits: Option, } impl SessionState { /// Create a new session state mirroring previous `State::default()` semantics. pub(crate) fn new(session_configuration: SessionConfiguration) -> Self { let history = ContextManager::new(); Self { session_configuration, history, latest_rate_limits: None, } } // History helpers pub(crate) fn record_items(&mut self, items: I, policy: TruncationPolicy) where I: IntoIterator, I::Item: std::ops::Deref, { self.history.record_items(items, policy); } pub(crate) fn clone_history(&self) -> ContextManager { self.history.clone() } pub(crate) fn replace_history(&mut self, items: Vec) { self.history.replace(items); } pub(crate) fn set_token_info(&mut self, info: Option) { self.history.set_token_info(info); } // Token/rate limit helpers pub(crate) fn update_token_info_from_usage( &mut self, usage: &TokenUsage, model_context_window: Option, ) { self.history.update_token_info(usage, model_context_window); } pub(crate) fn token_info(&self) -> Option { self.history.token_info() } pub(crate) fn set_rate_limits(&mut self, snapshot: RateLimitSnapshot) { self.latest_rate_limits = Some(merge_rate_limit_fields( self.latest_rate_limits.as_ref(), snapshot, )); } pub(crate) fn token_info_and_rate_limits( &self, ) -> (Option, Option) { (self.token_info(), self.latest_rate_limits.clone()) } pub(crate) fn set_token_usage_full(&mut self, context_window: i64) { self.history.set_token_usage_full(context_window); } pub(crate) fn get_total_token_usage(&self) -> i64 { self.history.get_total_token_usage() } } // Sometimes new snapshots don't include credits or plan information. fn merge_rate_limit_fields( previous: Option<&RateLimitSnapshot>, mut snapshot: RateLimitSnapshot, ) -> RateLimitSnapshot { if snapshot.credits.is_none() { snapshot.credits = previous.and_then(|prior| prior.credits.clone()); } if snapshot.plan_type.is_none() { snapshot.plan_type = previous.and_then(|prior| prior.plan_type); } snapshot }