<!-- Markdown mirror. Canonical: https://empryo.com/docs/tools/memory -->

# Memory

> Empryo remembers your preferences, project decisions and past gotchas across sessions, and adds up to 3 relevant ones to each turn automatically.

[Video: Recall at work: Empryo scores stored memories against the current request and the files in play, then adds the three most relevant, each labeled with why it was picked.](https://empryo.com/media/code-memory-recall.mp4)

*Recall at work: Empryo scores stored memories against the current request and the files in play, then adds the three most relevant, each labeled with why it was picked.*

Memory lets Empryo remember things between sessions: that you use bun, not npm; why the team switched state libraries; that a test is flaky on CI. You tell it once, and it comes back when it matters.

Memory is stored **per project** by default. You can also keep **global** memories that apply to every project, such as "be terse".

## What gets stored

Each memory has one of four categories:

| Category | Example |
|---|---|
| `pref` | "use bun not npm", "be terse" |
| `decision` | "switched to zustand because redux was too much boilerplate" |
| `gotcha` | "JWT expiry uses the container clock, which drifts in prod" |
| `context` | "legacy/ will be deleted next sprint, don't touch it" |

The agent saves a memory on its own when you state a preference, make a decision with a reason, or hit a bug that wasn't obvious. You can also ask it directly: "remember that we deploy on Thursdays".

[Video: Left: a rules file sent in full on every turn, outdated lines included. Right: Empryo adds three short memories that match the file being edited.](https://empryo.com/media/code-memory-prompt-bloat.mp4)

*Left: a rules file sent in full on every turn, outdated lines included. Right: Empryo adds three short memories that match the file being edited.*

## How recall works

Before each turn, Empryo compares your message and the files you are working on with what's stored. It adds up to **3 relevant memories** to the turn. You don't need to run a command or remember what's saved.

If nothing is relevant, nothing is added. Memories only take up space in the conversation when they help.

## Browse and edit: `/memory`

In the terminal UI, run `/memory` to open the memory browser. It has four tabs:

- **All:** every memory you can see, from the project and global stores. Pin, edit or hide a memory from here.
- **Hidden:** memories you hid. Restore them with `Ctrl+R`.
- **Cleanup:** suggestions to review, such as duplicates, memories that point at deleted files, and memories that are rarely useful.
- **Settings:** which stores the agent reads from and writes to, and which embedding model is in use.

Hiding a memory is reversible. **Purge all** (`Ctrl+X`) deletes permanently, so use it with care.

The desktop app has a Memory pane with the same data.

## Scopes

Memories live in two databases:

| Scope | Location | Use it for |
|---|---|---|
| Project | `.empryo/memory.db` in the repository | Decisions and gotchas about this code |
| Global | `~/.empryo/memory.db` | Your personal preferences and style |

New memories go to the project store by default. The read scope controls what recall can see:

```
all      project + global (default)
project  only this repository
global   only your global preferences
none     recall off
```

Change both in `/memory` → **Settings**.

## Pinning

Pin a memory to keep it out of cleanup suggestions and rank it higher in recall. Pin the rules you never want the agent to miss.

## Languages

Memory search works in any language or script, including Chinese, Japanese, Arabic and Cyrillic, and mixed-script identifiers. It matches both whole words and parts of words, so rephrased and non-English queries still find the right memory.

## Better matching with an embedding model

By default, Empryo uses a built-in offline embedder. It needs no API key and makes no network calls. It matches memories well when your wording overlaps with what was stored.

For better matching of rephrased text, point memory at an embedding model:

```json
{
  "memory": {
    "embeddingModel": "openai/text-embedding-3-small"
  }
}
```

### Do I need an API key?

Yes, for a hosted model. Empryo uses the key you already configured for that provider. For `openai/text-embedding-3-small`, that is your OpenAI key. If the key is missing or wrong, Empryo falls back to the offline embedder and memory keeps working. `/memory` → **Settings** shows the reason.

Local models in Ollama or LM Studio need no key. Pull the model, then name it in the config.

### Which models can I use?

Any `provider/model` ID whose provider offers embeddings:

| Provider | Model |
|---|---|
| OpenAI | `openai/text-embedding-3-small`, `openai/text-embedding-3-large` |
| Google | `google/text-embedding-004` |
| Mistral | `mistral/mistral-embed` |
| OpenRouter | `openrouter/openai/text-embedding-3-small` |
| Vercel AI Gateway | `vercel_gateway/openai/text-embedding-3-small` |
| Ollama (local) | `ollama/nomic-embed-text`, `ollama/mxbai-embed-large` |
| LM Studio (local) | `lmstudio/nomic-embed-text` |

Providers without an embeddings API, such as Anthropic, xAI, Groq, DeepSeek and Bedrock, use the offline embedder instead. Custom OpenAI-compatible providers work if they expose an `/embeddings` endpoint.

If you don't set `embeddingModel`, Empryo picks one in this order: your `taskRouter.semantic` model, then a default for your current chat provider (OpenAI, Google, OpenRouter or Vercel AI Gateway), then the offline embedder. Set `"embeddingModel": null` to always use the offline embedder.

### How do I check it's working?

Open `/memory` → **Settings** and look at the **Embedding** row. It shows the model in use and where the choice came from. If your configured model failed, the row says why: a missing key, a provider without embeddings, or a network error.
