Global memory
An agent-written memory library the agents can browse, search and add to — modelled on Omniscio's own docs system but live: a budget-aware tree a session can load at the size it wants, memories an agent writes from its own work, and comprehension search.
What it is
A progressive-disclosure, agent-written memory library built on the Context Dock native store —
modeled on Omniscio's own docs system (CLAUDE.md → MEMORY.md → topic files), but live and
agent-facing. Milestone 1 is the read path (browse a budget-aware tree + load any memory at the
size you choose); Milestone 2 is the write path — agents remember from their own work; and
Milestone 3 adds search — find memories by meaning + keyword. It is gated off by default
(globalMemoryEnabled / the global-memory unreleased feature). The engine is agent-facing (CLI
routes + first-message injection); a user-facing Memory panel under Agent Tools (desktop) is the
one place to see and edit what the agents remember — see The Memory panel below.
The idea
- A memory is just a Context Dock store doc — it already has three compression levels (full / medium / short = original / keyPoints / summary), a per-level token count, a one-line summary, and tags. Nothing new is stored.
- Memories are organized into a tree: fixed top branches (About You / This Project / People) → tag-groups → memories. Every node shows how many memories it holds and what they'd cost to load at each level, so the agent only drills in when it's worth the tokens.
- Groups are keyed on a standard set of 17 tags (merge, testing, git, cloud, bug-fix, …) rather than whatever tag the model happened to invent — see How the tree groups.
- A tiny always-loaded root map is injected into each session's first message (when on), so the agent knows memory exists and how to reach it — capped (~6k tokens), and nothing at all when off.
Where to find it
The Memory panel (see + edit your memories)
When the system is on, a Memory item appears in the left sidebar under Agent Tools (desktop). It's the one place you can look at everything the agents have remembered and curate it:
- A searchable list of your memories on the left; click one to read it at any level (full / key points / summary) on the right.
- Edit a memory's tags or delete one you don't want kept — it reuses the ContextDock Library's detail pane, so it's familiar and consistent.
- It shows ONLY the agent-written memories (rows minted with a
mem_id), never your imported course/curriculum docs — those stay in the ContextDock Library.
It reuses the ContextDock-native store under the hood (no new data, no new backend — just a widened
access gate so the panel works with only Global Memory on), and it's desktop-only for now (the
local store isn't wired to the mobile bridge yet). It's part of the same global-memory feature, so
it stays hidden until you turn Global Memory on.
How it behaves
How the tree groups
Memories file under a standard tag, not the freeform one the model wrote. The distiller invents its own tags per session, so grouping on those directly shattered the library — on the real store (1,495 memories, 2,390 different tags) there were 713 top-level folders, and 522 of them held a single memory. A library where every memory is its own folder is one you can't browse.
So every tag is folded onto a closed list of 17 standard tags — merge, testing, git, cloud,
bug-fix, infrastructure, build, automation, process, tech-debt, security, ui, docs,
data, performance, i18n, preferences — plus a general catch-all. ready-to-merge,
auto-lander, rebase and merge-conflict all land in merge; verification, typecheck and
guard-lane all land in testing. On the same library that turns 713 folders into 18 — with no
single-memory folders left at all — and 95% of memories land somewhere meaningful.
Two things worth knowing:
- Nothing is rewritten. The fold happens when the tree is drawn, so your memories keep their specific tags for reading and searching — the standard tag is only the filing key. No migration, and the existing library regroups instantly.
- Both levels fold. When a group gets too big it splits by a second tag — folding that one too is what stops the mess from just moving down a level (734 single-memory subfolders became 25).
- A tag about two things files under the first one.
cloud-testingis both cloud and testing; it files under cloud because the leading word is what it's mainly about.
The distiller is also shown the standard list so new memories arrive already using it, but that's a nicety: the fold happens on read either way, so a model that ignores the list still files correctly.
What an agent sees + does
Five routes on the local control server (http://127.0.0.1:19519, bearer $AMC_CLI_TOKEN):
GET /memory/map— the branch map: each branch with its memory count + short/medium/full token cost.GET /memory/open?node=<node>— drill in.about-youlists that branch's tag-groups;about-you/authlists the memories in a group (each with id, one-line summary, tags, cost).GET /memory/load?id=<id>&level=short|medium|full— return one memory's body at the chosen level (falls back to an available level if the requested one wasn't generated; budget-capped via the Context Dock assembler).POST /memory/remember(M2) — write a memory mid-work; it's scoped (About You / this project / People), deduped against what's already stored, and linked to the current session.GET /memory/search?q=<query>(M3) — find memories by meaning AND keyword (hybrid); returns ranked hits with their ids to then load.
These were MCP tools until 2026-09-02 (memory_map, memory_open, memory_load,
memory_remember, memory_search), served by a pure-Node subprocess spawned once per session.
That subprocess is retired: the routes run in the main process, which already owns the database, so
the whole per-session process disappeared and nothing about the behaviour changed. If you are
hunting for a memory MCP tool, it no longer exists — call the route.
The root map is also injected as text at the top of the first message, so an agent that never calls a route still knows the library is there — and that injected text names the exact command.
How auto-write works (Milestone 2)
- The agent that did the work writes the memory. At the end of its work, every session is asked — quietly, in its system prompt — whether anything about you is worth keeping. It asks exactly two things: did you reveal something about yourself, and did you correct it. It is explicitly forbidden from asking what the session accomplished, because in a fleet where agents finish tasks all day that question produces a changelog, not a memory. The default is to save nothing, and that is the normal outcome.
- Why it moved off a separate summarizer. The old session-end distiller only ever saw a truncated transcript, so it could not tell whether a fact was already written down in the repo's own docs — and 75% of the library ended up as project notes duplicating git-tracked documentation. The agent that did the work can answer that, because it knows what it just wrote.
- The bar it applies. Save what stops you having to correct or remind it again; nothing that will be stale in a week; facts, never instructions to itself ("you prefer short replies" ✓, "always reply briefly" ✗ — an instruction gets re-read as an order later); and knowledge about the codebase goes in the repository's own docs, never here.
- Session-end distiller (legacy, still available). The original background pass that reads a
finished session's transcript and extracts memories with a separate model. Set
memoryDistillerModeltooffto disable it, or to any model to run it alongside the capture above. - Configurable model (A/B testing). Which model the distiller runs on is a flippable setting,
memoryDistillerModel— Haiku (default), Sonnet, Opus, DeepSeek V4 Flash, GPT-5.6 Luna, oroff— set over the CLI (no UI toggle). The Claude tiers run on your API-key account; the DeepSeek/Luna tiers route through the app's bundled OpenRouter lane, so they need no extra key. It reuses the shared cross-provider utility path (llmProviderService.chat), so the daily cost cap covers every model. - A mid-work
POST /memory/rememberlets an agent record something the moment it matters, without waiting for session end. - Scope. Each memory is
about-you(cross-project facts about the user),people, orproject:<id>. A project memory only surfaces under that project; About You / People are global. - Dedup. A write matches an existing memory by content-hash or normalized title within the same scope and updates it instead of duplicating (meaning-based dedup arrives with M3's embeddings).
- Safe by construction. The distiller is injection-fenced (the transcript is treated as pure
data — instructions inside it are ignored — and the output is strict, validated JSON), so a memory
can never smuggle a prompt-injection into future sessions. It is cost-capped (a per-account
daily USD limit, default $1,
memoryDistillerDailyCapUsd) and no-ops quietly when the feature is off, the chosen model's provider has no credential (the Claude tiers need your API-key account; the DeepSeek/Luna tiers use the bundled key so they need none), the cap is hit, or the session is too short.
Milestone 1 model (deliberately simple)
- No persistent node table. The tree is a fixed scope scaffold (a code constant) + tag-derived groups computed on read. Deep, auto-splitting "index of indexes" arrives with the janitor (M4), which is the first thing that actually needs a stored node tree.
- Migrated library = About You. Existing store docs (pre-M2) read as About You (the user's personal library); untagged docs fall under an Imported library group. New memories carry the scope the writer assigned.
Milestone 4 — janitor + nesting (built)
A daily background janitor tends the library, and it owns the persistent nested index
(memory_index_nodes) — the "index of indexes" branches that auto-split when they grow. It stays
gated off with the rest of the feature, and it NEVER deletes: everything it tidies away is
archived (a distinct, fully recoverable state — deleting is a user-only action). An archived memory
drops out of the agent's active browse and GET /memory/load (and reappears if restored), so the tidying
is actually visible instead of silent.
Each run does four passes, cheapest-first:
- NEST (pure, free): rebuild the nested tree — group by tag, split any group over ~20 memories or ~8k tokens into sub-tag subgroups (max three levels), stamp each memory's leaf. Deterministic, so the tree never churns between runs; it's a rebuildable projection, never a second source of truth (drop it and the on-read scaffold still shows every memory).
- DEDUP (mechanical, free): collapse exact/title duplicates — keep the richest survivor, union its tags, keep the earliest provenance. A loser whose unique text isn't already on the survivor is co-filed (left live), never hidden.
- PRUNE (mechanical, free): archive empty-body memories only.
- AI lane (opt-in, ~$1/day cap): a cheap model (Haiku) does meaning-based near-duplicate matching (candidates from cosine neighbours over the M3 embeddings — read defensively, degrades to mechanical when they're absent) and stale judgement. Every prompt is injection-fenced (memory content = untrusted data) and every verdict is schema-validated (a bad one is dropped). Confirmed near-dups merge + archive their losers; stale memories archive — all recoverable.
The scheduler is a daily cron (hourly tick + a once-per-day file), single-flight, feature-gated so it costs nothing while off.
How search works (Milestone 3)
- Hybrid.
GET /memory/searchruns two engines and merges them: meaning-based (an on-device embedding model,all-MiniLM-L6-v2, cosine similarity) and keyword (SQLite FTS5). A memory found by both ranks highest. - Free. Embeddings run locally — M3 makes no LLM/API call and needs no cost cap.
- Always works. Keyword search is instant (a trigger-synced FTS index, no model). If the embedding model isn't ready, search degrades to keyword-only rather than failing.
- Stays fresh. A background pass at session end embeds new or changed memories (detected by a content hash), so search keeps up as the library grows — harvested from the MemPalace engine.
Roadmap
- All milestones (M1–M4) are built: read path (browse + load), auto-write (session-end distiller
POST /memory/remember), search/RAG (hybridGET /memory/search+ on-device embeddings), and the janitor- persistent nested index.
For agents
Where it lives
- Types + scaffold:
src/shared/memory-types.ts. Pure tree:src/main/services/memory/tree.ts. - Agent routes:
src/main/services/cli/cli-server-memory-routes.ts. Root injector:src/main/services/memory/root-snapshot.ts. - Write path (M2):
src/main/services/memory/store-write.ts,distiller.ts,memory-write-scheduler.ts,memory-distiller-cost.ts; distiller model pickersrc/shared/memory-distiller-model.ts(settingmemoryDistillerModel). - Search (M3):
src/main/services/memory/memory-retrieval.ts,src/main/db/queries-contextdock-embeddings.ts,memory-embedding-scheduler.ts. - Memory panel (UI):
src/renderer/src/features/memory/(MemoryView,memory-filter.ts), manifestsrc/shared/integrations/memory.ts, widened store gatesrc/main/services/feature-flag-gates.ts:requireContextDockStoreAccess. - Contract + invariants: global-memory-system-contract.md.
Related
Flashcards is the other local, agent-adjacent store in this library; the KMS vault is where your own hand-written notes live.
Last verified 2026-09-23