mod file_store;
mod memory_store;
mod session_data;
mod session_snapshot;
#[cfg(test)]
mod tests;
pub use file_store::FileSessionStore;
pub use memory_store::MemorySessionStore;
pub use session_data::{
ContextUsage, LlmConfigData, SessionConfig, SessionData, SessionState,
DEFAULT_AUTO_COMPACT_THRESHOLD,
};
pub use session_snapshot::{SessionSnapshotV1, SESSION_SNAPSHOT_SCHEMA_VERSION};
use crate::loop_checkpoint::LoopCheckpoint;
use crate::run::RunRecord;
use crate::subagent_task_tracker::SubagentTaskSnapshot;
use crate::tools::ArtifactStore;
use crate::trace::TraceEvent;
use crate::verification::VerificationReport;
use anyhow::{bail, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SessionStoreCapabilities {
pub atomic_session_snapshots: bool,
}
#[async_trait::async_trait]
pub trait SessionStore: Send + Sync {
async fn save(&self, session: &SessionData) -> Result<()>;
async fn load(&self, id: &str) -> Result<Option<SessionData>>;
async fn delete(&self, id: &str) -> Result<()>;
async fn list(&self) -> Result<Vec<String>>;
async fn exists(&self, id: &str) -> Result<bool>;
async fn save_snapshot(&self, _snapshot: &SessionSnapshotV1) -> Result<()> {
bail!(
"session store '{}' does not support aggregate session snapshots",
self.backend_name()
)
}
async fn load_snapshot(&self, id: &str) -> Result<Option<SessionSnapshotV1>> {
let Some(session) = self.load(id).await? else {
return Ok(None);
};
let artifacts = self.load_artifacts(id).await?.unwrap_or_default();
Ok(Some(SessionSnapshotV1::new(
session,
&artifacts,
self.load_trace_events(id).await?.unwrap_or_default(),
self.load_run_records(id).await?.unwrap_or_default(),
self.load_verification_reports(id)
.await?
.unwrap_or_default(),
self.load_subagent_tasks(id).await?.unwrap_or_default(),
)))
}
fn capabilities(&self) -> SessionStoreCapabilities {
SessionStoreCapabilities::default()
}
async fn save_artifacts(&self, _id: &str, artifacts: &ArtifactStore) -> Result<()> {
if !artifacts.is_empty() {
bail!(
"session store '{}' does not support artifacts",
self.backend_name()
);
}
Ok(())
}
async fn load_artifacts(&self, _id: &str) -> Result<Option<ArtifactStore>> {
Ok(None)
}
async fn save_trace_events(&self, _id: &str, events: &[TraceEvent]) -> Result<()> {
if !events.is_empty() {
bail!(
"session store '{}' does not support trace events",
self.backend_name()
);
}
Ok(())
}
async fn load_trace_events(&self, _id: &str) -> Result<Option<Vec<TraceEvent>>> {
Ok(None)
}
async fn save_run_records(&self, _id: &str, records: &[RunRecord]) -> Result<()> {
if !records.is_empty() {
bail!(
"session store '{}' does not support run records",
self.backend_name()
);
}
Ok(())
}
async fn load_run_records(&self, _id: &str) -> Result<Option<Vec<RunRecord>>> {
Ok(None)
}
async fn save_verification_reports(
&self,
_id: &str,
reports: &[VerificationReport],
) -> Result<()> {
if !reports.is_empty() {
bail!(
"session store '{}' does not support verification reports",
self.backend_name()
);
}
Ok(())
}
async fn load_verification_reports(
&self,
_id: &str,
) -> Result<Option<Vec<VerificationReport>>> {
Ok(None)
}
async fn save_subagent_tasks(&self, _id: &str, tasks: &[SubagentTaskSnapshot]) -> Result<()> {
if !tasks.is_empty() {
bail!(
"session store '{}' does not support subagent tasks",
self.backend_name()
);
}
Ok(())
}
async fn load_subagent_tasks(&self, _id: &str) -> Result<Option<Vec<SubagentTaskSnapshot>>> {
Ok(None)
}
async fn save_loop_checkpoint(
&self,
_run_id: &str,
_checkpoint: &LoopCheckpoint,
) -> Result<()> {
Ok(())
}
async fn load_loop_checkpoint(&self, _run_id: &str) -> Result<Option<LoopCheckpoint>> {
Ok(None)
}
async fn delete_loop_checkpoint(&self, _run_id: &str) -> Result<()> {
Ok(())
}
async fn save_workflow_checkpoint(
&self,
_workflow_id: &str,
_checkpoint: &crate::orchestration::WorkflowCheckpoint,
) -> Result<()> {
Ok(())
}
async fn load_workflow_checkpoint(
&self,
_workflow_id: &str,
) -> Result<Option<crate::orchestration::WorkflowCheckpoint>> {
Ok(None)
}
async fn delete_workflow_checkpoint(&self, _workflow_id: &str) -> Result<()> {
Ok(())
}
async fn health_check(&self) -> Result<()> {
Ok(())
}
fn backend_name(&self) -> &str {
"unknown"
}
}