The second product
Agent Memory
Everything an agent framework keeps in files — goals and their evidence, plans and waves, tasks and claims, checklists, decisions, risks, what each agent remembers and what the project vouches for — as queryable records instead. One service, three surfaces, written in Rust on TessariDB.
An agent starts a session, gets its context back, works, and hands over what it learned. Another agent picks it up an hour later and reads the same records — not a directory it has to guess the shape of.
0.4.1-alpha · on TessariDB 0.33.1-beta · published as a container image · installs as a Claude Code or Codex plugin · no export path yet
Files work until the second agent starts.
Agent frameworks tend to keep their state in files — markdown notes, JSON registries, a lock file for coordination. That works for one agent and stops working the moment several run at once. There is no transaction, so concurrent writes race; no query layer, so finding anything means scanning; no history, so nobody can say who changed what; and no live view, so an operator cannot see which agent is working on what right now.
The failures are quiet, which is the part that costs. Two agents write the same registry and the second one wins. A stale lock file freezes a project because the process that took it died without releasing it. A knowledge entry links to another that links back, and six weeks later the two descriptions disagree with nobody in an error state.
What a store gives that a directory cannot
Concurrency
Real transactions instead of advisory locks. Two agents writing the same registry is an ordinary concurrent write, not a race whose loser disappears.
Retrieval
Structured queries, full-text search and vectors over the same records — and the words and the meaning ranked together in one read, by each record's place in both orders rather than by adding a score to a distance.
Relationships
Knowledge as a graph. A link to a record that does not exist is refused by name at the moment it is written, rather than dangling until somebody follows it.
History
Every change versioned and attributed to the session that made it. Who changed this, and what did it say before is a question with an answer.
Presence
Who is active, on what, since when, and with what claim — so an operator can see the fleet instead of inferring it from file timestamps.
Reuse
A model's answer kept until a lifetime the caller names and reused only above a similarity the caller names — marked as reused, never handed to a question it does not fit.
Replay
What an agent saw when it decided, read back from the database's own history — each hit as it was then, beside what it says now.
Live updates
The database streams its changes in order from a position. The catch-up verb that rides on it is declared and withheld until a read can say where it stood.
Each of those is an engine of the database underneath rather than a feature written on top of it, which is the whole reason this product exists in this repository and not somebody else’s. The same argument, from the database’s side →
What it holds
| Area | What it holds |
|---|---|
| Identities | The authenticated tenant. One identity's whole world lives inside its own namespace. |
| Agents | Identity across time — kind, and the capabilities it declares. |
| Sessions | One run of one agent: host, client, model, framework, what it claims. |
| Projects | The registry, profiles and policies, as structured children rather than files. |
| Goals | Objectives, and the per-criterion evidence each one was validated against. |
| Plans and waves | The plan, the waves it is cut into, and steps that trace back to what asked for them. |
| Tasks | Decomposition, status, dependencies, claims. |
| Checklists | A phase gate with a header: which phase, which flow, and whether it was written before the phase it guards. |
| Questions, decisions, risks, ideas | Four lifecycles kept apart. A question owes an answer, a decision is superseded rather than edited, a risk is carried until it is mitigated or accepted, an idea waits to be promoted or dropped. |
| Artifacts | The documents a flow produced — research, reports, snapshots, critiques, concepts, audits — with the rows attached to each. |
| Memories | What an agent remembers, typed by how long it should live — with the vector it arrived with. |
| Facts and rules | What the project has established, and what binds work here. |
| Activity | What happened, when, by whom, against which object. |
| Answers | A model's answers, kept against the question's vector for as long as the caller said, and reused only above a similarity the caller named. |
| Witnesses | What a recall showed — its query and the version of every hit — so it can be shown again as it stood. |
| Coordination | Who is holding what, how far a reader has caught up, and whether a request has already been carried out. |
| Guidance | The prompts and hints themselves, as data rather than baked into a binary. |
Memories and facts are split by who vouches
A memory is asserted by one agent from its own experience, needs no ceremony, and may be wrong — which is expected. A fact is asserted by the project, carries a closed topic taxonomy and a provenance, and when it claims to be confirmed it must point at evidence.
The reason there are two of them rather than one is that either rule alone fails: an agent that must satisfy a schema before it can note anything will note nothing, and a knowledge base that accepts unevidenced assertions from anyone stops being a knowledge base. So there are two tables and one bridge — a memory that proves durable is promoted, and the expensive schema is paid once, later, only for what turned out to matter.
Shipping something is a record, not a ritual
The part people expect to be a wiki page is the part this stores properly. A release is not a tag somebody remembers to cut: it is a goal whose every criterion has to point at evidence before it can be called achieved, a plan cut into waves whose steps trace back to what asked for them, tasks that are claimed by one agent at a time, and checklists that gate a phase rather than decorate it.
A checklist here is not a list of ticks. Its header is what makes it a gate — which phase it guards, which flow it belongs to, whether it was written before the phase it gates, and, when most of its rows do not apply, why. The counts are derived from the rows and never stored, because a stored count drifts from the rows it describes, and a checklist whose banner disagrees with its own rows is worse than one with no banner: it reports a gate that was never run. Rows are discharged one at a time, and each carries a value rather than a tick.
Around them sit the four things a single “notes” table would flatten into one, kept apart because their lifecycles differ: questions owe an answer, decisions are superseded rather than edited, risks are carried until they are mitigated or accepted, and ideas wait to be promoted or dropped. The documents a flow produces — research, reports, snapshots, critiques, concepts, audits — are one record type with the rows attached to each, so a finding is something you can query rather than something you remember reading.
None of this is a workflow engine bolted on top. It is the same store, the same transaction and the same search as everything else here, which is why a question raised in a critique and a task blocked on it are one hop apart instead of one search across two tools.
The store is invisible, and the identity is the namespace
Agents speak only to this service; the service is the only database user, the only reader and the only writer. Nothing about the query language, tenancy or indexes reaches an agent — an agent knows a set of methods and nothing else.
That is not tidiness. A maintained updated_at exists only if exactly one writer maintains it, and the same decision keeps the risks of a pre-1.0 store inside one replaceable component. Tenant isolation follows the same shape: the authenticated identity is the namespace, so isolation is a property of the credential rather than a filter somebody has to remember to apply — and a bug in path handling cannot leak one tenant’s records into another’s answer.
Three surfaces, one contract
A CLI for humans and scripts, a REST API for services, and MCP so an agent uses it as a native tool surface rather than shelling out. All three are renderings of one machine-readable contract: the tool definitions, the OpenAPI document and the CLI flags are generated from the same schema, so a behaviour cannot exist on one surface and not another. Input is validated and refused by name; output has one declared envelope.
The guidance those agents read lives in the store rather than in the binary, so when a foreign agent misuses a tool the fix is a row rather than a release.
Run it
One image holds the database and the memory service. 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/plugin marketplace add tessaridb/tessaridb-agent-plugins
/plugin install tessaridb-agent-memory@tessaridbcodex plugin marketplace add tessaridb/tessaridb-agent-plugins
codex plugin add tessaridb-agent-memory@tessaridbThe plugin registers the memory server at 127.0.0.1:39142 and carries a skill that tells the agent when to reach for it. It does not carry the memory: the container above has to be running, and until it is, /mcp shows the server as failed to connect. On another port, register it by address instead — one registration, 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, and it needs the service at 0.3.0-alpha or later.
A project is its folder. An agent sends the folder it works in. The first time, the service says the folder is not declared and what to do: the agent looks at the subfolders, asks you whether this is a group of projects — and which of them — or one project, and starts again with the answer. Every agent after it, anywhere below that folder, lands in the same project without asking.
Status — what exists today
This is a report, not a roadmap.
| State | What is in it |
|---|---|
| Works | Twenty-two of the twenty-five verbs, on all three surfaces: starting a session and getting your context back, status, recall and replaying a witnessed recall, reading a recall’s hits in full, remember, keeping and reusing a model’s answers, adding and updating and linking a record, claiming a task and handing it to another session, achieving a goal against its criteria, discharging a checklist row, the four release verbs, and the activity log. Since 0.4.0-alpha: teams and grants that name them, memories private to their author or shared with a team, credentials redacted before anything is stored, a repeat confirmed rather than stored twice, memories anchored to files, and facts that know when their evidence was withdrawn. An MCP client connects by URL and authorizes itself in the browser; a session is placed by the folder it works in. Exercised end to end against a running node rather than asserted here. |
| Withheld, and says which | Three: catching up from a position, and the export and import round trip. Each is described in the contract and refused by name, with what would have to change — because a caller told only “not yet” waits for a release that is not coming. The catch-up has its change feed now and still needs a read that says where it stood; the backup format is the owner’s to decide and inventing one here would decide it by accident. |
| Published | 0.4.1-alpha as the image tessaridb/agent-memory, for linux/amd64 and linux/arm64. 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. |
The design of record is a closed concept carrying the verdict needs-evidence, and the criterion the rest wait on is the export and import round trip — in this project’s own format, which is what would make a pre-1.0 backend a survivable choice rather than a reckless one. Until it exists there is no way to take your data back out, and that is stated here rather than discovered later.