> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory

> Complete guide to Agentium unified memory — sessions, summaries, user facts, profiles, entities, decisions, graph, procedures, and context budgeting.

## Overview

Memory gives agents the ability to remember. Without it, every `agent.run()` is a blank slate. With it, agents maintain conversation history, learn user preferences, track entities, remember decisions, and build knowledge graphs.

All memory features share a single `storage` backend and are configured through the `memory` field on `AgentConfig`.

## Quick Start

```typescript theme={null}
import { Agent, openai, InMemoryStorage } from "@agentium/core";

const agent = new Agent({
  name: "assistant",
  model: openai("gpt-4o"),
  memory: {
    storage: new InMemoryStorage(),
  },
});

// First conversation
await agent.run("My name is Alex", { sessionId: "s1", userId: "u1" });

// Later — the agent remembers
const result = await agent.run("What's my name?", { sessionId: "s1", userId: "u1" });
// "Your name is Alex."
```

## UnifiedMemoryConfig

The complete memory configuration. Only `storage` is required — everything else is opt-in.

<Note>
  **Reading the `boolean | XConfig` types.** Several features (like `summaries`, `userFacts`, `entities`) accept a value of type `boolean | SomeConfig`. That's a union — it means you have **three ways** to set each one:

  ```typescript theme={null}
  entities: false                  // OFF (or just omit it entirely)
  entities: true                   // ON with default settings  ← the shortcut
  entities: { namespace: "acme" }  // ON with custom settings
  ```

  So when the table says `boolean | EntityConfig`, both `entities: true` and `entities: { namespace: "acme" }` are valid — `true` is simply a shorthand for "turn it on, use the defaults." Pass the config object only when you want to change a default. This is why you'll see both forms throughout the examples.

  (Features that always need a backend — `learnings` needs a `vectorStore`, `graph` needs a `GraphStore` — take only a config object, not a boolean, because there's no sensible default without that backend.)
</Note>

| Property        | Type                           | Default               | Description                                                                                                                                                                                         |
| --------------- | ------------------------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `storage`       | `StorageDriver`                | **Required**          | Storage backend shared by all memory subsystems. See [Storage](/storage/overview)                                                                                                                   |
| `maxMessages`   | `number`                       | `50`                  | Maximum messages kept in session history. Oldest are trimmed first when exceeded                                                                                                                    |
| `maxTokens`     | `number`                       | `undefined`           | Token-based history trimming. When set, history is trimmed to fit within this token count (instead of message count)                                                                                |
| `summaries`     | `boolean \| SummaryConfig`     | `true` (ON)           | Long-term conversation summaries. Auto-summarizes overflow messages. Pass `false` to disable                                                                                                        |
| `userFacts`     | `boolean \| UserFactsConfig`   | `false` (OFF)         | Extract and store facts about the user (preferences, location, interests). Pass `true` for defaults                                                                                                 |
| `userProfile`   | `boolean \| UserProfileConfig` | `false` (OFF)         | Structured user profile (name, role, timezone, language). Pass `true` for defaults                                                                                                                  |
| `entities`      | `boolean \| EntityConfig`      | `false` (OFF)         | Entity memory — companies, people, projects extracted from conversations                                                                                                                            |
| `decisions`     | `boolean \| DecisionConfig`    | `false` (OFF)         | Decision audit trail — what the agent decided and why                                                                                                                                               |
| `learnings`     | `LearningsConfig`              | `undefined` (OFF)     | Vector-backed learned insights from interactions. Requires a `vectorStore`                                                                                                                          |
| `corrections`   | `CorrectionsConfig`            | `undefined` (OFF)     | Structured human corrections of agent output, retrieved at inference time. Requires a `vectorStore`. See [Correction Capture](/memory/corrections)                                                  |
| `graph`         | `GraphMemoryConfig`            | `undefined` (OFF)     | Knowledge graph — entity-relationship graph with temporal awareness. Requires a `GraphStore`                                                                                                        |
| `procedures`    | `boolean \| ProceduresConfig`  | `false` (OFF)         | Procedural memory — records successful tool-call workflows for reuse                                                                                                                                |
| `contextBudget` | `ContextBudgetConfig`          | `undefined`           | Token budget allocation for how memory context is distributed                                                                                                                                       |
| `model`         | `ModelProvider`                | Agent's primary model | Separate (cheaper) model used for background extraction (summaries, facts, entities, etc.)                                                                                                          |
| `timezone`      | `string`                       | `undefined` (UTC)     | IANA timezone (e.g. `"Asia/Kolkata"`) used to anchor date-relative extraction (`"today"`, `"yesterday"`). **Always set in production** — otherwise users near midnight get the wrong date extracted |
| `tenantId`      | `string`                       | `undefined`           | Tenant identifier. Required for `scope: "tenant"` learnings/procedures to be saved and read back. See [Scope Hierarchy](#scope-hierarchy)                                                           |
| `eventBus`      | `EventBus`                     | Agent's event bus     | Auto-injected from the `Agent`. Memory mutations and extraction failures emit events here for observability. See [Observability](#observability)                                                    |

***

## Memory Architecture

Memory is not a single store — it's a **layered subsystem** with one orchestrator coordinating up to nine specialized stores over a shared storage backend.

```
        ┌──────────────────────────────────────────────────┐
        │                     Agent                          │
        │   beforeRun → buildContext() → LLM ⇄ tools        │
        │   afterRun  → appendMessages + background extract  │
        └────────────────────────┬───────────────────────────┘
                                 │
                                 ▼
        ┌──────────────────────────────────────────────────┐
        │                 MemoryManager                      │
        │  buildContext() · appendMessages() · afterRun()    │
        │  recall() · remember() · forget() · curator        │
        └───┬──────┬──────┬──────┬──────┬──────┬──────┬─────┘
            ▼      ▼      ▼      ▼      ▼      ▼      ▼
        Sessions Summary Facts Profile Entity Decision …
            │      │      │      │      │      │
            ▼      ▼      ▼      ▼      ▼      ▼
        ┌──────────────────────────────────────────────────┐
        │              StorageDriver interface               │
        │     MongoDB · Postgres · Redis · SQLite · …         │
        └──────────────────────────────────────────────────┘

        Learnings ──► VectorStore (Qdrant · Pinecone · …)
        Graph     ──► GraphStore  (Neo4j · InMemory)
```

### The memory lifecycle

Every `agent.run()` wraps the LLM call with two memory phases:

1. **Before the run — Context Assembly.** `MemoryManager.buildContext()` reads every enabled store, ranks the results, fits them within the token budget, and injects the result into the system prompt. Each block is wrapped in a `<memory section="…" scope="current_user">` sentinel so the LLM never conflates one store's data with another's.

2. **After the run — Background Extraction.** `MemoryManager.afterRun()` fires (non-blocking) and runs up to six parallel extractions against the *last 6 turns* of the conversation: user facts, profile, entities, learnings, graph, procedures. Failures emit `memory.error` events rather than failing silently.

```typescript theme={null}
// Conceptually, every run looks like this:
const ctx = await memory.buildContext(sessionId, userId, input, agentName);  // phase 1
const output = await llmLoop.run([systemPrompt + ctx, ...history, input]);
memory.afterRun(sessionId, userId, recentTurns, extractModel, agentName);     // phase 2 (async)
```

### The stores

| Store        | Holds                     | Default | Scope                                  |
| ------------ | ------------------------- | ------- | -------------------------------------- |
| Sessions     | Chat history              | ON      | `sessionId`                            |
| Summaries    | Compressed old history    | ON      | `sessionId`                            |
| User Facts   | "User lives in Mumbai"    | OFF     | `userId`                               |
| User Profile | Structured identity       | OFF     | `userId`                               |
| Entities     | Companies/people/projects | OFF     | `userId`                               |
| Decisions    | Audit trail of choices    | OFF     | `agentName` + `sessionId`              |
| Learnings    | Vector-backed insights    | OFF     | `userId` → **agent → tenant → global** |
| Procedures   | Reusable tool workflows   | OFF     | `userId` → **agent → tenant → global** |
| Graph        | Entity-relationship graph | OFF     | `userId`                               |

The same `memory` config works identically across `Agent`, `VoiceAgent`, and `BrowserAgent`. The only difference: voice agents load context once at session start (not per turn), because the realtime session is persistent.

***

## Sessions and History

Sessions are the foundation. Every `agent.run()` with a `sessionId` appends messages to a persistent history.

```typescript theme={null}
memory: {
  storage: new SqliteStorage("app.db"),
  maxMessages: 30,    // Keep last 30 messages (15 turns)
}
```

**What happens when `maxMessages` is exceeded**: The oldest messages are removed from the session. If `summaries` is enabled (the default), those removed messages are first summarized so the context isn't lost entirely.

**Token-based trimming**: Instead of counting messages, you can limit by tokens:

```typescript theme={null}
memory: {
  storage,
  maxTokens: 8000,   // Keep history within ~8K tokens
}
```

***

## Summaries

**Default: ON.** When session history overflows `maxMessages`, the overflow messages are summarized by the LLM and stored. These summaries are injected into the system prompt on future runs so the agent remembers older context.

### SummaryConfig

| Property    | Type     | Default | Description                                                             |
| ----------- | -------- | ------- | ----------------------------------------------------------------------- |
| `maxCount`  | `number` | `10`    | How many summary snippets to keep per conversation. Oldest pruned first |
| `maxTokens` | `number` | `2000`  | Token budget for the summary text shown to the agent each run           |

**Understanding the options:**

* **`maxCount`** caps how many "recap notes" exist for a single long conversation. Each overflow event creates one summary. More summaries = longer memory of the conversation's arc, but older ones eventually drop off. Lower it for short-lived chats; raise it for long-running threads (support cases that span weeks).
* **`maxTokens`** is the budget for how much of those summaries gets injected into the prompt. This directly affects cost — every run pays for these tokens. Lower it (e.g. `1000`) to save money; raise it (e.g. `4000`) when the conversation history is rich and the agent needs more of it.

```typescript theme={null}
// Default behavior (summaries ON)
memory: { storage }

// Smaller footprint — fewer, shorter summaries (cheaper)
memory: {
  storage,
  summaries: { maxCount: 5, maxTokens: 1000 },
}

// Disable summaries entirely
memory: {
  storage,
  summaries: false,
}
```

***

## User Facts

**Default: OFF.** Automatically extracts persistent facts about the user from conversations: preferences, location, profession, interests, communication style.

Facts are stored per `userId` and injected into the system prompt on every run for that user, across all sessions.

### UserFactsConfig

| Property   | Type     | Default | Description                                                                                |
| ---------- | -------- | ------- | ------------------------------------------------------------------------------------------ |
| `maxFacts` | `number` | `100`   | Maximum active facts kept per user. When exceeded, the least important + oldest are pruned |

**Understanding `maxFacts`:**

This is the ceiling on how much the agent remembers about one person. When a user accumulates more facts than this, the framework keeps the most **important and recent** ones (not just the newest — a high-importance fact like "allergic to peanuts" outranks a trivial recent one).

* **Default `100`** is plenty for most consumer apps.
* **Lower it (e.g. `30`)** to keep prompts lean and costs down when you only care about a handful of key preferences.
* **Raise it (e.g. `300`)** for power users or B2B accounts where the agent benefits from deep history. Higher values mean more storage and slightly larger prompts.

```typescript theme={null}
memory: {
  storage,
  userFacts: true,            // Enable with defaults (max 100 facts)
}

// Lean setup — keep only the most important 30 facts
memory: {
  storage,
  userFacts: { maxFacts: 30 },
}
```

**What gets extracted**: The LLM analyzes each conversation and extracts concrete facts like:

* "User prefers dark mode"
* "User is a software engineer in Mumbai"
* "User's favorite language is TypeScript"

**Contradiction handling**: If a new fact contradicts an old one (e.g., user moved from Mumbai to Berlin), the old fact is soft-deleted (`invalidatedAt` is set) and the new one takes over. Each invalidation records a **reason**:

* `"superseded"` — a newer fact about the same subject replaced it (e.g. "moved to Berlin" supersedes "lives in Mumbai"). Superseded facts disappear silently — they are never shown to the model.
* `"forgotten"` — the user explicitly asked the agent to forget the fact (e.g. "forget my birthday"). Forgotten facts are surfaced in a dedicated "the user asked you to forget these" block so the model knows not to restate them, and `recall_user_facts` will not return them.

This distinction matters: an over-cautious model that sees *both* the new fact and an old one in the "forget" block can refuse to answer at all. Separating the two reasons fixes that. Re-stating a previously forgotten fact **reactivates** it rather than dropping it.

**Timezone-aware dates**: facts like "my birthday is today" are resolved to an absolute date using the `timezone` config. Without it, a user near midnight gets the wrong date. Recurring events (birthdays, anniversaries) are stored without a year unless the user states one explicitly.

***

## User Profile

**Default: OFF.** A structured profile object with built-in fields (name, role, timezone, language) plus custom fields. More structured than user facts.

### UserProfileConfig

| Property       | Type       | Default | Description                                                              |
| -------------- | ---------- | ------- | ------------------------------------------------------------------------ |
| `customFields` | `string[]` | `[]`    | Extra named fields the agent is allowed to fill in, beyond the built-ins |

**Understanding `customFields`:**

The profile always tracks four built-in fields automatically: **name, role, timezone, language**. `customFields` lets you add your own domain-specific fields the extractor will look for.

* It's a **whitelist** — the agent can only populate the fields you list. It can't invent a `salary` field unless you ask for it. This keeps the profile clean and predictable.
* Use it for structured attributes your product cares about: `"company"`, `"plan"` (free/pro/enterprise), `"department"`, `"accountTier"`.
* Don't use it for free-form preferences ("likes dark mode") — those belong in [User Facts](#user-facts), which is unstructured by design.

```typescript theme={null}
memory: {
  storage,
  userProfile: true,  // Track only the built-ins: name, role, timezone, language
}

// Add product-specific fields
memory: {
  storage,
  userProfile: { customFields: ["company", "department", "plan"] },
}
```

<Tip>**Profile vs Facts:** use **Profile** for a fixed set of structured attributes (one value each, like a form). Use **Facts** for open-ended things you can't predict in advance.</Tip>

***

## Entity Memory

**Default: OFF.** Extracts companies, people, projects, and products mentioned in conversations. Each entity has facts, events, and relationships.

### EntityConfig

| Property    | Type     | Default    | Description                                            |
| ----------- | -------- | ---------- | ------------------------------------------------------ |
| `namespace` | `string` | `"global"` | A label that partitions entities into separate buckets |

**Understanding `namespace`:**

Entities are always scoped to the user who created them (privacy). `namespace` adds a **second, orthogonal partition on top of that** — think of it as which "team workspace" the entities belong to.

* **Leave it `"global"` (default)** if you have one product and don't need to separate entity sets. Simplest, works for most apps.
* **Set a per-tenant namespace** (`"acme"`, `"meridian"`) when one deployment serves multiple organizations and you want each org's entity knowledge kept in its own bucket.
* **Use a hierarchical path** (`"acme/engineering"`, `"acme/sales"`) when you want sub-divisions within a tenant — e.g. the engineering team's "Stripe" entity shouldn't mix with the finance team's.

```typescript theme={null}
// Default — one shared entity space
memory: { storage, entities: true }

// Per-team workspace
memory: { storage, entities: { namespace: "acme/engineering" } }
```

<Tip>**Rule of thumb:** start with the default. Only set a `namespace` once you actually have separate teams or tenants whose entity knowledge should not mix.</Tip>

**What gets extracted**: After each conversation, the LLM identifies entities and stores structured data:

* Entity: "Stripe" (type: company) — facts: \["Payment processor", "Used for billing"]
* Entity: "Raj" (type: person) — facts: \["Frontend engineer", "Works on checkout"]

***

## Decision Log

**Default: OFF.** Records what the agent decided and why. Useful for auditing and learning from past decisions.

### DecisionConfig

| Property              | Type     | Default | Description                                                     |
| --------------------- | -------- | ------- | --------------------------------------------------------------- |
| `maxContextDecisions` | `number` | `5`     | How many recent decisions are reminded to the agent on each run |

**Understanding `maxContextDecisions`:**

Every decision the agent logs is stored permanently (you can always search the full history with the `search_decisions` tool). This setting only controls **how many of the most recent decisions are automatically shown to the agent** at the start of each run, so it stays consistent with what it decided before.

* **Lower it (e.g. `3`)** to save prompt tokens, or if recent decisions aren't very relevant to the next one.
* **Raise it (e.g. `10`)** for agents that need strong continuity — e.g. a negotiation or approval agent that should remember its recent rulings to avoid contradicting itself.
* It does **not** limit how many decisions are stored — only how many are surfaced in context. Storage is unlimited.

```typescript theme={null}
memory: {
  storage,
  decisions: true,                      // remind the agent of its last 5 decisions
}

// High-continuity agent — remember more recent rulings
memory: {
  storage,
  decisions: { maxContextDecisions: 10 },
}
```

When enabled, the agent gets `log_decision`, `record_outcome`, and `search_decisions` tools automatically.

***

## Learned Knowledge

**Default: OFF.** Vector-backed insights that the agent learns over time. Requires a vector store for semantic search.

### LearningsConfig

| Property      | Type          | Required | Default                | Description                                                |
| ------------- | ------------- | -------- | ---------------------- | ---------------------------------------------------------- |
| `vectorStore` | `VectorStore` | **Yes**  | —                      | Where learnings are indexed for semantic search            |
| `collection`  | `string`      | No       | `"agentium_learnings"` | The "table name" inside the vector store                   |
| `topK`        | `number`      | No       | `3`                    | How many of the most relevant learnings to inject each run |

**Understanding the options:**

* **`vectorStore`** is required because learnings are recalled by *meaning*, not exact match — that needs a vector index (Qdrant, Pinecone, in-memory, etc.). This is why `learnings` takes a config object, not just `true`: there's no default backend.
* **`collection`** is the named bucket within that store. Change it only if you run multiple agents that should keep their learnings in separate collections within the same vector database.
* **`topK`** controls how many learnings are pulled into the prompt per run. Higher = the agent considers more accumulated knowledge but spends more tokens and risks diluting focus. `3` is a good default; raise to `5–8` for knowledge-heavy agents (research, support playbooks), lower to `1–2` for tight token budgets.

```typescript theme={null}
import { InMemoryVectorStore, OpenAIEmbedding } from "@agentium/core";

memory: {
  storage,
  learnings: {
    vectorStore: new InMemoryVectorStore(new OpenAIEmbedding()),
    topK: 5,   // pull the 5 most relevant insights each run
  },
}
```

See [Scope Hierarchy](#scope-hierarchy) for sharing learnings across a team or tenant.

***

## Graph Memory

**Default: OFF.** A knowledge graph where entities are connected by typed, directed relationships. Enables the agent to answer questions that require traversing relationships (e.g., "Who works with Raj?").

### GraphMemoryConfig

| Property          | Type         | Required | Default | Description                                                                    |
| ----------------- | ------------ | -------- | ------- | ------------------------------------------------------------------------------ |
| `store`           | `GraphStore` | **Yes**  | —       | Where the relationship graph lives (`InMemoryGraphStore` or `Neo4jGraphStore`) |
| `autoExtract`     | `boolean`    | No       | `true`  | Whether to auto-detect entities & relationships from each conversation         |
| `maxContextNodes` | `number`     | No       | `10`    | How many connected nodes to pull into the prompt                               |

**Understanding the options:**

* **`store`** is required — the graph needs somewhere to live. Use `InMemoryGraphStore` for development and `Neo4jGraphStore` for production (persistent, queryable at scale).
* **`autoExtract: true`** (default) means the agent automatically builds the graph as people talk ("Raj works at Acme" → creates `Raj —works_at→ Acme`). Set it to `false` if you want to build the graph manually via tools and avoid the extra extraction LLM call per turn.
* **`maxContextNodes`** caps how much of the graph is summarized into the prompt. Higher = the agent sees more of the relationship web (better for "who connects to whom" questions) but uses more tokens. Raise it for richly-connected domains (org charts, supply chains); keep it low otherwise.

```typescript theme={null}
import { InMemoryGraphStore } from "@agentium/core";

memory: {
  storage,
  graph: {
    store: new InMemoryGraphStore(),
    maxContextNodes: 15,   // surface up to 15 connected entities
  },
}
```

See [Graph Memory](/memory/graph-memory) for traversal, Neo4j setup, and temporal awareness.

***

## Procedural Memory

**Default: OFF.** Records successful multi-step tool workflows and suggests them when similar tasks arise.

### ProceduresConfig

| Property        | Type     | Default | Description                                              |
| --------------- | -------- | ------- | -------------------------------------------------------- |
| `maxProcedures` | `number` | `50`    | Maximum saved playbooks before the least-used are pruned |

**Understanding `maxProcedures`:**

Each procedure is a learned, reusable playbook ("to do X, call these tools in this order"). This caps how many the agent keeps. When the limit is hit, the **least-used / oldest** procedures are dropped first, so frequently-successful workflows survive.

* **Default `50`** suits most agents — they only repeat a handful of distinct workflows.
* **Raise it (e.g. `200`)** for agents that handle many distinct repeatable tasks (a broad ops agent).
* **Lower it** if you want the agent to only retain its few most-proven workflows.

```typescript theme={null}
memory: {
  storage,
  procedures: true,                    // keep up to 50 playbooks
}

// Broad ops agent with many distinct workflows
memory: {
  storage,
  procedures: { maxProcedures: 200 },
}
```

See [Procedural Memory](/memory/procedures) for details and the [Scope Hierarchy](#scope-hierarchy) for sharing procedures across a team.

***

## Scope Hierarchy

Most memory is personal — "User prefers dark mode" belongs to one user. But some knowledge is genuinely **shared**: a workflow like *"how to reconcile an invoice with a PO mismatch"* belongs to a whole team, and a policy like *"refunds over \$500 need VP approval"* belongs to the whole organization.

**Learnings** and **Procedures** support an explicit four-level scope so shared knowledge isn't trapped in one user's silo:

```
global       ← rarely used; built-in defaults, visible to everyone
  ↑
tenant       ← organization-wide (requires memory.tenantId)
  ↑
agent        ← workflow / role knowledge, shared across all users of this agent
  ↑
user         ← personal (default)
```

| Scope              | Visible to                     | Requires                          |
| ------------------ | ------------------------------ | --------------------------------- |
| `"user"` (default) | only the saving user           | `userId` in run context           |
| `"agent"`          | every user of that agent       | `agentName` (auto-set by `Agent`) |
| `"tenant"`         | every user/agent in the tenant | `memory.tenantId`                 |
| `"global"`         | everyone                       | nothing                           |

**Reads union all accessible scopes.** When Alice (user) talks to the `invoice-recon` agent at tenant `acme`, a `recall_procedure` / `search_learnings` call returns her personal items **plus** the agent's shared items **plus** the tenant's items **plus** globals — but never another user's personal scope.

**Writes pick one scope.** The `save_learning` tool exposes a `scope` parameter so the model can promote an insight to the team or org level:

```typescript theme={null}
// The agent's save_learning tool call:
save_learning({
  title: "Vendor X line-item drift",
  content: "Vendor X invoices consistently show $0.10–$0.50 drift per line.",
  context: "invoice reconciliation",
  scope: "agent",   // ← share with everyone using the invoice-recon agent
})
```

Saving directly through the store:

```typescript theme={null}
const agent = new Agent({
  name: "invoice-recon",
  model: openai("gpt-4o"),
  memory: {
    storage,
    learnings: { vectorStore },
    tenantId: "acme-corp",   // required for tenant-scoped reads
  },
});

const learnings = agent.memory!.getLearnedKnowledge()!;

// Org-wide policy — every user/agent in acme-corp sees it
await learnings.saveLearning({
  title: "Refund approval policy",
  content: "Refunds above $500 require VP approval.",
  context: "refunds",
  tags: ["policy"],
  namespace: "default",
  scope: "tenant",
  tenantId: "acme-corp",
});

// A union read returns user + agent + tenant + global learnings
const results = await learnings.searchLearnings("refund policy", {
  userId: "alice",
  agentName: "invoice-recon",
  tenantId: "acme-corp",
  topK: 5,
});
```

<Warning>
  **Auto-extracted learnings/procedures always save as `scope: "user"`.** The framework never auto-promotes an LLM-extracted insight to a shared scope — promotion requires an explicit caller decision. This prevents one user's accidental statement from leaking to the whole team.
</Warning>

See [Multi-User Isolation](/memory/isolation) for the full scope contract and security guarantees.

***

## Context Budget

Controls how the memory context string is distributed across sections when injected into the system prompt.

### ContextBudgetConfig

| Property     | Type                     | Default     | Description                                                                     |
| ------------ | ------------------------ | ----------- | ------------------------------------------------------------------------------- |
| `maxTokens`  | `number`                 | `undefined` | A hard cap on how many tokens *all* memory combined may use in the prompt       |
| `priorities` | `Record<string, number>` | `undefined` | Relative weights deciding which memory types get the budget when space is tight |

**Understanding the options:**

Once you enable several memory types, their combined context can grow large. `contextBudget` is the spending limit for that combined block and the rule for who gets priority when it overflows.

* **`maxTokens`** — the ceiling for the entire memory section of the prompt. Without it, every enabled store is injected in full (fine for small setups, risky once you have many). Set it to keep prompts predictable, e.g. `4000`.
* **`priorities`** — when the stores together exceed `maxTokens`, higher-weighted sections are kept and lower ones are trimmed or dropped first. Weights are *relative*, not absolute — `{ summaries: 3, userFacts: 2, entities: 1 }` means summaries get roughly half the budget, facts a third, entities a sixth. Valid keys: `"summaries"`, `"userProfile"`, `"userFacts"`, `"entities"`, `"decisions"`, `"graph"`, `"procedures"`. Any section you don't list uses its sensible default weight.

> **Tuning by use case:** a *support agent* might favor `summaries` (conversation continuity); a *research agent* might favor `learnings`; a *CRM agent* might favor `userFacts` + `entities`. Bump the weight of whatever matters most for your job.

```typescript theme={null}
memory: {
  storage,
  userFacts: true,
  entities: true,
  summaries: true,
  contextBudget: {
    maxTokens: 4000,            // cap total memory at ~4K tokens
    priorities: {
      summaries: 3,             // gets the largest share
      userFacts: 2,
      entities: 1,              // trimmed first if space runs out
    },
  },
}
```

***

## Using a Cheaper Model for Extraction

All background extraction (summaries, facts, entities, profiles) uses the agent's primary model by default. This can be expensive. Set `model` to use a cheaper model:

```typescript theme={null}
memory: {
  storage,
  summaries: true,
  userFacts: true,
  entities: true,
  model: openai("gpt-4o-mini"), // 10x cheaper for background work
}
```

***

## Full-Featured Example

Every option enabled:

```typescript theme={null}
import {
  Agent, openai, MongoDBStorage,
  InMemoryVectorStore, OpenAIEmbedding, InMemoryGraphStore,
} from "@agentium/core";

const storage = new MongoDBStorage("mongodb://localhost:27017/myapp");
await storage.initialize();

const agent = new Agent({
  name: "full-memory-agent",
  model: openai("gpt-4o"),
  memory: {
    storage,
    maxMessages: 30,
    summaries: { maxCount: 10, maxTokens: 2000 },
    userFacts: { maxFacts: 100 },
    userProfile: { customFields: ["company", "plan"] },
    entities: { namespace: "global" },
    decisions: { maxContextDecisions: 5 },
    learnings: {
      vectorStore: new InMemoryVectorStore(new OpenAIEmbedding()),
      topK: 3,
    },
    graph: {
      store: new InMemoryGraphStore(),
      maxContextNodes: 10,
    },
    procedures: { maxProcedures: 50 },
    contextBudget: {
      maxTokens: 5000,
      priorities: { summaries: 3, userFacts: 2, entities: 1 },
    },
    model: openai("gpt-4o-mini"),   // cheap model for background extraction
    timezone: "Asia/Kolkata",       // anchor date-relative extraction
    tenantId: "acme-corp",          // enable tenant-scoped learnings/procedures
    // eventBus is auto-injected from the Agent — no need to set it here
  },
});
```

***

## Observability

Memory mutations and failures emit typed events on the agent's `EventBus`. The most important is `memory.error` — without it, a broken extractor (malformed LLM JSON, an unreachable embedding service) fails silently and you only find out weeks later when a user complains.

```typescript theme={null}
// Surface silent extraction failures
agent.eventBus.on("memory.error", ({ store, error, agentName }) => {
  console.error(`[${agentName}] ${store} extraction failed:`, error.message);
  // forward to Sentry / Datadog / Langfuse
});

// Track how big the injected memory context is per run
agent.eventBus.on("memory.context.built", ({ totalTokens, sections }) => {
  metrics.histogram("memory_context_tokens", totalTokens);
});

// React to fact mutations
agent.eventBus.on("memory.fact.added", ({ userId, fact, source }) => {
  console.log(`[${userId}] learned (${source}): ${fact}`);
});
```

| Event                     | When                              | Payload                                |
| ------------------------- | --------------------------------- | -------------------------------------- |
| `memory.fact.added`       | A new user fact is stored         | `{ userId, fact, source, importance }` |
| `memory.fact.invalidated` | A fact is superseded or forgotten | `{ userId, factId, reason }`           |
| `memory.extract`          | Background extraction kicked off  | `{ sessionId, userId, agentName }`     |
| `memory.context.built`    | `buildContext` returned           | `{ sessionId, totalTokens, sections }` |
| `memory.error`            | Any background extraction failed  | `{ store, error, agentName }`          |

These flow into [@agentium/observability](/observability/overview) (OpenTelemetry, Prometheus, Langfuse) with no extra wiring.

***

## Multi-Tenant Example

A single agent serving many organizations. Personal facts stay private; tenant policies are shared org-wide.

```typescript theme={null}
import { Agent, openai, MongoDBStorage, InMemoryVectorStore, OpenAIEmbedding } from "@agentium/core";

function agentForTenant(tenantId: string) {
  return new Agent({
    name: "support",
    model: openai("gpt-4o"),
    memory: {
      storage: new MongoDBStorage(`mongodb://localhost/acme`),
      userFacts: true,
      learnings: { vectorStore: new InMemoryVectorStore(new OpenAIEmbedding()) },
      tenantId,                       // scopes tenant-level learnings
      timezone: "America/New_York",
      model: openai("gpt-4o-mini"),
    },
  });
}

