# Roma Agent
**Minimal, self-evolving autonomous agent framework in Rust.**
[](https://www.rust-lang.org)
[](#license)

[中文文档](docs/README.zh-CN.md) · [Architecture](ARCHITECTURE.md) · [Quick Start](#quick-start) · [Highlights](#highlights)
<img src="docs/images/01-architecture-layers.png" width="70%" alt="Roma overall architecture" />
Roma is a small, testable agent runtime. Every entry point — CLI, TUI, gateway, or a
workflow step — builds an `AgentLoop` and streams events out. Providers, tools, memory,
and skills sit behind traits, so tests swap in `MockProvider` / `NullMemoryStore` without
touching the loop. Workflow gates are the one thing kept *outside* the loop: they live in
the engine, where the model cannot reach them.
> Roma — Greek muse of epic poetry and eloquence, chief of the Muses.
## Highlights
- **Multi-provider** — OpenAI, Anthropic Claude, and Mock (testing), with `FailoverChain` + `CredentialPool` for config-driven failover.
- **Layered memory** — L0 (scratch) → L4 (identity), with background distillation off the critical path.
- **Self-evolving skills** — a discoverable `SKILL.md` index, CRUD tools, auto-crystallization, and background quality review.
- **Engine-enforced workflows** — YAML or runtime-built graphs with human-in-the-loop gates the model *cannot* skip (via gatedflow).
- **Rich tools** — file/code/ask builtins, MCP stdio tools merged at startup, and `web_fetch`.
- **Messaging gateway** — Telegram / WeChat (and extensible adapters) driving the same loop.
- **SubAgents & scheduler** — isolated-context delegation and autonomous periodic tasks (`--reflect`).
## Quick Start
```bash
cargo install roma-cli
```
This installs `roma` under `~/.cargo/bin` (make sure it's on your `PATH`).
Requires a recent Rust stable toolchain (edition 2024; tested on 1.92+).
Build and check the workspace:
```bash
cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
```
### CLI
```bash
roma # interactive TUI (default)
roma chat "Summarize the README" # single prompt, streaming UI
roma -z "explain this repo" # one-shot prompt to stdout
roma --workflow deploy.yaml # YAML workflow with engine-enforced gates
roma --reflect # cron / scheduler mode
roma gateway run # messaging gateway
roma setup # interactive configuration wizard
```
<details>
<summary>Full command reference</summary>
```bash
# Single-task mode (file in, file out, per-run artifacts)
mkdir -p /tmp/roma-task
printf "Summarize the README\n" > /tmp/roma-task/input.txt
roma --task /tmp/roma-task
cat /tmp/roma-task/output.txt
# Artifacts land in /tmp/roma-task/runs/<run_id>/
# Gateway
roma gateway run
roma gateway status
# Configuration
roma setup # full interactive wizard
roma setup model # provider / model / key
roma setup mcp # MCP servers
roma setup gateway # messaging channels
roma setup --quick # first-time minimal
roma setup --reset # reset config.toml to defaults
roma import hermes # import from hermes-agent
roma config show
roma status
roma doctor
```
</details>
### Library
```rust
use roma_agent::AgentLoop;
use roma_core::{AgentEvent, ChatSession};
use roma_provider::MockProvider;
use futures::StreamExt;
#[tokio::main]
async fn main() {
let provider = MockProvider::new("mock", "mock-model").with_text("Hello!");
let session = ChatSession::new().with_system("You are a helpful assistant.");
let lp = AgentLoop::builder()
.max_turns(10)
.build();
let mut stream = Box::pin(lp.run(provider, session, Some("Hello!".into())));
while let Some(event) = stream.next().await {
match event {
AgentEvent::TextChunk(text) => print!("{text}"),
AgentEvent::LoopDone { result, .. } => {
println!("\n[done: {:?}]", result.exit_reason);
}
_ => {}
}
}
}
```
## Architecture
The diagram above shows the whole runtime; the subsystems below cover the parts that
aren't obvious from the picture. For the full reference, see
[ARCHITECTURE.md](ARCHITECTURE.md) · diagram sources in [`docs/archify/`](docs/archify/) ·
[interactive overview](docs/archify/out/01-architecture-layers.html).
### Agent loop
One turn: inject context → stream the model → dispatch tools → continue or stop.
- `ToolHook` can veto a call; `Deny` returns `is_error` to the model so the turn can recover, instead of aborting the run.
- No tool call → the loop synthesizes `no_tool`. Cancel ends the current turn and does not start another.
<img src="docs/images/02-agentloop-cycle.png" width="60%" alt="AgentLoop turn cycle" />
[Interactive](docs/archify/out/02-agentloop-cycle.html)
### Layered memory
Layers are ordered by how long they should live. Consolidation is off the critical path:
the user gets `LoopDone` first; distillation runs afterward.
- **L0** never persists · **L1** auto-saves on exit · **L3** is background-distilled · **L4** is hand-curated and injected first.
- **L2** is reserved (dashed in the diagram) — chunked summarization is not wired yet.
- Injection is budgeted (few L4/L3 files, short per file), so distillation has to stay selective.
<img src="docs/images/03-memory-layers.png" width="60%" alt="Layered memory L0–L4" />
[Interactive](docs/archify/out/03-memory-layers.html)
### Workflow gates
A gate is a real pause in `gatedflow-core`, not a line in the system prompt — that is
what makes it unskippable. The engine owns routing; the model only proposes steps.
- Static: `roma --workflow deploy.yaml`. Dynamic: `workflow_start` at runtime; every `agent` step must declare a gate (Y rule).
- Reject fails the workflow. Recovery means a new workflow, not letting the agent mutate the current one.
<img src="docs/images/04-workflow-gates.png" width="60%" alt="Workflow and engine-enforced gates" />
[Interactive](docs/archify/out/04-workflow-gates.html)
### Gateway
Same level as REPL/TUI — platforms plug in via `ChannelAdapter`; the core loop is
unchanged. Telegram and WeChat ship today.
- One session lock per `platform:conversation_id`. A new message gets a fresh cancel token and aborts whatever was still streaming.
- `allowed_tools` and `is_user_allowed()` filter at the adapter before the handler runs.
<img src="docs/images/05-gateway-flow.png" width="60%" alt="Gateway message flow" />
[Interactive](docs/archify/out/05-gateway-flow.html)
### Skills
`SKILL.md` on disk. Only a compact index goes into the system prompt; full text loads
on demand via `skill_view`, so the prompt stays small as skills accumulate.
- After a hard task the agent is nudged to crystallize the approach with `skill_manage`; stale skills get patched in place.
- A background review spot-checks quality and writes findings into L3.
<img src="docs/images/06-skill-system.png" width="60%" alt="Skill self-evolution" />
[Interactive](docs/archify/out/06-skill-system.html)
## Workspace Layout
<details>
<summary>14 crates</summary>
| `roma-core` | Core types, session, errors, agent events |
| `roma-provider` | Provider transport + OpenAI/Claude/Mock + FailoverChain + CredentialPool + SSE |
| `roma-tools` | `Tool` trait + builtin tools (file, code_run, ask_user, no_tool, …) |
| `roma-memory` | Layered memory system (L0–L4) |
| `roma-config` | `config.toml` + `.env` configuration model |
| `roma-agent` | `AgentLoop`, middleware, subagent, reflect scheduler, harness |
| `roma-skills` | Skill data model, filesystem store, conditional activation |
| `roma-workflow` | Adapter between `AgentLoop` and `gatedflow-core` |
| `roma-gateway` | Messaging gateway (Telegram, WeChat, …) |
| `roma-cli` | `roma` binary — TUI, workflow, task, gateway, setup |
| `roma-web` | `WebFetchTool` — HTTP fetch + HTML-to-Markdown |
| `roma-mcp` | MCP dynamic tool registration |
| `roma-bench` | Benchmarks |
| `roma-test-utils` | Shared test helpers |
</details>
## Documentation
- [Architecture reference](ARCHITECTURE.md) — canonical, most detailed
- [中文文档](docs/README.zh-CN.md) · [架构详解(中文)](docs/ARCHITECTURE.zh-CN.md)
- [Diagram sources](docs/archify/) — `archify` specs + interactive HTML
## License
MIT — see the `license` field in `Cargo.toml`.