memories-lite 0.99.3 → 0.99.5

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.
@@ -33,6 +33,13 @@ export declare class MemoriesLite {
33
33
  capture(messages: string | Message[], userId: string, config: AddMemoryOptions): Promise<SearchResult>;
34
34
  get(memoryId: string, userId: string): Promise<MemoryItem | null>;
35
35
  retrieve(query: string, userId: string, config: SearchMemoryOptions): Promise<SearchResult>;
36
+ /**
37
+ * Met à jour une mémoire existante
38
+ * @param memoryId - ID de la mémoire
39
+ * @param data - Nouveau contenu
40
+ * @param userId - ID utilisateur
41
+ * @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
42
+ */
36
43
  update(memoryId: string, data: string, userId: string, metadata?: Record<string, any>): Promise<{
37
44
  message: string;
38
45
  }>;
@@ -132,11 +132,14 @@ class MemoriesLite {
132
132
  const embedding = await this.embedder.embed(title);
133
133
  //
134
134
  // Préparer les métadonnées
135
+ // applyMode par défaut = MEM_SMART (recherche sémantique)
135
136
  const memoryType = metadata.type || 'discussion';
137
+ const applyMode = metadata.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE;
136
138
  const memoryMetadata = {
137
139
  ...metadata,
138
140
  title,
139
141
  type: memoryType,
142
+ applyMode,
140
143
  userId,
141
144
  };
142
145
  //
@@ -203,6 +206,7 @@ class MemoriesLite {
203
206
  id: memory.id,
204
207
  memory: memory.payload.data,
205
208
  type: memory.payload.type,
209
+ applyMode: memory.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
206
210
  hash: memory.payload.hash,
207
211
  createdAt: memory.payload.createdAt,
208
212
  updatedAt: memory.payload.updatedAt,
@@ -252,12 +256,21 @@ class MemoriesLite {
252
256
  "data",
253
257
  "createdAt",
254
258
  "updatedAt",
259
+ "applyMode",
255
260
  ]);
256
- const results = memories.map((mem) => ({
261
+ //
262
+ // Filtrer uniquement les mémoires avec applyMode = MEM_SMART (recherche sémantique)
263
+ // Les MEM_ALWAYS et MEM_MANUAL ne sont pas retournées par retrieve()
264
+ const filteredMemories = memories.filter((mem) => {
265
+ const mode = mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE;
266
+ return mode === 'MEM_SMART';
267
+ });
268
+ const results = filteredMemories.map((mem) => ({
257
269
  id: mem.id,
258
270
  memory: mem.payload.data,
259
271
  hash: mem.payload.hash,
260
272
  type: mem.payload.type,
273
+ applyMode: mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
261
274
  createdAt: mem.payload.createdAt,
262
275
  updatedAt: mem.payload.updatedAt,
263
276
  score: mem.score,
@@ -271,6 +284,13 @@ class MemoriesLite {
271
284
  results,
272
285
  };
273
286
  }
287
+ /**
288
+ * Met à jour une mémoire existante
289
+ * @param memoryId - ID de la mémoire
290
+ * @param data - Nouveau contenu
291
+ * @param userId - ID utilisateur
292
+ * @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
293
+ */
274
294
  async update(memoryId, data, userId, metadata = {}) {
275
295
  // await this._captureEvent("update", { memory_id: memoryId });
276
296
  const embedding = await this.embedder.embed(data);
@@ -350,12 +370,14 @@ class MemoriesLite {
350
370
  "data",
351
371
  "createdAt",
352
372
  "updatedAt",
373
+ "applyMode",
353
374
  ]);
354
375
  const results = memories.map((mem) => ({
355
376
  id: mem.id,
356
377
  memory: mem.payload.data,
357
378
  hash: mem.payload.hash,
358
379
  type: mem.payload.type,
380
+ applyMode: mem.payload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE,
359
381
  createdAt: mem.payload.createdAt,
360
382
  updatedAt: mem.payload.updatedAt,
361
383
  metadata: Object.entries(mem.payload)
@@ -393,16 +415,23 @@ class MemoriesLite {
393
415
  //
394
416
  // Preserve origin fields to avoid corrupting ownership/linkage (discussionId/userId/agentId).
395
417
  // - title: keep original unless explicitly changed
418
+ // - applyMode: keep original unless explicitly changed
396
419
  // - discussionId, userId, agentId/runId: always keep original
397
420
  const title = (typeof metadata.title === "string" && metadata.title.trim())
398
421
  ? metadata.title.trim()
399
422
  : originalPayload.title;
423
+ //
424
+ // applyMode: si fourni dans metadata, on le prend, sinon on garde l'original ou le défaut
425
+ const applyMode = (metadata.applyMode && ['MEM_ALWAYS', 'MEM_SMART', 'MEM_MANUAL'].includes(metadata.applyMode))
426
+ ? metadata.applyMode
427
+ : (originalPayload.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE);
400
428
  const newMetadata = {
401
429
  ...originalPayload,
402
430
  data,
403
431
  hash: (0, crypto_1.createHash)("md5").update(data).digest("hex"),
404
432
  updatedAt: new Date().toISOString(),
405
433
  title: title,
434
+ applyMode,
406
435
  };
407
436
  await vectorStore.update(memoryId, embedding, newMetadata);
408
437
  await this.db.addHistory(memoryId, prevValue, data, "UPDATE", newMetadata.createdAt, newMetadata.updatedAt);
@@ -77,10 +77,24 @@ export interface MemoryConfig {
77
77
  capturePrompt?: string;
78
78
  }
79
79
  export type MemoryType = 'assistant_preference' | 'discussion';
80
+ /**
81
+ * Mode d'application de la mémoire (inspiré de Cursor AI)
82
+ * Stocké dans metadata.applyMode avec préfixe MEM_* pour migration soft
83
+ *
84
+ * - MEM_ALWAYS: Toujours appliquer - la mémoire est toujours injectée dans le contexte
85
+ * - MEM_SMART: Appliquer intelligemment - utilisé pour la recherche sémantique (défaut)
86
+ * - MEM_MANUAL: Appliquer manuellement - la mémoire n'est jamais injectée automatiquement
87
+ */
88
+ export type MemoryApplyMode = 'MEM_ALWAYS' | 'MEM_SMART' | 'MEM_MANUAL';
89
+ /**
90
+ * Mode par défaut pour la capture
91
+ */
92
+ export declare const DEFAULT_MEMORY_APPLY_MODE: MemoryApplyMode;
80
93
  export interface MemoryItem {
81
94
  id: string;
82
95
  memory: string;
83
96
  type: MemoryType;
97
+ applyMode?: MemoryApplyMode;
84
98
  event?: "ADD" | "UPDATE" | "DELETE" | "NONE";
85
99
  hash?: string;
86
100
  createdAt?: string;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MemoryConfigSchema = void 0;
3
+ exports.MemoryConfigSchema = exports.DEFAULT_MEMORY_APPLY_MODE = void 0;
4
4
  const zod_1 = require("zod");
5
+ /**
6
+ * Mode par défaut pour la capture
7
+ */
8
+ exports.DEFAULT_MEMORY_APPLY_MODE = 'MEM_SMART';
5
9
  exports.MemoryConfigSchema = zod_1.z.object({
6
10
  version: zod_1.z.string().optional(),
7
11
  embedder: zod_1.z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memories-lite",
3
- "version": "0.99.3",
3
+ "version": "0.99.5",
4
4
  "description": "A lightweight memory system for LLM agents",
5
5
  "author": "olivier@evaletolab.ch",
6
6
  "main": "dist/index.js",
@@ -5,6 +5,8 @@ import {
5
5
  MemoryConfigSchema,
6
6
  MemoryItem,
7
7
  MemoryType,
8
+ MemoryApplyMode,
9
+ DEFAULT_MEMORY_APPLY_MODE,
8
10
  Message,
9
11
  SearchFilters,
10
12
  SearchResult,
@@ -208,11 +210,14 @@ export class MemoriesLite {
208
210
 
209
211
  //
210
212
  // Préparer les métadonnées
213
+ // applyMode par défaut = MEM_SMART (recherche sémantique)
211
214
  const memoryType: MemoryType = metadata.type || 'discussion';
215
+ const applyMode: MemoryApplyMode = metadata.applyMode || DEFAULT_MEMORY_APPLY_MODE;
212
216
  const memoryMetadata = {
213
217
  ...metadata,
214
218
  title,
215
219
  type: memoryType,
220
+ applyMode,
216
221
  userId,
217
222
  };
218
223
 
@@ -315,6 +320,7 @@ export class MemoriesLite {
315
320
  id: memory.id,
316
321
  memory: memory.payload.data,
317
322
  type: memory.payload.type,
323
+ applyMode: memory.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
318
324
  hash: memory.payload.hash,
319
325
  createdAt: memory.payload.createdAt,
320
326
  updatedAt: memory.payload.updatedAt,
@@ -382,12 +388,23 @@ export class MemoriesLite {
382
388
  "data",
383
389
  "createdAt",
384
390
  "updatedAt",
391
+ "applyMode",
385
392
  ]);
386
- const results = memories.map((mem) => ({
393
+
394
+ //
395
+ // Filtrer uniquement les mémoires avec applyMode = MEM_SMART (recherche sémantique)
396
+ // Les MEM_ALWAYS et MEM_MANUAL ne sont pas retournées par retrieve()
397
+ const filteredMemories = memories.filter((mem) => {
398
+ const mode = mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE;
399
+ return mode === 'MEM_SMART';
400
+ });
401
+
402
+ const results = filteredMemories.map((mem) => ({
387
403
  id: mem.id,
388
404
  memory: mem.payload.data,
389
405
  hash: mem.payload.hash,
390
406
  type: mem.payload.type,
407
+ applyMode: mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
391
408
  createdAt: mem.payload.createdAt,
392
409
  updatedAt: mem.payload.updatedAt,
393
410
  score: mem.score,
@@ -403,6 +420,13 @@ export class MemoriesLite {
403
420
  };
404
421
  }
405
422
 
423
+ /**
424
+ * Met à jour une mémoire existante
425
+ * @param memoryId - ID de la mémoire
426
+ * @param data - Nouveau contenu
427
+ * @param userId - ID utilisateur
428
+ * @param metadata - Métadonnées optionnelles (peut inclure applyMode pour changer le mode)
429
+ */
406
430
  async update(
407
431
  memoryId: string,
408
432
  data: string,
@@ -506,12 +530,14 @@ export class MemoriesLite {
506
530
  "data",
507
531
  "createdAt",
508
532
  "updatedAt",
533
+ "applyMode",
509
534
  ]);
510
535
  const results = memories.map((mem) => ({
511
536
  id: mem.id,
512
537
  memory: mem.payload.data,
513
538
  hash: mem.payload.hash,
514
539
  type: mem.payload.type,
540
+ applyMode: mem.payload.applyMode || DEFAULT_MEMORY_APPLY_MODE,
515
541
  createdAt: mem.payload.createdAt,
516
542
  updatedAt: mem.payload.updatedAt,
517
543
  metadata: Object.entries(mem.payload)
@@ -576,17 +602,27 @@ export class MemoriesLite {
576
602
  //
577
603
  // Preserve origin fields to avoid corrupting ownership/linkage (discussionId/userId/agentId).
578
604
  // - title: keep original unless explicitly changed
605
+ // - applyMode: keep original unless explicitly changed
579
606
  // - discussionId, userId, agentId/runId: always keep original
580
607
  const title =
581
608
  (typeof metadata.title === "string" && metadata.title.trim())
582
609
  ? metadata.title.trim()
583
610
  : originalPayload.title;
611
+
612
+ //
613
+ // applyMode: si fourni dans metadata, on le prend, sinon on garde l'original ou le défaut
614
+ const applyMode: MemoryApplyMode =
615
+ (metadata.applyMode && ['MEM_ALWAYS', 'MEM_SMART', 'MEM_MANUAL'].includes(metadata.applyMode))
616
+ ? metadata.applyMode
617
+ : (originalPayload.applyMode || DEFAULT_MEMORY_APPLY_MODE);
618
+
584
619
  const newMetadata: Record<string, any> = {
585
620
  ...originalPayload,
586
621
  data,
587
622
  hash: createHash("md5").update(data).digest("hex"),
588
623
  updatedAt: new Date().toISOString(),
589
624
  title: title,
625
+ applyMode,
590
626
  };
591
627
 
592
628
  await vectorStore.update(memoryId, embedding, newMetadata);
@@ -88,10 +88,26 @@ export interface MemoryConfig {
88
88
 
89
89
  export type MemoryType = 'assistant_preference' | 'discussion';
90
90
 
91
+ /**
92
+ * Mode d'application de la mémoire (inspiré de Cursor AI)
93
+ * Stocké dans metadata.applyMode avec préfixe MEM_* pour migration soft
94
+ *
95
+ * - MEM_ALWAYS: Toujours appliquer - la mémoire est toujours injectée dans le contexte
96
+ * - MEM_SMART: Appliquer intelligemment - utilisé pour la recherche sémantique (défaut)
97
+ * - MEM_MANUAL: Appliquer manuellement - la mémoire n'est jamais injectée automatiquement
98
+ */
99
+ export type MemoryApplyMode = 'MEM_ALWAYS' | 'MEM_SMART' | 'MEM_MANUAL';
100
+
101
+ /**
102
+ * Mode par défaut pour la capture
103
+ */
104
+ export const DEFAULT_MEMORY_APPLY_MODE: MemoryApplyMode = 'MEM_SMART';
105
+
91
106
  export interface MemoryItem {
92
107
  id: string;
93
108
  memory: string;
94
109
  type: MemoryType;
110
+ applyMode?: MemoryApplyMode;
95
111
  event?: "ADD" | "UPDATE" | "DELETE" | "NONE";
96
112
  hash?: string;
97
113
  createdAt?: string;
@@ -0,0 +1,228 @@
1
+ /// <reference types="jest" />
2
+ import { MemoriesLite, DEFAULT_MEMORY_APPLY_MODE, MemoryApplyMode } from "../src";
3
+ import { SearchResult } from "../src/types";
4
+ import dotenv from "dotenv";
5
+ import { createTestMemory } from "./init.mem";
6
+
7
+ dotenv.config();
8
+
9
+ jest.setTimeout(30000);
10
+
11
+ /**
12
+ * Tests pour le MemoryApplyMode (type d'application de mémoire)
13
+ * - MEM_ALWAYS: Toujours appliquer
14
+ * - MEM_SMART: Appliquer intelligemment (défaut, recherche sémantique)
15
+ * - MEM_MANUAL: Appliquer manuellement
16
+ */
17
+ describe("Memory ApplyMode", () => {
18
+ let memory: MemoriesLite;
19
+ let userId: string;
20
+
21
+ beforeEach(async () => {
22
+ ({ memory, userId } = createTestMemory({}));
23
+ await memory.reset(userId);
24
+ });
25
+
26
+ afterEach(async () => {
27
+ await memory.reset(userId);
28
+ });
29
+
30
+ describe("Default ApplyMode", () => {
31
+ it("should set applyMode to MEM_SMART by default on capture", async () => {
32
+ //
33
+ // Capture une discussion avec le mode par défaut
34
+ const messages = [
35
+ { role: "user", content: "Bonjour, comment gérer les congés ?" },
36
+ { role: "assistant", content: "Pour gérer les congés, vous devez..." }
37
+ ];
38
+
39
+ const result = await memory.capture(messages, userId, {
40
+ metadata: { type: "discussion" }
41
+ });
42
+
43
+ expect(result.results.length).toBeGreaterThan(0);
44
+ const createdMemory = result.results[0];
45
+
46
+ //
47
+ // Vérifier que le mode par défaut est MEM_SMART
48
+ expect(createdMemory.metadata?.applyMode || DEFAULT_MEMORY_APPLY_MODE).toBe("MEM_SMART");
49
+ });
50
+
51
+ it("should return DEFAULT_MEMORY_APPLY_MODE as MEM_SMART", () => {
52
+ expect(DEFAULT_MEMORY_APPLY_MODE).toBe("MEM_SMART");
53
+ });
54
+ });
55
+
56
+ describe("ApplyMode Update", () => {
57
+ it("should update applyMode when specified in metadata", async () => {
58
+ //
59
+ // Créer une mémoire avec le mode par défaut
60
+ const messages = [
61
+ { role: "user", content: "Comment faire une facture ?" },
62
+ { role: "assistant", content: "Pour créer une facture..." }
63
+ ];
64
+
65
+ const result = await memory.capture(messages, userId, {
66
+ metadata: { type: "discussion" }
67
+ });
68
+
69
+ expect(result.results.length).toBeGreaterThan(0);
70
+ const memoryId = result.results[0].id;
71
+
72
+ //
73
+ // Mettre à jour avec un nouveau mode
74
+ await memory.update(
75
+ memoryId,
76
+ "Procédure de facturation mise à jour",
77
+ userId,
78
+ { applyMode: "MEM_ALWAYS" as MemoryApplyMode }
79
+ );
80
+
81
+ //
82
+ // Vérifier que le mode a été mis à jour
83
+ const updatedMemory = await memory.get(memoryId, userId);
84
+ expect(updatedMemory).not.toBeNull();
85
+ expect(updatedMemory?.applyMode).toBe("MEM_ALWAYS");
86
+ });
87
+
88
+ it("should preserve applyMode when not specified in update metadata", async () => {
89
+ //
90
+ // Créer une mémoire avec un mode spécifique
91
+ const messages = [
92
+ { role: "user", content: "Règles de sécurité" },
93
+ { role: "assistant", content: "Les règles de sécurité sont..." }
94
+ ];
95
+
96
+ const result = await memory.capture(messages, userId, {
97
+ metadata: { type: "discussion", applyMode: "MEM_MANUAL" }
98
+ });
99
+
100
+ expect(result.results.length).toBeGreaterThan(0);
101
+ const memoryId = result.results[0].id;
102
+
103
+ //
104
+ // Mettre à jour le contenu sans changer le mode
105
+ await memory.update(
106
+ memoryId,
107
+ "Règles de sécurité mises à jour",
108
+ userId,
109
+ {} // Pas de applyMode dans les metadata
110
+ );
111
+
112
+ //
113
+ // Vérifier que le mode est préservé
114
+ const updatedMemory = await memory.get(memoryId, userId);
115
+ expect(updatedMemory).not.toBeNull();
116
+ expect(updatedMemory?.applyMode).toBe("MEM_MANUAL");
117
+ });
118
+
119
+ it("should change applyMode from MEM_SMART to MEM_MANUAL", async () => {
120
+ //
121
+ // Créer une mémoire avec le mode par défaut
122
+ const messages = [
123
+ { role: "user", content: "Comment utiliser l'API ?" },
124
+ { role: "assistant", content: "L'API s'utilise de la manière suivante..." }
125
+ ];
126
+
127
+ const result = await memory.capture(messages, userId, {
128
+ metadata: { type: "discussion" }
129
+ });
130
+
131
+ const memoryId = result.results[0].id;
132
+
133
+ //
134
+ // Changer de MEM_SMART vers MEM_MANUAL
135
+ await memory.update(
136
+ memoryId,
137
+ "Documentation API mise à jour",
138
+ userId,
139
+ { applyMode: "MEM_MANUAL" as MemoryApplyMode }
140
+ );
141
+
142
+ const updatedMemory = await memory.get(memoryId, userId);
143
+ expect(updatedMemory?.applyMode).toBe("MEM_MANUAL");
144
+ });
145
+ });
146
+
147
+ describe("Retrieve Filtering by ApplyMode", () => {
148
+ it("should only return MEM_SMART memories in retrieve()", async () => {
149
+ //
150
+ // Créer 3 mémoires avec différents modes
151
+ const messagesBase = [
152
+ { role: "user", content: "Question sur les procédures" },
153
+ { role: "assistant", content: "Les procédures sont..." }
154
+ ];
155
+
156
+ //
157
+ // Mémoire 1: MEM_SMART (par défaut)
158
+ await memory.capture(messagesBase, userId, {
159
+ metadata: { type: "discussion", customTag: "smart" }
160
+ });
161
+
162
+ //
163
+ // Mémoire 2: MEM_ALWAYS
164
+ await memory.capture([
165
+ { role: "user", content: "Règle toujours appliquée" },
166
+ { role: "assistant", content: "Cette règle est toujours appliquée..." }
167
+ ], userId, {
168
+ metadata: { type: "discussion", applyMode: "MEM_ALWAYS", customTag: "always" }
169
+ });
170
+
171
+ //
172
+ // Mémoire 3: MEM_MANUAL
173
+ await memory.capture([
174
+ { role: "user", content: "Note manuelle" },
175
+ { role: "assistant", content: "Ceci est une note manuelle..." }
176
+ ], userId, {
177
+ metadata: { type: "discussion", applyMode: "MEM_MANUAL", customTag: "manual" }
178
+ });
179
+
180
+ //
181
+ // Vérifier que getAll retourne les 3 mémoires
182
+ const allMemories = await memory.getAll(userId, { type: "discussion" });
183
+ expect(allMemories.results.length).toBe(3);
184
+
185
+ //
186
+ // Vérifier que retrieve() ne retourne que MEM_SMART
187
+ const searchResult = await memory.retrieve("procédures", userId, { limit: 10 });
188
+
189
+ //
190
+ // Seule la mémoire MEM_SMART devrait être retournée
191
+ const smartMemories = searchResult.results.filter(m => m.applyMode === "MEM_SMART");
192
+ const nonSmartMemories = searchResult.results.filter(m => m.applyMode !== "MEM_SMART");
193
+
194
+ expect(smartMemories.length).toBeGreaterThanOrEqual(0);
195
+ expect(nonSmartMemories.length).toBe(0);
196
+ });
197
+ });
198
+
199
+ describe("GetAll returns all ApplyModes", () => {
200
+ it("should return memories of all applyModes in getAll()", async () => {
201
+ //
202
+ // Créer des mémoires avec différents modes
203
+ await memory.capture([
204
+ { role: "user", content: "Mémoire intelligente" },
205
+ { role: "assistant", content: "Réponse intelligente" }
206
+ ], userId, {
207
+ metadata: { type: "discussion" } // MEM_SMART par défaut
208
+ });
209
+
210
+ await memory.capture([
211
+ { role: "user", content: "Mémoire toujours" },
212
+ { role: "assistant", content: "Réponse toujours" }
213
+ ], userId, {
214
+ metadata: { type: "discussion", applyMode: "MEM_ALWAYS" }
215
+ });
216
+
217
+ //
218
+ // getAll doit retourner les deux
219
+ const allMemories = await memory.getAll(userId, { type: "discussion" });
220
+ expect(allMemories.results.length).toBe(2);
221
+
222
+ const modes = allMemories.results.map(m => m.applyMode);
223
+ expect(modes).toContain("MEM_SMART");
224
+ expect(modes).toContain("MEM_ALWAYS");
225
+ });
226
+ });
227
+ });
228
+