pub mod transport;
use crate::event::AgentEvent;
use crate::types::{RunResult, SessionId, Usage};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::SystemTime;
use tokio::sync::mpsc;
#[derive(Debug, thiserror::Error)]
pub enum SessionError {
#[error("session not found: {id}")]
NotFound { id: SessionId },
#[error("session is busy: {id}")]
Busy { id: SessionId },
#[error("session persistence is disabled")]
PersistenceDisabled,
#[error("session compaction is disabled")]
CompactionDisabled,
#[error("no turn running on session: {id}")]
NotRunning { id: SessionId },
#[error("store error: {0}")]
Store(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("agent error: {0}")]
Agent(#[from] crate::error::AgentError),
}
impl SessionError {
pub fn code(&self) -> &'static str {
match self {
Self::NotFound { .. } => "SESSION_NOT_FOUND",
Self::Busy { .. } => "SESSION_BUSY",
Self::PersistenceDisabled => "SESSION_PERSISTENCE_DISABLED",
Self::CompactionDisabled => "SESSION_COMPACTION_DISABLED",
Self::NotRunning { .. } => "SESSION_NOT_RUNNING",
Self::Store(_) => "SESSION_STORE_ERROR",
Self::Agent(_) => "AGENT_ERROR",
}
}
}
#[derive(Debug)]
pub struct CreateSessionRequest {
pub model: String,
pub prompt: String,
pub system_prompt: Option<String>,
pub max_tokens: Option<u32>,
pub event_tx: Option<mpsc::Sender<AgentEvent>>,
pub host_mode: bool,
pub skill_references: Option<Vec<crate::skills::SkillId>>,
}
#[derive(Debug)]
pub struct StartTurnRequest {
pub prompt: String,
pub event_tx: Option<mpsc::Sender<AgentEvent>>,
pub host_mode: bool,
pub skill_references: Option<Vec<crate::skills::SkillId>>,
}
#[derive(Debug, Default)]
pub struct SessionQuery {
pub limit: Option<usize>,
pub offset: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionSummary {
pub session_id: SessionId,
pub created_at: SystemTime,
pub updated_at: SystemTime,
pub message_count: usize,
pub total_tokens: u64,
pub is_active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionInfo {
pub session_id: SessionId,
pub created_at: SystemTime,
pub updated_at: SystemTime,
pub message_count: usize,
pub is_active: bool,
pub last_assistant_text: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionUsage {
pub total_tokens: u64,
pub usage: Usage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionView {
pub state: SessionInfo,
pub billing: SessionUsage,
}
impl SessionView {
pub fn session_id(&self) -> &SessionId {
&self.state.session_id
}
}
#[async_trait]
pub trait SessionService: Send + Sync {
async fn create_session(&self, req: CreateSessionRequest) -> Result<RunResult, SessionError>;
async fn start_turn(
&self,
id: &SessionId,
req: StartTurnRequest,
) -> Result<RunResult, SessionError>;
async fn interrupt(&self, id: &SessionId) -> Result<(), SessionError>;
async fn read(&self, id: &SessionId) -> Result<SessionView, SessionError>;
async fn list(&self, query: SessionQuery) -> Result<Vec<SessionSummary>, SessionError>;
async fn archive(&self, id: &SessionId) -> Result<(), SessionError>;
}
impl dyn SessionService {
pub fn into_arc(self: Box<Self>) -> Arc<dyn SessionService> {
Arc::from(self)
}
}