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