oxicode_sdk/lifecycle/
mod.rs1mod agent_pool;
15pub mod hub;
16mod snapshot;
17mod subagent_coordinator;
18mod supervisor;
19
20pub use agent_pool::AgentPool;
22pub use hub::{HubKind, HubStatus};
23pub use snapshot::{AgentSnapshot, FileSnapshotStore, SnapshotStore, ToolManifest};
24pub use subagent_coordinator::{
25 DEFAULT_MAX_SUBAGENT_DEPTH, SubagentCoordinator, SubagentCoordinatorError,
26 SubagentSpawnRequest, SubagentState, SubagentTracker,
27};
28pub use supervisor::{AgentHandle, AgentSupervisor, RestartBackoff, SupervisorPolicy};
29
30use serde::{Deserialize, Serialize};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum AgentStatus {
38 #[default]
40 Created,
41 Running,
43 Suspended,
45 Terminated,
47 Failed,
49}
50
51impl std::fmt::Display for AgentStatus {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 Self::Created => write!(f, "created"),
55 Self::Running => write!(f, "running"),
56 Self::Suspended => write!(f, "suspended"),
57 Self::Terminated => write!(f, "terminated"),
58 Self::Failed => write!(f, "failed"),
59 }
60 }
61}
62
63impl AgentStatus {
64 pub fn is_terminal(&self) -> bool {
66 matches!(self, Self::Terminated | Self::Failed)
67 }
68
69 pub fn is_runnable(&self) -> bool {
71 matches!(self, Self::Created | Self::Suspended)
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
79#[serde(tag = "type", rename_all = "snake_case")]
80#[non_exhaustive]
81pub enum AgentLifecycleEvent {
82 Spawned {
84 agent_id: String,
86 parent_id: Option<String>,
88 model_id: String,
90 timestamp_ms: u64,
92 },
93 RunStart {
95 agent_id: String,
97 timestamp_ms: u64,
99 },
100 RunEnd {
102 agent_id: String,
104 timestamp_ms: u64,
106 success: bool,
108 },
109 Suspended {
111 agent_id: String,
113 snapshot: Box<AgentSnapshot>,
115 timestamp_ms: u64,
117 },
118 Resumed {
120 agent_id: String,
122 from_snapshot_id: Option<String>,
124 timestamp_ms: u64,
126 },
127 Terminated {
129 agent_id: String,
131 timestamp_ms: u64,
133 },
134 ModelSwitched {
136 agent_id: String,
138 from_model: String,
140 to_model: String,
142 timestamp_ms: u64,
144 },
145}
146
147impl AgentLifecycleEvent {
148 pub fn now_ms() -> u64 {
150 std::time::SystemTime::now()
151 .duration_since(std::time::UNIX_EPOCH)
152 .map(|d| d.as_millis() as u64)
153 .unwrap_or(0)
154 }
155}
156
157pub use crate::metrics::MetricsSnapshot;
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn agent_status_display() {
166 assert_eq!(AgentStatus::Running.to_string(), "running");
167 assert_eq!(AgentStatus::Suspended.to_string(), "suspended");
168 assert_eq!(AgentStatus::Failed.to_string(), "failed");
169 }
170
171 #[test]
172 fn agent_status_terminal() {
173 assert!(AgentStatus::Terminated.is_terminal());
174 assert!(AgentStatus::Failed.is_terminal());
175 assert!(!AgentStatus::Running.is_terminal());
176 assert!(!AgentStatus::Created.is_terminal());
177 }
178
179 #[test]
180 fn agent_status_runnable() {
181 assert!(AgentStatus::Created.is_runnable());
182 assert!(AgentStatus::Suspended.is_runnable());
183 assert!(!AgentStatus::Running.is_runnable());
184 assert!(!AgentStatus::Terminated.is_runnable());
185 }
186
187 #[test]
188 fn lifecycle_event_now_ms() {
189 let ms = AgentLifecycleEvent::now_ms();
190 assert!(ms > 1_700_000_000_000); }
192
193 #[test]
194 fn metrics_snapshot_default() {
195 let m = MetricsSnapshot::default();
196 assert_eq!(m.total_runs, 0);
197 }
198}