Skip to content

About

Event-driven long-horizon memory for real-time wearable AI and LLM agents.

Resources

Stars

30 stars

Watchers

0 watching

Forks

Latest commit

 

History

544 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cortext

Release License npm

Local multimodal memory for AI apps, agents, devices, and humans.

Cortext is a C++20 memory engine that ingests text, audio, and image signals, stores durable memory traces in SQLite, and returns a small context packet of relevant memories on later calls. It is designed for long-running assistants and realtime applications that need memory without sending an entire history window back to an LLM.

This repository is the C++ engine (augmem/cortext.cpp). Language packages live in sibling repos.

Links: Releases / Python / TypeScript / Go / Dart / WASM / Paper / Roadmap

Recent Changes

  • Unreleased (engine v5): per-call evidence budget on every process call, standing_memory returned outside the retrieved ranking, "mixed" memories that return every signal's payload with its own mimetype and timestamp, text with caller media, and query reference time (reference_ms / as_of). cortext_config.procedural_enabled and the Soft Anchor fields are removed (C struct layout change), whole memories are returned unless a budget is set, and the late-interaction reranker AIST-87M-LI.gguf is required with the AIST model.
  • v1.2.0: Retention is now Natural (default), Durable, Boundary, and Ephemeral. Omitted retention is Natural: force_boundary=false and force_write=false so episode algorithms decide. Durable keeps force boundary + force write (explicit chat turns). Boundary forces the turn edge only. Ephemeral remains no-store query. Exposed on C++ Process*, C cortext_process_json_options.retention, and Python/JS/Go bindings.
  • v1.1.10: public Retention::Ephemeral calls force a retrieval boundary without writing the query, so CLI/package recall works as documented.
  • v1.1.9: package examples use real OpenAI Chat Completions message arrays.
  • v1.1.8: PyPI and npm packages ship cross-platform native libraries/addons and download the verified AIST q8_0 model into a user cache on first use.
  • Public retention policies: Natural (default/episode), Durable (force turn store), Boundary (force edge), Ephemeral (query without store).
  • Command-line tools are built with CORTEXT_BUILD_TOOLS=ON; examples remain behind CORTEXT_BUILD_EXAMPLES=ON.

Quick Start

Choose the surface you need.

Python (cortext.py — GitHub Releases wheelhouse, not bare default PyPI):

export CORTEXT_VERSION=1.2.4
pip install "cortext==${CORTEXT_VERSION}" \
  --find-links "https://github.com/augmem/cortext.py/releases/download/v${CORTEXT_VERSION}/index.html"

Node.js / TypeScript (cortext.ts):

npm install @augmem/cortext

C++ from this engine repo:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build -R cortext_tests --output-on-failure

Build the CLI:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCORTEXT_BUILD_TOOLS=ON
cmake --build build -j --target cortext_cli
./build/tools/cli/cortext_cli --help

Default CMake/Zig builds link Git AIST shards into libcortext and assemble the full GGUF on first load (no LFS, no runtime download, no CORTEXT_AIST_MODEL_PATH). Refresh tracked shards with:

python3 scripts/shard_model.py

Try It

cortext_cli is a small public-API smoke test and a useful local memory tool. It writes durable memories to a SQLite file and supports ephemeral recall.

./build/tools/cli/cortext_cli --db bailey.db remember \
  "Bailey is allergic to bee stings and needs Benadryl within 10 minutes."

./build/tools/cli/cortext_cli --db bailey.db remember \
  "The vet appointment for Bailey is on July 12 at 9am with Dr. Okafor."

./build/tools/cli/cortext_cli --db bailey.db recall \
  "what should the vet know about the dog?"

Corrections supersede stale facts:

./build/tools/cli/cortext_cli --db bailey.db remember \
  "Correction: the vet appointment was moved to July 14 at 2pm."

./build/tools/cli/cortext_cli --db bailey.db recall \
  "when is the vet appointment?" --top 1

