Skip to main content

a3s_code_core/store/
mod.rs

1//! Session persistence layer
2//!
3//! Provides pluggable session storage via the `SessionStore` trait.
4//!
5//! ## Default Implementation
6//!
7//! `FileSessionStore` stores each session as a JSON file:
8//! - Session metadata (id, name, timestamps)
9//! - Configuration (system prompt, policies)
10//! - Conversation history (messages)
11//! - Context usage statistics
12//!
13//! ## Custom Backends
14//!
15//! Implement `SessionStore` trait for custom backends (Redis, PostgreSQL, etc.):
16//!
17//! ```ignore
18//! use a3s_code::store::{SessionStore, SessionData};
19//!
20//! struct RedisStore { /* ... */ }
21//!
22//! #[async_trait::async_trait]
23//! impl SessionStore for RedisStore {
24//!     async fn save(&self, session: &SessionData) -> Result<()> { /* ... */ }
25//!     async fn load(&self, id: &str) -> Result<Option<SessionData>> { /* ... */ }
26//!     async fn delete(&self, id: &str) -> Result<()> { /* ... */ }
27//!     async fn list(&self) -> Result<Vec<String>> { /* ... */ }
28//!     async fn exists(&self, id: &str) -> Result<bool> { /* ... */ }
29//! }
30//! ```
31
32mod file_store;
33mod memory_store;
34mod session_data;
35
36#[cfg(test)]
37mod tests;
38
39pub use file_store::FileSessionStore;
40pub use memory_store::MemorySessionStore;
41pub use session_data::{
42    ContextUsage, LlmConfigData, SessionConfig, SessionData, SessionState,
43    DEFAULT_AUTO_COMPACT_THRESHOLD,
44};
45
46use crate::loop_checkpoint::LoopCheckpoint;
47use crate::run::RunRecord;
48use crate::subagent_task_tracker::SubagentTaskSnapshot;
49use crate::tools::ArtifactStore;
50use crate::trace::TraceEvent;
51use crate::verification::VerificationReport;
52use anyhow::Result;
53
54// ============================================================================
55// Session Store Trait
56// ============================================================================
57
58/// Session storage trait
59#[async_trait::async_trait]
60pub trait SessionStore: Send + Sync {
61    /// Save session data
62    async fn save(&self, session: &SessionData) -> Result<()>;
63
64    /// Load session data by ID
65    async fn load(&self, id: &str) -> Result<Option<SessionData>>;
66
67    /// Delete session data
68    async fn delete(&self, id: &str) -> Result<()>;
69
70    /// List all session IDs
71    async fn list(&self) -> Result<Vec<String>>;
72
73    /// Check if session exists
74    async fn exists(&self, id: &str) -> Result<bool>;
75
76    /// Save artifacts associated with a session.
77    async fn save_artifacts(&self, _id: &str, _artifacts: &ArtifactStore) -> Result<()> {
78        Ok(())
79    }
80
81    /// Load artifacts associated with a session.
82    async fn load_artifacts(&self, _id: &str) -> Result<Option<ArtifactStore>> {
83        Ok(None)
84    }
85
86    /// Save compact trace events associated with a session.
87    async fn save_trace_events(&self, _id: &str, _events: &[TraceEvent]) -> Result<()> {
88        Ok(())
89    }
90
91    /// Load compact trace events associated with a session.
92    async fn load_trace_events(&self, _id: &str) -> Result<Option<Vec<TraceEvent>>> {
93        Ok(None)
94    }
95
96    /// Save run snapshots and replayable runtime events associated with a session.
97    async fn save_run_records(&self, _id: &str, _records: &[RunRecord]) -> Result<()> {
98        Ok(())
99    }
100
101    /// Load run snapshots and replayable runtime events associated with a session.
102    async fn load_run_records(&self, _id: &str) -> Result<Option<Vec<RunRecord>>> {
103        Ok(None)
104    }
105
106    /// Save structured verification reports associated with a session.
107    async fn save_verification_reports(
108        &self,
109        _id: &str,
110        _reports: &[VerificationReport],
111    ) -> Result<()> {
112        Ok(())
113    }
114
115    /// Load structured verification reports associated with a session.
116    async fn load_verification_reports(
117        &self,
118        _id: &str,
119    ) -> Result<Option<Vec<VerificationReport>>> {
120        Ok(None)
121    }
122
123    /// Save the session's delegated subagent task tracker snapshots.
124    ///
125    /// Cluster-grade hosts need this so a migrated session keeps a
126    /// queryable history of its delegated child runs. Cancellers are
127    /// **not** persisted — they are runtime-only and re-attaching them
128    /// is the executor's job at task respawn time.
129    async fn save_subagent_tasks(&self, _id: &str, _tasks: &[SubagentTaskSnapshot]) -> Result<()> {
130        Ok(())
131    }
132
133    /// Load the session's delegated subagent task tracker snapshots.
134    async fn load_subagent_tasks(&self, _id: &str) -> Result<Option<Vec<SubagentTaskSnapshot>>> {
135        Ok(None)
136    }
137
138    /// Save the latest per-tool-round loop checkpoint for `run_id`.
139    ///
140    /// The agent loop calls this through the
141    /// [`SessionStoreCheckpointSink`](crate::loop_checkpoint::SessionStoreCheckpointSink)
142    /// adapter after each completed tool round. Implementations should
143    /// **overwrite** any earlier checkpoint for the same `run_id` — the
144    /// loop only ever needs the most recent boundary.
145    async fn save_loop_checkpoint(
146        &self,
147        _run_id: &str,
148        _checkpoint: &LoopCheckpoint,
149    ) -> Result<()> {
150        Ok(())
151    }
152
153    /// Load the latest loop checkpoint for `run_id`.
154    async fn load_loop_checkpoint(&self, _run_id: &str) -> Result<Option<LoopCheckpoint>> {
155        Ok(None)
156    }
157
158    /// Delete the loop checkpoint for `run_id`, if present.
159    ///
160    /// Called by the run lifecycle when a run reaches a terminal state
161    /// **in-process** (completed, failed, or cancelled) — at that point
162    /// the checkpoint is dead weight. Only a process crash (the agent
163    /// loop never returns) should leave a checkpoint behind for
164    /// crash-recovery resume. Without this, every tool-using run would
165    /// leak a checkpoint forever — the dominant unbounded-growth source
166    /// for long-running cluster deployments.
167    ///
168    /// Deleting a non-existent checkpoint is a no-op success.
169    async fn delete_loop_checkpoint(&self, _run_id: &str) -> Result<()> {
170        Ok(())
171    }
172
173    /// Health check — verify the store backend is reachable and operational
174    async fn health_check(&self) -> Result<()> {
175        Ok(())
176    }
177
178    /// Backend name for diagnostics
179    fn backend_name(&self) -> &str {
180        "unknown"
181    }
182}