Files
cassady/docs/embedding.md
T
owen 71f84c03ce
CI / Build (push) Waiting to run
CI / Test (push) Waiting to run
Add Rust embedding API
2026-06-25 06:53:13 -05:00

3.5 KiB

Experimental Rust embedding API

Cassady v0.2.6 includes an experimental Rust API for running headless agent sessions from another Rust program. The API is intended for early integrations and may change before Cassady declares a stable library contract.

The embedding API uses the same provider configuration, global instructions, prompts, access modes, tools, and JSONL conversation storage as the cass terminal UI. By default it reads and writes under ~/.cass, so run cass setup first or create compatible config.json, providers.json, and models.json files programmatically.

Minimal example

use cassady::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let session = SessionBuilder::new()
        .cwd(std::env::current_dir()?)
        .access_mode(AccessMode::ReadOnly)
        .build()
        .await?;

    let mut turn = session
        .start_turn("Summarize this project in a few sentences.")
        .await?;

    while let Some(event) = turn.next_event().await? {
        match event {
            Event::AssistantChunk(text) => print!("{text}"),
            Event::Finished => break,
            _ => {}
        }
    }

    let session = turn.finish().await?;
    eprintln!("\nResume chat with: cass --resume {}", session.id());
    Ok(())
}

Add Cassady from a git checkout or path dependency, and ensure your application runs on Tokio.

Creating or resuming sessions

Use SessionBuilder to set host-controlled options:

let session = SessionBuilder::new()
    .config_root("/tmp/my-cass-root")
    .cwd("/path/to/workspace")
    .access_mode(AccessMode::WorkspaceEdit)
    .model("my-model")
    .base_url("https://provider.example/v1")
    .api_key_env("MY_PROVIDER_KEY")
    .build()
    .await?;

let resumed = SessionBuilder::new()
    .cwd("/path/to/workspace")
    .resume(session.id())
    .await?;

build() is equivalent to new_session(). Resumed and new sessions use Cassady's normal conversations/*.jsonl files, so CLI and embedded sessions can interoperate.

Events and approvals

Session::start_turn consumes the session and returns a Turn. This type design prevents overlapping turns for the same session. Call turn.finish().await? after receiving Event::Finished to recover the updated Session.

Important events include:

  • AssistantChunk and ReasoningChunk
  • ToolCallStarted, ToolOutputChunk, and ToolResult
  • ApprovalRequested and ApprovalResolved
  • Status
  • Finished

When a tool needs approval, decide in host code:

while let Some(event) = turn.next_event().await? {
    match event {
        Event::ApprovalRequested(request) => {
            eprintln!("approval needed for {}: {}", request.name, request.reason);
            turn.deny(&request.request_id)?;
        }
        Event::Finished => break,
        _ => {}
    }
}

The approval policy is the same as the TUI: shell is unavailable in read-only, requires approval in workspace-edit, and runs directly in full-access unless destructive-operation confirmation is enabled.

Cancellation

Dropping a Turn aborts the underlying task. Prefer turn.cancel().await? when you want Cassady to repair the conversation with cancellation records before returning the session.

Current limitations

The v0.2.6 API is intentionally small and experimental. It does not include custom provider traits, custom tools, plugin loading, multi-agent orchestration, background daemons, task queues, or a synchronous/blocking wrapper.