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.
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.