> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK overview

> Embed Meerkat as a Rust library: the full engine with no subprocess overhead.

The Rust SDK is the primary interface. The Python/TypeScript SDKs and all API servers are thin wrappers over this same engine. The default embedding path is runtime-backed `SessionService`, which keeps Rust aligned with the same session lifecycle semantics used by every other Meerkat surface while still avoiding subprocess or JSON-RPC overhead.

## Method overview

| Area         | Method / Type                                                 | Purpose                                               |
| ------------ | ------------------------------------------------------------- | ----------------------------------------------------- |
| **Setup**    | `Config::load()`                                              | Load configuration from disk                          |
|              | `AgentFactory::new(store_root)`                               | Create a factory for building agents                  |
|              | `open_realm_persistence_in(...)`                              | Open a realm-backed persistence bundle                |
|              | `build_persistent_service(factory, config, cap, persistence)` | Build the runtime-backed persistent session substrate |
|              | `build_ephemeral_service(factory, config, cap)`               | Build an in-memory Queue-only substrate               |
| **Sessions** | `service.create_session(req)`                                 | Create a session and run the first turn               |
|              | `service.start_turn(id, req)`                                 | Continue an existing session                          |
|              | `service.read(id)`                                            | Read session state                                    |
|              | `service.list()`                                              | List active sessions                                  |
|              | `service.archive(id)`                                         | Remove a session                                      |
| **Agent**    | `agent.run(prompt)`                                           | Run agent with a prompt                               |
|              | `agent.run_with_events(prompt, tx)`                           | Run with event streaming                              |
|              | `agent.cancel()`                                              | Cancel the current run                                |

## Installation

<Steps>
  <Step title="Add the dependency">
    ```toml theme={null}
    [dependencies]
    meerkat = "0.6.6"
    tokio = { version = "1", features = ["full"] }
    ```
  </Step>

  <Step title="Choose feature flags">
    The default includes all three LLM providers and nothing else — add subsystems as needed:

    <CodeGroup>
      ```toml Default (all providers, no storage) theme={null}
      [dependencies]
      meerkat = "0.6.6"
      ```

      ```toml Add persistence theme={null}
      [dependencies]
      meerkat = { version = "0.6.6", features = ["sqlite-store", "session-store"] }
      ```

      ```toml Full harness theme={null}
      [dependencies]
      meerkat = { version = "0.6.6", features = [
          "sqlite-store", "session-store", "session-compaction",
          "memory-store-session", "comms", "mcp", "skills", "live"
      ] }
      ```

      ```toml Minimal (single provider) theme={null}
      [dependencies]
      meerkat = { version = "0.6.6", default-features = false, features = ["anthropic"] }
      ```
    </CodeGroup>
  </Step>
</Steps>

<Accordion title="Feature flag reference">
  | Feature                | Description                                             | Default |
  | ---------------------- | ------------------------------------------------------- | ------- |
  | `anthropic`            | Anthropic Claude API client                             | Yes     |
  | `openai`               | OpenAI API client                                       | Yes     |
  | `gemini`               | Google Gemini API client                                | Yes     |
  | `all-providers`        | Shorthand for all three providers                       | No      |
  | `sqlite-store`         | SQLite-backed persistent realms                         | No      |
  | `jsonl-store`          | File-based session persistence                          | No      |
  | `memory-store`         | In-memory session storage (testing)                     | No      |
  | `session-store`        | Persistent session lifecycle support                    | No      |
  | `session-compaction`   | Auto-compact long conversations                         | No      |
  | `memory-store-session` | Semantic memory indexing                                | No      |
  | `comms`                | Ed25519 inter-agent messaging                           | No      |
  | `mcp`                  | MCP protocol client and tool routing                    | No      |
  | `live`                 | Live-channel orchestration and realtime adapter helpers | No      |
  | `skills`               | Composable knowledge packs                              | No      |
</Accordion>

***

## Quick start

