A memory that can change its mind

Last month your customer moved their order database from Postgres to MariaDB. You told the agent, and it wrote that down. This month you ask, casually, which database the order service uses — and it answers Postgres without hesitation. When you go look, the newer fact is there, the older one is still there too, and nothing in either of them says which one you should believe. The hard part of memory was never storage. It is changing your mind: the world moves, a note turns out to have been wrong, a statement stops holding — and “forgetting” should not mean “the text is still sitting in the file.”
The local fact store holographic spent the last few weeks filling in exactly that half. It used to live inside the Hermes tree as a bundled provider; on October 11 it moved to its maintainer’s own repository and started shipping as a catalog plugin, and on October 13 it was bumped to 0.13.0. That upgrade brought more than a change of address.
From overwrite to revision: five actions
Until recently, there were roughly two things you could do with a remembered fact: add one, or delete it and add it again. The second option quietly changed the fact’s id, so anything pointing at that id broke. A fact now has its own history, and the actions split five ways:
| Action | When you use it | What it means |
|---|---|---|
change |
the world moved (same thing, new answer) | a new version of the same fact; the id stays |
correct |
the note was wrong when written | a correction; the superseded wording stops appearing |
end |
it no longer holds, but it was never wrong | close it, keep the history |
revert |
you want to undo the last edit | back to the previous version |
history |
you want to see what this fact has been through | lists every version |
Compatibility is handled: the old spellings still work. add supersedes is treated as change (and no longer changes the id), update content as correct, and remove as forget. Your existing scripts do not need rewriting.
Asking “what did we think back then”: as_of
Once history exists, a new question appears: do you want to know what is true now, or what you believed on September 10? The two answers can differ, and during an incident it is usually the second one you need.
Search and list therefore accept an as_of argument such as as_of: 2026-09-10. It answers only from the versions that held on that date, and returns a separate undetermined list for facts whose state on that date cannot be established. An unknown start is reported as unknown rather than invented, which is the part worth noticing. Default search still sees only current facts: historical text lives in its own table and index, so it never pollutes everyday results.
Every version also records where it was written — platform, chat or thread id, session id, and the turn author’s id and name. Chat titles are never stored, so provenance does not quietly drag your group names into the database.
Forgetting for real
On many systems “delete” flips a flag while the text stays in the file and in backups. forget goes further: the fact and every version of it are removed, both full-text indexes are rebuilt, the operation runs with secure_delete, then VACUUMs and truncates the WAL — the goal being that the text leaves the file, not just the query results.
What remains is a tombstone holding the id and a keyed hash, never the text. That keeps auto-extraction from resurrecting the fact, and lets other tools clean their own copies by id. The cost is worth stating plainly: this cannot be undone.
One bot, many unrelated groups: scope: chat and scope: thread
If your bot sits in several customer groups, cramming every fact into one database is a liability. scope: chat gives each conversation its own database; scope: thread goes one level finer, per forum topic. Both use the chat and thread ids the core already passes to memory providers — no core changes and no routing logic of your own.
Three safety lines: credentials, images, untrusted text
A memory store has an easily overlooked risk surface: whatever it holds is injected into every later turn. Writing is therefore not enough — content is checked on the way out too.
- Credentials with a value are never stored. A credential word with its value next to it (
password hunter2,API key sk-…) is refused with a reason; the word alone is still a legitimate fact (“I use a password manager”). 0.13.0 widened the window to three words while cutting false alarms:the token is 0.125 USDis a price, not a password. - An image is not a fact. Markdown or HTML image syntax is never treated as a fact, because the client that renders it fetches that address itself, and data can ride along in the path or the query string.
- Recall checks again before injecting. In 0.13.0 a fact is re-examined on the way out: chat-role or tool-call markup, instruction-like phrasing, a credential with its value, invisible characters and images all keep a fact out of recall, logged once per process. Note that this does not delete anything —
search,listandhistorystill show it, because you asked and you are entitled to correct or forget it.
One trap worth knowing: writes from cron
If you run memory maintenance on a schedule — a nightly tidy, say — there was a bug that stayed quiet for a long time: since 0.2.0, memory writes from a cron job simply did not take effect. The core passes agent_context: "cron", and the plugin treated every such context as read-only, so your maintenance job kept reporting success while storing nothing. The nightly consolidation plugin is the kind of thing that runs into this.
That is now split apart: explicit fact_store / fact_feedback calls from cron write again (cron_writes, on by default), while automatic writes — auto-extraction, mirroring, recall counts — stay off there. A manual tidy-up script can do its job without quietly accumulating facts unattended.
About the move: there is nothing for you to do
Holographic used to live in the core tree (plugins/memory/holographic/); since October 11 it is a catalog plugin maintained by itpartypattaya. The good news is that only the code changed address:
- the provider name is unchanged —
memory.provider: holographicstill works; - the config section is unchanged — still
plugins.hermes-memory-store; - the database is unchanged — still
$HERMES_HOME/memory_store.db, or thedb_pathyou set; - the tools are unchanged.
The upgrade path is automatic: hermes update installs the catalog plugin for every profile home that names the provider, and if you update through the desktop app the agent installs it and tells you the first time it starts. If the install cannot happen, you are not left silently memory-less — with security.allow_lazy_installs: false, offline, or a declined install, the warning names the exact hermes plugins install holographic command for that profile. Two commands confirm the state:
hermes memory status
hermes plugins list
The full install and switch flow:
hermes plugins install holographic # from the plugin catalog
hermes memory setup # pick a provider interactively
# or set it directly:
hermes config set memory.provider holographic
The mechanics of the move match what we wrote in memory providers leaving core; the difference this time is what the plugin grew afterwards. (If you installed Nous Research’s interim copy from its own repository, hermes plugins update holographic moves you onto the catalog entry and your data stays put.)
How it compares
| Option | What it stores | Best for |
|---|---|---|
| Built-in MEMORY.md / USER.md | preferences and facts you always want loaded | everyone (always active) |
| holographic (this post) | a local SQLite fact store with version history and trust scores | fully local, when you need revisions and real deletion |
| Semantic memory (LanceDB) | long-term memory retrieved by vector similarity | recall by meaning rather than by fact |
| Nightly consolidation (dreaming) | corroborates candidate facts overnight and proposes what to keep | you already keep a fact store and want it to converge |
Multilingual recall also gained ground. Simplified and Traditional Chinese are recognised as each other, Japanese spelling variants match, German compounds split both ways, Russian same-root words come from a dictionary, and English, Spanish, French and Portuguese word forms are covered — data from Open English Wordnet, jieba, SudachiDict, Wikidata Lexemes and DerivBase.Ru, under CC BY 4.0, MIT, Apache-2.0 and CC0 licences respectively. The maintainer’s blind-set numbers: Chinese questions written in the other script went from 33% to 100% recalled, and Russian same-root questions from 83% to 92%. Those figures are the maintainer’s own measurements — useful, not gospel. If you want less noise, recall_indicator (on by default) and auto_extract (off by default) are the two switches to reach for; trust moves +0.05 for helpful feedback and −0.10 for unhelpful.
The short version
- The world changes and notes turn out wrong. A memory that can revise itself deserves the name:
change/correct/end/revertkeep the versions, andas_oftakes you back to what held on a given day. - Forgetting is a destructive operation: every version, every index, every leftover in the file, leaving a tombstone with no text — and it cannot be undone.
- Re-checking content before injection is what makes this store safe to trust: credentials, images and imperative-sounding text never ride into the next turn unnoticed.
- If holographic is already configured, upgrading asks nothing of you. If it is not, it is now a catalog plugin and one command installs it.