1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use ulid::Ulid;
4
5use crate::capability::CapabilitySet;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct AgentId(pub String);
10
11impl AgentId {
12 pub fn new() -> Self {
13 Self(Ulid::new().to_string())
14 }
15
16 pub fn named(name: &str) -> Self {
17 Self(format!("{}-{}", name, Ulid::new()))
18 }
19}
20
21impl Default for AgentId {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl std::fmt::Display for AgentId {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{}", self.0)
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct AgentInfo {
38 pub id: AgentId,
39 pub name: String,
40 pub kind: AgentKind,
41 pub state: AgentState,
42 pub capabilities: CapabilitySet,
43 pub resource_limits: ResourceLimits,
44 pub metrics: AgentMetrics,
45 pub spawned_at: DateTime<Utc>,
46}
47
48impl AgentInfo {
49 pub fn new(name: impl Into<String>, kind: AgentKind) -> Self {
50 Self {
51 id: AgentId::new(),
52 name: name.into(),
53 kind,
54 state: AgentState::Spawning,
55 capabilities: CapabilitySet::default(),
56 resource_limits: ResourceLimits::default(),
57 metrics: AgentMetrics::default(),
58 spawned_at: Utc::now(),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65pub enum AgentKind {
66 AcpProcess { binary: String, args: Vec<String> },
69 Ollama {
72 base_url: String,
73 model: String,
74 api_key_env: Option<String>,
76 },
77 LlamaCpp {
80 base_url: String,
81 model: String,
82 api_key_env: Option<String>,
84 },
85 Mlx {
88 base_url: String,
89 model: String,
90 api_key_env: Option<String>,
91 },
92 WasmPlugin { module_path: String },
94 BuiltIn,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100pub enum AgentState {
101 Spawning,
102 Ready,
103 Busy { current_action: String },
104 Suspended,
105 Dead { reason: DeathReason },
106}
107
108impl AgentState {
109 pub fn is_alive(&self) -> bool {
110 !matches!(self, AgentState::Dead { .. })
111 }
112
113 pub fn is_available(&self) -> bool {
114 matches!(self, AgentState::Ready)
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119pub enum DeathReason {
120 Completed,
121 Killed,
122 Crashed { exit_code: Option<i32> },
123 ResourceLimitExceeded,
124 Timeout,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct ResourceLimits {
130 pub max_memory_bytes: Option<u64>,
131 pub max_cpu_time_secs: Option<u64>,
132 pub max_file_writes: Option<u32>,
133 pub max_network_calls: Option<u32>,
134 pub max_tokens: Option<u64>,
135 pub timeout_secs: Option<u64>,
136}
137
138impl Default for ResourceLimits {
139 fn default() -> Self {
140 Self {
141 max_memory_bytes: Some(512 * 1024 * 1024), max_cpu_time_secs: Some(300), max_file_writes: None,
144 max_network_calls: None,
145 max_tokens: Some(100_000),
146 timeout_secs: Some(600), }
148 }
149}
150
151#[derive(Debug, Clone, Default, Serialize, Deserialize)]
153pub struct AgentMetrics {
154 pub tokens_used: u64,
155 pub actions_taken: u32,
156 pub file_writes: u32,
157 pub network_calls: u32,
158 pub approvals_requested: u32,
159 pub approvals_granted: u32,
160 pub approvals_denied: u32,
161}