rsclaw 0.0.1-alpha.1

rsclaw: High-performance AI agent (BETA). Optimized for M4 Max and 2GB VPS. 100% compatible with openclaw
Documentation
use super::context::AgentContext;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};

/// Session state.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SessionState {
    Active,
    Idle,
    Expired,
}

/// A session binds an agent context with metadata.
#[derive(Debug)]
pub struct Session {
    pub id: Arc<str>,
    pub agent_name: Arc<str>,
    pub context: AgentContext,
    pub state: SessionState,
    pub created_at: Instant,
    pub last_active: Instant,
    pub timeout: Duration,
}

impl Session {
    /// Create a new session.
    pub fn new(
        id: Arc<str>,
        agent_name: Arc<str>,
        context: AgentContext,
        timeout: Duration,
    ) -> Self {
        let now = Instant::now();
        Self {
            id,
            agent_name,
            context,
            state: SessionState::Active,
            created_at: now,
            last_active: now,
            timeout,
        }
    }

    /// Check if session is expired.
    pub fn is_expired(&self) -> bool {
        self.last_active.elapsed() > self.timeout
    }

    /// Touch session to reset timeout.
    pub fn touch(&mut self) {
        self.last_active = Instant::now();
        self.state = SessionState::Active;
    }

    /// Mark session as idle.
    pub fn mark_idle(&mut self) {
        self.state = SessionState::Idle;
    }
}

/// Session manager.
pub struct SessionManager {
    sessions: HashMap<Arc<str>, Session>,
    default_timeout: Duration,
}

impl SessionManager {
    /// Create a new session manager.
    pub fn new(timeout_minutes: u32) -> Self {
        Self {
            sessions: HashMap::new(),
            default_timeout: Duration::from_secs(timeout_minutes as u64 * 60),
        }
    }

    /// Create a new session.
    pub fn create(&mut self, agent_name: Arc<str>, context: AgentContext) -> Arc<str> {
        let id: Arc<str> = Arc::from(format!("session_{}", uuid::Uuid::new_v4().simple()));
        let session = Session::new(id.clone(), agent_name, context, self.default_timeout);
        self.sessions.insert(id.clone(), session);
        id
    }

    /// Get a session by id.
    pub fn get(&self, id: &str) -> Option<&Session> {
        self.sessions.get(id)
    }

    /// Get a mutable session by id.
    pub fn get_mut(&mut self, id: &str) -> Option<&mut Session> {
        self.sessions.get_mut(id)
    }

    /// Get session by agent name.
    pub fn get_by_agent(&self, agent_name: &str) -> Option<&Session> {
        self.sessions
            .values()
            .find(|s| s.agent_name.as_ref() == agent_name)
    }

    /// Get mutable session by agent name.
    pub fn get_by_agent_mut(&mut self, agent_name: &str) -> Option<&mut Session> {
        self.sessions
            .values_mut()
            .find(|s| s.agent_name.as_ref() == agent_name)
    }

    /// Delete a session.
    pub fn delete(&mut self, id: &str) -> bool {
        self.sessions.remove(id).is_some()
    }

    /// Delete session by agent name.
    pub fn delete_by_agent(&mut self, agent_name: &str) -> bool {
        let id = self
            .sessions
            .iter()
            .find(|(_, s)| s.agent_name.as_ref() == agent_name)
            .map(|(id, _)| id.clone());

        if let Some(id) = id {
            self.sessions.remove(&id).is_some()
        } else {
            false
        }
    }

    /// List all sessions.
    pub fn list(&self) -> Vec<&Session> {
        self.sessions.values().collect()
    }

    /// Cleanup expired sessions.
    pub fn cleanup_expired(&mut self) -> usize {
        let expired: Vec<Arc<str>> = self
            .sessions
            .iter()
            .filter(|(_, s)| s.is_expired())
            .map(|(id, _)| id.clone())
            .collect();

        let count = expired.len();
        for id in expired {
            self.sessions.remove(&id);
        }
        count
    }

    /// Get session count.
    pub fn count(&self) -> usize {
        self.sessions.len()
    }
}

impl Default for SessionManager {
    fn default() -> Self {
        Self::new(30)
    }
}