brainwires_agent_network/client/
agent_ops.rs1use serde::{Deserialize, Serialize};
2use serde_json::json;
3
4use super::client::AgentNetworkClient;
5use super::error::AgentNetworkClientError;
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct AgentConfig {
10 #[serde(skip_serializing_if = "Option::is_none")]
12 pub max_iterations: Option<u32>,
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub enable_validation: Option<bool>,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub build_type: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub enable_mdap: Option<bool>,
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub mdap_preset: Option<String>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct AgentResult {
30 pub agent_id: String,
32 pub success: bool,
34 pub iterations: u32,
36 pub summary: String,
38 pub raw_output: String,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct AgentInfo {
45 pub agent_id: String,
47 pub status: String,
49 pub task_description: String,
51}
52
53impl AgentNetworkClient {
54 pub async fn spawn_agent(
56 &mut self,
57 description: &str,
58 working_dir: &str,
59 config: AgentConfig,
60 ) -> Result<String, AgentNetworkClientError> {
61 let mut args = json!({
62 "description": description,
63 "working_directory": working_dir,
64 });
65
66 if let Some(max_iter) = config.max_iterations {
67 args["max_iterations"] = json!(max_iter);
68 }
69 if let Some(enable_val) = config.enable_validation {
70 args["enable_validation"] = json!(enable_val);
71 }
72 if let Some(ref build_type) = config.build_type {
73 args["build_type"] = json!(build_type);
74 }
75 if let Some(enable_mdap) = config.enable_mdap {
76 args["enable_mdap"] = json!(enable_mdap);
77 }
78 if let Some(ref preset) = config.mdap_preset {
79 args["mdap_preset"] = json!(preset);
80 }
81
82 let result = self.call_tool("agent_spawn", args).await?;
83
84 let agent_id = extract_agent_id(&result)?;
87 Ok(agent_id)
88 }
89
90 pub async fn await_agent(
92 &mut self,
93 agent_id: &str,
94 timeout_secs: Option<u64>,
95 ) -> Result<AgentResult, AgentNetworkClientError> {
96 let mut args = json!({ "agent_id": agent_id });
97 if let Some(timeout) = timeout_secs {
98 args["timeout_secs"] = json!(timeout);
99 }
100
101 let result = self.call_tool("agent_await", args).await?;
102 parse_agent_result(&result, agent_id)
103 }
104
105 pub async fn list_agents(&mut self) -> Result<Vec<AgentInfo>, AgentNetworkClientError> {
107 let result = self.call_tool("agent_list", json!({})).await?;
108 parse_agent_list(&result)
109 }
110
111 pub async fn stop_agent(&mut self, agent_id: &str) -> Result<(), AgentNetworkClientError> {
113 self.call_tool("agent_stop", json!({ "agent_id": agent_id }))
114 .await?;
115 Ok(())
116 }
117
118 pub async fn get_agent_status(
120 &mut self,
121 agent_id: &str,
122 ) -> Result<AgentInfo, AgentNetworkClientError> {
123 let result = self
124 .call_tool("agent_status", json!({ "agent_id": agent_id }))
125 .await?;
126 parse_agent_info(&result, agent_id)
127 }
128}
129
130fn extract_agent_id(result: &serde_json::Value) -> Result<String, AgentNetworkClientError> {
131 if let Some(content) = result.get("content").and_then(|c| c.as_array()) {
133 for item in content {
134 if let Some(text) = item.get("text").and_then(|t| t.as_str()) {
135 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(text)
137 && let Some(id) = parsed.get("agent_id").and_then(|i| i.as_str())
138 {
139 return Ok(id.to_string());
140 }
141 if text.contains("agent_id") {
143 if let Some(start) = text.find("agent_id") {
145 let rest = &text[start..];
146 if let Some(colon) = rest.find(':') {
147 let value_part = rest[colon + 1..].trim();
148 let id = value_part
149 .trim_start_matches('"')
150 .split('"')
151 .next()
152 .unwrap_or(value_part.split_whitespace().next().unwrap_or(""));
153 if !id.is_empty() {
154 return Ok(id.to_string());
155 }
156 }
157 }
158 }
159 }
160 }
161 }
162
163 if let Some(id) = result.get("agent_id").and_then(|i| i.as_str()) {
165 return Ok(id.to_string());
166 }
167
168 Err(AgentNetworkClientError::Protocol(
169 "Could not extract agent_id from spawn result".to_string(),
170 ))
171}
172
173fn parse_agent_result(
174 result: &serde_json::Value,
175 agent_id: &str,
176) -> Result<AgentResult, AgentNetworkClientError> {
177 let raw = result.to_string();
179
180 let text = extract_text_content(result).unwrap_or_default();
181
182 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&text) {
184 return Ok(AgentResult {
185 agent_id: parsed
186 .get("agent_id")
187 .and_then(|i| i.as_str())
188 .unwrap_or(agent_id)
189 .to_string(),
190 success: parsed
191 .get("success")
192 .and_then(|s| s.as_bool())
193 .unwrap_or(false),
194 iterations: parsed
195 .get("iterations")
196 .and_then(|i| i.as_u64())
197 .unwrap_or(0) as u32,
198 summary: parsed
199 .get("summary")
200 .and_then(|s| s.as_str())
201 .unwrap_or("")
202 .to_string(),
203 raw_output: raw,
204 });
205 }
206
207 Ok(AgentResult {
209 agent_id: agent_id.to_string(),
210 success: text.contains("success") || text.contains("completed"),
211 iterations: 0,
212 summary: text,
213 raw_output: raw,
214 })
215}
216
217fn parse_agent_list(result: &serde_json::Value) -> Result<Vec<AgentInfo>, AgentNetworkClientError> {
218 let text = extract_text_content(result).unwrap_or_default();
219
220 if let Ok(agents) = serde_json::from_str::<Vec<AgentInfo>>(&text) {
221 return Ok(agents);
222 }
223
224 if let Ok(info) = serde_json::from_str::<AgentInfo>(&text) {
226 return Ok(vec![info]);
227 }
228
229 Ok(Vec::new())
230}
231
232fn parse_agent_info(
233 result: &serde_json::Value,
234 agent_id: &str,
235) -> Result<AgentInfo, AgentNetworkClientError> {
236 let text = extract_text_content(result).unwrap_or_default();
237
238 if let Ok(info) = serde_json::from_str::<AgentInfo>(&text) {
239 return Ok(info);
240 }
241
242 Ok(AgentInfo {
243 agent_id: agent_id.to_string(),
244 status: "unknown".to_string(),
245 task_description: text,
246 })
247}
248
249fn extract_text_content(result: &serde_json::Value) -> Option<String> {
250 if let Some(content) = result.get("content").and_then(|c| c.as_array()) {
251 for item in content {
252 if let Some(text) = item.get("text").and_then(|t| t.as_str()) {
253 return Some(text.to_string());
254 }
255 }
256 }
257 if let Some(s) = result.as_str() {
259 return Some(s.to_string());
260 }
261 None
262}