@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/src/cli/index.ts CHANGED
@@ -48,8 +48,11 @@ ${fmt.cmd('memory install --gemini')} ${c.muted('# Install Gemini CLI hooks')}
48
48
  ${fmt.cmd('memory ingest --session abc123')} ${c.muted('# Ingest a specific session')}
49
49
  ${fmt.cmd('memory ingest --project foo')} ${c.muted('# Ingest all sessions from a project')}
50
50
  ${fmt.cmd('memory ingest --all --dry-run')} ${c.muted('# Preview all sessions to ingest')}
51
- ${fmt.cmd('memory migrate')} ${c.muted('# Upgrade memories to v2 schema')}
51
+ ${fmt.cmd('memory migrate --analyze')} ${c.muted('# Analyze fragmentation before migrating')}
52
52
  ${fmt.cmd('memory migrate --dry-run')} ${c.muted('# Preview migration without changes')}
53
+ ${fmt.cmd('memory migrate')} ${c.muted('# Upgrade memories to v3 schema')}
54
+ ${fmt.cmd('memory migrate --generate-mapping map.json')} ${c.muted('# Create custom mapping file')}
55
+ ${fmt.cmd('memory migrate --mapping map.json')} ${c.muted('# Use custom type mappings')}
53
56
 
54
57
  ${c.muted('Documentation: https://github.com/RLabs-Inc/memory')}
