> ## Documentation Index
> Fetch the complete documentation index at: https://ixoworld-mintlify-a08eb7f4.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# memory

> Durable memory across conversations: who the user is, what you made for them, and what worked.

**Source:** [`packages/oracle-runtime/src/plugins/memory/`](https://github.com/ixoworld/ixo-oracles-boilerplate/blob/main/packages/oracle-runtime/src/plugins/memory/)

| Attribute     | Value                               |
| ------------- | ----------------------------------- |
| Visibility    | `always`                            |
| Stability     | `stable`                            |
| Category      | `memory`                            |
| Default state | Auto-detect (env: `MEMORY_MCP_URL`) |
| Depends on    | —                                   |

## Summary

Durable memory across conversations — who the user is, what you have made for them, and what worked. Surfaces the upstream Memory Engine MCP tools verbatim (`memory-engine__search_memory_engine`, `memory-engine__add_memory`, …). Tool list is resolved per-request because MCP headers depend on the in-flight user's UCAN delegation.

## Environment variables

| Var                 | Required | Description                                        |
| ------------------- | -------- | -------------------------------------------------- |
| `MEMORY_MCP_URL`    | yes      | Memory MCP HTTP(S) URL. Also triggers auto-detect. |
| `MEMORY_ENGINE_URL` | yes      | Memory Engine HTTP(S) URL.                         |

## What it contributes

* **Tools (default selection):**
  * `memory-engine__search_memory_engine` — recall stored facts.
  * `memory-engine__add_memory` — write a new memory.
  * `memory-engine__delete_episode` — delete a memory.
  * `memory-engine__clear` — clear (destructive; main-agent only).
* **Sub-agents:** none.
* **Middleware:** none directly contributed by the plugin (enrichment of `state.userContext` is handled upstream of the agent).
* **HTTP routes:** none.
* **Shared state:** `userProfile` — other plugins read it via `rtCtx.shared.userProfile`.

Tools available beyond the default selection (e.g. `memory-engine__add_oracle_knowledge`, `memory-engine__delete_edge`) can be enabled by passing `selectedTools` to `new MemoryPlugin({ selectedTools: [...] })`.

## How memory reaches the prompt

Memory context does not arrive in the prompt because the agent calls a tool to fetch it. Instead, a `UserContextFetcher` runs **before** the main agent is compiled — eagerly loading all six context slots in a single pass, before any message is processed.

**What the fetcher loads:** six slots, in this order:

1. `identity` — who the user is (name, role, background)
2. `work` — ongoing projects and responsibilities
3. `goals` — stated objectives and priorities
4. `interests` — topics and domains the user cares about
5. `relationships` — people and organizations mentioned in past conversations
6. `recent` — notable things from the last few sessions

**When it runs:** at agent-compile time, before turn 1. The fetcher caches the result for **5 minutes**, keyed by `roomId`. Subsequent turns within the same room reuse the cached context — including a fresh session for the same room — and the Memory Engine is not called again unless the cache expires.

**What appears in the prompt:** if at least one slot is non-empty, the runtime inserts a `## What you know about the user` block into the system prompt containing all populated slots. If every slot is empty (no prior memory for this user), the block is omitted entirely.

The block is deduped and budgeted before it reaches the model: entities are collapsed by name (keeping the richest summary), facts / episodes / communities are collapsed by normalized text so exact repeats across the six slots don't render 4-5 times, and per-entity and whole-block caps are applied. If either cap trims content, the block ends with a `_(More remembered — ask me to recall.)_` footer — trimmed detail is still reachable via the memory tool. No unique fact is dropped, and paraphrases survive.

**Implication for oracle authors:** you do not need to instruct the agent to "look up the user's context" or "recall memory before responding" in `config.prompt.opening` or anywhere else. The context is already in the system prompt when the agent sees the user's first message. Adding such instructions is redundant and wastes tokens.

<Note>
  The 5-minute room-keyed cache means very-recent memory writes (e.g. the agent just called `memory-engine__add_memory` in the current turn) may not appear in the fetched context until the cache expires. This is intentional — the fetcher is optimised for read latency, not write-through consistency.
</Note>

## Adding global oracle knowledge

Global knowledge is content the oracle should know on every turn, for every user — product docs, brand voice, FAQs, reference material. It's stored on the Memory Engine and indexed under the **oracle's entity DID**, not the user's DID.

<Steps>
  <Step title="Confirm you are an owner or controller of the oracle entity">
    Only an account that is `owner` or `controller` on the oracle's IXO entity (the entity created via `qiforge-cli create-entity`) can write global knowledge. The Memory Engine rejects writes from any other DID.

    Check with `qiforge-cli update-entity` or by inspecting the entity on chain. If you are not a controller, ask the entity owner to add your DID first.
  </Step>

  <Step title="Enable the add_oracle_knowledge tool on the memory plugin">
    The bundled `memory` plugin's default tool selection does **not** include `memory-engine__add_oracle_knowledge`. Instantiate `MemoryPlugin` explicitly and extend the default selection:

    ```ts theme={null}
    // src/main.ts
    import {
      createOracleApp,
      MemoryPlugin,
      DEFAULT_MEMORY_TOOLS,
      MEMORY_ADD_ORACLE_KNOWLEDGE_MCP_NAME,
    } from '@ixo/oracle-runtime';

    const app = await createOracleApp({
      config,
      plugins: [
        new MemoryPlugin({
          selectedTools: [
            ...DEFAULT_MEMORY_TOOLS,
            MEMORY_ADD_ORACLE_KNOWLEDGE_MCP_NAME,
          ],
        }),
      ],
    });
    ```

    The loader dedupes by name, so this explicit instance overrides the bundled default with the same name.
  </Step>

  <Step title="Boot the oracle">
    ```sh theme={null}
    pnpm dev
    ```

    The boot log should list `memory` as loaded. The agent now has `memory-engine__add_oracle_knowledge` available.
  </Step>

  <Step title="Open the Portal and connect to your oracle">
    Pick the environment matching the network your oracle is registered on:

    | Network | Portal URL                     |
    | ------- | ------------------------------ |
    | devnet  | `https://dev.portal.qi.space`  |
    | testnet | `https://test.portal.qi.space` |
    | mainnet | `https://portal.qi.space`      |

    Navigate directly to your oracle's connect page (replace `<ENTITY_DID>` with the value of `ORACLE_ENTITY_DID` from your `.env`):

    ```text theme={null}
    https://dev.portal.qi.space/domain/<ENTITY_DID>/connect
    ```

    Sign in as the entity owner/controller. On the connect page, click the highlighted "Connect" action — the Portal then opens a chat session bound to the oracle.
  </Step>

  <Step title="Drop in content and ask the oracle to save it as global knowledge">
    In the chat, drag and drop files (PDFs, markdown, text), paste links, or paste raw text — anything you want the oracle to know going forward. Then tell the oracle in plain language:

    > Save this into the global oracle knowledge.

    The agent calls `memory-engine__add_oracle_knowledge` with the dropped content. The Memory Engine accepts the write because your delegation chain proves you are owner/controller of the oracle entity.
  </Step>

  <Step title="Wait ~5 minutes for indexing">
    Newly written knowledge takes about five minutes to index before it appears in `memory-engine__search_memory_engine` results. After indexing, every user's session will be able to recall it through the normal memory search path.
  </Step>
</Steps>

<Warning>
  `memory-engine__add_oracle_knowledge` writes are scoped to the oracle entity, not to the calling user. Anything you add is visible to **every** user who talks to this oracle. Treat it like a public knowledge base.
</Warning>

## Opt out / Opt in

```ts theme={null}
const app = await createOracleApp({
  config,
  features: { memory: false },   // never load
  // features: { memory: true },  // force load (will fail env validation if vars missing)
  // features: { memory: 'auto' }, // run autoDetect (default)
});
```

## When to use it

* First contact (no prior context loaded): greet, ask the user's name and what they need help with, save the answer.
* You learn something durable about the user — name, role, ongoing project, a constraint, a relationship.
* You produce an artifact (file, document, edit, generated content) — record what it is, what it is for, the structural choices.
* The user expresses satisfaction or dissatisfaction with something you produced — capture what worked or did not.
* The user references something they told you before, or something you made before.

## When NOT to use it

* Ephemeral conversation-only state — use the current message thread.
* Behavioural preferences about how to respond — use [`user-preferences`](/build-an-oracle/reference/bundled-plugins/user-preferences).
* Public web facts not specific to this user — use [`firecrawl`](/build-an-oracle/reference/bundled-plugins/firecrawl).
* Anything the user asked you to forget or framed as temporary.

## Where to read next

<CardGroup cols={2}>
  <Card title="Shared state" icon="share-nodes" href="/build-an-oracle/understand/shared-state">
    How `userProfile` flows to other plugins.
  </Card>

  <Card title="Add a tool" icon="screwdriver-wrench" href="/build-an-oracle/develop/plugin-recipes/add-a-tool">
    Pattern for exposing upstream MCP tools.
  </Card>
</CardGroup>
