## Current State Observations - `Session` currently holds many unrelated responsibilities (history, approval queues, task handles, rollout recorder, shell discovery, token tracking, etc.), making it hard to reason about ownership and lifetimes. - The anonymous `State` struct inside `codex.rs` mixes session-long data with turn-scoped queues and approval bookkeeping. - Turn execution (`run_task`) relies on ad-hoc local variables that should conceptually belong to a per-turn state object. - External modules (`codex::compact`, tests) frequently poke the raw `Session.state` mutex, which couples them to implementation details. - Interrupts, approvals, and rollout persistence all have bespoke cleanup paths, contributing to subtle bugs when a turn is aborted mid-flight. ## Desired End State - Keep a slim `Session` object that acts as the orchestrator and façade. It should expose a focused API (submit, approvals, interrupts, event emission) without storing unrelated fields directly. - Introduce a `state` module that encapsulates all mutable data structures: - `SessionState`: session-persistent data (history, approved commands, token/rate-limit info, maybe user preferences). - `ActiveTurn`: metadata for the currently running turn (sub-id, task kind, abort handle) and an `Arc<TurnState>`. - `TurnState`: all turn-scoped pieces (pending inputs, approval waiters, diff tracker, review history, auto-compact flags, last agent message, outstanding tool call bookkeeping). - Group long-lived helpers/managers into a dedicated `SessionServices` struct so `Session` does not accumulate "random" fields. - Provide clear, lock-safe APIs so other modules never touch raw mutexes. - Ensure every turn creates/drops a `TurnState` and that interrupts/finishes delegate cleanup to it.
83 lines
2.3 KiB
Rust
83 lines
2.3 KiB
Rust
//! Session-wide mutable state.
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use codex_protocol::models::ResponseItem;
|
|
|
|
use crate::codex::AgentTask;
|
|
use crate::conversation_history::ConversationHistory;
|
|
use crate::protocol::RateLimitSnapshot;
|
|
use crate::protocol::TokenUsage;
|
|
use crate::protocol::TokenUsageInfo;
|
|
|
|
/// Persistent, session-scoped state previously stored directly on `Session`.
|
|
#[derive(Default)]
|
|
pub(crate) struct SessionState {
|
|
pub(crate) approved_commands: HashSet<Vec<String>>,
|
|
pub(crate) current_task: Option<AgentTask>,
|
|
pub(crate) history: ConversationHistory,
|
|
pub(crate) token_info: Option<TokenUsageInfo>,
|
|
pub(crate) latest_rate_limits: Option<RateLimitSnapshot>,
|
|
}
|
|
|
|
impl SessionState {
|
|
/// Create a new session state mirroring previous `State::default()` semantics.
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
history: ConversationHistory::new(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
// History helpers
|
|
pub(crate) fn record_items<I>(&mut self, items: I)
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: std::ops::Deref<Target = ResponseItem>,
|
|
{
|
|
self.history.record_items(items)
|
|
}
|
|
|
|
pub(crate) fn history_snapshot(&self) -> Vec<ResponseItem> {
|
|
self.history.contents()
|
|
}
|
|
|
|
pub(crate) fn replace_history(&mut self, items: Vec<ResponseItem>) {
|
|
self.history.replace(items);
|
|
}
|
|
|
|
// Approved command helpers
|
|
pub(crate) fn add_approved_command(&mut self, cmd: Vec<String>) {
|
|
self.approved_commands.insert(cmd);
|
|
}
|
|
|
|
pub(crate) fn approved_commands_ref(&self) -> &HashSet<Vec<String>> {
|
|
&self.approved_commands
|
|
}
|
|
|
|
// Token/rate limit helpers
|
|
pub(crate) fn update_token_info_from_usage(
|
|
&mut self,
|
|
usage: &TokenUsage,
|
|
model_context_window: Option<u64>,
|
|
) {
|
|
self.token_info = TokenUsageInfo::new_or_append(
|
|
&self.token_info,
|
|
&Some(usage.clone()),
|
|
model_context_window,
|
|
);
|
|
}
|
|
|
|
pub(crate) fn set_rate_limits(&mut self, snapshot: RateLimitSnapshot) {
|
|
self.latest_rate_limits = Some(snapshot);
|
|
}
|
|
|
|
pub(crate) fn token_info_and_rate_limits(
|
|
&self,
|
|
) -> (Option<TokenUsageInfo>, Option<RateLimitSnapshot>) {
|
|
(self.token_info.clone(), self.latest_rate_limits.clone())
|
|
}
|
|
|
|
// Pending input/approval moved to TurnState.
|
|
}
|