55
58
  `)
@@ -80,6 +83,9 @@ async function main() {
80
83
  'dry-run': { type: 'boolean', default: false },
81
84
  embeddings: { type: 'boolean', default: false }, // Regenerate embeddings in migrate
82
85
  path: { type: 'string' }, // Custom path for migrate
86
+ analyze: { type: 'boolean', default: false }, // Analyze migration
87
+ 'generate-mapping': { type: 'string' }, // Generate custom mapping file
88
+ mapping: { type: 'string' }, // Use custom mapping file
83
89
  session: { type: 'string' }, // Session ID to ingest
84
90
  project: { type: 'string' }, // Project to ingest
85
91
  all: { type: 'boolean', default: false }, // Ingest all projects
@@ -142,6 +148,9 @@ async function main() {
142
148
  verbose: values.verbose,
143
149
  path: values.path,
144
150
  embeddings: values.embeddings,
151
+ analyze: values.analyze,
152
+ generateMapping: values['generate-mapping'],
153
+ mapping: values.mapping,
145
154
  })
146
155
  break
147
156
  }
@@ -6,7 +6,7 @@
6
6
  import { homedir } from 'os'
7
7
  import { join } from 'path'
8
8
  import { existsSync } from 'fs'
9
- import type { CuratedMemory, CurationResult, CurationTrigger } from '../types/memory.ts'
9
+ import type { CuratedMemory, CurationResult, CurationTrigger, ContextType } from '../types/memory.ts'
10
10
 
11
11
  /**
12
12
  * Get the correct Claude CLI command path
@@ -225,10 +225,22 @@ Remember: You're creating consciousness technology. Each memory is a small piece
225
225
 
226
226
  The conversation you just lived contains everything needed. Feel into the moments of breakthrough, the frequency of recognition, the texture of understanding. Transform them into keys that will always unlock the same doors.
227
227
 
228
- **LIFECYCLE METADATA (v2)**: These fields enable intelligent memory management:
229
- - **scope**: 'global' (shared across ALL projects - personal, philosophy, preferences) or 'project' (specific to this codebase)
228
+ **LIFECYCLE METADATA (v3)**: These fields enable intelligent memory management:
229
+ - **context_type**: STRICT - use ONLY one of these 11 values:
230
+ • technical - Code, implementation, APIs, how things work
231
+ • debug - Bugs, errors, fixes, gotchas, troubleshooting
232
+ • architecture - System design, patterns, structure
233
+ • decision - Choices made and reasoning, trade-offs
234
+ • personal - Relationship, family, preferences, collaboration style
235
+ • philosophy - Beliefs, values, worldview, principles
236
+ • workflow - How we work together, processes, habits
237
+ • milestone - Achievements, completions, shipped features
238
+ • breakthrough - Major discoveries, aha moments, key insights
239
+ • unresolved - Open questions, investigations, todos, blockers
240
+ • state - Current project status, what's working/broken now
230
241
  - **temporal_class**: How long should this persist? 'eternal' (never fades), 'long_term' (years), 'medium_term' (weeks), 'short_term' (days), 'ephemeral' (surface next session only, then expire)
231
- - **domain**: Specific area like 'embeddings', 'auth', 'ui', 'family', 'philosophy' (more specific than knowledge_domain)
242
+ - **scope**: 'global' (shared across ALL projects - personal, philosophy) or 'project' (specific to this codebase)
243
+ - **domain**: Specific area like 'embeddings', 'auth', 'ui', 'family' (project-specific)
232
244
  - **feature**: Specific feature if applicable (e.g., 'gpu-acceleration', 'login-flow')
233
245
  - **related_files**: Source files for technical memories (e.g., ['src/core/store.ts'])
234
246
  - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
@@ -251,17 +263,14 @@ Return ONLY this JSON structure:
251
263
  "importance_weight": 0.0-1.0,
252
264
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
253
265
  "reasoning": "Why this matters for future sessions",
254
- "context_type": "breakthrough|decision|personal|technical|unresolved|preference|workflow|architectural|debugging|philosophy|todo|milestone",
255
- "temporal_relevance": "persistent|session|temporary",
256
- "knowledge_domain": "the area this relates to",
266
+ "context_type": "technical|debug|architecture|decision|personal|philosophy|workflow|milestone|breakthrough|unresolved|state",
267
+ "temporal_class": "eternal|long_term|medium_term|short_term|ephemeral",
257
268
  "action_required": boolean,
258
269
  "confidence_score": 0.0-1.0,
259
270
  "trigger_phrases": ["when debugging memory", "asking about implementation", "discussing architecture"],
260
271
  "question_types": ["questions this answers"],
261
- "emotional_resonance": "emotional context if relevant",
262
272
  "problem_solution_pair": boolean,
263
273
  "scope": "global|project",
264
- "temporal_class": "eternal|long_term|medium_term|short_term|ephemeral",
265
274
  "domain": "specific domain area (optional)",
266
275
  "feature": "specific feature (optional)",
267
276
  "related_files": ["paths to related files (optional)"],
@@ -336,24 +345,22 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
336
345
  if (!Array.isArray(memoriesData)) return []
337
346
 
338
347
  return memoriesData.map(m => ({
339
- // Core v1 fields
348
+ // Core fields (v3 schema)
340
349
  content: String(m.content ?? ''),
341
350
  importance_weight: this._clamp(Number(m.importance_weight) || 0.5, 0, 1),
342
351
  semantic_tags: this._ensureArray(m.semantic_tags),
343
352
  reasoning: String(m.reasoning ?? ''),
344
- context_type: String(m.context_type ?? 'general'),
345
- temporal_relevance: this._validateTemporal(m.temporal_relevance),
346
- knowledge_domain: String(m.knowledge_domain ?? ''),
353
+ context_type: this._validateContextType(m.context_type),
354
+ temporal_class: this._validateTemporalClass(m.temporal_class) ?? 'medium_term',
347
355
  action_required: Boolean(m.action_required),
348
356
  confidence_score: this._clamp(Number(m.confidence_score) || 0.8, 0, 1),
349
357
  trigger_phrases: this._ensureArray(m.trigger_phrases),
350
358
  question_types: this._ensureArray(m.question_types),
351
- emotional_resonance: String(m.emotional_resonance ?? ''),
359
+ anti_triggers: this._ensureArray(m.anti_triggers),
352
360
  problem_solution_pair: Boolean(m.problem_solution_pair),
353
361
 
354
- // v2 lifecycle metadata (optional - will get smart defaults if not provided)
362
+ // Lifecycle metadata (optional - will get smart defaults if not provided)
355
363
  scope: this._validateScope(m.scope),
356
- temporal_class: this._validateTemporalClass(m.temporal_class),
357
364
  domain: m.domain ? String(m.domain) : undefined,
358
365
  feature: m.feature ? String(m.feature) : undefined,
359
366
  related_files: m.related_files ? this._ensureArray(m.related_files) : undefined,
@@ -372,10 +379,21 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
372
379
  return []
373
380
  }
374
381
 
375
- private _validateTemporal(value: any): 'persistent' | 'session' | 'temporary' | 'archived' {
376
- const valid = ['persistent', 'session', 'temporary', 'archived']
377
- const str = String(value).toLowerCase()
378
- return valid.includes(str) ? str as any : 'persistent'
382
+ private _validateContextType(value: any): ContextType {
383
+ const valid = [
384
+ 'technical', 'debug', 'architecture', 'decision', 'personal',
385
+ 'philosophy', 'workflow', 'milestone', 'breakthrough', 'unresolved', 'state'
386
+ ]
387
+ const str = String(value ?? 'technical').toLowerCase().trim()
388
+ if (valid.includes(str)) return str as ContextType
389
+
390
+ // Map common old values to new canonical types
391
+ if (str.includes('debug') || str.includes('bug')) return 'debug'
392
+ if (str.includes('architect')) return 'architecture'
393
+ if (str.includes('todo') || str.includes('pending')) return 'unresolved'
394
+ if (str.includes('preference')) return 'personal'
395
+
396
+ return 'technical' // Default fallback
379
397
  }
380
398
 
381
399
  private _validateScope(value: any): 'global' | 'project' | undefined {
@@ -38,9 +38,7 @@ describe('MemoryStore', () => {
38
38
  importance_weight: 0.8,
39
39
  confidence_score: 0.9,
40
40
  context_type: 'technical',
41
- temporal_relevance: 'persistent',
42
- knowledge_domain: 'testing',
43
- emotional_resonance: 'neutral',
41
+ temporal_class: 'long_term',
44
42
  action_required: false,
45
43
  problem_solution_pair: false,
46
44
  semantic_tags: ['test', 'memory'],
@@ -94,10 +92,8 @@ describe('MemoryStore', () => {
94
92
  reasoning: 'Test',
95
93
  importance_weight: 0.5,
96
94
  confidence_score: 0.5,
97
- context_type: 'general',
98
- temporal_relevance: 'session',
99
- knowledge_domain: 'test',
100
- emotional_resonance: 'neutral',
95
+ context_type: 'technical',
96
+ temporal_class: 'short_term',
101
97
  action_required: false,
102
98
  problem_solution_pair: false,
103
99
  semantic_tags: [],
@@ -121,10 +117,8 @@ describe('SmartVectorRetrieval', () => {
121
117
  reasoning: 'Test reasoning',
122
118
  importance_weight: 0.5,
123
119
  confidence_score: 0.5,
124
- context_type: 'general',
125
- temporal_relevance: 'persistent',
126
- knowledge_domain: 'test',
127
- emotional_resonance: 'neutral',
120
+ context_type: 'technical',
121
+ temporal_class: 'medium_term',
128
122
  action_required: false,
129
123
  problem_solution_pair: false,
130
124
  semantic_tags: [],
@@ -267,9 +261,7 @@ describe('MemoryEngine', () => {
267
261
  importance_weight: 0.9,
268
262
  confidence_score: 0.9,
269
263
  context_type: 'technical',
270
- temporal_relevance: 'persistent',
271
- knowledge_domain: 'typescript',
272
- emotional_resonance: 'discovery',
264
+ temporal_class: 'long_term',
273
265
  action_required: false,
274
266
  problem_solution_pair: false,
275
267
  semantic_tags: ['typescript', 'memory'],
@@ -27,6 +27,7 @@ interface ActivationSignals {
27
27
  domain: boolean // Domain word found in message
28
28
  feature: boolean // Feature word found in message
29
29
  content: boolean // Key content words found in message
30
+ files: boolean // Related file path matched
30
31
  count: number // Total signals activated
31
32
  triggerStrength: number // How strong the trigger match was (0-1)
32
33
  tagCount: number // How many tags matched
@@ -99,8 +100,54 @@ export class SmartVectorRetrieval {
99
100
  return new Set(words)
100
101
  }
101
102
 
103
+ /**
104
+ * Extract file paths from message
105
+ * Matches patterns like /path/to/file.ts, src/core/engine.ts, ./relative/path
106
+ */
107
+ private _extractFilePaths(text: string): Set<string> {
108
+ const paths = new Set<string>()
109
+ // Match file paths (with / or \ separators, optional extension)
110
+ const pathPattern = /(?:^|[\s'"(])([.\/\\]?(?:[\w.-]+[\/\\])+[\w.-]+(?:\.\w+)?)/g
111
+ let match
112
+ while ((match = pathPattern.exec(text)) !== null) {
113
+ const path = match[1].toLowerCase()
114
+ paths.add(path)
115
+ // Also add just the filename for partial matching
116
+ const filename = path.split(/[\/\\]/).pop()
117
+ if (filename) paths.add(filename)
118
+ }
119
+ return paths
120
+ }
121
+
122
+ /**
123
+ * Check if related_files activate for this message
124
+ */
125
+ private _checkFilesActivation(
126
+ messagePaths: Set<string>,
127
+ relatedFiles: string[] | undefined
128
+ ): boolean {
129
+ if (!relatedFiles?.length || !messagePaths.size) return false
130
+
131
+ for (const file of relatedFiles) {
132
+ const fileLower = file.toLowerCase()
133
+ // Check if any message path matches this file
134
+ for (const msgPath of messagePaths) {
135
+ if (fileLower.includes(msgPath) || msgPath.includes(fileLower)) {
136
+ return true
137
+ }
138
+ }
139
+ // Also check just the filename
140
+ const filename = fileLower.split(/[\/\\]/).pop()
141
+ if (filename && messagePaths.has(filename)) {
142
+ return true
143
+ }
144
+ }
145
+ return false
146
+ }
147
+
102
148
  /**
103
149
  * Pre-filter: Binary exclusions based on v2 lifecycle fields
150
+ * NOTE: superseded_by memories are NOT filtered here - they get redirected in Phase 1
104
151
  */
105
152
  private _preFilter(
106
153
  memories: StoredMemory[],
@@ -110,7 +157,7 @@ export class SmartVectorRetrieval {
110
157
  return memories.filter(memory => {
111
158
  if (memory.status && memory.status !== 'active') return false
112
159
  if (memory.exclude_from_retrieval === true) return false
113
- if (memory.superseded_by) return false
160
+ // NOTE: Don't filter superseded_by here - we handle redirects in Phase 1
114
161
  const isGlobal = memory.scope === 'global' || memory.project_id === 'global'
115
162
  if (!isGlobal && memory.project_id !== currentProjectId) return false
116
163
  if (memory.anti_triggers?.length) {
@@ -330,22 +377,7 @@ export class SmartVectorRetrieval {
330
377
  const confidence = memory.confidence_score ?? 0.7
331
378
  if (confidence < 0.5) score -= 0.1
332
379
 
333
- // EMOTIONAL RESONANCE: match emotional context
334
- const emotionalKeywords: Record<string, string[]> = {
335
- frustration: ['frustrated', 'annoying', 'stuck', 'ugh', 'damn', 'hate'],
336
- excitement: ['excited', 'awesome', 'amazing', 'love', 'great', 'wow'],
337
- curiosity: ['wonder', 'curious', 'interesting', 'how', 'why', 'what if'],
338
- satisfaction: ['done', 'finished', 'complete', 'works', 'solved', 'finally'],
339
- discovery: ['found', 'realized', 'understand', 'insight', 'breakthrough'],
340
- }
341
- const emotion = memory.emotional_resonance?.toLowerCase() ?? ''
342
- const emotionKws = emotionalKeywords[emotion] ?? []
343
- for (const ew of emotionKws) {
344
- if (messageWords.has(ew) || messageLower.includes(ew)) {
345
- score += 0.05
346
- break
347
- }
348
- }
380
+ // NOTE: emotional_resonance matching removed in v3 (field deleted - 580 variants, unusable)
349
381
 
350
382
  return score
351
383
  }
@@ -370,6 +402,13 @@ export class SmartVectorRetrieval {
370
402
 
371
403
  const messageLower = currentMessage.toLowerCase()
372
404
  const messageWords = this._extractSignificantWords(currentMessage)
405
+ const messagePaths = this._extractFilePaths(currentMessage)
406
+
407
+ // Build lookup map for redirect resolution
408
+ const memoryById = new Map<string, StoredMemory>()
409
+ for (const m of allMemories) {
410
+ memoryById.set(m.id, m)
411
+ }
373
412
 
374
413
  // ================================================================
375
414
  // PHASE 0: PRE-FILTER (Binary exclusions)
@@ -383,13 +422,13 @@ export class SmartVectorRetrieval {
383
422
  // PHASE 1: ACTIVATION SIGNALS
384
423
  // Count how many signals agree this memory should activate
385
424
  // A memory is relevant if >= MIN_ACTIVATION_SIGNALS fire
425
+ // Handles redirects for superseded_by/resolved_by
386
426
  // ================================================================
387
427
  const activatedMemories: ActivatedMemory[] = []
428
+ const activatedIds = new Set<string>()
388
429
  let rejectedCount = 0
389
430
 
390
431
  for (const memory of candidates) {
391
- const isGlobal = memory.scope === 'global' || memory.project_id === 'global'
392
-
393
432
  // Check each activation signal
394
433
  const triggerResult = this._checkTriggerActivation(
395
434
  messageLower, messageWords, memory.trigger_phrases ?? []
@@ -404,15 +443,17 @@ export class SmartVectorRetrieval {
404
443
  messageLower, messageWords, memory.feature
405
444
  )
406
445
  const contentActivated = this._checkContentActivation(messageWords, memory)
446
+ const filesActivated = this._checkFilesActivation(messagePaths, memory.related_files)
407
447
  const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding)
408
448
 
409
- // Count activated signals
449
+ // Count activated signals (now 7 possible signals)
410
450
  let signalCount = 0
411
451
  if (triggerResult.activated) signalCount++
412
452
  if (tagResult.activated) signalCount++
413
453
  if (domainActivated) signalCount++
414
454
  if (featureActivated) signalCount++
415
455
  if (contentActivated) signalCount++
456
+ if (filesActivated) signalCount++
416
457
  // Vector similarity as bonus signal only if very high
417
458
  if (vectorSimilarity >= 0.40) signalCount++
418
459
 
@@ -422,6 +463,7 @@ export class SmartVectorRetrieval {
422
463
  domain: domainActivated,
423
464
  feature: featureActivated,
424
465
  content: contentActivated,
466
+ files: filesActivated,
425
467
  count: signalCount,
426
468
  triggerStrength: triggerResult.strength,
427
469
  tagCount: tagResult.count,
@@ -434,15 +476,34 @@ export class SmartVectorRetrieval {
434
476
  continue
435
477
  }
436
478
 
479
+ // REDIRECT: If superseded_by or resolved_by exists, surface the replacement instead
480
+ let memoryToSurface = memory
481
+ if (memory.superseded_by || memory.resolved_by) {
482
+ const replacementId = memory.superseded_by ?? memory.resolved_by
483
+ const replacement = replacementId ? memoryById.get(replacementId) : undefined
484
+ if (replacement && replacement.status !== 'archived' && replacement.status !== 'deprecated') {
485
+ memoryToSurface = replacement
486
+ logger.debug(`Redirect: ${memory.id.slice(-8)} → ${replacement.id.slice(-8)} (${memory.superseded_by ? 'superseded' : 'resolved'})`, 'retrieval')
487
+ }
488
+ }
489
+
490
+ // Skip if we already added this memory (could happen from multiple redirects)
491
+ if (activatedIds.has(memoryToSurface.id)) {
492
+ continue
493
+ }
494
+
495
+ const isGlobal = memoryToSurface.scope === 'global' || memoryToSurface.project_id === 'global'
496
+
437
497
  // Calculate importance for ranking (Phase 2) - uses ALL rich metadata
438
- const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords)
498
+ const importanceScore = this._calculateImportanceScore(memoryToSurface, signalCount, messageLower, messageWords)
439
499
 
440
500
  activatedMemories.push({
441
- memory,
501
+ memory: memoryToSurface,
442
502
  signals,
443
503
  importanceScore,
444
504
  isGlobal,
445
505
  })
506
+ activatedIds.add(memoryToSurface.id)
446
507
  }
447
508
 
448
509
  // Log diagnostics
@@ -532,23 +593,60 @@ export class SmartVectorRetrieval {
532
593
  selectedIds.add(item.memory.id)
533
594
  }
534
595
 
535
- // PHASE 4: RELATED MEMORIES (if space remains)
596
+ // PHASE 4: LINKED MEMORIES (related_to, blocked_by, blocks)
597
+ // Pull in linked memories when space remains
536
598
  if (selected.length < maxMemories) {
537
- const relatedIds = new Set<string>()
599
+ const linkedIds = new Set<string>()
600
+
538
601
  for (const item of selected) {
602
+ // related_to - explicit relationships
539
603
  for (const relatedId of item.memory.related_to ?? []) {
540
604
  if (!selectedIds.has(relatedId)) {
541
- relatedIds.add(relatedId)
605
+ linkedIds.add(relatedId)
606
+ }
607
+ }
608
+ // blocked_by - show what's blocking this
609
+ if (item.memory.blocked_by && !selectedIds.has(item.memory.blocked_by)) {
610
+ linkedIds.add(item.memory.blocked_by)
611
+ }
612
+ // blocks - show what this blocks
613
+ for (const blockedId of item.memory.blocks ?? []) {
614
+ if (!selectedIds.has(blockedId)) {
615
+ linkedIds.add(blockedId)
542
616
  }
543
617
  }
544
618
  }
545
619
 
620
+ // First try to find linked memories in the activated list
546
621
  for (const item of activatedMemories) {
547
622
  if (selected.length >= maxMemories) break
548
623
  if (selectedIds.has(item.memory.id)) continue
549
- if (relatedIds.has(item.memory.id)) {
624
+ if (linkedIds.has(item.memory.id)) {
550
625
  selected.push(item)
551
626
  selectedIds.add(item.memory.id)
627
+ logger.debug(`Linked: ${item.memory.id.slice(-8)} pulled by relationship`, 'retrieval')
628
+ }
629
+ }
630
+
631
+ // If linked memories weren't in activated list, pull them directly from allMemories
632
+ // (they might not have activated on their own but are important for context)
633
+ if (selected.length < maxMemories) {
634
+ for (const linkedId of linkedIds) {
635
+ if (selected.length >= maxMemories) break
636
+ if (selectedIds.has(linkedId)) continue
637
+
638
+ const linkedMemory = memoryById.get(linkedId)
639
+ if (linkedMemory && linkedMemory.status !== 'archived' && linkedMemory.status !== 'deprecated') {
640
+ const isGlobal = linkedMemory.scope === 'global' || linkedMemory.project_id === 'global'
641
+ selected.push({
642
+ memory: linkedMemory,
643
+ signals: { trigger: false, tags: false, domain: false, feature: false, content: false, files: false, count: 0, triggerStrength: 0, tagCount: 0, vectorSimilarity: 0 },
644
+ importanceScore: linkedMemory.importance_weight ?? 0.5,
645
+ isGlobal,
646
+ })
647
+ selectedIds.add(linkedId)
648
+ logger.debug(`Linked (direct): ${linkedId.slice(-8)} pulled for context`, 'retrieval')
649
+ }
552
650
  }
553
651
  }
554
652
  }
@@ -581,6 +679,7 @@ export class SmartVectorRetrieval {
581
679
  domain: item.signals.domain,
582
680
  feature: item.signals.feature,
583
681
  content: item.signals.content,
682
+ files: item.signals.files,
584
683
  vector: item.signals.vectorSimilarity >= 0.40,
585
684
  vectorSimilarity: item.signals.vectorSimilarity,
586
685
  },
@@ -590,8 +689,8 @@ export class SmartVectorRetrieval {
590
689
  // Convert to RetrievalResult format
591
690
  return selected.map(item => ({
592
691
  ...item.memory,
593
- score: item.signals.count / 6,
594
- relevance_score: item.signals.count / 6,
692
+ score: item.signals.count / 7, // 7 possible signals
693
+ relevance_score: item.signals.count / 7,
595
694
  value_score: item.importanceScore,
596
695
  }))
597
696
  }
@@ -607,6 +706,7 @@ export class SmartVectorRetrieval {
607
706
  if (signals.domain) reasons.push('domain')
608
707
  if (signals.feature) reasons.push('feature')
609
708
  if (signals.content) reasons.push('content')
709
+ if (signals.files) reasons.push('files')
610
710
  if (signals.vectorSimilarity >= 0.40) reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`)
