@amemhq/core 1.1.0 → 2.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.
- package/README.md +2 -2
- package/dist/{chunk-B2NS7WAM.js → chunk-K6WZTDM7.js} +229 -58
- package/dist/chunk-K6WZTDM7.js.map +1 -0
- package/dist/cli-migrate.cjs +206 -52
- package/dist/cli-migrate.cjs.map +1 -1
- package/dist/cli-migrate.d.cts +21 -7
- package/dist/cli-migrate.d.ts +21 -7
- package/dist/cli-migrate.js +103 -42
- package/dist/cli-migrate.js.map +1 -1
- package/dist/index.cjs +228 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -29
- package/dist/index.d.ts +145 -29
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-B2NS7WAM.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -6,15 +6,37 @@ declare function configure(opts: {
|
|
|
6
6
|
* embedding.ts — Local ONNX embedding via @huggingface/transformers
|
|
7
7
|
* Matches Python: SentenceTransformer.encode(text, normalize_embeddings=True)
|
|
8
8
|
*
|
|
9
|
-
* The model is selectable
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
* The model is selectable, and changing it is a breaking change whenever the
|
|
10
|
+
* dimension differs — Qdrant fixes a collection's vector size at creation. So
|
|
11
|
+
* nothing here decides on its own: a collection that already exists keeps the
|
|
12
|
+
* model it was built with (see `pinEmbeddingModel`), and moving is a deliberate
|
|
13
|
+
* `amem-migrate` run. See docs/reference/embedding-models.md.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* What a fresh install embeds with.
|
|
17
|
+
*
|
|
18
|
+
* Chosen for having no architectural question mark rather than for topping a
|
|
19
|
+
* leaderboard: XLM-RoBERTa, natively supported by Transformers.js, its ONNX
|
|
20
|
+
* maintained by that library's own author. 8192 tokens against the 128 of the
|
|
21
|
+
* model this replaced, which is the whole reason for the change — anything longer
|
|
22
|
+
* than a couple of sentences was being truncated before it reached the vector.
|
|
23
|
+
*/
|
|
24
|
+
declare const DEFAULT_EMBEDDING_MODEL = "Xenova/bge-m3";
|
|
25
|
+
/**
|
|
26
|
+
* The default before 2.0.0. Every store built by an earlier version holds its
|
|
27
|
+
* vectors, and nothing recorded that at the time — `LEGACY_DEFAULT_DIM` is how a
|
|
28
|
+
* collection from back then is recognised.
|
|
29
|
+
*/
|
|
30
|
+
declare const LEGACY_DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
31
|
+
declare const LEGACY_DEFAULT_DIM = 384;
|
|
32
|
+
/**
|
|
33
|
+
* Which model this process embeds with: the env var, else whatever the open
|
|
34
|
+
* collection was built with, else the shipped default.
|
|
35
|
+
*
|
|
36
|
+
* An explicit `AMEM_EMBED_MODEL` outranks the pin on purpose — someone who set it
|
|
37
|
+
* is migrating deliberately, and silently overriding them with the collection's
|
|
38
|
+
* old model would make the setting look broken.
|
|
39
|
+
*/
|
|
18
40
|
declare function getEmbeddingModel(): string;
|
|
19
41
|
/** How token embeddings are collapsed into one sentence vector. */
|
|
20
42
|
type PoolingMode = 'mean' | 'cls';
|
|
@@ -63,8 +85,9 @@ declare function getEmbeddingDtype(): string | undefined;
|
|
|
63
85
|
*/
|
|
64
86
|
declare function getEmbeddingDim(): Promise<number>;
|
|
65
87
|
/**
|
|
66
|
-
* Encode text to a normalized embedding vector. The width is the model's —
|
|
67
|
-
* for the default,
|
|
88
|
+
* Encode text to a normalized embedding vector. The width is the model's — 1024
|
|
89
|
+
* for the default, 384 for the one before it — so nothing here should assume a
|
|
90
|
+
* number.
|
|
68
91
|
* Singleton model, loaded once and reused.
|
|
69
92
|
*/
|
|
70
93
|
declare function encode(text: string): Promise<number[]>;
|
|
@@ -83,7 +106,8 @@ declare function isModelLoaded(): boolean;
|
|
|
83
106
|
/**
|
|
84
107
|
* storage.ts — Qdrant vector storage for A-MEM
|
|
85
108
|
* Uses native fetch (Node 18+) to avoid undici compatibility issues with Node v26
|
|
86
|
-
* Collection: amem_notes,
|
|
109
|
+
* Collection: amem_notes, cosine, width set by the embedding model, with
|
|
110
|
+
* agent_id isolation
|
|
87
111
|
*/
|
|
88
112
|
/** Per-agent override config. If collection is set, mode B (isolated collection) is used. */
|
|
89
113
|
interface AgentAmemConfig {
|
|
@@ -190,6 +214,21 @@ declare class EmbeddingModelMismatchError extends Error {
|
|
|
190
214
|
readonly configuredModel: string;
|
|
191
215
|
constructor(collection: string, collectionModel: string, configuredModel: string);
|
|
192
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Two collections open in one process that need two different models.
|
|
219
|
+
*
|
|
220
|
+
* Only reachable in mode B, and normally only mid-migration: per-agent
|
|
221
|
+
* collections built before 2.0.0 all resolve to the same old model, until one of
|
|
222
|
+
* them is migrated and the others are not. One process embeds with one model, so
|
|
223
|
+
* this has to stop rather than pick a winner — picking would write vectors of the
|
|
224
|
+
* wrong width into whichever collection lost.
|
|
225
|
+
*/
|
|
226
|
+
declare class MixedEmbeddingModelsError extends Error {
|
|
227
|
+
readonly collection: string;
|
|
228
|
+
readonly wanted: string;
|
|
229
|
+
readonly inUse: string;
|
|
230
|
+
constructor(collection: string, wanted: string, inUse: string);
|
|
231
|
+
}
|
|
193
232
|
/**
|
|
194
233
|
* Ask Qdrant whether it can serve, right now.
|
|
195
234
|
*
|
|
@@ -215,28 +254,34 @@ declare function makeCrud(collectionName: string, modeBIsolated?: boolean): {
|
|
|
215
254
|
addNote(note: MemoryNote): Promise<void>;
|
|
216
255
|
/**
|
|
217
256
|
* Story 36: this is the one read that bypasses the agent filter — it fetches
|
|
218
|
-
* straight by UUID.
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
257
|
+
* straight by UUID. An unreadable note comes back as `null`, indistinguishable
|
|
258
|
+
* from missing, so nothing leaks and callers already handle it.
|
|
259
|
+
*
|
|
260
|
+
* `reader` is required. It used to be optional, and omitting it skipped the
|
|
261
|
+
* check — which meant the safe behaviour was the one you had to remember to
|
|
262
|
+
* ask for. Pass `SYSTEM_ACTOR` to read as the engine itself; that reads as a
|
|
263
|
+
* deliberate act at the call site, where an absent argument did not.
|
|
222
264
|
*/
|
|
223
|
-
getNote(id: string,
|
|
265
|
+
getNote(id: string, reader: string): Promise<MemoryNote | null>;
|
|
224
266
|
updateNote(note: MemoryNote): Promise<void>;
|
|
225
267
|
findByHash(hash: string, agentId: string): Promise<MemoryNote | null>;
|
|
226
268
|
/**
|
|
227
|
-
* Story 33:
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
269
|
+
* Story 33: enforces the writers policy. Returns false — without writing —
|
|
270
|
+
* when the caller may not write. This fetch-then-check path exists for callers
|
|
271
|
+
* that only have an id (the plugin's CRUD hook); callers already holding the
|
|
272
|
+
* note can check `canWrite` themselves and skip a round trip.
|
|
273
|
+
*
|
|
274
|
+
* `caller` is required. Pass `SYSTEM_ACTOR` for the engine's own maintenance
|
|
275
|
+
* writes. The self-read below is a genuine `SYSTEM_ACTOR` case: it fetches the
|
|
276
|
+
* note in order to decide whether the caller may write it, and gating that
|
|
277
|
+
* fetch on the same policy it exists to evaluate would be circular.
|
|
233
278
|
*/
|
|
234
|
-
updateNoteContent(id: string, content: string, embedding: number[], hash: string,
|
|
279
|
+
updateNoteContent(id: string, content: string, embedding: number[], hash: string, caller: string): Promise<boolean>;
|
|
235
280
|
queryByEmbedding(embedding: number[], topK: number, agentId: string, scoreThreshold?: number, subject?: string): Promise<QueryResult[]>;
|
|
236
281
|
listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
|
|
237
282
|
deleteNote(id: string): Promise<void>;
|
|
238
283
|
/** Story 33: see `updateNoteContent` — returns false, unwritten, when denied. */
|
|
239
|
-
invalidateNote(id: string,
|
|
284
|
+
invalidateNote(id: string, caller: string): Promise<boolean>;
|
|
240
285
|
getNotesByDatePrefix(datePrefix: string, agentId: string): Promise<MemoryNote[]>;
|
|
241
286
|
countNotes(agentId?: string): Promise<number>;
|
|
242
287
|
updateNoteLinks(id: string, links: string[]): Promise<void>;
|
|
@@ -250,11 +295,11 @@ type StorageContext = ReturnType<typeof makeCrud>;
|
|
|
250
295
|
* Mode B (isolated collection): pass collectionName = 'amem_notes_<agentId>' and modeBIsolated = true.
|
|
251
296
|
*/
|
|
252
297
|
declare function createStorageContext(collectionName?: string, modeBIsolated?: boolean): StorageContext;
|
|
253
|
-
declare function getNote(id: string,
|
|
298
|
+
declare function getNote(id: string, reader: string): Promise<MemoryNote | null>;
|
|
254
299
|
declare function updateNote(note: MemoryNote): Promise<void>;
|
|
255
300
|
declare function listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
|
|
256
301
|
declare function deleteNote(id: string): Promise<void>;
|
|
257
|
-
declare function invalidateNote(id: string,
|
|
302
|
+
declare function invalidateNote(id: string, caller: string): Promise<boolean>;
|
|
258
303
|
declare function patchNotePayload(id: string, fields: Record<string, unknown>): Promise<void>;
|
|
259
304
|
|
|
260
305
|
/**
|
|
@@ -304,8 +349,28 @@ interface SearchResult {
|
|
|
304
349
|
keywords: string[];
|
|
305
350
|
links: string[];
|
|
306
351
|
timestamp: string;
|
|
352
|
+
/**
|
|
353
|
+
* Cosine similarity to the query. **Not** what ordered this list — that is
|
|
354
|
+
* `rrf`, which fuses the dense and BM25 rankings and then applies a heat/recency
|
|
355
|
+
* boost. The two disagree often, and a consumer that reads `similarity` as the
|
|
356
|
+
* ranking score concludes the ranking is broken.
|
|
357
|
+
*/
|
|
307
358
|
similarity: number;
|
|
359
|
+
/**
|
|
360
|
+
* The fused score the matches are sorted by, and 0 for anything no retriever
|
|
361
|
+
* ranked. It is `via`, not this, that says why a row is here.
|
|
362
|
+
*/
|
|
308
363
|
rrf: number;
|
|
364
|
+
/**
|
|
365
|
+
* Why this note is in the results.
|
|
366
|
+
*
|
|
367
|
+
* `match` — it was retrieved for the query and ranked by `rrf`.
|
|
368
|
+
* `link` — it was **not** retrieved; it is here because it links to one that
|
|
369
|
+
* was, within two hops and above the relevance gate. These are appended in
|
|
370
|
+
* discovery order after the matches and have no `rrf` of their own, so reading
|
|
371
|
+
* the tail of the list as "lower-ranked matches" is wrong.
|
|
372
|
+
*/
|
|
373
|
+
via: 'match' | 'link';
|
|
309
374
|
topics: string[];
|
|
310
375
|
note_type: 'memory' | 'knowledge';
|
|
311
376
|
}
|
|
@@ -411,6 +476,28 @@ declare function generateReviewBatch(agentId: string, outputPath?: string): Prom
|
|
|
411
476
|
* policy is unit-testable on its own and identical everywhere it is applied.
|
|
412
477
|
*/
|
|
413
478
|
|
|
479
|
+
/**
|
|
480
|
+
* The engine acting as itself, rather than on behalf of any agent.
|
|
481
|
+
*
|
|
482
|
+
* `getNote`, `updateNoteContent` and `invalidateNote` all used to take an
|
|
483
|
+
* *optional* identity, and omitting it skipped the authorization check. That put
|
|
484
|
+
* the safe behaviour behind remembering to ask for it, and made "no check here"
|
|
485
|
+
* invisible — an absent argument looks the same as an oversight. The identity is
|
|
486
|
+
* now required, and this is what a call declares when it genuinely has no agent
|
|
487
|
+
* on whose behalf it acts.
|
|
488
|
+
*
|
|
489
|
+
* Two call sites use it, both inside storage.ts: the fetch that
|
|
490
|
+
* `updateNoteContent` and `invalidateNote` perform in order to *evaluate* the
|
|
491
|
+
* write policy. Gating that read on the policy it exists to check would be
|
|
492
|
+
* circular. Everything else passes a real agent id — the audit that prompted this
|
|
493
|
+
* found the identity was already in scope at every one of them.
|
|
494
|
+
*
|
|
495
|
+
* The prefix keeps it from colliding with any plausible agent id. It is not a
|
|
496
|
+
* secret and does not need to be: amem is self-hosted, the operator owns every
|
|
497
|
+
* memory in the store, and there is no privilege boundary here to defend. This
|
|
498
|
+
* guards against a call site forgetting to pass an identity, not against a user.
|
|
499
|
+
*/
|
|
500
|
+
declare const SYSTEM_ACTOR = "__amem_system__";
|
|
414
501
|
/**
|
|
415
502
|
* May `callerAgentId` mutate `note`?
|
|
416
503
|
*
|
|
@@ -440,8 +527,10 @@ interface MigrateResult {
|
|
|
440
527
|
missingDerived: number;
|
|
441
528
|
/** Notes whose fields were re-extracted. 0 unless refreshFields. */
|
|
442
529
|
refreshed: number;
|
|
443
|
-
/** Notes written into the target. 0 on a dry run. */
|
|
530
|
+
/** Notes written into the target by THIS run. 0 on a dry run. */
|
|
444
531
|
migrated: number;
|
|
532
|
+
/** Notes a previous interrupted run had already written. */
|
|
533
|
+
skipped: number;
|
|
445
534
|
sourceDim: number | null;
|
|
446
535
|
targetDim: number;
|
|
447
536
|
model: string;
|
|
@@ -461,6 +550,33 @@ declare function migrateCollection(opts: {
|
|
|
461
550
|
warn: (m: string) => void;
|
|
462
551
|
};
|
|
463
552
|
}): Promise<MigrateResult>;
|
|
553
|
+
/**
|
|
554
|
+
* Put the migrated collection behind the name the source used, and drop the
|
|
555
|
+
* source.
|
|
556
|
+
*
|
|
557
|
+
* This is the only irreversible step in the whole migration, which is why it is
|
|
558
|
+
* a separate call rather than the tail of `migrateCollection`. Everything before
|
|
559
|
+
* it leaves the original untouched and can simply be abandoned.
|
|
560
|
+
*
|
|
561
|
+
* Qdrant cannot rename a collection and cannot create an alias over a name a real
|
|
562
|
+
* collection holds (409), so freeing the name means deleting it — after checking
|
|
563
|
+
* the target holds at least as much as the source, because that check is the last
|
|
564
|
+
* thing standing between a half-finished migration and a deleted store.
|
|
565
|
+
*/
|
|
566
|
+
declare function switchToMigrated(opts: {
|
|
567
|
+
/** The name readers are configured with. Becomes an alias. */
|
|
568
|
+
name: string;
|
|
569
|
+
/** The collection built by `migrateCollection`. */
|
|
570
|
+
to: string;
|
|
571
|
+
logger?: {
|
|
572
|
+
info: (m: string) => void;
|
|
573
|
+
warn: (m: string) => void;
|
|
574
|
+
};
|
|
575
|
+
}): Promise<{
|
|
576
|
+
name: string;
|
|
577
|
+
to: string;
|
|
578
|
+
moved: number;
|
|
579
|
+
}>;
|
|
464
580
|
|
|
465
581
|
/**
|
|
466
582
|
* Similarity floor for accepting an UPDATE target.
|
|
@@ -549,4 +665,4 @@ declare function llmCrudDecision(userText: string, assistantText: string, existi
|
|
|
549
665
|
content: string;
|
|
550
666
|
}>): Promise<MemoryOperation[]>;
|
|
551
667
|
|
|
552
|
-
export { type AgentAmemConfig, type AmemPluginConfig, type ConflictMode, type ConflictSweepResult, DEFAULT_CRUD_UPDATE_MIN_SIM, DEFAULT_EMBEDDING_MODEL, EmbeddingDimensionMismatchError, EmbeddingModelMismatchError, type EvolutionEntry, type LlmConfig, type LowQualityItem, type LowQualityReason, type MemoryNote, type MemoryOperation, type MigrateResult, type PoolingMode, type QueryResult, type SearchResult, type StorageContext, addEpisodic, addMemory, canRead, canWrite, checkQuality, configure, configureLlm, conflictSweep, consolidateMemories, createStorageContext, deleteNote, encode, ensureCollection, generateReviewBatch, getEmbeddingDevice, getEmbeddingDim, getEmbeddingDtype, getEmbeddingModel, getEmbeddingPooling, getNote, invalidateNote, isModelLoaded, isPlausibleUpdateTarget, listMemories, listNotes, llmCrudDecision, loadModel, mergeSimilarNotes, migrateCollection, patchNotePayload, pingQdrant, resolveCrudUpdateMinSim, scanLowQuality, searchMemory, updateNote };
|
|
668
|
+
export { type AgentAmemConfig, type AmemPluginConfig, type ConflictMode, type ConflictSweepResult, DEFAULT_CRUD_UPDATE_MIN_SIM, DEFAULT_EMBEDDING_MODEL, EmbeddingDimensionMismatchError, EmbeddingModelMismatchError, type EvolutionEntry, LEGACY_DEFAULT_DIM, LEGACY_DEFAULT_EMBEDDING_MODEL, type LlmConfig, type LowQualityItem, type LowQualityReason, type MemoryNote, type MemoryOperation, type MigrateResult, MixedEmbeddingModelsError, type PoolingMode, type QueryResult, SYSTEM_ACTOR, type SearchResult, type StorageContext, addEpisodic, addMemory, canRead, canWrite, checkQuality, configure, configureLlm, conflictSweep, consolidateMemories, createStorageContext, deleteNote, encode, ensureCollection, generateReviewBatch, getEmbeddingDevice, getEmbeddingDim, getEmbeddingDtype, getEmbeddingModel, getEmbeddingPooling, getNote, invalidateNote, isModelLoaded, isPlausibleUpdateTarget, listMemories, listNotes, llmCrudDecision, loadModel, mergeSimilarNotes, migrateCollection, patchNotePayload, pingQdrant, resolveCrudUpdateMinSim, scanLowQuality, searchMemory, switchToMigrated, updateNote };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,15 +6,37 @@ declare function configure(opts: {
|
|
|
6
6
|
* embedding.ts — Local ONNX embedding via @huggingface/transformers
|
|
7
7
|
* Matches Python: SentenceTransformer.encode(text, normalize_embeddings=True)
|
|
8
8
|
*
|
|
9
|
-
* The model is selectable
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
* The model is selectable, and changing it is a breaking change whenever the
|
|
10
|
+
* dimension differs — Qdrant fixes a collection's vector size at creation. So
|
|
11
|
+
* nothing here decides on its own: a collection that already exists keeps the
|
|
12
|
+
* model it was built with (see `pinEmbeddingModel`), and moving is a deliberate
|
|
13
|
+
* `amem-migrate` run. See docs/reference/embedding-models.md.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* What a fresh install embeds with.
|
|
17
|
+
*
|
|
18
|
+
* Chosen for having no architectural question mark rather than for topping a
|
|
19
|
+
* leaderboard: XLM-RoBERTa, natively supported by Transformers.js, its ONNX
|
|
20
|
+
* maintained by that library's own author. 8192 tokens against the 128 of the
|
|
21
|
+
* model this replaced, which is the whole reason for the change — anything longer
|
|
22
|
+
* than a couple of sentences was being truncated before it reached the vector.
|
|
23
|
+
*/
|
|
24
|
+
declare const DEFAULT_EMBEDDING_MODEL = "Xenova/bge-m3";
|
|
25
|
+
/**
|
|
26
|
+
* The default before 2.0.0. Every store built by an earlier version holds its
|
|
27
|
+
* vectors, and nothing recorded that at the time — `LEGACY_DEFAULT_DIM` is how a
|
|
28
|
+
* collection from back then is recognised.
|
|
29
|
+
*/
|
|
30
|
+
declare const LEGACY_DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
31
|
+
declare const LEGACY_DEFAULT_DIM = 384;
|
|
32
|
+
/**
|
|
33
|
+
* Which model this process embeds with: the env var, else whatever the open
|
|
34
|
+
* collection was built with, else the shipped default.
|
|
35
|
+
*
|
|
36
|
+
* An explicit `AMEM_EMBED_MODEL` outranks the pin on purpose — someone who set it
|
|
37
|
+
* is migrating deliberately, and silently overriding them with the collection's
|
|
38
|
+
* old model would make the setting look broken.
|
|
39
|
+
*/
|
|
18
40
|
declare function getEmbeddingModel(): string;
|
|
19
41
|
/** How token embeddings are collapsed into one sentence vector. */
|
|
20
42
|
type PoolingMode = 'mean' | 'cls';
|
|
@@ -63,8 +85,9 @@ declare function getEmbeddingDtype(): string | undefined;
|
|
|
63
85
|
*/
|
|
64
86
|
declare function getEmbeddingDim(): Promise<number>;
|
|
65
87
|
/**
|
|
66
|
-
* Encode text to a normalized embedding vector. The width is the model's —
|
|
67
|
-
* for the default,
|
|
88
|
+
* Encode text to a normalized embedding vector. The width is the model's — 1024
|
|
89
|
+
* for the default, 384 for the one before it — so nothing here should assume a
|
|
90
|
+
* number.
|
|
68
91
|
* Singleton model, loaded once and reused.
|
|
69
92
|
*/
|
|
70
93
|
declare function encode(text: string): Promise<number[]>;
|
|
@@ -83,7 +106,8 @@ declare function isModelLoaded(): boolean;
|
|
|
83
106
|
/**
|
|
84
107
|
* storage.ts — Qdrant vector storage for A-MEM
|
|
85
108
|
* Uses native fetch (Node 18+) to avoid undici compatibility issues with Node v26
|
|
86
|
-
* Collection: amem_notes,
|
|
109
|
+
* Collection: amem_notes, cosine, width set by the embedding model, with
|
|
110
|
+
* agent_id isolation
|
|
87
111
|
*/
|
|
88
112
|
/** Per-agent override config. If collection is set, mode B (isolated collection) is used. */
|
|
89
113
|
interface AgentAmemConfig {
|
|
@@ -190,6 +214,21 @@ declare class EmbeddingModelMismatchError extends Error {
|
|
|
190
214
|
readonly configuredModel: string;
|
|
191
215
|
constructor(collection: string, collectionModel: string, configuredModel: string);
|
|
192
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Two collections open in one process that need two different models.
|
|
219
|
+
*
|
|
220
|
+
* Only reachable in mode B, and normally only mid-migration: per-agent
|
|
221
|
+
* collections built before 2.0.0 all resolve to the same old model, until one of
|
|
222
|
+
* them is migrated and the others are not. One process embeds with one model, so
|
|
223
|
+
* this has to stop rather than pick a winner — picking would write vectors of the
|
|
224
|
+
* wrong width into whichever collection lost.
|
|
225
|
+
*/
|
|
226
|
+
declare class MixedEmbeddingModelsError extends Error {
|
|
227
|
+
readonly collection: string;
|
|
228
|
+
readonly wanted: string;
|
|
229
|
+
readonly inUse: string;
|
|
230
|
+
constructor(collection: string, wanted: string, inUse: string);
|
|
231
|
+
}
|
|
193
232
|
/**
|
|
194
233
|
* Ask Qdrant whether it can serve, right now.
|
|
195
234
|
*
|
|
@@ -215,28 +254,34 @@ declare function makeCrud(collectionName: string, modeBIsolated?: boolean): {
|
|
|
215
254
|
addNote(note: MemoryNote): Promise<void>;
|
|
216
255
|
/**
|
|
217
256
|
* Story 36: this is the one read that bypasses the agent filter — it fetches
|
|
218
|
-
* straight by UUID.
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
257
|
+
* straight by UUID. An unreadable note comes back as `null`, indistinguishable
|
|
258
|
+
* from missing, so nothing leaks and callers already handle it.
|
|
259
|
+
*
|
|
260
|
+
* `reader` is required. It used to be optional, and omitting it skipped the
|
|
261
|
+
* check — which meant the safe behaviour was the one you had to remember to
|
|
262
|
+
* ask for. Pass `SYSTEM_ACTOR` to read as the engine itself; that reads as a
|
|
263
|
+
* deliberate act at the call site, where an absent argument did not.
|
|
222
264
|
*/
|
|
223
|
-
getNote(id: string,
|
|
265
|
+
getNote(id: string, reader: string): Promise<MemoryNote | null>;
|
|
224
266
|
updateNote(note: MemoryNote): Promise<void>;
|
|
225
267
|
findByHash(hash: string, agentId: string): Promise<MemoryNote | null>;
|
|
226
268
|
/**
|
|
227
|
-
* Story 33:
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
269
|
+
* Story 33: enforces the writers policy. Returns false — without writing —
|
|
270
|
+
* when the caller may not write. This fetch-then-check path exists for callers
|
|
271
|
+
* that only have an id (the plugin's CRUD hook); callers already holding the
|
|
272
|
+
* note can check `canWrite` themselves and skip a round trip.
|
|
273
|
+
*
|
|
274
|
+
* `caller` is required. Pass `SYSTEM_ACTOR` for the engine's own maintenance
|
|
275
|
+
* writes. The self-read below is a genuine `SYSTEM_ACTOR` case: it fetches the
|
|
276
|
+
* note in order to decide whether the caller may write it, and gating that
|
|
277
|
+
* fetch on the same policy it exists to evaluate would be circular.
|
|
233
278
|
*/
|
|
234
|
-
updateNoteContent(id: string, content: string, embedding: number[], hash: string,
|
|
279
|
+
updateNoteContent(id: string, content: string, embedding: number[], hash: string, caller: string): Promise<boolean>;
|
|
235
280
|
queryByEmbedding(embedding: number[], topK: number, agentId: string, scoreThreshold?: number, subject?: string): Promise<QueryResult[]>;
|
|
236
281
|
listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
|
|
237
282
|
deleteNote(id: string): Promise<void>;
|
|
238
283
|
/** Story 33: see `updateNoteContent` — returns false, unwritten, when denied. */
|
|
239
|
-
invalidateNote(id: string,
|
|
284
|
+
invalidateNote(id: string, caller: string): Promise<boolean>;
|
|
240
285
|
getNotesByDatePrefix(datePrefix: string, agentId: string): Promise<MemoryNote[]>;
|
|
241
286
|
countNotes(agentId?: string): Promise<number>;
|
|
242
287
|
updateNoteLinks(id: string, links: string[]): Promise<void>;
|
|
@@ -250,11 +295,11 @@ type StorageContext = ReturnType<typeof makeCrud>;
|
|
|
250
295
|
* Mode B (isolated collection): pass collectionName = 'amem_notes_<agentId>' and modeBIsolated = true.
|
|
251
296
|
*/
|
|
252
297
|
declare function createStorageContext(collectionName?: string, modeBIsolated?: boolean): StorageContext;
|
|
253
|
-
declare function getNote(id: string,
|
|
298
|
+
declare function getNote(id: string, reader: string): Promise<MemoryNote | null>;
|
|
254
299
|
declare function updateNote(note: MemoryNote): Promise<void>;
|
|
255
300
|
declare function listNotes(agentId?: string, subject?: string): Promise<MemoryNote[]>;
|
|
256
301
|
declare function deleteNote(id: string): Promise<void>;
|
|
257
|
-
declare function invalidateNote(id: string,
|
|
302
|
+
declare function invalidateNote(id: string, caller: string): Promise<boolean>;
|
|
258
303
|
declare function patchNotePayload(id: string, fields: Record<string, unknown>): Promise<void>;
|
|
259
304
|
|
|
260
305
|
/**
|
|
@@ -304,8 +349,28 @@ interface SearchResult {
|
|
|
304
349
|
keywords: string[];
|
|
305
350
|
links: string[];
|
|
306
351
|
timestamp: string;
|
|
352
|
+
/**
|
|
353
|
+
* Cosine similarity to the query. **Not** what ordered this list — that is
|
|
354
|
+
* `rrf`, which fuses the dense and BM25 rankings and then applies a heat/recency
|
|
355
|
+
* boost. The two disagree often, and a consumer that reads `similarity` as the
|
|
356
|
+
* ranking score concludes the ranking is broken.
|
|
357
|
+
*/
|
|
307
358
|
similarity: number;
|
|
359
|
+
/**
|
|
360
|
+
* The fused score the matches are sorted by, and 0 for anything no retriever
|
|
361
|
+
* ranked. It is `via`, not this, that says why a row is here.
|
|
362
|
+
*/
|
|
308
363
|
rrf: number;
|
|
364
|
+
/**
|
|
365
|
+
* Why this note is in the results.
|
|
366
|
+
*
|
|
367
|
+
* `match` — it was retrieved for the query and ranked by `rrf`.
|
|
368
|
+
* `link` — it was **not** retrieved; it is here because it links to one that
|
|
369
|
+
* was, within two hops and above the relevance gate. These are appended in
|
|
370
|
+
* discovery order after the matches and have no `rrf` of their own, so reading
|
|
371
|
+
* the tail of the list as "lower-ranked matches" is wrong.
|
|
372
|
+
*/
|
|
373
|
+
via: 'match' | 'link';
|
|
309
374
|
topics: string[];
|
|
310
375
|
note_type: 'memory' | 'knowledge';
|
|
311
376
|
}
|
|
@@ -411,6 +476,28 @@ declare function generateReviewBatch(agentId: string, outputPath?: string): Prom
|
|
|
411
476
|
* policy is unit-testable on its own and identical everywhere it is applied.
|
|
412
477
|
*/
|
|
413
478
|
|
|
479
|
+
/**
|
|
480
|
+
* The engine acting as itself, rather than on behalf of any agent.
|
|
481
|
+
*
|
|
482
|
+
* `getNote`, `updateNoteContent` and `invalidateNote` all used to take an
|
|
483
|
+
* *optional* identity, and omitting it skipped the authorization check. That put
|
|
484
|
+
* the safe behaviour behind remembering to ask for it, and made "no check here"
|
|
485
|
+
* invisible — an absent argument looks the same as an oversight. The identity is
|
|
486
|
+
* now required, and this is what a call declares when it genuinely has no agent
|
|
487
|
+
* on whose behalf it acts.
|
|
488
|
+
*
|
|
489
|
+
* Two call sites use it, both inside storage.ts: the fetch that
|
|
490
|
+
* `updateNoteContent` and `invalidateNote` perform in order to *evaluate* the
|
|
491
|
+
* write policy. Gating that read on the policy it exists to check would be
|
|
492
|
+
* circular. Everything else passes a real agent id — the audit that prompted this
|
|
493
|
+
* found the identity was already in scope at every one of them.
|
|
494
|
+
*
|
|
495
|
+
* The prefix keeps it from colliding with any plausible agent id. It is not a
|
|
496
|
+
* secret and does not need to be: amem is self-hosted, the operator owns every
|
|
497
|
+
* memory in the store, and there is no privilege boundary here to defend. This
|
|
498
|
+
* guards against a call site forgetting to pass an identity, not against a user.
|
|
499
|
+
*/
|
|
500
|
+
declare const SYSTEM_ACTOR = "__amem_system__";
|
|
414
501
|
/**
|
|
415
502
|
* May `callerAgentId` mutate `note`?
|
|
416
503
|
*
|
|
@@ -440,8 +527,10 @@ interface MigrateResult {
|
|
|
440
527
|
missingDerived: number;
|
|
441
528
|
/** Notes whose fields were re-extracted. 0 unless refreshFields. */
|
|
442
529
|
refreshed: number;
|
|
443
|
-
/** Notes written into the target. 0 on a dry run. */
|
|
530
|
+
/** Notes written into the target by THIS run. 0 on a dry run. */
|
|
444
531
|
migrated: number;
|
|
532
|
+
/** Notes a previous interrupted run had already written. */
|
|
533
|
+
skipped: number;
|
|
445
534
|
sourceDim: number | null;
|
|
446
535
|
targetDim: number;
|
|
447
536
|
model: string;
|
|
@@ -461,6 +550,33 @@ declare function migrateCollection(opts: {
|
|
|
461
550
|
warn: (m: string) => void;
|
|
462
551
|
};
|
|
463
552
|
}): Promise<MigrateResult>;
|
|
553
|
+
/**
|
|
554
|
+
* Put the migrated collection behind the name the source used, and drop the
|
|
555
|
+
* source.
|
|
556
|
+
*
|
|
557
|
+
* This is the only irreversible step in the whole migration, which is why it is
|
|
558
|
+
* a separate call rather than the tail of `migrateCollection`. Everything before
|
|
559
|
+
* it leaves the original untouched and can simply be abandoned.
|
|
560
|
+
*
|
|
561
|
+
* Qdrant cannot rename a collection and cannot create an alias over a name a real
|
|
562
|
+
* collection holds (409), so freeing the name means deleting it — after checking
|
|
563
|
+
* the target holds at least as much as the source, because that check is the last
|
|
564
|
+
* thing standing between a half-finished migration and a deleted store.
|
|
565
|
+
*/
|
|
566
|
+
declare function switchToMigrated(opts: {
|
|
567
|
+
/** The name readers are configured with. Becomes an alias. */
|
|
568
|
+
name: string;
|
|
569
|
+
/** The collection built by `migrateCollection`. */
|
|
570
|
+
to: string;
|
|
571
|
+
logger?: {
|
|
572
|
+
info: (m: string) => void;
|
|
573
|
+
warn: (m: string) => void;
|
|
574
|
+
};
|
|
575
|
+
}): Promise<{
|
|
576
|
+
name: string;
|
|
577
|
+
to: string;
|
|
578
|
+
moved: number;
|
|
579
|
+
}>;
|
|
464
580
|
|
|
465
581
|
/**
|
|
466
582
|
* Similarity floor for accepting an UPDATE target.
|
|
@@ -549,4 +665,4 @@ declare function llmCrudDecision(userText: string, assistantText: string, existi
|
|
|
549
665
|
content: string;
|
|
550
666
|
}>): Promise<MemoryOperation[]>;
|
|
551
667
|
|
|
552
|
-
export { type AgentAmemConfig, type AmemPluginConfig, type ConflictMode, type ConflictSweepResult, DEFAULT_CRUD_UPDATE_MIN_SIM, DEFAULT_EMBEDDING_MODEL, EmbeddingDimensionMismatchError, EmbeddingModelMismatchError, type EvolutionEntry, type LlmConfig, type LowQualityItem, type LowQualityReason, type MemoryNote, type MemoryOperation, type MigrateResult, type PoolingMode, type QueryResult, type SearchResult, type StorageContext, addEpisodic, addMemory, canRead, canWrite, checkQuality, configure, configureLlm, conflictSweep, consolidateMemories, createStorageContext, deleteNote, encode, ensureCollection, generateReviewBatch, getEmbeddingDevice, getEmbeddingDim, getEmbeddingDtype, getEmbeddingModel, getEmbeddingPooling, getNote, invalidateNote, isModelLoaded, isPlausibleUpdateTarget, listMemories, listNotes, llmCrudDecision, loadModel, mergeSimilarNotes, migrateCollection, patchNotePayload, pingQdrant, resolveCrudUpdateMinSim, scanLowQuality, searchMemory, updateNote };
|
|
668
|
+
export { type AgentAmemConfig, type AmemPluginConfig, type ConflictMode, type ConflictSweepResult, DEFAULT_CRUD_UPDATE_MIN_SIM, DEFAULT_EMBEDDING_MODEL, EmbeddingDimensionMismatchError, EmbeddingModelMismatchError, type EvolutionEntry, LEGACY_DEFAULT_DIM, LEGACY_DEFAULT_EMBEDDING_MODEL, type LlmConfig, type LowQualityItem, type LowQualityReason, type MemoryNote, type MemoryOperation, type MigrateResult, MixedEmbeddingModelsError, type PoolingMode, type QueryResult, SYSTEM_ACTOR, type SearchResult, type StorageContext, addEpisodic, addMemory, canRead, canWrite, checkQuality, configure, configureLlm, conflictSweep, consolidateMemories, createStorageContext, deleteNote, encode, ensureCollection, generateReviewBatch, getEmbeddingDevice, getEmbeddingDim, getEmbeddingDtype, getEmbeddingModel, getEmbeddingPooling, getNote, invalidateNote, isModelLoaded, isPlausibleUpdateTarget, listMemories, listNotes, llmCrudDecision, loadModel, mergeSimilarNotes, migrateCollection, patchNotePayload, pingQdrant, resolveCrudUpdateMinSim, scanLowQuality, searchMemory, switchToMigrated, updateNote };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,10 @@ import {
|
|
|
2
2
|
DEFAULT_EMBEDDING_MODEL,
|
|
3
3
|
EmbeddingDimensionMismatchError,
|
|
4
4
|
EmbeddingModelMismatchError,
|
|
5
|
+
LEGACY_DEFAULT_DIM,
|
|
6
|
+
LEGACY_DEFAULT_EMBEDDING_MODEL,
|
|
7
|
+
MixedEmbeddingModelsError,
|
|
8
|
+
SYSTEM_ACTOR,
|
|
5
9
|
addEpisodic,
|
|
6
10
|
addMemory,
|
|
7
11
|
canRead,
|
|
@@ -33,8 +37,9 @@ import {
|
|
|
33
37
|
patchNotePayload,
|
|
34
38
|
pingQdrant,
|
|
35
39
|
searchMemory,
|
|
40
|
+
switchToMigrated,
|
|
36
41
|
updateNote
|
|
37
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-K6WZTDM7.js";
|
|
38
43
|
|
|
39
44
|
// src/quality.ts
|
|
40
45
|
import * as fs from "fs";
|
|
@@ -236,6 +241,10 @@ export {
|
|
|
236
241
|
DEFAULT_EMBEDDING_MODEL,
|
|
237
242
|
EmbeddingDimensionMismatchError,
|
|
238
243
|
EmbeddingModelMismatchError,
|
|
244
|
+
LEGACY_DEFAULT_DIM,
|
|
245
|
+
LEGACY_DEFAULT_EMBEDDING_MODEL,
|
|
246
|
+
MixedEmbeddingModelsError,
|
|
247
|
+
SYSTEM_ACTOR,
|
|
239
248
|
addEpisodic,
|
|
240
249
|
addMemory,
|
|
241
250
|
canRead,
|
|
@@ -270,6 +279,7 @@ export {
|
|
|
270
279
|
resolveCrudUpdateMinSim,
|
|
271
280
|
scanLowQuality,
|
|
272
281
|
searchMemory,
|
|
282
|
+
switchToMigrated,
|
|
273
283
|
updateNote
|
|
274
284
|
};
|
|
275
285
|
//# sourceMappingURL=index.js.map
|