prism-mcp-server 4.6.1 β 5.2.0
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/README.md +408 -1306
- package/dist/dashboard/server.js +391 -22
- package/dist/dashboard/ui.js +363 -17
- package/dist/server.js +15 -2
- package/dist/storage/sqlite.js +277 -6
- package/dist/storage/supabase.js +58 -0
- package/dist/storage/supabaseMigrations.js +104 -1
- package/dist/tools/compactionHandler.js +17 -7
- package/dist/tools/index.js +2 -2
- package/dist/tools/sessionMemoryDefinitions.js +70 -0
- package/dist/tools/sessionMemoryHandlers.js +167 -9
- package/dist/utils/migration/claudeAdapter.js +131 -0
- package/dist/utils/migration/geminiAdapter.js +87 -0
- package/dist/utils/migration/openaiAdapter.js +88 -0
- package/dist/utils/migration/types.js +18 -0
- package/dist/utils/migration/utils.js +99 -0
- package/dist/utils/testUniversalImporter.js +10 -0
- package/dist/utils/turboquant.js +730 -0
- package/dist/utils/universalImporter.js +295 -0
- package/package.json +8 -4
package/dist/dashboard/ui.js
CHANGED
|
@@ -492,6 +492,14 @@ export function renderDashboardHTML(version) {
|
|
|
492
492
|
border: 1px solid rgba(245,158,11,0.3); border-radius: 6px; cursor: pointer; transition: all 0.2s;
|
|
493
493
|
}
|
|
494
494
|
.ttl-save-btn:hover { background: rgba(245,158,11,0.3); }
|
|
495
|
+
.node-editor-panel {
|
|
496
|
+
background: var(--bg-card);
|
|
497
|
+
border: 1px solid var(--border-glow);
|
|
498
|
+
border-radius: 8px;
|
|
499
|
+
padding: 1rem;
|
|
500
|
+
margin-top: 1rem;
|
|
501
|
+
display: none;
|
|
502
|
+
}
|
|
495
503
|
</style>
|
|
496
504
|
</head>
|
|
497
505
|
<body>
|
|
@@ -593,6 +601,53 @@ export function renderDashboardHTML(version) {
|
|
|
593
601
|
<div style="font-size:0.7rem;color:var(--text-muted);margin-top:0.4rem">0 = disabled. Min 7 days. Rollups are never expired.</div>
|
|
594
602
|
</div>
|
|
595
603
|
|
|
604
|
+
<!-- Universal History Import (v5.2) -->
|
|
605
|
+
<div class="card" id="importCard" style="display:none">
|
|
606
|
+
<div class="card-title"><span class="dot" style="background:var(--accent-cyan)"></span> Import History π₯</div>
|
|
607
|
+
<div style="margin-bottom:0.75rem">
|
|
608
|
+
<label style="font-size:0.78rem;color:var(--text-muted);display:block;margin-bottom:0.3rem">Source File</label>
|
|
609
|
+
<div style="display:flex;gap:0.4rem;align-items:center">
|
|
610
|
+
<input type="text" id="importPath" class="ttl-input" style="flex:1;text-align:left;font-size:0.82rem;padding:0.45rem 0.65rem" placeholder="/path/to/conversations.jsonl">
|
|
611
|
+
<input type="file" id="importFileInput" accept=".jsonl,.json,.ndjson" style="display:none">
|
|
612
|
+
<button class="lc-btn compact" onclick="document.getElementById('importFileInput').click()" style="flex:none;padding:0.45rem 0.75rem;font-size:0.82rem;white-space:nowrap" title="Choose a file from your computer">
|
|
613
|
+
π Browse
|
|
614
|
+
</button>
|
|
615
|
+
<button class="lc-btn" onclick="clearImportFile()" id="importClearBtn" style="flex:none;padding:0.45rem 0.55rem;font-size:0.82rem;display:none;background:rgba(244,63,94,0.15);border-color:rgba(244,63,94,0.3);color:var(--accent-rose)" title="Clear selection">
|
|
616
|
+
β
|
|
617
|
+
</button>
|
|
618
|
+
</div>
|
|
619
|
+
<div id="importFileInfo" style="display:none;margin-top:0.35rem;font-size:0.72rem;color:var(--accent-cyan)"></div>
|
|
620
|
+
</div>
|
|
621
|
+
<div style="display:flex;gap:0.5rem;margin-bottom:0.75rem;flex-wrap:wrap">
|
|
622
|
+
<div style="flex:1;min-width:120px">
|
|
623
|
+
<label style="font-size:0.78rem;color:var(--text-muted);display:block;margin-bottom:0.3rem">Format</label>
|
|
624
|
+
<select id="importFormat" class="ttl-input" style="width:100%;text-align:left;font-size:0.82rem;padding:0.35rem 0.5rem;cursor:pointer">
|
|
625
|
+
<option value="">Auto-detect</option>
|
|
626
|
+
<option value="claude">Claude Code (.jsonl)</option>
|
|
627
|
+
<option value="gemini">Gemini (.json)</option>
|
|
628
|
+
<option value="openai">OpenAI (.json)</option>
|
|
629
|
+
</select>
|
|
630
|
+
</div>
|
|
631
|
+
<div style="flex:1;min-width:120px">
|
|
632
|
+
<label style="font-size:0.78rem;color:var(--text-muted);display:block;margin-bottom:0.3rem">Target Project</label>
|
|
633
|
+
<input type="text" id="importProject" class="ttl-input" style="width:100%;text-align:left;font-size:0.82rem;padding:0.45rem 0.65rem" placeholder="(auto from file)">
|
|
634
|
+
</div>
|
|
635
|
+
</div>
|
|
636
|
+
<div style="display:flex;gap:0.5rem;align-items:center">
|
|
637
|
+
<button class="lc-btn compact" id="importBtn" onclick="runImport(false)" style="flex:1">
|
|
638
|
+
π₯ Import
|
|
639
|
+
</button>
|
|
640
|
+
<button class="lc-btn export" id="importDryBtn" onclick="runImport(true)" style="flex:1" title="Validate without writing to storage">
|
|
641
|
+
π§ͺ Dry Run
|
|
642
|
+
</button>
|
|
643
|
+
</div>
|
|
644
|
+
<div id="importResult" style="display:none;margin-top:0.75rem;padding:0.65rem 0.85rem;border-radius:var(--radius-sm);font-size:0.82rem;line-height:1.5"></div>
|
|
645
|
+
<div style="font-size:0.68rem;color:var(--text-muted);margin-top:0.5rem">
|
|
646
|
+
Click <strong>Browse</strong> to pick a file, or type a server-side path.<br>
|
|
647
|
+
Supports Claude Code (.jsonl), Gemini (.json), and OpenAI (.json).
|
|
648
|
+
</div>
|
|
649
|
+
</div>
|
|
650
|
+
|
|
596
651
|
<div class="card" id="briefingCard" style="display:none">
|
|
597
652
|
<div class="card-title"><span class="dot" style="background:var(--accent-amber)"></span> Morning Briefing π
</div>
|
|
598
653
|
<div class="briefing-text" id="briefingText"></div>
|
|
@@ -608,14 +663,48 @@ export function renderDashboardHTML(version) {
|
|
|
608
663
|
<!-- Right Column -->
|
|
609
664
|
<div class="grid" style="align-content: start;">
|
|
610
665
|
|
|
611
|
-
<!-- Neural Graph (v2.3.0) -->
|
|
666
|
+
<!-- Neural Graph (v2.3.0 / v5.1) -->
|
|
612
667
|
<div class="card">
|
|
613
668
|
<div class="card-title">
|
|
614
669
|
<span class="dot" style="background:var(--accent-blue)"></span>
|
|
615
670
|
Neural Graph πΈοΈ
|
|
616
671
|
<button onclick="loadGraph()" class="refresh-btn">β»</button>
|
|
617
672
|
</div>
|
|
673
|
+
|
|
674
|
+
<!-- v5.1 Graph Filters -->
|
|
675
|
+
<div style="display:flex; gap:0.5rem; margin-bottom:1rem; flex-wrap:wrap;">
|
|
676
|
+
<select id="graphProjectFilter" class="input-modern" style="min-width:120px; font-size:0.75rem; padding:0.3rem 0.5rem" onchange="loadGraph()">
|
|
677
|
+
<option value="">All Projects</option>
|
|
678
|
+
</select>
|
|
679
|
+
<select id="graphDaysFilter" class="input-modern" style="font-size:0.75rem; padding:0.3rem 0.5rem" onchange="loadGraph()">
|
|
680
|
+
<option value="">All Time</option>
|
|
681
|
+
<option value="7">Last 7 Days</option>
|
|
682
|
+
<option value="30">Last 30 Days</option>
|
|
683
|
+
<option value="90">Last 90 Days</option>
|
|
684
|
+
</select>
|
|
685
|
+
<select id="graphImportanceFilter" class="input-modern" style="font-size:0.75rem; padding:0.3rem 0.5rem" onchange="loadGraph()">
|
|
686
|
+
<option value="">Any Importance</option>
|
|
687
|
+
<option value="5">Importance >= 5</option>
|
|
688
|
+
<option value="7">Graduated (>= 7)</option>
|
|
689
|
+
</select>
|
|
690
|
+
</div>
|
|
691
|
+
|
|
618
692
|
<div id="network-container">Loading nodes...</div>
|
|
693
|
+
|
|
694
|
+
<!-- v5.1 Node Editor Panel -->
|
|
695
|
+
<div id="nodeEditorPanel" class="node-editor-panel">
|
|
696
|
+
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:0.8rem;">
|
|
697
|
+
<h4 id="nodeEditorTitle" style="margin:0; font-size:0.9rem; color:var(--text-primary);">Node Name</h4>
|
|
698
|
+
<span id="nodeEditorGroup" class="badge">category</span>
|
|
699
|
+
</div>
|
|
700
|
+
|
|
701
|
+
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:0.3rem">Rename (Or leave empty to delete)</label>
|
|
702
|
+
<div style="display:flex; gap:0.5rem;">
|
|
703
|
+
<input type="text" id="nodeEditorInput" class="input-modern" style="flex:1; font-size:0.8rem; padding:0.3rem 0.6rem" placeholder="New keyword name...">
|
|
704
|
+
<button onclick="submitNodeEdit()" class="btn-modern" style="padding:0.3rem 0.8rem; font-size:0.8rem">Apply</button>
|
|
705
|
+
<button onclick="document.getElementById('nodeEditorPanel').style.display='none'" class="btn-modern" style="background:transparent; border-color:var(--border-subtle); padding:0.3rem 0.8rem; font-size:0.8rem">Cancel</button>
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
619
708
|
</div>
|
|
620
709
|
|
|
621
710
|
<!-- Time Travel -->
|
|
@@ -1079,6 +1168,12 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1079
1168
|
if (data.projects && data.projects.length > 0) {
|
|
1080
1169
|
select.innerHTML = '<option value="">β Select a project β</option>' +
|
|
1081
1170
|
data.projects.map(function(p) { return '<option value="' + p + '">' + p + '</option>'; }).join('');
|
|
1171
|
+
|
|
1172
|
+
var gp = document.getElementById('graphProjectFilter');
|
|
1173
|
+
if (gp) {
|
|
1174
|
+
gp.innerHTML = '<option value="">All Projects</option>' +
|
|
1175
|
+
data.projects.map(function(p) { return '<option value="' + p + '">' + p + '</option>'; }).join('');
|
|
1176
|
+
}
|
|
1082
1177
|
} else {
|
|
1083
1178
|
select.innerHTML = '<option value="">No projects found</option>';
|
|
1084
1179
|
}
|
|
@@ -1232,9 +1327,10 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1232
1327
|
document.getElementById('content').className = 'grid grid-main fade-in';
|
|
1233
1328
|
document.getElementById('content').style.display = 'grid';
|
|
1234
1329
|
|
|
1235
|
-
// v3.1: Analytics + Lifecycle Controls
|
|
1330
|
+
// v3.1: Analytics + Lifecycle Controls + Import
|
|
1236
1331
|
document.getElementById('analyticsCard').style.display = 'block';
|
|
1237
1332
|
document.getElementById('lifecycleCard').style.display = 'block';
|
|
1333
|
+
document.getElementById('importCard').style.display = 'block';
|
|
1238
1334
|
loadAnalytics(project);
|
|
1239
1335
|
loadRetention(project);
|
|
1240
1336
|
|
|
@@ -1355,6 +1451,122 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1355
1451
|
}
|
|
1356
1452
|
}
|
|
1357
1453
|
|
|
1454
|
+
// βββ v5.2: Universal History Import βββββββββββββββββββββββββββββββ
|
|
1455
|
+
|
|
1456
|
+
// Track the picked file for upload mode
|
|
1457
|
+
var _importPickedFile = null;
|
|
1458
|
+
|
|
1459
|
+
document.getElementById('importFileInput').addEventListener('change', function(e) {
|
|
1460
|
+
var file = e.target.files[0];
|
|
1461
|
+
if (!file) return;
|
|
1462
|
+
_importPickedFile = file;
|
|
1463
|
+
var pathInput = document.getElementById('importPath');
|
|
1464
|
+
pathInput.value = file.name;
|
|
1465
|
+
document.getElementById('importClearBtn').style.display = 'inline-flex';
|
|
1466
|
+
var infoEl = document.getElementById('importFileInfo');
|
|
1467
|
+
var sizeKB = (file.size / 1024).toFixed(1);
|
|
1468
|
+
var sizeMB = (file.size / (1024 * 1024)).toFixed(1);
|
|
1469
|
+
infoEl.textContent = 'π ' + file.name + ' (' + (file.size > 1048576 ? sizeMB + ' MB' : sizeKB + ' KB') + ')';
|
|
1470
|
+
infoEl.style.display = 'block';
|
|
1471
|
+
|
|
1472
|
+
// Auto-detect format from extension
|
|
1473
|
+
var fmt = document.getElementById('importFormat');
|
|
1474
|
+
if (file.name.endsWith('.jsonl') || file.name.endsWith('.ndjson')) {
|
|
1475
|
+
fmt.value = 'claude';
|
|
1476
|
+
} else if (file.name.toLowerCase().includes('gemini')) {
|
|
1477
|
+
fmt.value = 'gemini';
|
|
1478
|
+
} else if (file.name.toLowerCase().includes('openai') || file.name.toLowerCase().includes('chatgpt')) {
|
|
1479
|
+
fmt.value = 'openai';
|
|
1480
|
+
} else {
|
|
1481
|
+
fmt.value = '';
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
function clearImportFile() {
|
|
1486
|
+
_importPickedFile = null;
|
|
1487
|
+
document.getElementById('importPath').value = '';
|
|
1488
|
+
document.getElementById('importFileInput').value = '';
|
|
1489
|
+
document.getElementById('importClearBtn').style.display = 'none';
|
|
1490
|
+
document.getElementById('importFileInfo').style.display = 'none';
|
|
1491
|
+
document.getElementById('importResult').style.display = 'none';
|
|
1492
|
+
document.getElementById('importFormat').value = '';
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
async function runImport(dryRun) {
|
|
1496
|
+
var filePath = document.getElementById('importPath').value.trim();
|
|
1497
|
+
if (!filePath && !_importPickedFile) { showToast('β Pick a file or enter a path', true); return; }
|
|
1498
|
+
|
|
1499
|
+
var format = document.getElementById('importFormat').value || undefined;
|
|
1500
|
+
var project = document.getElementById('importProject').value.trim() || undefined;
|
|
1501
|
+
var importBtn = document.getElementById('importBtn');
|
|
1502
|
+
var dryBtn = document.getElementById('importDryBtn');
|
|
1503
|
+
var resultEl = document.getElementById('importResult');
|
|
1504
|
+
|
|
1505
|
+
importBtn.disabled = true;
|
|
1506
|
+
dryBtn.disabled = true;
|
|
1507
|
+
var activeBtn = dryRun ? dryBtn : importBtn;
|
|
1508
|
+
var origText = activeBtn.innerHTML;
|
|
1509
|
+
activeBtn.innerHTML = dryRun ? 'π Validating...' : 'π Importing...';
|
|
1510
|
+
|
|
1511
|
+
resultEl.style.display = 'block';
|
|
1512
|
+
resultEl.style.background = 'rgba(139,92,246,0.1)';
|
|
1513
|
+
resultEl.style.border = '1px solid rgba(139,92,246,0.25)';
|
|
1514
|
+
resultEl.style.color = 'var(--accent-purple)';
|
|
1515
|
+
resultEl.innerHTML = '<span class="spinner" style="width:16px;height:16px;border-width:2px;margin-right:0.4rem;vertical-align:middle"></span> ' +
|
|
1516
|
+
(dryRun ? 'Validating file...' : 'Importing turns...');
|
|
1517
|
+
|
|
1518
|
+
try {
|
|
1519
|
+
var endpoint, body, headers;
|
|
1520
|
+
|
|
1521
|
+
if (_importPickedFile) {
|
|
1522
|
+
// Upload mode: read file and send as base64
|
|
1523
|
+
var content = await _importPickedFile.text();
|
|
1524
|
+
endpoint = '/api/import-upload';
|
|
1525
|
+
headers = {'Content-Type':'application/json'};
|
|
1526
|
+
body = JSON.stringify({
|
|
1527
|
+
filename: _importPickedFile.name,
|
|
1528
|
+
content: content,
|
|
1529
|
+
format: format,
|
|
1530
|
+
project: project,
|
|
1531
|
+
dryRun: dryRun
|
|
1532
|
+
});
|
|
1533
|
+
} else {
|
|
1534
|
+
// Path mode: just send the server-side path
|
|
1535
|
+
endpoint = '/api/import';
|
|
1536
|
+
headers = {'Content-Type':'application/json'};
|
|
1537
|
+
body = JSON.stringify({ path: filePath, format: format, project: project, dryRun: dryRun });
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
var res = await fetch(endpoint, { method: 'POST', headers: headers, body: body });
|
|
1541
|
+
var d = await res.json();
|
|
1542
|
+
if (res.ok && d.ok) {
|
|
1543
|
+
resultEl.style.background = 'rgba(16,185,129,0.1)';
|
|
1544
|
+
resultEl.style.border = '1px solid rgba(16,185,129,0.25)';
|
|
1545
|
+
resultEl.style.color = 'var(--accent-green)';
|
|
1546
|
+
resultEl.innerHTML = 'β
' + escapeHtml(d.message) +
|
|
1547
|
+
'<div style="margin-top:0.4rem;font-size:0.75rem;color:var(--text-muted)">' +
|
|
1548
|
+
'Conversations: ' + (d.conversationCount || 0) + ' Β· Turns: ' + (d.successCount || 0) +
|
|
1549
|
+
(d.skipCount ? ' Β· Skipped: ' + d.skipCount : '') +
|
|
1550
|
+
(d.failCount ? ' Β· Failed: ' + d.failCount : '') + '</div>';
|
|
1551
|
+
if (!dryRun) { showToast('β Import complete'); loadProject(); }
|
|
1552
|
+
} else {
|
|
1553
|
+
resultEl.style.background = 'rgba(244,63,94,0.1)';
|
|
1554
|
+
resultEl.style.border = '1px solid rgba(244,63,94,0.25)';
|
|
1555
|
+
resultEl.style.color = 'var(--accent-rose)';
|
|
1556
|
+
resultEl.innerHTML = 'β ' + escapeHtml(d.error || 'Import failed');
|
|
1557
|
+
}
|
|
1558
|
+
} catch(e) {
|
|
1559
|
+
resultEl.style.background = 'rgba(244,63,94,0.1)';
|
|
1560
|
+
resultEl.style.border = '1px solid rgba(244,63,94,0.25)';
|
|
1561
|
+
resultEl.style.color = 'var(--accent-rose)';
|
|
1562
|
+
resultEl.innerHTML = 'β ' + escapeHtml(e.message);
|
|
1563
|
+
} finally {
|
|
1564
|
+
importBtn.disabled = false;
|
|
1565
|
+
dryBtn.disabled = false;
|
|
1566
|
+
activeBtn.innerHTML = origText;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1358
1570
|
function showToast(msg, isErr) {
|
|
1359
1571
|
var el = document.getElementById('fixedToast');
|
|
1360
1572
|
if (!el) return;
|
|
@@ -1383,14 +1595,24 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1383
1595
|
// Allow Enter key in select to trigger load
|
|
1384
1596
|
document.getElementById('projectSelect').addEventListener('change', loadProject);
|
|
1385
1597
|
|
|
1386
|
-
// βββ Neural Graph (v2.3.0) βββ
|
|
1598
|
+
// βββ Neural Graph (v2.3.0 / v5.1) βββ
|
|
1387
1599
|
// Renders a force-directed graph of projects β keywords β categories
|
|
1388
1600
|
async function loadGraph() {
|
|
1389
1601
|
var container = document.getElementById('network-container');
|
|
1390
1602
|
if (!container) return;
|
|
1391
1603
|
|
|
1604
|
+
var proj = document.getElementById('graphProjectFilter') ? document.getElementById('graphProjectFilter').value : '';
|
|
1605
|
+
var days = document.getElementById('graphDaysFilter') ? document.getElementById('graphDaysFilter').value : '';
|
|
1606
|
+
var imp = document.getElementById('graphImportanceFilter') ? document.getElementById('graphImportanceFilter').value : '';
|
|
1607
|
+
|
|
1608
|
+
var qs = [];
|
|
1609
|
+
if (proj) qs.push('project=' + encodeURIComponent(proj));
|
|
1610
|
+
if (days) qs.push('days=' + encodeURIComponent(days));
|
|
1611
|
+
if (imp) qs.push('min_importance=' + encodeURIComponent(imp));
|
|
1612
|
+
var url = '/api/graph' + (qs.length ? '?' + qs.join('&') : '');
|
|
1613
|
+
|
|
1392
1614
|
try {
|
|
1393
|
-
var res = await fetch(
|
|
1615
|
+
var res = await fetch(url);
|
|
1394
1616
|
var data = await res.json();
|
|
1395
1617
|
|
|
1396
1618
|
// Empty state β no ledger entries yet
|
|
@@ -1399,54 +1621,178 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1399
1621
|
return;
|
|
1400
1622
|
}
|
|
1401
1623
|
|
|
1624
|
+
// Safety cap: Vis.js Barnes-Hut physics blows the call stack at ~400+ nodes.
|
|
1625
|
+
// Truncate to 200 nodes max, keeping project and category nodes first.
|
|
1626
|
+
var MAX_NODES = 200;
|
|
1627
|
+
if (data.nodes.length > MAX_NODES) {
|
|
1628
|
+
// Priority: project > category > keyword
|
|
1629
|
+
var priority = { project: 0, category: 1, keyword: 2 };
|
|
1630
|
+
data.nodes.sort(function(a, b) { return (priority[a.group] || 9) - (priority[b.group] || 9); });
|
|
1631
|
+
var kept = new Set(data.nodes.slice(0, MAX_NODES).map(function(n) { return n.id; }));
|
|
1632
|
+
data.nodes = data.nodes.slice(0, MAX_NODES);
|
|
1633
|
+
data.edges = data.edges.filter(function(e) { return kept.has(e.from) && kept.has(e.to); });
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1402
1636
|
// Vis.js dark-theme config matching the glassmorphism palette
|
|
1403
1637
|
var options = {
|
|
1404
1638
|
nodes: {
|
|
1405
|
-
shape: 'dot',
|
|
1406
|
-
borderWidth: 0,
|
|
1639
|
+
shape: 'dot',
|
|
1640
|
+
borderWidth: 0,
|
|
1407
1641
|
font: { color: '#94a3b8', face: 'Inter', size: 12 }
|
|
1408
1642
|
},
|
|
1409
1643
|
edges: {
|
|
1410
|
-
width: 1,
|
|
1644
|
+
width: 1,
|
|
1411
1645
|
color: { color: 'rgba(139,92,246,0.15)', highlight: '#8b5cf6' },
|
|
1412
|
-
smooth: { type: 'continuous' }
|
|
1646
|
+
smooth: { type: 'continuous' }
|
|
1413
1647
|
},
|
|
1414
1648
|
groups: {
|
|
1415
|
-
project: {
|
|
1649
|
+
project: {
|
|
1416
1650
|
color: { background: '#8b5cf6', border: '#7c3aed' },
|
|
1417
1651
|
size: 20,
|
|
1418
1652
|
font: { size: 14, color: '#f1f5f9', face: 'Inter' }
|
|
1419
1653
|
},
|
|
1420
|
-
category: {
|
|
1654
|
+
category: {
|
|
1421
1655
|
color: { background: '#06b6d4', border: '#0891b2' },
|
|
1422
1656
|
size: 10,
|
|
1423
1657
|
shape: 'diamond'
|
|
1424
1658
|
},
|
|
1425
|
-
keyword: {
|
|
1659
|
+
keyword: {
|
|
1426
1660
|
color: { background: '#1e293b', border: '#334155' },
|
|
1427
1661
|
size: 6,
|
|
1428
1662
|
font: { size: 10, color: '#64748b' }
|
|
1429
1663
|
}
|
|
1430
1664
|
},
|
|
1431
1665
|
physics: {
|
|
1432
|
-
stabilization:
|
|
1666
|
+
stabilization: { iterations: 50 },
|
|
1433
1667
|
barnesHut: {
|
|
1434
|
-
gravitationalConstant: -3000,
|
|
1435
|
-
springConstant: 0.04,
|
|
1436
|
-
springLength: 80
|
|
1668
|
+
gravitationalConstant: -3000,
|
|
1669
|
+
springConstant: 0.04,
|
|
1670
|
+
springLength: 80
|
|
1437
1671
|
}
|
|
1438
1672
|
},
|
|
1439
|
-
interaction: { hover: true
|
|
1673
|
+
interaction: { hover: true, tooltipDelay: 200 }
|
|
1440
1674
|
};
|
|
1441
1675
|
|
|
1442
1676
|
// Create the network visualization
|
|
1443
|
-
new vis.Network(container, data, options);
|
|
1677
|
+
var network = new vis.Network(container, data, options);
|
|
1678
|
+
|
|
1679
|
+
// v5.1: Click-to-filter β click a node to isolate its connections
|
|
1680
|
+
var allNodes = data.nodes;
|
|
1681
|
+
var allEdges = data.edges;
|
|
1682
|
+
var isFiltered = false;
|
|
1683
|
+
|
|
1684
|
+
network.on('click', function(params) {
|
|
1685
|
+
if (params.nodes.length === 0) {
|
|
1686
|
+
// Click on empty space β reset the graph if filtered
|
|
1687
|
+
if (isFiltered) {
|
|
1688
|
+
network.setData({ nodes: allNodes, edges: allEdges });
|
|
1689
|
+
isFiltered = false;
|
|
1690
|
+
}
|
|
1691
|
+
var panel = document.getElementById('nodeEditorPanel');
|
|
1692
|
+
if (panel) panel.style.display = 'none';
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
var clickedId = params.nodes[0];
|
|
1697
|
+
|
|
1698
|
+
// Display Node Editor Panel for keywords and categories
|
|
1699
|
+
var nodeData = allNodes.find(function(n) { return n.id === clickedId; });
|
|
1700
|
+
if (nodeData && (nodeData.group === 'keyword' || nodeData.group === 'category')) {
|
|
1701
|
+
document.getElementById('nodeEditorTitle').textContent = nodeData.label;
|
|
1702
|
+
document.getElementById('nodeEditorGroup').textContent = nodeData.group;
|
|
1703
|
+
|
|
1704
|
+
var input = document.getElementById('nodeEditorInput');
|
|
1705
|
+
input.value = nodeData.label;
|
|
1706
|
+
input.dataset.oldId = clickedId;
|
|
1707
|
+
input.dataset.group = nodeData.group;
|
|
1708
|
+
|
|
1709
|
+
document.getElementById('nodeEditorPanel').style.display = 'block';
|
|
1710
|
+
} else {
|
|
1711
|
+
var panel = document.getElementById('nodeEditorPanel');
|
|
1712
|
+
if (panel) panel.style.display = 'none';
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Find all connected edges and nodes
|
|
1716
|
+
var connectedEdges = allEdges.filter(function(e) {
|
|
1717
|
+
return e.from === clickedId || e.to === clickedId;
|
|
1718
|
+
});
|
|
1719
|
+
var connectedNodeIds = new Set([clickedId]);
|
|
1720
|
+
connectedEdges.forEach(function(e) {
|
|
1721
|
+
connectedNodeIds.add(e.from);
|
|
1722
|
+
connectedNodeIds.add(e.to);
|
|
1723
|
+
});
|
|
1724
|
+
var connectedNodes = allNodes.filter(function(n) {
|
|
1725
|
+
return connectedNodeIds.has(n.id);
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
// Show only the clicked node and its neighbors
|
|
1729
|
+
network.setData({ nodes: connectedNodes, edges: connectedEdges });
|
|
1730
|
+
isFiltered = true;
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
// Double-click to reset
|
|
1734
|
+
network.on('doubleClick', function() {
|
|
1735
|
+
network.setData({ nodes: allNodes, edges: allEdges });
|
|
1736
|
+
isFiltered = false;
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
// Show node count in the card title area
|
|
1740
|
+
var graphTitle = container.parentElement.querySelector('.card-title');
|
|
1741
|
+
if (graphTitle) {
|
|
1742
|
+
var statsSpan = graphTitle.querySelector('.graph-stats');
|
|
1743
|
+
if (!statsSpan) {
|
|
1744
|
+
statsSpan = document.createElement('span');
|
|
1745
|
+
statsSpan.className = 'graph-stats';
|
|
1746
|
+
statsSpan.style.cssText = 'margin-left:auto;font-size:0.7rem;color:var(--text-muted);font-family:var(--font-mono);font-weight:400;text-transform:none;letter-spacing:0';
|
|
1747
|
+
graphTitle.appendChild(statsSpan);
|
|
1748
|
+
}
|
|
1749
|
+
var projectCount = allNodes.filter(function(n) { return n.group === 'project'; }).length;
|
|
1750
|
+
var kwCount = allNodes.filter(function(n) { return n.group === 'keyword'; }).length;
|
|
1751
|
+
statsSpan.textContent = projectCount + ' projects Β· ' + kwCount + ' keywords Β· ' + allEdges.length + ' edges';
|
|
1752
|
+
}
|
|
1444
1753
|
} catch (e) {
|
|
1445
1754
|
console.error('Graph error', e);
|
|
1446
1755
|
container.innerHTML = '<div style="padding:1rem;color:var(--accent-rose)">Graph failed to load</div>';
|
|
1447
1756
|
}
|
|
1448
1757
|
}
|
|
1449
1758
|
|
|
1759
|
+
async function submitNodeEdit() {
|
|
1760
|
+
var input = document.getElementById('nodeEditorInput');
|
|
1761
|
+
var btn = input.nextElementSibling;
|
|
1762
|
+
var newId = input.value.trim();
|
|
1763
|
+
var oldId = input.dataset.oldId;
|
|
1764
|
+
var group = input.dataset.group;
|
|
1765
|
+
|
|
1766
|
+
if (!oldId || !group) return;
|
|
1767
|
+
|
|
1768
|
+
btn.disabled = true;
|
|
1769
|
+
btn.textContent = '...';
|
|
1770
|
+
|
|
1771
|
+
try {
|
|
1772
|
+
var res = await fetch('/api/graph/node', {
|
|
1773
|
+
method: 'POST',
|
|
1774
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1775
|
+
body: JSON.stringify({ oldId: oldId, newId: newId, group: group })
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
if (!res.ok) throw new Error('Failed to update node');
|
|
1779
|
+
|
|
1780
|
+
showToast(newId ? 'Node renamed successfully' : 'Node deleted successfully');
|
|
1781
|
+
document.getElementById('nodeEditorPanel').style.display = 'none';
|
|
1782
|
+
|
|
1783
|
+
// Refresh graph and lists
|
|
1784
|
+
loadGraph();
|
|
1785
|
+
if (document.getElementById('projectSelect').value) {
|
|
1786
|
+
loadSessionList(); // refresh active project view too if one is loaded
|
|
1787
|
+
}
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
showToast(err.message || 'Error updating node', true);
|
|
1790
|
+
} finally {
|
|
1791
|
+
btn.disabled = false;
|
|
1792
|
+
btn.textContent = 'Apply';
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1450
1796
|
// Initialize the graph on page load
|
|
1451
1797
|
loadGraph();
|
|
1452
1798
|
|
package/dist/server.js
CHANGED
|
@@ -110,6 +110,8 @@ knowledgeSetRetentionHandler,
|
|
|
110
110
|
sessionSaveExperienceHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler,
|
|
111
111
|
// v4.2: Knowledge Sync Rules
|
|
112
112
|
KNOWLEDGE_SYNC_RULES_TOOL, knowledgeSyncRulesHandler,
|
|
113
|
+
// v5.1: Deep Storage Mode
|
|
114
|
+
DEEP_STORAGE_PURGE_TOOL, deepStoragePurgeHandler,
|
|
113
115
|
// βββ v3.0: Agent Hivemind tools βββ
|
|
114
116
|
AGENT_REGISTRY_TOOLS, agentRegisterHandler, agentHeartbeatHandler, agentListTeamHandler, } from "./tools/index.js";
|
|
115
117
|
// βββ Dynamic Tool Registration βββββββββββββββββββββββββββββββββββ
|
|
@@ -141,10 +143,13 @@ function buildSessionMemoryTools(autoloadList) {
|
|
|
141
143
|
let loadContextTool = SESSION_LOAD_CONTEXT_TOOL;
|
|
142
144
|
if (autoloadList.length > 0) {
|
|
143
145
|
const projectList = autoloadList.join(", ");
|
|
146
|
+
// Read the preferred context depth from dashboard config (defaults to "standard")
|
|
147
|
+
const defaultLevel = getSettingSync("default_context_depth", "standard");
|
|
144
148
|
const autoLoadPrefix = `IMPORTANT: At the start of every conversation, you MUST call this tool ` +
|
|
145
149
|
`for each of these projects: ${projectList}. Do this before any other work. ` +
|
|
146
|
-
`Load level should be "
|
|
147
|
-
`Do NOT skip this. Do NOT claim the tool is unavailable. The tool IS available β just call it.\n
|
|
150
|
+
`Load level should be "${defaultLevel}" unless the user specifies otherwise. ` +
|
|
151
|
+
`Do NOT skip this. Do NOT claim the tool is unavailable. The tool IS available β just call it.\n` +
|
|
152
|
+
`Then display the agent identity from the response: \`π€ Agent: <role> β <name>\`\n\n`;
|
|
148
153
|
loadContextTool = {
|
|
149
154
|
...SESSION_LOAD_CONTEXT_TOOL,
|
|
150
155
|
description: autoLoadPrefix + SESSION_LOAD_CONTEXT_TOOL.description,
|
|
@@ -175,6 +180,8 @@ function buildSessionMemoryTools(autoloadList) {
|
|
|
175
180
|
KNOWLEDGE_DOWNVOTE_TOOL, // knowledge_downvote β decrease entry importance
|
|
176
181
|
// βββ v4.2: Knowledge Sync Rules tool βββ
|
|
177
182
|
KNOWLEDGE_SYNC_RULES_TOOL, // knowledge_sync_rules β sync graduated insights to IDE rules files
|
|
183
|
+
// βββ v5.1: Deep Storage Mode tool βββ
|
|
184
|
+
DEEP_STORAGE_PURGE_TOOL, // deep_storage_purge β purge float32 embeddings for compressed entries
|
|
178
185
|
// βββ Phase 2: GDPR Export tool βββ
|
|
179
186
|
SESSION_EXPORT_MEMORY_TOOL, // session_export_memory β full portability export (Article 20)
|
|
180
187
|
];
|
|
@@ -713,6 +720,12 @@ export function createServer() {
|
|
|
713
720
|
throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
|
|
714
721
|
result = await knowledgeSyncRulesHandler(args);
|
|
715
722
|
break;
|
|
723
|
+
// βββ v5.1: Deep Storage Mode (The Purge) βββ
|
|
724
|
+
case "deep_storage_purge":
|
|
725
|
+
if (!SESSION_MEMORY_ENABLED)
|
|
726
|
+
throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
|
|
727
|
+
result = await deepStoragePurgeHandler(args);
|
|
728
|
+
break;
|
|
716
729
|
// βββ v3.0: Agent Hivemind Tools βββ
|
|
717
730
|
case "agent_register":
|
|
718
731
|
if (!SESSION_MEMORY_ENABLED)
|