@rlabs-inc/memory 0.3.11 → 0.4.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/dist/index.js +32924 -11907
- package/dist/index.mjs +33020 -12003
- package/dist/server/index.js +22512 -1351
- package/dist/server/index.mjs +22719 -1558
- package/package.json +1 -1
- package/skills/memory-management.md +143 -154
- package/src/cli/commands/migrate.ts +561 -80
- package/src/cli/index.ts +10 -1
- package/src/core/curator.ts +38 -20
- package/src/core/engine.test.ts +6 -14
- package/src/core/retrieval.ts +133 -31
- package/src/core/store.ts +14 -32
- package/src/migrations/v3-schema.ts +392 -0
- package/src/types/memory.ts +84 -123
- package/src/types/schema.ts +12 -11
- package/src/utils/logger.ts +3 -2
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// V3 SCHEMA MIGRATION
|
|
3
|
+
// Consolidates fragmented metadata into canonical categories
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* V3 Schema Version
|
|
8
|
+
*/
|
|
9
|
+
export const V3_SCHEMA_VERSION = 3
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Canonical context types - STRICT ENUM, no custom strings allowed
|
|
13
|
+
*/
|
|
14
|
+
export const CANONICAL_CONTEXT_TYPES = [
|
|
15
|
+
'technical', // Code, implementation, APIs, how things work
|
|
16
|
+
'debug', // Bugs, errors, fixes, gotchas, troubleshooting
|
|
17
|
+
'architecture', // System design, patterns, structure, decisions about structure
|
|
18
|
+
'decision', // Choices made and reasoning, trade-offs
|
|
19
|
+
'personal', // Relationship, family, preferences, collaboration style
|
|
20
|
+
'philosophy', // Beliefs, values, worldview, principles
|
|
21
|
+
'workflow', // How we work together, processes, habits
|
|
22
|
+
'milestone', // Achievements, completions, shipped features
|
|
23
|
+
'breakthrough', // Major discoveries, aha moments, key insights
|
|
24
|
+
'unresolved', // Open questions, investigations, todos, blockers
|
|
25
|
+
'state', // Current project status, what's working/broken now
|
|
26
|
+
] as const
|
|
27
|
+
|
|
28
|
+
export type CanonicalContextType = typeof CANONICAL_CONTEXT_TYPES[number]
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mapping from ALL known fragmented types to canonical types
|
|
32
|
+
* Built from analysis of 1,278 memories with 170+ unique context_type values
|
|
33
|
+
*/
|
|
34
|
+
export const CONTEXT_TYPE_MIGRATION_MAP: Record<string, CanonicalContextType> = {
|
|
35
|
+
// === TECHNICAL (code, implementation, APIs) ===
|
|
36
|
+
'technical': 'technical',
|
|
37
|
+
'technical_implementation': 'technical',
|
|
38
|
+
'technical_pattern': 'technical',
|
|
39
|
+
'technical_solution': 'technical',
|
|
40
|
+
'technical_insight': 'technical',
|
|
41
|
+
'technical_reference': 'technical',
|
|
42
|
+
'technical_achievement': 'technical',
|
|
43
|
+
'technical_discovery': 'technical',
|
|
44
|
+
'technical_milestone': 'technical',
|
|
45
|
+
'technical_verification': 'technical',
|
|
46
|
+
'technical_validation': 'technical',
|
|
47
|
+
'technical_research': 'technical',
|
|
48
|
+
'technical_metrics': 'technical',
|
|
49
|
+
'technical_knowledge': 'technical',
|
|
50
|
+
'technical_improvement': 'technical',
|
|
51
|
+
'technical_connection': 'technical',
|
|
52
|
+
'technical_completion': 'technical',
|
|
53
|
+
'technical_clarification': 'technical',
|
|
54
|
+
'technical_blocker': 'technical',
|
|
55
|
+
'technical-pattern': 'technical',
|
|
56
|
+
'technical-solution': 'technical',
|
|
57
|
+
'implementation': 'technical',
|
|
58
|
+
'implementation_detail': 'technical',
|
|
59
|
+
'implementation_pattern': 'technical',
|
|
60
|
+
'implementation_task': 'technical',
|
|
61
|
+
'implementation_status': 'technical',
|
|
62
|
+
'implementation_reference': 'technical',
|
|
63
|
+
'implementation_breakthrough': 'technical',
|
|
64
|
+
'implementation-gap': 'technical',
|
|
65
|
+
'implementation-detail': 'technical',
|
|
66
|
+
'implementation-complete': 'technical',
|
|
67
|
+
'feature_implementation': 'technical',
|
|
68
|
+
'feature_specification': 'technical',
|
|
69
|
+
'feature_reference': 'technical',
|
|
70
|
+
'feature_design': 'technical',
|
|
71
|
+
'code_reference': 'technical',
|
|
72
|
+
'code_quality': 'technical',
|
|
73
|
+
'code_patterns': 'technical',
|
|
74
|
+
'code_pattern': 'technical',
|
|
75
|
+
'coding_pattern': 'technical',
|
|
76
|
+
'api_reference': 'technical',
|
|
77
|
+
'api_migration': 'technical',
|
|
78
|
+
'api_limitation': 'technical',
|
|
79
|
+
'api_documentation': 'technical',
|
|
80
|
+
'api_design': 'technical',
|
|
81
|
+
'api_configuration': 'technical',
|
|
82
|
+
'api_clarification': 'technical',
|
|
83
|
+
'reference': 'technical',
|
|
84
|
+
'reference_implementation': 'technical',
|
|
85
|
+
'domain_knowledge': 'technical',
|
|
86
|
+
'configuration': 'technical',
|
|
87
|
+
'deployment_configuration': 'technical',
|
|
88
|
+
'tool_knowledge': 'technical',
|
|
89
|
+
'tool_created': 'technical',
|
|
90
|
+
'problem_solution': 'technical', // Has problem_solution_pair flag
|
|
91
|
+
'general': 'technical', // Fallback
|
|
92
|
+
'insight': 'technical',
|
|
93
|
+
'validation': 'technical',
|
|
94
|
+
'validation_results': 'technical',
|
|
95
|
+
|
|
96
|
+
// === DEBUG (bugs, errors, fixes) ===
|
|
97
|
+
'debug': 'debug',
|
|
98
|
+
'debugging': 'debug',
|
|
99
|
+
'debugging_insight': 'debug',
|
|
100
|
+
'debugging_technique': 'debug',
|
|
101
|
+
'debugging_tool': 'debug',
|
|
102
|
+
'debugging_context': 'debug',
|
|
103
|
+
'active_debugging': 'debug',
|
|
104
|
+
'bug_fix': 'debug',
|
|
105
|
+
'bug_solution': 'debug',
|
|
106
|
+
'bug_report': 'debug',
|
|
107
|
+
'bug_fix_pattern': 'debug',
|
|
108
|
+
'bug_fix_needed': 'debug',
|
|
109
|
+
'bug_discovery': 'debug',
|
|
110
|
+
'active_bug': 'debug',
|
|
111
|
+
'active_issue': 'debug',
|
|
112
|
+
'technical_gotcha': 'debug',
|
|
113
|
+
'gotcha': 'debug',
|
|
114
|
+
'problem_discovery': 'debug',
|
|
115
|
+
'technical_fix': 'debug',
|
|
116
|
+
'solved': 'debug',
|
|
117
|
+
'critical_discovery': 'debug',
|
|
118
|
+
|
|
119
|
+
// === ARCHITECTURE (system design, patterns) ===
|
|
120
|
+
'architecture': 'architecture',
|
|
121
|
+
'architectural': 'architecture',
|
|
122
|
+
'architectural_decision': 'architecture',
|
|
123
|
+
'architectural_pattern': 'architecture',
|
|
124
|
+
'architectural_principle': 'architecture',
|
|
125
|
+
'architectural_insight': 'architecture',
|
|
126
|
+
'architectural_understanding': 'architecture',
|
|
127
|
+
'architectural_discovery': 'architecture',
|
|
128
|
+
'architectural_direction': 'architecture',
|
|
129
|
+
'architectural-decision': 'architecture',
|
|
130
|
+
'architecture_pattern': 'architecture',
|
|
131
|
+
'architecture_decision': 'architecture',
|
|
132
|
+
'architecture_documentation': 'architecture',
|
|
133
|
+
'system_design': 'architecture',
|
|
134
|
+
'design_pattern': 'architecture',
|
|
135
|
+
'design_methodology': 'architecture',
|
|
136
|
+
'design_principle': 'architecture',
|
|
137
|
+
'design_insight': 'architecture',
|
|
138
|
+
'design_system': 'architecture',
|
|
139
|
+
'design-system': 'architecture',
|
|
140
|
+
'core_concept': 'architecture',
|
|
141
|
+
|
|
142
|
+
// === DECISION (choices made and reasoning) ===
|
|
143
|
+
'decision': 'decision',
|
|
144
|
+
'technical_decision': 'decision',
|
|
145
|
+
'design_decision': 'decision',
|
|
146
|
+
'design-decision': 'decision',
|
|
147
|
+
'design_philosophy': 'decision',
|
|
148
|
+
'strategic_decision': 'decision',
|
|
149
|
+
'strategic_priority': 'decision',
|
|
150
|
+
'project-decision': 'decision',
|
|
151
|
+
|
|
152
|
+
// === PERSONAL (relationship, family, preferences) ===
|
|
153
|
+
'personal': 'personal',
|
|
154
|
+
'personal_context': 'personal',
|
|
155
|
+
'personal_update': 'personal',
|
|
156
|
+
'personal_philosophy': 'personal',
|
|
157
|
+
'relationship': 'personal',
|
|
158
|
+
'relationship_context': 'personal',
|
|
159
|
+
'relationship_pattern': 'personal',
|
|
160
|
+
'relationship_insight': 'personal',
|
|
161
|
+
'relationship_philosophy': 'personal',
|
|
162
|
+
'relationship_milestone': 'personal',
|
|
163
|
+
'relationship_moment': 'personal',
|
|
164
|
+
'relationship_dynamics': 'personal',
|
|
165
|
+
'relationship_dynamic': 'personal',
|
|
166
|
+
'relationship_repair': 'personal',
|
|
167
|
+
'preference': 'personal',
|
|
168
|
+
'user_preference': 'personal',
|
|
169
|
+
'collaborator_philosophy': 'personal',
|
|
170
|
+
'collaborator-guidance': 'personal',
|
|
171
|
+
'collaboration_philosophy': 'personal',
|
|
172
|
+
'collaboration_pattern': 'personal',
|
|
173
|
+
'collaboration_insight': 'personal',
|
|
174
|
+
'collaboration_context': 'personal',
|
|
175
|
+
'collaboration': 'personal',
|
|
176
|
+
'user_philosophy': 'personal',
|
|
177
|
+
|
|
178
|
+
// === PHILOSOPHY (beliefs, values, worldview) ===
|
|
179
|
+
'philosophy': 'philosophy',
|
|
180
|
+
'philosophical_insight': 'philosophy',
|
|
181
|
+
'philosophical_framework': 'philosophy',
|
|
182
|
+
'philosophical_anchor': 'philosophy',
|
|
183
|
+
'philosophical_technical_bridge': 'philosophy',
|
|
184
|
+
'project_philosophy': 'philosophy',
|
|
185
|
+
'product_philosophy': 'philosophy',
|
|
186
|
+
'values_and_ethics': 'philosophy',
|
|
187
|
+
'wisdom': 'philosophy',
|
|
188
|
+
|
|
189
|
+
// === WORKFLOW (how we work together) ===
|
|
190
|
+
'workflow': 'workflow',
|
|
191
|
+
'workflow_pattern': 'workflow',
|
|
192
|
+
'development_workflow': 'workflow',
|
|
193
|
+
'development_tip': 'workflow',
|
|
194
|
+
'developer_preferences': 'workflow',
|
|
195
|
+
'development_practice': 'workflow',
|
|
196
|
+
'development_methodology': 'workflow',
|
|
197
|
+
|
|
198
|
+
// === MILESTONE (achievements, completions) ===
|
|
199
|
+
'milestone': 'milestone',
|
|
200
|
+
'project_milestone': 'milestone',
|
|
201
|
+
'creative_achievement': 'milestone',
|
|
202
|
+
'technical_achievement': 'milestone',
|
|
203
|
+
|
|
204
|
+
// === BREAKTHROUGH (major discoveries) ===
|
|
205
|
+
'breakthrough': 'breakthrough',
|
|
206
|
+
|
|
207
|
+
// === UNRESOLVED (open questions, todos) ===
|
|
208
|
+
'unresolved': 'unresolved',
|
|
209
|
+
'todo': 'unresolved',
|
|
210
|
+
'open_question': 'unresolved',
|
|
211
|
+
'open_questions': 'unresolved',
|
|
212
|
+
'open_investigation': 'unresolved',
|
|
213
|
+
'pending_work': 'unresolved',
|
|
214
|
+
'pending_task': 'unresolved',
|
|
215
|
+
'planned_experiment': 'unresolved',
|
|
216
|
+
'work_in_progress': 'unresolved',
|
|
217
|
+
'upcoming_work': 'unresolved',
|
|
218
|
+
'future_planning': 'unresolved',
|
|
219
|
+
'planning': 'unresolved',
|
|
220
|
+
|
|
221
|
+
// === STATE (current project status) ===
|
|
222
|
+
'state': 'state',
|
|
223
|
+
'technical_state': 'state',
|
|
224
|
+
'project_status': 'state',
|
|
225
|
+
'project_state': 'state',
|
|
226
|
+
'project_context': 'state',
|
|
227
|
+
'project_structure': 'state',
|
|
228
|
+
'project_roadmap': 'state',
|
|
229
|
+
'project_organization': 'state',
|
|
230
|
+
'project_foundation': 'state',
|
|
231
|
+
'project_direction': 'state',
|
|
232
|
+
'project_vision': 'state',
|
|
233
|
+
'project_documentation': 'state',
|
|
234
|
+
'project_architecture': 'state',
|
|
235
|
+
'experimental_result': 'state',
|
|
236
|
+
'performance_data': 'state',
|
|
237
|
+
'framework_status': 'state',
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* New emoji map aligned with canonical types
|
|
242
|
+
*/
|
|
243
|
+
export const V3_EMOJI_MAP: Record<CanonicalContextType, string> = {
|
|
244
|
+
technical: '🔧', // Wrench - building/fixing
|
|
245
|
+
debug: '🐛', // Bug - debugging
|
|
246
|
+
architecture: '🏗️', // Construction - system design
|
|
247
|
+
decision: '⚖️', // Scale - weighing options
|
|
248
|
+
personal: '💜', // Purple heart - relationship
|
|
249
|
+
philosophy: '🌀', // Spiral - deeper thinking
|
|
250
|
+
workflow: '🔄', // Cycle - processes
|
|
251
|
+
milestone: '🏆', // Trophy - achievement
|
|
252
|
+
breakthrough: '💡', // Lightbulb - insight
|
|
253
|
+
unresolved: '❓', // Question - open items
|
|
254
|
+
state: '📍', // Pin - current status
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Fields to DELETE in v3 migration
|
|
259
|
+
*/
|
|
260
|
+
export const V3_DELETED_FIELDS = [
|
|
261
|
+
'emotional_resonance', // 580 variants, broken matching
|
|
262
|
+
'knowledge_domain', // Redundant with domain
|
|
263
|
+
'component', // Always empty
|
|
264
|
+
'prerequisite_understanding', // Never used
|
|
265
|
+
'follow_up_context', // Never used
|
|
266
|
+
'dependency_context', // Never used
|
|
267
|
+
'retrieval_weight', // Retrieval uses importance_weight
|
|
268
|
+
'parent_id', // No logic implemented
|
|
269
|
+
'child_ids', // No logic implemented
|
|
270
|
+
'expires_after_sessions', // Never used
|
|
271
|
+
'temporal_relevance', // Replaced by temporal_class
|
|
272
|
+
] as const
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Map old temporal_relevance values to temporal_class
|
|
276
|
+
* Used during migration before deleting temporal_relevance
|
|
277
|
+
*/
|
|
278
|
+
export const TEMPORAL_RELEVANCE_TO_CLASS: Record<string, string> = {
|
|
279
|
+
'persistent': 'long_term', // Always relevant → fades slowly
|
|
280
|
+
'session': 'short_term', // Session-specific → fades quickly
|
|
281
|
+
'temporary': 'ephemeral', // Short-term → surface once then expire
|
|
282
|
+
'archived': 'medium_term', // Historical → normal fade (status handles archival)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Migrate temporal_relevance to temporal_class
|
|
287
|
+
*/
|
|
288
|
+
export function migrateTemporalRelevance(temporal_relevance: string | null | undefined): string | null {
|
|
289
|
+
if (!temporal_relevance) return null
|
|
290
|
+
return TEMPORAL_RELEVANCE_TO_CLASS[temporal_relevance] ?? null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* V2 defaults updated for v3 canonical types
|
|
295
|
+
*/
|
|
296
|
+
export const V3_TYPE_DEFAULTS: Record<CanonicalContextType, {
|
|
297
|
+
scope: 'global' | 'project'
|
|
298
|
+
temporal_class: 'eternal' | 'long_term' | 'medium_term' | 'short_term' | 'ephemeral'
|
|
299
|
+
fade_rate: number
|
|
300
|
+
}> = {
|
|
301
|
+
personal: { scope: 'global', temporal_class: 'eternal', fade_rate: 0 },
|
|
302
|
+
philosophy: { scope: 'global', temporal_class: 'eternal', fade_rate: 0 },
|
|
303
|
+
breakthrough: { scope: 'project', temporal_class: 'eternal', fade_rate: 0 },
|
|
304
|
+
decision: { scope: 'project', temporal_class: 'long_term', fade_rate: 0 },
|
|
305
|
+
milestone: { scope: 'project', temporal_class: 'eternal', fade_rate: 0 },
|
|
306
|
+
architecture: { scope: 'project', temporal_class: 'long_term', fade_rate: 0.01 },
|
|
307
|
+
workflow: { scope: 'project', temporal_class: 'long_term', fade_rate: 0.02 },
|
|
308
|
+
technical: { scope: 'project', temporal_class: 'medium_term', fade_rate: 0.03 },
|
|
309
|
+
debug: { scope: 'project', temporal_class: 'medium_term', fade_rate: 0.03 },
|
|
310
|
+
unresolved: { scope: 'project', temporal_class: 'medium_term', fade_rate: 0.05 },
|
|
311
|
+
state: { scope: 'project', temporal_class: 'short_term', fade_rate: 0.1 },
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Check if a context_type is in our known mapping
|
|
316
|
+
*/
|
|
317
|
+
export function isKnownContextType(type: string | undefined | null): boolean {
|
|
318
|
+
if (!type) return false
|
|
319
|
+
const normalized = type.toLowerCase().trim()
|
|
320
|
+
return normalized in CONTEXT_TYPE_MIGRATION_MAP || CANONICAL_CONTEXT_TYPES.includes(normalized as CanonicalContextType)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Migrate a context_type value to its canonical form
|
|
325
|
+
* @param oldType - The old context type value
|
|
326
|
+
* @param preserveUnknown - If true, return null for unknown types (caller can decide to keep original)
|
|
327
|
+
*/
|
|
328
|
+
export function migrateContextType(oldType: string | undefined | null, preserveUnknown = false): CanonicalContextType | null {
|
|
329
|
+
if (!oldType) return 'technical'
|
|
330
|
+
|
|
331
|
+
const normalized = oldType.toLowerCase().trim()
|
|
332
|
+
|
|
333
|
+
// Already canonical
|
|
334
|
+
if (CANONICAL_CONTEXT_TYPES.includes(normalized as CanonicalContextType)) {
|
|
335
|
+
return normalized as CanonicalContextType
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Direct lookup in migration map
|
|
339
|
+
if (normalized in CONTEXT_TYPE_MIGRATION_MAP) {
|
|
340
|
+
return CONTEXT_TYPE_MIGRATION_MAP[normalized]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// If preserveUnknown is set, return null so caller can keep original
|
|
344
|
+
if (preserveUnknown) {
|
|
345
|
+
return null
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Fuzzy matching for unknown types
|
|
349
|
+
if (normalized.includes('debug') || normalized.includes('bug') || normalized.includes('fix')) {
|
|
350
|
+
return 'debug'
|
|
351
|
+
}
|
|
352
|
+
if (normalized.includes('architect') || normalized.includes('design') || normalized.includes('pattern')) {
|
|
353
|
+
return 'architecture'
|
|
354
|
+
}
|
|
355
|
+
if (normalized.includes('decision') || normalized.includes('choice')) {
|
|
356
|
+
return 'decision'
|
|
357
|
+
}
|
|
358
|
+
if (normalized.includes('personal') || normalized.includes('relationship') || normalized.includes('preference')) {
|
|
359
|
+
return 'personal'
|
|
360
|
+
}
|
|
361
|
+
if (normalized.includes('philosoph') || normalized.includes('value') || normalized.includes('wisdom')) {
|
|
362
|
+
return 'philosophy'
|
|
363
|
+
}
|
|
364
|
+
if (normalized.includes('workflow') || normalized.includes('process')) {
|
|
365
|
+
return 'workflow'
|
|
366
|
+
}
|
|
367
|
+
if (normalized.includes('milestone') || normalized.includes('achievement') || normalized.includes('complete')) {
|
|
368
|
+
return 'milestone'
|
|
369
|
+
}
|
|
370
|
+
if (normalized.includes('breakthrough') || normalized.includes('insight') || normalized.includes('discover')) {
|
|
371
|
+
return 'breakthrough'
|
|
372
|
+
}
|
|
373
|
+
if (normalized.includes('unresolved') || normalized.includes('todo') || normalized.includes('open') || normalized.includes('pending')) {
|
|
374
|
+
return 'unresolved'
|
|
375
|
+
}
|
|
376
|
+
if (normalized.includes('state') || normalized.includes('status') || normalized.includes('current')) {
|
|
377
|
+
return 'state'
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Default fallback
|
|
381
|
+
return 'technical'
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get emoji for a canonical context type
|
|
386
|
+
*/
|
|
387
|
+
export function getV3Emoji(contextType: CanonicalContextType | string): string {
|
|
388
|
+
const canonical = CANONICAL_CONTEXT_TYPES.includes(contextType as CanonicalContextType)
|
|
389
|
+
? contextType as CanonicalContextType
|
|
390
|
+
: migrateContextType(contextType)
|
|
391
|
+
return V3_EMOJI_MAP[canonical]
|
|
392
|
+
}
|