const acme = agentForTenant("acme-corp");
const meridian = agentForTenant("meridian-llc");

// Alice's personal facts are private to Alice within acme-corp
await acme.run("I prefer email over phone", { userId: "alice", sessionId: "s1" });

// A tenant-wide policy saved once, visible to every acme-corp user/agent
await acme.memory!.getLearnedKnowledge()!.saveLearning({
  title: "Escalation policy",
  content: "Tier-2 issues escalate to a human after 2 failed attempts.",
  context: "support escalation",
  tags: ["policy"],
  namespace: "default",
  scope: "tenant",
  tenantId: "acme-corp",
});

// meridian-llc users never see acme-corp's policy or Alice's facts
await meridian.run("How do escalations work?", { userId: "bob", sessionId: "s2" });
```

***

## Debugging Memory Context

If the model seems to "forget" something, inspect exactly what `buildContext` produced:

```typescript theme={null}
const mm = agent.memory!;
const ctx = await mm.buildContext(
  "session-abc",        // sessionId
  "user-42",            // userId
  "what's my order?",   // currentInput (drives relevance ranking)
  "support",            // agentName (drives agent-scoped reads)
);
console.log(ctx);
```

The output shows each store wrapped in a scope sentinel:

```xml theme={null}
<memory section="userFacts" scope="current_user">
What you know about this user:
- User's name is Akash.
- Akash is based in Mumbai.
</memory>

