Skip to main content

codetether_agent/swarm/
subtask.rs

1//! SubTask and SubAgent definitions
2//!
3//! A SubTask is a unit of work that can be executed in parallel.
4//! A SubAgent is a dynamically instantiated agent for executing a subtask.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11/// A sub-task that can be executed by a sub-agent
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SubTask {
14    /// Unique identifier
15    pub id: String,
16
17    /// Human-readable name/description
18    pub name: String,
19
20    /// The task instruction for the sub-agent
21    pub instruction: String,
22
23    /// Specialty/domain for this subtask
24    pub specialty: Option<String>,
25
26    /// Dependencies on other subtasks (by ID)
27    pub dependencies: Vec<String>,
28
29    /// Priority (higher = more important)
30    pub priority: i32,
31
32    /// Maximum steps allowed for this subtask
33    pub max_steps: usize,
34
35    /// Input context from parent or dependencies
36    pub context: SubTaskContext,
37
38    /// Current status
39    pub status: SubTaskStatus,
40
41    /// Assigned sub-agent ID
42    pub assigned_agent: Option<String>,
43
44    /// Creation timestamp
45    pub created_at: DateTime<Utc>,
46
47    /// Completion timestamp
48    pub completed_at: Option<DateTime<Utc>>,
49
50    /// Stage in the execution plan (0 = can run immediately)
51    pub stage: usize,
52}
53
54impl SubTask {
55    /// Create a new subtask
56    pub fn new(name: impl Into<String>, instruction: impl Into<String>) -> Self {
57        Self {
58            id: Uuid::new_v4().to_string(),
59            name: name.into(),
60            instruction: instruction.into(),
61            specialty: None,
62            dependencies: Vec::new(),
63            priority: 0,
64            max_steps: 100,
65            context: SubTaskContext::default(),
66            status: SubTaskStatus::Pending,
67            assigned_agent: None,
68            created_at: Utc::now(),
69            completed_at: None,
70            stage: 0,
71        }
72    }
73
74    /// Add a specialty
75    pub fn with_specialty(mut self, specialty: impl Into<String>) -> Self {
76        self.specialty = Some(specialty.into());
77        self
78    }
79
80    /// Add dependencies
81    pub fn with_dependencies(mut self, deps: Vec<String>) -> Self {
82        self.dependencies = deps;
83        self
84    }
85
86    /// Set priority
87    pub fn with_priority(mut self, priority: i32) -> Self {
88        self.priority = priority;
89        self
90    }
91
92    /// Set max steps
93    pub fn with_max_steps(mut self, max_steps: usize) -> Self {
94        self.max_steps = max_steps;
95        self
96    }
97
98    /// Add context
99    pub fn with_context(mut self, context: SubTaskContext) -> Self {
100        self.context = context;
101        self
102    }
103
104    /// Check if this subtask can run (all dependencies complete)
105    pub fn can_run(&self, completed: &[String]) -> bool {
106        self.dependencies.iter().all(|dep| completed.contains(dep))
107    }
108
109    /// Mark as running
110    pub fn start(&mut self, agent_id: &str) {
111        self.status = SubTaskStatus::Running;
112        self.assigned_agent = Some(agent_id.to_string());
113    }
114
115    /// Mark as completed
116    pub fn complete(&mut self, success: bool) {
117        self.status = if success {
118            SubTaskStatus::Completed
119        } else {
120            SubTaskStatus::Failed
121        };
122        self.completed_at = Some(Utc::now());
123    }
124}
125
126/// Context passed to a subtask
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
128pub struct SubTaskContext {
129    /// Parent task information
130    pub parent_task: Option<String>,
131
132    /// Results from dependency subtasks
133    pub dependency_results: HashMap<String, String>,
134
135    /// Shared files or resources
136    pub shared_resources: Vec<String>,
137
138    /// Additional metadata
139    pub metadata: HashMap<String, serde_json::Value>,
140}
141
142/// Status of a subtask
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "snake_case")]
145pub enum SubTaskStatus {
146    /// Waiting to be scheduled
147    Pending,
148
149    /// Waiting for dependencies
150    Blocked,
151
152    /// Currently executing
153    Running,
154
155    /// Successfully completed
156    Completed,
157
158    /// Failed with error
159    Failed,
160
161    /// Cancelled by orchestrator
162    Cancelled,
163
164    /// Timed out
165    TimedOut,
166}
167
168/// A sub-agent executing a subtask
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct SubAgent {
171    /// Unique identifier
172    pub id: String,
173
174    /// Display name
175    pub name: String,
176
177    /// Specialty/role (e.g., "AI Researcher", "Code Writer", "Fact Checker")
178    pub specialty: String,
179
180    /// The subtask this agent is working on
181    pub subtask_id: String,
182
183    /// Current status
184    pub status: SubAgentStatus,
185
186    /// Number of steps taken
187    pub steps: usize,
188
189    /// Tool calls made
190    pub tool_calls: Vec<ToolCallRecord>,
191
192    /// Accumulated output
193    pub output: String,
194
195    /// Model being used
196    pub model: String,
197
198    /// Provider
199    pub provider: String,
200
201    /// Creation timestamp
202    pub created_at: DateTime<Utc>,
203
204    /// Last activity timestamp
205    pub last_active: DateTime<Utc>,
206}
207
208impl SubAgent {
209    /// Create a new sub-agent for a subtask
210    pub fn new(
211        name: impl Into<String>,
212        specialty: impl Into<String>,
213        subtask_id: impl Into<String>,
214        model: impl Into<String>,
215        provider: impl Into<String>,
216    ) -> Self {
217        let now = Utc::now();
218        Self {
219            id: Uuid::new_v4().to_string(),
220            name: name.into(),
221            specialty: specialty.into(),
222            subtask_id: subtask_id.into(),
223            status: SubAgentStatus::Initializing,
224            steps: 0,
225            tool_calls: Vec::new(),
226            output: String::new(),
227            model: model.into(),
228            provider: provider.into(),
229            created_at: now,
230            last_active: now,
231        }
232    }
233
234    /// Record a tool call
235    pub fn record_tool_call(&mut self, name: &str, success: bool) {
236        self.tool_calls.push(ToolCallRecord {
237            name: name.to_string(),
238            timestamp: Utc::now(),
239            success,
240        });
241        self.steps += 1;
242        self.last_active = Utc::now();
243    }
244
245    /// Append to output
246    pub fn append_output(&mut self, text: &str) {
247        self.output.push_str(text);
248        self.last_active = Utc::now();
249    }
250
251    /// Set status
252    pub fn set_status(&mut self, status: SubAgentStatus) {
253        self.status = status;
254        self.last_active = Utc::now();
255    }
256}
257
258/// Status of a sub-agent
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
260#[serde(rename_all = "snake_case")]
261pub enum SubAgentStatus {
262    /// Being initialized
263    Initializing,
264
265    /// Actively working
266    Working,
267
268    /// Waiting for resource/dependency
269    Waiting,
270
271    /// Successfully completed
272    Completed,
273
274    /// Failed
275    Failed,
276
277    /// Terminated by orchestrator
278    Terminated,
279}
280
281/// Record of a tool call
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct ToolCallRecord {
284    pub name: String,
285    pub timestamp: DateTime<Utc>,
286    pub success: bool,
287}
288
289/// Result of a subtask execution
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct SubTaskResult {
292    /// Subtask ID
293    pub subtask_id: String,
294
295    /// Sub-agent ID that executed it
296    pub subagent_id: String,
297
298    /// Whether it succeeded
299    pub success: bool,
300
301    /// Result text
302    pub result: String,
303
304    /// Number of steps taken
305    pub steps: usize,
306
307    /// Tool calls made
308    pub tool_calls: usize,
309
310    /// Execution time (milliseconds)
311    pub execution_time_ms: u64,
312
313    /// Any error message
314    pub error: Option<String>,
315
316    /// Artifacts produced
317    pub artifacts: Vec<String>,
318}