LISAOS // DOCS
GATEWAY // MEMORY

Gateway Module — `memory/`

1. Purpose

The persistent-memory tier of the Lisa Memory Gateway. It owns three durable document types — preference, knowledge, summary — stored in SQLite with Voyage-AI embeddings for semantic recall and an FTS5 sidecar for keyword recall. It also owns the shared database substrate: memory/db.ts (1,258 lines) is the single schema-owning module for the entire gateway — every other module's tables, virtual tables, triggers, and migrations are declared here, not in the consuming module. The module exposes an EmbeddingProvider (Voyage → keyword fallback, resilient wrapper) that psychic-cache, context, and vault-index all consume for their own embeddings.

Architectural note (code-vs-doc): the module's REST surface is not defined inside memory/. The memory/index.ts barrel is a factory (createMemoryLayer) plus a background-embedding helper — it exports no Express router. The /api/health, /api/health/detail, and GET/POST/PUT/DELETE /api/memory[/:id] endpoints are declared inline in the top-level server/index.ts (≈ lines 531–731), wired directly against memory.memoryRepo / memory.embeddingRepo. This is the one gateway module whose HTTP surface lives outside its own folder.

2. File Inventory

FileLinesResponsibility
db.ts1,258Gateway-wide SQLite schema owner: all CREATE TABLE/CREATE VIRTUAL TABLE/CREATE TRIGGER/CREATE INDEX DDL + idempotent migrations for every module (memory, psychic_cache, agent_memory, vault_*, knowledge_graph, cross_references, dispatch, sessions, activity, source_weights, context_assembly_log, curator, compute_capture, skills_telemetry). openDatabase() factory
repositories.ts310createMemoryRepository (CRUD + FTS on memory_documents/memory_fts) + createEmbeddingRepository (KNN on vec_embeddings via sqlite-vec). Read-path Zod content validation (Preference/Knowledge/SummaryContentSchema)
embeddings.ts288createVoyageProvider (batched, retry/backoff, VoyageAuthError 401 sentinel), createKeywordFallbackProvider (trigram/unigram hash TF, L2-normalised), createResilientProvider (primary→fallback with onAuthFailure/onPrimaryRecovered hooks, dim pad/truncate)
cache.ts198createLRUCache (Map insertion-order LRU + TTL sweep) + createCachedMemoryRepository (read-through decorator on findById/findByIds/findByType, invalidate-on-write)
index.ts143createMemoryLayer factory (assembles providers + repos + LRU caches) + scheduleExchangeEmbedding background helper
types.ts134Interfaces: EmbeddingProvider, MemoryDocument(+content unions), MemoryRepository, EmbeddingRepository, Cache, MemoryLayer
Total2,331(REST routes for this module live in server/index.ts, not counted here)

3. Public API Surface

REST Endpoints

All routes declared inline in server/index.ts. Auth posture per AK-415/435/436: /api/health is the only public GET (liveness only); every other GET requires a Bearer; every mutation requires the write Bearer only.

MethodPathAuthBody / QueryResponseSide Effects
GET/api/healthpublic (health only){ok, gateway_uptime_seconds} (liveness-only; recon fields stripped)none
GET/api/health/detailBearer{ok, uptime, version, schema_version, status, timestamp, memory.provider, modules{}}none
GET/api/memoryBearer?limit&offset&type{data: MemoryDocument[]}none
GET/api/memory/:idBearer{data: MemoryDocument} / 404none
POST/api/memoryBearerCreateMemoryInputSchema (Zod discriminated content union)201 {data: MemoryDocument} / 400DB insert + FTS insert; async embed → vec_embeddings
PUT/api/memory/:idBearerUpdateMemoryInputSchema (partial content/tokenCount){data: MemoryDocument} / 404 / 400DB update + FTS update; async re-embed if content changed
DELETE/api/memory/:idBearer{data:{deleted:true}} / 404DB delete + FTS delete + vec delete

POST /api/memory/search was removed (D27) — sole consumer of the deleted createContextRetrievalService. Canonical retrieval is now POST /api/context/assemble.

MCP Tools

Consumed via the persistent-memory dual-write path.

Tool NameRaw payload?EndpointNotes
commit_memoryNo (flat)POST /api/memoryNested content object ({type, content:{…}})
commit_memory_rawYes ({payload})POST /api/memoryStaleness-immune twin
update_memoryNoPUT /api/memory/:id
(list/get/delete)GET/DELETE /api/memory[/:id]No dedicated MCP tool; REST-only

4. Internal API

