Model or dataset
pacifio/cersei avatar
pacifio/cersei

Cersei: a Rust SDK that treats a coding agent as a set of composable functions

The Rust SDK for building coding agents. Tools, streaming, graph, sub-agent orchestration, MCP — as composable functions

458 stars77 forksRustMIT

At a glance

What is it?
Cersei packages tool execution, provider streaming, sub-agent orchestration, memory and MCP into Rust crates you can call from your own binary. The interesting part is the split between the library and the Abstract CLI built on top of it.
Who is it for?
Adopt Cersei if you are writing a Rust program that needs an agent inside it, or if you want a CLI whose permission policy, tool set and provider you control at the call site rather than through plugins. Do not adopt it if you need a stable published crate version, or if your team is not already comfortable maintaining Rust dependencies: the install instructions point at a git dependency, not a release on crates.io.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 40 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap Cersei targets: agent logic trapped inside CLIs

Claude Code and OpenCode are applications. If you want the same loop inside a service, a test runner or an editor plugin, you either shell out to a binary and parse its output, or you rebuild tool dispatch, streaming and memory yourself. Cersei's stated position is that these should be library functions. The README frames the goal directly: build a Claude Code replacement, embed an agent in your app, or create something entirely new. The audience is Rust developers who want the agentic loop as a dependency rather than a subprocess. The comparison table makes the split explicit, listing form factor as the first row: CLI app for Claude Code and OpenCode, library for the SDK, CLI app again for Abstract, which the README describes as using the SDK. That two-layer arrangement is the actual pitch. The SDK is the product; Abstract is the worked example that proves the SDK is complete enough to carry a full agent.

Crate layout and the direction data flows

The architecture section lists ten crates. cersei is the facade, used through cersei::prelude::*. Below it, cersei-types holds provider-agnostic messages, errors and stream events. cersei-provider defines the Provider trait plus Anthropic and OpenAI implementations. cersei-tools carries the tools, permissions, a bash classifier, skills and git utilities, with cersei-tools-derive supplying the #[derive(Tool)] macro. cersei-agent contains the builder, the agentic loop, compaction, the coordinator and effort levels. cersei-memory holds the Memory trait, memdir, CLAUDE.md handling, sessions and the Grafeo graph. cersei-hooks is a hook and middleware system, and cersei-mcp is an MCP client speaking JSON-RPC 2.0 over stdio. abstract-cli sits on top. The flow implied by that layering runs one way: your code builds an Agent, the agent calls a Provider for streamed events, tool calls are dispatched against a tool set, tool results return to the loop, and memory writes go through the Memory trait. Because cersei-types is provider-agnostic, swapping Anthropic for a local Ollama endpoint does not touch tool code. That is a real structural benefit, not a slogan: it means the tool implementations never see provider-specific message shapes.

The Agent builder, provider selection and tool sets

The README's opening example is the whole adoption path in nine lines. Agent::builder() takes a provider, a tool set, a permission policy, and then run_with("Fix the failing tests in src/"). The result exposes output.text(). Anthropic::from_env() reads credentials from the environment; the README notes the Anthropic provider supports OAuth. OpenAI is configured through a builder that accepts base_url, model and api_key, and the documented example points base_url at http://localhost:11434/v1 with model llama3.1:70b and api_key "ollama", which is the Ollama shape. The same builder is described as compatible with Azure and vLLM. A custom backend means implementing the Provider trait. Tool selection is done by set rather than by individual registration: cersei::tools::all() for the full 30+, and narrower sets for filesystem (Read, Write, Edit, Glob, Grep, NotebookEdit), shell (Bash, PowerShell), web (WebFetch, WebSearch), planning (EnterPlanMode, ExitPlanMode, TodoWrite), scheduling (CronCreate/List/Delete, Sleep, RemoteTrigger) and orchestration (SendMessage, six task tools, Worktree). The permission_policy(AllowAll) call in the example is worth pausing on. It is the shortest path to a working demo and the wrong default for anything that runs unattended against a real repository.

Custom tools and a documentation defect worth knowing about

Tools are the extension point. The README shows a struct annotated with #[derive(Tool)] and attributes for name, description and permission ("read_only" in the example), plus an impl ToolExecute with an associated Input type that derives Deserialize and JsonSchema, and an async run method receiving the input and a &ToolContext. The README claims custom tools in ten lines, which the example roughly supports. There is a caveat printed in the same block, and it is not cosmetic: the derive macro is said to generate code referencing cercei-tools, spelled with the letters transposed relative to the crate name cersei-tools. The note tells you to add async-trait and cersei-tools as dependencies, or alternatively to write use cersei::tools as cersei_tools; so the generated path resolves. That workaround is a signal about the macro's maturity. It is the kind of detail that is easy to fix upstream and easy to trip over if you copy the example without reading the note. Anyone planning to lean on #[derive(Tool)] should confirm the current state of that generated path on main before designing around it.

