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
- Unreleased (engine v5): per-call evidence budget on every process call,
standing_memoryreturned outside the retrieved ranking,"mixed"memories that return every signal's payload with its ownmimetypeandtimestamp, text with caller media, and query reference time (reference_ms/as_of).cortext_config.procedural_enabledand the Soft Anchor fields are removed (C struct layout change), whole memories are returned unless a budget is set, and the late-interaction rerankerAIST-87M-LI.ggufis required with the AIST model. v1.2.0: Retention is nowNatural(default),Durable,Boundary, andEphemeral. Omitted retention is Natural:force_boundary=falseandforce_write=falseso episode algorithms decide.Durablekeeps force boundary + force write (explicit chat turns).Boundaryforces the turn edge only.Ephemeralremains no-store query. Exposed on C++ Process*, Ccortext_process_json_options.retention, and Python/JS/Go bindings.v1.1.10: publicRetention::Ephemeralcalls 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 behindCORTEXT_BUILD_EXAMPLES=ON.
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/cortextC++ 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-failureBuild 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 --helpDefault 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.pycortext_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 1Other 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].
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.
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-failureModel-free CI-style gate:
./build/tests/cortext_tests '~[aist]' --reporter compactBuild 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 --helpBuild an installable CLI with Zig:
zig build -Doptimize=ReleaseFast -Dshared=false -Dcli=true -Dfetch-aist-model=false
./zig-out/bin/cortext_cli --helpCross-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=falseImportant 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 intolibcortextand 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: chooseq8_0,q5_1, orallwhen 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.
#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,ProcessImagewithCortext::Media; Ccortext_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 istext/*. 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.
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 ofretrieved_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: orderedNone,Recommended, orRequiredmaintenance 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 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.
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.pyTracked 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/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.sha256On 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"Cortext requires the AIST-87M GGUF encoder. Default shared-library builds
behave as if the model lives inside libcortext:
- Git stores only under-100 MiB shards (no LFS) under
models/AIST-87M-GGUF/chunks/plusmodels/manifest.jsonand vocab. Optional name CSVs use the same pattern underdata/surnames/chunks/; reassemble withpython3 scripts/shard_surnames.py --assemblewhen needed. - The build links those shards (and vocab) into the shared library via
.incbin(CORTEXT_EMBED_AIST_MODEL=ON). - 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 noCORTEXT_AIST_MODEL_PATH.
Resolution order:
CORTEXT_AIST_MODEL_PATH(explicit full.gguf)- On-disk search under
models/(checkout layout) - 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.
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.
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.
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.
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-solagent; the published rows usedgpt-5.6-luna.
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.
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
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.
The browser build uses Emscripten and emits an ES module plus .wasm payload:
./build-wasm.shBrowser 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 8000Then 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.
- 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.
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.
The architecture and experimental record are specified in the manuscript:
QUARTO_DISABLE_GIT=1 QUARTO_DISABLE_GITHUB=1 quarto render docs/paperStart with docs/paper/_manuscript/index.md, or edit source sections under
docs/paper/sections/.
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.