Production surfaces (CLI, REST, RPC, MCP) use the **runtime-backed** path where `SessionService` is substrate and `MeerkatMachine` owns keep-alive, Queue/Steer routing, and comms drain. See the [JSON-RPC API](/api/rpc) or [REST API](/api/rest) for those entry points.

For the main production embedding path, use the runtime-backed persistent flow:

```rust theme={null}
use meerkat::{
    AgentFactory, Config, CreateSessionRequest, SessionService,
    build_persistent_service, open_realm_persistence_in,
    DeferredPromptPolicy,
};
use meerkat_core::service::InitialTurnPolicy;
use meerkat_store::RealmBackend;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::load().await?;
    let realms_root = std::env::current_dir()?.join(".rkat").join("realms");
    let (_manifest, persistence) = open_realm_persistence_in(
        &realms_root,
        "team-alpha",
        Some(RealmBackend::Sqlite),
        None,
    ).await?;
    let factory = AgentFactory::new(realms_root.clone()).runtime_root(realms_root);
    let service = build_persistent_service(factory, config, 64, persistence);

    let result = service.create_session(CreateSessionRequest {
        model: "claude-sonnet-4-6".into(),
        prompt: "What is the capital of France?".into(),
        render_metadata: None,
        system_prompt: Some("You are a helpful assistant.".into()),
        max_tokens: Some(1024),
        event_tx: None,
        skill_references: None,
        initial_turn: InitialTurnPolicy::RunImmediately,
        deferred_prompt_policy: DeferredPromptPolicy::Discard,
        build: None,
        labels: None,
    }).await?;

    println!("Response: {}", result.text);
    println!("Session ID: {}", result.session_id);
    Ok(())
}
```

For **testing or embedded use** where runtime semantics are intentionally not needed, `build_ephemeral_service` remains available as a direct Queue-only substrate.

***

## Sessions

`SessionService` is the canonical lifecycle API. All surfaces (CLI, REST, MCP, RPC) route through it.

### Multi-turn conversations

```rust theme={null}
use meerkat::{
    CreateSessionRequest, DeferredPromptPolicy, SessionService,
    StartTurnRequest, StartTurnRuntimeSemantics,
};
use meerkat_core::service::InitialTurnPolicy;

// Turn 1: create session
let result = service.create_session(CreateSessionRequest {
    model: "claude-sonnet-4-6".into(),
    prompt: "My name is Alice.".into(),
    render_metadata: None,
    system_prompt: Some("You are a helpful assistant with memory.".into()),
    max_tokens: None,
    event_tx: None,
    skill_references: None,
    initial_turn: InitialTurnPolicy::RunImmediately,
    deferred_prompt_policy: DeferredPromptPolicy::Discard,
    build: None,
    labels: None,
}).await?;

let session_id = result.session_id;

// Turn 2: agent remembers "Alice"
let result = service.start_turn(&session_id, StartTurnRequest {
    prompt: "What's my name?".into(),
    system_prompt: None,
    event_tx: None,
    runtime: StartTurnRuntimeSemantics::default(),
}).await?;

// Read session state
let view = service.read(&session_id).await?;
println!("Messages: {}", view.state.message_count);

// Archive when done
service.archive(&session_id).await?;
```

### Deferred first-turn system prompt override

```rust theme={null}
use meerkat::{
    CreateSessionRequest, DeferredPromptPolicy, SessionService,
    StartTurnRequest, StartTurnRuntimeSemantics,
};
use meerkat_core::service::InitialTurnPolicy;

let created = service.create_session(CreateSessionRequest {
    model: "claude-sonnet-4-6".into(),
    prompt: "Let's wait before we run.".into(),
    render_metadata: None,
    system_prompt: Some("You are the original prompt.".into()),
    max_tokens: None,
    event_tx: None,
    skill_references: None,
    initial_turn: InitialTurnPolicy::Defer,
    deferred_prompt_policy: DeferredPromptPolicy::Stage,
    build: None,
    labels: None,
}).await?;

let result = service.start_turn(&created.session_id, StartTurnRequest {
    prompt: "Now run with the first-turn override.".into(),
    system_prompt: Some("You are the deferred first-turn override.".into()),
    event_tx: None,
    runtime: StartTurnRuntimeSemantics::default(),
}).await?;
```

