Skip to main content
Glama
Recall by Polign

Recall by Polign

Official
README.md
# Polign Recall

Recall gives an agent memory and state that survive the conversation and the
process. Memory is what the agent knows: facts a user stated, with every
correction kept, so the agent can say what it believes now, what it believed
before, and why. State is where the agent is: its progress, its notes to its
next instance, and the tool calls it had in flight, so a run that crashes or
moves machines resumes from its own records.

Recall is a Go library, a `recall` binary that serves the same operations as
MCP tools and as HTTP, and a Python client. It stores everything in a
database you run: polign_db, PostgreSQL, MongoDB or Qdrant. Apache 2.0.

![Recall, mem0 and Zep answering questions about a user whose facts changed over nine months](docs/demo.gif)

One user over nine months, three homes, three employers, three work emails,
six questions, Claude Opus 5.5 extracting, answering and judging for all three
systems: Recall 18/18, mem0 18/18, Zep 12/18. Tokens to save one message:
Recall 3.0k, mem0 12.7k, Zep 14.7k.

The code to the Recall repository.

<img src="docs/qr-code.png" alt="QR code for https://github.com/Polign/recall" width="160">


## Install

```sh
pip install polign-recall          # the Python client, the recall binary, and the database it runs
brew install polign/tap/recall     # the same for Homebrew
go install github.com/Polign/recall/cmd/recall@latest   # the binary alone; it fetches the database on first use
```

Then give it to the agent you use:

```sh
claude mcp add recall -- recall mcp -write      # Claude Code
```

