Skip to main content

a3s_code_core/store/
session_data.rs

1use crate::llm::{Message, TokenUsage, ToolDefinition};
2use crate::planning::Task;
3use crate::prompts::PlanningMode;
4use crate::queue::SessionQueueConfig;
5use serde::{Deserialize, Serialize};
6
7// ============================================================================
8// Serializable Session Data
9// ============================================================================
10
11/// Session state persisted with saved sessions.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
13pub enum SessionState {
14    #[default]
15    Unknown = 0,
16    Active = 1,
17    Paused = 2,
18    Completed = 3,
19    Error = 4,
20}
21
22/// Context usage statistics persisted with saved sessions.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ContextUsage {
25    pub used_tokens: usize,
26    pub max_tokens: usize,
27    pub percent: f32,
28    pub turns: usize,
29}
30
31impl Default for ContextUsage {
32    fn default() -> Self {
33        Self {
34            used_tokens: 0,
35            max_tokens: 200_000,
36            percent: 0.0,
37            turns: 0,
38        }
39    }
40}
41
42/// Default auto-compact threshold (80% of context window).
43pub const DEFAULT_AUTO_COMPACT_THRESHOLD: f32 = 0.80;
44
45pub(crate) fn default_auto_compact_threshold() -> f32 {
46    DEFAULT_AUTO_COMPACT_THRESHOLD
47}
48
49/// Serializable session configuration.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct SessionConfig {
52    pub name: String,
53    pub workspace: String,
54    pub system_prompt: Option<String>,
55    pub max_context_length: u32,
56    pub auto_compact: bool,
57    /// Context usage percentage threshold to trigger auto-compaction (0.0 - 1.0).
58    /// Only used when `auto_compact` is true. Default: 0.80 (80%).
59    #[serde(default = "default_auto_compact_threshold")]
60    pub auto_compact_threshold: f32,
61    /// Storage type for this session.
62    #[serde(default)]
63    pub storage_type: crate::config::StorageBackend,
64    /// Optional advanced queue configuration.
65    ///
66    /// Queue infrastructure is initialized only when this is set. Ordinary
67    /// sessions stay queue-free.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub queue_config: Option<SessionQueueConfig>,
70    /// Confirmation policy (optional, uses defaults if None).
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub confirmation_policy: Option<crate::hitl::ConfirmationPolicy>,
73    /// Permission policy (optional, uses defaults if None).
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub permission_policy: Option<crate::permissions::PermissionPolicy>,
76    /// Maximum sibling branches/tools to run concurrently.
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub max_parallel_tasks: Option<usize>,
79    /// Automatic subagent delegation settings.
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub auto_delegation: Option<crate::config::AutoDelegationConfig>,
82    /// Parent session ID (for delegated child sessions).
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub parent_id: Option<String>,
85    /// Security configuration (optional, enables security features).
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub security_config: Option<crate::security::SecurityConfig>,
88    /// Shared hook engine for lifecycle events.
89    #[serde(skip)]
90    pub hook_engine: Option<std::sync::Arc<dyn crate::hooks::HookExecutor>>,
91    /// Enable planning phase before execution.
92    #[serde(default)]
93    pub planning_mode: PlanningMode,
94    /// Enable goal tracking.
95    #[serde(default)]
96    pub goal_tracking: bool,
97}
98
99impl Default for SessionConfig {
100    fn default() -> Self {
101        Self {
102            name: String::new(),
103            workspace: String::new(),
104            system_prompt: None,
105            max_context_length: 0,
106            auto_compact: false,
107            auto_compact_threshold: DEFAULT_AUTO_COMPACT_THRESHOLD,
108            storage_type: crate::config::StorageBackend::default(),
109            queue_config: None,
110            confirmation_policy: None,
111            permission_policy: None,
112            max_parallel_tasks: None,
113            auto_delegation: None,
114            parent_id: None,
115            security_config: None,
116            hook_engine: None,
117            planning_mode: PlanningMode::default(),
118            goal_tracking: false,
119        }
120    }
121}
122
123/// Serializable session data for persistence
124///
125/// Contains only the fields that can be serialized.
126/// Non-serializable fields (event_tx, command_queue, etc.) are rebuilt on load.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct SessionData {
129    /// Session ID
130    pub id: String,
131
132    /// Session configuration
133    pub config: SessionConfig,
134
135    /// Current state
136    pub state: SessionState,
137
138    /// Conversation history
139    pub messages: Vec<Message>,
140
141    /// Context usage statistics
142    pub context_usage: ContextUsage,
143
144    /// Total token usage
145    pub total_usage: TokenUsage,
146
147    /// Cumulative dollar cost for this session
148    #[serde(default)]
149    pub total_cost: f64,
150
151    /// Model name for cost calculation
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub model_name: Option<String>,
154
155    /// LLM cost records for this session
156    #[serde(default)]
157    pub cost_records: Vec<crate::telemetry::LlmCostRecord>,
158
159    /// Tool definitions (names only, rebuilt from executor on load)
160    pub tool_names: Vec<String>,
161
162    /// Whether thinking mode is enabled
163    pub thinking_enabled: bool,
164
165    /// Thinking budget if set
166    pub thinking_budget: Option<usize>,
167
168    /// Creation timestamp (Unix epoch seconds)
169    pub created_at: i64,
170
171    /// Last update timestamp (Unix epoch seconds)
172    pub updated_at: i64,
173
174    /// LLM configuration for per-session client (if set)
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub llm_config: Option<LlmConfigData>,
177
178    /// Task list for tracking
179    #[serde(default, alias = "todos")]
180    pub tasks: Vec<Task>,
181
182    /// Parent session ID (for delegated child sessions)
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub parent_id: Option<String>,
185
186    /// Multi-tenant identifier. The framework only transports this string;
187    /// the host (e.g. 书安OS) decides what "tenant" means and how to
188    /// aggregate/bill on it.
189    #[serde(default, skip_serializing_if = "Option::is_none")]
190    pub tenant_id: Option<String>,
191
192    /// Identity of the principal that triggered this session (user id,
193    /// service account, etc). Framework treats as opaque; emitted to
194    /// hooks/traces for accounting and audit.
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub principal: Option<String>,
197
198    /// Logical identifier of the agent template / definition the session
199    /// was instantiated from. Lets the host aggregate sessions by
200    /// "which agent recipe" independent of the concrete session id.
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub agent_template_id: Option<String>,
203
204    /// Distributed-trace correlation id. Propagated through hooks/traces
205    /// so a session's events can be joined with upstream/downstream work
206    /// in the host's observability pipeline.
207    #[serde(default, skip_serializing_if = "Option::is_none")]
208    pub correlation_id: Option<String>,
209}
210
211/// Serializable LLM configuration
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct LlmConfigData {
214    pub provider: String,
215    pub model: String,
216    /// API key is NOT stored - must be provided on session resume
217    #[serde(skip_serializing, default)]
218    pub api_key: Option<String>,
219    pub base_url: Option<String>,
220}
221
222impl SessionData {
223    /// Extract tool names from definitions
224    pub fn tool_names_from_definitions(tools: &[ToolDefinition]) -> Vec<String> {
225        tools.iter().map(|t| t.name.clone()).collect()
226    }
227}