clawft_kernel/wasm_runner/
tools_agent.rs1use std::sync::Arc;
4
5use super::catalog::builtin_tool_catalog;
6use super::registry::BuiltinTool;
7use super::types::*;
8
9pub struct AgentSpawnTool {
14 spec: BuiltinToolSpec,
15 process_table: Arc<crate::process::ProcessTable>,
16}
17
18impl AgentSpawnTool {
19 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
20 let catalog = builtin_tool_catalog();
21 let spec = catalog
22 .into_iter()
23 .find(|s| s.name == "agent.spawn")
24 .expect("agent.spawn must be in catalog");
25 Self {
26 spec,
27 process_table,
28 }
29 }
30}
31
32impl BuiltinTool for AgentSpawnTool {
33 fn name(&self) -> &str {
34 "agent.spawn"
35 }
36
37 fn spec(&self) -> &BuiltinToolSpec {
38 &self.spec
39 }
40
41 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
42 let agent_id = args
43 .get("agent_id")
44 .and_then(|v| v.as_str())
45 .ok_or_else(|| ToolError::InvalidArgs("missing 'agent_id' parameter".into()))?;
46
47 let backend = args
48 .get("backend")
49 .and_then(|v| v.as_str())
50 .unwrap_or("native");
51
52 if backend == "wasm" {
53 return Err(ToolError::ExecutionFailed(
54 "WASM backend not yet available for agent.spawn".into(),
55 ));
56 }
57
58 let entry = crate::process::ProcessEntry {
62 pid: 0, agent_id: agent_id.to_string(),
64 state: crate::process::ProcessState::Running,
65 capabilities: crate::capability::AgentCapabilities::default(),
66 resource_usage: crate::process::ResourceUsage::default(),
67 cancel_token: crate::process::CancellationToken::new(),
68 parent_pid: None,
69 };
70
71 let pid = self
72 .process_table
73 .insert(entry)
74 .map_err(|e| ToolError::ExecutionFailed(format!("spawn failed: {e}")))?;
75
76 Ok(serde_json::json!({
77 "pid": pid,
78 "agent_id": agent_id,
79 "state": "running",
80 }))
81 }
82}
83
84pub struct AgentStopTool {
86 spec: BuiltinToolSpec,
87 process_table: Arc<crate::process::ProcessTable>,
88}
89
90impl AgentStopTool {
91 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
92 let spec = builtin_tool_catalog()
93 .into_iter()
94 .find(|s| s.name == "agent.stop")
95 .unwrap();
96 Self {
97 spec,
98 process_table,
99 }
100 }
101}
102
103impl BuiltinTool for AgentStopTool {
104 fn name(&self) -> &str {
105 "agent.stop"
106 }
107 fn spec(&self) -> &BuiltinToolSpec {
108 &self.spec
109 }
110 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
111 let pid = args
112 .get("pid")
113 .and_then(|v| v.as_u64())
114 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
115 let entry = self
116 .process_table
117 .get(pid)
118 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
119 entry.cancel_token.cancel();
120 self.process_table
121 .update_state(pid, crate::process::ProcessState::Stopping)
122 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
123 Ok(serde_json::json!({"stopped": pid, "agent_id": entry.agent_id}))
124 }
125}
126
127pub struct AgentListTool {
129 spec: BuiltinToolSpec,
130 process_table: Arc<crate::process::ProcessTable>,
131}
132
133impl AgentListTool {
134 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
135 let spec = builtin_tool_catalog()
136 .into_iter()
137 .find(|s| s.name == "agent.list")
138 .unwrap();
139 Self {
140 spec,
141 process_table,
142 }
143 }
144}
145
146impl BuiltinTool for AgentListTool {
147 fn name(&self) -> &str {
148 "agent.list"
149 }
150 fn spec(&self) -> &BuiltinToolSpec {
151 &self.spec
152 }
153 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
154 let list = self.process_table.list();
155 let entries: Vec<serde_json::Value> = list
156 .iter()
157 .map(|e| {
158 serde_json::json!({
159 "pid": e.pid,
160 "agent_id": e.agent_id,
161 "state": format!("{:?}", e.state),
162 })
163 })
164 .collect();
165 Ok(serde_json::json!({"agents": entries, "count": entries.len()}))
166 }
167}
168
169pub struct AgentInspectTool {
171 spec: BuiltinToolSpec,
172 process_table: Arc<crate::process::ProcessTable>,
173}
174
175impl AgentInspectTool {
176 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
177 let spec = builtin_tool_catalog()
178 .into_iter()
179 .find(|s| s.name == "agent.inspect")
180 .unwrap();
181 Self {
182 spec,
183 process_table,
184 }
185 }
186}
187
188impl BuiltinTool for AgentInspectTool {
189 fn name(&self) -> &str {
190 "agent.inspect"
191 }
192 fn spec(&self) -> &BuiltinToolSpec {
193 &self.spec
194 }
195 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
196 let pid = args
197 .get("pid")
198 .and_then(|v| v.as_u64())
199 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
200 let entry = self
201 .process_table
202 .get(pid)
203 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
204 Ok(serde_json::json!({
205 "pid": entry.pid,
206 "agent_id": entry.agent_id,
207 "state": format!("{:?}", entry.state),
208 "parent_pid": entry.parent_pid,
209 "resource_usage": {
210 "messages_sent": entry.resource_usage.messages_sent,
211 "tool_calls": entry.resource_usage.tool_calls,
212 "cpu_time_ms": entry.resource_usage.cpu_time_ms,
213 },
214 "capabilities": {
215 "can_spawn": entry.capabilities.can_spawn,
216 "can_ipc": entry.capabilities.can_ipc,
217 "can_exec_tools": entry.capabilities.can_exec_tools,
218 "can_network": entry.capabilities.can_network,
219 },
220 }))
221 }
222}
223
224#[cfg(feature = "native")]
226pub struct AgentSendTool {
227 spec: BuiltinToolSpec,
228 process_table: Arc<crate::process::ProcessTable>,
229 a2a: Arc<crate::a2a::A2ARouter>,
230}
231
232#[cfg(feature = "native")]
233impl AgentSendTool {
234 pub fn new(
235 process_table: Arc<crate::process::ProcessTable>,
236 a2a: Arc<crate::a2a::A2ARouter>,
237 ) -> Self {
238 let spec = builtin_tool_catalog()
239 .into_iter()
240 .find(|s| s.name == "agent.send")
241 .unwrap();
242 Self {
243 spec,
244 process_table,
245 a2a,
246 }
247 }
248}
249
250#[cfg(feature = "native")]
251impl BuiltinTool for AgentSendTool {
252 fn name(&self) -> &str {
253 "agent.send"
254 }
255 fn spec(&self) -> &BuiltinToolSpec {
256 &self.spec
257 }
258 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
259 let pid = args
260 .get("pid")
261 .and_then(|v| v.as_u64())
262 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
263 let message = args
264 .get("message")
265 .cloned()
266 .ok_or_else(|| ToolError::InvalidArgs("missing 'message'".into()))?;
267 let _ = self
269 .process_table
270 .get(pid)
271 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
272 let msg = crate::ipc::KernelMessage::new(
273 0, crate::ipc::MessageTarget::Process(pid),
275 crate::ipc::MessagePayload::Json(message),
276 );
277 let msg_id = msg.id.clone();
278 let a2a = self.a2a.clone();
281 std::thread::spawn(move || {
282 let rt = tokio::runtime::Builder::new_current_thread()
283 .enable_all()
284 .build()
285 .unwrap();
286 rt.block_on(async { a2a.send(msg).await })
287 })
288 .join()
289 .map_err(|_| ToolError::ExecutionFailed("send thread panicked".into()))?
290 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
291 Ok(serde_json::json!({"sent": true, "pid": pid, "msg_id": msg_id}))
292 }
293}
294
295pub struct AgentSuspendTool {
297 spec: BuiltinToolSpec,
298 process_table: Arc<crate::process::ProcessTable>,
299}
300
301impl AgentSuspendTool {
302 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
303 let spec = builtin_tool_catalog()
304 .into_iter()
305 .find(|s| s.name == "agent.suspend")
306 .unwrap();
307 Self {
308 spec,
309 process_table,
310 }
311 }
312}
313
314impl BuiltinTool for AgentSuspendTool {
315 fn name(&self) -> &str {
316 "agent.suspend"
317 }
318 fn spec(&self) -> &BuiltinToolSpec {
319 &self.spec
320 }
321 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
322 let pid = args
323 .get("pid")
324 .and_then(|v| v.as_u64())
325 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
326 self.process_table
327 .update_state(pid, crate::process::ProcessState::Suspended)
328 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
329 Ok(serde_json::json!({"suspended": pid}))
330 }
331}
332
333pub struct AgentResumeTool {
335 spec: BuiltinToolSpec,
336 process_table: Arc<crate::process::ProcessTable>,
337}
338
339impl AgentResumeTool {
340 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
341 let spec = builtin_tool_catalog()
342 .into_iter()
343 .find(|s| s.name == "agent.resume")
344 .unwrap();
345 Self {
346 spec,
347 process_table,
348 }
349 }
350}
351
352impl BuiltinTool for AgentResumeTool {
353 fn name(&self) -> &str {
354 "agent.resume"
355 }
356 fn spec(&self) -> &BuiltinToolSpec {
357 &self.spec
358 }
359 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
360 let pid = args
361 .get("pid")
362 .and_then(|v| v.as_u64())
363 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
364 self.process_table
365 .update_state(pid, crate::process::ProcessState::Running)
366 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
367 Ok(serde_json::json!({"resumed": pid}))
368 }
369}
370
371pub struct IpcSendTool {
379 spec: BuiltinToolSpec,
380}
381
382impl Default for IpcSendTool {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
388impl IpcSendTool {
389 pub fn new() -> Self {
390 let catalog = builtin_tool_catalog();
391 let spec = catalog
392 .into_iter()
393 .find(|s| s.name == "ipc.send")
394 .expect("ipc.send must be in catalog");
395 Self { spec }
396 }
397}
398
399impl BuiltinTool for IpcSendTool {
400 fn name(&self) -> &str {
401 "ipc.send"
402 }
403 fn spec(&self) -> &BuiltinToolSpec {
404 &self.spec
405 }
406 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
407 Err(ToolError::ExecutionFailed(
409 "ipc.send requires async kernel context".into(),
410 ))
411 }
412}
413
414pub struct IpcSubscribeTool {
418 spec: BuiltinToolSpec,
419}
420
421impl Default for IpcSubscribeTool {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427impl IpcSubscribeTool {
428 pub fn new() -> Self {
429 let catalog = builtin_tool_catalog();
430 let spec = catalog
431 .into_iter()
432 .find(|s| s.name == "ipc.subscribe")
433 .expect("ipc.subscribe must be in catalog");
434 Self { spec }
435 }
436}
437
438impl BuiltinTool for IpcSubscribeTool {
439 fn name(&self) -> &str {
440 "ipc.subscribe"
441 }
442 fn spec(&self) -> &BuiltinToolSpec {
443 &self.spec
444 }
445 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
446 Err(ToolError::ExecutionFailed(
448 "ipc.subscribe requires async kernel context".into(),
449 ))
450 }
451}