yoagent 0.26.4

The agent loop for Rust: stream from 7 LLM protocols, run tools, loop until done. Native or wasm32 (Cloudflare Workers).
Documentation
# GASP: Your Agent Is a Git Repo

[GASP](https://github.com/yologdev/gasp) — the Git Agent State Protocol —
keeps an agent's durable self in a git repository: an **append-only semantic
event log** (`state/events.jsonl`) that folds into a typed
goal/run/model/tool graph, alongside identity, skills, and memory tiers.
Restore = `git clone` + replay. Clone your agent onto a new machine and it
remembers everything, with lineage.

yoagent bridges to GASP through the `gasp` feature (backed by
[`yoagent-state`](https://crates.io/crates/yoagent-state), the reference
runtime). The bridge is a consumer of the [`AgentEvent`] stream — **zero
agent-loop changes**:

```toml
yoagent = { version = "0.26", features = ["gasp"] }
```

The `gasp` feature needs the filesystem and git, so it is native-only: it
is not available on wasm32 (see
[WebAssembly & Cloudflare Workers](../guides/wasm-workers.md)).


```rust
use yoagent::gasp::{GaspRecorder, GoalRef};

let recorder = GaspRecorder::init(
    "./my-agent-repo", "my-agent", "worker-1",
    GoalRef::New { title: "ship the feature".into() },
).await?;

let (tx, record_handle) = recorder.recording_sender("implement the parser", None);
agent.prompt_with_sender("implement the parser", tx).await;
let run_id = record_handle.await??;   // Option<RunId>: None if no AgentStart arrived
```

## What gets recorded

| Agent activity | GASP events |
|---|---|
| loop starts | `run.started` (the goal is stamped in the run commit's `Goal:` trailer) |
| each assistant turn | `model.called` / `model.finished` paired nodes (a retried provider attempt — an error `MessageEnd` followed by `ProviderRetry` — is not recorded) |
| each tool execution | `tool.called` / `tool.finished` (with success flag) |
| a plugin logs (opt-in, below) | `observation.created`, linked to the run, `{source: "plugin", level}` metadata |
| loop ends | `run.finished` with the outcome: `completed` / `truncated` / `error` / `aborted` (a cancelled run) / `refused`, `loop_aborted:<tool>` when loop detection stopped it, `rejected` when an input filter refused the prompt, `interrupted` for a run a crashed process left open |

The semantic log stores bounded one-line summaries — the **task string
(verbatim)**, model ids, and the **first 200 characters of tool inputs, tool
outputs, and assistant text** — never full transcripts. If secrets can flow
through tool arguments or outputs, install a redacting summarizer
(`recorder.with_summarizer(...)`) before recording: the repo is designed to
be cloned and shared, and committed history is hard to scrub. Full
transcripts belong in GASP's cold `transcripts/` tier —
[`Session::to_jsonl`](session-trees.md) is a natural format for it.

Robustness: a crashed process leaves no open run — the recorder closes stale
runs as `interrupted` on open, and a dropped event stream finishes the run
with the outcome derived so far. One git commit per run keeps the history
append-only, and the init scaffolding (manifest, identity) is committed so a
fresh clone is a complete, conformant agent. If recording fails mid-run
(disk, lease, git), recording stops and the error surfaces via the returned
handle — **always await it** — while event forwarding to your UI continues
uninterrupted. GASP repos are single-writer: don't share one repo between
live workers, and record one run at a time.

## Plugin logs (opt-in)

A rutis plugin's diagnostics — the [yoagent-rutis](https://github.com/yologdev/yoagent/tree/main/integrations/yoagent-rutis) bridge's `log`, such as a pi extension's "command `/todos` not available" or a DSH image that could not be read — can be part of the run's record. Install two things from the recorder:

```rust,ignore
use tracing_subscriber::layer::SubscriberExt;

tracing::subscriber::set_global_default(
    tracing_subscriber::registry().with(recorder.plugin_log_layer()),
)?;
let agent = agent.with_extension(recorder.extension()); // learns each run's id
```

`tracing-subscriber` 0.3 must be a dependency of your own crate (the layer implements its `Layer` trait). The layer forwards lines of the target `yoagent_rutis::plugin` (`gasp::PLUGIN_LOG_TARGET`) whose `run_id` is one of this recorder's runs — another agent's plugins in the same process never land here, and a line without a run stays in your logs only. Each line goes through the summarizer, like tool output, and is recorded as an `observation.created` on the run. Installed with `with_tree_extension`, delegated runs' lines are recorded on the same run. Record every run of an agent that has the extension: a run started *without* a recording while one is open is attributed to that recording.

## Tested conformance

yoagent's CI emits an agent repo with a mock provider and runs the GASP
conformance checker against it — all seven mechanical checks (envelope
round-trip, replay, vocabulary, append-only git history, causation integrity,
restore, domain↔ops consistency) must pass on every commit. Try it yourself:

```bash
cargo run --example gasp_emit --features gasp -- /tmp/my-agent
git clone https://github.com/yologdev/gasp && cd gasp/conformance-check
cargo run -q -- /tmp/my-agent
# conformant: all checks passed
```

[`AgentEvent`]: https://docs.rs/yoagent/latest/yoagent/enum.AgentEvent.html