[Add to Cursor](https://cursor.com/en/install-mcp?name=recall&config=eyJjb21tYW5kIjoicmVjYWxsIiwiYXJncyI6WyJtY3AiLCItd3JpdGUiXX0%3D) ·
[Add to VS Code](https://vscode.dev/redirect/mcp/install?name=recall&config=%7B%22command%22%3A%22recall%22%2C%22args%22%3A%5B%22mcp%22%2C%22-write%22%5D%7D) ·
any other MCP host: command `recall`, arguments `mcp -write` ([details](docs/mcp.md)).

With nothing configured, memory lives in a local database in
`~/.local/share/recall`, started on first use and shared by every agent on the
machine. No model and no API key are needed to start. To keep memory in a
database you already run, see [Backends](#backends).

## What a correction looks like

Every change is kept. Three commands read the record:

```text
$ recall history "which editor do I use"
user preferred editor
  believed now: user preferred editor: zed
  2026-09-01 10:00  stated, user_stated
      + user preferred editor: vim
  2026-09-20 10:00  replaced, user_stated
      - user preferred editor: vim
      + user preferred editor: emacs
  2026-10-05 10:00  replaced, user_stated
      - user preferred editor: emacs
      + user preferred editor: zed

$ recall ask -as-of 2026-09-25 "which editor do I use"
Believed as of 2026-09-25 00:00:
user preferred editor: emacs  (since 2026-09-20)
  replaced: vim  (since 2026-09-01)

$ recall diff -from 2026-09-15 -to 2026-10-31
Changed between 2026-09-15 00:00 and 2026-10-31 00:00:
- user home city: Lisbon
+ user home city: Porto
- user preferred editor: vim
+ user preferred editor: zed
```

## Why Recall

- **Corrections are records, not overwrites.** A new value replaces the old
  one by a rule you can read, and every answer arrives with what it replaced.
- **Any moment is readable.** `as_of`, `history` and `diff` say what the agent
  believed then and when that changed.
- **Every answer has a source.** `recall explain` shows the message a fact came
  from, the rule that decided it, and its history.
- **Agents resume from records.** A crashed or moved agent gets its working
  state, recent turns and unfinished tool calls back, not a snapshot.
- **Your storage, your rules.** Memory lives in a database you already run, and
  the vocabulary of what gets remembered is yours to define or leave open.

## Quickstart

Python 3.10 or later. Save a preference, then correct it:

```sh
pip install polign-recall
python - <<'PY'
from polign_recall import Client

with Client(local_dir="./recall-data", typed=True) as memory:
    memory.remember("user", "prefers_editor", "vim")
    memory.remember("user", "prefers_editor", "neovim")
PY
```

A second process reads the current value, the history, and what was believed
before the correction:

```sh
python - <<'PY'
from polign_recall import Client

with Client(local_dir="./recall-data", typed=True) as memory:
    print(memory.recall("user", "prefers_editor")[0].value)   # neovim

    events = memory.history("user", "prefers_editor")
    print([e.value for e in events])                           # ['vim', 'neovim']

    before = memory.recall("user", "prefers_editor", as_of=events[0].observed_at)
    print(before[0].value)                                      # vim
PY
```

The first client to open `./recall-data` starts a local database for it and
later clients share it; the memories stay there after the program exits. A
bare `Client()` uses `~/.local/share/recall`, the memory every agent on the
machine shares. The [Python client guide](python/README.md) covers the rest:
plain-text memory with a model, forgetting, history, and resuming an agent.

## How memory works

A memory is a **subject**, a **predicate** and a **typed value**: `user`,
`prefers_editor`, `neovim`. Predicates live in a registry that says what type
a value has, whether the predicate holds one value or many, and which
predicates it contradicts: `dislikes` excludes `likes`, `gave_away` excludes
`owns`. A single-valued predicate replaces its value on a new statement; a
multi-valued one accumulates. Forgetting records a withdrawal; nothing is
deleted. Recall ships [starter registries](docs/reference.md#starter-registries)
for coding, personal assistant, support, sales and voice agents, and you can
[change a registry later](docs/reference.md#changing-the-registry) without
touching what was already remembered.

You do not have to define predicates. With an
[open vocabulary](docs/reference.md#open-vocabulary) the first use of a
predicate defines it, and with a model configured the MCP tools take plain
text and no predicates appear anywhere:

```sh
recall setup -extract-model anthropic:claude-haiku-4-5-20251001
```

```text
remember("I switched from helix to zed last week")
recall("which editor do I use?")  ->  "user prefers editor: zed" (before: helix)
forget("my editor")
```

The model decides how each statement is filed. Once filed, replacement follows
the predicate's rule, every filing is recorded, and a wrong filing is corrected
with one event that every later answer follows. No model reconciles memory on
its own: Recall applies the registry's rules, shows what may conflict, and
lets the agent confirm the rest, so every correction reads back to the rule or
statement that made it. `recall mcp -typed` serves the typed tools
(`remember(subject, predicate, value)`) instead. The
[developer reference](docs/reference.md) has the details and the
[Memory API](docs/memory-api-v1.md) is the contract.

## State

The second half of Recall, with its own contract,
[docs/state-api-v1.md](docs/state-api-v1.md). An agent writes down where it is
as it works, and a new process resumes from those records instead of from a
snapshot:

- **Working state**: goal, plan, progress, focus, decisions, open questions.
- **Turns**, verbatim, and large tool **outputs** stored by reference.
- **Pointers** to where the work lives: a branch, an object key, a process.
- **Actions**: each tool call that touches the world, recorded before it runs
  and closed with its result, so a crash between the two is visible and the
  next resume lists the call as unconfirmed instead of repeating it.

`Resume` takes a lease on the agent id, so one process acts for an agent at a
time, rebuilds the context within a token budget, and renders it as one
briefing for the model:

```go
agent, err := client.Resume(ctx, recall.ResumeRequest{AgentID: "coder-1", TokenBudget: 8000})
if err != nil {
    return err
}
defer agent.Release(context.Background())

briefing := agent.Resumed().Briefing // hand this to the model
```

In Python, an agent remembers a fact and records where it is, the process is
killed, and a new process resumes from the records:

```python
import os, signal
from polign_recall import Client

with Client(local_dir="./recall-data", agent=True, typed=True) as client:
    client.remember("user", "project_constraint", "deploy only on weekdays after 18:00 UTC")
    with client.resume("coder-1") as agent:
        agent.update_working_state(goal="Move billing to the v2 API",
                                   plan=["find call sites", "migrate them", "run the tests"])
        agent.record_turn("user", "Start with the charge endpoint.")
        agent.milestone("call sites found", progress="12 call sites listed")
        os.kill(os.getpid(), signal.SIGKILL)   # dies here, lease unreleased
```

```python
import time
from polign_recall import Client

with Client(local_dir="./recall-data", agent=True, typed=True) as client:
    agent = client.resume("coder-1", token_budget=8000, defer_lease=True)
    print(agent.context.briefing)   # at once: the goal, plan, milestone, turn, and constraint
    while not agent.acquire():      # False until the dead process's lease expires
        time.sleep(1)
    agent.record_turn("assistant", "Picking up at the charge endpoint.")
```

Without `defer_lease=True`, `resume` raises `lease_held` until the crashed
process's lease expires ([Python client guide](python/README.md#resuming-an-agent)).

The same operations are MCP tools (`agent_resume`, `update_working_state`,
`record_turn`, `begin_action`, ...) and, on `recall serve`, HTTP routes at
`POST /v1/agent/<verb>`. Over HTTP the server holds the lease between
requests: `resume` returns an epoch and every write carries it, so a stale
process cannot write over its successor. The State API is beta; the records it
writes are stable ([STABILITY.md](STABILITY.md)). The
[resume example](examples/resume) kills an agent mid-task and continues it in
a new process.

## Serving many agents

`recall mcp` is one agent, one process. `recall serve` is the same memory and
state for many agents over HTTP, each inside the namespace its key names. The
server holds the database credentials; agents hold Recall keys.

```sh
recall serve -backend polign -url http://polign:23000 -bootstrap-key-file admin.key
recall key create -backend polign -url http://polign:23000 -namespace acme:agent-7 -expires 168h
```

Each namespace gets its own collection, so nothing a caller sends can reach
another namespace's records. `POST /mcp` is MCP over streamable HTTP;
`POST /v1/<tool>` takes a memory tool's arguments as the body, and
`POST /v1/agent/<verb>` a state tool's. Any stdio MCP client reaches the
server through `recall connect` with `RECALL_URL` and `RECALL_API_KEY` set.

On Kubernetes a pod needs no key at all. With `recall serve -kubernetes`, it
posts its projected service account token and gets a short-lived key fixed to
its own agent id, which `recall connect` and the Python client obtain and
renew by themselves. The Helm chart is `charts/recall` in
[Polign/polign](https://github.com/Polign/polign); the
[agent-sandbox demo](deploy/agent-sandbox) runs it on kind.

## Backends

| | local (default) | polign_db | PostgreSQL + pgvector | MongoDB | Qdrant |
| --- | --- | --- | --- | --- | --- |
| `RECALL_BACKEND` | `local` | `polign` | `pgvector` | `mongodb` | `qdrant` |
| `RECALL_BACKEND_URL` | a directory | `http://host:23000` | `postgres://user:pw@host/db` | `mongodb://user:pw@host/db` | `http://host:6333` |
| Memory, history, as-of | yes | yes | yes | yes | yes |
| Keyword search | yes | yes | yes | yes | no |
| Cached reads | yes | yes | yes | yes | no |
| State API (leases) | yes | yes | yes | yes | no |

```sh
export RECALL_BACKEND=pgvector RECALL_BACKEND_URL=postgres://user:password@localhost:5432/mydb
recall mcp -write
```

Three settings name the database for every command: `-backend`, `-url` and
`-key`, or the variables above. Everything else, including what each backend
needs and how to add one, is in [docs/backends.md](docs/backends.md).

## Benchmark

LongMemEval user-facts subset, 267 questions about a user's own facts,
preferences, updates and dates, with Claude Sonnet 5.5 as reader and judge
and the upstream prompts:

| What the reader is given | Score |
| --- | --- |
| Nothing | 6.0 |
| The full chat history | 88.0 |
| BM25 over sessions, top 10 | 89.9 |
| Recall, built-in word search | 88.8 |
| Recall with a model embedder (OpenAI text-embedding-3-small, 512 dims) | 92.9 |
| Only the sessions that hold the answer | 96.6 |

With word search Recall is even with BM25 overall and behind it on preference
questions (70.0 against 83.3); a model embedder puts it ahead. Recall handed
the reader about 28k tokens per question, against 44k for BM25 and 168k for
the full history. The harness is in [`eval/longmemeval`](eval/longmemeval).

## Integrations

| | |
| --- | --- |
| **Python** | `pip install polign-recall`, then the [client guide](python/README.md). |
| **Go** | `go get github.com/Polign/recall`, then the [Go library guide](docs/reference.md#go-library). |
| **MCP** | [Connect any agent](docs/mcp.md) to remember, recall and forget. |
| **Claude Code** | The [Recall plugin](https://github.com/Polign/polign/tree/main/plugins/recall): memory across coding sessions. |
| **LangGraph** | `pip install recall-langgraph` to [resume LangGraph agents](https://github.com/Polign/polign/tree/main/python/recall-langgraph). |
| **LiveKit**, **Vapi** | `recall-livekit` and `recall-vapi` for [caller memory in voice agents](https://github.com/Polign/polign/tree/main/python). |
| **Pydantic AI** | `pip install recall-pydantic-ai` for [memory tools in Pydantic AI agents](https://github.com/Polign/polign/tree/main/python/recall-pydantic-ai). |
| **Vercel AI SDK** | The [Next.js chat example](examples/vercel-ai-sdk), one click to deploy. |
| **OpenClaw** | `@polign/openclaw-recall` on npm, the [OpenClaw plugin](https://github.com/Polign/polign/tree/main/plugins/openclaw). |

## Documentation

[Developer reference](docs/reference.md) ·
[Memory API contract](docs/memory-api-v1.md) ·
[State API contract](docs/state-api-v1.md) ·
[Backends](docs/backends.md) ·
[Connecting through MCP](docs/mcp.md) ·
[Caching memory reads](docs/materialization.md) ·
[Exporting and replaying history](docs/audit.md) ·
[What is stable and what is beta](STABILITY.md) ·
[Development guide](docs/development.md)

## Support the project

If Recall kept your agent from acting on a stale fact, a star on this
repository helps other people find it. Bugs, questions and ideas go in
[issues](https://github.com/Polign/recall/issues).

## License

[Apache 2.0](LICENSE).

TDQS

A4.1/5.0

Scored across 5 tools

Disambiguation4/5

The five tools cover clearly different operations: storing, querying, withdrawing, tracing one item's history, and comparing beliefs across time. The only mild overlap is between recall's `since` mode and diff, which both answer 'what changed', though their descriptions differentiate instants versus periods.

Naming Consistency4/5

All names are single, lowercase words with a consistent terse style. However, remember/recall/forget are verbs while history and diff are nouns, a minor deviation from a pure verb convention.

Tool Count5/5

Five tools is well scoped for a focused memory service, with each tool earning its place across the read/write/history lifecycle. No tool feels redundant or missing from the core set.

Completeness5/5

The surface covers the full memory lifecycle: write (remember), read (recall with profile, point-in-time, and delta views), soft delete (forget), per-item history, and cross-instant diff. No dead ends are apparent for the stated purpose.

Maintenance

ActivityActive
ResponsivenessOutdated data