@yamo/memory-mesh 2.0.1

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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/bin/memory_mesh.js +69 -0
  4. package/bin/scrubber.js +81 -0
  5. package/index.d.ts +111 -0
  6. package/lib/adapters/index.js +3 -0
  7. package/lib/embeddings/factory.js +150 -0
  8. package/lib/embeddings/index.js +2 -0
  9. package/lib/embeddings/service.js +586 -0
  10. package/lib/index.js +18 -0
  11. package/lib/lancedb/client.js +631 -0
  12. package/lib/lancedb/config.js +215 -0
  13. package/lib/lancedb/errors.js +144 -0
  14. package/lib/lancedb/index.js +4 -0
  15. package/lib/lancedb/schema.js +197 -0
  16. package/lib/memory/index.js +3 -0
  17. package/lib/memory/memory-context-manager.js +388 -0
  18. package/lib/memory/memory-mesh.js +910 -0
  19. package/lib/memory/memory-translator.js +130 -0
  20. package/lib/memory/migrate-memory.js +227 -0
  21. package/lib/memory/migrate-to-v2.js +120 -0
  22. package/lib/memory/scorer.js +85 -0
  23. package/lib/memory/vector-memory.js +364 -0
  24. package/lib/privacy/audit-logger.js +176 -0
  25. package/lib/privacy/dlp-redactor.js +72 -0
  26. package/lib/privacy/index.js +10 -0
  27. package/lib/reporting/skill-report-generator.js +283 -0
  28. package/lib/scrubber/.gitkeep +1 -0
  29. package/lib/scrubber/config/defaults.js +62 -0
  30. package/lib/scrubber/errors/scrubber-error.js +43 -0
  31. package/lib/scrubber/index.js +25 -0
  32. package/lib/scrubber/scrubber.js +130 -0
  33. package/lib/scrubber/stages/chunker.js +103 -0
  34. package/lib/scrubber/stages/metadata-annotator.js +74 -0
  35. package/lib/scrubber/stages/normalizer.js +59 -0
  36. package/lib/scrubber/stages/semantic-filter.js +61 -0
  37. package/lib/scrubber/stages/structural-cleaner.js +82 -0
  38. package/lib/scrubber/stages/validator.js +66 -0
  39. package/lib/scrubber/telemetry.js +66 -0
  40. package/lib/scrubber/utils/hash.js +39 -0
  41. package/lib/scrubber/utils/html-parser.js +45 -0
  42. package/lib/scrubber/utils/pattern-matcher.js +63 -0
  43. package/lib/scrubber/utils/token-counter.js +31 -0
  44. package/lib/search/filter.js +275 -0
  45. package/lib/search/hybrid.js +137 -0
  46. package/lib/search/index.js +3 -0
  47. package/lib/search/pattern-miner.js +160 -0
  48. package/lib/utils/error-sanitizer.js +84 -0
  49. package/lib/utils/handoff-validator.js +85 -0
  50. package/lib/utils/index.js +4 -0
  51. package/lib/utils/spinner.js +190 -0
  52. package/lib/utils/streaming-client.js +128 -0
  53. package/package.json +39 -0
  54. package/skills/SKILL.md +462 -0
  55. package/skills/skill-scrubber.yamo +41 -0
