MrCogito · agent memory · simplified design · 17 October 2026
Superseded on 18 Oct 2026; kept as background. docs/requirements.md is the current design. Changes since this page:
  • Protocol. Keys are always static. The unit is one message. The query only retrieves (no named questions, no latest/timeline modes, no trimming inside a message). The two times are now valid_at and created_at, with as_of as the viewpoint.
  • Who it is for. People's own chats with Claude, Codex and ChatGPT: getting back "that idea from a week ago" that nobody wrote down.
  • MCP and setup. MCP ships in v0. Setup is one command (for example uvx cogito-mem install claude-code), and automatic capture uses Claude Code and Codex hooks. The MVP runs locally for the author alone; access from claude.ai and ChatGPT on the web is postponed.
  • No race on numbers. The benchmarks remain internal checks on quality and speed, not a leaderboard target. The "Done when" lines in the plan below are replaced by the acceptance tests in requirements §3.5.
  • Competition. Claude's and ChatGPT's built-in memory, claude-mem, OpenMemory and Basic Memory. See section J of docs/literature/agent_memory_systems.md.
The measured speed numbers below still hold (PyTorch on Polonez, 17 Oct).

Memory Tape v0

Two operations and off-the-shelf parts, with no training to start. Messages go onto an append-only tape, an open embedding model indexes them, and any LLM queries in plain JSON with a question and a time.

EmbedEmbeddingGemma 2, cut to 256 numbers

Google's open embedding model (Apache 2.0, released 6 October 2026; 270M parameters for text, inputs up to 8k tokens). Its first 256 of 768 numbers work on their own: 3× smaller for under one benchmark point, no quantization.

StoreOne SQLite file + one vector file

The tape holds exact text and both times, never updated in place. A plain array of vectors sits beside it, one row per chunk.

SearchVector + keyword, filtered by time

Exact vector search plus keyword search (catches names, numbers and ids), merged by rank. No search server.

Protocolappend and query

Corrections, retractions and later offline summaries are all appends. Queries are named questions with an optional time, answered by name, like Jev.

Interactive architecture

How the pieces connect

Pick an operation to trace its path. Pick a version to see what switches on as we add small pieces of training and, later, the offline librarian. Click any box for the chosen component and why.

