Invoking Tools
Tools are invoked via agent.invoke_tool(name, input). The input and output are JSON Value objects.
Basic Invocation
#![allow(unused)] fn main() { let output = agent.invoke_tool("echo", json!({"message": "hello"})).await?; println!("{}", output["message"]); // "hello" }
Error Handling
#![allow(unused)] fn main() { use libagent::agent::AgentError; match agent.invoke_tool("web.fetch", json!({"url": "https://example.com"})).await { Ok(output) => { let status = output["status"].as_i64().unwrap_or(0); let body = output["body"].as_str().unwrap_or(""); println!("HTTP {}: {} bytes", status, body.len()); } Err(AgentError::ToolFailed(msg)) if msg.contains("access denied") => { eprintln!("Missing capability for web.fetch"); } Err(AgentError::ToolFailed(msg)) if msg.contains("requires approval") => { eprintln!("Waiting for human approval..."); // The call blocks until approved - no special handling needed } Err(e) => eprintln!("Tool error: {}", e), } }
Tool Input/Output Schemas
Each tool declares input and output JSON schemas. View them with:
ash tools schema lm.complete
ash tools schema fs.write
Using Secrets in Tool Calls
Reference secrets with handle syntax in JSON strings:
#![allow(unused)] fn main() { let output = agent.invoke_tool("web.fetch", json!({ "url": "https://api.openai.com/v1/models", "headers": { "Authorization": "Bearer {{secret:openai-key}}" } })).await?; }
The daemon substitutes the secret value before calling the tool handler.
Tool Invocation in the Act Phase
Tool calls should be made in the Act lifecycle state. While the daemon does not strictly enforce this in all cases, Act is the intended state for tool execution:
#![allow(unused)] fn main() { agent.transition(LifecycleState::Act).await?; let result = agent.invoke_tool("lm.complete", json!({/* ... */})).await?; agent.transition(LifecycleState::Observe).await?; }
Available Built-in Tools
| Tool | Capability Required | Description |
|---|---|---|
echo | tool.invoke:echo | Returns input as-is |
lm.complete | tool.invoke:lm.complete | LLM completion via OpenRouter |
lm.embed | tool.invoke:lm.embed | Dense vector embedding |
fs.read | tool.invoke:fs.read | Read a file |
fs.write | tool.invoke:fs.write | Write or append to a file |
fs.list | tool.invoke:fs.list | List directory contents |
fs.delete | tool.invoke:fs.delete | Delete a file |
web.fetch | tool.invoke:web.fetch | Fetch a URL |
web.search | tool.invoke:web.search | Web search via DuckDuckGo |
sandbox.exec | sandbox.exec | Execute code in a sandbox |
agent.info | tool.invoke:agent.info | Get agent metadata |
sensitive-op | tool.invoke:sensitive-op | Human-approval-required operation |
Direct IPC (Advanced)
The Agent SDK wraps AgentdClient. For advanced use cases (custom IPC requests), access the client directly:
#![allow(unused)] fn main() { use libagent::ipc::Request; let response = agent.client.send(Request::ListAgents).await?; }