@@ -0,0 +1,388 @@
1
+ /**
2
+ * MemoryContextManager - High-level memory management for YAMO
3
+ *
4
+ * Provides automatic memory capture and intelligent recall for YAMO interactions.
5
+ * Integrates with MemoryMesh for storage, MemoryScorer for importance calculation,
6
+ * and MemoryTranslator for YAMO agent formatting.
7
+ *
8
+ * Features:
9
+ * - Automatic memory capture from interactions
10
+ * - Intelligent memory recall with caching
11
+ * - Duplicate detection
12
+ * - YAMO agent formatting
13
+ * - Graceful degradation on errors
14
+ */
15
+
16
+ import { MemoryMesh } from './memory-mesh.js';
17
+ import { MemoryScorer } from './scorer.js';
18
+ import { MemoryTranslator } from './memory-translator.js';
19
+
20
+ export class MemoryContextManager {
21
+ #config;
22
+ #mesh;
23
+ #scorer;
24
+ #initialized = false;
25
+ #queryCache = new Map();
26
+ #cacheConfig = {
27
+ maxSize: 100,
28
+ ttlMs: 2 * 60 * 1000, // 2 minutes
29
+ };
30
+
31
+ /**
32
+ * Create a new MemoryContextManager
33
+ * @param {Object} config - Configuration object
34
+ * @param {MemoryMesh} [config.mesh] - Optional existing MemoryMesh instance
35
+ * @param {boolean} [config.autoInit=true] - Auto-initialize on first use
36
+ * @param {boolean} [config.enableCache=true] - Enable query caching
37
+ * @param {number} [config.recallLimit=5] - Max memories to recall
38
+ * @param {number} [config.minImportance=0.1] - Min importance score to store
39
+ * @param {boolean} [config.silent=true] - Suppress console warnings (prevents spinner corruption)
40
+ */
41
+ constructor(config = {}) {
42
+ this.#config = {
43
+ autoInit: true,
44
+ enableCache: true,
45
+ recallLimit: 5,
46
+ minImportance: 0.1,
47
+ // Silent mode prevents console output that corrupts spinner/REPL display
48
+ silent: config.silent !== false,
49
+ ...config,
50
+ };
51
+
52
+ // Use provided mesh or create new instance
53
+ this.#mesh = config.mesh || new MemoryMesh();
54
+ this.#scorer = new MemoryScorer(this.#mesh);
55
+ }
56
+
57
+ /**
58
+ * Initialize the memory context manager
59
+ * @returns {Promise<void>}
60
+ */
61
+ async initialize() {
62
+ if (this.#initialized) {
63
+ return;
64
+ }
65
+
66
+ try {
67
+ await this.#mesh.init();
68
+ this.#initialized = true;
69
+ } catch (error) {
70
+ // Graceful degradation - silent by default to avoid corrupting spinner/REPL
71
+ const e = error instanceof Error ? error : new Error(String(error));
72
+ this.#logWarn(`Initialization failed: ${e.message}`);
73
+ this.#initialized = false;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Capture an interaction as memory
79
+ * @param {string} prompt - User prompt
80
+ * @param {string} response - System response
81
+ * @param {Object} context - Additional context
82
+ * @param {string} [context.interactionType='llm_response'] - Type of interaction
83
+ * @param {Array<string>} [context.toolsUsed] - Tools used in interaction
84
+ * @param {Array<string>} [context.filesInvolved] - Files involved
85
+ * @param {Array<string>} [context.tags] - Optional tags
86
+ * @returns {Promise<Object|null>} Created memory record or null on failure
87
+ */
88
+ async captureInteraction(prompt, response, context = {}) {
89
+ try {
90
+ // Auto-initialize if needed
91
+ if (this.#config.autoInit && !this.#initialized) {
92
+ await this.initialize();
93
+ }
94
+
95
+ if (!this.#initialized) {
96
+ return null;
97
+ }
98
+
99
+ // Format the interaction content
100
+ const content = this.#formatInteraction(prompt, response);
101
+
102
+ // Build metadata
103
+ const metadata = this.#buildMetadata(context);
104
+
105
+ // Check for duplicates
106
+ const isDuplicate = await this.#scorer.isDuplicate(content);
107
+ if (isDuplicate) {
108
+ return null;
109
+ }
110
+
111
+ // Calculate importance
112
+ const importance = await this.#scorer.calculateImportance(content, metadata);
113
+
114
+ // Skip if below threshold
115
+ if (importance < this.#config.minImportance) {
116
+ return null;
117
+ }
118
+
119
+ // Add to memory
120
+ const memory = await this.#mesh.add(content, {
121
+ ...metadata,
122
+ importanceScore: importance,
123
+ });
124
+
125
+ return memory;
126
+
127
+ } catch (error) {
128
+ // Graceful degradation - silent by default to avoid corrupting spinner/REPL
129
+ const e = error instanceof Error ? error : new Error(String(error));
130
+ this.#logWarn(`Failed to capture interaction: ${e.message}`);
131
+ return null;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Recall relevant memories for a query
137
+ * @param {string} query - Query to search for
138
+ * @param {Object} options - Recall options
139
+ * @param {number} [options.limit] - Max memories to recall (default from config)
140
+ * @param {boolean} [options.useCache] - Use cache (default from config)
141
+ * @param {string} [options.memoryType] - Filter by memory type
142
+ * @returns {Promise<Array>} Array of relevant memories
143
+ */
144
+ async recallMemories(query, options = {}) {
145
+ try {
146
+ // Auto-initialize if needed
147
+ if (this.#config.autoInit && !this.#initialized) {
148
+ await this.initialize();
149
+ }
150
+
151
+ if (!this.#initialized) {
152
+ return [];
153
+ }
154
+
155
+ const {
156
+ limit = this.#config.recallLimit,
157
+ useCache = this.#config.enableCache,
158
+ memoryType = null,
159
+ } = options;
160
+
161
+ // Check cache
162
+ if (useCache) {
163
+ const cacheKey = this.#cacheKey(query, { limit, memoryType });
164
+ const cached = this.#getCached(cacheKey);
165
+ if (cached) {
166
+ return cached;
167
+ }
168
+ }
169
+
170
+ // Build filter if memoryType specified
171
+ const filter = memoryType ? `memoryType == '${memoryType}'` : null;
172
+
173
+ // Search memories
174
+ // @ts-ignore
175
+ let memories = await this.#mesh.search(query, { limit, filter, useCache: false });
176
+
177
+ // Add importance scores
178
+ memories = await Promise.all(memories.map(async (memory) => {
179
+ const metadata = typeof memory.metadata === 'string'
180
+ ? JSON.parse(memory.metadata)
181
+ : memory.metadata || {};
182
+
183
+ return {
184
+ ...memory,
185
+ importanceScore: metadata.importanceScore || 0,
186
+ memoryType: metadata.memoryType || 'global',
187
+ };
188
+ }));
189
+
190
+ // Cache results
191
+ if (useCache) {
192
+ const cacheKey = this.#cacheKey(query, { limit, memoryType });
193
+ this.#setCached(cacheKey, memories);
194
+ }
195
+
196
+ return memories;
197
+
198
+ } catch (error) {
199
+ // Graceful degradation - silent by default to avoid corrupting spinner/REPL
200
+ const e = error instanceof Error ? error : new Error(String(error));
201
+ this.#logWarn(`Failed to recall memories: ${e.message}`);
202
+ return [];
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Format memories for inclusion in prompt
208
+ * @param {Array} memories - Memories to format
209
+ * @param {Object} options - Formatting options
210
+ * @param {string} [options.mode='background_context'] - YAMO agent mode
211
+ * @param {boolean} [options.includeMetadata=true] - Include metadata
212
+ * @param {number} [options.maxContentLength=500] - Max content length per memory
213
+ * @returns {string} Formatted memories ready for prompt injection
214
+ */
215
+ formatMemoriesForPrompt(memories, options = {}) {
216
+ try {
217
+ if (!memories || memories.length === 0) {
218
+ return '';
219
+ }
220
+
221
+ return MemoryTranslator.toYAMOContext(memories, options);
222
+
223
+ } catch (error) {
224
+ // Graceful degradation - silent by default to avoid corrupting spinner/REPL
225
+ const e = error instanceof Error ? error : new Error(String(error));
226
+ this.#logWarn(`Failed to format memories: ${e.message}`);
227
+ return '';
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Log warning message (respects silent mode to avoid corrupting spinner/REPL)
233
+ */
234
+ #logWarn(message) {
235
+ // Only log if not in silent mode or if YAMO_DEBUG is set
236
+ if (!this.#config.silent || process.env.YAMO_DEBUG === 'true') {
237
+ console.warn(`[MemoryContextManager] ${message}`);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Format interaction for storage
243
+ */
244
+ #formatInteraction(prompt, response) {
245
+ // Create a structured representation
246
+ const lines = [
247
+ `[USER] ${prompt}`,
248
+ `[ASSISTANT] ${response.substring(0, 500)}${response.length > 500 ? '...' : ''}`,
249
+ ];
250
+
251
+ return lines.join('\n\n');
252
+ }
253
+
254
+ /**
255
+ * Build metadata object from context
256
+ */
257
+ #buildMetadata(context) {
258
+ const metadata = {
259
+ interaction_type: context.interactionType || 'llm_response',
260
+ created_at: new Date().toISOString(),
261
+ };
262
+
263
+ if (context.toolsUsed?.length > 0) {
264
+ metadata.tools_used = context.toolsUsed;
265
+ }
266
+
267
+ if (context.filesInvolved?.length > 0) {
268
+ metadata.files_involved = context.filesInvolved;
269
+ }
270
+
271
+ if (context.tags?.length > 0) {
272
+ metadata.tags = context.tags;
273
+ }
274
+
275
+ if (context.skillName) {
276
+ metadata.skill_name = context.skillName;
277
+ }
278
+
279
+ if (context.sessionId) {
280
+ metadata.session_id = context.sessionId;
281
+ }
282
+
283
+ return metadata;
284
+ }
285
+
286
+ /**
287
+ * Generate cache key
288
+ */
289
+ #cacheKey(query, options) {
290
+ return `recall:${query}:${JSON.stringify(options)}`;
291
+ }
292
+
293
+ /**
294
+ * Get cached result
295
+ */
296
+ #getCached(key) {
297
+ const entry = this.#queryCache.get(key);
298
+ if (!entry) {
299
+ return null;
300
+ }
301
+
302
+ // Check TTL
303
+ if (Date.now() - entry.timestamp > this.#cacheConfig.ttlMs) {
304
+ this.#queryCache.delete(key);
305
+ return null;
306
+ }
307
+
308
+ // Move to end (MRU)
309
+ this.#queryCache.delete(key);
310
+ this.#queryCache.set(key, entry);
311
+
312
+ return entry.result;
313
+ }
314
+
315
+ /**
316
+ * Set cached result
317
+ */
318
+ #setCached(key, result) {
319
+ // Evict oldest if at max size
320
+ if (this.#queryCache.size >= this.#cacheConfig.maxSize) {
321
+ const firstKey = this.#queryCache.keys().next().value;
322
+ this.#queryCache.delete(firstKey);
323
+ }
324
+
325
+ this.#queryCache.set(key, {
326
+ result,
327
+ timestamp: Date.now(),
328
+ });
329
+ }
330
+
331
+ /**
332
+ * Clear the query cache
333
+ */
334
+ clearCache() {
335
+ this.#queryCache.clear();
336
+ }
337
+
338
+ /**
339
+ * Get cache statistics
340
+ * @returns {Object} Cache stats
341
+ */
342
+ getCacheStats() {
343
+ return {
344
+ size: this.#queryCache.size,
345
+ maxSize: this.#cacheConfig.maxSize,
346
+ ttlMs: this.#cacheConfig.ttlMs,
347
+ };
348
+ }
349
+
350
+ /**
351
+ * Health check for the memory context manager
352
+ * @returns {Promise<Object>} Health status
353
+ */
354
+ async healthCheck() {
355
+ const health = {
356
+ status: 'healthy',
357
+ timestamp: new Date().toISOString(),
358
+ initialized: this.#initialized,
359
+ checks: {},
360
+ };
361
+
362
+ // Check mesh
363
+ try {
364
+ health.checks.mesh = await this.#mesh.healthCheck();
365
+ if (health.checks.mesh.status !== 'healthy') {
366
+ health.status = 'degraded';
367
+ }
368
+ } catch (error) {
369
+ const e = error instanceof Error ? error : new Error(String(error));
370
+ health.checks.mesh = {
371
+ status: 'error',
372
+ error: e.message,
373
+ };
374
+ health.status = 'unhealthy';
375
+ }
376
+
377
+ // Check cache
378
+ health.checks.cache = {
379
+ status: 'up',
380
+ size: this.#queryCache.size,
381
+ maxSize: this.#cacheConfig.maxSize,
382
+ };
383
+
384
+ return health;
385
+ }
386
+ }
387
+
388
+ export default MemoryContextManager;