pub mod cache;
pub mod collapse_controller;
pub mod executor;
pub mod kubernetes_executor;
pub mod orchestrator;
pub mod rate_limiter;
pub mod remote_subtask;
pub mod result_store;
pub mod subtask;
pub use cache::{CacheConfig, CacheStats, SwarmCache};
pub use collapse_controller::{
BranchEvaluation, BranchObservation, BranchRuntimeState, CoherenceScore, CollapseController,
CollapsePolicy, CollapseTick, KillDecision,
};
pub use executor::{SwarmExecutor, run_agent_loop};
pub use orchestrator::Orchestrator;
pub use rate_limiter::{AdaptiveRateLimiter, RateLimitInfo, RateLimitStats};
pub use result_store::{ResultStore, ResultStoreContext, SharedResult, SubTaskStoreHandle};
pub use subtask::{SubAgent, SubTask, SubTaskContext, SubTaskResult, SubTaskStatus};
use anyhow::Result;
use async_trait::async_trait;
#[async_trait]
pub trait Actor: Send + Sync {
fn actor_id(&self) -> &str;
fn actor_status(&self) -> ActorStatus;
async fn initialize(&mut self) -> Result<()>;
async fn shutdown(&mut self) -> Result<()>;
}
#[async_trait]
pub trait Handler<M>: Actor {
type Response: Send + Sync;
async fn handle(&mut self, message: M) -> Result<Self::Response>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActorStatus {
Initializing,
Ready,
Busy,
Paused,
ShuttingDown,
Stopped,
}
impl std::fmt::Display for ActorStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ActorStatus::Initializing => write!(f, "initializing"),
ActorStatus::Ready => write!(f, "ready"),
ActorStatus::Busy => write!(f, "busy"),
ActorStatus::Paused => write!(f, "paused"),
ActorStatus::ShuttingDown => write!(f, "shutting_down"),
ActorStatus::Stopped => write!(f, "stopped"),
}
}
}
#[derive(Debug, Clone)]
pub enum SwarmMessage {
ExecuteTask {
task_id: String,
instruction: String,
},
Progress {
task_id: String,
progress: f32,
message: String,
},
TaskCompleted { task_id: String, result: String },
TaskFailed { task_id: String, error: String },
ToolRequest {
tool_id: String,
arguments: serde_json::Value,
},
ToolResponse {
tool_id: String,
result: crate::tool::ToolResult,
},
}
use serde::{Deserialize, Serialize};
pub const MAX_SUBAGENTS: usize = 100;
pub const MAX_TOOL_CALLS: usize = 1500;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwarmConfig {
pub max_subagents: usize,
pub max_steps_per_subagent: usize,
pub max_total_steps: usize,
pub subagent_timeout_secs: u64,
pub parallel_enabled: bool,
pub critical_path_threshold: usize,
pub model: Option<String>,
pub max_concurrent_requests: usize,
pub request_delay_ms: u64,
pub worktree_enabled: bool,
pub worktree_auto_merge: bool,
pub working_dir: Option<String>,
#[serde(default)]
pub execution_mode: ExecutionMode,
#[serde(default = "default_k8s_pod_budget")]
pub k8s_pod_budget: usize,
#[serde(default)]
pub k8s_subagent_image: Option<String>,
#[serde(default = "default_max_retries")]
pub max_retries: u32,
#[serde(default = "default_base_delay_ms")]
pub base_delay_ms: u64,
#[serde(default = "default_max_delay_ms")]
pub max_delay_ms: u64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum ExecutionMode {
#[default]
LocalThread,
KubernetesPod,
}
impl ExecutionMode {
pub fn from_cli_value(value: &str) -> Self {
match value {
"k8s" | "kubernetes" | "kubernetes-pod" | "pod" => Self::KubernetesPod,
_ => Self::LocalThread,
}
}
}
fn default_k8s_pod_budget() -> usize {
8
}
fn default_max_retries() -> u32 {
3
}
fn default_base_delay_ms() -> u64 {
500
}
fn default_max_delay_ms() -> u64 {
30_000
}
impl Default for SwarmConfig {
fn default() -> Self {
Self {
max_subagents: MAX_SUBAGENTS,
max_steps_per_subagent: 100,
max_total_steps: MAX_TOOL_CALLS,
subagent_timeout_secs: 600, parallel_enabled: true,
critical_path_threshold: 10,
model: None,
max_concurrent_requests: 3, request_delay_ms: 1000, worktree_enabled: true, worktree_auto_merge: true, working_dir: None,
execution_mode: ExecutionMode::LocalThread,
k8s_pod_budget: 8,
k8s_subagent_image: None,
max_retries: 3,
base_delay_ms: 500,
max_delay_ms: 30_000,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SwarmStats {
pub subagents_spawned: usize,
pub subagents_completed: usize,
pub subagents_failed: usize,
pub total_tool_calls: usize,
pub critical_path_length: usize,
pub execution_time_ms: u64,
pub sequential_time_estimate_ms: u64,
pub speedup_factor: f64,
pub stages: Vec<StageStats>,
pub rate_limit_stats: RateLimitStats,
pub cache_hits: u64,
pub cache_misses: u64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StageStats {
pub stage: usize,
pub subagent_count: usize,
pub max_steps: usize,
pub total_steps: usize,
pub execution_time_ms: u64,
}
impl SwarmStats {
pub fn calculate_speedup(&mut self) {
if self.execution_time_ms > 0 {
self.speedup_factor =
self.sequential_time_estimate_ms as f64 / self.execution_time_ms as f64;
}
}
pub fn calculate_critical_path(&mut self) {
self.critical_path_length = self.stages.iter().map(|s| s.max_steps).sum();
}
pub fn merge_cache_stats(&mut self, cache_stats: &cache::CacheStats) {
self.cache_hits = cache_stats.hits;
self.cache_misses = cache_stats.misses;
}
pub fn cache_hit_rate(&self) -> f64 {
let total = self.cache_hits + self.cache_misses;
if total == 0 {
0.0
} else {
self.cache_hits as f64 / total as f64
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum DecompositionStrategy {
#[default]
Automatic,
ByDomain,
ByData,
ByStage,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwarmResult {
pub success: bool,
pub result: String,
pub subtask_results: Vec<SubTaskResult>,
pub stats: SwarmStats,
pub artifacts: Vec<SwarmArtifact>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwarmArtifact {
pub artifact_type: String,
pub name: String,
pub content: String,
pub source_subagent: Option<String>,
pub mime_type: Option<String>,
}