@amemhq/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,502 @@
1
+ declare function configure(opts: {
2
+ dataDir?: string;
3
+ }): void;
4
+
5
+ /**
6
+ * embedding.ts — Local ONNX embedding via @huggingface/transformers
7
+ * Matches Python: SentenceTransformer.encode(text, normalize_embeddings=True)
8
+ *
9
+ * The model is selectable because the default is not a good retrieval model: it
10
+ * caps at 128 tokens, so anything longer is truncated before it reaches the
11
+ * vector. Changing it is a breaking change whenever the dimension differs —
12
+ * Qdrant fixes a collection's vector size at creation — so the default stays put
13
+ * and the switch is opt-in. See docs/reference/embedding-models.md.
14
+ */
15
+ /** The model shipped since the beginning. Not changed here on purpose. */
16
+ declare const DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
17
+ /** Which model this process embeds with. */
18
+ declare function getEmbeddingModel(): string;
19
+ /**
20
+ * The vector width this model produces, measured rather than looked up.
21
+ *
22
+ * A hardcoded table would be wrong the moment someone points AMEM_EMBED_MODEL at
23
+ * something not in it, and wrong silently — the collection would be created with
24
+ * the wrong size and every insert would fail. Encoding one short string costs one
25
+ * forward pass on a model that has to load anyway, and is right for any model.
26
+ */
27
+ declare function getEmbeddingDim(): Promise<number>;
28
+ /**
29
+ * Encode text to 384-dim normalized embedding vector.
30
+ * Singleton model, loaded once and reused.
31
+ */
32
+ declare function encode(text: string): Promise<number[]>;
33
+ /**
34
+ * Load the model now rather than on the first encode(). A long-lived service
35
+ * pays the download at startup, not on a user's first write.
36
+ */
37
+ declare function loadModel(): Promise<void>;
38
+ /**
39
+ * Whether the model is resident. Synchronous and I/O-free — unlike encode() it
40
+ * can never trigger the several-hundred-megabyte download, so a health check is
41
+ * free to poll it.
42
+ */
43
+ declare function isModelLoaded(): boolean;
44
+
45
+ /**
46
+ * storage.ts — Qdrant vector storage for A-MEM
47
+ * Uses native fetch (Node 18+) to avoid undici compatibility issues with Node v26
48
+ * Collection: amem_notes, 384-dim cosine, with agent_id isolation
49
+ */
50
+ /** Per-agent override config. If collection is set, mode B (isolated collection) is used. */
51
+ interface AgentAmemConfig {
52
+ agentId?: string;
53
+ collection?: string;
54
+ }
55
+ /** Top-level plugin config shape (superset — existing fields preserved). */
56
+ interface AmemPluginConfig {
57
+ agentId?: string;
58
+ collection?: string;
59
+ topK?: number;
60
+ /** Per-agent overrides keyed by agentId */
61
+ agents?: Record<string, AgentAmemConfig>;
62
+ llmProvider?: string;
63
+ llmModel?: string;
64
+ llmBaseURL?: string;
65
+ llmStrongProvider?: string;
66
+ llmStrongModel?: string;
67
+ llmStrongBaseURL?: string;
68
+ /** Which tier the agent_end CRUD decision runs on: `fast` (default) or `strong`. */
69
+ llmCrudRole?: 'fast' | 'strong';
70
+ /** Story 43: run the nightly contradiction sweep. Default true. */
71
+ conflictSweep?: boolean;
72
+ /** Similarity floor for accepting an LLM-chosen UPDATE target. Raise it for
73
+ * cheaper models — a rejected update is stored as a new memory, never lost. */
74
+ crudUpdateMinSim?: number;
75
+ }
76
+ /** One entry in a note's evolution history (Story 13-B) */
77
+ interface EvolutionEntry {
78
+ triggeredBy: string;
79
+ triggeredAt: string;
80
+ oldContext: string;
81
+ newContext: string;
82
+ oldTags: string[];
83
+ newTags: string[];
84
+ action?: 'update_neighbor' | 'strengthen' | 'consolidate' | 'crud_update';
85
+ /** Story 41: the content this entry replaced, so an overwrite stays recoverable. */
86
+ oldContent?: string;
87
+ suggestedConnections?: string[];
88
+ tagsUpdated?: string[];
89
+ }
90
+ interface MemoryNote {
91
+ id: string;
92
+ content: string;
93
+ keywords: string[];
94
+ tags: string[];
95
+ context: string;
96
+ links: string[];
97
+ embedding: number[];
98
+ timestamp: string;
99
+ agent_id: string;
100
+ hash: string;
101
+ retrieval_count: number;
102
+ last_accessed: string;
103
+ evolution_history: EvolutionEntry[];
104
+ category: string;
105
+ is_active: boolean;
106
+ note_type: 'memory' | 'knowledge';
107
+ topics: string[];
108
+ pending_merge: boolean;
109
+ evolution_type?: 'EVOLVE' | 'CONFLICT' | 'EXPAND' | 'NEW';
110
+ conflict: boolean;
111
+ conflicts_with?: string[];
112
+ conflict_reason?: string;
113
+ /**
114
+ * Story 43: when this note was last included in a contradiction scan.
115
+ * Absent = never scanned. Lets the sweep skip batches it has already judged,
116
+ * which is what makes a daily run cost one or two calls instead of re-reading
117
+ * the whole store every night.
118
+ */
119
+ conflict_scanned_at?: string;
120
+ subjects: string[];
121
+ ephemeral: boolean;
122
+ low_quality: boolean;
123
+ owner: string;
124
+ readers: string[];
125
+ writers: string[];
126
+ }
127
+ interface QueryResult {
128
+ note: MemoryNote;
129
+ score: number;
130
+ }
131
+ /**
132
+ * Raised when the configured model's vector width does not match the collection
133
+ * that already exists. Its own class so the plugin can log it loudly instead of
134
+ * as one more startup warning — this one needs the operator to act.
135
+ */
136
+ declare class EmbeddingDimensionMismatchError extends Error {
137
+ readonly collection: string;
138
+ readonly collectionDim: number;
139
+ readonly modelDim: number;
140
+ readonly model: string;
141
+ constructor(collection: string, collectionDim: number, modelDim: number, model: string);
142
+ }
143
+ /**
144
+ * Ask Qdrant whether it can serve, right now.
145
+ *
146
+ * `ensureCollection()` cannot answer this: it latches `_collectionReady` and
147
+ * short-circuits on every later call, so once it has succeeded it keeps
148
+ * reporting success long after Qdrant has gone away. `/readyz` answers in plain
149
+ * text, so it deliberately bypasses the JSON-parsing `qdrant()` helper above.
150
+ */
151
+ declare function pingQdrant(): Promise<void>;
152
+ /**
153
+ * Ensure the given Qdrant collection exists with the correct schema.
154
+ * If collectionName is omitted, uses process.env.AMEM_COLLECTION (default: amem_notes).
155
+ * Mode B agents pass their dedicated collection name here.
156
+ */
157
+ declare function ensureCollection(collectionName?: string): Promise<void>;
158
+ /**
159
+ * Core CRUD implementation scoped to a specific collection and agent filter mode.
160
+ * collectionName: which Qdrant collection to operate on.
161
+ * modeBIsolated: if true, skip the "also include shared" filter in agentFilter
162
+ * (mode B collections are already per-agent, so no cross-agent filter needed).
163
+ */
164
+ declare function makeCrud(collectionName: string, modeBIsolated?: boolean): {
165
+ addNote(note: MemoryNote): Promise<void>;
166
+ /**
167
+ * Story 36: this is the one read that bypasses the agent filter — it fetches
168
+ * straight by UUID. Pass `readerAgentId` to enforce `readers`; an unreadable
169
+ * note comes back as `null` (indistinguishable from missing, so nothing leaks,
170
+ * and callers already handle null). Omitting it skips the check, preserving
171
+ * behaviour for internal callers that only ever hold their own ids.
172
+ */
173
+ getNote(id: string, readerAgentId?: string): Promise<MemoryNote | null>;
174
+ updateNote(note: MemoryNote): Promise<void>;
175
+ findByHash(hash: string, agentId: string): Promise<MemoryNote | null>;
176
+ /**
177
+ * Story 33: pass `callerAgentId` to enforce the writers policy. Callers that
178
+ * hold the note already should prefer checking `canWrite` themselves; this
179
+ * fetch-then-check path exists for callers that only have an id (the plugin's
180
+ * CRUD hook). Returns false — without writing — when the caller may not write.
181
+ * Omitting `callerAgentId` skips the check, preserving existing behaviour for
182
+ * internal callers that are already scoped to their own notes.
183
+ */
184
+ updateNoteContent(id: string, content: string, embedding: number[], hash: string, callerAgentId?: string): Promise<boolean>;
185
+ queryByEmbedding(embedding: number[], topK: number, agentId: string, scoreThreshold?: number, subject?: string): Promise<QueryResult[]>;
186
+ listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
187
+ deleteNote(id: string): Promise<void>;
188
+ /** Story 33: see `updateNoteContent` — returns false, unwritten, when denied. */
189
+ invalidateNote(id: string, callerAgentId?: string): Promise<boolean>;
190
+ getNotesByDatePrefix(datePrefix: string, agentId: string): Promise<MemoryNote[]>;
191
+ countNotes(agentId?: string): Promise<number>;
192
+ updateNoteLinks(id: string, links: string[]): Promise<void>;
193
+ patchNotePayload(id: string, fields: Record<string, unknown>): Promise<void>;
194
+ replaceLinkReferences(oldId: string, newId: string, agentId: string): Promise<void>;
195
+ };
196
+ type StorageContext = ReturnType<typeof makeCrud>;
197
+ /**
198
+ * Create a StorageContext scoped to a specific collection (mode B) or the default collection (mode A).
199
+ * Mode A (same collection): pass collectionName = undefined → uses AMEM_COLLECTION env var.
200
+ * Mode B (isolated collection): pass collectionName = 'amem_notes_<agentId>' and modeBIsolated = true.
201
+ */
202
+ declare function createStorageContext(collectionName?: string, modeBIsolated?: boolean): StorageContext;
203
+ declare function getNote(id: string, readerAgentId?: string): Promise<MemoryNote | null>;
204
+ declare function updateNote(note: MemoryNote): Promise<void>;
205
+ declare function listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
206
+ declare function deleteNote(id: string): Promise<void>;
207
+ declare function invalidateNote(id: string, callerAgentId?: string): Promise<boolean>;
208
+ declare function patchNotePayload(id: string, fields: Record<string, unknown>): Promise<void>;
209
+
210
+ /**
211
+ * memory.ts — A-MEM core logic: addMemory, searchMemory, listMemories
212
+ * Full TypeScript port of amem_client.py
213
+ */
214
+
215
+ interface QualityCheckResult {
216
+ ok: boolean;
217
+ ephemeral: boolean;
218
+ reason?: string;
219
+ }
220
+ declare function checkQuality(content: string): QualityCheckResult;
221
+ declare function addMemory(content: string, agentId?: string, opts?: {
222
+ scope?: 'private' | 'shared';
223
+ storageCtx?: StorageContext;
224
+ /**
225
+ * Story 44: who this memory is about. Empty (the default) means it is about
226
+ * the world or the agent itself, and stays visible whoever is present.
227
+ */
228
+ subjects?: string[];
229
+ }): Promise<string>;
230
+ /**
231
+ * The cheap write path: quality gate → embed the raw content → store.
232
+ *
233
+ * Deliberately skips LLM note construction, similarity dedup, link generation
234
+ * and evolution, so a real-time caller (a game brain logging events tick by
235
+ * tick) never pays for an LLM round-trip. Cost is one embed + one upsert.
236
+ *
237
+ * Episodic notes are an **append-only, faithful event log**: the same content
238
+ * written twice is two events, so there is no hash or vector dedup here.
239
+ * Evolution rewrites a note's context over time — precisely what you do not
240
+ * want for "remember the time the ender dragon killed us". The offline
241
+ * consolidation pass distils these raw events into long-term, linked notes.
242
+ */
243
+ declare function addEpisodic(content: string, agentId?: string, opts?: {
244
+ scope?: 'private' | 'shared';
245
+ storageCtx?: StorageContext;
246
+ /** Story 44: who this episode is about. Empty = world/self. */
247
+ subjects?: string[];
248
+ }): Promise<string>;
249
+ interface SearchResult {
250
+ id: string;
251
+ content: string;
252
+ context: string;
253
+ tags: string[];
254
+ keywords: string[];
255
+ links: string[];
256
+ timestamp: string;
257
+ similarity: number;
258
+ rrf: number;
259
+ topics: string[];
260
+ note_type: 'memory' | 'knowledge';
261
+ }
262
+ declare function searchMemory(query: string, topK?: number, agentId?: string, opts?: {
263
+ useBfs?: boolean;
264
+ bfsSimThreshold?: number;
265
+ topicsFilter?: string[];
266
+ storageCtx?: StorageContext;
267
+ /**
268
+ * Story 44: scope retrieval to one person. Returns memories that name them
269
+ * plus memories that name nobody (world facts, facts about the agent).
270
+ * Omitted = no person scoping, i.e. today's behaviour.
271
+ */
272
+ subject?: string;
273
+ }): Promise<SearchResult[]>;
274
+ declare function listMemories(agentId?: string, storageCtx?: StorageContext): Promise<{
275
+ count: number;
276
+ }>;
277
+ /**
278
+ * Merge semantically similar notes written today.
279
+ * Called asynchronously from agent_end hook; failures are silent.
280
+ * Returns the number of notes merged (deleted).
281
+ *
282
+ * Story 30: pending_merge=true notes are routed through LLM evolution judgment
283
+ * (EVOLVE/CONFLICT/EXPAND/NEW) instead of simple merge.
284
+ * Story 32: shared notes (agent_id='shared') are never merged/consolidated.
285
+ */
286
+ declare function mergeSimilarNotes(agentId: string, storageCtx?: StorageContext): Promise<number>;
287
+ /**
288
+ * Consolidate semantically similar memories.
289
+ * Performs deep deduplication by category and similarity score.
290
+ * Story 32: shared notes (agent_id='shared') are skipped entirely.
291
+ */
292
+ declare function consolidateMemories(agentId: string, logger?: any, storageCtx?: StorageContext): Promise<number>;
293
+ /**
294
+ * How a detected contradiction is handled.
295
+ *
296
+ * `review` (default) marks both notes and leaves the decision to a human — the
297
+ * safe option, because even a strong model is only around 55% accurate at
298
+ * spotting implicit contradictions.
299
+ *
300
+ * `auto` additionally retires the older note of each pair. It needs no human,
301
+ * but at that accuracy roughly two in five retirements will silence a memory
302
+ * that was still true. The retirement is a soft delete, so it is recoverable —
303
+ * but for a system answering in real time, "recoverable" only helps once someone
304
+ * notices. Documented as such; opt in deliberately.
305
+ */
306
+ type ConflictMode = 'review' | 'auto';
307
+ interface ConflictSweepResult {
308
+ scanned: number;
309
+ pairsFound: number;
310
+ retired: number;
311
+ batchesScanned: number;
312
+ batchesSkipped: number;
313
+ }
314
+ /**
315
+ * Find memories that contradict each other and mark them.
316
+ *
317
+ * This is the cold half of the tiering split. The per-turn CRUD decision runs on
318
+ * a cheap model, which is safe (the update guard stops it writing to the wrong
319
+ * note) but dull — it misses contradictions it should have caught. This sweep is
320
+ * what catches them: it runs offline, in batches, on the strong tier, and it
321
+ * sees far more context than any single turn does.
322
+ *
323
+ * Batches by category and hands each batch to the model whole, rather than
324
+ * pairing by similarity — see llmConflictScan for why that distinction is the
325
+ * entire point.
326
+ */
327
+ declare function conflictSweep(agentId: string, opts?: {
328
+ mode?: ConflictMode;
329
+ storageCtx?: StorageContext;
330
+ logger?: {
331
+ info: (m: string) => void;
332
+ };
333
+ /** Re-read every batch, including ones already judged. For a full re-sweep
334
+ * after changing the prompt or the model. */
335
+ force?: boolean;
336
+ }): Promise<ConflictSweepResult>;
337
+
338
+ /**
339
+ * quality.ts — Memory quality scanning and review batch generation (Story 31)
340
+ */
341
+
342
+ interface LowQualityItem {
343
+ note: MemoryNote;
344
+ reasons: LowQualityReason[];
345
+ }
346
+ type LowQualityReason = 'too_short' | 'expired_ephemeral' | 'pending_conflict';
347
+ declare function scanLowQuality(agentId: string): Promise<LowQualityItem[]>;
348
+ declare function generateReviewBatch(agentId: string, outputPath?: string): Promise<string>;
349
+
350
+ /**
351
+ * auth.ts — write authorization for the Access Protocol (Story 33).
352
+ *
353
+ * Story 32 gave every note `owner` / `readers` / `writers` and enforced `readers`
354
+ * at query time. It deliberately left `writers` unenforced. The consequence: the
355
+ * agent filter matches `agent_id == caller OR agent_id == 'shared'`, so ANY query
356
+ * can return another agent's shared note — and every mutation then wrote to it
357
+ * unchecked. An audit found eight such write sites (dedup, link generation,
358
+ * evolution ×2, CRUD update/delete, quality scan, link rewriting).
359
+ *
360
+ * This is the one rule they all gate on. Kept pure and dependency-free so the
361
+ * policy is unit-testable on its own and identical everywhere it is applied.
362
+ */
363
+
364
+ /**
365
+ * May `callerAgentId` mutate `note`?
366
+ *
367
+ * True when the caller owns it, is listed in `writers`, or `writers` is open
368
+ * (`'*'`). Everything else — notably another agent's shared note, which is
369
+ * readable but not writable — is denied.
370
+ */
371
+ declare function canWrite(note: Pick<MemoryNote, 'owner' | 'writers'>, callerAgentId: string): boolean;
372
+ /**
373
+ * May `callerAgentId` read `note`? (Story 36 — the read half of the protocol.)
374
+ *
375
+ * True when the caller owns it, is listed in `readers`, or the note is public
376
+ * (`readers` contains `'*'`, which is how a shared-scope write is stored).
377
+ *
378
+ * Queries already filter by `agent_id`, so list/search paths never surface an
379
+ * unreadable note. This guards the one primitive that bypasses that filter —
380
+ * `getNote(id)` fetches straight by UUID — and the link-neighbourhood walks that
381
+ * use it: a shared note's `links[]` can name its owner's PRIVATE notes, so
382
+ * following those links would otherwise read memory the caller may not see.
383
+ */
384
+ declare function canRead(note: Pick<MemoryNote, 'owner' | 'readers'>, callerAgentId: string): boolean;
385
+
386
+ interface MigrateResult {
387
+ /** Points found in the source. */
388
+ total: number;
389
+ /** Notes whose derived fields were empty — the pre-pipeline cohort. */
390
+ missingDerived: number;
391
+ /** Notes whose fields were re-extracted. 0 unless refreshFields. */
392
+ refreshed: number;
393
+ /** Notes written into the target. 0 on a dry run. */
394
+ migrated: number;
395
+ sourceDim: number | null;
396
+ targetDim: number;
397
+ model: string;
398
+ dryRun: boolean;
399
+ }
400
+ declare function migrateCollection(opts: {
401
+ /** Source collection. Read-only; never modified. */
402
+ from: string;
403
+ /** Target collection. Created if absent; must not already hold points. */
404
+ to: string;
405
+ /** Re-extract keywords/tags/context for notes that never had them. Default true. */
406
+ refreshFields?: boolean;
407
+ /** Report what would happen and write nothing. Default TRUE — opt in to writing. */
408
+ dryRun?: boolean;
409
+ logger?: {
410
+ info: (m: string) => void;
411
+ warn: (m: string) => void;
412
+ };
413
+ }): Promise<MigrateResult>;
414
+
415
+ /**
416
+ * Similarity floor for accepting an UPDATE target.
417
+ *
418
+ * Heuristic, not empirically tuned: it sits just above the 0.3 bar the engine
419
+ * already uses for "these two notes are related at all", because a legitimate
420
+ * CRUD UPDATE is often a correction or contradiction ("drinks tea" → "switched to
421
+ * coffee") that is related but not near-identical. Set it too high and real
422
+ * corrections get downgraded; too low and the guard does nothing.
423
+ *
424
+ * Failing this check is SAFE by construction — the caller inserts the fact as a
425
+ * new memory instead of overwriting, and scheduled consolidation can merge later.
426
+ * So the cost of a false positive is a duplicate, and the cost of a false
427
+ * negative is a destroyed memory. Bias accordingly: raise it for cheaper models.
428
+ */
429
+ declare const DEFAULT_CRUD_UPDATE_MIN_SIM = 0.35;
430
+ /** Resolve the threshold: env var wins, then an explicit override, then default. */
431
+ declare function resolveCrudUpdateMinSim(override?: number): number;
432
+ /**
433
+ * May `newEmbedding`'s fact overwrite the memory `targetEmbedding` belongs to?
434
+ *
435
+ * True when the replacement is plausibly about the same thing. False means the
436
+ * LLM most likely named the wrong index — the caller should insert instead of
437
+ * overwrite, never throw.
438
+ *
439
+ * Both vectors are L2-normalized by `encode`, so this is a dot product.
440
+ */
441
+ declare function isPlausibleUpdateTarget(newEmbedding: number[], targetEmbedding: number[], minSimilarity?: number): boolean;
442
+
443
+ /**
444
+ * llm.ts — LLM helpers for A-MEM note construction, linking, evolution
445
+ */
446
+ /**
447
+ * Which tier a call runs on (Story 42).
448
+ *
449
+ * The engine's calls split cleanly by how much model capability they actually
450
+ * need. Published results are consistent that memory quality is mostly
451
+ * architecture-bound — extraction differs ~2 points between a cheap and a strong
452
+ * model — with ONE exception: judging whether a new fact CONTRADICTS a stored
453
+ * one, where the gap is large. So the frequent, easy calls run `fast`, and the
454
+ * rare, genuinely hard judgements can run `strong` if the operator configures
455
+ * one. See docs/guide/design-rationale.md for the evidence.
456
+ */
457
+ type LlmRole = 'fast' | 'strong';
458
+ /** Provider/model/endpoint for one role. */
459
+ interface LlmRoleConfig {
460
+ provider?: string;
461
+ model?: string;
462
+ baseURL?: string;
463
+ }
464
+ /** Runtime LLM settings a host may inject. See the precedence note above. */
465
+ interface LlmConfig extends LlmRoleConfig {
466
+ /** Per-request timeout in ms for the SDK client. Guards against a slow or
467
+ * stuck endpoint hanging the whole addMemory pipeline. Default 30000.
468
+ * Shared by both roles — it is a transport concern, not a tier one. */
469
+ timeoutMs?: number;
470
+ /**
471
+ * Optional `strong` tier. Each field falls back to the `fast` value
472
+ * INDIVIDUALLY, so all three useful shapes work:
473
+ * - only `model` → same endpoint, better model (gpt-4o-mini → gpt-4o)
474
+ * - all three → a wholly separate backend (local Ollama + cloud Claude)
475
+ * - nothing → strong IS fast, i.e. today's single-model behaviour
476
+ * There is deliberately no built-in strong default: inventing one would start
477
+ * spending an existing user's money on a pricier model without them asking.
478
+ */
479
+ strong?: LlmRoleConfig;
480
+ /** Which role the agent_end CRUD decision uses. Default `fast`. */
481
+ crudRole?: LlmRole;
482
+ }
483
+ /**
484
+ * Point the engine's LLM calls at a provider/model/endpoint chosen by the host.
485
+ *
486
+ * Environment variables still win over anything passed here, and any field left
487
+ * undefined falls through to the default — so `configureLlm({ model })` changes
488
+ * only the model. Safe to call before or after the first LLM call.
489
+ */
490
+ declare function configureLlm(cfg: LlmConfig): void;
491
+ interface MemoryOperation {
492
+ action: 'NEW' | 'UPDATE' | 'DELETE' | 'NONE';
493
+ fact: string;
494
+ existingIdx?: number;
495
+ reason?: string;
496
+ }
497
+ declare function llmCrudDecision(userText: string, assistantText: string, existingMemories: Array<{
498
+ idx: number;
499
+ content: string;
500
+ }>): Promise<MemoryOperation[]>;
501
+
502
+ export { type AgentAmemConfig, type AmemPluginConfig, type ConflictMode, type ConflictSweepResult, DEFAULT_CRUD_UPDATE_MIN_SIM, DEFAULT_EMBEDDING_MODEL, EmbeddingDimensionMismatchError, type EvolutionEntry, type LlmConfig, type LowQualityItem, type LowQualityReason, type MemoryNote, type MemoryOperation, type MigrateResult, type QueryResult, type SearchResult, type StorageContext, addEpisodic, addMemory, canRead, canWrite, checkQuality, configure, configureLlm, conflictSweep, consolidateMemories, createStorageContext, deleteNote, encode, ensureCollection, generateReviewBatch, getEmbeddingDim, getEmbeddingModel, getNote, invalidateNote, isModelLoaded, isPlausibleUpdateTarget, listMemories, listNotes, llmCrudDecision, loadModel, mergeSimilarNotes, migrateCollection, patchNotePayload, pingQdrant, resolveCrudUpdateMinSim, scanLowQuality, searchMemory, updateNote };