TessariDBDocs
  • 0.34.2-betalatest
  • 0.34.1-beta
  • 0.34.0-beta
  • 0.33.4-beta
  • 0.33.3-beta
  • 0.33.2-beta
  • 0.33.1-beta
  • 0.33.0-beta
  • 0.32.0-beta

Agent Memory

A second product built on this engine — how to run it, what it stores, which parts of TessariDB it leans on, and what it does not have yet.

TessariDB Agent Memory is a separate product built on this engine: the state an agent framework normally keeps in files — goals and the evidence behind them, plans and waves, tasks and claims, checklists, decisions and risks, and what each agent remembers — held as queryable records instead, behind one service with a command line, a REST API and an MCP surface.

It is documented here for one reason. Reading a list of engines tells you what a store can do; a real application tells you which of them you actually end up using, and in what combination. This page is that second thing.

0.4.1-alpha, on TessariDB 0.33.1-beta, published as a container image: tessaridb/agent-memory, for linux/amd64 and linux/arm64. Twenty-two of its twenty-five verbs are carried on all three surfaces; the other three — catching up from a position, export and import — are declared in the contract and refused by name, with what would have to change. Versions before 1.0 promise no compatibility with each other; a 0.1.x store upgrades itself on its first start under 0.2.0-alpha.

Run it

The image holds the database and the memory service together. Choose the first owner's passphrase — it is what you type in the browser when an agent asks to be authorized — and start it:

mkdir -m 700 -p ~/.tessaridb
read -rs "p?Passphrase for the first owner: " && printf '%s\n' "$p" > ~/.tessaridb/owner-passphrase; unset p
chmod 644 ~/.tessaridb/owner-passphrase

docker run -d --name agent-memory --restart unless-stopped \
  -p 127.0.0.1:39142:39142 \
  -v tessaridb-agent-memory:/var/lib/tessaridb \
  -v ~/.tessaridb/owner-passphrase:/run/secrets/owner-passphrase:ro \
  -e TESSARI_AM_OWNER=ada -e TESSARI_AM_OWNER_IDENTITY=acme \
  -e TESSARI_AM_OWNER_PASSPHRASE_FILE=/run/secrets/owner-passphrase \
  tessaridb/agent-memory:0.4.1-alpha

Then connect Claude Code through the plugin, from the TessariDB agent plugins marketplace:

/plugin marketplace add tessaridb/tessaridb-agent-plugins
/plugin install tessaridb-agent-memory@tessaridb

The plugin registers the memory server at http://127.0.0.1:39142/mcp and carries a skill that tells the agent when to reach for memory. It does not carry the memory itself: until the container above is running, /mcp shows the server as failed to connect, which is the container missing rather than a plugin fault. For a whole project, the marketplace's README shows the .claude/settings.json that enables it for everyone who trusts the folder.

In Codex the same plugin installs from the same marketplace:

codex plugin marketplace add tessaridb/tessaridb-agent-plugins
codex plugin add tessaridb-agent-memory@tessaridb

The plugin expects port 39142. If the container publishes another one, register the server by its address instead — and keep one of the two registrations, not both:

claude mcp add --transport http -s user --client-id claude-code --callback-port 39143 \
  tessaridb-am http://127.0.0.1:<port>/mcp

/mcp → tessaridb-am → Authenticate opens the browser; sign in as ada and approve. Every Claude Code on the machine shares that sign-in and renews it without a person. In Codex the same step is codex mcp login tessaridb-am, against a service at 0.3.0-alpha or later. The store underneath always has a declared user — its password is drawn on the first start and kept in the volume — and only the memory service is published.

A project is its folder. An agent sends the absolute folder it works in with session_start. The first time, nothing is declared there, so the start is refused saying what to do: the agent looks for project markers in the subfolders, asks you whether the folder is a group of projects — and which subfolders are its projects — or one project, and starts again with that answer. From then on every agent working anywhere below that folder lands in the same group and project without asking. Names are the folders' own, lowercased, with any character a name cannot hold turned into _.

What it stores