<memory section="summaries" scope="current_user">
Previous conversation context (most recent first):
...
</memory>
```

If a section is missing, the store either isn't enabled, has no data for that user, or wasn't given the scope identifier it requires (e.g. an agent-scoped learning needs `agentName`).

***

## When to Enable What

| Feature                  | Enable When                                                        | Cost Impact                             |
| ------------------------ | ------------------------------------------------------------------ | --------------------------------------- |
| **Sessions + Summaries** | Always (default)                                                   | Low — 1 LLM call per overflow           |
| **User Facts**           | You need cross-session personalization                             | Low — 1 LLM call per run for extraction |
| **User Profile**         | You need structured user data (name, role, timezone)               | Low                                     |
| **Entities**             | Your conversations reference companies, people, projects           | Medium — extraction call per run        |
| **Decisions**            | You need audit trails or want the agent to learn from past choices | Negligible — no LLM calls, just storage |
| **Learnings**            | You want vector-search over accumulated knowledge                  | Medium — requires a vector store        |
| **Graph**                | You need relationship traversal ("who works with whom")            | Higher — extraction + graph storage     |
| **Procedures**           | Your agent repeats similar multi-tool workflows                    | Low                                     |

***

## Storage Options

The `storage` field accepts any `StorageDriver`. Choose based on your needs:

| Driver            | Persistence                | Best For                       |
| ----------------- | -------------------------- | ------------------------------ |
| `InMemoryStorage` | None (lost on restart)     | Development, testing           |
| `SqliteStorage`   | File-based                 | Prototypes, single-server apps |
| `PostgresStorage` | Durable                    | Production                     |
| `MongoDBStorage`  | Durable                    | Production                     |
| `MySQLStorage`    | Durable                    | Production                     |
| `RedisStorage`    | In-memory with persistence | High-throughput, caching       |
| `DynamoDBStorage` | Durable                    | AWS-native, serverless         |

```typescript theme={null}
import { SqliteStorage } from "@agentium/core";
memory: { storage: new SqliteStorage("app.db") }

import { PostgresStorage } from "@agentium/core";
const storage = new PostgresStorage("postgresql://user:pass@localhost/db");
await storage.initialize();
memory: { storage }

import { MongoDBStorage } from "@agentium/core";
const storage = new MongoDBStorage("mongodb://localhost:27017/myapp");
await storage.initialize();
memory: { storage }
```

See [Storage Overview](/storage/overview) for setup details for each driver.
