yoagent 0.25.0

Simple, effective agent loop with tool execution and event streaming
Documentation
<div align="center">

<picture>
  <img alt="yoagent" src="docs/images/banner.png" width="100%" height="auto">
</picture>

<a href="https://crates.io/crates/yoagent">crates.io</a> · <a href="https://yologdev.github.io/yoagent/">Docs</a> · <a href="https://docs.rs/yoagent">API</a> · <a href="https://github.com/yologdev/yoagent">GitHub</a> · <a href="https://deepwiki.com/yologdev/yoagent">DeepWiki</a> · <a href="CHANGELOG.md">Changelog</a>

[![][crates-shield]][crates-link]
[![][docsrs-shield]][docsrs-link]
[![][ci-shield]][ci-link]
[![][msrv-shield]][msrv-link]
[![][license-shield]][license-link]

**The agent loop for Rust.** Stream from any of 7 LLM protocols, run tools, loop until done.

</div>

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/images/loop.svg">
  <source media="(prefers-color-scheme: light)" srcset="docs/images/loop-light.svg">
  <img alt="The yoagent loop: prompt, LLM stream, tool execution, loop" src="docs/images/loop.svg" width="100%">
</picture>

---

## Try it in one command — no API key

```bash
git clone https://github.com/yologdev/yoagent && cd yoagent
ollama serve &
ollama pull llama3.1:8b                           # or pass --model <any pulled model>
cargo run --example cli -- --provider ollama
```

That's a working coding agent in your terminal — file read/write/edit, shell, ripgrep search,
streaming output, skills. No signup, no key, nothing to configure.

```
  yoagent cli — mini coding agent
  Type /quit to exit, /clear to reset

  model: llama3.1:8b
  cwd:   /home/user/my-project

> find all TODO comments in src/

  ▶ search 'TODO' ✓

Found 3 TODOs:
  src/main.rs:42: // TODO: handle edge case
  src/lib.rs:15:  // TODO: add tests
  src/utils.rs:8: // TODO: optimize this

  tokens: 1250 in / 89 out
```

Point it at a hosted model instead by swapping the flag:

```bash
ANTHROPIC_API_KEY=sk-... cargo run --example cli
GROQ_API_KEY=...        cargo run --example cli -- --provider groq --model openai/gpt-oss-120b
cargo run --example cli -- --api-url http://localhost:1234/v1 --model my-model   # LM Studio, llama.cpp, vLLM
```

---

## Install

```toml
[dependencies]
yoagent = "0.25"
tokio = { version = "1", features = ["full"] }
```

