use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use crate::capability::CapabilitySet;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(pub String);
impl AgentId {
pub fn new() -> Self {
Self(Ulid::new().to_string())
}
pub fn named(name: &str) -> Self {
Self(format!("{}-{}", name, Ulid::new()))
}
}
impl Default for AgentId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for AgentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentInfo {
pub id: AgentId,
pub name: String,
pub kind: AgentKind,
pub state: AgentState,
pub capabilities: CapabilitySet,
pub resource_limits: ResourceLimits,
pub metrics: AgentMetrics,
pub spawned_at: DateTime<Utc>,
}
impl AgentInfo {
pub fn new(name: impl Into<String>, kind: AgentKind) -> Self {
Self {
id: AgentId::new(),
name: name.into(),
kind,
state: AgentState::Spawning,
capabilities: CapabilitySet::default(),
resource_limits: ResourceLimits::default(),
metrics: AgentMetrics::default(),
spawned_at: Utc::now(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AgentKind {
AcpProcess { binary: String, args: Vec<String> },
Ollama {
base_url: String,
model: String,
api_key_env: Option<String>,
},
LlamaCpp {
base_url: String,
model: String,
api_key_env: Option<String>,
},
Mlx {
base_url: String,
model: String,
api_key_env: Option<String>,
},
WasmPlugin { module_path: String },
BuiltIn,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AgentState {
Spawning,
Ready,
Busy { current_action: String },
Suspended,
Dead { reason: DeathReason },
}
impl AgentState {
pub fn is_alive(&self) -> bool {
!matches!(self, AgentState::Dead { .. })
}
pub fn is_available(&self) -> bool {
matches!(self, AgentState::Ready)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeathReason {
Completed,
Killed,
Crashed { exit_code: Option<i32> },
ResourceLimitExceeded,
Timeout,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceLimits {
pub max_memory_bytes: Option<u64>,
pub max_cpu_time_secs: Option<u64>,
pub max_file_writes: Option<u32>,
pub max_network_calls: Option<u32>,
pub max_tokens: Option<u64>,
pub timeout_secs: Option<u64>,
}
impl Default for ResourceLimits {
fn default() -> Self {
Self {
max_memory_bytes: Some(512 * 1024 * 1024), max_cpu_time_secs: Some(300), max_file_writes: None,
max_network_calls: None,
max_tokens: Some(100_000),
timeout_secs: Some(600), }
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentMetrics {
pub tokens_used: u64,
pub actions_taken: u32,
pub file_writes: u32,
pub network_calls: u32,
pub approvals_requested: u32,
pub approvals_granted: u32,
pub approvals_denied: u32,
}