1pub mod builtin;
6
7use crate::config::PermissionAction;
8use crate::provider::{CompletionRequest, ContentPart, Message, Provider, Role};
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(&self, session: &mut Session, prompt: &str) -> Result<AgentResponse> {
68 session.add_message(Message {
70 role: Role::User,
71 content: vec![ContentPart::Text {
72 text: prompt.to_string(),
73 }],
74 });
75
76 let mut steps = 0;
77 let max_steps = self.info.max_steps.unwrap_or(100);
78
79 loop {
80 steps += 1;
81 if steps > max_steps {
82 anyhow::bail!("Exceeded maximum steps ({})", max_steps);
83 }
84
85 let request = CompletionRequest {
87 messages: self.build_messages(session),
88 tools: self.tools.definitions(),
89 model: self
90 .info
91 .model
92 .clone()
93 .unwrap_or_else(|| match self.provider.name() {
94 "zhipuai" | "zai" => "glm-5".to_string(),
95 "openrouter" => "z-ai/glm-5".to_string(),
96 _ => "glm-5".to_string(),
97 }),
98 temperature: self.info.temperature,
99 top_p: self.info.top_p,
100 max_tokens: None,
101 stop: vec![],
102 };
103
104 let response = self.provider.complete(request).await?;
106 session.add_message(response.message.clone());
107
108 let tool_calls: Vec<_> = response
110 .message
111 .content
112 .iter()
113 .filter_map(|p| match p {
114 ContentPart::ToolCall {
115 id,
116 name,
117 arguments,
118 } => Some((id.clone(), name.clone(), arguments.clone())),
119 _ => None,
120 })
121 .collect();
122
123 if tool_calls.is_empty() {
124 let text = response
126 .message
127 .content
128 .iter()
129 .filter_map(|p| match p {
130 ContentPart::Text { text } => Some(text.clone()),
131 _ => None,
132 })
133 .collect::<Vec<_>>()
134 .join("\n");
135
136 return Ok(AgentResponse {
137 text,
138 tool_uses: session.tool_uses.clone(),
139 usage: session.usage.clone(),
140 });
141 }
142
143 for (id, name, arguments) in tool_calls {
145 let result = self.execute_tool(&name, &arguments).await;
146
147 session.tool_uses.push(ToolUse {
148 id: id.clone(),
149 name: name.clone(),
150 input: arguments.clone(),
151 output: result.output.clone(),
152 success: result.success,
153 });
154
155 session.add_message(Message {
156 role: Role::Tool,
157 content: vec![ContentPart::ToolResult {
158 tool_call_id: id,
159 content: result.output,
160 }],
161 });
162 }
163 }
164 }
165
166 fn build_messages(&self, session: &Session) -> Vec<Message> {
168 let mut messages = vec![Message {
169 role: Role::System,
170 content: vec![ContentPart::Text {
171 text: self.system_prompt.clone(),
172 }],
173 }];
174 messages.extend(session.messages.clone());
175 messages
176 }
177
178 async fn execute_tool(&self, name: &str, arguments: &str) -> ToolResult {
180 if let Some(permission) = self.permissions.get(name) {
182 tracing::debug!(tool = name, permission = ?permission, "Checking tool permission");
183 }
186
187 match self.tools.get(name) {
188 Some(tool) => {
189 let args: serde_json::Value = match serde_json::from_str(arguments) {
190 Ok(v) => v,
191 Err(e) => {
192 return ToolResult {
193 output: format!("Failed to parse arguments: {}", e),
194 success: false,
195 metadata: HashMap::new(),
196 };
197 }
198 };
199
200 match tool.execute(args).await {
201 Ok(result) => result,
202 Err(e) => ToolResult {
203 output: format!("Tool execution failed: {}", e),
204 success: false,
205 metadata: HashMap::new(),
206 },
207 }
208 }
209 None => {
210 let available_tools = self.tools.list().iter().map(|s| s.to_string()).collect();
212 let invalid_tool = crate::tool::invalid::InvalidTool::with_context(
213 name.to_string(),
214 available_tools,
215 );
216 let args = serde_json::json!({
217 "requested_tool": name,
218 "args": serde_json::from_str::<serde_json::Value>(arguments).unwrap_or(serde_json::json!({}))
219 });
220 match invalid_tool.execute(args).await {
221 Ok(result) => result,
222 Err(e) => ToolResult {
223 output: format!("Unknown tool: {}. Error: {}", name, e),
224 success: false,
225 metadata: HashMap::new(),
226 },
227 }
228 }
229 }
230 }
231
232 pub fn get_tool(&self, name: &str) -> Option<Arc<dyn Tool>> {
234 self.tools.get(name)
235 }
236
237 pub fn register_tool(&mut self, tool: Arc<dyn Tool>) {
239 self.tools.register(tool);
240 }
241
242 pub fn list_tools(&self) -> Vec<&str> {
244 self.tools.list()
245 }
246
247 pub fn has_tool(&self, name: &str) -> bool {
249 self.tools.get(name).is_some()
250 }
251}
252
253#[async_trait]
255impl Actor for Agent {
256 fn actor_id(&self) -> &str {
257 &self.info.name
258 }
259
260 fn actor_status(&self) -> ActorStatus {
261 ActorStatus::Ready
263 }
264
265 async fn initialize(&mut self) -> Result<()> {
266 tracing::info!(
269 "Agent '{}' initialized for swarm participation",
270 self.info.name
271 );
272 Ok(())
273 }
274
275 async fn shutdown(&mut self) -> Result<()> {
276 tracing::info!("Agent '{}' shutting down", self.info.name);
277 Ok(())
278 }
279}
280
281#[async_trait]
283impl Handler<SwarmMessage> for Agent {
284 type Response = SwarmMessage;
285
286 async fn handle(&mut self, message: SwarmMessage) -> Result<Self::Response> {
287 match message {
288 SwarmMessage::ExecuteTask {
289 task_id,
290 instruction,
291 } => {
292 let mut session = Session::new().await?;
294
295 match self.execute(&mut session, &instruction).await {
297 Ok(response) => Ok(SwarmMessage::TaskCompleted {
298 task_id,
299 result: response.text,
300 }),
301 Err(e) => Ok(SwarmMessage::TaskFailed {
302 task_id,
303 error: e.to_string(),
304 }),
305 }
306 }
307 SwarmMessage::ToolRequest { tool_id, arguments } => {
308 let result = if let Some(tool) = self.get_tool(&tool_id) {
310 match tool.execute(arguments).await {
311 Ok(r) => r,
312 Err(e) => ToolResult::error(format!("Tool execution failed: {}", e)),
313 }
314 } else {
315 let available_tools = self.tools.list().iter().map(|s| s.to_string()).collect();
317 let invalid_tool = crate::tool::invalid::InvalidTool::with_context(
318 tool_id.clone(),
319 available_tools,
320 );
321 let args = serde_json::json!({
322 "requested_tool": tool_id,
323 "args": arguments
324 });
325 match invalid_tool.execute(args).await {
326 Ok(r) => r,
327 Err(e) => ToolResult::error(format!("Tool '{}' not found: {}", tool_id, e)),
328 }
329 };
330
331 Ok(SwarmMessage::ToolResponse { tool_id, result })
332 }
333 _ => {
334 Ok(SwarmMessage::TaskFailed {
336 task_id: "unknown".to_string(),
337 error: "Unsupported message type".to_string(),
338 })
339 }
340 }
341 }
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct AgentResponse {
347 pub text: String,
348 pub tool_uses: Vec<ToolUse>,
349 pub usage: crate::provider::Usage,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct ToolUse {
355 pub id: String,
356 pub name: String,
357 pub input: String,
358 pub output: String,
359 pub success: bool,
360}
361
362pub struct AgentRegistry {
364 agents: HashMap<String, AgentInfo>,
365}
366
367impl AgentRegistry {
368 #[allow(dead_code)]
369 pub fn new() -> Self {
370 Self {
371 agents: HashMap::new(),
372 }
373 }
374
375 pub fn register(&mut self, info: AgentInfo) {
377 self.agents.insert(info.name.clone(), info);
378 }
379
380 #[allow(dead_code)]
382 pub fn get(&self, name: &str) -> Option<&AgentInfo> {
383 self.agents.get(name)
384 }
385
386 pub fn list(&self) -> Vec<&AgentInfo> {
388 self.agents.values().collect()
389 }
390
391 #[allow(dead_code)]
393 pub fn list_primary(&self) -> Vec<&AgentInfo> {
394 self.agents
395 .values()
396 .filter(|a| a.mode == AgentMode::Primary && !a.hidden)
397 .collect()
398 }
399
400 pub fn with_builtins() -> Self {
402 let mut registry = Self::new();
403
404 registry.register(builtin::build_agent());
405 registry.register(builtin::plan_agent());
406 registry.register(builtin::explore_agent());
407
408 registry
409 }
410}
411
412impl Default for AgentRegistry {
413 fn default() -> Self {
414 Self::with_builtins()
415 }
416}