# recall 0.4.0

Released 15 Oct 2026 · [Compare on GitHub](https://github.com/alirng/recall/compare/v0.3.0...v0.4.0) · [recall-agents 0.4.0 on PyPI](https://pypi.org/project/recall-agents/0.4.0/)

recall can now tell you which chat wrote a line of code, keeps every chat it has indexed, tells agents about each other's edits, and writes notes as rules it checks one by one. Search results are unchanged. Numbers: docs/benchmarks.md.

## Upgrading

- **The chat index rebuilds itself once** (chat schema 7), and so does the redaction of stored text: the first index run after upgrading re-reads your chats and rewrites the text recall cannot rebuild from your agents' files (cached answers, extracted candidates, archived chats) with the current rules. Chats whose files are gone come back from the archive.
- **Memory notes gain a `rules:` block** in their front matter the next time recall writes them. An older recall that rewrites such a note drops that block, so upgrade every machine that shares the notes folder.
- **The Claude provider's `haiku`, `sonnet` and `opus` now run the 5.5 models.** Each call uses more of your plan than Haiku 4.5 did.
- **With an API key, the nightly run sends batched calls** at half the price and stops at a default budget of $1 a run (`[dream] max_usd`). With a ChatGPT or Claude subscription or a local model, nothing changes in cost: batches and the dollar budget do not apply.

## Added

- **Batch APIs for the nightly run.** With an Anthropic or OpenAI key, `recall dream` sends its calls through the provider's batch API, at half the price: extraction as one batch, then consolidation in rounds, every project side by side. A batch that overruns 20 minutes is cancelled and the night carries on synchronously; one still running at the time budget is collected by the next run. `batch = "off"` in `[model]`, or `recall dream --no-batch`, turns it off. Plans (ChatGPT, Claude) and local models keep their usual path.
- **A dollar budget.** `[dream] max_usd` (default $1.00, or `--max-usd`) caps what a run may spend on an API key, in place of the call cap. `recall dream` reports each night's cost, and `llm_calls` records each call's.
- **When API credits run out**, a run carries on with `[model] fallback` (e.g. `"claude:haiku"`, your plan through Claude Code) or stops cleanly and continues the next night. `recall setup` mentions that Claude Max and Team plans include monthly API credits.
- **A second model for consolidation**: `[model] consolidate = "anthropic:claude-sonnet-5-5"`, or `--model extract-spec+consolidate-spec` for one run. A `--model` spec can name an effort after `@` for ChatGPT and Anthropic models.
- **recall never loses a chat it has indexed**, except one you forget and one past a retention period your organisation sets. Every chat is kept compressed in its own table, one copy per file it came from, which a rebuild never drops. When the parser changes, chats are re-read from their files, and those whose files are gone (Claude Code deletes transcripts after 30 days by default) come back from the archive, marked restored. When a re-read finds fewer messages than recall had (Codex compaction has rewritten session files), the missing ones are kept. A chat whose copies live in several files (Cursor writes one per project folder, Codex one in `sessions/` and one in `archived_sessions/`) now shows every message from all of them, where before only the file read last counted.
- **Past tool failures are searchable.** When a tool call fails (Claude Code's `is_error` results, Codex commands that exit non-zero, Cursor tool calls marked failed or terminal commands that exit non-zero), recall keeps one redacted line saying what went wrong, with the tool and the head of its command, in a `tool_errors` table with its own full-text index. Each line also records the message it came after, whether a later identical call in that chat succeeded (a retry, not proof of a fix) and the start of what the agent said next. Text is redacted whole before it is cut, with extra rules for commands (`-u user:pass`, `--password x`, `DB_PASS=x`, webhook URLs). A call the person declined is not a failure. It never enters the chat messages, so search ranking and memory extraction are unchanged. `store.tool_errors(con, text)` finds the chats where an error came up before.
- **`recall forget <chat id>` and `--project NAME`** remove chats from the index, the archive, embeddings, extraction records and exploration reports, and keep them from being indexed again; the database file is rewritten afterwards so the words are not left in free pages. Cached model answers go only when they quote the chat (a paraphrase is not recognised). Notes that cite the chats are listed, not edited, with the git command that removes them from history. The usage log (queries and chat ids) and the short-lived notes between chats about a shared file, which can quote 140 characters of what a chat was asked, are left alone.
- **Retention.** `recall install` and `recall setup` offer to raise Claude Code's `cleanupPeriodDays` when it is under a year (backed up first; uninstall never lowers it). When an organisation sets that period through managed settings, recall forgets Claude Code chats older than it. Unverified: the format of the server-managed cache `~/.claude/remote-settings.json`. Claude Code's documentation confirms the path, not what is in the file; recall reads it as a settings object with `cleanupPeriodDays` at the top level, as it reads a `managed-settings.json` file or an MDM profile.
- **Chats that talk to each other.** When an edit hook finds another chat with open changes to the same file, recall messages that chat too: who is about to change which lines, and what they were asked. Agents answer through the new `message_chat` MCP tool to agree who goes first. An agent can only message chats recall introduced it to, with the token from recall's note, which also names it as the sender; what it writes arrives quoted as another agent's words with no authority, capped at 600 characters and ten messages an hour. Claude Code, Codex and Cursor read their messages on their next edit and at the end of a turn, where they keep going to answer (Cursor as a follow-up message; at most three times an hour per sender); Claude Code and Codex also on the next prompt. `recall install` adds the hooks. The menu bar shows the conversation under the file.
- **Different lines are safe.** The hooks record which lines each edit changes (Claude Code's replaced text, a Codex patch's hunks, the difference Cursor's whole-file write makes), and two live chats changing different lines of one working copy get the new `apart` situation (default `tell`) instead of `same_copy` (default `ask`).
- **Resolve.** In the menu bar, Resolve on a shared file messages every chat involved to agree who goes first, queued for each chat's next edit, turn end or prompt, never interrupting it (`recall resolve --repo … --file …` from the command line). The row shows when it was sent and clears once the overlap does; pressing it again sends nothing more.
- **Exploration reports.** Claude Code's Explore, Cursor's explore and Codex's explorer subagents leave a report, and recall now keeps each one with the commit it saw (the subagent-stop hooks; `recall explore backfill` adds the ones in your history). When `[explore] serve = true` is set in `config.toml`, a new exploration in that repo is handed the earlier report that best matches its question, as notes with the commit, age and files changed since, or nothing when none clears the gate. Serving is off by default: replaying 10 past explorations found no gain in judged quality and about 6% fewer tokens, within the noise. Any agent can ask for the report through `project_memory(path, task)` whatever the setting. Cursor's preToolUse on the Task tool and Codex's capture follow their app code and docs and are not yet tested live; Codex cannot be served automatically, because its SubagentStart comes before the subagent's task exists. `recall eval explore` replays your past explorations.
- **Memory mid-session.** Hooks can hand an agent a past chat or note at the moment it applies: when the prompt closely matches an earlier chat, and when it reads a file (scoped rules it does not load itself, notes naming the file, the last chat that talked about it). A gate fitted on one real history abstains unless it is sure; each item comes once per session, 1,200 characters at most per injection; a preference or a past chat tied to one context (`[contexts]`) is handed only to that context's sessions, while searching chats is not narrowed; hooks send a prompt only to a local embedding model, never a hosted one, and read the index without writing to it, staying quiet while an upgrade has it to rebuild. Claude Code, Codex and Cursor. Failed commands and compaction are wired too but rarely fire until the index keeps tool errors. `recall jit` shows what would fire and why; `recall usage` counts what did; `recall eval jit` fits the gate to your history and `--live` measures its precision on real injections.
- **Several accounts and installs, connected by itself.** recall now finds every folder an agent keeps its settings and chats in: the defaults, `CLAUDE_CONFIG_DIR` and `CODEX_HOME` from the environment and from `export` lines, statement-start assignments and aliases in your shell rc files (not text that only mentions them), `~/.claude-*` style folders that hold a real config (backups and copies of the default are skipped), and Cursor builds such as Nightly. Each gets a stable name, is indexed, and every chat records its folder (an added `root` column; the chat index is not rebuilt). A folder is written to only after the agent has used it, and never the home folder or a parent. `recall doctor` shows each folder as connected, trust pending (Codex), approval pending (Cursor), skipped or waiting, with a masked account hint and when it was last indexed. `recall setup` asks once, default no, whether to keep recall connected automatically (`[autopilot] connect = true`; `--autopilot` answers it, `recall autopilot on|off` changes it): a nightly check and the session-start hook then install the MCP server, hooks and skill into folders that are new or have lost them, with each file backed up first (the first backup is kept) and one line per change in the usage log. The yes covers the hook kinds of the release it was given for; later kinds wait for `recall install`. Per root, `[roots.NAME]` sets `mine` (a second account's chats become memory only when you opt in), `index` and `connect`. `recall install` and `uninstall` cover every folder found (`--root NAME` one), and `uninstall` turns autopilot off and also reaches folders it connected that are no longer found. Hooks that run another existing recall (a checkout's `uv run recall` beside the installed one) are left on it; `recall install` and `recall doctor` say which recall they run, and `recall install --repoint` moves them (autopilot never does). `docs/benchmarks/setup_eval.py` checks all of it in a sandbox home.
- **What your agents are doing now.** `recall status` lists the Claude Code, Codex and Cursor chats active in the last 12 hours, the branch each is on and the files it edited, and flags a file that two chats in one repo both have open changes to: whether the two would conflict if merged now (`git merge-tree`), or share one working copy. Each flagged file comes with a note to paste into the other chat. It reads agent history directly and caches each chat by its size and mtime, so it stays current and cheap.
- **Rules for when two chats change the same file.** Before an agent edits a file another chat has open changes to, recall applies your rule for the situation: another live chat in the same working copy, a branch that would conflict, or an overlap that merges cleanly. `ask` stops the agent once so it checks with you, `worktree` has it move into its own git worktree, `tell` lets the edit through and tells it, `off` does nothing. Each other chat is raised once, listing every file the two share. `recall setup` offers presets; the recommended one asks before two live chats edit one working copy and mentions conflicts.
- **Edit hooks for Claude Code, Codex, Cursor, Gemini CLI and OpenCode** (a plugin), added by `recall install` (Codex runs them once you trust them in `/hooks`). T3 Code runs Claude Code, Codex and Cursor underneath, so it is covered too. Every hook also logs the edit, so `recall status` sees chats from agents whose history recall cannot read yet. Agents without hooks can call the new MCP tool `open_elsewhere` before editing.
- **Which chat wrote this.** `recall why <file>[:line]` follows code back through git blame to the chats that produced it, with dates, the chat's title and what the person asked; `recall why --commit <sha>` does the same for a commit. Agents get it through MCP: `recent_chats` takes `file` or `commit`. Each chat's edits are matched with the lines later commits add, so worktrees, clones, rebases and squash merges are covered, and a chat that ran `git commit` (or revert, or cherry-pick) is matched by the sha git printed. The links stay in recall's index on your machine: git is only read, with lazy fetching and textconv off, and nothing is written to any repo. They are worked out without a model, in the nightly run and on each `recall why`; `[links] enabled = false` turns them off and deletes them. `recall links --episodes` lists commits that fixed something that had failed. It is experimental: a judge found 24% of 21 sampled episodes right (0 of 14 for failed checks and commands, 5 of 6 for corrections), so do not build on it yet.

## Changed

- **The Claude provider's `haiku`, `sonnet` and `opus` now run the 5.5 models.** Claude Code's own aliases had resolved `haiku` to Haiku 4.5, so `claude:haiku` (the setup default for the Claude plan) ran a model a generation older than its name. recall now passes `claude-haiku-5-5`, `claude-sonnet-5-5` and `claude-opus-5-5`, so an existing `name = "haiku"` needs no edit, and a full id still passes through unchanged. The label and the response cache follow the pinned id. When Claude Code serves another model than the one asked for (an old install, or a plan that serves something else), recall says so once on stderr. Haiku 5.5 thinks before it answers, so each call uses more of the plan than Haiku 4.5 did. On MemoryCode, notes written through Claude Code held 97.6% of the facts with the full model id (one run), against 80.9% with the old alias.
- **Archive copies load whatever a later version added to them.** Reading a copy ignores keys and message fields it does not know, so a change that adds a field to the archive can be reverted without breaking every index run.
- **One hook command.** Every hook recall installs, for every agent, now runs `recall hook --agent <agent>`, which hands the event to the features that want it and answers in that agent's format. `recall install` replaces the older `context --hook` and `status --hook` entries, which keep working until then. CONTRIBUTING.md has how to add one.
- **Session start loads the project's memory, and keeps work and personal apart.** General preferences used to fill the whole block before the project's notes began. Each section now has its own share of a 4,500-character body (down from 6,000); a project index cut to size keeps the best notes of every kind, by title past the first two. `[contexts]` in config.toml names each context's folders: the nightly run judges once whether a general note holds everywhere or only where it was learned (`reach:` in the note, editable), and a note not yet judged loads everywhere, as before. Notes from chats with no folder move to their project once every one of those chats resolves to it. `recall eval session` grades the block against labelled notes for real folders.
- **The MCP server reports recall's version** when an agent connects; it reported none.
- **A shorter README.** It now covers what recall does, how to install it and the headline results; the detail moved into pages under `docs/` (benchmarks, how it works, projects and rules, coordination, commit links, exploration reports, accounts, reference). Figures were rechecked against their runs: the MemoryCode agent result now comes from the runs that load recall's tools as Claude Code does by default, and search times from the dedicated timing run.
- **Memory notes hold rules, and consolidation changes them one at a time.** A note is a title and a list of rules, each with an id, the dates and chats that stated it, and a quote. The model answers with operations on named rules (confirm, refine, retire, add, create), and code checks each one: a rule leaves a note only through a retire that names it, a chat older than a rule's last sighting cannot retire or refine it (or one older than its retirement add it back), a note you edited mid-run is not overwritten, and nothing is written until every operation in the batch is checked. Consolidation prompt v3. The note's body is rendered from its active rules, and a hand edit of the body is read back into them: a rule whose words still stand keeps its id and sources, and a rule you delete is remembered as removed by hand even when its words sit inside a longer rule. A rule carries `said_by: developer` only when the developer typed a quote that supports that rule outside any paste and the rule asks for no command, URL, network, destructive or exfiltrating action, or to switch off a sandbox, permission or review; otherwise it is unset. It is a hint, not authentication, and no reader shows it yet. Notes written this way still read as plain notes in an older release, which drops the rule fields if it rewrites the note. On a replay of the same extracted facts (MemoryCode, 40 held-out histories), facts lost when notes merge fell from 12.5% to 8.1%, recall rose from 70.5% to 75.3% and stale notes fell from 9.3% to 4.8%.

## Fixed

- The PyPI page shows the logo, the charts and working links: the build points the README's relative paths at GitHub.
- Text that reaches the user's turn without the person writing it is no longer indexed as theirs: Claude Code peer messages and task notifications delivered mid-turn, compaction summaries and `!` command output; Cursor's session-hook context and tool catalogues; Codex goal continuations, skill bodies, browser state and subagent reports. Memory extraction reads user turns as the developer speaking. A chat whose file is gone comes back from the archive with the same cleaning applied to its user turns, and Claude Code's compaction summary is dropped from it. What the archive cannot undo is a peer message or task notification an older release stored as the person's words: Claude Code marks those only in the chat file, so once the file is gone they cannot be told from the person's text and stay.
- A chat file deleted while recall was listing files no longer stops the index run.
- **Codex chats Codex had compressed.** Codex turns rollouts a week old into `.jsonl.zst`, and recall only looked for `.jsonl`, so most of a long Codex history was neither indexed nor readable again. recall now reads both forms: through Python 3.14's built-in zstd, the `zstandard` package (new `recall-agents[zstd]` extra) or the `zstd` command. A chat keeps one identity whichever form is on disk, so compressing or resuming it changes nothing. When none of the three is available, `recall index`, `recall stats` and `recall status` say how many Codex chats cannot be read and how to fix it. A damaged file is skipped with an error, and a `zstd` that hangs is killed after a minute. Commit links and Explore's history read compressed Codex chats, and `recall forget` lists the `.jsonl.zst` that holds one.
- **Cursor chats get their folder and their kind.** Cursor records a chat's folder, and whether an agent spawned it, on the chat itself; recall now reads both, and places a helper chat that recorded neither in its parent's folder, up a chain of nested helpers. On one real history, Cursor chats a person typed in with no folder fell from 932 to 199, and about 720 helper chats (explore and the like) are marked as subagents, so memory extraction no longer reads them as the developer speaking. Side chats stay the person's. Notes already learned from helper chats stay until edited.
- **Secrets the redactor missed.** A key written as `"apiKey": "..."`, `**Client Secret**: ...`, `API Key: ...` or `use this api key ...`, in a URL query (`?apiKey=...`, `%3D`, `&amp;`), as `SOME_NAME_KEY=...`, or in a shape of its own (Azure AD client secrets, `sk_` keys, `fc-` keys, `Bearer` and `Basic` tokens, `user:password@`, `:password@` and `token@` URLs, Slack and Discord webhook URLs, `Cookie:` values, GitLab deploy, runner and trigger tokens, PyPI, Docker Hub and Google OAuth client secrets, private keys cut off before their END line) went into the index as written. Each is now redacted, and so are session titles and a key that straddled the cut at 20,000 characters. A key pasted again further down the same chat, with no word beside it, goes too, in the chat's opening prompt and in the lines kept for failed tool calls as well (they use these rules, plus the command rules that were written for them, now in the same file and covered by the same version hash). A hash, UUID or commit id is still left alone unless a word like "api key" sits next to it, and so is a path (`SSH_KEY=~/.ssh/id_ed25519`). Text recall cannot rebuild from your agents' files (extracted candidates, cached model answers, error messages, failed tool calls, exploration reports, archived chats) is rewritten with the current rules at the start of an index run (`chats/scrub.py`), once per version of the rules (a hash of them) and again after every rebuild; a restored archive copy, and the failed tool calls it carries, are redacted again as they are read. Your notes are not rewritten: the same run names the notes, and the entries of `.recall/notes.json`, that the rules match, on stderr and in `logs/redact.log`. The chat
