claude-memory-layer 1.0.32 → 1.0.33

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.
@@ -76,7 +76,9 @@ function setupEventListeners() {
76
76
  b.classList.toggle('active', b.dataset.kpiWindow === window);
77
77
  });
78
78
  await loadKpiData();
79
+ await loadMemoryUsefulnessData();
79
80
  updateKpiCardsUI();
81
+ updateMemoryUsefulnessUI();
80
82
  renderKpiTrendChart();
81
83
  });
82
84
  });
@@ -4,28 +4,51 @@ async function loadKpiData() {
4
4
  .catch(() => null);
5
5
  }
6
6
 
7
+ async function loadMemoryUsefulnessData() {
8
+ state.memoryUsefulness = await fetch(apiUrl(`${API_BASE}/stats/usefulness`, { window: state.kpiWindow }))
9
+ .then(r => r.json())
10
+ .catch(() => null);
11
+ }
12
+
7
13
  async function refreshData() {
8
14
  const btn = document.getElementById('refresh-btn');
9
15
  if(btn) btn.classList.add('loading');
16
+ const refreshRequestId = (state.refreshRequestId || 0) + 1;
17
+ state.refreshRequestId = refreshRequestId;
18
+ const projectAtStart = state.currentProject;
19
+ const kpiWindowAtStart = state.kpiWindow;
10
20
 
11
21
  try {
12
- const [stats, shared, mostAccessed, helpfulness, retrievalTraces, adherenceSummary] = await Promise.all([
22
+ const [stats, shared, mostAccessed, helpfulness, memoryUsefulness, retrievalTraces, retrievalReviewQueue, adherenceSummary] = await Promise.all([
13
23
  fetch(apiUrl(`${API_BASE}/stats`)).then(r => r.json()).catch(() => null),
14
24
  fetch(apiUrl(`${API_BASE}/stats/shared`)).then(r => r.json()).catch(() => null),
15
25
  fetch(apiUrl(`${API_BASE}/stats/most-accessed`, { limit: 10 })).then(r => r.json()).catch(() => null),
16
26
  fetch(apiUrl(`${API_BASE}/stats/helpfulness`, { limit: 5 })).then(r => r.json()).catch(() => null),
27
+ fetch(apiUrl(`${API_BASE}/stats/usefulness`, { window: state.kpiWindow })).then(r => r.json()).catch(() => null),
17
28
  fetch(apiUrl(`${API_BASE}/stats/retrieval-traces`, { limit: 20 })).then(r => r.json()).catch(() => null),
29
+ fetch(apiUrl(`${API_BASE}/stats/retrieval-review-queue`, { limit: 10 })).then(r => r.json()).catch(() => null),
18
30
  fetchAdherenceSummary().catch(() => null)
19
31
  ]);
20
32
 
33
+ if (
34
+ refreshRequestId !== state.refreshRequestId ||
35
+ projectAtStart !== state.currentProject ||
36
+ kpiWindowAtStart !== state.kpiWindow
37
+ ) {
38
+ return;
39
+ }
40
+
21
41
  state.stats = stats;
22
42
  state.sharedStats = shared;
23
43
  state.mostAccessed = mostAccessed;
24
44
  state.helpfulness = helpfulness;
45
+ state.memoryUsefulness = memoryUsefulness;
25
46
  state.retrievalTraces = retrievalTraces;
47
+ state.retrievalReviewQueue = retrievalReviewQueue;
26
48
  state.adherenceSummary = adherenceSummary;
27
49
 
28
50
  await loadKpiData();
51
+ if (refreshRequestId !== state.refreshRequestId) return;
29
52
 
30
53
  updateStatsUI();
31
54
  updateSharedUI();
@@ -336,6 +359,7 @@ function updateTopAccessedEventsUI() {
336
359
 
337
360
  function updateMemoryUsageUI() {
338
361
  updateGraduationBars();
362
+ updateMemoryUsefulnessUI();
339
363
  updateHelpfulnessUI();
340
364
  updateMostHelpfulList();
341
365
  updateTopAccessedEventsUI();
@@ -431,6 +455,78 @@ function updateGraduationBars() {
431
455
  }).join('');
432
456
  }
433
457
 
458
+ function updateMemoryUsefulnessUI() {
459
+ const scoreEl = document.getElementById('memory-usefulness-score');
460
+ const summaryEl = document.getElementById('memory-usefulness-summary');
461
+ const breakdownEl = document.getElementById('memory-usefulness-breakdown');
462
+ const diagnosticsEl = document.getElementById('memory-usefulness-diagnostics');
463
+ if (!scoreEl || !summaryEl || !breakdownEl) return;
464
+
465
+ const payload = state.memoryUsefulness;
466
+ if (!payload || payload.error || !payload.score) {
467
+ scoreEl.textContent = '-';
468
+ scoreEl.className = 'memory-usefulness-score score-unknown';
469
+ summaryEl.innerHTML = '<span style="color:var(--text-muted);">No usefulness telemetry yet.</span>';
470
+ breakdownEl.innerHTML = '';
471
+ if (diagnosticsEl) diagnosticsEl.innerHTML = '';
472
+ return;
473
+ }
474
+
475
+ const score = payload.score || {};
476
+ const counts = payload.counts || {};
477
+ const label = score.label || 'unknown';
478
+ const confidencePct = ((score.confidence || 0) * 100).toFixed(0);
479
+ scoreEl.textContent = Number(score.value || 0).toFixed(1).replace(/\.0$/, '');
480
+ scoreEl.className = `memory-usefulness-score score-${label}`;
481
+
482
+ summaryEl.innerHTML = `
483
+ <span class="usefulness-pill usefulness-${escapeHtml(label)}">${escapeHtml(label)}</span>
484
+ <span><strong>${formatNumber(counts.retrievalQueries || 0)}</strong> queries</span>
485
+ <span><strong>${formatNumber(counts.rewrittenQueries || 0)}</strong> rewritten</span>
486
+ <span><strong>${formatNumber(counts.promptCount || 0)}</strong> prompts</span>
487
+ <span><strong>${formatNumber(counts.totalEvaluated || 0)}</strong> evaluated</span>
488
+ <span title="How many input signals were available for the composite score"><strong>${confidencePct}%</strong> confidence</span>
489
+ `;
490
+
491
+ const components = payload.components || [];
492
+ breakdownEl.innerHTML = components.map((component) => {
493
+ const valuePct = `${((component.value || 0) * 100).toFixed(1)}%`;
494
+ const availableClass = component.available ? '' : ' unavailable';
495
+ const availableText = component.available ? valuePct : 'n/a';
496
+ const width = Math.max(0, Math.min(100, (component.value || 0) * 100));
497
+ return `
498
+ <div class="usefulness-component${availableClass}">
499
+ <div class="usefulness-component-row">
500
+ <span>${escapeHtml(component.label || component.key)}</span>
501
+ <strong>${availableText}</strong>
502
+ </div>
503
+ <div class="usefulness-bar-track"><div class="usefulness-bar-fill" style="width:${width}%;"></div></div>
504
+ </div>
505
+ `;
506
+ }).join('');
507
+
508
+ const diagnostics = (payload.diagnostics || []).slice(0, 3);
509
+ if (!diagnosticsEl) return;
510
+ if (diagnostics.length === 0) {
511
+ diagnosticsEl.innerHTML = '<div class="usefulness-diagnostics-empty">No immediate improvement actions.</div>';
512
+ return;
513
+ }
514
+
515
+ diagnosticsEl.innerHTML = `
516
+ <div class="usefulness-diagnostics-title">Top improvement actions</div>
517
+ ${diagnostics.map((diagnostic) => `
518
+ <div class="usefulness-diagnostic usefulness-diagnostic-${escapeHtml(diagnostic.severity || 'info')}">
519
+ <div class="usefulness-diagnostic-header">
520
+ <span class="usefulness-diagnostic-severity">${escapeHtml(diagnostic.severity || 'info')}</span>
521
+ <strong>${escapeHtml(diagnostic.title || diagnostic.key || 'Improve memory usefulness')}</strong>
522
+ </div>
523
+ <div class="usefulness-diagnostic-detail">${escapeHtml(diagnostic.detail || '')}</div>
524
+ <div class="usefulness-diagnostic-action">${escapeHtml(diagnostic.action || '')}</div>
525
+ </div>
526
+ `).join('')}
527
+ `;
528
+ }
529
+
434
530
  function updateHelpfulnessUI() {
435
531
  const container = document.getElementById('helpfulness-summary');
436
532
  if (!container) return;
@@ -490,7 +586,68 @@ function updateMostHelpfulList() {
490
586
  }
491
587
 
492
588
 
589
+ function updateRetrievalReviewQueueUI() {
590
+ const summaryEl = document.getElementById('retrieval-review-summary');
591
+ const listEl = document.getElementById('retrieval-review-list');
592
+ if (!summaryEl || !listEl) return;
593
+
594
+ const payload = state.retrievalReviewQueue;
595
+ if (payload?.error) {
596
+ summaryEl.innerHTML = '<span style="color:var(--warning, #FEB019);">Retrieval review queue is temporarily unavailable.</span>';
597
+ listEl.innerHTML = '<div style="padding:12px; text-align:center; color:var(--text-muted); font-size:13px;">Unable to load bad retrieval cases right now.</div>';
598
+ return;
599
+ }
600
+ const summary = payload?.summary;
601
+ const items = payload?.items || [];
602
+
603
+ if (!summary || !Number.isFinite(summary.reviewItems) || summary.reviewItems === 0) {
604
+ summaryEl.innerHTML = '<span style="color:var(--text-muted);">No retrieval traces need review.</span>';
605
+ listEl.innerHTML = '<div style="padding:12px; text-align:center; color:var(--text-muted); font-size:13px;">No bad retrieval cases in the current scan</div>';
606
+ return;
607
+ }
608
+
609
+ summaryEl.innerHTML = `
610
+ <div style="display:flex; gap:14px; flex-wrap:wrap; font-size:12px;">
611
+ <span><strong>${formatNumber(summary.reviewItems)}</strong> review items</span>
612
+ <span><strong>${formatNumber(summary.rewrittenNoSelection || 0)}</strong> rewritten no-selection</span>
613
+ <span><strong>${formatNumber(summary.candidateNoSelection || 0)}</strong> candidate no-selection</span>
614
+ <span><strong>${formatNumber(summary.emptyCandidateSet || 0)}</strong> empty candidates</span>
615
+ </div>
616
+ `;
617
+
618
+ listEl.innerHTML = items.slice(0, 10).map((item) => {
619
+ const ts = item.createdAt ? new Date(item.createdAt).toLocaleString() : '-';
620
+ const severityColor = item.severity === 'warn' ? 'var(--warning, #FEB019)' : 'var(--accent-primary)';
621
+ const rewriteKind = item.queryRewriteKind || (item.rewritten ? 'rewritten' : 'none');
622
+ const rewriteBadge = rewriteKind && rewriteKind !== 'none'
623
+ ? `<span class="event-type-badge" title="Safe rewrite classification">${escapeHtml(rewriteKind)}</span>`
624
+ : '';
625
+ const candidateIds = (item.candidateEventIds || []).slice(0, 3)
626
+ .map((id) => `<span class="event-type-badge">${escapeHtml((id || '').slice(0, 8))}...</span>`)
627
+ .join(' ');
628
+ const selectedIds = (item.selectedEventIds || []).slice(0, 3)
629
+ .map((id) => `<span class="event-type-badge">${escapeHtml((id || '').slice(0, 8))}...</span>`)
630
+ .join(' ');
631
+ return `
632
+ <div class="shared-item" style="align-items:flex-start; border-left:2px solid ${severityColor};">
633
+ <div class="shared-info" style="align-items:flex-start; flex-direction:column; gap:4px;">
634
+ <span style="font-size:12px; color:var(--text-secondary);"><strong>${escapeHtml(item.title || 'Retrieval trace needs review')}</strong></span>
635
+ <span style="font-size:11px; color:var(--text-muted);">Trace ${escapeHtml((item.traceId || '').slice(0, 18))} · ${ts} · reason=${escapeHtml(item.reason || 'unknown')} · strategy=${escapeHtml(item.strategy || 'auto')} ${rewriteBadge}</span>
636
+ <span style="font-size:11px; color:var(--text-muted);">${escapeHtml(item.detail || '')}</span>
637
+ <span style="font-size:11px; color:var(--text-secondary);"><strong>Action:</strong> ${escapeHtml(item.action || '')}</span>
638
+ <span style="font-size:11px; color:var(--text-muted);">candidates: ${candidateIds || '-'} · selected: ${selectedIds || '-'}</span>
639
+ </div>
640
+ <div style="display:flex; flex-direction:column; align-items:flex-end; gap:2px; min-width:68px;">
641
+ <span style="font-size:13px; font-weight:600; color:${severityColor};">${Number(item.selectedCount || 0)}/${Number(item.candidateCount || 0)}</span>
642
+ <span style="font-size:10px; color:var(--text-muted);">sel/cand</span>
643
+ </div>
644
+ </div>
645
+ `;
646
+ }).join('');
647
+ }
648
+
493
649
  function updateRetrievalTraceUI() {
650
+ updateRetrievalReviewQueueUI();
494
651
  const summaryEl = document.getElementById('retrieval-trace-summary');
495
652
  const listEl = document.getElementById('retrieval-trace-list');
496
653
  if (!summaryEl || !listEl) return;
@@ -506,12 +663,14 @@ function updateRetrievalTraceUI() {
506
663
  }
507
664
 
508
665
  const selectionRate = ((stats.selectionRate || 0) * 100).toFixed(1);
666
+ const rewriteRate = ((stats.rewriteRate || 0) * 100).toFixed(1);
509
667
  summaryEl.innerHTML = `
510
668
  <div style="display:flex; gap:14px; flex-wrap:wrap; font-size:12px;">
511
669
  <span><strong>${formatNumber(stats.totalQueries)}</strong> queries</span>
512
670
  <span><strong>${Number(stats.avgCandidateCount || 0).toFixed(1)}</strong> avg candidates</span>
513
671
  <span><strong>${Number(stats.avgSelectedCount || 0).toFixed(1)}</strong> avg selected</span>
514
672
  <span><strong>${selectionRate}%</strong> selection rate</span>
673
+ <span title="Share of retrieval queries enriched with previous prompt/assistant context"><strong>${rewriteRate}%</strong> rewrite rate</span>
515
674
  </div>
516
675
  `;
517
676
 
@@ -520,6 +679,10 @@ function updateRetrievalTraceUI() {
520
679
  const confidence = t.confidence || 'n/a';
521
680
  const selected = Number(t.selectedCount || 0);
522
681
  const candidates = Number(t.candidateCount || 0);
682
+ const rewriteKind = t.queryRewriteKind || (t.rewritten ? 'rewritten' : 'none');
683
+ const rewriteBadge = rewriteKind && rewriteKind !== 'none'
684
+ ? `<span class="event-type-badge" title="Query was enriched before retrieval">${escapeHtml(rewriteKind)}</span>`
685
+ : '';
523
686
  const selectedDetails = (t.selectedDetails || []).slice(0, 2);
524
687
  const candidateDetails = (t.candidateDetails || []).slice(0, 3);
525
688
  const selectedIdsHtml = selectedDetails.length > 0
@@ -536,8 +699,8 @@ function updateRetrievalTraceUI() {
536
699
  return `
537
700
  <div class="shared-item" style="align-items:flex-start;">
538
701
  <div class="shared-info" style="align-items:flex-start; flex-direction:column; gap:4px;">
539
- <span style="font-size:12px; color:var(--text-secondary);"><strong>Q:</strong> ${escapeHtml((t.queryText || '').slice(0, 120))}</span>
540
- <span style="font-size:11px; color:var(--text-muted);">${ts} · strategy=${escapeHtml(t.strategy || 'auto')} · conf=${escapeHtml(confidence)}</span>
702
+ <span style="font-size:12px; color:var(--text-secondary);"><strong>Trace:</strong> ${escapeHtml((t.traceId || '').slice(0, 12)) || '-'}</span>
703
+ <span style="font-size:11px; color:var(--text-muted);">${ts} · strategy=${escapeHtml(t.strategy || 'auto')} · conf=${escapeHtml(confidence)} ${rewriteBadge}</span>
541
704
  <span style="font-size:11px; color:var(--text-muted);">selected IDs: ${selectedIdsHtml}</span>
542
705
  <span style="font-size:11px; color:var(--text-muted);">candidates: ${candidateDetails.map((d) => `<span class=\"event-type-badge\" style=\"cursor:pointer;\" onclick=\"openDetailModal('${d.eventId}')\">${escapeHtml((d.eventId || '').slice(0, 8))}...</span>`).join(' ') || '-'}</span>
543
706
  ${scoreBreakdownHtml}
@@ -11,7 +11,9 @@ const state = {
11
11
  sharedStats: null,
12
12
  mostAccessed: null,
13
13
  helpfulness: null,
14
+ memoryUsefulness: null,
14
15
  retrievalTraces: null,
16
+ retrievalReviewQueue: null,
15
17
  adherenceSummary: null,
16
18
  adherenceWindow: '24h',
17
19
  userPromptSearchQuery: '',
@@ -22,6 +24,7 @@ const state = {
22
24
  currentSort: 'recent',
23
25
  currentView: 'overview',
24
26
  currentProject: '', // empty = global
27
+ refreshRequestId: 0,
25
28
  projects: [],
26
29
  events: [],
27
30
  isLoading: false,
@@ -340,6 +340,18 @@
340
340
  <div id="graduation-bars"></div>
341
341
  </div>
342
342
 
343
+ <div style="margin-top:20px;">
344
+ <div class="section-label">Memory Usefulness Score</div>
345
+ <div class="memory-usefulness-panel">
346
+ <div class="memory-usefulness-header">
347
+ <div class="memory-usefulness-score score-unknown" id="memory-usefulness-score">-</div>
348
+ <div id="memory-usefulness-summary" class="memory-usefulness-summary">Loading...</div>
349
+ </div>
350
+ <div id="memory-usefulness-breakdown" class="memory-usefulness-breakdown"></div>
351
+ <div id="memory-usefulness-diagnostics" class="memory-usefulness-diagnostics"></div>
352
+ </div>
353
+ </div>
354
+
343
355
  <div style="margin-top:20px;">
344
356
  <div class="section-label">Helpfulness</div>
345
357
  <div id="helpfulness-summary" style="padding:8px 0; font-size:13px; color:var(--text-muted);">Loading...</div>
@@ -378,6 +390,14 @@
378
390
  <div style="padding:12px; text-align:center; color:var(--text-muted); font-size:13px;">Loading...</div>
379
391
  </div>
380
392
  </div>
393
+
394
+ <div style="margin-top:20px;">
395
+ <div class="section-label">Bad Retrieval Review Queue</div>
396
+ <div id="retrieval-review-summary" style="padding:8px 0; font-size:13px; color:var(--text-muted);">Loading...</div>
397
+ <div id="retrieval-review-list" class="shared-list">
398
+ <div style="padding:12px; text-align:center; color:var(--text-muted); font-size:13px;">Loading...</div>
399
+ </div>
400
+ </div>
381
401
  </div>
382
402
 
383
403
  </div>
package/dist/ui/style.css CHANGED
@@ -676,6 +676,199 @@ body {
676
676
  text-align: right;
677
677
  }
678
678
 
679
+ /* Memory Usefulness */
680
+ .memory-usefulness-panel {
681
+ padding: 12px;
682
+ border: 1px solid var(--border-subtle);
683
+ border-radius: 12px;
684
+ background: rgba(255,255,255,0.03);
685
+ }
686
+
687
+ .memory-usefulness-header {
688
+ display: flex;
689
+ align-items: center;
690
+ gap: 12px;
691
+ margin-bottom: 12px;
692
+ }
693
+
694
+ .memory-usefulness-score {
695
+ min-width: 62px;
696
+ font-size: 30px;
697
+ font-weight: 800;
698
+ line-height: 1;
699
+ letter-spacing: -1px;
700
+ }
701
+
702
+ .memory-usefulness-score.score-excellent,
703
+ .memory-usefulness-score.score-good {
704
+ color: var(--success);
705
+ }
706
+
707
+ .memory-usefulness-score.score-watch {
708
+ color: var(--warning);
709
+ }
710
+
711
+ .memory-usefulness-score.score-low {
712
+ color: var(--error);
713
+ }
714
+
715
+ .memory-usefulness-score.score-unknown {
716
+ color: var(--text-muted);
717
+ }
718
+
719
+ .memory-usefulness-summary {
720
+ display: flex;
721
+ flex-wrap: wrap;
722
+ gap: 6px 10px;
723
+ font-size: 12px;
724
+ color: var(--text-secondary);
725
+ }
726
+
727
+ .usefulness-pill {
728
+ padding: 2px 7px;
729
+ border-radius: 999px;
730
+ font-size: 10px;
731
+ font-weight: 800;
732
+ text-transform: uppercase;
733
+ letter-spacing: 0.4px;
734
+ }
735
+
736
+ .usefulness-excellent,
737
+ .usefulness-good {
738
+ background: rgba(52, 211, 153, 0.14);
739
+ color: #34D399;
740
+ }
741
+
742
+ .usefulness-watch {
743
+ background: rgba(254, 176, 25, 0.14);
744
+ color: #FEB019;
745
+ }
746
+
747
+ .usefulness-low {
748
+ background: rgba(255, 69, 96, 0.14);
749
+ color: #FF4560;
750
+ }
751
+
752
+ .usefulness-unknown {
753
+ background: rgba(148, 163, 184, 0.14);
754
+ color: #94A3B8;
755
+ }
756
+
757
+ .memory-usefulness-breakdown {
758
+ display: flex;
759
+ flex-direction: column;
760
+ gap: 8px;
761
+ }
762
+
763
+ .memory-usefulness-diagnostics {
764
+ margin-top: 12px;
765
+ display: flex;
766
+ flex-direction: column;
767
+ gap: 8px;
768
+ }
769
+
770
+ .usefulness-diagnostics-title {
771
+ font-size: 11px;
772
+ font-weight: 800;
773
+ color: var(--text-secondary);
774
+ text-transform: uppercase;
775
+ letter-spacing: 0.4px;
776
+ }
777
+
778
+ .usefulness-diagnostic {
779
+ padding: 10px;
780
+ border: 1px solid var(--border-subtle);
781
+ border-radius: 10px;
782
+ background: rgba(255,255,255,0.025);
783
+ }
784
+
785
+ .usefulness-diagnostic-warn {
786
+ border-color: rgba(254, 176, 25, 0.32);
787
+ background: rgba(254, 176, 25, 0.06);
788
+ }
789
+
790
+ .usefulness-diagnostic-info {
791
+ border-color: rgba(0, 240, 255, 0.24);
792
+ background: rgba(0, 240, 255, 0.04);
793
+ }
794
+
795
+ .usefulness-diagnostic-header {
796
+ display: flex;
797
+ align-items: center;
798
+ gap: 8px;
799
+ font-size: 12px;
800
+ color: var(--text-primary);
801
+ margin-bottom: 5px;
802
+ }
803
+
804
+ .usefulness-diagnostic-severity {
805
+ padding: 1px 6px;
806
+ border-radius: 999px;
807
+ font-size: 9px;
808
+ font-weight: 800;
809
+ text-transform: uppercase;
810
+ color: var(--bg-primary);
811
+ background: var(--warning);
812
+ }
813
+
814
+ .usefulness-diagnostic-info .usefulness-diagnostic-severity {
815
+ background: var(--accent-secondary);
816
+ }
817
+
818
+ .usefulness-diagnostic-detail,
819
+ .usefulness-diagnostic-action,
820
+ .usefulness-diagnostics-empty {
821
+ font-size: 11px;
822
+ line-height: 1.45;
823
+ color: var(--text-muted);
824
+ }
825
+
826
+ .usefulness-diagnostic-action {
827
+ margin-top: 5px;
828
+ color: var(--text-secondary);
829
+ }
830
+
831
+ .usefulness-diagnostic-action::before {
832
+ content: 'Action: ';
833
+ color: var(--accent-secondary);
834
+ font-weight: 700;
835
+ }
836
+
837
+ .usefulness-component {
838
+ opacity: 1;
839
+ }
840
+
841
+ .usefulness-component.unavailable {
842
+ opacity: 0.45;
843
+ }
844
+
845
+ .usefulness-component-row {
846
+ display: flex;
847
+ justify-content: space-between;
848
+ gap: 10px;
849
+ font-size: 11px;
850
+ color: var(--text-secondary);
851
+ margin-bottom: 4px;
852
+ }
853
+
854
+ .usefulness-component-row strong {
855
+ color: var(--text-primary);
856
+ }
857
+
858
+ .usefulness-bar-track {
859
+ height: 5px;
860
+ background: rgba(255,255,255,0.05);
861
+ border-radius: 999px;
862
+ overflow: hidden;
863
+ }
864
+
865
+ .usefulness-bar-fill {
866
+ height: 100%;
867
+ background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
868
+ border-radius: 999px;
869
+ transition: width 0.5s;
870
+ }
871
+
679
872
  /* Endless Mode */
680
873
  .endless-card {
681
874
  background: linear-gradient(135deg, rgba(16, 16, 24, 0.6) 0%, rgba(20, 20, 30, 0.6) 100%);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-memory-layer",
3
- "version": "1.0.32",
3
+ "version": "1.0.33",
4
4
  "description": "Claude Code plugin that learns from conversations to provide personalized assistance",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -30,6 +30,10 @@
30
30
  "ops:health": "npm run ops:sync-gap:report && npm run ops:sync-gap:heal && npm run ops:review:resolve",
31
31
  "ops:projects:clean-unknown": "node scripts/delete-unknown-projects.js",
32
32
  "benchmark:replay": "tsx scripts/replay-retrieval-benchmark.ts",
33
+ "eval:retrieval-replay": "tsx scripts/replay-retrieval-benchmark.ts --fixture benchmarks/replay/golden-memory-usefulness-v1.json --format markdown --no-per-query --min-query-yield 1 --min-no-match-accuracy 1 --max-forbidden-hits 0 --max-failed-queries 0",
34
+ "eval:retrieval-replay:report": "tsx scripts/replay-retrieval-benchmark.ts --fixture benchmarks/replay/golden-memory-usefulness-v1.json --format markdown --no-per-query",
35
+ "benchmark:replay:promote-review": "tsx scripts/promote-retrieval-review-queue.ts",
36
+ "benchmark:replay:validate-promotion": "tsx scripts/validate-replay-promotion-candidates.ts",
33
37
  "benchmark:qrels": "tsx scripts/generate-session-qrels.ts",
34
38
  "postinstall": "node scripts/postinstall-embedding-backend.cjs"
35
39
  },