claude-memory-layer 1.0.7 → 1.0.8
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/.claude/settings.local.json +4 -1
- package/.history/package_20260201192048.json +47 -0
- package/dist/cli/index.js +569 -39
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +192 -5
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/session-end.js +262 -18
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +262 -18
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +262 -18
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +262 -18
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +404 -39
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +413 -46
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +269 -18
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/index.html +495 -15
- package/package.json +2 -1
- package/scripts/build.ts +3 -2
- package/src/cli/index.ts +226 -0
- package/src/core/db-wrapper.ts +8 -1
- package/src/core/event-store.ts +52 -3
- package/src/core/graduation-worker.ts +171 -0
- package/src/core/graduation.ts +15 -2
- package/src/core/index.ts +1 -0
- package/src/core/retriever.ts +18 -0
- package/src/server/api/citations.ts +7 -3
- package/src/server/api/events.ts +7 -3
- package/src/server/api/search.ts +7 -3
- package/src/server/api/sessions.ts +7 -3
- package/src/server/api/stats.ts +129 -12
- package/src/server/index.ts +18 -9
- package/src/services/memory-service.ts +107 -19
- package/src/ui/index.html +495 -15
package/dist/ui/index.html
CHANGED
|
@@ -358,6 +358,158 @@
|
|
|
358
358
|
background: rgba(251, 191, 36, 0.2);
|
|
359
359
|
color: var(--warning);
|
|
360
360
|
}
|
|
361
|
+
|
|
362
|
+
/* Memory Level Navigation */
|
|
363
|
+
.level-nav {
|
|
364
|
+
display: flex;
|
|
365
|
+
gap: 8px;
|
|
366
|
+
margin-bottom: 20px;
|
|
367
|
+
flex-wrap: wrap;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.level-tab {
|
|
371
|
+
padding: 12px 20px;
|
|
372
|
+
background: var(--bg-card);
|
|
373
|
+
border: 1px solid var(--border);
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
cursor: pointer;
|
|
376
|
+
transition: all 0.2s;
|
|
377
|
+
display: flex;
|
|
378
|
+
flex-direction: column;
|
|
379
|
+
align-items: center;
|
|
380
|
+
min-width: 80px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.level-tab:hover {
|
|
384
|
+
border-color: var(--accent);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.level-tab.active {
|
|
388
|
+
background: var(--accent);
|
|
389
|
+
border-color: var(--accent);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.level-tab .level-name {
|
|
393
|
+
font-weight: 600;
|
|
394
|
+
font-size: 1.1rem;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.level-tab .level-count {
|
|
398
|
+
font-size: 0.75rem;
|
|
399
|
+
color: var(--text-secondary);
|
|
400
|
+
margin-top: 4px;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.level-tab.active .level-count {
|
|
404
|
+
color: rgba(255, 255, 255, 0.8);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.level-description {
|
|
408
|
+
font-size: 0.85rem;
|
|
409
|
+
color: var(--text-secondary);
|
|
410
|
+
margin-bottom: 16px;
|
|
411
|
+
padding: 12px;
|
|
412
|
+
background: var(--bg-card);
|
|
413
|
+
border-radius: 8px;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.level-pipeline {
|
|
417
|
+
display: flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
justify-content: center;
|
|
420
|
+
gap: 8px;
|
|
421
|
+
margin-bottom: 20px;
|
|
422
|
+
flex-wrap: wrap;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.pipeline-step {
|
|
426
|
+
padding: 8px 16px;
|
|
427
|
+
background: var(--bg-card);
|
|
428
|
+
border-radius: 20px;
|
|
429
|
+
font-size: 0.8rem;
|
|
430
|
+
opacity: 0.5;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.pipeline-step.active {
|
|
434
|
+
opacity: 1;
|
|
435
|
+
background: var(--accent);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.pipeline-arrow {
|
|
439
|
+
color: var(--text-secondary);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.event-card {
|
|
443
|
+
padding: 16px;
|
|
444
|
+
background: var(--bg-card);
|
|
445
|
+
border-radius: 8px;
|
|
446
|
+
margin-bottom: 12px;
|
|
447
|
+
border: 1px solid transparent;
|
|
448
|
+
transition: all 0.2s;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.event-card:hover {
|
|
452
|
+
border-color: var(--accent);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.event-type {
|
|
456
|
+
display: inline-block;
|
|
457
|
+
padding: 2px 8px;
|
|
458
|
+
border-radius: 4px;
|
|
459
|
+
font-size: 0.75rem;
|
|
460
|
+
font-weight: 600;
|
|
461
|
+
margin-right: 8px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.event-type.user_prompt {
|
|
465
|
+
background: rgba(59, 130, 246, 0.2);
|
|
466
|
+
color: #3b82f6;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.event-type.agent_response {
|
|
470
|
+
background: rgba(16, 185, 129, 0.2);
|
|
471
|
+
color: #10b981;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.event-type.tool_observation {
|
|
475
|
+
background: rgba(245, 158, 11, 0.2);
|
|
476
|
+
color: #f59e0b;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.event-type.session_summary {
|
|
480
|
+
background: rgba(139, 92, 246, 0.2);
|
|
481
|
+
color: #8b5cf6;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.event-content {
|
|
485
|
+
margin-top: 8px;
|
|
486
|
+
font-size: 0.9rem;
|
|
487
|
+
color: var(--text-secondary);
|
|
488
|
+
line-height: 1.5;
|
|
489
|
+
white-space: pre-wrap;
|
|
490
|
+
word-break: break-word;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.load-more-btn {
|
|
494
|
+
width: 100%;
|
|
495
|
+
padding: 12px;
|
|
496
|
+
background: var(--bg-card);
|
|
497
|
+
border: 1px solid var(--border);
|
|
498
|
+
color: var(--text-primary);
|
|
499
|
+
border-radius: 8px;
|
|
500
|
+
cursor: pointer;
|
|
501
|
+
transition: all 0.2s;
|
|
502
|
+
margin-top: 16px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.load-more-btn:hover {
|
|
506
|
+
border-color: var(--accent);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.load-more-btn:disabled {
|
|
510
|
+
opacity: 0.5;
|
|
511
|
+
cursor: not-allowed;
|
|
512
|
+
}
|
|
361
513
|
</style>
|
|
362
514
|
</head>
|
|
363
515
|
<body>
|
|
@@ -409,6 +561,108 @@
|
|
|
409
561
|
</div>
|
|
410
562
|
</div>
|
|
411
563
|
|
|
564
|
+
<!-- Memory Level Explorer -->
|
|
565
|
+
<div class="section" style="margin-bottom: 30px;">
|
|
566
|
+
<h2 class="section-title">🎯 Memory Level Explorer</h2>
|
|
567
|
+
|
|
568
|
+
<!-- Pipeline Visualization -->
|
|
569
|
+
<div class="level-pipeline" id="level-pipeline">
|
|
570
|
+
<span class="pipeline-step active" data-level="L0">L0 Raw</span>
|
|
571
|
+
<span class="pipeline-arrow">→</span>
|
|
572
|
+
<span class="pipeline-step" data-level="L1">L1 Structured</span>
|
|
573
|
+
<span class="pipeline-arrow">→</span>
|
|
574
|
+
<span class="pipeline-step" data-level="L2">L2 Validated</span>
|
|
575
|
+
<span class="pipeline-arrow">→</span>
|
|
576
|
+
<span class="pipeline-step" data-level="L3">L3 Verified</span>
|
|
577
|
+
<span class="pipeline-arrow">→</span>
|
|
578
|
+
<span class="pipeline-step" data-level="L4">L4 Active</span>
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
<!-- Level Tabs -->
|
|
582
|
+
<div class="level-nav" id="level-nav">
|
|
583
|
+
<div class="level-tab active" data-level="L0" onclick="selectLevel('L0')">
|
|
584
|
+
<span class="level-name">L0</span>
|
|
585
|
+
<span class="level-count" id="level-count-L0">0</span>
|
|
586
|
+
</div>
|
|
587
|
+
<div class="level-tab" data-level="L1" onclick="selectLevel('L1')">
|
|
588
|
+
<span class="level-name">L1</span>
|
|
589
|
+
<span class="level-count" id="level-count-L1">0</span>
|
|
590
|
+
</div>
|
|
591
|
+
<div class="level-tab" data-level="L2" onclick="selectLevel('L2')">
|
|
592
|
+
<span class="level-name">L2</span>
|
|
593
|
+
<span class="level-count" id="level-count-L2">0</span>
|
|
594
|
+
</div>
|
|
595
|
+
<div class="level-tab" data-level="L3" onclick="selectLevel('L3')">
|
|
596
|
+
<span class="level-name">L3</span>
|
|
597
|
+
<span class="level-count" id="level-count-L3">0</span>
|
|
598
|
+
</div>
|
|
599
|
+
<div class="level-tab" data-level="L4" onclick="selectLevel('L4')">
|
|
600
|
+
<span class="level-name">L4</span>
|
|
601
|
+
<span class="level-count" id="level-count-L4">0</span>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<!-- Level Description -->
|
|
606
|
+
<div class="level-description" id="level-description">
|
|
607
|
+
<strong>L0 - Raw Events:</strong> Unprocessed events from conversations. Includes user prompts, agent responses, and tool observations.
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<!-- Events List -->
|
|
611
|
+
<div id="level-events-list">
|
|
612
|
+
<div class="loading-spinner">Loading...</div>
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<!-- Load More Button -->
|
|
616
|
+
<button class="load-more-btn" id="load-more-btn" onclick="loadMoreEvents()" style="display: none;">
|
|
617
|
+
Load More Events
|
|
618
|
+
</button>
|
|
619
|
+
|
|
620
|
+
<!-- Graduation Controls -->
|
|
621
|
+
<div class="graduation-section" style="margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border);">
|
|
622
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
|
623
|
+
<h3 style="font-size: 1rem; color: var(--text-secondary);">🎓 Graduation Pipeline</h3>
|
|
624
|
+
<button class="refresh-btn" id="run-graduation-btn" onclick="runGraduation()" style="padding: 8px 16px; font-size: 0.85rem;">
|
|
625
|
+
<span id="graduation-icon">⚡</span> Run Graduation
|
|
626
|
+
</button>
|
|
627
|
+
</div>
|
|
628
|
+
<div class="graduation-criteria" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
|
|
629
|
+
<div class="criteria-card" style="background: var(--bg-card); padding: 12px; border-radius: 8px;">
|
|
630
|
+
<div style="color: var(--accent); font-weight: 600; margin-bottom: 8px;">L0 → L1</div>
|
|
631
|
+
<div style="font-size: 0.85rem; color: var(--text-secondary);">
|
|
632
|
+
• Access: ≥1<br>
|
|
633
|
+
• Confidence: ≥50%<br>
|
|
634
|
+
• Cross-session: 0
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
<div class="criteria-card" style="background: var(--bg-card); padding: 12px; border-radius: 8px;">
|
|
638
|
+
<div style="color: var(--accent); font-weight: 600; margin-bottom: 8px;">L1 → L2</div>
|
|
639
|
+
<div style="font-size: 0.85rem; color: var(--text-secondary);">
|
|
640
|
+
• Access: ≥3<br>
|
|
641
|
+
• Confidence: ≥70%<br>
|
|
642
|
+
• Cross-session: ≥1
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
<div class="criteria-card" style="background: var(--bg-card); padding: 12px; border-radius: 8px;">
|
|
646
|
+
<div style="color: var(--accent); font-weight: 600; margin-bottom: 8px;">L2 → L3</div>
|
|
647
|
+
<div style="font-size: 0.85rem; color: var(--text-secondary);">
|
|
648
|
+
• Access: ≥5<br>
|
|
649
|
+
• Confidence: ≥85%<br>
|
|
650
|
+
• Cross-session: ≥2
|
|
651
|
+
</div>
|
|
652
|
+
</div>
|
|
653
|
+
<div class="criteria-card" style="background: var(--bg-card); padding: 12px; border-radius: 8px;">
|
|
654
|
+
<div style="color: var(--accent); font-weight: 600; margin-bottom: 8px;">L3 → L4</div>
|
|
655
|
+
<div style="font-size: 0.85rem; color: var(--text-secondary);">
|
|
656
|
+
• Access: ≥10<br>
|
|
657
|
+
• Confidence: ≥92%<br>
|
|
658
|
+
• Cross-session: ≥3
|
|
659
|
+
</div>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
<div id="graduation-result" style="margin-top: 12px; display: none;"></div>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
|
|
412
666
|
<div class="main-grid">
|
|
413
667
|
<div class="section">
|
|
414
668
|
<h2 class="section-title">📋 Recent Sessions</h2>
|
|
@@ -465,6 +719,13 @@
|
|
|
465
719
|
</div>
|
|
466
720
|
</div>
|
|
467
721
|
|
|
722
|
+
<div class="section" style="margin-top: 30px;">
|
|
723
|
+
<h2 class="section-title">🔥 Most Referenced Memories</h2>
|
|
724
|
+
<div id="most-accessed-list">
|
|
725
|
+
<div class="loading-spinner">Loading...</div>
|
|
726
|
+
</div>
|
|
727
|
+
</div>
|
|
728
|
+
|
|
468
729
|
<div class="timeline-container section" style="margin-top: 30px;">
|
|
469
730
|
<h2 class="section-title">📊 Activity Timeline (Last 7 Days)</h2>
|
|
470
731
|
<div class="timeline-bar" id="timeline-bar">
|
|
@@ -480,6 +741,20 @@
|
|
|
480
741
|
const API_BASE = '/api';
|
|
481
742
|
let refreshInterval = null;
|
|
482
743
|
|
|
744
|
+
// Level Explorer State
|
|
745
|
+
let currentLevel = 'L0';
|
|
746
|
+
let currentOffset = 0;
|
|
747
|
+
const pageSize = 20;
|
|
748
|
+
let hasMoreEvents = false;
|
|
749
|
+
|
|
750
|
+
const LEVEL_DESCRIPTIONS = {
|
|
751
|
+
L0: '<strong>L0 - Raw Events:</strong> Unprocessed events from conversations. Includes user prompts, agent responses, and tool observations.',
|
|
752
|
+
L1: '<strong>L1 - Structured:</strong> Events that have been analyzed and structured. Contains session summaries and extracted patterns.',
|
|
753
|
+
L2: '<strong>L2 - Validated:</strong> Type candidates with validated schemas. Events that have been cross-referenced and verified.',
|
|
754
|
+
L3: '<strong>L3 - Verified:</strong> Cross-session validated knowledge. High-confidence information verified across multiple sessions.',
|
|
755
|
+
L4: '<strong>L4 - Active:</strong> Indexed and readily searchable memories. The most refined and accessible knowledge.'
|
|
756
|
+
};
|
|
757
|
+
|
|
483
758
|
async function fetchStats() {
|
|
484
759
|
try {
|
|
485
760
|
const response = await fetch(`${API_BASE}/stats`);
|
|
@@ -535,6 +810,171 @@
|
|
|
535
810
|
}
|
|
536
811
|
}
|
|
537
812
|
|
|
813
|
+
async function fetchMostAccessed() {
|
|
814
|
+
try {
|
|
815
|
+
const response = await fetch(`${API_BASE}/stats/most-accessed?limit=10`);
|
|
816
|
+
if (!response.ok) throw new Error('Failed to fetch most accessed');
|
|
817
|
+
return await response.json();
|
|
818
|
+
} catch (error) {
|
|
819
|
+
console.error('Most accessed fetch error:', error);
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
async function fetchLevelEvents(level, offset = 0) {
|
|
825
|
+
try {
|
|
826
|
+
const response = await fetch(`${API_BASE}/stats/levels/${level}?limit=${pageSize}&offset=${offset}`);
|
|
827
|
+
if (!response.ok) throw new Error('Failed to fetch level events');
|
|
828
|
+
return await response.json();
|
|
829
|
+
} catch (error) {
|
|
830
|
+
console.error('Level events fetch error:', error);
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function selectLevel(level) {
|
|
836
|
+
currentLevel = level;
|
|
837
|
+
currentOffset = 0;
|
|
838
|
+
|
|
839
|
+
// Update tab styles
|
|
840
|
+
document.querySelectorAll('.level-tab').forEach(tab => {
|
|
841
|
+
tab.classList.toggle('active', tab.dataset.level === level);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// Update pipeline visualization
|
|
845
|
+
document.querySelectorAll('.pipeline-step').forEach(step => {
|
|
846
|
+
step.classList.toggle('active', step.dataset.level === level);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// Update description
|
|
850
|
+
document.getElementById('level-description').innerHTML = LEVEL_DESCRIPTIONS[level];
|
|
851
|
+
|
|
852
|
+
// Load events for this level
|
|
853
|
+
loadLevelEvents(level, true);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async function loadLevelEvents(level, reset = false) {
|
|
857
|
+
const container = document.getElementById('level-events-list');
|
|
858
|
+
const loadMoreBtn = document.getElementById('load-more-btn');
|
|
859
|
+
|
|
860
|
+
if (reset) {
|
|
861
|
+
currentOffset = 0;
|
|
862
|
+
container.innerHTML = '<div class="loading-spinner">Loading...</div>';
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const data = await fetchLevelEvents(level, currentOffset);
|
|
866
|
+
|
|
867
|
+
if (!data || !data.events) {
|
|
868
|
+
if (reset) {
|
|
869
|
+
container.innerHTML = '<div class="empty-state">Failed to load events</div>';
|
|
870
|
+
}
|
|
871
|
+
loadMoreBtn.style.display = 'none';
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (data.events.length === 0 && reset) {
|
|
876
|
+
container.innerHTML = `<div class="empty-state">No events at level ${level} yet</div>`;
|
|
877
|
+
loadMoreBtn.style.display = 'none';
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const eventsHtml = data.events.map(event => {
|
|
882
|
+
const typeClass = event.eventType.replace('_', '-');
|
|
883
|
+
const time = formatTimeAgo(event.timestamp);
|
|
884
|
+
return `
|
|
885
|
+
<div class="event-card">
|
|
886
|
+
<div>
|
|
887
|
+
<span class="event-type ${event.eventType}">${event.eventType}</span>
|
|
888
|
+
<span style="color: var(--text-secondary); font-size: 0.85rem;">${time}</span>
|
|
889
|
+
<span style="color: var(--text-secondary); font-size: 0.75rem; margin-left: 8px;">
|
|
890
|
+
Session: ${event.sessionId.slice(0, 8)}...
|
|
891
|
+
</span>
|
|
892
|
+
</div>
|
|
893
|
+
<div class="event-content">${escapeHtml(event.content)}</div>
|
|
894
|
+
</div>
|
|
895
|
+
`;
|
|
896
|
+
}).join('');
|
|
897
|
+
|
|
898
|
+
if (reset) {
|
|
899
|
+
container.innerHTML = eventsHtml;
|
|
900
|
+
} else {
|
|
901
|
+
container.innerHTML += eventsHtml;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
hasMoreEvents = data.hasMore;
|
|
905
|
+
loadMoreBtn.style.display = hasMoreEvents ? 'block' : 'none';
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function loadMoreEvents() {
|
|
909
|
+
currentOffset += pageSize;
|
|
910
|
+
loadLevelEvents(currentLevel, false);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
async function runGraduation() {
|
|
914
|
+
const btn = document.getElementById('run-graduation-btn');
|
|
915
|
+
const icon = document.getElementById('graduation-icon');
|
|
916
|
+
const resultDiv = document.getElementById('graduation-result');
|
|
917
|
+
|
|
918
|
+
btn.classList.add('loading');
|
|
919
|
+
icon.textContent = '⏳';
|
|
920
|
+
resultDiv.style.display = 'none';
|
|
921
|
+
|
|
922
|
+
try {
|
|
923
|
+
const response = await fetch('/api/stats/graduation/run', { method: 'POST' });
|
|
924
|
+
const data = await response.json();
|
|
925
|
+
|
|
926
|
+
if (data.success) {
|
|
927
|
+
let message = `✅ Evaluated ${data.evaluated} events, graduated ${data.graduated}`;
|
|
928
|
+
if (data.graduated > 0 && Object.keys(data.byLevel).length > 0) {
|
|
929
|
+
const levels = Object.entries(data.byLevel)
|
|
930
|
+
.map(([level, count]) => `${level}: ${count}`)
|
|
931
|
+
.join(', ');
|
|
932
|
+
message += ` (${levels})`;
|
|
933
|
+
}
|
|
934
|
+
resultDiv.innerHTML = `<div style="padding: 12px; background: rgba(74, 222, 128, 0.1); border-radius: 8px; color: var(--success);">${message}</div>`;
|
|
935
|
+
resultDiv.style.display = 'block';
|
|
936
|
+
|
|
937
|
+
// Refresh data to show updated counts
|
|
938
|
+
if (data.graduated > 0) {
|
|
939
|
+
setTimeout(() => {
|
|
940
|
+
refreshData();
|
|
941
|
+
}, 500);
|
|
942
|
+
}
|
|
943
|
+
} else {
|
|
944
|
+
resultDiv.innerHTML = `<div style="padding: 12px; background: rgba(233, 69, 96, 0.1); border-radius: 8px; color: var(--accent);">❌ ${data.error || 'Graduation failed'}</div>`;
|
|
945
|
+
resultDiv.style.display = 'block';
|
|
946
|
+
}
|
|
947
|
+
} catch (error) {
|
|
948
|
+
resultDiv.innerHTML = `<div style="padding: 12px; background: rgba(233, 69, 96, 0.1); border-radius: 8px; color: var(--accent);">❌ Error: ${error.message}</div>`;
|
|
949
|
+
resultDiv.style.display = 'block';
|
|
950
|
+
} finally {
|
|
951
|
+
btn.classList.remove('loading');
|
|
952
|
+
icon.textContent = '⚡';
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function escapeHtml(text) {
|
|
957
|
+
const div = document.createElement('div');
|
|
958
|
+
div.textContent = text;
|
|
959
|
+
return div.innerHTML;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function updateLevelCounts(stats) {
|
|
963
|
+
if (!stats || !stats.levelStats) return;
|
|
964
|
+
|
|
965
|
+
// Reset all counts to 0
|
|
966
|
+
['L0', 'L1', 'L2', 'L3', 'L4'].forEach(level => {
|
|
967
|
+
const el = document.getElementById(`level-count-${level}`);
|
|
968
|
+
if (el) el.textContent = '0';
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// Update with actual counts
|
|
972
|
+
stats.levelStats.forEach(stat => {
|
|
973
|
+
const el = document.getElementById(`level-count-${stat.level}`);
|
|
974
|
+
if (el) el.textContent = formatNumber(stat.count);
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
|
|
538
978
|
async function searchMemories(query) {
|
|
539
979
|
try {
|
|
540
980
|
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(query)}&limit=10`);
|
|
@@ -622,19 +1062,24 @@
|
|
|
622
1062
|
return;
|
|
623
1063
|
}
|
|
624
1064
|
|
|
625
|
-
container.innerHTML = sessions.map(session =>
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
<
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
1065
|
+
container.innerHTML = sessions.map(session => {
|
|
1066
|
+
// Handle both API formats: id/sessionId, lastEventAt/lastActivity
|
|
1067
|
+
const sessionId = session.id || session.sessionId || 'unknown';
|
|
1068
|
+
const lastTime = session.lastEventAt || session.lastActivity || session.startedAt || session.startTime;
|
|
1069
|
+
const eventCount = session.eventCount || 0;
|
|
1070
|
+
|
|
1071
|
+
return `
|
|
1072
|
+
<li class="session-item">
|
|
1073
|
+
<div class="session-header">
|
|
1074
|
+
<span class="session-id">${sessionId.slice(0, 16)}...</span>
|
|
1075
|
+
<span class="session-time">${formatTimeAgo(lastTime)}</span>
|
|
1076
|
+
</div>
|
|
1077
|
+
<div class="session-meta">
|
|
1078
|
+
<span>📝 ${eventCount} events</span>
|
|
1079
|
+
</div>
|
|
1080
|
+
</li>
|
|
1081
|
+
`;
|
|
1082
|
+
}).join('');
|
|
638
1083
|
}
|
|
639
1084
|
|
|
640
1085
|
function updateTimeline(timeline) {
|
|
@@ -666,6 +1111,37 @@
|
|
|
666
1111
|
labels.innerHTML = `<span>${firstDate}</span><span>${lastDate}</span>`;
|
|
667
1112
|
}
|
|
668
1113
|
|
|
1114
|
+
function updateMostAccessed(data) {
|
|
1115
|
+
const container = document.getElementById('most-accessed-list');
|
|
1116
|
+
|
|
1117
|
+
if (!data || !data.memories || data.memories.length === 0) {
|
|
1118
|
+
container.innerHTML = '<div class="empty-state">No memory access data yet. Memories will appear here as they are referenced.</div>';
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
container.innerHTML = data.memories.map(memory => {
|
|
1123
|
+
const accessBars = '█'.repeat(Math.min(10, memory.accessCount));
|
|
1124
|
+
const accessEmpty = '░'.repeat(Math.max(0, 10 - memory.accessCount));
|
|
1125
|
+
const lastAccessed = memory.lastAccessed ? formatTimeAgo(memory.lastAccessed) : 'Never';
|
|
1126
|
+
|
|
1127
|
+
return `
|
|
1128
|
+
<div class="session-item">
|
|
1129
|
+
<div class="session-header">
|
|
1130
|
+
<span class="session-id">🧠 ${memory.topics?.slice(0, 2).join(', ') || 'Memory'}</span>
|
|
1131
|
+
<span class="session-time">${lastAccessed}</span>
|
|
1132
|
+
</div>
|
|
1133
|
+
<div style="margin: 8px 0;">
|
|
1134
|
+
<span style="color: var(--text-secondary); font-size: 0.85rem;">Access count:</span>
|
|
1135
|
+
<span style="font-family: monospace; color: var(--accent);">[${accessBars}${accessEmpty}] ${memory.accessCount}</span>
|
|
1136
|
+
</div>
|
|
1137
|
+
<p style="color: var(--text-secondary); font-size: 0.9rem; margin-top: 8px;">
|
|
1138
|
+
${memory.summary?.slice(0, 150) || 'No summary'}${memory.summary?.length > 150 ? '...' : ''}
|
|
1139
|
+
</p>
|
|
1140
|
+
</div>
|
|
1141
|
+
`;
|
|
1142
|
+
}).join('');
|
|
1143
|
+
}
|
|
1144
|
+
|
|
669
1145
|
function showSearchResults(results) {
|
|
670
1146
|
const container = document.getElementById('search-results');
|
|
671
1147
|
const content = document.getElementById('search-results-content');
|
|
@@ -714,12 +1190,13 @@
|
|
|
714
1190
|
document.getElementById('refresh-icon').textContent = '⏳';
|
|
715
1191
|
|
|
716
1192
|
try {
|
|
717
|
-
const [stats, sharedStats, endlessStatus, sessions, timeline] = await Promise.all([
|
|
1193
|
+
const [stats, sharedStats, endlessStatus, sessions, timeline, mostAccessed] = await Promise.all([
|
|
718
1194
|
fetchStats(),
|
|
719
1195
|
fetchSharedStats(),
|
|
720
1196
|
fetchEndlessStatus(),
|
|
721
1197
|
fetchSessions(),
|
|
722
|
-
fetchTimeline()
|
|
1198
|
+
fetchTimeline(),
|
|
1199
|
+
fetchMostAccessed()
|
|
723
1200
|
]);
|
|
724
1201
|
|
|
725
1202
|
updateStats(stats);
|
|
@@ -727,6 +1204,9 @@
|
|
|
727
1204
|
updateEndlessStatus(endlessStatus);
|
|
728
1205
|
updateSessions(sessions?.sessions || sessions || []);
|
|
729
1206
|
updateTimeline(timeline);
|
|
1207
|
+
updateMostAccessed(mostAccessed);
|
|
1208
|
+
updateLevelCounts(stats);
|
|
1209
|
+
loadLevelEvents(currentLevel, true);
|
|
730
1210
|
} catch (error) {
|
|
731
1211
|
console.error('Refresh error:', error);
|
|
732
1212
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-memory-layer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Claude Code plugin that learns from conversations to provide personalized assistance",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"node": ">=18.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"@hono/node-server": "^1.13.0",
|
|
32
33
|
"@lancedb/lancedb": "^0.5.0",
|
|
33
34
|
"@xenova/transformers": "^2.17.0",
|
|
34
35
|
"commander": "^12.0.0",
|
package/scripts/build.ts
CHANGED
|
@@ -23,6 +23,8 @@ const commonOptions: esbuild.BuildOptions = {
|
|
|
23
23
|
format: 'esm',
|
|
24
24
|
sourcemap: true,
|
|
25
25
|
external: [
|
|
26
|
+
'@hono/node-server',
|
|
27
|
+
'@hono/node-server/serve-static',
|
|
26
28
|
'@lancedb/lancedb',
|
|
27
29
|
'@xenova/transformers',
|
|
28
30
|
'duckdb',
|
|
@@ -30,8 +32,7 @@ const commonOptions: esbuild.BuildOptions = {
|
|
|
30
32
|
'zod',
|
|
31
33
|
'hono',
|
|
32
34
|
'hono/cors',
|
|
33
|
-
'hono/logger'
|
|
34
|
-
'hono/bun'
|
|
35
|
+
'hono/logger'
|
|
35
36
|
],
|
|
36
37
|
banner: {
|
|
37
38
|
js: `import { createRequire } from 'module';
|