Expand description
Where basis’s conversations are persisted, and how they are scoped.
mentra persists every agent to its runtime store — since basis 0.7 the
file-backed one, plain files under one root, no database (ADR-0023;
upstream mentra#28) — and tags each record with a runtime
identifier. basis uses that tag to answer one question: which
conversations belong to this workspace? — which is what ACP’s
session/list asks and the only reading of “my sessions” that is both
honest and useful, since ACP scopes a session to a cwd from the moment
session/new opens it.
§Why the workspace path, verbatim
mentra’s default identifier is the literal string "default", and its
default store is one shared root. Listing under "default" would
therefore enumerate the agents of every mentra program on the machine —
worse than returning nothing, because a client would offer a user
conversations that are not theirs.
The identifier is the canonicalized workspace path with a basis: prefix. No
hash: the identifier never becomes a filename — mentra keeps it as a field
of each agent’s agent.json and filters listings by comparing it — so
every character survives, and a readable value is one a person debugging
the store can understand by opening the file. The prefix keeps basis’s
records from ever colliding with another program’s "default".
Changing the identifier does not move the store — mentra’s default path is
independent of it — so nothing already written is lost. Records created
before this scheme carry "default" and do not appear in any workspace’s
list, which is the correct answer for a conversation whose workspace was
never recorded. They are not stranded: mentra loads an agent by id alone,
so resuming one still works. WorkspaceBuilder::open
is where the tag is set, and where that ruling is written down.
They stopped re-tagging themselves in mentra 0.27, and mentra 0.28 turns
that back on as an explicit basis-side choice. Every persist used to
re-derive the tag from the live runtime’s own current identifier, so the
first resume-and-run of an old "default"-tagged conversation silently
adopted it into the resuming workspace’s list. mentra 0.27’s fix for
mentra#54 rebound a
resumed agent to its own stored identifier and carried that forward
instead, which was the right fix for #54’s bug (a shared runtime’s
resumed-then-run rows re-filing under its generic tag) but also closed
this door: a legacy record stayed "default"-tagged forever, with no
code path left to adopt it — filed as
mentra#59. mentra 0.28
reopens it explicitly: SessionResumeOptions::runtime_identifier rehomes
a resumed agent under a chosen identifier on its next persist, without
retagging any other agent or the shared runtime’s own identifier.
Runtime::resume_minted (crate-private) passes
this workspace’s own identifier on every resume, not only for a legacy
"default" row — the check it makes immediately after already refuses to
hand back a session whose base_dir disagrees with this workspace, so
every row a resume succeeds on is provably this workspace’s own, and
restating its identifier is always correct.
Every workspace tags its own rows, on a private runtime and on a shared
one alike: Runtime’s mint states the identifier per
session, so one store file serving five repositories still lists each one’s
conversations apart.
The gap that used to live here is closed. A resumed session used to
carry no identifier — mentra’s SessionResumeOptions had no field for one
— so a resumed conversation re-persisted under the runtime’s own tag on
its next save, taking its row out of the workspace’s list on a shared
runtime the moment it was used again. mentra 0.27 fixes this at the
source rather than by adding that field: every persisted-agent
reconstruction now retains the row’s own stored runtime identifier
through every later save
(mentra#54), so
Runtime::resume_minted needed no change to pick it up.
§Where the files go
mentra’s default directory is keyed by the process’s current directory,
not by the workspace basis opened, so every program started from one place
shares one store root whatever workspace it went on to open — including
every test binary in one cargo test.
RuntimeBuilder::with_store_dir
is how a caller says otherwise, and list_in is how the same caller reads
back what it wrote. The directory the caller names is the store’s root
— mentra lays agents/, rules.json and runs.jsonl inside it — and the
root is bound to the store in exactly one place, store_in, because two
places would eventually disagree and a conversation written under one root
and looked for under another is simply missing.
§When the directory holds a database instead
basis 0.6 and earlier kept conversations in runtime.sqlite under the
same directory. This build links no SQLite and does not migrate
(ADR-0023’s E2 precedent): opening, listing or deleting against a
directory that holds one is refused with RunError::LegacyStore, which
names the two ways forward, rather than starting an empty file store
beside data it cannot see — the refusal is refuse_legacy_store, and
every path that opens a store the caller pointed somewhere runs it.
§When there is no file
RuntimeBuilder::with_ephemeral_history
answers where with nowhere, and opens mentra’s in-memory store instead.
Nothing in this module can see one of those conversations: there is no file
for list_in to read and no row for list to filter, whichever
directory either is pointed at. One file still gets written even then — a
compaction snapshot, which mentra persists without consulting the store —
and it goes to a per-runtime directory under the OS temp directory, which
is as close to nowhere as that file gets. Everything else below is about
the durable case.
Structs§
- Persisted
Session - A conversation mentra has on disk, as basis reports it.
Functions§
- default_
directory - The directory mentra keeps basis’s conversations in, for a caller that wants to say where the history lives.
- forget
- Removes a conversation from the default directory’s store for good.
- forget_
in - The same, for conversations kept somewhere of the caller’s choosing.
- list
- Every conversation persisted for
workspace, most recently used first. - list_in
- The same, for conversations kept somewhere of the caller’s choosing.
- runtime_
identifier - The runtime identifier for conversations in
workspace.