Operation
Version
on the selected pathpart of this versionnot in this version yet
Memory Tape v0: callers, gateway, write path (chunk, embed, shrink), stores (tape, keyword index, vectors), read path (embed question, hybrid search with time filter, optional rerank, trim), and an offline librarian that appends derived notes. WRITE PATH STORES · APPEND-ONLY READ PATH OFFLINE · LATER Any LLM,agent or peerHTTP · MCP Gatewayappendquery Chunker≤256 tokens + header EmbedderEmbeddingGemma 2 Shrinker768 → 256 numbers TapeSQLiteexact texttwo clocks Keyword indexSQLite FTS5 · BM25 Vectors16-bit array Hybrid search + time filterexact vector · BM25 · rank fusion · as_of Embed questionsame model Rerankeroptional · GPU Trim to budgetbest sentences Offline librarianscheduled · reads the tape · appends summaries, links, "replaced by" notes

    Chosen components · click a card to find it in the diagram

    Chosen components

    Protocol · two operations

    append and query

    One HTTP endpoint per operation, plus the same two as MCP tools so agents can call them directly. Requests and answers are JSON. Like Jev, a query carries named questions and gets answers keyed by those names; each question may carry its own time.

    append

    • Send a whole chat or any part of it. Each event's id is unique per thread, so resending a chat adds only the new messages.
    • t_event is when it was said; the memory stamps t_ingest when it arrives. Nothing is ever edited or deleted.
    • kind covers everything else: message, document, retract (hides the events it names), note (offline summaries and links, citing their sources).
    • The reply comes after the tape write (milliseconds). Embedding follows in a background batch, so new text is searchable by keyword at once and by meaning about a second later.
    {
      "op": "append",
      "memory": "team-alpha",
      "source": "agent:planner",
      "thread": "launch-q4",
      "events": [
        { "id": "msg-8812", "role": "user",
          "t_event": "2026-10-15T09:12:00Z",
          "text": "Launch moves to 14 November. Bob takes over the checklist." },
        { "id": "fix-0031", "kind": "retract", "refs": ["msg-7710"],
          "text": "Ad budget figure was entered by mistake." }
      ]
    }
    { "ok": true, "added": 2, "skipped": 0,
      "seq": [1849220, 1849221], "chunks": 1, "ms": 4 }

    query

    • Named questions in plain text. Each takes an optional time: as_of answers with what the memory knew then; since / until limit when things happened.
    • mode: relevant (best matches, each dated), latest (among matches that say the same thing, the newest first, older ones attached), timeline (matches in time order; with since_seq it streams raw events, which is how another memory syncs).
    • Answers come back keyed by question name: exact text, times, source and a score. With the reranker on, the score is the probability that the item answers the question.
    • A budget caps time and tokens out; the trimmer keeps the sentences that best match.
    {
      "op": "query",
      "memory": "team-alpha",
      "filters": { "thread": "launch-q4" },
      "questions": {
        "launch_date": { "text": "When is the Q4 launch?", "mode": "latest" },
        "owner_then":  { "text": "Who owns the launch checklist?",
                         "as_of": "2026-10-01T00:00:00Z" },
        "risks":       { "text": "known launch risks", "since": "2026-10-01", "k": 5 }
      },
      "budget": { "max_ms": 100, "max_tokens_out": 800 }
    }
    {
      "launch_date": { "items": [ {
          "text": "Launch moves to 14 November. Bob takes over the checklist.",
          "t_event": "2026-10-15T09:12:00Z", "source": "agent:planner",
          "seq": 1849220, "score": 0.83,
          "older": [ { "seq": 1712004, "t_event": "2026-09-12T10:00:00Z",
                       "text": "Q4 launch planned for 7 November." } ] } ] },
      "owner_then": { "items": [ { "text": "Alice owns the launch checklist.",
          "t_event": "2026-09-20T15:30:00Z", "score": 0.79 } ] },
      "risks": { "items": [] },
      "usage": { "ms": 38, "candidates": 200, "tokens_out": 96 }
    }
    Time, explained · example thread

    What a query sees at a given time

    Five events in one thread. Move the "as of" date to ask the memory what it knew then, and switch the mode. Nothing on the tape changes; only the view does.

      Mode

      In v0, "says the same thing" means the matches' vectors are very close (and both mention the same subject). The offline librarian later replaces this guess with explicit "replaced by" notes.

      Compression and speed

      How small, how fast

      Compression comes in rungs. Each one is optional, none is quantization, and the tape always keeps the exact text, so every rung can be redone later.

      full vector1,536 B

      768 numbers × 2 bytes per chunk, as the model outputs them.

      v0 · cut to 256512 B

      Keep the first 256 numbers (the model is trained for this). About one byte per token of text at 160-token chunks.

      v0 option · cut to 128256 B

      Smaller again, at a larger retrieval cost; worth it only if memory size binds.

      v1 · learned 64128 B

      A small linear map trained on our data to keep the ranking, applied to stored vectors without re-embedding.

      sent per answer≈ 2–4 KB

      The trimmer returns only matching sentences: the compression that matters to the reading LLM.

      Memory size1M tokens10M tokens100M tokens
      Chunks (about 160 tokens each)estimate6.3k63k625k
      Tape text (4.2 bytes per token)measured4.2 MB42 MB420 MB
      Vectors at 256 numbers, 16-bitestimate3.2 MB32 MB320 MB
      Keyword index (roughly the text size)estimate≈ 4 MB≈ 40 MB≈ 400 MB
      Exact vector search, one question, 8 CPU threadsmeasured at 100M, scaled below< 1 ms≈ 6 ms61 ms
      Exact vector search on one GPUmeasured at 100M< 0.1 ms< 0.1 ms0.45 ms
      Embedding one questionmeasured59 ms on 8 CPU threads · 30 ms on one GPU
      Whole query, CPU, no rerankersum of the above≈ 60 ms≈ 65 ms≈ 120 ms: over budget
      Bulk ingest, one RTX 3090measured at 1M, scaled14 s≈ 2.4 min≈ 24 min
      Bulk ingest, 8 CPU threadsmeasured rate, scaled≈ 11 min≈ 1.9 h≈ 18 h
      Our page writer, for comparison, one RTX 3090measured0.44 s≈ 4 s≈ 44 s
      Three numbers to remember, measured on Polonez on 17 October with the same 1M-token document for both models. First, EmbeddingGemma 2 ingests 33× slower than our page writer on a GPU (14.3 s against 0.44 s) and 14× slower on 8 CPU threads. Second, at 100M tokens a CPU-only query takes about 120 ms (59 ms to embed the question, 61 ms of exact search), so it misses the 100 ms budget; below about 60M tokens it fits, and above that the fix is a graph index at full precision (still no quantization) or a GPU. Third, at 256 numbers per 160-token chunk the vectors are about the size of the text; our own latent notes are about ten times the text, so off-the-shelf chunk vectors are the more compact index.
      Plan · each version is usable on its own

      Build order

      No training

      • Tape, chunker, EmbeddingGemma cut to 256, exact vector search, keyword search, rank fusion, time filters.
      • HTTP service with append and query; the same two as MCP tools.
      • Exams: our world documents from 4k to 1M tokens with time questions, and LongMemEval (about 1.5M tokens of chat).
      Done when: a query answers in under 100 ms on CPU at 10M tokens, and far lookups in our world documents pass.

      Small training

      • A query adapter: one small linear map on question vectors, trained on question–evidence pairs. Stored vectors never change, so nothing is re-indexed.
      • Optional: a learned 256 → 64 map for 4× smaller storage, trained to keep the ranking.
      • Optional reranker on GPU, giving each answer a probability.
      Done when: each piece beats v0 on the same exams; otherwise it is dropped.

      Offline librarian

      • A scheduled job reads the tape since its last run and appends notes: summaries, links between facts, "replaced by" markers.
      • Starts as a prompted LLM; later our trained models (the E33 reader, the E36 librarian) take over.
      • It uses the same append operation, so the protocol never grows.
      Done when: "latest" and two-step questions improve with no change to the query path.
      What this gives up compared with the full design: multi-step lookups happen in the calling LLM, which simply asks again; "latest" is a similarity guess until the librarian writes its markers; and the embedding model ingests 33× slower than our own page writer on a GPU (measured: 14.3 s against 0.44 s for the same 1M-token document). None of these blocks a first working service, and each is a later slot for our research models.