Skip to content

MemoryLtmService

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:81

new MemoryLtmService(prisma, stmService?, embeddingsService?, vectorStore?, importanceService?, duplicateDetectionService?, ingestPipeline?, contradictionDetectionService?): MemoryLtmService

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:85

PrismaService

PostgresStmAdapter

EmbeddingsService

VectorStore

ImportanceScoringService

DuplicateDetectionService

IngestPipelineService

ContradictionDetectionService

MemoryLtmService

applyDecayPolicy(options?): Promise<DecayPolicyResult>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1430

DecayPolicyOptions = {}

Promise<DecayPolicyResult>


casMetadataUpdate(memoryId, userId, organizationId, expectedVersion, data, options?): Promise<PrismaMemory | null>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1963

Compare-and-swap metadata write for internal lifecycle paths (G3-T3).

Mirrors the user-facing update()’s CAS semantics exactly — same where shape (id + userId + type, plus organizationId when supplied) with the expected version folded in, and a version: { increment: 1 } — so lifecycle writes participate in the SAME optimistic-concurrency protocol as user edits instead of silently clobbering them.

options.bumpVersion: false keeps the version-KEYED where (a stale write still can never clobber a concurrent edit — it just misses) but skips the increment. Used ONLY by the access-bookkeeping hot path: get() and recall() record accesses, so an access write that bumped version would invalidate the version the caller just read and every read-then-update flow using expectedVersion (G4-T2) would 409 against its own access bump.

Returns null when the CAS missed (version moved or row gone — Prisma P2025); callers decide whether to re-read + retry once or skip. Non-P2025 errors are rethrown unchanged.

Public-but-internal: exposed (not private) ONLY so CorpusConsolidationService (G3-T2) can route its supersede/tag-union writes through the exact same G3-T3 CAS protocol instead of duplicating it. Not part of the app-facing API — application code must use update().

string

string

string | null | undefined

number

Record<string, unknown>

boolean

Promise<PrismaMemory | null>


clear(userId): Promise<number>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:841

Clear all long-term memories for a user

string

Promise<number>


count(userId, filters?): Promise<number>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:785

Count total long-term memories for a user

string

Partial<LtmQueryOptions>

Promise<number>


create(input): Promise<LtmMemory>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:112

Create a new long-term memory.

Runs the B0 ingest pipeline (steps 1–6) before write, then handles steps 7 (PostgresWrite), 11 (EmbeddingGenerate), and 12 (SearchIndexUpdate) inline. Steps 8–10 and 13 fire asynchronously after a successful write.

CreateLtmMemoryData

Promise<LtmMemory>


delete(userId, memoryId, organizationId?, scope?): Promise<boolean>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:642

Delete a long-term memory. Pass organizationId in user-facing paths to prevent cross-tenant deletes. See get() for the full isolation contract.

string

string

string

string

Promise<boolean>


findInsightCandidates(topic, limit, userId?): Promise<LtmMemory[]>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1414

Return LTM memories that carry a given topic tag and have not yet been clustered (i.e. not tagged insight or clustered). Used by the insight extraction job. The clustered tag is written atomically with the insightId metadata field in the same update call, so filtering by tag is sufficient to exclude already-processed memories.

string

number

string

Promise<LtmMemory[]>


get(userId, memoryId, organizationId?, scope?): Promise<LtmMemory | null>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:322

Retrieve a long-term memory by ID.

