1pub mod builtin;
6
7use crate::config::PermissionAction;
8use crate::provider::{CompletionRequest, Message, Provider, Role, ContentPart};
9use crate::session::Session;
10use crate::swarm::{Actor, ActorStatus, Handler, SwarmMessage};
11use crate::tool::{Tool, ToolRegistry, ToolResult};
12use anyhow::Result;
13use async_trait::async_trait;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::Arc;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct AgentInfo {
21 pub name: String,
22 pub description: Option<String>,
23 pub mode: AgentMode,
24 pub native: bool,
25 pub hidden: bool,
26 pub model: Option<String>,
27 pub temperature: Option<f32>,
28 pub top_p: Option<f32>,
29 pub max_steps: Option<usize>,
30}
31
32#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
33#[serde(rename_all = "lowercase")]
34pub enum AgentMode {
35 Primary,
36 Subagent,
37 All,
38}
39
40pub struct Agent {
42 pub info: AgentInfo,
43 pub provider: Arc<dyn Provider>,
44 pub tools: ToolRegistry,
45 pub permissions: HashMap<String, PermissionAction>,
46 system_prompt: String,
47}
48
49impl Agent {
50 pub fn new(
52 info: AgentInfo,
53 provider: Arc<dyn Provider>,
54 tools: ToolRegistry,
55 system_prompt: String,
56 ) -> Self {
57 Self {
58 info,
59 provider,
60 tools,
61 permissions: HashMap::new(),
62 system_prompt,
63 }
64 }
65
66 pub async fn execute(
68 &self,
69 session: &mut Session,
70 prompt: &str,
71 ) -> Result<AgentResponse> {
72 session.add_message(Message {
74 role: Role::User,
75 content: vec![ContentPart::Text { text: prompt.to_string() }],
76 });
77
78 let mut steps = 0;
79 let max_steps = self.info.max_steps.unwrap_or(100);
80
81 loop {
82 steps += 1;
83 if steps > max_steps {
84 anyhow::bail!("Exceeded maximum steps ({})", max_steps);
85 }
86
87 let request = CompletionRequest {
89 messages: self.build_messages(session),
90 tools: self.tools.definitions(),
91 model: self.info.model.clone().unwrap_or_else(|| "gpt-4o".to_string()),
92 temperature: self.info.temperature,
93 top_p: self.info.top_p,
94 max_tokens: None,
95 stop: vec![],
96 };
97
98 let response = self.provider.complete(request).await?;
100 session.add_message(response.message.clone());
101
102 let tool_calls: Vec<_> = response
104 .message
105 .content
106 .iter()
107 .filter_map(|p| match p {
108 ContentPart::ToolCall { id, name, arguments } => {
109 Some((id.clone(), name.clone(), arguments.clone()))
110 }
111 _ => None,
112 })
113 .collect();
114
115 if tool_calls.is_empty() {
116 let text = response
118 .message
119 .content
120 .iter()
121 .filter_map(|p| match p {
122 ContentPart::Text { text } => Some(text.clone()),
123 _ => None,
124 })
125 .collect::<Vec<_>>()
126 .join("\n");
127
128 return Ok(AgentResponse {
129 text,
130 tool_uses: session.tool_uses.clone(),
131 usage: session.usage.clone(),
132 });
133 }
134
135 for (id, name, arguments) in tool_calls {
137 let result = self.execute_tool(&name, &arguments).await;
138
139 session.tool_uses.push(ToolUse {
140 id: id.clone(),
141 name: name.clone(),
142 input: arguments.clone(),
143 output: result.output.clone(),
144 success: result.success,
145 });
146
147 session.add_message(Message {
148 role: Role::Tool,
149 content: vec![ContentPart::ToolResult {
150 tool_call_id: id,
151 content: result.output,
152 }],
153 });
154 }
155 }
156 }
157
158 fn build_messages(&self, session: &Session) -> Vec<Message> {
160 let mut messages = vec![Message {
161 role: Role::System,
162 content: vec![ContentPart::Text {
163 text: self.system_prompt.clone(),
164 }],
165 }];
166 messages.extend(session.messages.clone());
167 messages
168 }
169
170 async fn execute_tool(&self, name: &str, arguments: &str) -> ToolResult {
172 if let Some(permission) = self.permissions.get(name) {
174 tracing::debug!(tool = name, permission = ?permission, "Checking tool permission");
175 }
178
179 match self.tools.get(name) {
180 Some(tool) => {
181 let args: serde_json::Value = match serde_json::from_str(arguments) {
182 Ok(v) => v,
183 Err(e) => {
184 return ToolResult {
185 output: format!("Failed to parse arguments: {}", e),
186 success: false,
187 metadata: HashMap::new(),
188 }
189 }
190 };
191
192 match tool.execute(args).await {
193 Ok(result) => result,
194 Err(e) => ToolResult {
195 output: format!("Tool execution failed: {}", e),
196 success: false,
197 metadata: HashMap::new(),
198 },
199 }
200 }
201 None => {
202 let available_tools = self.tools.list().iter().map(|s| s.to_string()).collect();
204 let invalid_tool = crate::tool::invalid::InvalidTool::with_context(name.to_string(), available_tools);
205 let args = serde_json::json!({
206 "requested_tool": name,
207 "args": serde_json::from_str::<serde_json::Value>(arguments).unwrap_or(serde_json::json!({}))
208 });
209 match invalid_tool.execute(args).await {
210 Ok(result) => result,
211 Err(e) => ToolResult {
212 output: format!("Unknown tool: {}. Error: {}", name, e),
213 success: false,
214 metadata: HashMap::new(),
215 },
216 }
217 }
218 }
219 }
220
221 pub fn get_tool(&self, name: &str) -> Option<Arc<dyn Tool>> {
223 self.tools.get(name)
224 }
225
226 pub fn register_tool(&mut self, tool: Arc<dyn Tool>) {
228 self.tools.register(tool);
229 }
230
231 pub fn list_tools(&self) -> Vec<&str> {
233 self.tools.list()
234 }
235
236 pub fn has_tool(&self, name: &str) -> bool {
238 self.tools.get(name).is_some()
239 }
240}
241
242#[async_trait]
244impl Actor for Agent {
245 fn actor_id(&self) -> &str {
246 &self.info.name
247 }
248
249 fn actor_status(&self) -> ActorStatus {
250 ActorStatus::Ready
252 }
253
254 async fn initialize(&mut self) -> Result<()> {
255 tracing::info!("Agent '{}' initialized for swarm participation", self.info.name);
258 Ok(())
259 }
260
261 async fn shutdown(&mut self) -> Result<()> {
262 tracing::info!("Agent '{}' shutting down", self.info.name);
263 Ok(())
264 }
265}
266
267#[async_trait]
269impl Handler<SwarmMessage> for Agent {
270 type Response = SwarmMessage;
271
272 async fn handle(&mut self, message: SwarmMessage) -> Result<Self::Response> {
273 match message {
274 SwarmMessage::ExecuteTask { task_id, instruction } => {
275 let mut session = Session::new().await?;
277
278 match self.execute(&mut session, &instruction).await {
280 Ok(response) => {
281 Ok(SwarmMessage::TaskCompleted {
282 task_id,
283 result: response.text,
284 })
285 }
286 Err(e) => {
287 Ok(SwarmMessage::TaskFailed {
288 task_id,
289 error: e.to_string(),
290 })
291 }
292 }
293 }
294 SwarmMessage::ToolRequest { tool_id, arguments } => {
295 let result = if let Some(tool) = self.get_tool(&tool_id) {
297 match tool.execute(arguments).await {
298 Ok(r) => r,
299 Err(e) => ToolResult::error(format!("Tool execution failed: {}", e)),
300 }
301 } else {
302 let available_tools = self.tools.list().iter().map(|s| s.to_string()).collect();
304 let invalid_tool = crate::tool::invalid::InvalidTool::with_context(tool_id.clone(), available_tools);
305 let args = serde_json::json!({
306 "requested_tool": tool_id,
307 "args": arguments
308 });
309 match invalid_tool.execute(args).await {
310 Ok(r) => r,
311 Err(e) => ToolResult::error(format!("Tool '{}' not found: {}", tool_id, e)),
312 }
313 };
314
315 Ok(SwarmMessage::ToolResponse {
316 tool_id,
317 result,
318 })
319 }
320 _ => {
321 Ok(SwarmMessage::TaskFailed {
323 task_id: "unknown".to_string(),
324 error: "Unsupported message type".to_string(),
325 })
326 }
327 }
328 }
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct AgentResponse {
334 pub text: String,
335 pub tool_uses: Vec<ToolUse>,
336 pub usage: crate::provider::Usage,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct ToolUse {
342 pub id: String,
343 pub name: String,
344 pub input: String,
345 pub output: String,
346 pub success: bool,
347}
348
349pub struct AgentRegistry {
351 agents: HashMap<String, AgentInfo>,
352}
353
354impl AgentRegistry {
355 #[allow(dead_code)]
356 pub fn new() -> Self {
357 Self {
358 agents: HashMap::new(),
359 }
360 }
361
362 pub fn register(&mut self, info: AgentInfo) {
364 self.agents.insert(info.name.clone(), info);
365 }
366
367 #[allow(dead_code)]
369 pub fn get(&self, name: &str) -> Option<&AgentInfo> {
370 self.agents.get(name)
371 }
372
373 pub fn list(&self) -> Vec<&AgentInfo> {
375 self.agents.values().collect()
376 }
377
378 #[allow(dead_code)]
380 pub fn list_primary(&self) -> Vec<&AgentInfo> {
381 self.agents
382 .values()
383 .filter(|a| a.mode == AgentMode::Primary && !a.hidden)
384 .collect()
385 }
386
387 pub fn with_builtins() -> Self {
389 let mut registry = Self::new();
390
391 registry.register(builtin::build_agent());
392 registry.register(builtin::plan_agent());
393 registry.register(builtin::explore_agent());
394
395 registry
396 }
397}
398
399impl Default for AgentRegistry {
400 fn default() -> Self {
401 Self::with_builtins()
402 }
403}