Memory: files, sessions and the Grafeo graph

Cersei-memory covers the Memory trait, memdir, CLAUDE.md, session persistence and a graph component called Grafeo. The graph feature is opt-in at the dependency level: the README shows cersei-memory with features = ["graph"]. Abstract is described as having graph memory on by default, and the comparison table lists Cersei's memory as File + Graph against SQLite for OpenCode and file-based for Claude Code. The most concrete claim in the README concerns recall. It states that Claude Code calls Sonnet every turn to rank the top five files by relevance, measured at 7.5 seconds, while Abstract's graph does indexed lookups in 98 microseconds with no LLM call and no API cost. Treat that as a design argument rather than a verified result. I have not run the benchmark and cannot confirm the numbers. The design argument itself is sound and separable from the figures: if recall is an index lookup, recall costs no tokens, and a cheaper recall path changes how often you can afford to consult memory mid-loop. The trade-off is that a graph index has to be built and kept current, and the README does not describe what happens when it drifts from the working tree.

Abstract, and what its benchmark table does not settle

Abstract is the CLI built on the SDK: cargo install --path crates/abstract-cli, then abstract for a REPL, abstract "fix the failing tests" for a single shot, --resume, --model opus --max, and --no-permissions --json for CI with NDJSON output. It ships 34 built-in tools, twelve slash commands, TOML config at ~/.abstract/config.toml and .abstract/config.toml, session persistence in Claude Code-compatible JSONL, and MCP server support. The README's comparison table reports startup at 32ms warm against 266ms, a 6.0 MB binary against 174 MB, and 4.9 MB RSS against 333 MB, with all numbers attributed to run_tool_bench.sh --full and a REPORT.md under crates/abstract-cli/benchmarks/. The startup and footprint gaps are plausible for a Rust binary against a Node one, and they are the kind of claim a reader can reproduce locally in a few minutes. The memory-recall row, 98 microseconds against 7,545 milliseconds, compares two different operations: an index lookup and a model call. That is the point the README is making, but the ratio in the table reads as a like-for-like speed comparison when it is not. Also note that the tool dispatch range, 0.02-17ms against 5-265ms+, is wide enough on both sides that it says little on its own.

Where Cersei is the wrong choice

The install section is the first limitation. Both dependency snippets use git = "https://github.com/pacifio/cersei" rather than a version from crates.io, and the repository metadata shows no releases retrieved. A git dependency means your lockfile pins a commit, upgrades are manual, and there is no semver contract to reason about. For a library that sits in the middle of your agent loop, that is a real maintenance cost. Second, the README describes the project as a reverse-engineered Rust port of Claude Code's architecture. That is an honest description and also a warning: behaviour is anchored to another product's design, and the port has to track it. Third, the documentation is uneven. The homepage is listed as cersei.tryatlas.cc/docs while the README links https://cersei.pacifio.dev/docs, and the excerpt available here stops mid-signature in the ToolExecute example, so the full custom-tool contract cannot be confirmed from this material. Fourth, if you want a finished agent rather than an SDK, the SDK layer is overhead: Abstract already exists, and writing your own loop on top of Cersei means you own compaction, permission prompts and session handling that Abstract has already solved.

Alternatives and the actual difference in approach

The obvious comparison is Claude Code itself, which the README positions as the thing Cersei can replace. The difference is not features, it is control flow. Claude Code is a CLI you invoke and configure; its custom tools arrive as plugins, and its provider is Anthropic only. Cersei is a library you link, its tools are Rust types implementing a trait, and providers are swappable through the Provider trait. If your requirement is that the agent runs inside your process with your own permission logic, the CLI form is disqualifying regardless of how good it is. OpenCode is the closer comparison on paper: multi-provider, TypeScript, CLI. Choosing between them is mostly a language decision, with the caveat that Cersei's tool implementations are Rust and therefore usable from any Rust binary without an FFI boundary or a sidecar process. If your application is not Rust, the SDK's main advantage disappears and you would be embedding a foreign runtime for no gain. On the memory axis, the graph index is the distinguishing mechanism against both CLIs; if you do not enable the graph feature, Cersei's memory story collapses back to files and sessions, which is roughly what the alternatives already do.

Editorial conclusion

Adopt Cersei if you are writing a Rust program that needs an agent inside it, or if you want a CLI whose permission policy, tool set and provider you control at the call site rather than through plugins. Do not adopt it if you need a stable published crate version, or if your team is not already comfortable maintaining Rust dependencies: the install instructions point at a git dependency, not a release on crates.io. Before committing, verify three things in the repository itself: whether the derive macro's generated module path has been fixed, whether the benchmark script and REPORT.md are present on the branch you pin, and whether the MIT licence text matches the crates you actually pull in.

Official sources

  1. Issues
  2. License: MIT
  3. pacifio/cersei on GitHub
  4. Project website
  5. README
Community notes

Community notes