When organizationId is provided the query is narrowed to that org’s rows, preventing cross-tenant access. Omitting it is permitted for admin / system callers (e.g. reindex) but must NOT be used in user-facing paths — the auth layer (#128, #130) must always supply the caller’s org context.

string

string

string

string

Promise<LtmMemory | null>


lexicalSearch(userId, query, options?): Promise<SemanticSearchResult[]>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1318

Keyword retrieval over memory content — the degraded-mode counterpart to semanticSearchDetailed.

This exists so a deployment without an embedding backend still answers recall instead of silently returning nothing (issue 288). scripts/install.sh has always told operators that “memories still store and recall lexically” when Ollama is absent; this is that promise implemented.

How it scores ───────────── The query is split into distinct terms and each candidate is scored by the fraction of those terms its content contains. That fraction occupies the similarity slot of the existing blended ranking, so recency and importance keep their usual influence and scores stay in [0, 1] — the same range and the same shape as the semantic path.

What it is not: term overlap is not meaning. A lexical hit set is narrower and dumber than a semantic one, which is exactly why callers surface retrievalMode rather than passing these results off as equivalent.

Every filter the semantic path applies is mirrored here — tenant, type, scope, tags, date bounds, and the superseded exclusion — so degrading changes retrieval quality without widening the set of memories a caller is allowed to see.

string

string

LexicalSearchOptions

Promise<SemanticSearchResult[]>


list(userId, options?): Promise<PaginatedResult<LtmMemory>>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:685

List long-term memories for a user with pagination and filtering

string

LtmQueryOptions

Promise<PaginatedResult<LtmMemory>>


promote(userId, memoryId, organizationId?, scope?): Promise<LtmMemory>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:865

Promote a memory from short-term to long-term storage. Pass organizationId and scope to preserve both namespaces through the STM→LTM transfer.

string

string

string

string

Promise<LtmMemory>


recordLifecycleAudit(entry): Promise<void>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:2053

Append a lifecycle mutation to the memory_audits trail (G3-T3).

Same column shape as the mcp-server MemoryAuditService.record() write (WP2 T5) — action delete rows here are picked up unchanged by findLatestDeleteSnapshot() / restore_memory — but attributed to a system actor (actorType: 'system', actorId naming the job) because no verified API-key principal exists inside a background job. Best-effort: NEVER throws — a lost audit row must not fail the lifecycle mutation that already happened.

Public-but-internal (same rationale as casMetadataUpdate): shared with CorpusConsolidationService (G3-T2, actor corpus_consolidation) so there is exactly ONE lifecycle-audit writer.

"supersede" | "delete"

"ltm_decay" | "dedup_supersede" | "corpus_consolidation"

Record<string, unknown>

Record<string, unknown>

string

string | null

string | null

string

Promise<void>


recreateVectorIndex(): Promise<void>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1699

Drop and rebuild the vector index from scratch. Destructive and NOT atomic: recall returns empty for all tenants until a subsequent reindex backfills the index. Callers that chunk their own backfill (e.g. the async reindex queue) invoke this exactly once up front, then reindex batch-by-batch with recreate: false — the per-batch recreate guard in reindex would otherwise skip the rebuild because every chunked call passes maxMemories. No-op (with a warning) when no vector store is configured.

Promise<void>


reembed(userId, memoryId, organizationId?, scope?): Promise<LtmMemory>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:527

Regenerate the embedding for a memory’s CURRENT content and re-index it, clearing metadata.embeddingStale (WP2 T7/D10). Repairs a memory whose content was edited during an embeddings outage.

Deliberately does NOT bump version: re-sending identical content through update() would trip the T4 compare-and-swap for other writers. updatedAt is allowed to move. Throws LtmEmbeddingUnavailableError when no embedding can be produced, leaving the staleness flag in place for a later retry.

string

string

string

string

Promise<LtmMemory>


reindex(options?): Promise<ReindexResult>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1718

Backfill / reindex the vector store from Postgres.

Pages through long-term memories using a stable cursor, (re)generates embeddings as needed, and upserts them into the configured vector store. The operation is idempotent and cursor-resumable: re-running it is safe and picks up where a prior run stopped when a cursor is supplied. Postgres remains the source of truth, so per-item failures are counted and skipped rather than aborting the whole run.

ReindexOptions = {}

Promise<ReindexResult>


restore(input): Promise<LtmMemory>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:587

Recreate a long-term memory from a delete snapshot, preserving its ORIGINAL id so id-keyed vector upserts and inbound links stay valid (WP2 T5/G5). Runs through the same quota-guarded insert path as create, re-embeds the content, and re-indexes the vector. Fails with LtmMemoryQuotaExceededError if the user is at quota, and is a no-op-safe recreate: if the id already exists the Prisma insert throws and surfaces as a database error.

string

string

Record<string, unknown> | null

string | null

string | null

string[]

string

Promise<LtmMemory>


semanticSearch(userId, query, options?): Promise<SemanticSearchResult[]>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1149

Perform a semantic (vector) recall over a user’s long-term memories.

Embeds the query, runs a tenant-scoped kNN search in the vector store, then hydrates the matching memories from Postgres and attaches similarity scores. Returns an empty array when embeddings or the vector store are unavailable.

string

string

SemanticSearchOptions

Promise<SemanticSearchResult[]>


semanticSearchDetailed(userId, query, options?): Promise<DetailedSemanticSearchResult>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:1174

Semantic search that reports whether the semantic path actually ran.

Identical to semanticSearch except for the return shape. It exists because SemanticSearchResult[] cannot distinguish “the corpus had no match” from “embeddings are switched off, so nothing could match” — both are []. Callers that want to degrade to lexical retrieval branch on degraded; callers that legitimately want semantic-or-nothing keep using semanticSearch.

degraded is set only for genuine unavailability of the semantic path. A healthy search that simply matched nothing returns degraded: false with an empty results, and must never trigger a fallback: “no memories match this query” is a correct answer, and re-running it lexically would silently widen the result set.

string

string

SemanticSearchOptions

Promise<DetailedSemanticSearchResult>


update(userId, memoryId, input, organizationId?, scope?): Promise<LtmMemory>

Defined in: packages/memory-ltm/src/memory-ltm.service.ts:362

Update a long-term memory. Pass organizationId in user-facing paths to prevent cross-tenant writes. See get() for the full isolation contract.

string

string

UpdateLtmMemoryData

string

string

Promise<LtmMemory>