`system_prompt` on `StartTurnRequest` is only supported for a deferred session's
first turn. Once the session has existing history/messages, turn-time
`system_prompt` overrides are rejected.

### Error handling

```rust theme={null}
use meerkat::SessionError;

match service.start_turn(&id, req).await {
    Ok(result) => println!("Response: {}", result.text),
    Err(SessionError::NotFound { id }) => println!("Session {} not found", id),
    Err(SessionError::Busy { id }) => println!("Session {} is busy, retry later", id),
    Err(e) => println!("Error: {}", e),
}
```

***

## Direct agent APIs

<Warning>
  `Agent::run(...)` and `AgentBuilder` are expert-level escape hatches. Prefer `SessionService` for normal embedding so your Rust code follows the same runtime-backed session semantics as CLI, REST, RPC, MCP, Python, and TypeScript.
</Warning>

## Running agents directly

### Basic run

```rust theme={null}
let result = agent.run("What is 2 + 2?".into()).await?;
println!("Answer: {}", result.text);
```

### Run with event streaming

```rust theme={null}
use tokio::sync::mpsc;
use meerkat::AgentEvent;

let (tx, mut rx) = mpsc::channel::<AgentEvent>(100);

tokio::spawn(async move {
    while let Some(event) = rx.recv().await {
        match event {
            AgentEvent::TextDelta { delta } => print!("{}", delta),
            AgentEvent::ToolExecutionStarted { name, .. } => {
                println!("[Calling {}...]", name);
            }
            AgentEvent::TurnCompleted { usage, .. } => {
                println!("\n[Tokens: {}]", usage.total_tokens());
            }
            _ => {}
        }
    }
});

let result = agent.run_with_events("Tell me a story".into(), tx).await?;
```

### Agent methods

| Method                        | Description                                                 |
| ----------------------------- | ----------------------------------------------------------- |
| `run(prompt)`                 | Run agent with a `ContentInput` prompt (text or multimodal) |
| `run_with_events(prompt, tx)` | Run with event streaming; prompt is `ContentInput`          |
| `session()`                   | Get current session (read-only)                             |
| `budget()`                    | Get current budget tracker                                  |
| `state()`                     | Get current loop state                                      |
| `cancel()`                    | Cancel the current run                                      |

### Error handling

```rust theme={null}
use meerkat::AgentError;

match agent.run("prompt".into()).await {
    Ok(result) => println!("Success: {}", result.text),
    Err(AgentError::LlmError(msg)) => println!("LLM error: {}", msg),
    Err(AgentError::TokenBudgetExceeded { used, limit }) => {
        println!("Token budget exceeded: {} / {}", used, limit);
    }
    Err(e) => println!("Other error: {}", e),
}
```

***

## Events

