Embedding Orchard

Orchard is a library first. The orch CLI is a thin layer over it. The same core API, the orchard crate, is wrapped by four targets: Rust, C (FFI), Python (PyO3), and WebAssembly (wasm-bindgen). Every boundary, the provider, the store, the HTTP client, and tools, is a trait with a pure-Rust default, so a session builds with zero config and a host can override any single piece.

Rust

The Rust API is the source of truth. You load and check an agent, build a session with whatever boundaries you want to override, and drive turns against it. Loading runs the parser and the checker and returns structured diagnostics on failure, never a panic. A native Rust tool registered on the builder can be called by the agent's delegate loop.

use orchard::{Agent, Runtime, NativeTool};
use std::sync::Arc;

async fn run() -> Result<(), orchard::Error> {
    // Load + check + lower (structured diagnostics on failure, never a panic).
    let agent = Agent::load(source, "ivy.orch")?;

    // Register a native Rust tool the agent's delegate loop can call.
    let tool = NativeTool::builder("lookup_user")
        .description("Look up a user by id.")
        .param("id", "integer", true)
        .handler(|args| async move {
            let id = args.get("id").and_then(|v| v.as_i64()).unwrap_or(0);
            Ok(serde_json::json!({ "id": id, "name": "Ada" }))
        });

    let session = Runtime::builder(agent)
        .provider(Arc::new(my_provider))   // route model calls through a host gateway
        .store(Arc::new(my_store))         // share a host database
        .register_tool(tool)
        .base_dir(".")
        .build()?;

    let reply = session.message("who is user 7?").await?;   // drive on message
    let out   = session.task("draft the standup").await?;   // one-shot task
    let val   = session.skill("triage", args).await?;       // a named skill
    Ok(())
}

Runnable as cargo run -p orchard --example embed_host, which registers a native tool and a custom provider and drives a delegate turn offline.

The injectable boundaries

Each boundary is a trait, and each trait is Send + Sync so a session can move and share work across threads. Override the ones you care about and let the rest fall back to their pure-Rust defaults.

Provider
async fn chat(ChatRequest) returns ChatResponse. Route to your LLM gateway, or a fake for tests. The mock provider ships for offline use.
Store
conversation, facts, state, semantic, and trace. The default is the in-memory InMemoryStore. Inject your own database to share host state.
Tool
built-in packs, custom declarative tools, MCP, or a NativeTool you register from the host.
HttpClient
the egress-guarded default (rustls), or your own client.
Embedder
pluggable embeddings for semantic memory.
Clock
pluggable time, useful for deterministic tests.

C (FFI)

The C binding exposes the same capabilities through a C ABI generated with cbindgen. You load an agent, open a session against a base directory, drive a turn, and free what you allocated. Diagnostics are returned through an out-parameter rather than thrown.

#include "orchard.h"
char *err = NULL;
OrchAgent *agent = orch_agent_load(source, "a.orch", &err);
OrchSession *session = orch_session_new(agent, ".", &err);
char *reply = orch_session_message(session, "hello");
printf("%s\n", reply);
orch_string_free(reply); orch_session_free(session); orch_agent_free(agent);

Build and run with sh examples/embed-c/build.sh, which compiles liborchard_ffi and links the C demo. The header is examples/embed-c/orchard.h.

Python (PyO3)

The Python module is a thin PyO3 adapter over the same core. You load an agent, open a session, and drive turns from Python.

import orchard
agent = orchard.Agent.load(src, "a.orch")
session = agent.session()
print(session.message("hello"))

Build with pip install maturin && maturin develop -m crates/orchard-py/Cargo.toml, then run python examples/embed-python/demo.py.

WebAssembly (wasm-bindgen)

The WASM build runs the same agent in the browser. You import the generated module, initialize it once, then construct an Agent and drive a turn.

import init, { Agent } from "./pkg/orchard_wasm.js";
await init();
const reply = await new Agent(src, "a.orch").message("hello");

Build with wasm-pack build crates/orchard-wasm --target web, then serve examples/embed-web/. The WASM runtime uses a cooperative executor and an in-memory store (no tokio); spawn, await, and parallel interleave at await points.

Note. The C-FFI, the PyO3 module, and the WASM build are thin adapters over the orchard crate, so each exposes the same surface: load, check, and compile, build a session, drive turns, inject a provider, store, or tool, observe events, and set policy. See the API Reference for the full surface.