@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.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/memory_mesh.js +69 -0
- package/bin/scrubber.js +81 -0
- package/index.d.ts +111 -0
- package/lib/adapters/index.js +3 -0
- package/lib/embeddings/factory.js +150 -0
- package/lib/embeddings/index.js +2 -0
- package/lib/embeddings/service.js +586 -0
- package/lib/index.js +18 -0
- package/lib/lancedb/client.js +631 -0
- package/lib/lancedb/config.js +215 -0
- package/lib/lancedb/errors.js +144 -0
- package/lib/lancedb/index.js +4 -0
- package/lib/lancedb/schema.js +197 -0
- package/lib/memory/index.js +3 -0
- package/lib/memory/memory-context-manager.js +388 -0
- package/lib/memory/memory-mesh.js +910 -0
- package/lib/memory/memory-translator.js +130 -0
- package/lib/memory/migrate-memory.js +227 -0
- package/lib/memory/migrate-to-v2.js +120 -0
- package/lib/memory/scorer.js +85 -0
- package/lib/memory/vector-memory.js +364 -0
- package/lib/privacy/audit-logger.js +176 -0
- package/lib/privacy/dlp-redactor.js +72 -0
- package/lib/privacy/index.js +10 -0
- package/lib/reporting/skill-report-generator.js +283 -0
- package/lib/scrubber/.gitkeep +1 -0
- package/lib/scrubber/config/defaults.js +62 -0
- package/lib/scrubber/errors/scrubber-error.js +43 -0
- package/lib/scrubber/index.js +25 -0
- package/lib/scrubber/scrubber.js +130 -0
- package/lib/scrubber/stages/chunker.js +103 -0
- package/lib/scrubber/stages/metadata-annotator.js +74 -0
- package/lib/scrubber/stages/normalizer.js +59 -0
- package/lib/scrubber/stages/semantic-filter.js +61 -0
- package/lib/scrubber/stages/structural-cleaner.js +82 -0
- package/lib/scrubber/stages/validator.js +66 -0
- package/lib/scrubber/telemetry.js +66 -0
- package/lib/scrubber/utils/hash.js +39 -0
- package/lib/scrubber/utils/html-parser.js +45 -0
- package/lib/scrubber/utils/pattern-matcher.js +63 -0
- package/lib/scrubber/utils/token-counter.js +31 -0
- package/lib/search/filter.js +275 -0
- package/lib/search/hybrid.js +137 -0
- package/lib/search/index.js +3 -0
- package/lib/search/pattern-miner.js +160 -0
- package/lib/utils/error-sanitizer.js +84 -0
- package/lib/utils/handoff-validator.js +85 -0
- package/lib/utils/index.js +4 -0
- package/lib/utils/spinner.js +190 -0
- package/lib/utils/streaming-client.js +128 -0
- package/package.json +39 -0
- package/skills/SKILL.md +462 -0
- 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;
|