Cersei Rust SDK

repository·main·Indexed 19 days ago

https://github.com/pacifio/cersei

A complete Rust SDK for building production-grade coding agents. Cersei provides composable building blocks including tool execution, LLM streaming, sub-agent orchestration, and a three-tier memory system (flat files, CLAUDE.md hierarchy, and graph-backed memory). It includes cersei-compression for reducing tool output token usage and supports multiple LLM providers, custom tool definition via #[derive(Tool)], and MCP (Model Context Protocol) server integration.

Tokens
211.8K
Snippets
651
Records
870
Agent score
64%

What's inside Cersei

  1. Overview of cersei-vms sandbox and VM isolation

    main

    cersei-vms provides a pluggable sandbox runtime layer for Cersei coding agents. It prevents agents from executing shell commands or editing files directly on the host machine, providing isolation and security.

    Key Capabilities

    • Sandbox Runtimes: Supports different execution environments via the SandboxRuntime trait.
    • Per-sandbox Surface: Each Sandbox provides access to commands() (for running, streaming, or signaling processes) and filesystem() (for reading, writing, listing, and managing files).
    • Cross-sandbox Primitives: Allows parallel agents to communicate and share state safely through host-mediated primitives without direct network links:
      • Volume: Host-mounted directories.
      • Mailbox: Broadcast pub/sub messaging.
      • KvStore: Versioned Content-Addressable Storage (CAS).
    • Snapshots: Capture the state of a sandbox using Sandbox::snapshot() -> SnapshotId and restore it using SandboxRuntime::restore(&id). For Docker, this uses docker commit; for local runtimes, it copies the directory. Manifests are stored in ~/.cersei/vms/snapshots/.
    use cersei::prelude easily;
    use cersei::vms::prelude easily;
  2. Compare Abstract architecture performance vs Claude Code

    main

    The abstract-cli (referred to as 'Abstract' in benchmarks) is designed for high-performance agentic workflows. Key architectural advantages include:

    • Low Startup Latency: Average startup time is ~32ms, significantly faster than Claude Code (~266ms).
    • Minimal Resource Footprint: A single 6.0 MB static Rust binary with a median peak memory (RSS) of ~4.9 MB.
    • High-Speed Tool Dispatch: SDK-level tool execution (e.g., Read, Write, Edit) occurs in sub-millisecond time (e.g., Read at 0.09ms), whereas Claude Code relies on process forking (~265ms).
    • Efficient Memory Management: Uses a Graph-based memory backend (Grafeo) that provides relationship-aware queries with negligible overhead (<3%) compared to file-only scans.
    • Lean System Prompts: Uses ~2,200 tokens for system prompts and tool definitions, saving approximately 5,800 tokens per session compared to Claude Code.
  3. What is cersei-compression and how does it work?

    main

    cersei-compression is a structural and command-aware compression utility for tool outputs within the Cersei SDK. It is designed to sit between a tool's raw execute() result and the agent's cap_tool_result() truncation step.

    Its primary purpose is to reduce token usage by trimming 60–90% of typical tool output, such as comments, ANSI escape codes, blank lines, noisy progress messages, or unchanged boilerplate, without losing the essential information required by the agent.

  4. What is AgentRL and how does it work?

    main

    AgentRL is a self-evolving orchestration layer for Cersei that transforms single-shot coding agents into self-improving systems. When an agent fails a task, AgentRL performs the following loop:

    1. Failure Tracing: The ExecutionGraph distills a FailureTrace from the agent's event stream.
    2. Planning: A PlannerAgent generates multiple fix proposals based on the trace.
    3. Sandboxed Execution: Proposals are executed in isolated environments (using cersei-vms or local directories) to prevent interference.
    4. Verification: An independent Verifier checks if the proposal actually works.
    5. Promotion & Registration: The successful proposal is promoted to the working directory and registered in the ToolRegistry as a reusable DynamicTool.

    This allows the agent to solve similar future problems via tool recall rather than re-derivation, reducing LLM spend and latency.

  5. What is AgentTemplate language?

    main

    AgentTemplate is a tiny functional Domain Specific Language (DSL) designed for LLMs to author and the Cersei runtime to execute. It allows agents to perform chained file, network, or agent operations safely. It is the programmable surface of AgentRL, enabling LLMs to emit short programs that can be executed and even registered as reusable tools.

    It is provided via the cersei-agentlang crate, which is re-exported as cersei::agentlang when the agentlang or agentrl feature is enabled.

  6. What is a Provider and how does it work?

    main

    A Provider is an abstraction over an LLM backend. It encapsulates authentication, request formatting, SSE (Server-Sent Events) streaming, and capability discovery. By using the Provider trait, Cersei can interact with different LLM backends (like Anthropic or OpenAI) using a unified interface.

    The Provider trait requires implementing the following methods:

    • name(): Returns the provider's name.
    • context_window(model): Returns the token limit for a specific model.
    • capabilities(model): Returns a ProviderCapabilities struct describing supported features (streaming, tool use, etc.).
    • complete(request): Asynchronous method for streaming responses via CompletionStream.
    • complete_blocking(request): Asynchronous method for non-streaming responses via CompletionResponse.
    • count_tokens(messages, model): Returns the token count for a set of messages.
    #[async_trait]
    pub trait Provider: Send + Sync {
        fn name(&self) -> &str;
        fn context_window(&self, model: &str) -> u64;
        fn capabilities(&self, model: &str) -> ProviderCapabilities;
        async fn complete(&self, request: CompletionRequest) -> Result<CompletionStream>;
        async fn complete_blocking(&self, request: CompletionRequest) -> Result<CompletionResponse>;
        async fn count_tokens(&self, messages: &[Message], model: &str) -> Result<u64>;
    }
  7. Manage memory confidence decay and revalidation

    main

    Memories in Cersei automatically lose confidence over time based on a decay_rate (default is 0.01 per day). This ensures that stale or unverified information does not dominate the agent's context.

    To prevent decay and reset the clock for a specific memory, use revalidate_memory.

    // Memory stored with confidence 0.95
    let id = graph.store_memory("API uses v2 endpoints", MemoryType::Project, 0.95)?;
    
    // Revalidate to reset the clock
    graph.revalidate_memory(&id)?;
    // Now effective_confidence resets to the stored value
  8. How event distribution works in Cersei

    main

    The agent loop emits AgentEvents, which are distributed through three primary mechanisms:

    1. on_event callback: A synchronous callback executed directly within the loop.
    2. Broadcast channel: An asynchronous, multi-consumer channel (if enabled) that allows multiple subscribers to receive events.
    3. Reporters: A collection of Arc<dyn Reporter> implementations (e.g., ConsoleReporter, JsonReporter, MetricsReporter) that handle event processing for specific outputs.
  9. How the Cersei agentic loop works

    main

    The Agent::run("prompt") method initiates an agentic loop that manages the lifecycle of a conversation. The flow follows these steps:

    1. Session Loading: If Memory is configured, the session is loaded.
    2. Completion Request: An LLM request is built and sent via a Provider.
    3. Stream Accumulation: A StreamAccumulator collects StreamEvents (like TextDelta, ThinkingDelta, or ToolUse).
    4. Turn Processing: Once a model turn completes, the agent checks the stop_reason:
      • EndTurn: The loop breaks.
      • ToolUse: The agent dispatches tools. For each tool, it runs PreToolUse hooks, checks the PermissionPolicy, executes Tool.execute(), and runs PostToolUse hooks.
      • MaxTokens: The agent injects a continuation and continues the loop.
    5. Session Saving: If Memory is configured, the updated session is saved.
    6. Output: Returns an AgentOutput containing the message, usage, stop reason, turns, and tool calls.
  10. Understand Abstract's memory architecture

    main

    Unlike Claude Code and Codex CLI, which rely on LLM calls (e.g., Sonnet or GPT) to rank and retrieve relevant memory files, Abstract uses an embedded graph database for memory operations. This allows for extremely low-latency indexed lookups without API costs or per-turn LLM overhead.

    OperationAbstract (Graph)Claude Code (Sonnet)Codex CLI (GPT)
    Memory recall (agent)98us7545ms5751ms
    Memory write (agent)28us20687ms5882ms
    MEMORY.md load9.6us17.1ms
    File scan (100 files)1.2ms26.6ms
  11. Use the Sub-Agent pattern for parallel research

    main
    To research multiple sub-topics concurrently, use a 'Coordinator' agent. The coordinator's system prompt should instruct it to use the Agent tool to spawn parallel research sub-agents. Each sub-agent operates independently to search, read, and summarize its specific sub-topic, and the coordinator then synthesizes all results into a final report.
  12. Understand Cersei Memory and session persistence

    main

    Memory in Cersei provides session persistence and retrieval, which allows for resumable conversations and long-term knowledge storage. By associating an Agent with a Memory backend and a specific session_id, the agent can automatically load previous conversation history when a new session starts and save the updated history when the session completes.

    Agent Lifecycle with Memory

    1. On agent.run(): If memory and session_id are configured, the agent calls memory.load(session_id) and prepends existing messages to the current conversation.
    2. During execution: New messages (user prompts, assistant responses, and tool results) are accumulated.
    3. On completion: The agent calls memory.store(session_id, all_messages) to persist the full conversation.

    Emitted Events

    You can monitor the memory lifecycle via these events:

    • AgentEvent::SessionLoaded { session_id, message_count }: Emitted after loading previous messages.
    • AgentEvent::SessionSaved { session_id }: Emitted after persisting the conversation.