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;
20pub mod orchestrator;
21pub mod rate_limiter;
22pub mod remote_subtask;
23pub mod result_store;
24pub mod speculative;
25pub mod subtask;
26pub mod token_exhaustion;
27pub mod token_truncate;
28mod tool_policy;
29pub mod validation;
30
31pub use cache::{CacheConfig, CacheStats, SwarmCache};
32pub use collapse_controller::{
33 BranchEvaluation, BranchObservation, BranchRuntimeState, CoherenceScore, CollapseController,
34 CollapsePolicy, CollapseTick, KillDecision,
35};
36pub use executor::{SwarmExecutor, run_agent_loop};
37pub use orchestrator::Orchestrator;
38pub use rate_limiter::{AdaptiveRateLimiter, RateLimitInfo, RateLimitStats};
39pub use result_store::{ResultStore, ResultStoreContext, SharedResult, SubTaskStoreHandle};
40pub use subtask::{SubAgent, SubTask, SubTaskContext, SubTaskResult, SubTaskStatus};
41
42use anyhow::Result;
43use async_trait::async_trait;
44
45#[async_trait]
50pub trait Actor: Send + Sync {
51 fn actor_id(&self) -> &str;
53
54 fn actor_status(&self) -> ActorStatus;
56
57 async fn initialize(&mut self) -> Result<()>;
59
60 async fn shutdown(&mut self) -> Result<()>;
62}
63
64#[async_trait]
69pub trait Handler<M>: Actor {
70 type Response: Send + Sync;
72
73 async fn handle(&mut self, message: M) -> Result<Self::Response>;
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum ActorStatus {
80 Initializing,
82 Ready,
84 Busy,
86 Paused,
88 ShuttingDown,
90 Stopped,
92}
93
94impl std::fmt::Display for ActorStatus {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 ActorStatus::Initializing => write!(f, "initializing"),
98 ActorStatus::Ready => write!(f, "ready"),
99 ActorStatus::Busy => write!(f, "busy"),
100 ActorStatus::Paused => write!(f, "paused"),
101 ActorStatus::ShuttingDown => write!(f, "shutting_down"),
102 ActorStatus::Stopped => write!(f, "stopped"),
103 }
104 }
105}
106
107#[derive(Debug, Clone)]
109pub enum SwarmMessage {
110 ExecuteTask {
112 task_id: String,
113 instruction: String,
114 },
115 Progress {
117 task_id: String,
118 progress: f32,
119 message: String,
120 },
121 TaskCompleted { task_id: String, result: String },
123 TaskFailed { task_id: String, error: String },
125 ToolRequest {
127 tool_id: String,
128 arguments: serde_json::Value,
129 },
130 ToolResponse {
132 tool_id: String,
133 result: crate::tool::ToolResult,
134 },
135}
136
137use serde::{Deserialize, Serialize};
138
139pub const MAX_SUBAGENTS: usize = 100;
141
142pub const MAX_TOOL_CALLS: usize = 1500;
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct SwarmConfig {
148 pub max_subagents: usize,
150
151 pub max_steps_per_subagent: usize,
153
154 pub max_total_steps: usize,
156
157 pub subagent_timeout_secs: u64,
159
160 pub parallel_enabled: bool,
162
163 pub critical_path_threshold: usize,
165
166 pub model: Option<String>,
168
169 pub max_concurrent_requests: usize,
171
172 pub request_delay_ms: u64,
174
175 pub worktree_enabled: bool,
177
178 pub worktree_auto_merge: bool,
180
181 pub working_dir: Option<String>,
183
184 #[serde(default)]
186 pub execution_mode: ExecutionMode,
187
188 #[serde(default = "default_k8s_pod_budget")]
190 pub k8s_pod_budget: usize,
191
192 #[serde(default)]
194 pub k8s_subagent_image: Option<String>,
195
196 #[serde(default = "default_max_retries")]
198 pub max_retries: u32,
199
200 #[serde(default = "default_base_delay_ms")]
202 pub base_delay_ms: u64,
203
204 #[serde(default = "default_max_delay_ms")]
206 pub max_delay_ms: u64,
207}
208
209#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
211#[serde(rename_all = "snake_case")]
212#[derive(Default)]
213pub enum ExecutionMode {
214 #[default]
216 LocalThread,
217 KubernetesPod,
219}
220
221impl ExecutionMode {
222 pub fn from_cli_value(value: &str) -> Self {
223 match value {
224 "k8s" | "kubernetes" | "kubernetes-pod" | "pod" => Self::KubernetesPod,
225 _ => Self::LocalThread,
226 }
227 }
228}
229
230fn default_k8s_pod_budget() -> usize {
231 8
232}
233
234fn default_max_retries() -> u32 {
235 3
236}
237
238fn default_base_delay_ms() -> u64 {
239 500
240}
241
242fn default_max_delay_ms() -> u64 {
243 30_000
244}
245
246impl Default for SwarmConfig {
247 fn default() -> Self {
248 Self {
249 max_subagents: MAX_SUBAGENTS,
250 max_steps_per_subagent: 100,
251 max_total_steps: MAX_TOOL_CALLS,
252 subagent_timeout_secs: 600, parallel_enabled: true,
254 critical_path_threshold: 10,
255 model: None,
256 max_concurrent_requests: 8,
260 request_delay_ms: 250,
261 worktree_enabled: true, worktree_auto_merge: true, working_dir: None,
264 execution_mode: ExecutionMode::LocalThread,
265 k8s_pod_budget: 8,
266 k8s_subagent_image: None,
267 max_retries: 3,
268 base_delay_ms: 500,
269 max_delay_ms: 30_000,
270 }
271 }
272}
273
274#[derive(Debug, Clone, Default, Serialize, Deserialize)]
276pub struct SwarmStats {
277 pub subagents_spawned: usize,
279
280 pub subagents_completed: usize,
282
283 pub subagents_failed: usize,
285
286 pub total_tool_calls: usize,
288
289 pub critical_path_length: usize,
291
292 pub execution_time_ms: u64,
294
295 pub sequential_time_estimate_ms: u64,
297
298 pub speedup_factor: f64,
300
301 pub stages: Vec<StageStats>,
303
304 pub rate_limit_stats: RateLimitStats,
306
307 pub cache_hits: u64,
309
310 pub cache_misses: u64,
312}
313
314#[derive(Debug, Clone, Default, Serialize, Deserialize)]
316pub struct StageStats {
317 pub stage: usize,
319
320 pub subagent_count: usize,
322
323 pub max_steps: usize,
325
326 pub total_steps: usize,
328
329 pub execution_time_ms: u64,
331}
332
333impl SwarmStats {
334 pub fn calculate_speedup(&mut self) {
336 if self.execution_time_ms > 0 {
337 self.speedup_factor =
338 self.sequential_time_estimate_ms as f64 / self.execution_time_ms as f64;
339 }
340 }
341
342 pub fn calculate_critical_path(&mut self) {
344 self.critical_path_length = self.stages.iter().map(|s| s.max_steps).sum();
345 }
346
347 pub fn merge_cache_stats(&mut self, cache_stats: &cache::CacheStats) {
349 self.cache_hits = cache_stats.hits;
350 self.cache_misses = cache_stats.misses;
351 }
352
353 pub fn cache_hit_rate(&self) -> f64 {
355 let total = self.cache_hits + self.cache_misses;
356 if total == 0 {
357 0.0
358 } else {
359 self.cache_hits as f64 / total as f64
360 }
361 }
362}
363
364#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
366#[serde(rename_all = "snake_case")]
367#[derive(Default)]
368pub enum DecompositionStrategy {
369 #[default]
371 Automatic,
372
373 ByDomain,
375
376 ByData,
378
379 ByStage,
381
382 None,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct SwarmResult {
389 pub success: bool,
391
392 pub result: String,
394
395 pub subtask_results: Vec<SubTaskResult>,
397
398 pub stats: SwarmStats,
400
401 pub artifacts: Vec<SwarmArtifact>,
403
404 pub error: Option<String>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct SwarmArtifact {
411 pub artifact_type: String,
413
414 pub name: String,
416
417 pub content: String,
419
420 pub source_subagent: Option<String>,
422
423 pub mime_type: Option<String>,
425}