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
49fn is_false(value: &bool) -> bool {
50    !*value
51}
52
53/// Serializable session configuration.
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct SessionConfig {
56    pub name: String,
57    pub workspace: String,
58    pub system_prompt: Option<String>,
59    pub max_context_length: u32,
60    pub auto_compact: bool,
61    /// Context usage percentage threshold to trigger auto-compaction (0.0 - 1.0).
62    /// Only used when `auto_compact` is true. Default: 0.80 (80%).
63    #[serde(default = "default_auto_compact_threshold")]
64    pub auto_compact_threshold: f32,
65    /// Storage type for this session.
66    #[serde(default)]
67    pub storage_type: crate::config::StorageBackend,
68    /// Optional advanced queue configuration.
69    ///
70    /// Queue infrastructure is initialized only when this is set. Ordinary
71    /// sessions stay queue-free.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub queue_config: Option<SessionQueueConfig>,
74    /// Confirmation policy (optional, uses defaults if None).
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub confirmation_policy: Option<crate::hitl::ConfirmationPolicy>,
77    /// Permission policy (optional, uses defaults if None).
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub permission_policy: Option<crate::permissions::PermissionPolicy>,
80    /// Whether active skill `allowed-tools` restrict ordinary session tools.
81    #[serde(default, skip_serializing_if = "is_false")]
82    pub enforce_active_skill_tool_restrictions: bool,
83    /// Maximum sibling branches/tools to run concurrently.
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub max_parallel_tasks: Option<usize>,
86    /// Automatic subagent delegation settings.
87    #[serde(default, skip_serializing_if = "Option::is_none")]
88    pub auto_delegation: Option<crate::config::AutoDelegationConfig>,
89    /// Parent session ID (for delegated child sessions).
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub parent_id: Option<String>,
92    /// Security configuration (optional, enables security features).
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub security_config: Option<crate::security::SecurityConfig>,
95    /// Shared hook engine for lifecycle events.
96    #[serde(skip)]
97    pub hook_engine: Option<std::sync::Arc<dyn crate::hooks::HookExecutor>>,
98    /// Enable planning phase before execution.
99    #[serde(default)]
100    pub planning_mode: PlanningMode,
101    /// Enable goal tracking.
102    #[serde(default)]
103    pub goal_tracking: bool,
104    /// Exact model-facing Tool presentation profile pinned by the host.
105    #[serde(default)]
106    pub tool_presentation_profile: crate::tools::ToolPresentationProfileV1,
107    /// Exact deterministic Tool-result projection policy pinned by the host.
108    #[serde(default)]
109    pub tool_result_transform_policy: crate::tools::ToolResultTransformPolicyV1,
110}
111
112impl Default for SessionConfig {
113    fn default() -> Self {
114        Self {
115            name: String::new(),
116            workspace: String::new(),
117            system_prompt: None,
118            max_context_length: 0,
119            auto_compact: false,
120            auto_compact_threshold: DEFAULT_AUTO_COMPACT_THRESHOLD,
121            storage_type: crate::config::StorageBackend::default(),
122            queue_config: None,
123            confirmation_policy: None,
124            permission_policy: None,
125            enforce_active_skill_tool_restrictions: false,
126            max_parallel_tasks: None,
127            auto_delegation: None,
128            parent_id: None,
129            security_config: None,
130            hook_engine: None,
131            planning_mode: PlanningMode::default(),
132            goal_tracking: false,
133            tool_presentation_profile: crate::tools::ToolPresentationProfileV1::default(),
134            tool_result_transform_policy: crate::tools::ToolResultTransformPolicyV1::default(),
135        }
136    }
137}
138
139/// Serializable session data for persistence
140///
141/// Contains only the fields that can be serialized.
142/// Non-serializable fields (event_tx, command_queue, etc.) are rebuilt on load.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct SessionData {
145    /// Session ID
146    pub id: String,
147
148    /// Session configuration
149    pub config: SessionConfig,
150
151    /// Current state
152    pub state: SessionState,
153
154    /// Conversation history
155    pub messages: Vec<Message>,
156
157    /// Context usage statistics
158    pub context_usage: ContextUsage,
159
160    /// Total token usage
161    pub total_usage: TokenUsage,
162
163    /// Cumulative dollar cost for this session
164    #[serde(default)]
165    pub total_cost: f64,
166
167    /// Model name for cost calculation
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub model_name: Option<String>,
170
171    /// LLM cost records for this session
172    #[serde(default)]
173    pub cost_records: Vec<crate::telemetry::LlmCostRecord>,
174
175    /// Tool definitions (names only, rebuilt from executor on load)
176    pub tool_names: Vec<String>,
177
178    /// Whether thinking mode is enabled
179    pub thinking_enabled: bool,
180
181    /// Thinking budget if set
182    pub thinking_budget: Option<usize>,
183
184    /// Creation timestamp (Unix epoch seconds)
185    pub created_at: i64,
186
187    /// Last update timestamp (Unix epoch seconds)
188    pub updated_at: i64,
189
190    /// LLM configuration for per-session client (if set)
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub llm_config: Option<LlmConfigData>,
193
194    /// Task list for tracking
195    #[serde(default, alias = "todos")]
196    pub tasks: Vec<Task>,
197
198    /// Parent session ID (for delegated child sessions)
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub parent_id: Option<String>,
201
202    /// Multi-tenant identifier. The framework only transports this string;
203    /// the host decides what "tenant" means and how to
204    /// aggregate/bill on it.
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub tenant_id: Option<String>,
207
208    /// Identity of the principal that triggered this session (user id,
209    /// service account, etc). Framework treats as opaque; emitted to
210    /// hooks/traces for accounting and audit.
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    pub principal: Option<String>,
213
214    /// Logical identifier of the agent template / definition the session
215    /// was instantiated from. Lets the host aggregate sessions by
216    /// "which agent recipe" independent of the concrete session id.
217    #[serde(default, skip_serializing_if = "Option::is_none")]
218    pub agent_template_id: Option<String>,
219
220    /// Distributed-trace correlation id. Propagated through hooks/traces
221    /// so a session's events can be joined with upstream/downstream work
222    /// in the host's observability pipeline.
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub correlation_id: Option<String>,
225
226    /// Exact A3S Use cognitive-package generation visible to the next Run.
227    ///
228    /// The provider/lease is intentionally not serialized. A resume host must
229    /// inject a provider with this byte-equivalent binding; Code never resolves
230    /// a replacement or `latest` generation. Historical Runs retain their own
231    /// exact binding on [`crate::run::RunSnapshot`].
232    #[serde(default, skip_serializing_if = "Option::is_none")]
233    pub cognitive_package_binding: Option<crate::cognitive_context::CognitivePackageBindingV1>,
234
235    /// Secret-free identity of the host immutable-content authority.
236    ///
237    /// The non-serializable adapter is re-injected by the host on resume and
238    /// must carry this exact binding.
239    #[serde(default, skip_serializing_if = "Option::is_none")]
240    pub immutable_content_adapter_binding: Option<crate::tools::ImmutableContentAdapterBindingV1>,
241}
242
243/// Serializable LLM configuration
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct LlmConfigData {
246    pub provider: String,
247    pub model: String,
248    /// API key is NOT stored - must be provided on session resume
249    #[serde(skip_serializing, default)]
250    pub api_key: Option<String>,
251    pub base_url: Option<String>,
252}
253
254impl SessionData {
255    /// Extract tool names from definitions
256    pub fn tool_names_from_definitions(tools: &[ToolDefinition]) -> Vec<String> {
257        tools.iter().map(|t| t.name.clone()).collect()
258    }
259}