Other CLI commands:

./build/tools/cli/cortext_cli --db memory.db repl
./build/tools/cli/cortext_cli --db memory.db remember - < facts.txt
./build/tools/cli/cortext_cli --db memory.db consolidate

# Store a photo with its caption: the caption is embedded, the photo stored.
./build/tools/cli/cortext_cli --db memory.db --media bailey.jpg \
  --mime image/jpeg remember "Bailey at the beach"

# Fit recalled evidence in 2,000 bytes; recall as of a past moment.
./build/tools/cli/cortext_cli --db memory.db --evidence-budget 2000 \
  recall "what does Bailey like?"
./build/tools/cli/cortext_cli --db memory.db --reference-ms 1783463360158 \
  --as-of recall "when is the vet appointment?"

recall is ephemeral by default. Pass --durable if you also want the recall query stored under the cli/recall source. --reference-ms and --as-of apply to ephemeral recall only. Non-text payloads print as [mimetype, N bytes].

Description

Cortext is built around a local feedback loop:

  • Durable memory: signal metadata in SQLite, payloads in sqlite-objstore, and vector retrieval through sqlite-vec.
  • Multimodal embeddings: text, audio, speech, and image inputs share one AIST-87M GGUF retrieval space.
  • Three control knobs: Focus (F), Sensitivity (S), and Stability (T) derive thresholds, decay, storage cadence, consolidation, and retrieval behavior.
  • Graph-native context: memories are connected by reinforcement, sequence, consolidation, and supersession edges.
  • Small native surface: C++ facade, stable C ABI, and bindings for Python, Go, JavaScript/TypeScript, Dart, and WebAssembly.
  • Research traceability: experiments, ablations, and manuscript sections live in docs/paper/ next to the implementation.

Use Cortext when an app or agent outlives its context window. Instead of resending tens of thousands of history tokens or maintaining a separate RAG pipeline, you ask Cortext for the current memory packet and inject the returned retrieval results into your model or UI.

Build

Requirements:

  • C++20 compiler
  • CMake 3.16+
  • Git and Python 3 for dependency/model bootstrap

Standard debug build:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
ctest --test-dir build -R cortext_tests --output-on-failure

Model-free CI-style gate:

./build/tests/cortext_tests '~[aist]' --reporter compact

Build examples:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCORTEXT_BUILD_EXAMPLES=ON
cmake --build build -j --target cortext_topical_chat_analysis
./build/examples/topical_chat_analysis/cortext_topical_chat_analysis --help

Build an installable CLI with Zig:

zig build -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
./zig-out/bin/cortext_cli --help

Cross-build CLI artifacts:

zig build -Dtarget=x86_64-linux-gnu -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
zig build -Dtarget=aarch64-linux-gnu -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
zig build -Dtarget=x86_64-windows-gnu -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
zig build -Dtarget=x86_64-macos -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
zig build -Dtarget=aarch64-macos -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false

Important CMake options:

  • CORTEXT_BUILD_TOOLS=ON: build command-line tools.
  • CORTEXT_BUILD_EXAMPLES=ON: build examples and benchmark demos.
  • CORTEXT_EMBED_AIST_MODEL=ON (default): link AIST shards into libcortext and assemble at load. Opt out with =OFF / -Dembed-aist-model=false.
  • CORTEXT_FETCH_AIST_MODEL=ON: only used when bootstrapping missing shards at build time (default off).
  • CORTEXT_AIST_MODEL_QUANT=q8_0: choose q8_0, q5_1, or all when fetching a full model to re-shard.
  • CORTEXT_FETCH_GGML=ON: fetch and build bundled GGML.
  • CORTEXT_USE_SYSTEM_GGML=ON: use a preinstalled GGML for packagers.
  • CORTEXT_EXPERIMENT_HOOKS=OFF: compile out eval-only ablation hooks.

C++ API

#include <cortext/cortext.hpp>

#include <iostream>
#include <string>