Building for wasm32? Use `default-features = false` (see
[WebAssembly & Cloudflare Workers](https://yologdev.github.io/yoagent/guides/wasm-workers.html)).
On a native target keep the default `native` feature: without it reqwest has no TLS, and every
HTTPS provider call fails at runtime.

## Quick start

An agent that actually uses a tool — the thing the crate exists for:

```rust
use yoagent::provider::ModelConfig;
use yoagent::{tools, Agent, AgentEvent, StreamDelta};

#[tokio::main]
async fn main() {
    // The provider is selected from the config's protocol and the key is read
    // from ANTHROPIC_API_KEY. Call `.with_api_key(k)` to pass one explicitly.
    let mut agent = Agent::from_config(ModelConfig::claude_sonnet_5())
        .with_system_prompt("You are a coding assistant.")
        .with_tools(tools::default_tools());

    let mut events = agent.prompt("Find every TODO in src/ and summarise them").await;

    while let Some(event) = events.recv().await {
        match event {
            AgentEvent::MessageUpdate { delta: StreamDelta::Text { delta }, .. } => print!("{delta}"),
            AgentEvent::ToolExecutionStart { tool_name, .. } => println!("\n▶ {tool_name}"),
            AgentEvent::AgentEnd { .. } => break,
            _ => {}
        }
    }
    agent.finish().await;
}
```

Swap the model by swapping the config — the provider follows, and the key is read from that
provider's conventional env var:

```rust
Agent::from_config(ModelConfig::groq("openai/gpt-oss-120b", "GPT-OSS 120B"));    // GROQ_API_KEY
Agent::from_config(ModelConfig::google("gemini-3.8-flash", "Gemini 3.8 Flash")); // GEMINI_API_KEY
Agent::from_config(ModelConfig::ollama("http://localhost:11434/v1", "llama3.1:8b")); // no key
```

---

## How yoagent differs

yoagent is deliberately narrow. It is the loop, tool execution, and the machinery you need to
run that loop in production. It ships **no** vector stores, embedding pipelines, or task-graph
layer — if your problem is retrieval or orchestration, one of these is the better fit:

| If you need | Look at |
|---|---|
| RAG pipelines, vector stores, embeddings, transcription and image generation | [`rig`](https://github.com/0xPlaygrounds/rig) — *"Build modular and scalable LLM Applications in Rust"* |
| Typed task graphs and streaming RAG indexing alongside agents | [`swiftide`](https://github.com/bosun-ai/swiftide) — *"Composable LLM agents and harness, typed task graphs, and streaming RAG pipelines in Rust"* |
| A tool-calling loop you host, gate, steer, branch, and record | yoagent |

What that focus bought:

- **The loop is a free function.** [`agent_loop()`](src/agent_loop.rs) is stateless and takes
  everything it needs as arguments. `Agent` is an *optional* wrapper that adds history and queues.
  You can drive the loop yourself without adopting our state model.
- **7 native wire protocols**, not one OpenAI-compat shim with adapters bolted on. Anthropic
  Messages, OpenAI Completions, OpenAI Responses, Azure, Gemini, Vertex, and Bedrock each have a
  real implementation, so provider-specific features (thinking budgets, prompt-cache breakpoints,
  reasoning deltas) survive instead of being flattened away.
- **One plug-in contract for the whole run.** An `Extension` can add tools, check input, allow,
  **modify** or deny each tool call, redact results, and check the final answer, with state
  that starts fresh each run. Install it as host policy and it governs every sub-agent too.
- **Steer a run that's already going.** Inject guidance mid-flight; it's picked up between tool
  batches without restarting the turn.
- **History is a tree, not a list.** [`Session`](src/session.rs) forks, checkpoints, and seeks.
  Edit an earlier turn and re-run it without destroying the original branch.
- **Runs are recordable.** With `features = ["gasp"]`, a run becomes an append-only semantic
  event log in a git repo — restore is clone + replay. Conformance-checked in CI.
- **The whole loop is testable offline.** `MockProvider` scripts multi-turn tool-calling
  conversations and honours cancellation, so abort and steering paths are testable with no
  network or key.

---

## Built with yoagent

**[yoyo-evolve](https://github.com/yologdev/yoyo-evolve)** [![][yoyo-stars]][yoyo-link] — a coding
agent that evolves its own source in public. It began as 200 lines of Rust; every commit since has
been agent-written and gated on tests. It runs on this loop with the `openapi` feature enabled.

Also built on yoagent:

| Project | What it is |
|---|---|
| [`rab`](https://github.com/markokocic/rab) | A lightweight, extensible Rust coding agent |
| [`greatsage`](https://github.com/rick68/greatsage) | "Rimuru's Unique Skill, you know the one" |
| [`yoclaw`](https://github.com/yologdev/yoclaw) | OpenClaw reborn in Rust — a single-binary agent that remembers you |

Built something on yoagent? [Open a PR](CONTRIBUTING.md) and add it here — we'd like to see it.

---

## What's in the box

Each line links to its chapter in [the book](https://yologdev.github.io/yoagent/).

- **The loop** — a full event stream, parallel / sequential / batched tools, steering and follow-ups, execution limits, retry with backoff and jitter, and the original hooks (`ToolMiddleware`, input filters, `TurnHook`, lifecycle callbacks). [Agent loop](https://yologdev.github.io/yoagent/concepts/agent-loop.html) · [Events](https://yologdev.github.io/yoagent/concepts/messages-events.html) · [Retry](https://yologdev.github.io/yoagent/concepts/retry.html) · [Callbacks & hooks](https://yologdev.github.io/yoagent/concepts/callbacks.html)
- **Extensions** — one plug-in contract for the whole run: add tools, check input, gate and rewrite tool calls, redact results, verify the final answer, enforce a dollar `Budget`, audit events, and cover sub-agents with host policy. The [`yoagent-rutis`](integrations/yoagent-rutis/) bridge (not yet on crates.io) installs [rutis](https://crates.io/crates/rutis) plugins, in Rust, TypeScript or Python, as one extension. [Extensions](https://yologdev.github.io/yoagent/concepts/extensions.html)
- **Providers** — 7 native protocols (Anthropic, OpenAI Completions and Responses, Azure, Gemini, Vertex, Bedrock) reaching 20+ providers, with thinking controls, prompt-cache hints and centralised context-overflow detection. [Providers](https://yologdev.github.io/yoagent/providers/overview.html) · [Prompt caching](https://yologdev.github.io/yoagent/concepts/prompt-caching.html)
- **Tools** — built-in `bash`, file read/write/edit, `list_files` and `search` (native), custom tools via one trait, MCP over stdio or HTTP, OpenAPI specs, and per-run `ToolSource`s. [Tools](https://yologdev.github.io/yoagent/concepts/tools.html) · [MCP](https://yologdev.github.io/yoagent/guides/mcp.html) · [OpenAPI](https://yologdev.github.io/yoagent/guides/openapi.html)
- **Sub-agents and shared state** — delegate to child loops with their own model and tools; pass large artifacts by reference. [Sub-agents](https://yologdev.github.io/yoagent/concepts/sub-agents.html)
- **Context** — usage-calibrated tracking, tiered compaction, optional `LlmCompaction`, loop detection. [Context management](https://yologdev.github.io/yoagent/concepts/context-management.html)
- **Sessions, skills, structured outputs** — branching session trees with JSONL persistence, AgentSkills `SKILL.md` loading, typed `prompt_structured::<T>()`. [Session trees](https://yologdev.github.io/yoagent/concepts/session-trees.html) · [Skills](https://yologdev.github.io/yoagent/concepts/skills.html) · [Structured outputs](https://yologdev.github.io/yoagent/concepts/structured-outputs.html)
- **Decision models** (feature `decision`) — typed yes/no, one-of-N and score judgments in a few hundred ms (TypeSafe Jev, Cloudflare Clef, OpenAI's Decisions API, any logprobs server); a tool gate and an input guard built on them. [Decision models](https://yologdev.github.io/yoagent/concepts/decision-models.html)
- **Cost and telemetry** — opt-in per-model pricing (`prices::enable_bundled()` offline, or `enable_live` from models.dev; nothing is priced by default), `SessionStats` on every run including sub-agents, `tracing` spans with tokens and cost. [Pricing](https://yologdev.github.io/yoagent/concepts/pricing.html) · [Telemetry](https://yologdev.github.io/yoagent/concepts/telemetry.html)
- **Recording and persistence** — serde on every core type; record runs into a [GASP](https://github.com/yologdev/gasp) repo (feature `gasp`). [Persistence](https://yologdev.github.io/yoagent/concepts/persistence.html) · [GASP](https://yologdev.github.io/yoagent/concepts/gasp.html)
- **WebAssembly** — `--no-default-features` builds for `wasm32-unknown-unknown`, e.g. Cloudflare Workers. [WebAssembly & Workers](https://yologdev.github.io/yoagent/guides/wasm-workers.html)

---

## Examples

Seventeen of the runnable examples in [`examples/`](examples/) are below; ten need no API key at all (eleven counting `cli` with a local model). The rest are live-provider harnesses and offline evaluation sweeps.

| Example | What it shows | Key needed |
|---|---|---|
| [`cli`](examples/cli.rs) | A ~400-line coding agent — all tools, skills, streaming, colored output. Like a baby Claude Code | optional¹ |
| [`rlm`](examples/rlm.rs) | An LLM that explores a codebase on its own by spawning sub-agents | yes |
| [`code_review`](examples/code_review.rs) | Three sub-agents reviewing a diff in parallel, results merged | yes |
| [`shared_state`](examples/shared_state.rs) | Passing a large artifact between sub-agents by reference | yes |
| [`sub_agent`](examples/sub_agent.rs) | Delegation basics with a per-sub-agent model | yes |
| [`basic`](examples/basic.rs) | The smallest possible agent | yes |
| [`callbacks`](examples/callbacks.rs) | Lifecycle hooks and a custom tool | **no** |
| [`persistence`](examples/persistence.rs) | Save and restore a session | **no** |
| [`telemetry`](examples/telemetry.rs) | `tracing` spans with token and cost fields | **no** |
| [`gasp_emit`](examples/gasp_emit.rs) | Recording a run into a GASP repo | **no** |
| [`decision`](examples/decision.rs) | Decision-model questions in one line, and attaching a model to an agent (feature `decision`) | yes |
| [`extension_policy`](examples/extension_policy.rs), [`_redact`](examples/extension_redact.rs), [`_verifier`](examples/extension_verifier.rs), [`_budget`](examples/extension_budget.rs), [`_tree`](examples/extension_tree.rs), [`_audit`](examples/extension_audit.rs) | Extensions: a tool policy, redaction, a verifier, budgets, policy over sub-agents, an audit log ([guide](https://yologdev.github.io/yoagent/concepts/extensions.html)) | **no**² |

¹ `--provider ollama` or `--api-url` needs no key; hosted providers read their conventional env var.

² Scripted offline by default; `-- --live` uses `DEEPSEEK_API_KEY` or `ANTHROPIC_API_KEY`.

---

## Testing

`MockProvider` scripts a whole multi-turn tool-calling conversation with no network, and honours
cancellation, so abort and steering paths are testable too. See
[Testing Your Agent](https://yologdev.github.io/yoagent/guides/testing.html); how the crate itself is tested and what CI runs is
in [CONTRIBUTING](CONTRIBUTING.md).

---

## Documentation

- **[The book](https://yologdev.github.io/yoagent/)** — concepts, guides, a page per provider, and the [architecture and module map](https://yologdev.github.io/yoagent/architecture/overview.html) ([source](docs/))
- **[API reference](https://docs.rs/yoagent)** — built with all features enabled
- **[CHANGELOG](CHANGELOG.md)** — every release
- **[CONTRIBUTING](CONTRIBUTING.md)** — how to build, test, and send a PR

MSRV is **1.86**, enforced in CI. Raising it is a minor-version change.

## License

MIT — see [LICENSE](LICENSE).

<!-- Badge link definitions -->
[crates-shield]: https://img.shields.io/crates/v/yoagent?labelColor=black&style=flat-square&logo=rust&color=orange
[crates-link]: https://crates.io/crates/yoagent
[docsrs-shield]: https://img.shields.io/docsrs/yoagent?labelColor=black&style=flat-square&logo=docsdotrs&label=docs.rs
[docsrs-link]: https://docs.rs/yoagent
[ci-shield]: https://img.shields.io/github/actions/workflow/status/yologdev/yoagent/ci.yml?labelColor=black&style=flat-square&logo=github&label=CI
[ci-link]: https://github.com/yologdev/yoagent/actions/workflows/ci.yml
[msrv-shield]: https://img.shields.io/badge/MSRV-1.86-blue?labelColor=black&style=flat-square&logo=rust
[msrv-link]: https://github.com/yologdev/yoagent/blob/main/Cargo.toml
[license-shield]: https://img.shields.io/badge/license-MIT-white?labelColor=black&style=flat-square
[license-link]: https://github.com/yologdev/yoagent/blob/main/LICENSE
[yoyo-stars]: https://img.shields.io/github/stars/yologdev/yoyo-evolve?labelColor=black&style=flat-square&color=c4f042
[yoyo-link]: https://github.com/yologdev/yoyo-evolve