@rlabs-inc/memory 0.3.5 → 0.3.6

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.
@@ -41,10 +41,29 @@ const sym = {
41
41
  target: '🎯',
42
42
  }
43
43
 
44
+ /**
45
+ * Logger configuration
46
+ */
47
+ let _verbose = false
48
+
44
49
  /**
45
50
  * Logger with beautiful styled output
46
51
  */
47
52
  export const logger = {
53
+ /**
54
+ * Set verbose mode
55
+ */
56
+ setVerbose(enabled: boolean) {
57
+ _verbose = enabled
58
+ },
59
+
60
+ /**
61
+ * Check if verbose mode is enabled
62
+ */
63
+ isVerbose(): boolean {
64
+ return _verbose
65
+ },
66
+
48
67
  /**
49
68
  * Info message
50
69
  */
@@ -159,11 +178,11 @@ export const logger = {
159
178
  },
160
179
 
161
180
  /**
162
- * Log retrieved memories
181
+ * Log retrieved memories (Activation Signal Algorithm)
163
182
  */
164
183
  logRetrievedMemories(memories: Array<{
165
184
  content: string
166
- score: number
185
+ score: number // signals.count / 6
167
186
  context_type: string
168
187
  }>, query: string) {
169
188
  const queryPreview = query.length > 40
@@ -176,7 +195,9 @@ export const logger = {
176
195
  technical_state: '📍', unresolved: '❓', preference: '⚙️', workflow: '🔄',
177
196
  architectural: '🏗️', debugging: '🐛', philosophy: '🌀', todo: '🎯',
178
197
  implementation: '⚡', problem_solution: '✅', project_context: '📦',
179
- milestone: '🏆', general: '📝',
198
+ milestone: '🏆', general: '📝', project_state: '📍', pending_task: '⏳',
199
+ work_in_progress: '🔨', system_feedback: '📣', project_milestone: '🏆',
200
+ architectural_insight: '🏛️', architectural_direction: '🧭',
180
201
  }
181
202
 
182
203
  console.log()
@@ -191,7 +212,9 @@ export const logger = {
191
212
  }
192
213
 
193
214
  memories.forEach((m, i) => {
194
- const score = style('green', `${(m.score * 100).toFixed(0)}%`)
215
+ // Convert score back to signal count (score is count/6)
216
+ const signalCount = Math.round(m.score * 6)
217
+ const signalStr = style('green', `${signalCount}sig`)
195
218
  const emoji = emojiMap[m.context_type?.toLowerCase()] ?? '📝'
196
219
  const num = style('dim', `${i + 1}.`)
197
220
 
@@ -199,7 +222,7 @@ export const logger = {
199
222
  ? m.content.slice(0, 55) + style('dim', '...')
200
223
  : m.content
201
224
 
202
- console.log(` ${num} [${score}] ${emoji}`)
225
+ console.log(` ${num} [${signalStr}] ${emoji}`)
203
226
  console.log(` ${preview}`)
204
227
  })
205
228
  console.log()
@@ -265,53 +288,211 @@ export const logger = {
265
288
  },
266
289
 
267
290
  /**
268
- * Log memory retrieval scoring details
291
+ * Log management agent starting
292
+ */
293
+ logManagementStart(memoriesCount: number) {
294
+ console.log(`${timestamp()} ${style('blue', '🔧')} ${style('bold', 'MANAGEMENT AGENT')}`)
295
+ console.log(` ${style('dim', 'processing:')} ${memoriesCount} new memories`)
296
+ },
297
+
298
+ /**
299
+ * Log management agent results
300
+ * In verbose mode: shows all details beautifully formatted
301
+ * In normal mode: shows compact summary
302
+ */
303
+ logManagementComplete(result: {
304
+ success: boolean
305
+ superseded?: number
306
+ resolved?: number
307
+ linked?: number
308
+ filesRead?: number
309
+ filesWritten?: number
310
+ primerUpdated?: boolean
311
+ actions?: string[]
312
+ summary?: string
313
+ fullReport?: string
314
+ error?: string
315
+ }) {
316
+ // Helper to format action with icon
317
+ const formatAction = (action: string, truncate = true): string => {
318
+ let icon = ' •'
319
+ if (action.startsWith('READ OK')) icon = style('dim', ' 📖')
320
+ else if (action.startsWith('READ FAILED')) icon = style('red', ' ❌')
321
+ else if (action.startsWith('WRITE OK')) icon = style('green', ' ✏️')
322
+ else if (action.startsWith('WRITE FAILED')) icon = style('red', ' ❌')
323
+ else if (action.startsWith('RECEIVED')) icon = style('cyan', ' 📥')
324
+ else if (action.startsWith('CREATED')) icon = style('green', ' ✨')
325
+ else if (action.startsWith('UPDATED')) icon = style('blue', ' 📝')
326
+ else if (action.startsWith('SUPERSEDED')) icon = style('yellow', ' 🔄')
327
+ else if (action.startsWith('RESOLVED')) icon = style('green', ' ✅')
328
+ else if (action.startsWith('LINKED')) icon = style('cyan', ' 🔗')
329
+ else if (action.startsWith('PRIMER')) icon = style('magenta', ' 💜')
330
+ else if (action.startsWith('SKIPPED')) icon = style('dim', ' ⏭️')
331
+ else if (action.startsWith('NO_ACTION')) icon = style('dim', ' ◦')
332
+
333
+ const text = truncate && action.length > 70 ? action.slice(0, 67) + '...' : action
334
+ return `${icon} ${style('dim', text)}`
335
+ }
336
+
337
+ if (result.success) {
338
+ console.log(` ${style('green', sym.check)} ${style('bold', 'Completed')}`)
339
+
340
+ if (_verbose) {
341
+ // ═══════════════════════════════════════════════════════════════
342
+ // VERBOSE MODE: Show everything beautifully formatted
343
+ // ═══════════════════════════════════════════════════════════════
344
+
345
+ // File I/O stats section
346
+ console.log(` ${style('dim', '─'.repeat(50))}`)
347
+ console.log(` ${style('cyan', '📊')} ${style('bold', 'Statistics')}`)
348
+
349
+ const filesRead = result.filesRead ?? 0
350
+ const filesWritten = result.filesWritten ?? 0
351
+ console.log(` ${style('dim', 'Files read:')} ${filesRead > 0 ? style('green', String(filesRead)) : style('dim', '0')}`)
352
+ console.log(` ${style('dim', 'Files written:')} ${filesWritten > 0 ? style('green', String(filesWritten)) : style('dim', '0')}`)
353
+
354
+ // Memory changes section
355
+ const superseded = result.superseded ?? 0
356
+ const resolved = result.resolved ?? 0
357
+ const linked = result.linked ?? 0
358
+ console.log(` ${style('dim', 'Superseded:')} ${superseded > 0 ? style('yellow', String(superseded)) : style('dim', '0')}`)
359
+ console.log(` ${style('dim', 'Resolved:')} ${resolved > 0 ? style('green', String(resolved)) : style('dim', '0')}`)
360
+ console.log(` ${style('dim', 'Linked:')} ${linked > 0 ? style('cyan', String(linked)) : style('dim', '0')}`)
361
+ console.log(` ${style('dim', 'Primer:')} ${result.primerUpdated ? style('magenta', 'updated') : style('dim', 'unchanged')}`)
362
+
363
+ // Actions section - show ALL actions in verbose mode
364
+ if (result.actions && result.actions.length > 0) {
365
+ console.log(` ${style('dim', '─'.repeat(50))}`)
366
+ console.log(` ${style('cyan', '🎬')} ${style('bold', 'Actions')} ${style('dim', `(${result.actions.length} total)`)}`)
367
+ for (const action of result.actions) {
368
+ console.log(` ${formatAction(action, false)}`) // No truncation in verbose
369
+ }
370
+ }
371
+
372
+ // Full report section
373
+ if (result.fullReport) {
374
+ console.log(` ${style('dim', '─'.repeat(50))}`)
375
+ console.log(` ${style('cyan', '📋')} ${style('bold', 'Full Report')}`)
376
+ const reportLines = result.fullReport.split('\n')
377
+ for (const line of reportLines) {
378
+ // Highlight section headers
379
+ if (line.includes('===')) {
380
+ console.log(` ${style('bold', line)}`)
381
+ } else if (line.match(/^[A-Z_]+:/)) {
382
+ // Highlight stat lines like "memories_processed: 5"
383
+ console.log(` ${style('cyan', line)}`)
384
+ } else {
385
+ console.log(` ${style('dim', line)}`)
386
+ }
387
+ }
388
+ }
389
+
390
+ console.log(` ${style('dim', '─'.repeat(50))}`)
391
+
392
+ } else {
393
+ // ═══════════════════════════════════════════════════════════════
394
+ // NORMAL MODE: Compact summary
395
+ // ═══════════════════════════════════════════════════════════════
396
+
397
+ // Memory change stats (one line)
398
+ const stats: string[] = []
399
+ if (result.superseded && result.superseded > 0) stats.push(`${result.superseded} superseded`)
400
+ if (result.resolved && result.resolved > 0) stats.push(`${result.resolved} resolved`)
401
+ if (result.linked && result.linked > 0) stats.push(`${result.linked} linked`)
402
+ if (result.primerUpdated) stats.push('primer updated')
403
+
404
+ if (stats.length > 0) {
405
+ console.log(` ${style('dim', 'changes:')} ${stats.join(style('dim', ', '))}`)
406
+ } else {
407
+ console.log(` ${style('dim', 'changes:')} none (memories are current)`)
408
+ }
409
+
410
+ // Show limited actions
411
+ if (result.actions && result.actions.length > 0) {
412
+ console.log(` ${style('dim', 'actions:')}`)
413
+ for (const action of result.actions.slice(0, 10)) {
414
+ console.log(` ${formatAction(action, true)}`)
415
+ }
416
+ if (result.actions.length > 10) {
417
+ console.log(` ${style('dim', ` ... and ${result.actions.length - 10} more actions`)}`)
418
+ }
419
+ }
420
+ }
421
+
422
+ } else {
423
+ // ═══════════════════════════════════════════════════════════════
424
+ // ERROR: Always show error details
425
+ // ═══════════════════════════════════════════════════════════════
426
+ console.log(` ${style('yellow', sym.warning)} ${style('bold', 'Failed')}`)
427
+ if (result.error) {
428
+ console.log(` ${style('red', 'error:')} ${result.error}`)
429
+ }
430
+
431
+ // Show full report on error (always, for debugging)
432
+ if (result.fullReport) {
433
+ console.log(` ${style('dim', '─'.repeat(50))}`)
434
+ console.log(` ${style('red', '📋')} ${style('bold', 'Error Report:')}`)
435
+ const reportLines = result.fullReport.split('\n')
436
+ for (const line of reportLines) {
437
+ console.log(` ${style('dim', line)}`)
438
+ }
439
+ }
440
+ }
441
+ console.log()
442
+ },
443
+
444
+ /**
445
+ * Log memory retrieval scoring details (Activation Signal Algorithm)
269
446
  */
270
447
  logRetrievalScoring(params: {
271
448
  totalMemories: number
272
449
  currentMessage: string
273
450
  alreadyInjected: number
274
- mustIncludeCount: number
275
- remainingSlots: number
451
+ preFiltered: number
452
+ globalCount: number
453
+ projectCount: number
276
454
  finalCount: number
455
+ durationMs?: number
277
456
  selectedMemories: Array<{
278
457
  content: string
279
- reasoning: string
280
- score: number
281
- relevance_score: number
458
+ reasoning: string // "Activated: trigger:67%, tags:2, content (3 signals)"
459
+ signalCount: number
282
460
  importance_weight: number
283
461
  context_type: string
284
462
  semantic_tags: string[]
285
- components: {
286
- trigger: number
287
- vector: number
288
- importance: number
289
- temporal: number
290
- context: number
291
- tags: number
292
- question: number
293
- emotion: number
294
- problem: number
295
- action: number
463
+ isGlobal: boolean
464
+ // Activation signals
465
+ signals: {
466
+ trigger: boolean
467
+ triggerStrength: number
468
+ tags: boolean
469
+ tagCount: number
470
+ domain: boolean
471
+ feature: boolean
472
+ content: boolean
473
+ vector: boolean
474
+ vectorSimilarity: number
296
475
  }
297
476
  }>
298
477
  }) {
299
- const { totalMemories, currentMessage, alreadyInjected, mustIncludeCount, remainingSlots, finalCount, selectedMemories } = params
478
+ const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, durationMs, selectedMemories } = params
479
+
480
+ const timeStr = durationMs !== undefined ? style('cyan', `${durationMs.toFixed(1)}ms`) : ''
300
481
 
301
482
  console.log()
302
- console.log(`${timestamp()} ${style('magenta', sym.brain)} ${style('bold', 'TWO-STAGE MEMORY FILTERING')}`)
303
- console.log(` ${style('dim', 'candidates:')} ${totalMemories} memories`)
483
+ console.log(`${timestamp()} ${style('magenta', sym.brain)} ${style('bold', 'RETRIEVAL')} ${timeStr}`)
484
+ console.log(` ${style('dim', 'total:')} ${totalMemories} → ${style('dim', 'filtered:')} ${preFiltered} → ${style('dim', 'candidates:')} ${totalMemories - preFiltered}`)
304
485
  console.log(` ${style('dim', 'already injected:')} ${alreadyInjected}`)
305
486
 
306
487
  const msgPreview = currentMessage.length > 60
307
488
  ? currentMessage.slice(0, 60) + '...'
308
489
  : currentMessage
309
- console.log(` ${style('dim', 'trigger:')} "${msgPreview}"`)
490
+ console.log(` ${style('dim', 'message:')} "${msgPreview}"`)
310
491
  console.log()
311
492
 
312
- // Stage summary
313
- console.log(` ${style('cyan', 'Stage 1:')} ${mustIncludeCount} must-include (critical/action-required)`)
314
- console.log(` ${style('cyan', 'Stage 2:')} ${remainingSlots} slots for scored selection`)
493
+ // Selection summary
494
+ console.log(` ${style('cyan', 'Global:')} ${globalCount} candidates → max 2 selected`)
495
+ console.log(` ${style('cyan', 'Project:')} ${projectCount} candidates`)
315
496
  console.log(` ${style('green', 'Final:')} ${finalCount} memories selected`)
316
497
  console.log()
317
498
 
@@ -328,11 +509,12 @@ export const logger = {
328
509
 
329
510
  selectedMemories.forEach((m, i) => {
330
511
  const num = style('dim', `${i + 1}.`)
331
- const score = style('green', `${(m.score * 100).toFixed(0)}%`)
332
- const relevance = style('cyan', `rel:${(m.relevance_score * 100).toFixed(0)}%`)
512
+ const signalsStr = style('green', `${m.signalCount} signals`)
513
+ const imp = style('magenta', `imp:${(m.importance_weight * 100).toFixed(0)}%`)
333
514
  const type = style('yellow', m.context_type.toUpperCase())
515
+ const scope = m.isGlobal ? style('blue', ' [G]') : ''
334
516
 
335
- console.log(` ${num} [${score} ${relevance}] ${type}`)
517
+ console.log(` ${num} [${signalsStr} ${imp}] ${type}${scope}`)
336
518
 
337
519
  // Content preview
338
520
  const preview = m.content.length > 60
@@ -340,27 +522,109 @@ export const logger = {
340
522
  : m.content
341
523
  console.log(` ${style('white', preview)}`)
342
524
 
343
- // Scoring components (top 3)
344
- const components = Object.entries(m.components)
345
- .sort((a, b) => b[1] - a[1])
346
- .slice(0, 3)
347
- .filter(([, v]) => v > 0.1)
348
- .map(([k, v]) => `${k}:${(v * 100).toFixed(0)}%`)
349
- .join(', ')
350
-
351
- if (components) {
352
- console.log(` ${style('dim', 'scores:')} ${components}`)
525
+ // Show which signals fired with their strengths
526
+ const firedSignals: string[] = []
527
+ if (m.signals.trigger) {
528
+ firedSignals.push(`trigger:${(m.signals.triggerStrength * 100).toFixed(0)}%`)
529
+ }
530
+ if (m.signals.tags) {
531
+ firedSignals.push(`tags:${m.signals.tagCount}`)
532
+ }
533
+ if (m.signals.domain) firedSignals.push('domain')
534
+ if (m.signals.feature) firedSignals.push('feature')
535
+ if (m.signals.content) firedSignals.push('content')
536
+ if (m.signals.vector) {
537
+ firedSignals.push(`vector:${(m.signals.vectorSimilarity * 100).toFixed(0)}%`)
353
538
  }
354
539
 
355
- // Tags
356
- if (m.semantic_tags?.length) {
357
- const tags = m.semantic_tags.slice(0, 3).join(', ')
358
- console.log(` ${style('dim', 'tags:')} ${tags}`)
540
+ if (firedSignals.length > 0) {
541
+ console.log(` ${style('cyan', 'signals:')} ${firedSignals.join(', ')}`)
359
542
  }
360
543
 
361
544
  console.log()
362
545
  })
363
546
  },
547
+
548
+ /**
549
+ * Log score distribution for diagnostics (supports both old and new algorithm)
550
+ */
551
+ logScoreDistribution(params: {
552
+ totalCandidates: number
553
+ passedGatekeeper: number
554
+ rejectedByGatekeeper: number
555
+ buckets: Record<string, number>
556
+ stats: { min: number; max: number; mean: number; stdev: number; spread: number }
557
+ percentiles: Record<string, number>
558
+ relevanceStats?: { min: number; max: number; spread: number }
559
+ triggerAnalysis?: { perfect: number; zero: number; total: number }
560
+ top5Spread?: number
561
+ compressionWarning: boolean
562
+ signalBreakdown?: {
563
+ trigger: number
564
+ tags: number
565
+ domain: number
566
+ feature: number
567
+ content: number
568
+ vector: number
569
+ total: number
570
+ }
571
+ }) {
572
+ const { totalCandidates, passedGatekeeper, rejectedByGatekeeper, buckets, stats, signalBreakdown } = params
573
+
574
+ console.log()
575
+ console.log(style('dim', ' ─'.repeat(30)))
576
+ console.log(` ${style('bold', 'ACTIVATION SIGNALS')}`)
577
+ console.log()
578
+
579
+ // Gatekeeper stats
580
+ const passRate = totalCandidates > 0 ? ((passedGatekeeper / totalCandidates) * 100).toFixed(0) : '0'
581
+ console.log(` ${style('dim', 'Activated:')} ${style('green', String(passedGatekeeper))}/${totalCandidates} (${passRate}%)`)
582
+ console.log(` ${style('dim', 'Rejected:')} ${rejectedByGatekeeper} (< 2 signals)`)
583
+ console.log()
584
+
585
+ // Signal breakdown
586
+ if (signalBreakdown && signalBreakdown.total > 0) {
587
+ console.log(` ${style('cyan', 'Signal Breakdown:')}`)
588
+ const signals = [
589
+ { name: 'trigger', count: signalBreakdown.trigger },
590
+ { name: 'tags', count: signalBreakdown.tags },
591
+ { name: 'domain', count: signalBreakdown.domain },
592
+ { name: 'feature', count: signalBreakdown.feature },
593
+ { name: 'content', count: signalBreakdown.content },
594
+ { name: 'vector', count: signalBreakdown.vector },
595
+ ]
596
+ for (const sig of signals) {
597
+ const pct = ((sig.count / signalBreakdown.total) * 100).toFixed(0)
598
+ const bar = '█'.repeat(Math.round(sig.count / signalBreakdown.total * 20))
599
+ console.log(` ${sig.name.padEnd(8)} ${bar.padEnd(20)} ${sig.count} (${pct}%)`)
600
+ }
601
+ console.log()
602
+ }
603
+
604
+ // Stats
605
+ if (stats.max > 0) {
606
+ console.log(` ${style('cyan', 'Signals:')} min=${stats.min} max=${stats.max} mean=${stats.mean}`)
607
+ console.log()
608
+ }
609
+
610
+ // Histogram by signal count
611
+ if (Object.keys(buckets).length > 0) {
612
+ console.log(` ${style('bold', 'Distribution:')}`)
613
+ const maxBucketCount = Math.max(...Object.values(buckets), 1)
614
+ const bucketOrder = ['2 signals', '3 signals', '4 signals', '5 signals', '6 signals']
615
+
616
+ for (const bucket of bucketOrder) {
617
+ const count = buckets[bucket] ?? 0
618
+ if (count > 0 || bucket === '2 signals') {
619
+ const barLen = Math.round((count / maxBucketCount) * 25)
620
+ const bar = '█'.repeat(barLen) + style('dim', '░'.repeat(25 - barLen))
621
+ const countStr = count.toString().padStart(3)
622
+ console.log(` ${style('dim', bucket.padEnd(10))} ${bar} ${style('cyan', countStr)}`)
623
+ }
624
+ }
625
+ console.log()
626
+ }
627
+ },
364
628
  }
365
629
 
366
630
  export default logger