Voice Memory concepts
MemoAir Voice has two model surfaces every integrator needs to internalise: the identity model (account → project → agent → end-user) and the four retrieval lanes (profile, working, permanent, org). Identity decides whose memory is in scope; lanes decide where a memory is recalled from.
On top of the lanes, v2 adds a cognitive kind to every stored item (semantic, episodic, preference, decision, procedural, strategy, open-loop, entity) and a set of automatic recall behaviors — time-scoped episodic recall, "what changed" history, resurfaced open loops, replayable how-tos, learned tool guidance, referent resolution, and anti-repetition. None of these require new integration code: you still call search_memory once per turn and the richer context is assembled for you.
Identity model — account, project, agent, end-user
MemoAir Voice runs four nested levels of identity. Each layer narrows scope; together they answer which voice bot, in which workspace, owned by which company, is talking to which caller right now.
account "Fundamento" — your company, one MemoAir org└── project "Acme support bot project" — proj_acme └── agent "Acme returns concierge" — agent_returns └── end-user "caller hash 0xab12…" — varies per call- Account. One MemoAir org per company. Holds billing, the account API key, the team roster. There is exactly one
memoair_pk_…key per account; it works against every project and agent in the org. - Project. A workspace inside the account — usually one per customer or one per product surface. Owns its own org index, profiles, permanent memory, and agents. Identified by a stable
proj_…ID. - Agent. A voice bot identity inside a project — usually one per use-case ("returns concierge", "triage IVR", "onboarding tutor"). Owns its prompt version, eval suite, dashboard analytics. Identified by
agent_…. - End-user. The caller on the other end of the audio link — a phone-number hash, a logged-in user_id, a session token. Per call, not per process. Owns the profile and permanent lanes; routed via per-call
user={id,name,metadata}through the SDK runtime pool.
Wire format on every cloud call
The SDK attaches all four identity layers as headers when it talks to MemoAir cloud. The runtime pool fills these in for you; you only need to construct MemoAirVoiceClient with the first three and pass user per call.
Authorization: Bearer memoair_pk_<...>X-Project-Id: proj_<...>X-Agent-Id: agent_<...>X-User-Id: <your end-user id>Stable identity, preferences, constraints.
A compact, versioned snapshot of who the user is. Always rendered first when non-empty. Beats any conflicting permanent statement on the same canonical attribute.
What was just said this call.
The current call's running summary. Maintained on disk per session and surfaced before permanent and org hits so the model always has tight short-term context.
User-scoped facts that survive across sessions.
Extracted from prior calls and synced down to the runtime as a per-user .mv2 projection. Recalled by vector similarity against the live query.
Shared knowledge across the workspace.
Documents, FAQs, policies, runbooks — anything ingested at the workspace level. The same projection format as permanent, but scoped to the whole workspace rather than one user.
Memory kinds (v2) — what gets stored
The four lanes are where a memory is recalled from. Orthogonal to that, every stored item carries a kind that governs how it is written, deduped, recalled, and shown. Kinds are extracted automatically at session end — you don't tag them.
semanticDurable facts about the user (incl. decision / constraint). Superseded over time; the latest value wins via the currency filter.preferenceHow the user likes things — tone, format, channel. Surfaced as profile + permanent guidance.decisionChoices the user committed to ('chose Deepgram over Whisper'). Carried into the next session's preamble.episodicImmutable 'what happened' events with an occurred_at time. Never superseded or currency-dropped; powers 'what did I do last month'.proceduralReplayable how-tos ('the way I reset the router is…') surfaced step-by-step on how-to intent.strategyLearned corrections to how the agent should use a tool ('filter by year before searching'). Surfaced as guidance before the next similar action.open_loopUnresolved threads ('revisit financing next week') that resurface every session until resolved.entity / objectNamed things the agent showed — cars, people, companies — resolvable by referent ('the red one').Cloud is canonical for kinds; each kind is mined once at session end and synced down version-gated, so a user's phone and laptop sessions converge automatically.
Automatic recall behaviors (v2)
These run on the hot path inside a single search_memory call — no extra tools, no LLM hop, all deterministic and flag-gated (default on). Set the matching MEMOAIR_RUNTIME_* env var to false to disable any one.
- •Episodic time-scoped recall. "what did I do last month?" pulls events by
occurred_at. - •Open-loop resurface. Unresolved threads lead the continuity block every session until closed (exempt from anti-repetition).
- •Procedure + strategy surfacing. A how-to question surfaces the matching replayable steps; active tool-use corrections are injected as a compact "learned tool guidance" block.
- •Referent resolution + attribute expansion. "the red one" resolves to a concrete object; "what about the blue one — pricing?" expands the embed to the object's attributes so the right facts are retrieved.
- •Anti-repetition ledger. A card injected in the last few turns isn't re-injected, so context stays fresh.
The dashboard surfaces the cold side: a "what changed" changelog (built from the supersession DAG), a session digest per call, and a kind badge on every permanent fact.
Trust hierarchy
When the same statement appears across lanes the composer keeps the highest-trust version and discards duplicates:
That means a freshly captured profile attribute will override a stale permanent fact about the same attribute, and the working brief will always rank above static facts inside the same call.
Where the writes happen
- Per turn: the SDK calls
after_turn(...); the runtime appends to a session JSONL and to an outbox that streams back to MemoAir cloud. - Per session end: the runtime flushes the working brief and pushes the outbox before returning from
end_session. - Cloud-side, asynchronously: one consolidation pass distills the session into typed memories — facts (with kind), an episode, open loops, procedures, and strategies — guarded by two rails (every fact must cite a real transcript turn; anything that trips the secret scanner is dropped). It dedups (BLAKE3 + SimHash), records supersession edges for the changelog, then rebuilds the user’s
.mv2projection with the kind tags. - Next call start: bootstrap pulls the new manifest version and applies the delta (or downloads a fresh snapshot) before
start_sessionreturns.
Identity binding (runtime layer)
A voice-runtime process is single-tenant by design — each process pins one (project_id, user.id) at boot and refuses mismatched calls with runtime.identity_mismatch. That isolation guarantee is enforced at the runtime, not the SDK.
The SDK delivers concurrent users on top of that single-tenant contract by managing a pool of runtimes inside MemoAirVoiceClient: one runtime per (project_id, user.id), bounded LRU, per-call routing through user={...}. See the RuntimePool section in the SDK reference for capacity tuning.