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 { spec, process_table }
26 }
27}
28
29impl BuiltinTool for AgentSpawnTool {
30 fn name(&self) -> &str {
31 "agent.spawn"
32 }
33
34 fn spec(&self) -> &BuiltinToolSpec {
35 &self.spec
36 }
37
38 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
39 let agent_id = args
40 .get("agent_id")
41 .and_then(|v| v.as_str())
42 .ok_or_else(|| ToolError::InvalidArgs("missing 'agent_id' parameter".into()))?;
43
44 let backend = args
45 .get("backend")
46 .and_then(|v| v.as_str())
47 .unwrap_or("native");
48
49 if backend == "wasm" {
50 return Err(ToolError::ExecutionFailed(
51 "WASM backend not yet available for agent.spawn".into(),
52 ));
53 }
54
55 let entry = crate::process::ProcessEntry {
59 pid: 0, agent_id: agent_id.to_string(),
61 state: crate::process::ProcessState::Running,
62 capabilities: crate::capability::AgentCapabilities::default(),
63 resource_usage: crate::process::ResourceUsage::default(),
64 cancel_token: tokio_util::sync::CancellationToken::new(),
65 parent_pid: None,
66 };
67
68 let pid = self
69 .process_table
70 .insert(entry)
71 .map_err(|e| ToolError::ExecutionFailed(format!("spawn failed: {e}")))?;
72
73 Ok(serde_json::json!({
74 "pid": pid,
75 "agent_id": agent_id,
76 "state": "running",
77 }))
78 }
79}
80
81pub struct AgentStopTool {
83 spec: BuiltinToolSpec,
84 process_table: Arc<crate::process::ProcessTable>,
85}
86
87impl AgentStopTool {
88 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
89 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.stop").unwrap();
90 Self { spec, process_table }
91 }
92}
93
94impl BuiltinTool for AgentStopTool {
95 fn name(&self) -> &str { "agent.stop" }
96 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
97 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
98 let pid = args.get("pid").and_then(|v| v.as_u64())
99 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
100 let entry = self.process_table.get(pid)
101 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
102 entry.cancel_token.cancel();
103 self.process_table.update_state(pid, crate::process::ProcessState::Stopping)
104 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
105 Ok(serde_json::json!({"stopped": pid, "agent_id": entry.agent_id}))
106 }
107}
108
109pub struct AgentListTool {
111 spec: BuiltinToolSpec,
112 process_table: Arc<crate::process::ProcessTable>,
113}
114
115impl AgentListTool {
116 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
117 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.list").unwrap();
118 Self { spec, process_table }
119 }
120}
121
122impl BuiltinTool for AgentListTool {
123 fn name(&self) -> &str { "agent.list" }
124 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
125 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
126 let list = self.process_table.list();
127 let entries: Vec<serde_json::Value> = list.iter().map(|e| {
128 serde_json::json!({
129 "pid": e.pid,
130 "agent_id": e.agent_id,
131 "state": format!("{:?}", e.state),
132 })
133 }).collect();
134 Ok(serde_json::json!({"agents": entries, "count": entries.len()}))
135 }
136}
137
138pub struct AgentInspectTool {
140 spec: BuiltinToolSpec,
141 process_table: Arc<crate::process::ProcessTable>,
142}
143
144impl AgentInspectTool {
145 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
146 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.inspect").unwrap();
147 Self { spec, process_table }
148 }
149}
150
151impl BuiltinTool for AgentInspectTool {
152 fn name(&self) -> &str { "agent.inspect" }
153 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
154 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
155 let pid = args.get("pid").and_then(|v| v.as_u64())
156 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
157 let entry = self.process_table.get(pid)
158 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
159 Ok(serde_json::json!({
160 "pid": entry.pid,
161 "agent_id": entry.agent_id,
162 "state": format!("{:?}", entry.state),
163 "parent_pid": entry.parent_pid,
164 "resource_usage": {
165 "messages_sent": entry.resource_usage.messages_sent,
166 "tool_calls": entry.resource_usage.tool_calls,
167 "cpu_time_ms": entry.resource_usage.cpu_time_ms,
168 },
169 "capabilities": {
170 "can_spawn": entry.capabilities.can_spawn,
171 "can_ipc": entry.capabilities.can_ipc,
172 "can_exec_tools": entry.capabilities.can_exec_tools,
173 "can_network": entry.capabilities.can_network,
174 },
175 }))
176 }
177}
178
179pub struct AgentSendTool {
181 spec: BuiltinToolSpec,
182 process_table: Arc<crate::process::ProcessTable>,
183 a2a: Arc<crate::a2a::A2ARouter>,
184}
185
186impl AgentSendTool {
187 pub fn new(
188 process_table: Arc<crate::process::ProcessTable>,
189 a2a: Arc<crate::a2a::A2ARouter>,
190 ) -> Self {
191 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.send").unwrap();
192 Self { spec, process_table, a2a }
193 }
194}
195
196impl BuiltinTool for AgentSendTool {
197 fn name(&self) -> &str { "agent.send" }
198 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
199 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
200 let pid = args.get("pid").and_then(|v| v.as_u64())
201 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
202 let message = args.get("message").cloned()
203 .ok_or_else(|| ToolError::InvalidArgs("missing 'message'".into()))?;
204 let _ = self.process_table.get(pid)
206 .ok_or_else(|| ToolError::NotFound(format!("pid {pid}")))?;
207 let msg = crate::ipc::KernelMessage::new(
208 0, crate::ipc::MessageTarget::Process(pid),
210 crate::ipc::MessagePayload::Json(message),
211 );
212 let msg_id = msg.id.clone();
213 let a2a = self.a2a.clone();
216 std::thread::spawn(move || {
217 let rt = tokio::runtime::Builder::new_current_thread()
218 .enable_all()
219 .build()
220 .unwrap();
221 rt.block_on(async { a2a.send(msg).await })
222 }).join()
223 .map_err(|_| ToolError::ExecutionFailed("send thread panicked".into()))?
224 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
225 Ok(serde_json::json!({"sent": true, "pid": pid, "msg_id": msg_id}))
226 }
227}
228
229pub struct AgentSuspendTool {
231 spec: BuiltinToolSpec,
232 process_table: Arc<crate::process::ProcessTable>,
233}
234
235impl AgentSuspendTool {
236 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
237 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.suspend").unwrap();
238 Self { spec, process_table }
239 }
240}
241
242impl BuiltinTool for AgentSuspendTool {
243 fn name(&self) -> &str { "agent.suspend" }
244 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
245 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
246 let pid = args.get("pid").and_then(|v| v.as_u64())
247 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
248 self.process_table.update_state(pid, crate::process::ProcessState::Suspended)
249 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
250 Ok(serde_json::json!({"suspended": pid}))
251 }
252}
253
254pub struct AgentResumeTool {
256 spec: BuiltinToolSpec,
257 process_table: Arc<crate::process::ProcessTable>,
258}
259
260impl AgentResumeTool {
261 pub fn new(process_table: Arc<crate::process::ProcessTable>) -> Self {
262 let spec = builtin_tool_catalog().into_iter().find(|s| s.name == "agent.resume").unwrap();
263 Self { spec, process_table }
264 }
265}
266
267impl BuiltinTool for AgentResumeTool {
268 fn name(&self) -> &str { "agent.resume" }
269 fn spec(&self) -> &BuiltinToolSpec { &self.spec }
270 fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
271 let pid = args.get("pid").and_then(|v| v.as_u64())
272 .ok_or_else(|| ToolError::InvalidArgs("missing 'pid'".into()))?;
273 self.process_table.update_state(pid, crate::process::ProcessState::Running)
274 .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
275 Ok(serde_json::json!({"resumed": pid}))
276 }
277}
278
279pub struct IpcSendTool {
287 spec: BuiltinToolSpec,
288}
289
290impl Default for IpcSendTool {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296impl IpcSendTool {
297 pub fn new() -> Self {
298 let catalog = builtin_tool_catalog();
299 let spec = catalog
300 .into_iter()
301 .find(|s| s.name == "ipc.send")
302 .expect("ipc.send must be in catalog");
303 Self { spec }
304 }
305}
306
307impl BuiltinTool for IpcSendTool {
308 fn name(&self) -> &str { "ipc.send" }
309 fn spec(&self) -> &BuiltinToolSpec {
310 &self.spec
311 }
312 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
313 Err(ToolError::ExecutionFailed("ipc.send requires async kernel context".into()))
315 }
316}
317
318pub struct IpcSubscribeTool {
322 spec: BuiltinToolSpec,
323}
324
325impl Default for IpcSubscribeTool {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330
331impl IpcSubscribeTool {
332 pub fn new() -> Self {
333 let catalog = builtin_tool_catalog();
334 let spec = catalog
335 .into_iter()
336 .find(|s| s.name == "ipc.subscribe")
337 .expect("ipc.subscribe must be in catalog");
338 Self { spec }
339 }
340}
341
342impl BuiltinTool for IpcSubscribeTool {
343 fn name(&self) -> &str { "ipc.subscribe" }
344 fn spec(&self) -> &BuiltinToolSpec {
345 &self.spec
346 }
347 fn execute(&self, _args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
348 Err(ToolError::ExecutionFailed("ipc.subscribe requires async kernel context".into()))
350 }
351}