Source: https://docs.any.org/agents/memory.html
# Memory and recall
The agent's memory is two channels with different jobs. **History** (turns and chunk summaries) keeps everything verbatim and drillable; **memory items** hold only distilled, stable facts. Recall composes both, plus the space's own content, through one surface.
## Memory, history and context
| Layer | What it holds | Lifetime |
| --- | --- | --- |
| Application data | tasks, documents, contacts, and other structured records | stored in the space |
| History | original conversation turns and summaries of older turns | stored on each chat's log object |
| Memory items | selected facts, preferences, decisions, and lessons | stored on the agent's brain object |
| Model context | instructions, selected history and memories, current message, tool results | assembled for a model call |
A stored record becomes model context only when the harness selects it; a fresh run has no Python state and rebuilds its context from these records. The stores and runnable queries are in [Agent data](https://docs.any.org/agents/agent-data.md.txt).
## Memory items
A memory item is a record in the `agent_memory_items` dataset on the brain object of the bao space. A fact about another space names that space in its `context` or `tags`.
Saving requires `category` (an open slug set such as preference, decision, lesson, or fact) and `context` (a one-line fact). Optional fields are `body`, `confidence`, `importance`, `tags`, `entities`, `keywords`, `edges` (typed links to other objects), and `validFrom`. User-stated facts outrank inferred ones.
Save policy, taught by the `_memory` skill (the write path enforces the required fields and value ranges):
| Save | Skip |
| --- | --- |
| stable preferences | greetings, meta-chatter |
| decisions **with the why** | restatements of what was just said |
| durable domain facts | anything already in history (it is drillable) |
| hard lessons, shipped outcomes | low-confidence speculation |
Budget is ~1–2 saves per turn. Episodes and session summaries never become memory items — that is the history channel's job.
### Save a fact from a program
Guest code with the `agent` overlay configured. `space` is the space recall searches for context; the item itself always lands on the agent space's brain:
```python
c = use("agent:any@v1")
r = use("agent:recall@v1").recall(c, space)
m = use("agent:memory@v1").memory(c) # no space: the brain is the bao space's
m.save_with_dedup({"category": "decision",
"context": "picked sqlite for the importer: simpler ops",
"confidence": 8}, recall=r)
# → {itemId, action} or {deduplicated: true, mergedInto: "", action: "merge"}
```
### Dedup on save
Every save first runs recall over scope `agent`, then a cheap classify-tier judge decides *same fact?* → **merge** (evolve the existing item), **supersede** (new item plus a `supersedes` edge) or **create**. A `{deduplicated: true}` reply is a success. Merges are humble: a machine-sourced candidate never overwrites stored text, never lowers confidence, and unions tags and edges. There is no similarity threshold — the judge decides.
## Recall: one surface, three axes
`recall@v1` is read-only and binds to one space; its memory source is always the bao space's brain:
- **Semantic** — `r.search(q, scopes=["agent", "history", "basic", "email"])` over the [search index](https://docs.any.org/search/index.md.txt); `r.hydrate(hits)` turns hits into full records in one read.
- **Temporal** — `r.by_period(from, to)` merges memory items (`validFrom`), turns (`createdAt`) and chunks overlapping the range.
- **Graph** — `r.neighbors(id)` walks forward relation properties plus the server's backlinks, types and properties named by xKey.
- **Drill-down** — a chunk expands to its children (chunks or raw turns); a turn's `traceRef` opens the run.
Every memory item auto-recall injects gets its `accessCount` bumped, and so does an item a deliberate dig actually uses (`m.bump_access`), so the store measures its own usefulness.
## Auto-recall
Prompt guidance alone does not produce memory behaviour — an agent asked nicely saves and searches only when memory is the topic. So recall is structural: at the start of every turn `autorecall@v1` runs `recall.search(user_message)` (index-backed, no model call; the query embedding comes from the server's configured [embedder](https://docs.any.org/search/embedders.md.txt)) and injects the top hits, budget-capped.
The injection is framed as a synthetic `run_cell` call whose code is the literal recall idiom, plus a digest-shaped result — evidence the model weighs and can discount as stale, not prompt truth. Memory hits render as distilled facts with provenance date and confidence; history hits as "related past discussion, " pointers with a drill-down handle. Guards: hits already inside the boot window are skipped (auto-recall is the topical channel; the window owns recency), a relevance threshold means generic messages inject nothing, and every injection is logged to `agent_roi_injections`.
## History: turns and the chunk pyramid
Turns are append-only records on the chat's log child. `rollup@v1` (an hourly cron) summarises complete batches of ten turns into level-1 chunks and ten level-1 chunks into a level-2 chunk — level-2 summaries are built from child summaries only.
```jsonc
{"seq": 12, "level": 1, "fromSeq": 40, "toSeq": 49,
"summary": "…", "periodStart": …, "periodEnd": …, "unitsCovered": 10}
```
Same-level chunks are contiguous and non-overlapping, and every summary keeps explicit pointers to what it covers, so drill-down never dead-ends. The boot window is token-budgeted over this pyramid: newest raw turns, then L1, then L2 — all history present at decreasing resolution, at roughly 11% record overhead.
## Background jobs
All maintenance runs as standing [triggers](https://docs.any.org/scheduling/index.md.txt) on the election-active device, each fire a traced run with an `agent_runs` summary; when the bird's-eye view is stale, a run says why. A stopped device runs nothing, but the trigger records and everything they produced stay in the space.
| Program | Schedule | State |
| --- | --- | --- |
| `extraction@v1` | every 15 minutes, over newly persisted turns | on — proposes only stable-fact shapes, through the dedup judge, provenance `fromSeq`, confidence capped at 6 |
| `rollup@v1` | hourly | on — the chunk pyramid |
| `linkgen@v1` | hourly | on — seed-search neighbours, classify-tier proposes typed links from the curated vocabulary (`relates_to`, `caused_by`, `supersedes`, `decided_in`, `part_of`, `owned_by`, `discussed_in`); never invents edge types |
| `evolution@v1` | every 6 hours | ships disabled — refreshes `context`/`tags` from linked neighbours |
| `reflection@v1` | daily | ships disabled — synthesises never-recalled clusters into insights, flags contradictions |
| `decay@v1` | daily | ships disabled — salience halves per idle half-life, never deletes |
Disabled jobs ship their mechanism and activate one at a time behind an eval; scoring fields are recorded before they are consumed.
## What leaves the device
Dedup, extraction and summarisation are model calls: whatever provider serves the configured tier reads their inputs. The recall index is a second path — the server's default `index.embedder: auto` embeds online with a local fallback, `local` keeps it on the device once the model is present ([Embedders](https://docs.any.org/search/embedders.md.txt)).
> **Why it matters.** The memory is a dataset in your encrypted space, not a vendor's RAG store. You can query it (guest code resolves the key `agent_memory_items`; raw HTTP uses the discovered `_agent_memory_items` storage collection), subscribe to it, audit what the extractor saved and from which turn, and delete an item with a normal record delete. See [Agent data](https://docs.any.org/agents/agent-data.md.txt#reading-it-yourself) for the fields and [Runs and monitoring](https://docs.any.org/scheduling/runs-and-monitoring.md.txt) for a maintenance job's run.