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 spawn
  • SCARAB_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),
}
}