use super::database::Store;
use crate::agent::AgentContext;
use crate::provider::ChatMessage;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionData {
pub id: Arc<str>,
pub agent_name: Arc<str>,
pub messages: Vec<ChatMessageData>,
pub created_at: u64,
pub last_active: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessageData {
pub role: Arc<str>,
pub content: Arc<str>,
}
pub struct SessionStore {
store: Arc<Store>,
}
impl SessionStore {
pub fn new(store: Arc<Store>) -> Self {
Self { store }
}
pub fn save(&self, session_id: &str, agent_name: &str, messages: &[ChatMessage]) -> Result<()> {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs();
let message_data: Vec<ChatMessageData> = messages
.iter()
.map(|m| ChatMessageData {
role: Arc::from(format!("{:?}", m.role).to_lowercase()),
content: Arc::from(m.content.as_str()),
})
.collect();
let session_data = SessionData {
id: Arc::from(session_id),
agent_name: Arc::from(agent_name),
messages: message_data,
created_at: now,
last_active: now,
};
let serialized = serde_json::to_vec(&session_data)?;
self.store.save_session(session_id, &serialized)?;
tracing::info!("Session '{}' saved to disk", session_id);
Ok(())
}
pub fn load(&self, session_id: &str) -> Result<Option<SessionData>> {
match self.store.get_session(session_id)? {
Some(data) => {
let session: SessionData = serde_json::from_slice(&data)?;
Ok(Some(session))
}
None => Ok(None),
}
}
pub fn delete(&self, session_id: &str) -> Result<bool> {
self.store.delete_session(session_id)
}
pub fn list(&self) -> Result<Vec<String>> {
self.store.list_sessions()
}
pub fn cleanup_expired(&self, max_age: Duration) -> Result<usize> {
let sessions = self.list()?;
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs();
let max_age_secs = max_age.as_secs();
let mut cleaned = 0;
for session_id in sessions {
if let Some(session) = self.load(&session_id)? {
if now - session.last_active > max_age_secs {
self.delete(&session_id)?;
cleaned += 1;
}
}
}
if cleaned > 0 {
tracing::info!("Cleaned up {} expired sessions", cleaned);
}
Ok(cleaned)
}
}