# cel-brief
The per-turn LLM briefing layer. Assemble memory + perception + history + tools + user message into one budgeted, governed, receipted bundle from pluggable streams.
**Status:** Phases 1–4 shipped — core types, the `Source` trait and all built-in sources, the `Governance` trait, and the `BriefBuilder` (tokenizer + priority/budget pruning + `BriefReceipt`). The crate is feature-complete for assembling per-turn briefs.
## Why
Every non-trivial AI agent — chat, code, computer-use, robotics — has to decide what to put in the prompt: memory retrievals, current screen state, recent actions, tool schemas, the user's message. Today everyone solves it inside the agent loop, ad-hoc, with string concatenation and homegrown budget code. `cel-brief` is the abstraction that fills that gap.
## Three commitments
1. **Everything is a `Source`.** Memory, perception, history, tools — all the same trait. Plug in [`cel-memory`](../cel-memory), Mem0, Letta, or your own — `cel-brief` doesn't care.
2. **Structured output, not a string.** `Brief { messages, tools, system, receipt }` is provider-agnostic; renderers map to OpenAI / Anthropic / local-model wire formats.
3. **Governance and budget are first-class.** Importance scoring, redaction hooks, token budgets, receipts — built in, not bolted on.
## Comparison
| Pluggable sources | ✓ trait-based | ✗ template strings | partial (retrievers only) | n/a |
| Token budgeting | ✓ priority-aware, per-source floor | ✗ caller's problem | ✗ caller's problem | ✗ rebuilt each turn |
| Importance-aware pruning | ✓ `[0.0, 1.0]` per contribution | ✗ | ✗ | ✗ |
| Governance / redaction hooks | ✓ `Governance` trait + receipts | ✗ | ✗ | ✗ |
| Receipts (what the model saw and why) | ✓ `BriefReceipt` with per-source stats | ✗ | ✗ | ✗ |
| Provider-agnostic output | ✓ structured `Brief` | partial | ✓ | depends |
| Async fan-out across sources | ✓ `async fn contribute` | ✗ sync | ✓ | n/a |
| Memory integration as a Source | ✓ `MemorySource` over any `MemoryProvider` | partial (`Memory` class) | ✓ | n/a |
| Perception / screen state as a Source | ✓ `PerceptionSource` over any backend | n/a | n/a | n/a |
| Language | Rust | Python | Python | n/a |
## Built-in sources
| `SystemPromptSource` | default | Critical | Static system text. Never redactable. |
| `UserMessageSource` | default | Critical | Pulls `ctx.user_message`. Never redactable. |
| `ToolCatalogSource` | default | High | Owns `Vec<ToolSchema>`. |
| `HistorySource<H>` | default | Normal | Window of past N entries from any `HistoryStore`. Redactable. |
| `MemorySource<P>` | `memory` | Normal | Hybrid retrieval over any `cel_memory::MemoryProvider`. Redactable. |
| `PerceptionSource<P>` | `perception` | High | Defines the `PerceptionSnapshot` trait; downstream runtimes adapt their own perception engine (e.g. `cel-cortex`) into it. Redactable. |
## Quick start
```rust
use cel_brief::{
BriefContext, BriefError, Source, SourceError, SystemPromptSource, TokenBudget,
UserMessageSource,
};
let ctx = BriefContext::new(TokenBudget::default())
.with_user_message("Hello!");
let sys = SystemPromptSource::new("You are a helpful assistant.");
let user = UserMessageSource::new();
let cs = sys.contribute(&ctx).await?;
assert_eq!(cs.len(), 1);
# Ok::<_, BriefError>(())
```
See [`examples/no_cellar.rs`](examples/no_cellar.rs) for a self-contained hand-fanout (no other Cellar crates) and [`examples/with_memory.rs`](examples/with_memory.rs) for the `cel-memory` integration:
```sh
cargo run -p cel-brief --example no_cellar
cargo run -p cel-brief --features memory --example with_memory
```
## `BriefBuilder`
The `BriefBuilder` fans out to every registered source, tokenizes and prunes to budget, runs governance, and returns a `Brief` plus its `BriefReceipt`:
```rust
let brief = BriefBuilder::new()
.source(SystemPromptSource::new("You help with code."))
.source(UserMessageSource::new())
.source(MemorySource::new(memory.clone(), "embedded", 8))
.source(ToolCatalogSource::new(tools))
.governance(NoOpGovernance) // swap in your own Governance
.budget(TokenBudget::new(8000, 1024))
.build(ctx).await?;
let response = openai.chat(brief.to_openai_request()).await?;
println!(
"brief receipt: {} tokens, {} dropped, {} redactions",
brief.receipt.total_tokens,
brief.receipt.dropped.len(),
brief.receipt.redactions.len(),
);
```
## Governance
`Governance::review(&mut draft, &ctx)` runs after budget pruning, before the brief is returned. The verdict is one of:
- `Allow` — the brief is fine as-is.
- `Redacted(Vec<RedactionRecord>)` — the hook mutated redactable content; the records describe what changed and which rule did it.
- `Rejected(String)` — policy violation; the builder returns `BriefError::Rejected`.
The default `NoOpGovernance` always allows. Production callers (e.g. the Cellar daemon's agent runtime) plug in a real implementation that consults their rules engine.
## Features
- `memory` — enable `MemorySource<P>` (depends on [`cel-memory`](../cel-memory)).
- `perception` — enable the `PerceptionSnapshot` trait + `PerceptionSource<P>`. Perception backends live downstream: a runtime adapts its own live perception engine (e.g. `cel-cortex`) into a `PerceptionSnapshot`. This feature adds no dependency on any perception crate.
## Benchmark
A microbenchmark for hand-assembled briefs lives at [`benches/build.rs`](benches/build.rs). Target: under 50 ms p95 for a realistic brief once the `BriefBuilder` ships. Run with:
```sh
cargo bench -p cel-brief --features memory
```
## License
Apache-2.0