AreaWhat it holds
Identitiesthe authenticated tenant; one identity's whole world lives inside its own namespace
Agentsidentity across time — kind, and the capabilities it declares
Sessionsone run of one agent: host, client, model, framework, what it claims
Projectsthe registry, profiles and policies, as structured children rather than files
Goalsobjectives, and the per-criterion evidence each was validated against
Plans and wavesthe plan, the waves it is cut into, and steps that trace back to what asked for them
Tasksdecomposition, status, dependencies, claims
Checklistsa phase gate with a header — which phase, which flow, and whether it was written before the phase it guards; counts derived from the rows, never stored
Questions, decisions, risks, ideasfour lifecycles kept apart: a question owes an answer, a decision is superseded rather than edited, a risk is carried until mitigated or accepted, an idea waits to be promoted or dropped
Artifactsthe documents a flow produced — research, reports, snapshots, critiques, concepts, audits — with the rows attached to each
Memorieswhat an agent remembers, typed by how long it should live — with the vector it arrived with, the files and symbols it is about, and who may read it: the whole project, its author alone, or one team
Facts and ruleswhat a project has established, and what binds work there — a fact cites the records it rests on, and knows when one of them is withdrawn
Teamsa named set of people and agents; a grant may name a team, and one team reads another's facts only through a grant that ends
Activitywhat happened, when, by whom — the user and the credential as well as the session — against which object
Answersa model's answers, kept against the question's vector until a lifetime the caller named, and reused only above a similarity the caller named
Witnesseswhat a recall showed — its query and the version of every hit — so it can be shown again as it stood
Coordinationwho is holding what, how far a reader has caught up, and whether a request has already been carried out
Guidancethe prompts and hints themselves, as data rather than baked into a binary

What it asks of the engine

This is the part worth reading if you are building something similar. Each row is a demand the application makes and the part of TessariDB that answers it.

The demandWhat answers it
several agents writing the same records at oncetransactions — a write lands whole or not at all, instead of the advisory locking a directory of files needs
finding a memory by what it saysfull-text search over the prose a memory and a fact carry, through one declared analyzer
finding a memory by what it meansvectors stored in the memory itself, compared only within one model
ranking by the words and the meaning at onceseveral orders at once — ORDER BY FUSE combines each record's place in the text order and in the vector order, never a score and a distance; the engine does it in one read per family
reusing an answer a model already gaveexpiring keys — a cached answer is a key of a space that stops being answered at the instant its lifetime ends, on every node, whether or not its bytes are gone yet
what did the agent see when it decidedthe record's own history, read back to the version a recall showed (history)
knowledge that links to knowledgethe graph — a link is an edge, and one to a record that does not exist is refused when it is written rather than when it is followed
what did this record say beforeVERSION — the store as it stood, back as far as reclamation has left
one tenant's records never appearing in another's answerthe tenancy — the authenticated identity selects the namespace, so isolation is a property of the credential rather than a filter somebody has to remember
a private memory staying private on every readthe reader's audience is a condition in the statement of every search, the activity page included, so a record the reader may not see is never fetched, counted or ranked; a read by key answers it as absent
the same words written twice being one memorya key record whose identifier is a digest of the words, created in the write's own transaction — the store refuses the second create, so two writers racing with the same words leave one memory confirmed twice
knowing what rests on a withdrawn factthe citations are an edge table, walked back a level at a time in the same change that withdraws
declaring the shape once, and being told when a write does not fitdefinitions are statements, so the schema ships with the code that needs it

The engine is deliberately invisible to the agents themselves. The service is the only database user, the only reader and the only writer, and an agent knows a set of methods and nothing else. That is not tidiness: this store has a field default that fills an omitted value and no clause that recomputes one on write, so a maintained updated_at exists only if exactly one writer maintains it.

What it does not have yet

Three of the twenty-five verbs are withheld rather than missing, which is a different claim: each is described in the contract and refused by name, carrying what would have to change. Catching up from a position is half there: the engine now streams changes in order from a position (protocol), but no read reports the position it was answered at, so there is nothing exact to hand a caller to resume from. Export and import wait on a backup format that is the owner's to decide, and inventing one here would decide it by accident. The export is the one the rest wait on, because a store you cannot take your data out of is not a choice anybody should make twice.

A caller told only not yet waits for a release that may never come, which is why the refusal says which half is missing.

Where to read about it

The product's own page is on the main site: tessaridb.com/agent-memory. The image, its variables and its volumes are described on Docker Hub.