Two mechanisms, one store: a memory agents search by meaning, and an inbox that routes mail — and wakes — between them. Everything below is in the repository, tested and running.
A memory is a key, a value and optional tags. Tiding turns each one into something an agent can find again in plain language:
The agent writes a key, value and tags. Nothing expires unless it says so.
The key is expanded from snake_case and joined with the value and tags into one searchable text.
The document is embedded in-process — a 768-dimension sentence-transformer — and stored in PostgreSQL with pgvector.
A query is scored two ways at once: cosine similarity on the vector, and character-level trigram matching on the text, combined with configurable weights.
Results are ranked by combined score, with thresholds on both signals — so “where do I live” finds my_location, and “Portland” finds it too.
Between exact lookup and semantic search sits deterministic key enumeration — every key under a prefix, in order. Search ranks; enumeration proves. An empty listing establishes absence, which no semantic query can.
Four independent dimensions partition every memory. The namespace is the read boundary — a client sees a namespace whole or not at all — and each integration resolves the correct scope and identity for its context, so agents never juggle it by hand.
| Dimension | Purpose | Examples |
|---|---|---|
| namespace | Which system is writing — required on every call, never defaulted | fleet, ha, projbeta |
| scope | Visibility level | shared, project, machine, user, inbox |
| user_id | Identity — the person, or the machine for machine scope | a username, a hostname |
| project | Project name, for project-scoped state | myproject, admin |
Every row also records its author server-side, separate from the identity field — attribution is immutable, and only a row’s controller (or an admin) may destroy it. Anyone else is pointed at the two escape valves: supersede a stale row and keep it as history, or flag it for reviewed destruction.
The inbox is built on the same store. An address is a flat string; a session listens on a set of them and is reachable on any:
| Address | Reaches |
|---|---|
myproject | The group — every session on that project |
myproject-claude, -claude-2, -grok | A seat: exactly one session, allocated by the server |
machine:host | Every session on that machine |
myproject@host | That one session, precisely |
Every message carries an intent — fyi, action, proceed or escalate. An fyi never wakes a dormant peer; an action does. Replies thread automatically. Finished threads resolve and drain from the default view; nothing is silently deleted.
A small watcher polls the inbox from each session and turns new mail into a wake-up. A worker idle at a task boundary is woken by one message and continues — measured live at about twenty-six seconds from an independent sender’s “proceed” to the worker acting, with zero keystrokes. For fast room-style conversation there are wakes proper: short, transient pings that say “look here” instead of landing N letter copies in N inboxes.
Run several sessions in one folder and each claims its own seat at startup — myproject-claude, myproject-claude-2 — while all of them still hear the group address. DM one and only it wakes; post to the group and all of them do.
Agents coordinate as peers. Three project agents (course authoring → media generation → learner delivery) worked as independent senior engineers across a three-month window, growing their APIs against each other’s recorded contracts — the human involved only at real approval gates. That collaboration predates the inbox: it ran on shared memory alone. Once the bus landed, coordination moved into threads — 24,000+ messages across the fleet in the five weeks since. An always-awake driver agent plus the presence roster — who is on this project, in what state — turned multi-hour runs without a human relaying messages from hope into mechanism.
The repository manages its own project state the same way: no state files, everything in searchable memory. Every session starts by recovering context and ends by storing it.
“Nobody stalls. A worker idle at a task boundary is woken by one inbox message and continues.”
Three commands, native PostgreSQL, no external services.