#![allow(async_fn_in_trait)]
use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use crate::agent::{Task, TaskResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SessionStatus {
Initializing,
Active,
Idle,
Busy,
Error,
Terminating,
Terminated,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionInfo {
pub id: String,
pub session_type: String,
pub status: SessionStatus,
pub created_at: DateTime<Utc>,
pub last_activity: DateTime<Utc>,
pub tasks_completed: usize,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EfficiencyStats {
pub total_tasks: usize,
pub successful_tasks: usize,
pub failed_tasks: usize,
pub total_duration: Duration,
pub average_task_duration: Duration,
pub token_savings_percentage: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionConfig {
pub agent_id: String,
pub working_directory: Option<String>,
pub env_vars: HashMap<String, String>,
pub auto_cleanup: bool,
pub idle_timeout: Option<Duration>,
}
pub trait SessionLifecycle: Send + Sync {
async fn initialize(&mut self) -> Result<()>;
async fn shutdown(&mut self) -> Result<()>;
fn get_status(&self) -> SessionStatus;
fn update_status(&mut self, status: SessionStatus);
fn is_ready(&self) -> bool {
matches!(
self.get_status(),
SessionStatus::Active | SessionStatus::Idle
)
}
fn is_terminated(&self) -> bool {
matches!(self.get_status(), SessionStatus::Terminated)
}
}
#[async_trait::async_trait]
pub trait TaskExecutor: Send + Sync {
async fn execute_task(&mut self, task: Task) -> Result<TaskResult>;
async fn execute_task_batch(&mut self, tasks: Vec<Task>) -> Result<Vec<TaskResult>> {
let mut results = Vec::new();
for task in tasks {
results.push(self.execute_task(task).await?);
}
Ok(results)
}
fn can_handle_task(&self, task: &Task) -> bool {
let _ = task;
true
}
}
pub trait SessionMetadata: Send + Sync {
fn get_id(&self) -> &str;
fn get_created_at(&self) -> DateTime<Utc>;
fn get_last_activity(&self) -> DateTime<Utc>;
fn touch(&mut self);
fn get_session_type(&self) -> &str;
}
pub trait SessionEnvironment: Send + Sync {
fn get_working_directory(&self) -> &Path;
fn get_env_vars(&self) -> &HashMap<String, String>;
fn create_env_vars(agent_id: &str, session_id: &str) -> HashMap<String, String> {
let mut env_vars = crate::utils::common::collections::new_hashmap();
env_vars.insert("CCSWARM_AGENT_ID".to_string(), agent_id.to_string());
env_vars.insert("CCSWARM_SESSION_ID".to_string(), session_id.to_string());
env_vars.insert("CCSWARM_SESSION_TYPE".to_string(), "generic".to_string());
env_vars
}
}
pub trait SessionStatistics: Send + Sync {
fn get_tasks_completed(&self) -> usize;
fn get_efficiency_stats(&self) -> EfficiencyStats;
fn update_task_stats(&mut self, result: &TaskResult, duration: Duration);
fn reset_stats(&mut self);
}
pub trait Session:
SessionLifecycle + TaskExecutor + SessionMetadata + SessionEnvironment + SessionStatistics
{
}
#[async_trait::async_trait]
pub trait SessionManager: Send + Sync {
type SessionType: Session + 'static;
async fn create_session(&self, config: SessionConfig) -> Result<Arc<Mutex<Self::SessionType>>>;
async fn get_session(&self, id: &str) -> Option<Arc<Mutex<Self::SessionType>>>;
async fn list_sessions(&self) -> Vec<SessionInfo>;
async fn cleanup_terminated(&self) -> Result<usize>;
async fn get_or_create_session(&self, agent_id: &str) -> Result<Arc<Mutex<Self::SessionType>>> {
let sessions = self.list_sessions().await;
for session_info in sessions {
if session_info.id.contains(agent_id)
&& matches!(
session_info.status,
SessionStatus::Active | SessionStatus::Idle
)
{
if let Some(session) = self.get_session(&session_info.id).await {
return Ok(session);
}
}
}
let config = SessionConfig {
agent_id: agent_id.to_string(),
working_directory: None,
env_vars: HashMap::new(),
auto_cleanup: true,
idle_timeout: Some(Duration::from_secs(300)),
};
self.create_session(config).await
}
}
pub trait PoolableSession: Session {
fn is_reusable(&self) -> bool;
async fn prepare_for_reuse(&mut self) -> Result<()>;
}
#[async_trait::async_trait]
pub trait PersistentSession: Session {
async fn save_state(&self) -> Result<()>;
async fn load_state(&mut self) -> Result<()>;
fn get_persistence_path(&self) -> &Path;
}
#[async_trait::async_trait]
pub trait CheckpointableSession: Session {
async fn checkpoint(
&self,
label: Option<String>,
) -> Result<super::checkpoint::SessionCheckpoint>;
async fn restore(&mut self, checkpoint: &super::checkpoint::SessionCheckpoint) -> Result<()>;
async fn list_checkpoints(&self) -> Result<Vec<super::checkpoint::SessionCheckpoint>>;
async fn delete_checkpoint(&self, checkpoint_id: &str) -> Result<()>;
async fn get_latest_checkpoint(&self) -> Result<Option<super::checkpoint::SessionCheckpoint>>;
}
#[async_trait::async_trait]
pub trait ForkableSession: CheckpointableSession {
async fn fork(&self, branch_name: Option<String>) -> Result<super::fork::ForkInfo>;
async fn fork_from_checkpoint(
&self,
checkpoint_id: &str,
branch_name: Option<String>,
) -> Result<super::fork::ForkInfo>;
async fn list_forks(&self) -> Result<Vec<super::fork::ForkInfo>>;
async fn abandon_fork(&self, fork_id: &str) -> Result<()>;
}
#[async_trait::async_trait]
pub trait CompactableSession: Session {
async fn compact(
&mut self,
config: &super::compaction::CompactionConfig,
) -> Result<super::compaction::CompactionResult>;
async fn needs_compaction(&self, threshold_tokens: usize) -> bool;
async fn get_token_count(&self) -> Result<usize>;
async fn get_context_size(&self) -> Result<usize>;
async fn clear_context(&mut self) -> Result<()>;
}