Exported for cross-module use:

  • openDatabase(): Database — the shared handle every module's factory receives (db.ts).
  • createMemoryLayer(config): MemoryLayer{embeddingProvider, embeddingRepo, memoryRepo, close()}.
  • scheduleExchangeEmbedding(layer, opts) — fire-and-forget knowledge-doc embedding of a chat exchange (skips < minTokens, default 50).
  • createVoyageProvider / createKeywordFallbackProvider / createResilientProvider + ResilientProviderHooks, VoyageAuthError — imported by server/index.ts for the degraded-alert wiring.
  • createMemoryRepository / createEmbeddingRepository — consumed by context adapters.
  • Type re-exports: MemoryLayer, MemoryDocument, MemoryContent, EmbeddingProvider, etc. (context, psychic-cache, vault-index all import EmbeddingProvider from here).

5. Background Services

  • Async embedding on writesetImmediate after POST/PUT /api/memory and inside scheduleExchangeEmbedding; embedding failure is caught + logged, never blocks the response (FTS row still lands).
  • LRU + TTL cachesdocCache (max 1,000, 5-min TTL), typeCache (max 200, 2-min TTL). Eviction is lazy (on set/get), no timer. typeCache.clear() on any write for conservative invalidation.
  • Resilient provider recoveryonPrimaryRecovered fires on first Voyage success after a failure, clearing the degraded-suppression flag (AK-321/H-07). No timer; edge-triggered.

6. Data Contracts

Read-path content validation (repositories.ts):

  • PreferenceContentSchema = {key, value, source, confidence:number}
  • KnowledgeContentSchema = {topic, content, sourceSessionIds:string[]=[], lastReferencedAt:string=''}
  • SummaryContentSchema = {summary, keyTopics[], decisionsReached[], unresolvedQuestions[]}

Write-path (server/index.ts):

  • CreateMemoryInputSchema = {type:enum, content:union(3 above), tokenCount?, userId?, sessionId?}
  • UpdateMemoryInputSchema = {content?:union, tokenCount?}

VoyageResponseSchema (embeddings.ts) = {data:[{embedding:number[]}], usage:{total_tokens}}.

Owned tables (declared in db.ts): memory_documents (+4 indexes), conversation_summaries, memory_fts (fts5), vec_embeddings (vec0, 512-dim), schema_meta. Note db.ts also declares every other module's tables.

7. Dependencies

  • Gateway modules consumed: shared/search-utils (buildFts5Query). Depended-on-by: psychic-cache, context, vault-index, agent-memory, dispatch, sessions, activity, etc. (all consume openDatabase + EmbeddingProvider).
  • External libraries: better-sqlite3, sqlite-vec (vec0 virtual tables), zod, Voyage AI HTTP API (api.voyageai.com/v1/embeddings), Node crypto.
  • Environment variables: VOYAGE_API_KEY (credential-store-first: systemd LoadCredential → env fallback; empty → keyword-fallback-only boot), voyageModel (default voyage-3-lite, 512-dim), dbPath (the gateway SQLite datastore).

8. Test Coverage

LayerFileTests
LRU + cached repotest/cache.test.ts23
Voyage/keyword/resilient providerstest/embeddings.test.ts21
PUT validation (D884/AK-323)test/memory-update-validation.test.ts1 describe block (multiple assertions)
MCP commit_memory_rawtest/commit-memory-raw.test.ts— (raw-twin contract)

No dedicated memory/repositories.ts suite; the read/write CRUD is exercised indirectly via context + search + validation suites. Coverage of db.ts migrations is via cross-module suites, not a targeted migration test.

9. Known Limitations

  • REST surface lives in server/index.ts, not the module — the one gateway module with no self-contained router. A future refactor should extract a createMemoryRouter for symmetry; until then, changes to memory endpoints require editing the 900+-line root file.
  • db.ts is a god-file — 1,258 lines owning every module's schema. High blast radius: any migration bug affects the whole gateway. Accepted for single-DB-handle simplicity, but flagged for the LisaOSMap dependency graph.
  • Keyword fallback quality — trigram/unigram hashing produces low-quality vectors; recall degrades (never fails) when Voyage is unreachable.
  • No direct-repository unit suite for repositories.ts — CRUD correctness is inferred from consumers.

10. Change History

DateDispatchSummary
2026-07-042295Initial per-module spec filed (audit campaign Phase 4, §5.2 template). Documented inline-REST-in-root and db.ts-god-file findings
(historical)D27Removed POST /api/memory/search + createContextRetrievalService + memory/retrieval.ts (dead code)
(historical)D80 (AK-264)Zod discriminated union on POST /api/memory content
(historical)D321/H-07VoyageAuthError 401 sentinel + resilient-provider degraded-alert hooks
(historical)D884 (AK-323)Zod validation on PUT /api/memory/:id
(historical)AK-321 C5Credstore-first VOYAGE_API_KEY loading (fail-closed audit)

On this page