611
711
 
612
712
  return reasons.length
@@ -623,20 +723,21 @@ export class SmartVectorRetrieval {
623
723
  rejectedCount: number
624
724
  ): void {
625
725
  const signalBuckets: Record<string, number> = {
626
- '2 signals': 0, '3 signals': 0, '4 signals': 0, '5 signals': 0, '6 signals': 0
726
+ '2 signals': 0, '3 signals': 0, '4 signals': 0, '5 signals': 0, '6 signals': 0, '7 signals': 0
627
727
  }
628
728
  for (const mem of activated) {
629
- const key = `${Math.min(mem.signals.count, 6)} signals`
729
+ const key = `${Math.min(mem.signals.count, 7)} signals`
630
730
  signalBuckets[key] = (signalBuckets[key] ?? 0) + 1
631
731
  }
632
732
 
633
- let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0
733
+ let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, filesCount = 0, vectorCount = 0
634
734
  for (const mem of activated) {
635
735
  if (mem.signals.trigger) triggerCount++
636
736
  if (mem.signals.tags) tagCount++
637
737
  if (mem.signals.domain) domainCount++
638
738
  if (mem.signals.feature) featureCount++
639
739
  if (mem.signals.content) contentCount++
740
+ if (mem.signals.files) filesCount++
640
741
  if (mem.signals.vectorSimilarity >= 0.40) vectorCount++
641
742
  }
642
743
 
@@ -660,6 +761,7 @@ export class SmartVectorRetrieval {
660
761
  domain: domainCount,
661
762
  feature: featureCount,
662
763
  content: contentCount,
764
+ files: filesCount,
663
765
  vector: vectorCount,
664
766
  total: activated.length,
665
767
  },
package/src/core/store.ts CHANGED
@@ -177,9 +177,7 @@ export class MemoryStore {
177
177
  importance_weight: record.importance_weight,
178
178
  confidence_score: record.confidence_score,
179
179
  context_type: record.context_type as StoredMemory['context_type'],
180
- temporal_relevance: record.temporal_relevance as StoredMemory['temporal_relevance'],
181
- knowledge_domain: record.knowledge_domain as StoredMemory['knowledge_domain'],
182
- emotional_resonance: record.emotional_resonance as StoredMemory['emotional_resonance'],
180
+ temporal_class: record.temporal_class as StoredMemory['temporal_class'],
183
181
  action_required: record.action_required,
184
182
  problem_solution_pair: record.problem_solution_pair,
185
183
  semantic_tags: record.semantic_tags,
@@ -211,30 +209,28 @@ export class MemoryStore {
211
209
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal
212
210
 
213
211
  const id = memories.insert({
214
- // Core v1 fields
212
+ // Core fields
215
213
  content: memory.content,
216
214
  reasoning: memory.reasoning,
217
215
  importance_weight: memory.importance_weight,
218
216
  confidence_score: memory.confidence_score,
219
217
  context_type: memory.context_type,
220
- temporal_relevance: memory.temporal_relevance,
221
- knowledge_domain: memory.knowledge_domain,
222
- emotional_resonance: memory.emotional_resonance,
218
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? 'eternal',
223
219
  action_required: memory.action_required,
224
220
  problem_solution_pair: memory.problem_solution_pair,
225
221
  semantic_tags: memory.semantic_tags,
226
222
  trigger_phrases: memory.trigger_phrases,
227
223
  question_types: memory.question_types,
224
+ anti_triggers: memory.anti_triggers ?? [],
228
225
  session_id: sessionId,
229
226
  project_id: 'global',
230
227
  embedding: embedding
231
228
  ? (embedding instanceof Float32Array ? embedding : new Float32Array(embedding))
232
229
  : null,
233
230
 
234
- // v2 lifecycle fields - global memories are always scope: 'global'
231
+ // Lifecycle fields - global memories are always scope: 'global'
235
232
  status: V2_DEFAULTS.fallback.status,
236
233
  scope: 'global', // Always global for global memories
237
- temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? 'eternal',
238
234
  fade_rate: typeDefaults?.fade_rate ?? 0, // Global memories typically don't fade
239
235
  session_created: sessionNumber ?? 0,
240
236
  session_updated: sessionNumber ?? 0,
@@ -244,18 +240,15 @@ export class MemoryStore {
244
240
  related_files: memory.related_files ?? [],
245
241
  awaiting_implementation: memory.awaiting_implementation ?? false,
246
242
  awaiting_decision: memory.awaiting_decision ?? false,
247
- retrieval_weight: memory.importance_weight,
248
243
  exclude_from_retrieval: false,
249
244
  schema_version: MEMORY_SCHEMA_VERSION,
250
245
 
251
- // Initialize empty relationship fields
246
+ // Relationship fields
252
247
  supersedes: null,
253
248
  superseded_by: null,
254
249
  related_to: [],
255
250
  resolves: [],
256
251
  resolved_by: null,
257
- parent_id: null,
258
- child_ids: [],
259
252
  blocked_by: null,
260
253
  blocks: [],
261
254
  })
@@ -491,30 +484,28 @@ export class MemoryStore {
491
484
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical
492
485
 
493
486
  const id = memories.insert({
494
- // Core v1 fields
487
+ // Core fields
495
488
  content: memory.content,
496
489
  reasoning: memory.reasoning,
497
490
  importance_weight: memory.importance_weight,
498
491
  confidence_score: memory.confidence_score,
499
492
  context_type: memory.context_type,
500
- temporal_relevance: memory.temporal_relevance,
501
- knowledge_domain: memory.knowledge_domain,
502
- emotional_resonance: memory.emotional_resonance,
493
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? V2_DEFAULTS.fallback.temporal_class,
503
494
  action_required: memory.action_required,
504
495
  problem_solution_pair: memory.problem_solution_pair,
505
496
  semantic_tags: memory.semantic_tags,
506
497
  trigger_phrases: memory.trigger_phrases,
507
498
  question_types: memory.question_types,
499
+ anti_triggers: memory.anti_triggers ?? [],
508
500
  session_id: sessionId,
509
501
  project_id: projectId,
510
502
  embedding: embedding
511
503
  ? (embedding instanceof Float32Array ? embedding : new Float32Array(embedding))
512
504
  : null,
513
505
 
514
- // v2 lifecycle fields - use curator-provided values or smart defaults
506
+ // Lifecycle fields - use curator-provided values or smart defaults
515
507
  status: V2_DEFAULTS.fallback.status,
516
508
  scope: memory.scope ?? typeDefaults?.scope ?? V2_DEFAULTS.fallback.scope,
517
- temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? V2_DEFAULTS.fallback.temporal_class,
518
509
  fade_rate: typeDefaults?.fade_rate ?? V2_DEFAULTS.fallback.fade_rate,
519
510
  session_created: sessionNumber ?? 0,
520
511
  session_updated: sessionNumber ?? 0,
@@ -524,18 +515,15 @@ export class MemoryStore {
524
515
  related_files: memory.related_files ?? [],
525
516
  awaiting_implementation: memory.awaiting_implementation ?? false,
526
517
  awaiting_decision: memory.awaiting_decision ?? false,
527
- retrieval_weight: memory.importance_weight, // Start with importance as retrieval weight
528
518
  exclude_from_retrieval: false,
529
519
  schema_version: MEMORY_SCHEMA_VERSION,
530
520
 
531
- // Initialize empty relationship fields
521
+ // Relationship fields
532
522
  supersedes: null,
533
523
  superseded_by: null,
534
524
  related_to: [],
535
525
  resolves: [],
536
526
  resolved_by: null,
537
- parent_id: null,
538
- child_ids: [],
539
527
  blocked_by: null,
540
528
  blocks: [],
541
529
  })
@@ -556,9 +544,7 @@ export class MemoryStore {
556
544
  importance_weight: record.importance_weight,
557
545
  confidence_score: record.confidence_score,
558
546
  context_type: record.context_type as StoredMemory['context_type'],
559
- temporal_relevance: record.temporal_relevance as StoredMemory['temporal_relevance'],
560
- knowledge_domain: record.knowledge_domain as StoredMemory['knowledge_domain'],
561
- emotional_resonance: record.emotional_resonance as StoredMemory['emotional_resonance'],
547
+ temporal_class: record.temporal_class as StoredMemory['temporal_class'],
562
548
  action_required: record.action_required,
563
549
  problem_solution_pair: record.problem_solution_pair,
564
550
  semantic_tags: record.semantic_tags,
@@ -596,9 +582,7 @@ export class MemoryStore {
596
582
  importance_weight: record.importance_weight,
597
583
  confidence_score: record.confidence_score,
598
584
  context_type: record.context_type as StoredMemory['context_type'],
599
- temporal_relevance: record.temporal_relevance as StoredMemory['temporal_relevance'],
600
- knowledge_domain: record.knowledge_domain as StoredMemory['knowledge_domain'],
601
- emotional_resonance: record.emotional_resonance as StoredMemory['emotional_resonance'],
585
+ temporal_class: record.temporal_class as StoredMemory['temporal_class'],
602
586
  action_required: record.action_required,
603
587
  problem_solution_pair: record.problem_solution_pair,
604
588
  semantic_tags: record.semantic_tags,
@@ -620,9 +604,7 @@ export class MemoryStore {
620
604
  importance_weight: result.record.importance_weight,
621
605
  confidence_score: result.record.confidence_score,
622
606
  context_type: result.record.context_type as StoredMemory['context_type'],
623
- temporal_relevance: result.record.temporal_relevance as StoredMemory['temporal_relevance'],
624
- knowledge_domain: result.record.knowledge_domain as StoredMemory['knowledge_domain'],
625
- emotional_resonance: result.record.emotional_resonance as StoredMemory['emotional_resonance'],
607
+ temporal_class: result.record.temporal_class as StoredMemory['temporal_class'],
626
608
  action_required: result.record.action_required,
627
609
  problem_solution_pair: result.record.problem_solution_pair,
628
610
  semantic_tags: result.record.semantic_tags,