Memory & Knowledge
Aura's native memory system gives every agent a persistent brain and filing cabinet, so it doesn't start each task from a blank slate. Six pieces work together — hybrid recall, layered distillation, CodeGraph, the Wiki, a concept knowledge graph, and team access control — and all of it runs on Aura's own stack: SQLite + FTS5 with optional embeddings, and no external memory service. It works identically in the desktop app and in a deployed daemon container. Everything is configured and browsable under Settings → Memory.
How it fits together
Think of it as one persistent memory with several access paths. Facts about you and your projects accumulate in a single store; recall finds the relevant ones and injects them each turn; distillation keeps that store compact; and three specialized indexes — CodeGraph, the Wiki, and the concept graph — give the agent structured ways to look things up. A visibility tag on every row gates all of it.
| Piece | What it does | How the agent uses it |
|---|---|---|
| 1. Hybrid recall | Finds the relevant stored facts | Injected automatically each turn as ## Recalled Facts |
| 2. Layered distillation | Rolls raw facts up into summaries and a profile | Higher layers flow back through recall |
| 3. CodeGraph | Symbol + import index of a project | codegraph_query tool |
| 4. Wiki | Curated markdown knowledge pages | wiki_search / wiki_read / wiki_write tools |
| 5. Team ACL | Private / team / global visibility on every row | Gates recall and every query |
| 6. Concept graph | Entity → relation → entity triples | concept_query tool + interactive graph view |
Hybrid recall — the memory that actually finds things
As you work, the agent stores small facts about you and your projects. At the start of each turn it needs the relevant ones, so it searches two ways at once and blends the results:
- Keyword (lexical) — an FTS5 index over the fact store, ranked with
bm25(). Exact words, like Ctrl+F. - Meaning (semantic) — vector cosine over stored embeddings, so semantically related facts surface even without shared words. Only active when embeddings are enabled.
The two candidate lists are fused with Reciprocal Rank Fusion (RRF, k = 60)
in core/memory_search.rs::hybrid_recall. Results are project-isolated,
ACL-filtered, and bounded by a max-item count plus a character budget. Each turn,
core/task_agent.rs runs recall against your message and appends a
## Recalled Facts block to the system prompt (gated on memory_enabled).
No tool call is needed — it happens implicitly. Settings has a Test recall
box to preview exactly what would surface for a given query.
core/embeddings.rs handles both the
openai and legacy request shapes and stores little-endian f32 blobs.
A background backfill (spawn_embedding_backfill) vectorizes existing facts after
you enable embeddings, so recall improves without re-entering anything. With embeddings off,
recall is keyword-only.
Layered distillation — tidy the pile of sticky notes
Raw facts pile up and repeat. Distillation periodically rolls them up in three layers so memory stays sharp and compact instead of turning into noise:
| Layer | Contents | Produced by |
|---|---|---|
| L1 | Raw atomic facts | Captured as you work |
| L2 | Consolidated summaries (1–5 durable summaries per scope) | Rolled up from un-distilled L1 facts |
| L3 | A single high-level profile (e.g. "builds Rust backends, prefers minimal UIs, deploys via Docker") | Synthesized from L2, one per scope |
core/memory_distill.rs runs a background loop (its own thread + runtime, gated on
memory_distill_enabled), and you can also trigger it on demand with
Distill now (the memory_distill_now command). The L1→L2 step
consolidates via core/llm_util.rs using your selected provider — it's skipped on
CLI-tool models — then marks the sources distilled = 1. The L2→L3 step writes
one profile per scope with a deterministic id, so it overwrites in place. Because higher
layers write back into the same fact store, they flow through hybrid recall automatically.
CodeGraph — a map of your codebase
Point CodeGraph at a project folder and Scan. It indexes every function,
class, and type, plus each file's imports, so the agent (or the Settings search box) can
instantly answer "where is login() defined?" — file and line — without
reading the whole tree.
core/codegraph.rs::scan_project walks the tree (skipping node_modules,
target, .git, dist, and similar, and capping file size
and count), extracting symbols and import edges for Rust, TypeScript/JavaScript,
Python, and Go via lightweight heuristics — no full parser. Symbols land in
code_symbols (with an FTS index) and import edges in code_edges.
Re-scanning replaces that project's index. The agent queries it with the
codegraph_query tool, which returns kind name — file:line.
The Wiki — a shared notebook
The Wiki is curated markdown — architecture notes, runbooks, conventions — that gives durable project knowledge a permanent home instead of leaving it buried in old chats. Both you (in Settings) and the agent (via tools) can read, write, search, and edit pages.
core/wiki.rs stores pages in wiki_pages (plus an FTS index). Slugs
are unique per project scope, and wiki_write upserts by title. Page content is
injection-scanned before it ever reaches the agent, so a malicious note can't smuggle
instructions into the prompt. The agent reaches it through three tools —
wiki_search (full-text), wiki_read (a full page by title or slug),
and wiki_write (create or update).
Concept knowledge graph
Where hybrid recall retrieves facts that are similar to your query, the concept graph captures the relationships between things. An LLM pass extracts subject→relation→object triples from your facts, which become nodes (entities) connected by typed, directed edges. This lets the agent answer "what is related to X" or "how does X connect to Y" — not just "what's similar to X".
Build it from Settings → Memory → Knowledge Graph (it needs some
facts plus a cloud model). core/concept_graph.rs runs the triple-extraction pass
and writes concept_nodes (entities, with a mentions count) and
concept_edges (typed, directed, and weighted). Everything is ACL-scoped like the
rest of memory.
Then explore the interactive force-directed view rendered by
src/components/KnowledgeGraph.tsx — a canvas graph with no external
libraries. Drag to pan, scroll to zoom, drag nodes, and hover to highlight a node's
connections; nodes are colored by entity type and sized by mention count. During a task the
agent traverses the same graph via the concept_query tool, which returns the
relationships an entity participates in.
Team ACL — who can see what
Every fact, wiki page, code symbol, and concept carries a visibility tag, and recall plus every query respects it. This keeps things safe when multiple people or teams share one setup.
| Visibility | Who sees it |
|---|---|
| private | Just you (the local operator) |
| team | Only when the row's team_id matches your Active team setting |
| global | Everyone |
core/memory_search.rs::AclFilter builds a parameterless SQL predicate:
global and private rows are always visible to the local operator,
while team rows appear only when their team_id matches
active_team_id (team ids are sanitized before inlining). New writes inherit
active_team_id and default_memory_visibility, so once you set your
team scope, everything you capture is tagged correctly by default.
Agent tools
Five knowledge tools are available to the agent during any task (defined in
tools/memory_tools.rs, dispatched in agent/tool_executor.rs, and
advertised in the system prompt's tool surface). Hybrid recall is injected implicitly every
turn, so it needs no tool call. See Skills & Agent
Tools for the full tool catalog.
| Tool | Purpose |
|---|---|
codegraph_query | Find symbols by name; returns kind name — file:line |
wiki_search | Full-text search across wiki pages |
wiki_read | Read a full page by title or slug |
wiki_write | Create or update a page (upserts by title) |
concept_query | Traverse the knowledge graph — the relationships an entity participates in |
Settings
All of the following live under Settings → Memory (and mirror to the daemon):
| Setting | Default | Meaning |
|---|---|---|
memory_enabled | true | Master switch for recall injection |
memory_recall_max_items | 8 | Max facts injected per turn |
memory_recall_char_budget | 2000 | Character cap on injected memory |
memory_embeddings_enabled | false | Turn on the vector half of recall |
memory_embeddings_url / _model / _api_key / _format | — | Embeddings endpoint (openai or legacy shape) |
memory_distill_enabled | false | Background distillation loop |
memory_distill_interval_hours | 12 | Distillation cadence |
memory_distill_min_facts | 12 | Min L1 facts per scope before rollup |
active_team_id | "" | Team scope for new writes ("" = personal) |
default_memory_visibility | private | private / team / global |
codegraph_enabled | true | Enable CodeGraph and its tool |
wiki_enabled | true | Enable the Wiki and its tools |
Data model & API
All memory lives in the single SQLite database (~/Library/Application
Support/aura-workshop/aura-workshop.db on macOS, /data/aura-workshop.db in
the daemon image; WAL mode). No separate service, no vector database.
| Table | Holds | Key columns |
|---|---|---|
memory_facts | Atomic facts + distilled summaries/profiles | content, category, confidence, embedding (LE f32 blob), layer (1/2/3), distilled, project_path, team_id, visibility |
memory_facts_fts | FTS5 index over facts | trigger-maintained; bm25() ranking |
code_symbols / code_symbols_fts | CodeGraph symbols | name, kind, file, line, lang, team_id, visibility |
code_edges | Import edges | from_file, to_target, kind |
wiki_pages / wiki_pages_fts | Wiki pages | title, slug, content, tags, team_id, visibility |
concept_nodes | Knowledge-graph entities | name, node_type, mentions, team_id, visibility |
concept_edges | Knowledge-graph relationships | from_name, to_name, relation, weight, team_id, visibility |
pending_memory_reviews | Facts awaiting review before commit | source task + proposed fact |
Commands & REST (daemon parity)
Every capability is a Tauri command with a matching REST route, so the desktop app and a
headless daemon behave identically. Tauri commands include memory_search,
memory_stats, memory_layer_stats, memory_distill_now,
codegraph_scan, codegraph_query, codegraph_stats,
wiki_list, wiki_get, wiki_upsert,
wiki_search, wiki_delete, wiki_stats, plus the
concept-graph commands. Each mirrors to REST under /api/memories/*,
/api/codegraph/*, /api/wiki/*, and
/api/concept-graph/* — see the API Reference.
Where it runs
Startup wiring lives in both the desktop entry point and the headless daemon: apply the embedding config, start the embedding backfill, start the distillation loop, and create the CodeGraph, Wiki, and concept-graph tables. Memory therefore works the same way whether you run the desktop app or a deployed daemon container — see Deployment & Security.