std::string MemoryText (const cortext::Cortext::Context::Memory &memory)
{
  std::string text;
  for (const auto &blob : memory.content)
    {
      if (!text.empty ())
        {
          text.push_back (' ');
        }
      text.append (blob.begin (), blob.end ());
    }
  return text;
}

int main ()
{
  cortext::Cortext::Config cfg;
  cfg.focus = 0.7;
  cfg.sensitivity = 0.5;
  cfg.stability = 0.8;

  auto engine = cortext::Cortext::Create (cfg, "memory.db");

  engine->ProcessText ("The garage door code is 8841.", "chat/main",
                       cortext::Retention::Durable);

  auto ctx = engine->ProcessText (
      "garage door code",
      "chat/query",
      cortext::Retention::Ephemeral);

  for (const auto &memory : ctx.retrieved_memory)
    {
      std::cout << memory.relevance << " " << MemoryText (memory) << "\n";
    }

  if (ctx.consolidation_state != cortext::ConsolidationState::None)
    {
      engine->Consolidate ();
    }

  engine->Flush ();
}

Public entrypoints:

  • C++ API: include/cortext/cortext.hpp
  • C API: include/cortext/capi.h

Core calls:

  • ProcessText, ProcessAudio, ProcessImage: process a signal (default retention is Natural — no forced turn edge; storage via write gate).
  • Media overloads (ProcessText, ProcessAudio, ProcessImage with Cortext::Media; C cortext_process_{text,audio,image}_with_media*) keep the canonical input for embedding and processing. Non-empty caller media replaces the stored payload and MIME type; empty media stores the canonical input, exactly like the calls without media. A payload is used as text (keyword index, standing memories, packet text) only when its MIME type is text/*.
  • Retention::Durable: force turn boundary + force write (chat convenience).
  • Retention::Boundary: force turn boundary only; write gate still applies.
  • Retention::Ephemeral: process and retrieve without storing the input.
  • EmbedText, EmbedAudio, EmbedImage: embedding-only calls that do not mutate memory state.
  • Consolidate: explicit shallow consolidation.
  • Flush: commit pending episode writes.
  • Reset: reset volatile processor state while keeping durable memory.

Context Packet

Processing calls return Cortext::Context in C++ and JSON through the C API and language bindings.

Top-level fields include:

  • retrieved_memory: long-term memories selected for the current signal.
  • standing_memory: standing directives for a query ("Always ...", "I prefer ..."), outside the retrieved ranking: each entry holds only the stored signals that state a directive, best first, within its own small byte budget. Place it ahead of retrieved_memory; it does not count against a cut of the retrieved list.
  • working_memory: active short-term memory slots.
  • embedding: the current signal embedding when requested.

Every process call takes an optional evidence budget in bytes (C++ Cortext::EvidenceBudget, C cortext_process_json_options.evidence_budget_bytes, bindings evidence_budget_bytes / evidenceBudgetBytes). Unset, memories come back whole. Set, standing_memory plus retrieved_memory (with attached neighbours) fit within it: text counts its UTF-8 bytes and every other payload 480 bytes, grouped memories keep their members most similar to the query (the best of each modality always), and more distinct memories are kept trimmed before lower-ranked ones are left out. A reader budgeted in tokens converts with its tokenizer's rate (8,000 tokens at 4.35 characters per token is 34,800 bytes of ASCII text).

A query (Ephemeral) call also takes an optional reference time (C++ cortext::RetrievalTime{reference_ms, as_of}, C cortext_process_json_options.has_reference_ms / reference_ms / as_of). Unset, retrieval is unchanged. Set, the long-term memories nearest that time (past or future, by distance to each memory's [start_ts, end_ts]) join the candidates through a temporal channel fused by reciprocal rank; nothing is dropped for being later. With as_of, memories that start after the reference time are hidden, as are later turns and restatements attached to retrieved ones.

  • should_interrupt, interrupt_aborted, at_boundary: realtime behavior.
  • consolidation_state: ordered None, Recommended, or Required maintenance urgency derived from the current throughput range.
  • output: scores, write decisions, operation timings, and storage ids.
  • encode_ms, process_ms, hydrate_ms, total_ms: latency breakdown.

Each memory entry includes provenance, modality, stored content, retrieval scores, and usage counts. modality is the signals' shared modality, or "mixed" when one memory holds signals of different modalities (a turn's text and its photo); a mixed memory returns every signal's payload. Each content[] entry carries its own mimetype, and the memory's mimetype is the MIME type of its first signal:

{
  "id": 1,
  "source_id": "chat/main",
  "timestamp": 1783463360158,
  "modality": "text",
  "mimetype": "text/plain",
  "content": [
    {
      "base64": "VGhlIGdhcmFnZSBkb29yIGNvZGUgaXMgODg0MS4=",
      "mimetype": "text/plain",
      "size_bytes": 29,
      "timestamp": 1783463360158
    }
  ],
  "relevance": 0.96,
  "salience": 0.0,
  "contradiction": 0.0,
  "retrieved_count": 1,
  "used_count": 0
}

For C++ callers, Memory::content is already raw bytes, with Memory::content_mimetypes in the same order. For JSON callers, decode content[].base64 according to content[].mimetype.

A retrieved memory can carry blobs from other memories (adjacent turns, the older version of a fact it superseded, the newer version that superseded it), so each blob has its own time: Memory::content_timestamps (JSON content[].timestamp) is the stored signal time in ms of each blob, in content order, 0 when unknown. The memory's timestamp is the memory's own time.

Language packages (standalone)

Language bindings are not vendored in this engine repository. Use:

Language Repo Package
Python augmem/cortext.py cortext (GitHub Releases wheelhouse)
TypeScript / Node augmem/cortext.ts npm @augmem/cortext
Go augmem/cortext.go github.com/augmem/cortext.go
Dart augmem/cortext.dart pub cortext
WebAssembly augmem/cortext.wasm npm @augmem/cortext-wasm

This repo still builds optional N-API glue under ffi/node/ (for producing cortext.node artifacts consumed by cortext.ts) and WASM via ./build-wasm.sh (Emscripten). Shared multi-arch natives and model shards ship as GitHub Release assets (cortext-assets-<version>.tar.gz).

Native C++ and CLI users should keep the model under models/ or set CORTEXT_AIST_MODEL_PATH when not using an embed-on library build.

Git-friendly AIST model shards (for language bindings)

This repo owns the AIST q8_0 GGUF shards and the shards of its late-interaction reranker (AIST-87M-LI.gguf, required with the model). Binding packages (cortext.go, plugins, …) should copy the tree — they must not re-implement sharding.

# After download_aist_model / a local full GGUF under models/:
python3 scripts/shard_model.py

Tracked layout (full .gguf stays gitignored):

models/manifest.json
models/AIST-87M-GGUF/chunks/AIST-87M_q8_0.gguf.part-*
models/AIST-87M-GGUF/chunks/AIST-87M-LI.gguf.part-*
models/mdbr-leaf-ir/vocab.txt

Optional surname/forename CSVs used by probe tools follow the same no-LFS pattern (python3 scripts/shard_surnames.py, full CSVs gitignored):

data/surnames/manifest.json
data/surnames/chunks/*.part-*
data/surnames/country_codes.csv
data/surnames/LICENSE.txt

Bindings reassemble every manifest asset on first use (verify per-chunk and whole-file SHA-256 from models/manifest.json), place AIST-87M-LI.gguf beside AIST-87M_q8_0.gguf, then set CORTEXT_AIST_MODEL_PATH (and CORTEXT_LI_MODEL_PATH when the reranker lives elsewhere). Example vendor step for a sibling checkout:

# Preserve relative paths under models/ (manifest + chunks + vocab).
mkdir -p ../cortext.go/models/AIST-87M-GGUF ../cortext.go/models/mdbr-leaf-ir
cp models/manifest.json ../cortext.go/models/
cp -R models/AIST-87M-GGUF/chunks ../cortext.go/models/AIST-87M-GGUF/
cp models/mdbr-leaf-ir/vocab.txt ../cortext.go/models/mdbr-leaf-ir/

Shared release assets (natives + model shards)

Multi-arch shared libraries and the same model shard tree are also published on each GitHub Release so thin installers can pull one tarball:

# Build locally (Zig cross-compile all six platform tags + shard model)
python3 scripts/build_release_assets.py

# Outputs:
#   dist/release-assets/native/<tag>/…
#   dist/release-assets/models/…   (chunks + vocab + manifest)
#   dist/cortext-assets-<version>.tar.gz
#   dist/cortext-assets-<version>.tar.gz.sha256

On v* tags (and published releases), .github/workflows/release-assets.yml builds that tree and uploads cortext-assets-<version>.tar.gz to the release.

Unpacked layout for binding installers (natives may already embed AIST when built with -Dembed-aist-model=true; the models/ tree is still published so thin/embed-off consumers can reassemble or vendor shards):

native/manifest.json
native/linux-x86_64/libcortext.so
native/linux-aarch64/libcortext.so
native/macos-x86_64/libcortext.dylib
native/macos-aarch64/libcortext.dylib
native/windows-x86_64/cortext.dll
native/windows-aarch64/cortext.dll
models/manifest.json
models/AIST-87M-GGUF/chunks/AIST-87M_q8_0.gguf.part-*
models/AIST-87M-GGUF/chunks/AIST-87M-LI.gguf.part-*
models/mdbr-leaf-ir/vocab.txt

Install assets for a binding package. There is no core CORTEXT_ASSETS_DIR env var — use the paths the runtime and bindings already read (CORTEXT_LIBRARY_PATH, CORTEXT_AIST_MODEL_PATH, CORTEXT_LI_MODEL_PATH):

ASSETS_VERSION=2.0.0   # or the release you want
ASSETS="$HOME/.cache/augmem/cortext/assets"
curl -fsSL -o cortext-assets.tgz \
  "https://github.com/augmem/cortext.cpp/releases/download/v${ASSETS_VERSION}/cortext-assets-${ASSETS_VERSION}.tar.gz"
mkdir -p "$ASSETS"
tar -xzf cortext-assets.tgz -C "$ASSETS"

# Shared library (pick the host platform tag):
#   linux-x86_64 | linux-aarch64 | macos-x86_64 | macos-aarch64 | windows-x86_64 | windows-aarch64
export CORTEXT_LIBRARY_PATH="$ASSETS/native/linux-x86_64/libcortext.so"

# Default release natives embed AIST (assemble-at-load). Only if you need an
# external GGUF (embed-off build, or tools that require a file path):
mkdir -p "$ASSETS/models/AIST-87M-GGUF"
cat "$ASSETS"/models/AIST-87M-GGUF/chunks/AIST-87M_q8_0.gguf.part-* \
  > "$ASSETS/models/AIST-87M-GGUF/AIST-87M_q8_0.gguf"
# The late-interaction reranker is required with the model; beside it, it
# resolves without CORTEXT_LI_MODEL_PATH.
cat "$ASSETS"/models/AIST-87M-GGUF/chunks/AIST-87M-LI.gguf.part-* \
  > "$ASSETS/models/AIST-87M-GGUF/AIST-87M-LI.gguf"
export CORTEXT_AIST_MODEL_PATH="$ASSETS/models/AIST-87M-GGUF/AIST-87M_q8_0.gguf"

Runtime Model

Cortext requires the AIST-87M GGUF encoder. Default shared-library builds behave as if the model lives inside libcortext:

  1. Git stores only under-100 MiB shards (no LFS) under models/AIST-87M-GGUF/chunks/ plus models/manifest.json and vocab. Optional name CSVs use the same pattern under data/surnames/chunks/; reassemble with python3 scripts/shard_surnames.py --assemble when needed.
  2. The build links those shards (and vocab) into the shared library via .incbin (CORTEXT_EMBED_AIST_MODEL=ON).
  3. On first engine create, MaterializeEmbeddedAistModel() assembles the full GGUF in the process cache, verifies per-shard and whole-file SHA-256, and opens that path. No network download and no CORTEXT_AIST_MODEL_PATH.

Resolution order:

  1. CORTEXT_AIST_MODEL_PATH (explicit full .gguf)
  2. On-disk search under models/ (checkout layout)
  3. Linked shards → assemble-at-load cache

Disable with -DCORTEXT_EMBED_AIST_MODEL=OFF / -Dembed-aist-model=false if you intentionally want an external model only.

The late-interaction reranker AIST-87M-LI.gguf (0.4 MB) is required whenever the AIST model is used; engine creation fails without it. It resolves from CORTEXT_LI_MODEL_PATH, else AIST-87M-LI.gguf beside the resolved AIST model, else its shards linked into the library. scripts/download_aist_model.py assembles it beside the downloaded model.

AIST maps text, audio, and images into one retrieval space. Audio inputs are 16 kHz mono float32 PCM. Image inputs are row-major RGB/RGBA bytes with explicit width, height, and channel count.

Every database pins the encoder fingerprint that produced its embeddings. Changing encoder assets for an existing database fails loudly instead of silently comparing vectors from different spaces.

Useful environment variables:

  • CORTEXT_AIST_MODEL_PATH: explicit model file.
  • CORTEXT_LI_MODEL_PATH: explicit late-interaction reranker file.
  • CORTEXT_AIST_THREADS: native runtime thread count.
  • CORTEXT_AIST_N_GPU_LAYERS: GPU offload layer hint.
  • CORTEXT_AIST_CONTEXT_LENGTH: tokenizer/runtime context length.
  • CORTEXT_SQLITE_*, CORTEXT_OBJSTORE_*: storage tuning for packagers and profiling.

Storage

Cortext separates metadata from payload storage:

  • cortext::Store / cortext::Transaction: database boundary.
  • SQLiteStore: built-in metadata store.
  • cortext::ObjectStore / cortext::ObjectTransaction: payload boundary.
  • SqlObjectStore: built-in sqlite-objstore implementation.

Store and transaction instances are single-owner handles. Use a single writer per database; Cortext does not merge concurrent writer state.

Benchmarks

Cortext makes no LLM calls to store or recall. Every score below comes from replaying each benchmark's conversations into the engine as they happened: live ingest, Natural retention, and consolidation when the engine recommends it. Then each question is asked against the final store, and the retrieved context goes to an answer model and a judge, using each benchmark's own published QA prompts. The answer model and the judge are both gpt-6.1-sol at low reasoning effort. Packets are capped at a 34,800-byte evidence budget (about 8k tokens). Measured October 2026 on the v5 engine, macOS arm64.

Same pipeline, three memory layers: LoCoMo

All three systems ran on the same LoCoMo conversations (1,540 questions, categories 1–4). Each one's retrieval went to the same answer model and judge, under the same evidence budget.

System Accuracy Multi-hop Temporal Single-hop Open-domain Recall p50 / p95 LLM calls to store
Cortext 86.2% 83.3% 85.1% 90.0% 64.6% 9.7 / 13.7 ms 0
Mem0 OSS 2.2.1 89.0% 91.8% 89.1% 91.0% 62.5% 167 / 250 ms 544 calls, 5.7M tokens
Graphiti 0.31.1 (Zep's OSS engine) 81.0% 89.0% 78.8% 81.3% 61.5% 143 / 157 ms 4,151 calls, 14.4M tokens

The competitors were set up as follows:

  • Shared setup: the same extraction model as the answer model. The embedder is EmbeddingGemma-2 Q8_0 running locally, close to Cortext's own local encoder.
  • Mem0: run per Mem0's eval convention: each session is added from both speakers' perspectives, and both speakers' memories are searched. Each session's date is injected into extraction, which helps Mem0.
  • Graphiti: one episode per session, anchored to the session time, with hybrid search.

Mem0's 2.8-point lead comes almost entirely from multi-hop and temporal questions, where its write-time LLM writes consolidated, dated facts. Cortext gets within that margin with no model calls at write time and recall about 17× faster.

Against published results

These competitor numbers are the vendors' or papers' own, each with its own answer model, judge and protocol. They were not re-run here, so treat the gaps as directional.

Benchmark Cortext Reranker training Published
LoCoMo (1,540 q) 86.2% (87.9% on corrected keys) zero-shot Mem0 92.5, Honcho 89.9, Hindsight 89.6, Zep 75.1
BEAM 100K / 1M / 10M, ExaBase protocol 83.8 / 80.0 / 70.8 100K, 10M zero-shot; 1M overlaps past.dev 92.1 / 90.7 / 85.0; Exabase M-1 76.9 / 75.0 / 68.0
BEAM 100K / 1M / 10M, AMB protocol 72.2 / 70.4 / 53.6 100K, 10M zero-shot; 1M overlaps Hindsight 73.4 / 73.9 / 64.1; Honcho 63.0 / 63.1 / 40.6; Mem0 – / 64.1 / 48.6
LongMemEval-S (500 q) 93.8% overlaps Mem0 94.4, Hindsight 91.4, EmergenceMem 86.0, Supermemory 85.2, Zep 71.2
LongMemEval-M (100 q) 78.0% overlaps —
DolphinBench (600 tests, agent with memory tools) 87.0% overlaps Mem0 + Hermes 70.7, Hindsight + Hermes 69.5, Honcho + Hermes 68.5
MemLens agent subset (195 q, 32k, images) 72.3% overlaps Mem0 43.2 (text captions), MemOS 36.5, MemAgent-7B 32.8

Caveats:

  • Reranker training overlap: the late-interaction reranker was trained partly on LongMemEval, MemLens, DolphinBench and the BEAM 1M train conversations, with held-out splits (20% of LongMemEval and MemLens questions, 30% of BEAM 1M conversations, one DolphinBench persona). Rows marked "overlaps" are therefore not zero-shot. LoCoMo and BEAM 100K and 10M were never trained on. On the questions it never saw, Cortext scores 96.5% (LongMemEval-S, 115 q) and 78.6% (MemLens, 56 q).
  • Agent models: DolphinBench ran with a gpt-6.1-sol agent; the published rows used gpt-5.6-luna.

Earlier evaluations

These are packet-quality judge runs from before v5.

Eval Result Context Cost
MSC hosted frontier judge, 9 probes, 3 reps Cortext 7/9 probe wins, 21/27 row wins 998 tokens vs 49,196 for chat+RAG
MSC 128k RAG ablation, 6 systems Cortext 6/9 probe wins, 19/27 row wins 816 tokens; compaction 7,110; rolling window 15,999
One-year sparse replay, local Gemma4 judge Cortext 47/93 raw wins 467 tokens vs 7,447 for chat+RAG

Full protocols, caveats, and artifacts are in docs/paper/sections/9_experimental.qmd and docs/paper/_manuscript/index.md.

How It Works

flowchart TD
    input["input<br/>(text / audio / image)"] --> perception[perception]
    perception --> accumulator[stream accumulator]
    accumulator --> wm[working memory]
    wm --> retrieval[graph retrieval]
    retrieval --> ctx[context out]
    wm --> consolidation[shallow consolidation]
    consolidation --> ltm[long-term store]

    retrieval -. usage / prediction error .-> control
    ltm -. storage pressure .-> control

    control["homeostatic control<br/>F / S / T"]

    control -. write gates / thresholds / decay / cadence .-> perception
    control -.-> accumulator
    control -.-> wm
    control -.-> consolidation
Loading

The production loop is composed from small operations in src/operations/. Retrieval combines embedding similarity, graph edges, temporal scoring, supersession demotion, and working-memory state. Feedback updates F/S/T so later writes, decay, thresholds, attention width, and consolidation cadence adapt to the stream.

WebAssembly

The browser build uses Emscripten and emits an ES module plus .wasm payload:

./build-wasm.sh

Browser package and wrapper: augmem/cortext.wasm. The in-repo browser demo lives in examples/web/; it remembers text (with an optional media file) and recalls with an evidence budget and a reference time. For demos, either select both model files in the UI (AIST-87M_q8_0.gguf and AIST-87M-LI.gguf) or preload them:

python3 scripts/download_aist_model.py --output-dir models --quant q8_0
# Preload only the runtime files (not the Git chunks).
mkdir -p wasm-models/AIST-87M-GGUF wasm-models/mdbr-leaf-ir
cp models/AIST-87M-GGUF/AIST-87M_q8_0.gguf models/AIST-87M-GGUF/AIST-87M-LI.gguf \
  wasm-models/AIST-87M-GGUF/
cp models/mdbr-leaf-ir/vocab.txt wasm-models/mdbr-leaf-ir/
./build-wasm.sh -DCORTEXT_WASM_PRELOAD_MODEL_ASSETS_DIR="$PWD/wasm-models"
python3 -m http.server 8000

Then open http://localhost:8000/examples/web/. node tests/wasm/smoke.mjs build-wasm/dist/wasm smoke-tests the same bundle in Node (CI runs it).

The browser build has no threads and no shared-memory file locking, so file databases use SQLite's rollback journal (not WAL), object payloads live in the Emscripten filesystem beside the database (the vfs objstore backend; CORTEXT_* environment knobs such as CORTEXT_SQLITE_JOURNAL_MODE can be set from JavaScript on Module.ENV in a preRun callback), and C API errors come back through cortext_last_error() (native WebAssembly exceptions, -fwasm-exceptions). Audio is compiled out of the browser preset.

Tradeoffs

  • Context reduction over maximal sufficiency. Cortext is optimized to return a small, relevant packet. Full history can be more complete if you can afford the tokens and prefill latency.
  • Pinned local encoder. Databases are tied to the embedding model fingerprint that produced them.
  • Single writer per database. Multi-device sync and concurrent-writer merge are not implemented.
  • Source-backed traces. v1 stores and retrieves observed signals; it does not run an LLM fact extraction layer that rewrites memories.
  • Native runtime. C++20 and local model assets are part of the core deployment story.

Repository Layout

  • include/, src/: public headers and C++ implementation.
  • src/operations/: control-loop and memory pipeline operations.
  • tests/: Catch2 test suite.
  • examples/: benchmarks, demos, and smoke tests.
  • ffi/node/: optional N-API addon source (addon.cpp) for Node package builds.
  • scripts/, tools/: CLI, release packaging, experiment harnesses, and offline utilities.
  • docs/paper/: manuscript source, generated markdown, and artifacts.
  • models/, third_party/: local model assets and vendored dependencies.

Paper

The architecture and experimental record are specified in the manuscript:

QUARTO_DISABLE_GIT=1 QUARTO_DISABLE_GITHUB=1 quarto render docs/paper

Start with docs/paper/_manuscript/index.md, or edit source sections under docs/paper/sections/.

Motivation

Cortext began for a personal reason. In 2022, my father-in-law was diagnosed with dementia. The long-term goal is memory augmentation that helps people preserve continuity, confidence, and independence.

The same architecture is useful for long-horizon LLM memory, but the primary motivation is human: a realtime system that notices what matters, surfaces relevant context, and does not force the user to manage memory by hand.

License

Apache-2.0. See LICENSE and NOTICE.

About

Event-driven long-horizon memory for real-time wearable AI and LLM agents.

Resources

Stars

30 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages