Rust port of @earendil-works/pi-agent-core. Five workspace crates:
grain-agent-core— provider-agnostic agent runtime (messages, tools, events, loop,Agentwrapper).grain-agent-harness— engineering plumbing (session tree, custom messages, system prompt assembly, output truncation, context-window guard, compaction, JSONL persistence).grain-llm-models— standardized model registry (models.dev-backed snapshot, descriptor, capability flags, pricing).grain-llm-genai—LlmStreamimplementation backed by thegenaicrate; builder + env-key resolver + OpenAI-compat presets.grain-ai-agent-headless— thegrain-headlessCLI binary + a coding-agent toolkit (file / shell / web / semantic-search tools, skills loader, JSONL session, telemetry, …).
中文版文档:docs/zh/.
Never built an agent before? Read getting-started.md. It walks from "what's an agent?" → run the bundled CLI → write your own custom tool in Rust. ~30 minutes total.
Want the CLI reference right now? headless-cli.md.
Building a custom agent? Skip to core-agent.md after the tutorial.
| Module | Doc | What it is |
|---|---|---|
types |
core-types.md | Messages, tools, events, state primitives |
stream |
core-stream.md | LlmStream trait — the LLM provider injection point |
agent_loop |
core-agent-loop.md | Low-level run_agent_loop / run_agent_loop_continue |
agent |
core-agent.md | High-level Agent wrapper: subscribe / abort / steer / follow-up |
| Module | Doc | What it is |
|---|---|---|
agent_harness |
agent-harness.md | Top-level orchestrator: bundles Agent + Session + tools + queues + reconfig + UI hooks into one façade |
messages |
harness-messages.md | Custom messages (branch / compaction / custom) + harness convert_to_llm |
session |
harness-session.md | Session tree, storage trait, in-memory impl, branching + fork |
session_jsonl |
session-jsonl.md | JSONL directory-on-disk session persistence |
system_prompt |
harness-system-prompt.md | <available_skills> XML block renderer |
truncate |
harness-truncate.md | Tool-output head/tail truncation utilities |
context_guard |
context-guard.md | Registry-driven transform_context budget enforcement |
compaction |
compaction.md | LLM-driven context summarization between turns |
| Crate | Doc | What it is |
|---|---|---|
grain-llm-models |
llm-models.md | Model descriptor + registry, vendored models.dev snapshot, optional runtime fetch |
grain-llm-genai |
llm-genai.md | LlmStream impl on top of genai 0.5: builder, env keys, OpenAI-compat routing |
| Surface | Doc | What it is |
|---|---|---|
grain-headless binary |
headless-cli.md | The ready-to-run CLI; all flags + slash commands |
| Tools (built-in) | headless-tools.md | Every tool the CLI / library ships, with example arguments |
| Config file | config.md | TOML config at <workspace>/.grain/config.toml + ~/.config/grain/config.toml |
| Telemetry | telemetry.md | Opt-in local JSONL event log (with sensitive-data warning) |
| Surface | Doc | What it is |
|---|---|---|
grain-tui binary |
headless-tui.md | ratatui terminal UI on top of headless — themes, slash autocomplete, prompt history, provider picker |
| Crate | Doc | What it is |
|---|---|---|
Plugin system (lazy.gagent) |
plugins.md | Drop <workspace>/.grain/plugins/<name>/plugin.toml and ship skills/, themes/, prompts/*.md, scripts/*.js — auto-discovered at startup, no rebuild |
grain-script-boa |
scripting.md | Boa-powered JS scripting layer — drop .js into <workspace>/.grain/scripts/ to register agent tools at runtime |
grain-pi-compat |
pi-compat.md | pi.dev-style extensions running on grain — supports registerTool / registerCommand / registerShortcut / on / ui.notify / ui.confirm / ui.input / ui.select |
| Topic | Doc | What it is |
|---|---|---|
| Provider profiles | providers.md | Multi-vendor / multi-account / OAuth-subscription configuration; consumed by both grain-headless and grain-tui |
| Topic | Doc |
|---|---|
| Testing & CI | testing.md — running unit, clippy, and .env.test-gated live integration suites |
cargo build --release -p grain-ai-agent-headless --bin grain-headlessMethod A — put your key in .grain/config.toml (no export needed):
mkdir -p .grain
cat > .grain/config.toml << 'EOF'
[[provider]]
name = "anthropic"
kind = "anthropic"
model = "anthropic/claude-sonnet-4-5"
auth = { kind = "api_key", env = "ANTHROPIC_API_KEY", value = "sk-ant-你的key" }
EOF
./target/release/grain-headless -C ./my-project --prompt "What does main.rs do?"Method B — traditional env var:
export ANTHROPIC_API_KEY=...
./target/release/grain-headless -C ./my-project --prompt "What does main.rs do?"Add --allow-write to let it edit; --allow-bash to let it run shell; --interactive for multi-turn chat. Full reference: headless-cli.md. Config reference: config.md.
[dependencies]
grain-agent-core = { path = "grain-agent-core" }
grain-llm-models = { path = "grain-llm-models" }
grain-llm-genai = { path = "grain-llm-genai" }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }use std::sync::Arc;
use grain_agent_core::{Agent, AgentOptions};
use grain_llm_genai::GenaiStream;
use grain_llm_models::Registry;
#[tokio::main]
async fn main() {
let stream = Arc::new(GenaiStream::builder().build());
let model = Registry::from_embedded_snapshot()
.to_core_model("anthropic/claude-sonnet-4-5")
.unwrap();
let agent = Agent::new(AgentOptions::new(model, stream));
agent.prompt_text("hello").await.unwrap();
}Full walkthrough with a custom tool: getting-started.md.