<Accordion title="All event types">
  ```rust theme={null}
  use meerkat::AgentEvent;

  match event {
      // Session lifecycle
      AgentEvent::RunStarted { session_id, prompt } => {}
      AgentEvent::RunCompleted { session_id, result, usage } => {}
      AgentEvent::RunFailed { session_id, error } => {}

      // Hook lifecycle
      AgentEvent::HookStarted { hook_id, point } => {}
      AgentEvent::HookCompleted { hook_id, point, duration_ms } => {}
      AgentEvent::HookFailed { hook_id, point, error } => {}
      AgentEvent::HookDenied { hook_id, point, reason_code, message, .. } => {}

      // LLM interaction
      AgentEvent::TurnStarted { turn_number } => {}
      AgentEvent::ReasoningDelta { delta } => {}
      AgentEvent::ReasoningComplete { content } => {}
      AgentEvent::TextDelta { delta } => {}
      AgentEvent::TextComplete { content } => {}
      AgentEvent::ToolCallRequested { id, name, args } => {}
      AgentEvent::ToolResultReceived { id, name, content, is_error } => {}
      AgentEvent::TurnCompleted { stop_reason, usage } => {}

      // Tool execution
      AgentEvent::ToolExecutionStarted { id, name } => {}
      AgentEvent::ToolExecutionCompleted { id, name, result, content, is_error, duration_ms } => {}
      AgentEvent::ToolExecutionTimedOut { id, name, timeout_ms } => {}

      // Compaction
      AgentEvent::CompactionStarted { input_tokens, estimated_history_tokens, message_count } => {}
      AgentEvent::CompactionCompleted { summary_tokens, messages_before, messages_after } => {}
      AgentEvent::CompactionFailed { error } => {}

      // Budget
      AgentEvent::BudgetWarning { budget_type, used, limit, percent } => {}

      // Retry
      AgentEvent::Retrying { attempt, max_attempts, error, delay_ms } => {}

      // Skills
      AgentEvent::SkillsResolved { skills, injection_bytes } => {}
      AgentEvent::SkillResolutionFailed { skill_key, reason, reference, error } => {}

      // Comms interaction lifecycle
      AgentEvent::InteractionComplete { interaction_id, result } => {}
      AgentEvent::InteractionFailed { interaction_id, error } => {}
      AgentEvent::StreamTruncated { reason } => {}

      // Tool config changes
      AgentEvent::ToolConfigChanged { payload } => {}

      _ => {} // non_exhaustive: forward compatibility
  }
  ```
</Accordion>

***

## Core types

### Message and ContentBlock

```rust theme={null}
use meerkat::{Message, UserMessage, AssistantMessage, SystemMessage, ToolResult};
use meerkat::ContentBlock;

let system = Message::System(SystemMessage { content: "You are helpful.".to_string() });

// Text-only user message (convenience)
let user = Message::User(UserMessage::text("Hello!"));

// Multimodal user message with text and image
let user = Message::User(UserMessage {
    content: vec![
        ContentBlock::Text { text: "What is in this image?".to_string() },
        ContentBlock::Image {
            media_type: "image/png".to_string(),
            data: ImageData::Inline { data: base64_data },
        },
    ],
});
```

### ContentInput

`ContentInput` is the prompt type accepted by `CreateSessionRequest` and `StartTurnRequest`. It supports both text-only and multimodal prompts:

```rust theme={null}
use meerkat::ContentInput;

// Text-only (most common) — implements From<&str> and From<String>
let prompt: ContentInput = "What is Rust?".into();

// Multimodal — blocks with mixed content types
let prompt = ContentInput::Blocks(vec![
    ContentBlock::Text { text: "Describe this image.".to_string() },
    ContentBlock::Image {
        media_type: "image/jpeg".to_string(),
        data: ImageData::Inline { data: base64_data },
    },
]);
```

### ToolCall and ToolResult

```rust theme={null}
use meerkat::{ToolCall, ToolResult};

let tool_call = ToolCall {
    id: "tc_123".to_string(),
    name: "get_weather".to_string(),
    args: json!({"city": "Tokyo"}),
};

let result = ToolResult::new("tc_123".to_string(), "Sunny, 25C".to_string(), false);
let error = ToolResult::new("tc_123".to_string(), "City not found".to_string(), true);
```

### RunResult

```rust theme={null}
let result: RunResult = agent.run("Hello".into()).await?;

println!("Response: {}", result.text);
println!("Session: {}", result.session_id);
println!("Tokens: {}", result.usage.total_tokens());
println!("Turns: {}", result.turns);
println!("Tool calls: {}", result.tool_calls);
```

***

## See also

* [Tools and stores](/rust/tools-and-stores) - tool system, session stores, MCP integration
* [Advanced](/rust/advanced) - expert-only direct agent construction, providers, budgets, and hooks
* [API reference](/reference/api-reference) - quick-lookup type index
