Canonical language
Why stored memory content is canonical English, and how other languages are handled on write, read, and in the dashboard.
Every memory in zero-memory is stored in canonical English, regardless of the language the fact was originally expressed in. The original phrasing is never lost: a translated memory keeps its pre-translation text and language alongside the canonical content, and a writer can additionally record the exact original wording in the memory's provenance.
Why one canonical language
Memory is written once and read many times. Every session briefing and every
recall re-reads the store, so keeping content in the language of origin has
compounding costs.
Duplicates are the first. Deduplication is language-sensitive: the same fact stored once in English and once in another language produces different embeddings and never collapses into one memory, and each duplicate pair becomes work for the hygiene pipeline. Reads get more expensive too. Non-English text often tokenizes into significantly more tokens than the equivalent English, roughly 1.5–2.5x for some languages, and that surcharge is paid on every briefing and every recall rather than once. Retrieval also comes out uneven. The embedding model is English-specialized — the stronger choice precisely because stored content is canonical English — so text left in another language is matched weakly, and the full-text leg of hybrid search cannot match across languages at all. Shared scopes settle it: teams are often mixed-language, and a shared scope has to be readable by everyone in it.
The economics point one way. Translation on the write path is nearly free, because extraction already makes an LLM call per chunk, so emitting canonical English is just an instruction. Translating on the read path would invert the write-once/read-many economics and add latency to session start. It would also cost the property that makes a search auditable: once the server may rewrite a query, what a caller asked for and what actually ran are two different strings, and every surface that reports a search has to keep both in step.
Translating on write, never on read
Write path
The extractor behind the watcher and ingest emits memory content in English, keeping code, identifiers, file paths, and quoted domain terms verbatim. Any non-English content that still reaches the store goes through the same canonicalization as any other write (below), so the original text is preserved next to the English rendering.
For an explicit remember, the tool contract asks the calling LLM to author
content in English. This costs nothing extra: the caller is already an LLM with
the full conversation in view, so its own English rendering usually preserves
intent better than a post-hoc server translation. Non-English content is still
accepted, never rejected. The write is stored immediately, and a background
pass canonicalizes the content to English right after it. The original text and
its detected language stay on the memory (content_original), and an async
worker retries any failed pass. When the memory captures someone's exact
words — a preference or decision phrased just so — the caller can also pass
verbatim: the operative phrase as originally said, in any language, preserved
in provenance next to the canonical English content.
Read path
The read path does not translate at all. A query is embedded and matched exactly as it arrived, so the string a caller sent, the string the search ran on, and the string the activity log shows are the same string. They cannot drift apart, because there is only one of them.
For agent recall and build_context, the tool contract asks for English
queries, and the calling LLM renders the user's intent for free: it holds the
whole conversation, so it translates better than the server could, and it
costs nothing extra. This is a discipline the contract states, not a
mechanism the server enforces. An agent that sends its user's language
verbatim will match poorly against an English corpus, and that is visible in
the activity log rather than silently patched.
A person typing in their own language gets what they typed.
Dashboard search is the same recall call with no
translation gate: the query runs through the usual hybrid vector + full-text
search as written. Searching in a language the corpus is not written in returns
little, and the remedy is to rephrase, which the searcher can see the need for,
rather than to have the server guess at the intent behind the words.
Dashboard display
The dashboard follows the browser's language for its own labels. Independently
of that, every memory card can reveal its original-language source: the
pre-translation text with its language for a memory that was canonicalized, or
the writer-supplied verbatim phrase when one was recorded. The canonical
English content stays the primary rendering; the original is one disclosure
away, so nothing is flattened irreversibly.

The contract, the fallback, and what was rejected
The server is deterministic where the contract is probabilistic. The "author in English" instruction lives in the tool contract, which an LLM follows most but not all of the time. The server therefore keeps a cheap deterministic fallback: language detection on input, canonicalization by a write-triggered background pass. This server-plus-contract pattern recurs across zero-memory. The contract makes the common path free; the server makes the invariant hold.
Dual storage, one record per language, was rejected: it doubles embeddings and storage, and complicates dedup and hygiene for no read-side gain. Translate-on-read was rejected too, since it means an LLM call on every recall and every briefing, in the critical path of session start.
Nuance loss is bounded. A translation can flatten domain phrasing, so the original is always preserved and auditable, and code, identifiers, and quoted domain terms are kept verbatim.