# rpi — Rust port of the Pi agent SDK
A Rust port of [earendil-works/pi](https://github.com/earendil-works/pi)'s SDK
layer — a library-first, multi-crate workspace for building personal LLM coding
agents in Rust, plus an `rpi` CLI built on top.
> **Naming.** The published crates use the `rpi-` prefix (the upstream `pi-*`
> names are owned on crates.io by a parallel port). The on-disk directories stay
> `crates/pi-*` for history; the `package.name` in each `Cargo.toml` is
> `rpi-*`, so `extern crate` / `use` paths are `rpi_ai`, `rpi_agent`, etc.
## Crates (published as `rpi-*`)
| `rpi-telemetry` | `pi-telemetry/` | Telemetry span/event contracts (noop default). |
| `rpi-ai` | `pi-ai/` | Unified multi-provider LLM types + streaming (Anthropic + faux). |
| `rpi-agent` | `pi-agent/` | Agent runtime + loop, `AgentTool` trait, events, hooks, queues. |
| `rpi-tools` | `pi-tools/` | Built-in tools (`read`/`write`/`edit`/`bash`/`grep`/`find`/`ls`) + `ExecutionEnv`. |
| `rpi-harness` | `pi-harness/` | `AgentHarness`: session tree, JSONL persistence, compaction, run loop. |
| `rpi-cli` | `pi-cli/` | Terminal coding-agent CLI (`rpi` binary) on top of the library crates. |
Dependency direction: `rpi-telemetry → rpi-ai → rpi-agent → rpi-tools → rpi-harness → rpi-cli`.
## Relationship to the TypeScript source
The TypeScript reference is checked out under `.reference/pi/` (read-only). Every
Rust module names the TS file it mirrors in its module-level doc comment. The
crate family is a Rust-native reimplementation, not a thin wrapper — it ports the
SDK surface (`pi-ai`, `pi-agent-core`, the harness tools, the session layer) and
the CLI, keeping the layering and behavior faithful while using idiomatic Rust
(`async`/`await`, `Arc`, `serde`, `tokio`).
## How you build an agent
```rust
use rpi_agent::{Agent, AgentTool, AgentEvent};
use rpi_ai::{providers::faux::faux_provider, Model};
let model = faux_provider().model("echo");
let mut agent = Agent::builder(model)
.system_prompt("You are a helpful assistant.")
.tool(MyTool)
.build();
let mut events = agent.subscribe();
tokio::spawn(async move {
while let Some(ev) = events.recv().await {
match ev { /* AgentEvent::MessageUpdate { .. }, etc. */ }
}
});
agent.prompt("Hello!").await.unwrap();
```
See [docs/architecture.md](docs/architecture.md) for the full design.
## Status (v1)
- **Providers:** Anthropic (API-key auth) + a faux provider for tests. OAuth /
Copilot auth is deferred — bring an `ANTHROPIC_API_KEY`.
- **Tools:** `read`, `write`, `edit`, `bash` (mutating, run through a
`MutationQueue`) + `grep`, `find`, `ls` (read-only, in-process via the
`FileSystem` trait — no `rg`/`fd` shell-out).
- **Sessions:** JSONL v4 durable backend + in-memory ephemeral; compaction + a
split-turn two-LLM-call invariant.
## Releasing
Publish the crate family with `release.ps1` (Windows) or `release.sh` (Unix/CI),
dep-ordered, dry-run by default:
```
./release.ps1 # safe dry run
./release.ps1 -DryRun # same, explicit
./release.ps1 -Publish # real publish to crates.io (run `cargo login` first)
```
## License
MIT. See [LICENSE](LICENSE).