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.