Agent SDK
The libagent crate provides a high-level Rust SDK for agent binaries. The entry point is Agent::from_env(), which reads environment variables injected by agentd at spawn time.
Adding the Dependency
In your agent's Cargo.toml:
[dependencies]
libagent = { path = "../libagent" } # or published crate path
tokio = { version = "1", features = ["full"] }
serde_json = "1"
Quickstart
use libagent::agent::Agent; use libagent::types::LifecycleState; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Connect to agentd using environment variables let mut agent = Agent::from_env().await?; // Declare the Plan phase agent.transition(LifecycleState::Plan).await?; // Transition to Act agent.transition(LifecycleState::Act).await?; // Invoke a tool let result = agent.invoke_tool("lm.complete", json!({ "prompt": "Summarize the key benefits of renewable energy in 3 bullet points.", "system": "You are a helpful research assistant." })).await?; println!("{}", result["text"]); // Record an observation agent.observe( "completed_task", "Summary generated successfully", vec!["llm".to_string(), "success".to_string()], ).await?; // Transition to Terminate agent.transition(LifecycleState::Terminate).await?; Ok(()) }
API Reference
Agent::from_env() -> Result<Agent, AgentError>
Connects to agentd using:
SCARAB_AGENT_ID- UUID assigned at spawnSCARAB_SOCKET- path to agentd socket (defaults to/run/agentd/agentd.sock)
Fails with AgentError::EnvMissing if SCARAB_AGENT_ID is not set.
agent.transition(state) -> Result<(), AgentError>
Transitions the agent to the given lifecycle state.
#![allow(unused)] fn main() { agent.transition(LifecycleState::Plan).await?; agent.transition(LifecycleState::Act).await?; agent.transition(LifecycleState::Observe).await?; agent.transition(LifecycleState::Terminate).await?; }
agent.invoke_tool(name, input) -> Result<Value, AgentError>
Invokes a tool by name with a JSON input. Returns the tool's JSON output on success.
#![allow(unused)] fn main() { let out = agent.invoke_tool("echo", json!({"message": "hello"})).await?; }
agent.observe(action, result, tags) -> Result<(), AgentError>
Appends a structured observation to the agent's observation log.
#![allow(unused)] fn main() { agent.observe("fetched_url", "200 OK, 1.2KB", vec!["web".into()]).await?; }
agent.memory_get(key) -> Result<Option<Value>, AgentError>
Reads a value from persistent memory.
#![allow(unused)] fn main() { let config = agent.memory_get("config").await?; }
agent.memory_set(key, value) -> Result<(), AgentError>
Writes a value to persistent memory.
#![allow(unused)] fn main() { agent.memory_set("last_run", json!({"timestamp": "2026-02-22T12:00:00Z"})).await?; }
agent.plan(steps) -> Result<(), AgentError>
Declares a structured plan for the upcoming Act phase.
#![allow(unused)] fn main() { use libagent::types::PlanStep; agent.plan(vec![ PlanStep { description: "Search for news".to_string(), tool: Some("web.search".to_string()) }, PlanStep { description: "Summarize findings".to_string(), tool: Some("lm.complete".to_string()) }, ]).await?; }
agent.revise_plan(steps, reason) -> Result<(), AgentError>
Revises the current plan with an explicit reason (creates an audit entry).
#![allow(unused)] fn main() { agent.revise_plan(new_steps, "Found more relevant sources".to_string()).await?; }
agent.pending_escalations() -> Result<Vec<Value>, AgentError>
Polls for escalation messages addressed to this agent on the bus.
#![allow(unused)] fn main() { let escalations = agent.pending_escalations().await?; for esc in escalations { eprintln!("Escalation received: {}", esc); } }
Error Handling
#![allow(unused)] fn main() { use libagent::agent::AgentError; match agent.invoke_tool("web.fetch", input).await { Ok(output) => { /* success */ } Err(AgentError::ToolFailed(msg)) => { eprintln!("Tool failed: {}", msg); } Err(AgentError::Ipc(e)) => { eprintln!("IPC error: {}", e); } Err(e) => eprintln!("Error: {}", e), } }