1pub mod cache;
13pub mod collapse_controller;
14pub mod delegation;
15pub mod delegation_outcome;
16pub mod executor;
17pub mod k8s_result;
18pub mod kubernetes_executor;
19mod live_bus; pub mod orchestrator;
20pub mod rate_limiter;
21pub mod remote_subtask;
22pub mod result_store;
23pub mod speculative;
24pub mod subtask;
25pub mod token_exhaustion;
26pub mod token_truncate;
27mod tool_policy;
28pub mod validation;
29
30pub use cache::{CacheConfig, CacheStats, SwarmCache};
31pub use collapse_controller::{
32 BranchEvaluation, BranchObservation, BranchRuntimeState, CoherenceScore, CollapseController,
33 CollapsePolicy, CollapseTick, KillDecision,
34};
35pub use executor::{SwarmExecutor, run_agent_loop};
36pub use orchestrator::Orchestrator;
37pub use rate_limiter::{AdaptiveRateLimiter, RateLimitInfo, RateLimitStats};
38pub use result_store::{ResultStore, ResultStoreContext, SharedResult, SubTaskStoreHandle};
39pub use subtask::{SubAgent, SubTask, SubTaskContext, SubTaskResult, SubTaskStatus};
40
41use anyhow::Result;
42use async_trait::async_trait;
43
44#[async_trait]
49pub trait Actor: Send + Sync {
50 fn actor_id(&self) -> &str;
52
53 fn actor_status(&self) -> ActorStatus;
55
56 async fn initialize(&mut self) -> Result<()>;
58
59 async fn shutdown(&mut self) -> Result<()>;
61}
62
63#[async_trait]
68pub trait Handler<M>: Actor {
69 type Response: Send + Sync;
71
72 async fn handle(&mut self, message: M) -> Result<Self::Response>;
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ActorStatus {
79 Initializing,
81 Ready,
83 Busy,
85 Paused,
87 ShuttingDown,
89 Stopped,
91}
92
93impl std::fmt::Display for ActorStatus {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 ActorStatus::Initializing => write!(f, "initializing"),
97 ActorStatus::Ready => write!(f, "ready"),
98 ActorStatus::Busy => write!(f, "busy"),
99 ActorStatus::Paused => write!(f, "paused"),
100 ActorStatus::ShuttingDown => write!(f, "shutting_down"),
101 ActorStatus::Stopped => write!(f, "stopped"),
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
108pub enum SwarmMessage {
109 ExecuteTask {
111 task_id: String,
112 instruction: String,
113 },
114 Progress {
116 task_id: String,
117 progress: f32,
118 message: String,
119 },
120 TaskCompleted { task_id: String, result: String },
122 TaskFailed { task_id: String, error: String },
124 ToolRequest {
126 tool_id: String,
127 arguments: serde_json::Value,
128 },
129 ToolResponse {
131 tool_id: String,
132 result: crate::tool::ToolResult,
133 },
134}
135
136use serde::{Deserialize, Serialize};
137
138pub const MAX_SUBAGENTS: usize = 100;
140
141pub const MAX_TOOL_CALLS: usize = 1500;
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct SwarmConfig {
147 pub max_subagents: usize,
149
150 pub max_steps_per_subagent: usize,
152
153 pub max_total_steps: usize,
155
156 pub subagent_timeout_secs: u64,
158
159 pub parallel_enabled: bool,
161
162 pub critical_path_threshold: usize,
164
165 pub model: Option<String>,
167
168 pub max_concurrent_requests: usize,
170
171 pub request_delay_ms: u64,
173
174 pub worktree_enabled: bool,
176
177 pub worktree_auto_merge: bool,
179
180 pub working_dir: Option<String>,
182
183 #[serde(default)]
185 pub execution_mode: ExecutionMode,
186
187 #[serde(default = "default_k8s_pod_budget")]
189 pub k8s_pod_budget: usize,
190
191 #[serde(default)]
193 pub k8s_subagent_image: Option<String>,
194
195 #[serde(default = "default_max_retries")]
197 pub max_retries: u32,
198
199 #[serde(default = "default_base_delay_ms")]
201 pub base_delay_ms: u64,
202
203 #[serde(default = "default_max_delay_ms")]
205 pub max_delay_ms: u64,
206}
207
208#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
210#[serde(rename_all = "snake_case")]
211#[derive(Default)]
212pub enum ExecutionMode {
213 #[default]
215 LocalThread,
216 KubernetesPod,
218}
219
220impl ExecutionMode {
221 pub fn from_cli_value(value: &str) -> Self {
222 match value {
223 "k8s" | "kubernetes" | "kubernetes-pod" | "pod" => Self::KubernetesPod,
224 _ => Self::LocalThread,
225 }
226 }
227}
228
229fn default_k8s_pod_budget() -> usize {
230 8
231}
232
233fn default_max_retries() -> u32 {
234 3
235}
236
237fn default_base_delay_ms() -> u64 {
238 500
239}
240
241fn default_max_delay_ms() -> u64 {
242 30_000
243}
244
245impl Default for SwarmConfig {
246 fn default() -> Self {
247 Self {
248 max_subagents: MAX_SUBAGENTS,
249 max_steps_per_subagent: 100,
250 max_total_steps: MAX_TOOL_CALLS,
251 subagent_timeout_secs: 600, parallel_enabled: true,
253 critical_path_threshold: 10,
254 model: None,
255 max_concurrent_requests: 8,
259 request_delay_ms: 250,
260 worktree_enabled: true, worktree_auto_merge: true, working_dir: None,
263 execution_mode: ExecutionMode::LocalThread,
264 k8s_pod_budget: 8,
265 k8s_subagent_image: None,
266 max_retries: 3,
267 base_delay_ms: 500,
268 max_delay_ms: 30_000,
269 }
270 }
271}
272
273#[derive(Debug, Clone, Default, Serialize, Deserialize)]
275pub struct SwarmStats {
276 pub subagents_spawned: usize,
278
279 pub subagents_completed: usize,
281
282 pub subagents_failed: usize,
284
285 pub total_tool_calls: usize,
287
288 pub critical_path_length: usize,
290
291 pub execution_time_ms: u64,
293
294 pub sequential_time_estimate_ms: u64,
296
297 pub speedup_factor: f64,
299
300 pub stages: Vec<StageStats>,
302
303 pub rate_limit_stats: RateLimitStats,
305
306 pub cache_hits: u64,
308
309 pub cache_misses: u64,
311}
312
313#[derive(Debug, Clone, Default, Serialize, Deserialize)]
315pub struct StageStats {
316 pub stage: usize,
318
319 pub subagent_count: usize,
321
322 pub max_steps: usize,
324
325 pub total_steps: usize,
327
328 pub execution_time_ms: u64,
330}
331
332impl SwarmStats {
333 pub fn calculate_speedup(&mut self) {
335 if self.execution_time_ms > 0 {
336 self.speedup_factor =
337 self.sequential_time_estimate_ms as f64 / self.execution_time_ms as f64;
338 }
339 }
340
341 pub fn calculate_critical_path(&mut self) {
343 self.critical_path_length = self.stages.iter().map(|s| s.max_steps).sum();
344 }
345
346 pub fn merge_cache_stats(&mut self, cache_stats: &cache::CacheStats) {
348 self.cache_hits = cache_stats.hits;
349 self.cache_misses = cache_stats.misses;
350 }
351
352 pub fn cache_hit_rate(&self) -> f64 {
354 let total = self.cache_hits + self.cache_misses;
355 if total == 0 {
356 0.0
357 } else {
358 self.cache_hits as f64 / total as f64
359 }
360 }
361}
362
363#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
365#[serde(rename_all = "snake_case")]
366#[derive(Default)]
367pub enum DecompositionStrategy {
368 #[default]
370 Automatic,
371
372 ByDomain,
374
375 ByData,
377
378 ByStage,
380
381 None,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct SwarmResult {
388 pub success: bool,
390
391 pub result: String,
393
394 pub subtask_results: Vec<SubTaskResult>,
396
397 pub stats: SwarmStats,
399
400 pub artifacts: Vec<SwarmArtifact>,
402
403 pub error: Option<String>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct SwarmArtifact {
410 pub artifact_type: String,
412
413 pub name: String,
415
416 pub content: String,
418
419 pub source_subagent: Option<String>,
421
422 pub mime_type: Option<String>,
424}