@kage-core/kage-graph-mcp 1.1.36 → 1.1.37
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 +101 -2
- package/dist/cli.js +429 -3
- package/dist/daemon.js +314 -7
- package/dist/index.js +329 -3
- package/dist/kernel.js +3255 -64
- package/package.json +1 -1
- package/viewer/app.js +1250 -41
- package/viewer/data.html +2 -9
- package/viewer/graph.html +2 -9
- package/viewer/index.html +2 -9
- package/viewer/intel.html +2 -9
- package/viewer/memory.html +73 -9
- package/viewer/owners.html +2 -9
- package/viewer/review.html +13 -9
- package/viewer/styles.css +582 -103
package/viewer/app.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
visibleEntityIds: new Set(),
|
|
14
14
|
visibleEdgeIds: new Set(),
|
|
15
15
|
selected: null,
|
|
16
|
+
revealSelection: false,
|
|
16
17
|
viewerPage: "overview",
|
|
17
18
|
viewerSection: "overview",
|
|
18
19
|
viewerAction: null,
|
|
@@ -29,11 +30,23 @@
|
|
|
29
30
|
quality: null,
|
|
30
31
|
benchmark: null,
|
|
31
32
|
contributors: null,
|
|
33
|
+
profile: null,
|
|
34
|
+
capabilities: null,
|
|
35
|
+
slots: null,
|
|
32
36
|
decisions: null,
|
|
33
37
|
risk: null,
|
|
34
38
|
moduleHealth: null,
|
|
35
39
|
graphInsights: null,
|
|
36
|
-
workspace: null
|
|
40
|
+
workspace: null,
|
|
41
|
+
sessions: null,
|
|
42
|
+
replay: null,
|
|
43
|
+
memoryAccess: null,
|
|
44
|
+
memoryAudit: null,
|
|
45
|
+
handoff: null,
|
|
46
|
+
lifecycle: null,
|
|
47
|
+
timeline: null,
|
|
48
|
+
lineage: null,
|
|
49
|
+
setup: null
|
|
37
50
|
},
|
|
38
51
|
pendingPackets: [],
|
|
39
52
|
reviewText: "",
|
|
@@ -173,6 +186,23 @@
|
|
|
173
186
|
memoryStatus: document.getElementById("memoryStatus"),
|
|
174
187
|
memoryStats: document.getElementById("memoryStats"),
|
|
175
188
|
memoryOverview: document.getElementById("memoryOverview"),
|
|
189
|
+
lifecycleStatus: document.getElementById("lifecycleStatus"),
|
|
190
|
+
lifecycleSummary: document.getElementById("lifecycleSummary"),
|
|
191
|
+
lifecycleList: document.getElementById("lifecycleList"),
|
|
192
|
+
memoryReviewStatus: document.getElementById("memoryReviewStatus"),
|
|
193
|
+
memoryReviewActions: document.getElementById("memoryReviewActions"),
|
|
194
|
+
memoryTimelineStatus: document.getElementById("memoryTimelineStatus"),
|
|
195
|
+
memoryTimelineSummary: document.getElementById("memoryTimelineSummary"),
|
|
196
|
+
memoryTimelineList: document.getElementById("memoryTimelineList"),
|
|
197
|
+
memoryAuditStatus: document.getElementById("memoryAuditStatus"),
|
|
198
|
+
memoryAuditSummary: document.getElementById("memoryAuditSummary"),
|
|
199
|
+
memoryAuditList: document.getElementById("memoryAuditList"),
|
|
200
|
+
memoryLineageStatus: document.getElementById("memoryLineageStatus"),
|
|
201
|
+
memoryLineageSummary: document.getElementById("memoryLineageSummary"),
|
|
202
|
+
memoryLineageList: document.getElementById("memoryLineageList"),
|
|
203
|
+
sessionCaptureStatus: document.getElementById("sessionCaptureStatus"),
|
|
204
|
+
sessionCaptureSummary: document.getElementById("sessionCaptureSummary"),
|
|
205
|
+
sessionCaptureList: document.getElementById("sessionCaptureList"),
|
|
176
206
|
memorySearch: document.getElementById("memorySearch"),
|
|
177
207
|
memoryFilter: document.getElementById("memoryFilter"),
|
|
178
208
|
memoryList: document.getElementById("memoryList"),
|
|
@@ -180,6 +210,9 @@
|
|
|
180
210
|
ownersSummary: document.getElementById("ownersSummary"),
|
|
181
211
|
ownersList: document.getElementById("ownersList"),
|
|
182
212
|
reviewOverview: document.getElementById("reviewOverview"),
|
|
213
|
+
handoffStatus: document.getElementById("handoffStatus"),
|
|
214
|
+
handoffSummary: document.getElementById("handoffSummary"),
|
|
215
|
+
handoffList: document.getElementById("handoffList"),
|
|
183
216
|
reviewList: document.getElementById("reviewList"),
|
|
184
217
|
proofOverview: document.getElementById("proofOverview"),
|
|
185
218
|
proofStatus: document.getElementById("proofStatus"),
|
|
@@ -256,10 +289,12 @@
|
|
|
256
289
|
els.pathFromInput.addEventListener("keydown", function (event) { if (event.key === "Enter") findDependencyPath(); });
|
|
257
290
|
els.pathToInput.addEventListener("keydown", function (event) { if (event.key === "Enter") findDependencyPath(); });
|
|
258
291
|
els.viewMode.addEventListener("change", function () { clearGraphActionFilter(); render(); });
|
|
259
|
-
els.renderMode
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
292
|
+
if (els.renderMode) {
|
|
293
|
+
els.renderMode.addEventListener("change", function () {
|
|
294
|
+
state.lastVisibleSignature = "";
|
|
295
|
+
render();
|
|
296
|
+
});
|
|
297
|
+
}
|
|
263
298
|
els.typeFilter.addEventListener("change", function () { clearGraphActionFilter(); render(); });
|
|
264
299
|
els.relationFilter.addEventListener("change", function () { clearGraphActionFilter(); render(); });
|
|
265
300
|
els.scopeFilter.addEventListener("change", render);
|
|
@@ -298,7 +333,7 @@
|
|
|
298
333
|
function resetGraphView() {
|
|
299
334
|
els.searchInput.value = "";
|
|
300
335
|
els.viewMode.value = "combined";
|
|
301
|
-
els.renderMode.value = "2d";
|
|
336
|
+
if (els.renderMode) els.renderMode.value = "2d";
|
|
302
337
|
els.typeFilter.value = "";
|
|
303
338
|
els.relationFilter.value = "";
|
|
304
339
|
els.scopeFilter.value = "signal";
|
|
@@ -466,10 +501,12 @@
|
|
|
466
501
|
|
|
467
502
|
function selectEntity(id, openInspector) {
|
|
468
503
|
state.selected = { kind: "entity", id: id };
|
|
504
|
+
state.revealSelection = Boolean(openInspector);
|
|
469
505
|
}
|
|
470
506
|
|
|
471
507
|
function selectEdge(id, openInspector) {
|
|
472
508
|
state.selected = { kind: "edge", id: id };
|
|
509
|
+
state.revealSelection = Boolean(openInspector);
|
|
473
510
|
}
|
|
474
511
|
|
|
475
512
|
function handleFile(event) {
|
|
@@ -562,11 +599,23 @@
|
|
|
562
599
|
var qualityPath = params.get("quality");
|
|
563
600
|
var benchmarkPath = params.get("benchmark");
|
|
564
601
|
var contributorsPath = params.get("contributors");
|
|
602
|
+
var profilePath = params.get("profile") || params.get("projectProfile") || params.get("project-profile");
|
|
603
|
+
var capabilitiesPath = params.get("capabilities") || params.get("capabilityAudit") || params.get("capability-audit");
|
|
604
|
+
var slotsPath = params.get("slots") || params.get("contextSlots") || params.get("context-slots");
|
|
565
605
|
var decisionsPath = params.get("decisions");
|
|
566
606
|
var riskPath = params.get("risk");
|
|
567
607
|
var moduleHealthPath = params.get("moduleHealth") || params.get("module-health");
|
|
568
608
|
var graphInsightsPath = params.get("graphInsights") || params.get("graph-insights");
|
|
569
609
|
var workspacePath = params.get("workspace");
|
|
610
|
+
var sessionsPath = params.get("sessions");
|
|
611
|
+
var replayPath = params.get("replay") || params.get("sessionReplay") || params.get("session-replay");
|
|
612
|
+
var memoryAccessPath = params.get("memoryAccess") || params.get("memory-access");
|
|
613
|
+
var memoryAuditPath = params.get("memoryAudit") || params.get("memory-audit") || params.get("auditLog") || params.get("audit-log");
|
|
614
|
+
var handoffPath = params.get("handoff") || params.get("memoryHandoff") || params.get("memory-handoff");
|
|
615
|
+
var lifecyclePath = params.get("lifecycle") || params.get("memoryLifecycle") || params.get("memory-lifecycle");
|
|
616
|
+
var timelinePath = params.get("timeline") || params.get("memoryTimeline") || params.get("memory-timeline");
|
|
617
|
+
var lineagePath = params.get("lineage") || params.get("memoryLineage") || params.get("memory-lineage");
|
|
618
|
+
var setupPath = params.get("setup") || params.get("setupDoctor") || params.get("setup-doctor");
|
|
570
619
|
var inferredRoot = inferMemoryRoot(graphPaths[0] || "");
|
|
571
620
|
if (!inboxPath && inferredRoot) inboxPath = inferredRoot + "/inbox.json";
|
|
572
621
|
if (!reviewPath && inferredRoot) reviewPath = inferredRoot + "/review/memory-review.md";
|
|
@@ -575,11 +624,23 @@
|
|
|
575
624
|
if (!qualityPath) qualityPath = inferredRoot + "/reports/quality.json";
|
|
576
625
|
if (!benchmarkPath) benchmarkPath = inferredRoot + "/reports/benchmark.json";
|
|
577
626
|
if (!contributorsPath) contributorsPath = inferredRoot + "/reports/contributors.json";
|
|
627
|
+
if (!profilePath) profilePath = inferredRoot + "/reports/profile.json";
|
|
628
|
+
if (!capabilitiesPath) capabilitiesPath = inferredRoot + "/reports/capabilities.json";
|
|
629
|
+
if (!slotsPath) slotsPath = inferredRoot + "/reports/context-slots.json";
|
|
578
630
|
if (!decisionsPath) decisionsPath = inferredRoot + "/reports/decisions.json";
|
|
579
631
|
if (!riskPath) riskPath = inferredRoot + "/reports/risk.json";
|
|
580
632
|
if (!moduleHealthPath) moduleHealthPath = inferredRoot + "/reports/module-health.json";
|
|
581
633
|
if (!graphInsightsPath) graphInsightsPath = inferredRoot + "/reports/graph-insights.json";
|
|
582
634
|
if (!workspacePath) workspacePath = inferredRoot + "/reports/workspace.json";
|
|
635
|
+
if (!sessionsPath) sessionsPath = inferredRoot + "/reports/sessions.json";
|
|
636
|
+
if (!replayPath) replayPath = inferredRoot + "/reports/replay.json";
|
|
637
|
+
if (!memoryAccessPath) memoryAccessPath = inferredRoot + "/reports/memory-access.json";
|
|
638
|
+
if (!memoryAuditPath) memoryAuditPath = inferredRoot + "/reports/memory-audit.json";
|
|
639
|
+
if (!handoffPath) handoffPath = inferredRoot + "/reports/handoff.json";
|
|
640
|
+
if (!lifecyclePath) lifecyclePath = inferredRoot + "/reports/lifecycle.json";
|
|
641
|
+
if (!timelinePath) timelinePath = inferredRoot + "/reports/timeline.json";
|
|
642
|
+
if (!lineagePath) lineagePath = inferredRoot + "/reports/lineage.json";
|
|
643
|
+
if (!setupPath) setupPath = inferredRoot + "/reports/setup.json";
|
|
583
644
|
}
|
|
584
645
|
var jobs = [];
|
|
585
646
|
if (metricsPath) jobs.push(fetchJson(metricsPath).then(function (metrics) { state.metrics = metrics; }));
|
|
@@ -589,11 +650,23 @@
|
|
|
589
650
|
if (qualityPath) jobs.push(fetchJson(qualityPath).then(function (report) { state.reports.quality = report; }).catch(function () { state.reports.quality = null; }));
|
|
590
651
|
if (benchmarkPath) jobs.push(fetchJson(benchmarkPath).then(function (report) { state.reports.benchmark = report; }).catch(function () { state.reports.benchmark = null; }));
|
|
591
652
|
if (contributorsPath) jobs.push(fetchJson(contributorsPath).then(function (report) { state.reports.contributors = report; }).catch(function () { state.reports.contributors = null; }));
|
|
653
|
+
if (profilePath) jobs.push(fetchJson(profilePath).then(function (report) { state.reports.profile = report; }).catch(function () { state.reports.profile = null; }));
|
|
654
|
+
if (capabilitiesPath) jobs.push(fetchJson(capabilitiesPath).then(function (report) { state.reports.capabilities = report; }).catch(function () { state.reports.capabilities = null; }));
|
|
655
|
+
if (slotsPath) jobs.push(fetchJson(slotsPath).then(function (report) { state.reports.slots = report; }).catch(function () { state.reports.slots = null; }));
|
|
592
656
|
if (decisionsPath) jobs.push(fetchJson(decisionsPath).then(function (report) { state.reports.decisions = report; }).catch(function () { state.reports.decisions = null; }));
|
|
593
657
|
if (riskPath) jobs.push(fetchJson(riskPath).then(function (report) { state.reports.risk = report; }).catch(function () { state.reports.risk = null; }));
|
|
594
658
|
if (moduleHealthPath) jobs.push(fetchJson(moduleHealthPath).then(function (report) { state.reports.moduleHealth = report; }).catch(function () { state.reports.moduleHealth = null; }));
|
|
595
659
|
if (graphInsightsPath) jobs.push(fetchJson(graphInsightsPath).then(function (report) { state.reports.graphInsights = report; }).catch(function () { state.reports.graphInsights = null; }));
|
|
596
660
|
if (workspacePath) jobs.push(fetchJson(workspacePath).then(function (report) { state.reports.workspace = report; }).catch(function () { state.reports.workspace = null; }));
|
|
661
|
+
if (sessionsPath) jobs.push(fetchJson(sessionsPath).then(function (report) { state.reports.sessions = report; }).catch(function () { state.reports.sessions = null; }));
|
|
662
|
+
if (replayPath) jobs.push(fetchJson(replayPath).then(function (report) { state.reports.replay = report; }).catch(function () { state.reports.replay = null; }));
|
|
663
|
+
if (memoryAccessPath) jobs.push(fetchJson(memoryAccessPath).then(function (report) { state.reports.memoryAccess = report; }).catch(function () { state.reports.memoryAccess = null; }));
|
|
664
|
+
if (memoryAuditPath) jobs.push(fetchJson(memoryAuditPath).then(function (report) { state.reports.memoryAudit = report; }).catch(function () { state.reports.memoryAudit = null; }));
|
|
665
|
+
if (handoffPath) jobs.push(fetchJson(handoffPath).then(function (report) { state.reports.handoff = report; }).catch(function () { state.reports.handoff = null; }));
|
|
666
|
+
if (lifecyclePath) jobs.push(fetchJson(lifecyclePath).then(function (report) { state.reports.lifecycle = report; }).catch(function () { state.reports.lifecycle = null; }));
|
|
667
|
+
if (timelinePath) jobs.push(fetchJson(timelinePath).then(function (report) { state.reports.timeline = report; }).catch(function () { state.reports.timeline = null; }));
|
|
668
|
+
if (lineagePath) jobs.push(fetchJson(lineagePath).then(function (report) { state.reports.lineage = report; }).catch(function () { state.reports.lineage = null; }));
|
|
669
|
+
if (setupPath) jobs.push(fetchJson(setupPath).then(function (report) { state.reports.setup = report; }).catch(function () { state.reports.setup = null; }));
|
|
597
670
|
if (!graphPaths.length && !jobs.length) {
|
|
598
671
|
loadHostedDefault();
|
|
599
672
|
return;
|
|
@@ -626,20 +699,44 @@
|
|
|
626
699
|
fetchJson("./data/kage/inbox.json").catch(function () { return null; }),
|
|
627
700
|
fetchJson("./data/kage/reports/risk.json").catch(function () { return null; }),
|
|
628
701
|
fetchJson("./data/kage/reports/contributors.json").catch(function () { return null; }),
|
|
702
|
+
fetchJson("./data/kage/reports/profile.json").catch(function () { return null; }),
|
|
703
|
+
fetchJson("./data/kage/reports/capabilities.json").catch(function () { return null; }),
|
|
704
|
+
fetchJson("./data/kage/reports/context-slots.json").catch(function () { return null; }),
|
|
629
705
|
fetchJson("./data/kage/reports/decisions.json").catch(function () { return null; }),
|
|
630
706
|
fetchJson("./data/kage/reports/module-health.json").catch(function () { return null; }),
|
|
631
707
|
fetchJson("./data/kage/reports/graph-insights.json").catch(function () { return null; }),
|
|
632
|
-
fetchJson("./data/kage/reports/workspace.json").catch(function () { return null; })
|
|
708
|
+
fetchJson("./data/kage/reports/workspace.json").catch(function () { return null; }),
|
|
709
|
+
fetchJson("./data/kage/reports/sessions.json").catch(function () { return null; }),
|
|
710
|
+
fetchJson("./data/kage/reports/replay.json").catch(function () { return null; }),
|
|
711
|
+
fetchJson("./data/kage/reports/memory-access.json").catch(function () { return null; }),
|
|
712
|
+
fetchJson("./data/kage/reports/memory-audit.json").catch(function () { return null; }),
|
|
713
|
+
fetchJson("./data/kage/reports/handoff.json").catch(function () { return null; }),
|
|
714
|
+
fetchJson("./data/kage/reports/lifecycle.json").catch(function () { return null; }),
|
|
715
|
+
fetchJson("./data/kage/reports/timeline.json").catch(function () { return null; }),
|
|
716
|
+
fetchJson("./data/kage/reports/lineage.json").catch(function () { return null; }),
|
|
717
|
+
fetchJson("./data/kage/reports/setup.json").catch(function () { return null; })
|
|
633
718
|
]).then(function (items) {
|
|
634
719
|
var merged = mergeNormalizedGraphs([normalizeGraph(items[0]), normalizeGraph(items[1])]);
|
|
635
720
|
state.metrics = items[2];
|
|
636
721
|
state.inbox = items[3];
|
|
637
722
|
state.reports.risk = items[4];
|
|
638
723
|
state.reports.contributors = items[5];
|
|
639
|
-
state.reports.
|
|
640
|
-
state.reports.
|
|
641
|
-
state.reports.
|
|
642
|
-
state.reports.
|
|
724
|
+
state.reports.profile = items[6];
|
|
725
|
+
state.reports.capabilities = items[7];
|
|
726
|
+
state.reports.slots = items[8];
|
|
727
|
+
state.reports.decisions = items[9];
|
|
728
|
+
state.reports.moduleHealth = items[10];
|
|
729
|
+
state.reports.graphInsights = items[11];
|
|
730
|
+
state.reports.workspace = items[12];
|
|
731
|
+
state.reports.sessions = items[13];
|
|
732
|
+
state.reports.replay = items[14];
|
|
733
|
+
state.reports.memoryAccess = items[15];
|
|
734
|
+
state.reports.memoryAudit = items[16];
|
|
735
|
+
state.reports.handoff = items[17];
|
|
736
|
+
state.reports.lifecycle = items[18];
|
|
737
|
+
state.reports.timeline = items[19];
|
|
738
|
+
state.reports.lineage = items[20];
|
|
739
|
+
state.reports.setup = items[21];
|
|
643
740
|
loadNormalizedGraph(merged, "Kage repo graph");
|
|
644
741
|
setAutoLoad("Kage repo graph loaded", true);
|
|
645
742
|
}).catch(function () {
|
|
@@ -1383,6 +1480,7 @@
|
|
|
1383
1480
|
|
|
1384
1481
|
renderActiveGraph(graphChanged);
|
|
1385
1482
|
renderPagePanels();
|
|
1483
|
+
revealSelectionIfRequested();
|
|
1386
1484
|
}
|
|
1387
1485
|
|
|
1388
1486
|
function scheduleRender() {
|
|
@@ -2438,6 +2536,18 @@
|
|
|
2438
2536
|
document.body.classList.toggle("has-code-selection", Boolean(entity && entity.graph_kind === "code"));
|
|
2439
2537
|
}
|
|
2440
2538
|
|
|
2539
|
+
function revealSelectionIfRequested() {
|
|
2540
|
+
if (!state.revealSelection) return;
|
|
2541
|
+
state.revealSelection = false;
|
|
2542
|
+
window.requestAnimationFrame(function () {
|
|
2543
|
+
var panel = document.querySelector(".details-panel");
|
|
2544
|
+
if (!panel || window.getComputedStyle(panel).display === "none") return;
|
|
2545
|
+
panel.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
2546
|
+
var selectedRow = document.querySelector("[aria-selected=\"true\"], .node.selected");
|
|
2547
|
+
if (selectedRow && selectedRow.scrollIntoView) selectedRow.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2441
2551
|
function prefillPathFromSelection(silent) {
|
|
2442
2552
|
if (!state.selected || state.selected.kind !== "entity") {
|
|
2443
2553
|
if (!silent) setPathStatus("Select a code node first. Path tracing is for files, symbols, routes, tests, and scripts.", "warn");
|
|
@@ -2879,20 +2989,20 @@
|
|
|
2879
2989
|
var risk = reports.risk || {};
|
|
2880
2990
|
var riskTargets = Array.isArray(risk.targets) ? risk.targets : Object.keys(risk.targets || {});
|
|
2881
2991
|
var inboxCounts = state.inbox && state.inbox.counts ? state.inbox.counts : {};
|
|
2992
|
+
var handoff = memoryHandoffSummary(reports.handoff);
|
|
2882
2993
|
var pendingReview = Number(firstNumber(inboxCounts.pending, memoryGraph.pending_packets, (state.pendingPackets || []).length, 0));
|
|
2883
2994
|
var staleFlags = Number(firstNumber(inboxCounts.stale, 0));
|
|
2884
2995
|
var duplicateFlags = Number(firstNumber(inboxCounts.duplicates, memoryGraph.duplicate_candidate_pairs, 0));
|
|
2885
2996
|
var missingContext = Number(firstNumber(inboxCounts.missing_context, 0));
|
|
2886
2997
|
var ownerSilos = Array.isArray(risk.ownership_silos) ? risk.ownership_silos.length : 0;
|
|
2887
2998
|
var hotspots = Array.isArray(risk.global_hotspots) ? risk.global_hotspots.length : 0;
|
|
2888
|
-
var readiness = dashboardReadiness(metrics, pendingReview, staleFlags, duplicateFlags, missingContext);
|
|
2999
|
+
var readiness = handoff || dashboardReadiness(metrics, pendingReview, staleFlags, duplicateFlags, missingContext);
|
|
2889
3000
|
var memoryCoverage = dashboardMemoryCoverage(reports, memoryCodeEdges, memoryGraph, memoryNodes);
|
|
2890
3001
|
var riskHealth = riskTargets.length || hotspots ? (riskTargets.length + hotspots) + " signals" : "No flags";
|
|
2891
3002
|
var statRows = [
|
|
2892
3003
|
["Handoff", readiness.label, readiness.detail, readiness.status],
|
|
2893
3004
|
["Memory", memoryCoverage.label, memoryCoverage.detail, memoryCoverage.status],
|
|
2894
|
-
["Risk", riskHealth, riskTargets.length + " targets, " + ownerSilos + " ownership silos", riskTargets.length || ownerSilos || hotspots ? "warn" : "ok"]
|
|
2895
|
-
["Code map", firstNumber(codeGraph.files, structural.files, countEntitiesByType("file")) + " files", firstNumber(codeGraph.symbols, structural.symbols, codeNodes) + " symbols indexed", "code"]
|
|
3005
|
+
["Risk", riskHealth, riskTargets.length + " targets, " + ownerSilos + " ownership silos", riskTargets.length || ownerSilos || hotspots ? "warn" : "ok"]
|
|
2896
3006
|
];
|
|
2897
3007
|
els.dashboardStats.textContent = "";
|
|
2898
3008
|
statRows.forEach(function (row) {
|
|
@@ -2920,7 +3030,13 @@
|
|
|
2920
3030
|
["Ownership silos", ownerSilos || "none"],
|
|
2921
3031
|
["Decision coverage", reports.decisions && reports.decisions.coverage_percent != null ? reports.decisions.coverage_percent + "%" : "not loaded"]
|
|
2922
3032
|
]);
|
|
2923
|
-
setDashboardRows("dashboardReview", [
|
|
3033
|
+
setDashboardRows("dashboardReview", handoff ? [
|
|
3034
|
+
["Next", handoff.actionLabel],
|
|
3035
|
+
["Open", handoff.openItems ? handoff.openItems + " item" + (handoff.openItems === 1 ? "" : "s") : "none"],
|
|
3036
|
+
["Distill", handoff.distillableSessions ? handoff.distillableSessions + " session" + (handoff.distillableSessions === 1 ? "" : "s") : "none"],
|
|
3037
|
+
["Recent", handoff.recentChanges + " changes"],
|
|
3038
|
+
["Mutations", handoff.recentMutations || "none"]
|
|
3039
|
+
] : [
|
|
2924
3040
|
["Handoff", readiness.label],
|
|
2925
3041
|
["Pending", pendingReview || "none"],
|
|
2926
3042
|
["Stale / duplicate", staleFlags + " / " + duplicateFlags],
|
|
@@ -2940,7 +3056,8 @@
|
|
|
2940
3056
|
missingContext: missingContext,
|
|
2941
3057
|
riskTargets: riskTargets,
|
|
2942
3058
|
ownerSilos: ownerSilos,
|
|
2943
|
-
hotspots: hotspots
|
|
3059
|
+
hotspots: hotspots,
|
|
3060
|
+
handoff: handoff
|
|
2944
3061
|
});
|
|
2945
3062
|
}
|
|
2946
3063
|
|
|
@@ -2956,24 +3073,315 @@
|
|
|
2956
3073
|
});
|
|
2957
3074
|
var memoryGrounding = approvedPackets ? Math.round(linkedPacketIds.size / approvedPackets * 100) : 0;
|
|
2958
3075
|
var sourceCoverage = Number(firstNumber(data.codeGraph.indexer_coverage_percent, 0));
|
|
2959
|
-
var blockers = data.pendingReview + data.staleFlags + data.duplicateFlags + data.missingContext;
|
|
3076
|
+
var blockers = data.handoff ? data.handoff.openItems : data.pendingReview + data.staleFlags + data.duplicateFlags + data.missingContext;
|
|
2960
3077
|
var riskSignals = data.riskTargets.length + data.ownerSilos + data.hotspots;
|
|
2961
|
-
|
|
2962
|
-
|
|
3078
|
+
var retrieval = benchmarkRetrievalSummary(data.reports && data.reports.benchmark);
|
|
3079
|
+
var scale = benchmarkScaleSummary(data.reports && data.reports.benchmark);
|
|
3080
|
+
var access = memoryAccessSummary(data.reports && data.reports.memoryAccess, approvedPackets);
|
|
3081
|
+
var audit = memoryAuditSummary(data.reports && data.reports.memoryAudit);
|
|
3082
|
+
var lifecycle = memoryLifecycleSummary(data.reports && data.reports.lifecycle);
|
|
3083
|
+
var timeline = memoryTimelineSummary(data.reports && data.reports.timeline);
|
|
3084
|
+
var lineage = memoryLineageSummary(data.reports && data.reports.lineage);
|
|
3085
|
+
var setup = setupDoctorSummary(data.reports && data.reports.setup);
|
|
3086
|
+
var profile = projectProfileSummary(data.reports && data.reports.profile);
|
|
3087
|
+
var capabilities = capabilityAuditSummary(data.reports && data.reports.capabilities);
|
|
3088
|
+
var slots = contextSlotsSummary(data.reports && data.reports.slots);
|
|
3089
|
+
var replay = sessionReplaySummary(data.reports && data.reports.replay);
|
|
3090
|
+
var cards = [
|
|
2963
3091
|
metricDonut("Memory grounding", memoryGrounding, linkedPacketIds.size + " of " + approvedPackets + " packets linked to code", "Open Memory and fix Needs paths first.", memoryGrounding >= 70 ? "ok" : "warn"),
|
|
2964
|
-
metricDonut("Source map", sourceCoverage, firstNumber(data.codeGraph.files, data.structural.files, 0) + " files indexed for graph recall", "If this drops, refresh indexing before relying on graph answers.", sourceCoverage >= 90 ? "ok" : "warn")
|
|
2965
|
-
|
|
3092
|
+
metricDonut("Source map", sourceCoverage, firstNumber(data.codeGraph.files, data.structural.files, 0) + " files indexed for graph recall", "If this drops, refresh indexing before relying on graph answers.", sourceCoverage >= 90 ? "ok" : "warn")
|
|
3093
|
+
];
|
|
3094
|
+
if (capabilities) {
|
|
3095
|
+
cards.push(metricBars("Capability audit", capabilities.label, capabilities.rows, capabilities.action, capabilities.status));
|
|
3096
|
+
}
|
|
3097
|
+
if (profile) {
|
|
3098
|
+
cards.push(metricBars("Project profile", profile.label, [
|
|
3099
|
+
{ label: "Concepts", value: profile.concepts, score: Math.min(100, profile.concepts * 10), status: profile.concepts ? "ok" : "warn" },
|
|
3100
|
+
{ label: "Key files", value: profile.keyFiles, score: Math.min(100, profile.keyFiles * 8), status: profile.keyFiles ? "ok" : "warn" },
|
|
3101
|
+
{ label: "Commands", value: profile.commands, score: Math.min(100, profile.commands * 16), status: profile.commands ? "ok" : "warn" }
|
|
3102
|
+
], profile.action, profile.status));
|
|
3103
|
+
}
|
|
3104
|
+
if (slots) {
|
|
3105
|
+
cards.push(metricBars("Pinned context", slots.label, [
|
|
3106
|
+
{ label: "Pinned", value: slots.pinned, score: Math.min(100, slots.pinned * 30), status: slots.pinned ? "ok" : "warn" },
|
|
3107
|
+
{ label: "Slots", value: slots.total, score: Math.min(100, slots.total * 20), status: slots.total ? "ok" : "warn" },
|
|
3108
|
+
{ label: "Chars", value: slots.chars, score: slots.chars ? Math.min(100, Math.round(slots.chars / 60)) : 0, status: slots.chars ? "ok" : "warn" }
|
|
3109
|
+
], slots.action, slots.status));
|
|
3110
|
+
}
|
|
3111
|
+
if (replay) {
|
|
3112
|
+
cards.push(metricBars("Session replay", replay.label, [
|
|
3113
|
+
{ label: "Events", value: replay.events, score: Math.min(100, replay.events * 8), status: replay.events ? "ok" : "warn" },
|
|
3114
|
+
{ label: "Candidates", value: replay.candidates, score: Math.min(100, replay.candidates * 24), status: replay.candidates ? "warn" : "ok" },
|
|
3115
|
+
{ label: "Sessions", value: replay.sessions, score: Math.min(100, replay.sessions * 24), status: replay.sessions ? "ok" : "warn" }
|
|
3116
|
+
], replay.action, replay.status));
|
|
3117
|
+
}
|
|
3118
|
+
if (setup) {
|
|
3119
|
+
cards.push(metricBars("Agent setup", setup.label, [
|
|
3120
|
+
{ label: "Configured", value: setup.configured + "/" + setup.total, score: setup.total ? Math.round(setup.configured / setup.total * 100) : 0, status: setup.configured ? "ok" : "warn" },
|
|
3121
|
+
{ label: "Claude hooks", value: setup.claudeHookReady ? "ready" : "missing", score: setup.claudeHookReady ? 100 : 0, status: setup.claudeHookReady ? "ok" : "warn" },
|
|
3122
|
+
{ label: "Missing", value: setup.missingCount, score: setup.missingCount ? Math.min(100, setup.missingCount * 18) : 0, status: setup.missingCount ? "warn" : "ok" }
|
|
3123
|
+
], setup.action, setup.status));
|
|
3124
|
+
}
|
|
3125
|
+
if (access) {
|
|
3126
|
+
cards.push(metricBars("Memory reuse", access.uses30d + " recalls", [
|
|
3127
|
+
{ label: "Hot", value: access.hot, score: Math.min(100, access.hot * 24), status: access.hot ? "ok" : "warn" },
|
|
3128
|
+
{ label: "Cold", value: access.cold, score: approvedPackets ? Math.round(access.cold / approvedPackets * 100) : 0, status: access.cold ? "warn" : "ok" },
|
|
3129
|
+
{ label: "Tracked", value: access.tracked, score: approvedPackets ? Math.round(access.tracked / approvedPackets * 100) : 0, status: access.tracked ? "ok" : "warn" }
|
|
3130
|
+
], access.tracked ? "Cold packets may be stale, too broad, or never needed by agents." : "Recall a task to start measuring which memories actually help.", access.cold ? "warn" : "ok"));
|
|
3131
|
+
}
|
|
3132
|
+
if (audit) {
|
|
3133
|
+
cards.push(metricBars("Memory audit", audit.total + " mutations", [
|
|
3134
|
+
{ label: "Capture", value: audit.capture, score: Math.min(100, audit.capture * 12), status: audit.capture ? "ok" : "warn" },
|
|
3135
|
+
{ label: "Review", value: audit.review, score: Math.min(100, audit.review * 20), status: audit.review ? "ok" : "warn" },
|
|
3136
|
+
{ label: "Supersede", value: audit.supersede, score: Math.min(100, audit.supersede * 24), status: audit.supersede ? "ok" : "warn" }
|
|
3137
|
+
], audit.total ? "Memory changes are auditable for team handoff." : "No explicit memory mutations audited yet.", audit.total ? "ok" : "warn"));
|
|
3138
|
+
}
|
|
3139
|
+
if (data.handoff) {
|
|
3140
|
+
cards.push(metricBars("Memory handoff", data.handoff.label, [
|
|
3141
|
+
{ label: "Open", value: data.handoff.openItems, score: Math.min(100, data.handoff.openItems * 24), status: data.handoff.openItems ? "warn" : "ok" },
|
|
3142
|
+
{ label: "Distill", value: data.handoff.distillableSessions, score: Math.min(100, data.handoff.distillableSessions * 30), status: data.handoff.distillableSessions ? "warn" : "ok" },
|
|
3143
|
+
{ label: "Recent", value: data.handoff.recentChanges, score: Math.min(100, data.handoff.recentChanges * 8), status: data.handoff.recentChanges ? "ok" : "warn" },
|
|
3144
|
+
{ label: "Mutations", value: data.handoff.recentMutations, score: Math.min(100, data.handoff.recentMutations * 18), status: data.handoff.recentMutations ? "ok" : "warn" }
|
|
3145
|
+
], data.handoff.action, data.handoff.status));
|
|
3146
|
+
}
|
|
3147
|
+
if (lifecycle) {
|
|
3148
|
+
cards.push(metricBars("Memory lifecycle", lifecycle.needsReview ? lifecycle.needsReview + " need review" : "healthy", [
|
|
3149
|
+
{ label: "Hot/healthy", value: lifecycle.ready, score: approvedPackets ? Math.round(lifecycle.ready / approvedPackets * 100) : 0, status: lifecycle.ready ? "ok" : "warn" },
|
|
3150
|
+
{ label: "Ungrounded", value: lifecycle.ungrounded, score: approvedPackets ? Math.round(lifecycle.ungrounded / approvedPackets * 100) : 0, status: lifecycle.ungrounded ? "warn" : "ok" },
|
|
3151
|
+
{ label: "Stale/disputed", value: lifecycle.stale, score: approvedPackets ? Math.round(lifecycle.stale / approvedPackets * 100) : 0, status: lifecycle.stale ? "danger" : "ok" }
|
|
3152
|
+
], lifecycle.needsReview ? "Open Memory and resolve lifecycle actions before handoff." : "Repo memory is ready for agent handoff.", lifecycle.needsReview ? "warn" : "ok"));
|
|
3153
|
+
}
|
|
3154
|
+
if (timeline) {
|
|
3155
|
+
cards.push(metricBars("Memory timeline", timeline.total + " recent", [
|
|
3156
|
+
{ label: "Added", value: timeline.added, score: Math.min(100, timeline.added * 16), status: timeline.added ? "ok" : "warn" },
|
|
3157
|
+
{ label: "Updated", value: timeline.updated, score: Math.min(100, timeline.updated * 16), status: timeline.updated ? "ok" : "warn" },
|
|
3158
|
+
{ label: "Pending", value: timeline.pending, score: Math.min(100, timeline.pending * 24), status: timeline.pending ? "warn" : "ok" }
|
|
3159
|
+
], timeline.total ? "Review recent memory changes before teammate handoff." : "No recent memory activity loaded.", timeline.pending ? "warn" : "ok"));
|
|
3160
|
+
}
|
|
3161
|
+
if (lineage) {
|
|
3162
|
+
cards.push(metricBars("Memory lineage", lineage.chains + " chains", [
|
|
3163
|
+
{ label: "Replaced", value: lineage.superseded, score: Math.min(100, lineage.superseded * 18), status: lineage.superseded ? "ok" : "warn" },
|
|
3164
|
+
{ label: "Chains", value: lineage.chains, score: Math.min(100, lineage.chains * 24), status: lineage.chains ? "ok" : "warn" },
|
|
3165
|
+
{ label: "Needs repair", value: lineage.orphans, score: Math.min(100, lineage.orphans * 32), status: lineage.orphans ? "danger" : "ok" }
|
|
3166
|
+
], lineage.orphans ? "Fix superseded memory without replacement links." : "Retired memory points at current replacements.", lineage.orphans ? "danger" : "ok"));
|
|
3167
|
+
}
|
|
3168
|
+
if (retrieval) {
|
|
3169
|
+
cards.push(metricBars("Retrieval proof", retrieval.r10 + "% R@10", [
|
|
3170
|
+
{ label: "R@5", value: retrieval.r5 != null ? retrieval.r5 + "%" : "n/a", score: retrieval.r5 || 0, status: retrieval.r5 >= 95 ? "ok" : "warn" },
|
|
3171
|
+
{ label: "R@10", value: retrieval.r10 + "%", score: retrieval.r10, status: retrieval.r10 >= 95 ? "ok" : "warn" },
|
|
3172
|
+
{ label: "MRR", value: retrieval.mrr != null ? retrieval.mrr : "n/a", score: retrieval.mrr != null ? retrieval.mrr * 100 : 0, status: retrieval.mrr >= 0.85 ? "ok" : "warn" }
|
|
3173
|
+
], retrieval.label + ". Measures memory retrieval proof, not answer accuracy.", retrieval.r10 >= 95 ? "ok" : "warn"));
|
|
3174
|
+
}
|
|
3175
|
+
if (scale) {
|
|
3176
|
+
cards.push(metricBars("Scale proof", scale.hitRate + "% hit", [
|
|
3177
|
+
{ label: "Packets", value: scale.packets, score: Math.min(100, scale.packets / 10), status: scale.packets >= 240 ? "ok" : "warn" },
|
|
3178
|
+
{ label: "Median", value: scale.medianLatency + "ms", score: Math.max(0, 100 - scale.medianLatency), status: scale.medianLatency <= 50 ? "ok" : "warn" },
|
|
3179
|
+
{ label: "Context cut", value: scale.contextReduction + "%", score: scale.contextReduction, status: scale.contextReduction >= 80 ? "ok" : "warn" }
|
|
3180
|
+
], "Large memory corpus stays searchable without loading every packet.", scale.hitRate >= 95 ? "ok" : "warn"));
|
|
3181
|
+
}
|
|
3182
|
+
els.dashboardCharts.textContent = "";
|
|
3183
|
+
var tailCards = [];
|
|
3184
|
+
if (!data.handoff) {
|
|
3185
|
+
tailCards.push(metricBars("Handoff blockers", blockers ? blockers + " open" : "clear", [
|
|
2966
3186
|
{ label: "Pending", value: data.pendingReview, score: Math.min(100, data.pendingReview * 24), status: data.pendingReview ? "warn" : "ok" },
|
|
2967
3187
|
{ label: "Stale", value: data.staleFlags, score: Math.min(100, data.staleFlags * 24), status: data.staleFlags ? "warn" : "ok" },
|
|
2968
3188
|
{ label: "Duplicate", value: data.duplicateFlags, score: Math.min(100, data.duplicateFlags * 24), status: data.duplicateFlags ? "warn" : "ok" },
|
|
2969
3189
|
{ label: "Missing context", value: data.missingContext, score: Math.min(100, data.missingContext * 18), status: data.missingContext ? "warn" : "ok" }
|
|
2970
|
-
], blockers ? "Resolve Review before handing work to another agent." : "Memory is clean for handoff.", blockers ? "warn" : "ok")
|
|
3190
|
+
], blockers ? "Resolve Review before handing work to another agent." : "Memory is clean for handoff.", blockers ? "warn" : "ok"));
|
|
3191
|
+
}
|
|
3192
|
+
tailCards.push(
|
|
2971
3193
|
metricBars("Change risk", riskSignals ? riskSignals + " signals" : "none", [
|
|
2972
3194
|
{ label: "Targets", value: data.riskTargets.length, score: Math.min(100, data.riskTargets.length * 18), status: data.riskTargets.length ? "warn" : "ok" },
|
|
2973
3195
|
{ label: "Silos", value: data.ownerSilos, score: Math.min(100, data.ownerSilos * 18), status: data.ownerSilos ? "warn" : "ok" },
|
|
2974
3196
|
{ label: "Hotspots", value: data.hotspots, score: Math.min(100, data.hotspots * 18), status: data.hotspots ? "danger" : "ok" }
|
|
2975
3197
|
], riskSignals ? "Open Intel or Owners before editing risky files." : "No loaded risk flags.", riskSignals ? "warn" : "ok")
|
|
2976
|
-
|
|
3198
|
+
);
|
|
3199
|
+
cards.concat(tailCards).slice(0, 3).forEach(function (card) { els.dashboardCharts.appendChild(card); });
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
function projectProfileSummary(report) {
|
|
3203
|
+
if (!report || !report.totals) return null;
|
|
3204
|
+
var concepts = Array.isArray(report.top_concepts) ? report.top_concepts.length : 0;
|
|
3205
|
+
var keyFiles = Array.isArray(report.key_files) ? report.key_files.length : 0;
|
|
3206
|
+
var commands = Array.isArray(report.run_commands) ? report.run_commands.length : 0;
|
|
3207
|
+
var coverage = Number(report.totals.memory_code_coverage_percent || 0);
|
|
3208
|
+
var topConcept = concepts ? report.top_concepts[0].concept : "no concepts";
|
|
3209
|
+
var actions = Array.isArray(report.next_actions) ? report.next_actions : [];
|
|
3210
|
+
return {
|
|
3211
|
+
concepts: concepts,
|
|
3212
|
+
keyFiles: keyFiles,
|
|
3213
|
+
commands: commands,
|
|
3214
|
+
label: topConcept,
|
|
3215
|
+
action: actions[0] || report.summary || "Use this as the first orientation packet for agents.",
|
|
3216
|
+
status: coverage >= 60 && keyFiles ? "ok" : "warn"
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3220
|
+
function capabilityAuditSummary(report) {
|
|
3221
|
+
if (!report || !Array.isArray(report.pillars)) return null;
|
|
3222
|
+
var rows = report.pillars.slice(0, 4).map(function (pillar) {
|
|
3223
|
+
return {
|
|
3224
|
+
label: pillar.label || pillar.id || "pillar",
|
|
3225
|
+
value: Number(pillar.score || 0) + "%",
|
|
3226
|
+
score: Number(pillar.score || 0),
|
|
3227
|
+
status: pillar.status === "ready" ? "ok" : (pillar.status === "gap" ? "danger" : "warn")
|
|
3228
|
+
};
|
|
3229
|
+
});
|
|
3230
|
+
var open = report.pillars.filter(function (pillar) { return pillar.status !== "ready"; }).length;
|
|
3231
|
+
return {
|
|
3232
|
+
label: Number(report.overall_score || 0) + "/100",
|
|
3233
|
+
rows: rows,
|
|
3234
|
+
action: open
|
|
3235
|
+
? (Array.isArray(report.next_actions) && report.next_actions[0] ? report.next_actions[0] : "Review capability gaps before publishing claims.")
|
|
3236
|
+
: "Memory, benchmark, collaboration, and viewer proof surfaces are ready.",
|
|
3237
|
+
status: report.status === "ready" ? "ok" : (report.status === "gap" ? "danger" : "warn")
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
function contextSlotsSummary(report) {
|
|
3242
|
+
if (!report || !report.totals) return null;
|
|
3243
|
+
var slots = Array.isArray(report.slots) ? report.slots : [];
|
|
3244
|
+
var pinned = Number(report.totals.pinned || 0);
|
|
3245
|
+
var total = Number(report.totals.slots || slots.length || 0);
|
|
3246
|
+
var chars = Number(report.totals.context_chars || 0);
|
|
3247
|
+
var firstPinned = slots.find(function (slot) { return slot && slot.pinned && slot.content; });
|
|
3248
|
+
return {
|
|
3249
|
+
pinned: pinned,
|
|
3250
|
+
total: total,
|
|
3251
|
+
chars: chars,
|
|
3252
|
+
label: firstPinned ? firstPinned.label : (pinned ? String(pinned) + " pinned" : "none"),
|
|
3253
|
+
action: pinned
|
|
3254
|
+
? "Pinned slots are included before task-specific recall for stable repo guidance."
|
|
3255
|
+
: "Add a slot for tiny always-relevant repo context instead of repeating it every session.",
|
|
3256
|
+
status: pinned ? "ok" : "warn"
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
function sessionReplaySummary(report) {
|
|
3261
|
+
if (!report || !report.totals) return null;
|
|
3262
|
+
var events = Number(report.totals.events || 0);
|
|
3263
|
+
var candidates = Number(report.totals.durable_candidates || 0);
|
|
3264
|
+
var sessions = Number(report.totals.sessions || 0);
|
|
3265
|
+
return {
|
|
3266
|
+
events: events,
|
|
3267
|
+
candidates: candidates,
|
|
3268
|
+
sessions: sessions,
|
|
3269
|
+
label: candidates ? candidates + " distillable" : (events ? events + " observed" : "none"),
|
|
3270
|
+
action: candidates
|
|
3271
|
+
? "Open Memory and distill durable session observations into reviewable packets."
|
|
3272
|
+
: "Replay digest proves what agents observed without exposing raw transcripts.",
|
|
3273
|
+
status: candidates ? "warn" : (events ? "ok" : "warn")
|
|
3274
|
+
};
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
function setupDoctorSummary(report) {
|
|
3278
|
+
if (!Array.isArray(report) || !report.length) return null;
|
|
3279
|
+
var configured = report.filter(function (item) { return item && item.configured; }).length;
|
|
3280
|
+
var claude = report.find(function (item) { return item && item.agent === "claude-code"; });
|
|
3281
|
+
var hookSummary = claude && claude.hook_summary;
|
|
3282
|
+
var missing = hookSummary && Array.isArray(hookSummary.missing) ? hookSummary.missing : [];
|
|
3283
|
+
var claudeHookReady = Boolean(hookSummary && hookSummary.ready);
|
|
3284
|
+
var missingCount = missing.length;
|
|
3285
|
+
var action = "";
|
|
3286
|
+
if (missingCount) {
|
|
3287
|
+
action = "Run kage setup claude-code --project . --write before relying on automatic memory.";
|
|
3288
|
+
} else if (configured) {
|
|
3289
|
+
action = "Automatic memory setup is visible for teammate handoff.";
|
|
3290
|
+
} else {
|
|
3291
|
+
action = "Run kage setup doctor to choose the next agent setup step.";
|
|
3292
|
+
}
|
|
3293
|
+
return {
|
|
3294
|
+
total: report.length,
|
|
3295
|
+
configured: configured,
|
|
3296
|
+
claudeHookReady: claudeHookReady,
|
|
3297
|
+
missingCount: missingCount,
|
|
3298
|
+
label: missingCount ? missingCount + " missing" : configured + "/" + report.length + " ready",
|
|
3299
|
+
action: action,
|
|
3300
|
+
status: missingCount || !configured ? "warn" : "ok"
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
function memoryAccessSummary(report, approvedPackets) {
|
|
3305
|
+
var totals = report && report.totals;
|
|
3306
|
+
if (!totals) return null;
|
|
3307
|
+
return {
|
|
3308
|
+
tracked: Number(totals.tracked_packets || 0),
|
|
3309
|
+
uses30d: Number(totals.uses_30d || 0),
|
|
3310
|
+
hot: Number(totals.hot_packets || 0),
|
|
3311
|
+
cold: Number(totals.cold_packets == null ? Math.max(0, approvedPackets) : totals.cold_packets)
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
function memoryAuditSummary(report) {
|
|
3316
|
+
var totals = report && report.totals;
|
|
3317
|
+
if (!totals) return null;
|
|
3318
|
+
return {
|
|
3319
|
+
total: Number(totals.total || 0),
|
|
3320
|
+
capture: Number(totals.capture || 0),
|
|
3321
|
+
review: Number(totals.approve || 0) + Number(totals.reject || 0),
|
|
3322
|
+
supersede: Number(totals.supersede || 0)
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
function memoryHandoffSummary(report) {
|
|
3327
|
+
var totals = report && report.totals;
|
|
3328
|
+
if (!totals) return null;
|
|
3329
|
+
var primary = report.primary_action || {};
|
|
3330
|
+
var openItems = Number(firstNumber(totals.open_items, 0));
|
|
3331
|
+
var severity = primary.severity || (openItems ? "warning" : "ok");
|
|
3332
|
+
var status = severity === "blocker" ? "danger" : severity === "warning" ? "warn" : severity === "ok" ? "ok" : "memory";
|
|
3333
|
+
var label = primary.label || (openItems ? "Resolve handoff" : "Ready for handoff");
|
|
3334
|
+
return {
|
|
3335
|
+
label: label,
|
|
3336
|
+
actionLabel: label.indexOf("Resolve") === 0 ? "Resolve" : label.indexOf("Ready") === 0 ? "Ready" : label.indexOf("Review") === 0 ? "Review" : label,
|
|
3337
|
+
detail: primary.summary || report.summary || "",
|
|
3338
|
+
action: primary.action || report.summary || "Open Review before handing work to another agent.",
|
|
3339
|
+
status: status,
|
|
3340
|
+
openItems: openItems,
|
|
3341
|
+
blockers: Number(firstNumber(totals.blockers, 0)),
|
|
3342
|
+
warnings: Number(firstNumber(totals.warnings, 0)),
|
|
3343
|
+
recentChanges: Number(firstNumber(totals.recent_changes, 0)),
|
|
3344
|
+
recentMutations: Number(firstNumber(totals.recent_mutations, 0)),
|
|
3345
|
+
distillableSessions: Number(firstNumber(totals.distillable_sessions, 0)),
|
|
3346
|
+
durableObservations: Number(firstNumber(totals.durable_observations, 0))
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
function memoryLifecycleSummary(report) {
|
|
3351
|
+
var totals = report && report.totals;
|
|
3352
|
+
if (!totals) return null;
|
|
3353
|
+
var stale = Number(totals.stale || 0) + Number(totals.disputed || 0);
|
|
3354
|
+
var ungrounded = Number(totals.ungrounded || 0);
|
|
3355
|
+
var pending = Number(totals.pending || 0);
|
|
3356
|
+
return {
|
|
3357
|
+
ready: Number(totals.hot || 0) + Number(totals.healthy || 0),
|
|
3358
|
+
ungrounded: ungrounded,
|
|
3359
|
+
stale: stale,
|
|
3360
|
+
needsReview: stale + ungrounded + pending
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
function memoryTimelineSummary(report) {
|
|
3365
|
+
var totals = report && report.totals;
|
|
3366
|
+
if (!totals) return null;
|
|
3367
|
+
return {
|
|
3368
|
+
total: Number(totals.total || 0),
|
|
3369
|
+
added: Number(totals.added || 0),
|
|
3370
|
+
updated: Number(totals.updated || 0),
|
|
3371
|
+
pending: Number(totals.pending || 0),
|
|
3372
|
+
deprecated: Number(totals.deprecated || 0)
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
function memoryLineageSummary(report) {
|
|
3377
|
+
var totals = report && report.totals;
|
|
3378
|
+
if (!totals) return null;
|
|
3379
|
+
return {
|
|
3380
|
+
superseded: Number(totals.superseded || 0),
|
|
3381
|
+
chains: Number(totals.chains || 0),
|
|
3382
|
+
orphans: Number(totals.orphans || 0),
|
|
3383
|
+
replacementsMissing: Number(totals.replacements_missing || 0)
|
|
3384
|
+
};
|
|
2977
3385
|
}
|
|
2978
3386
|
|
|
2979
3387
|
function metricDonut(title, percent, detail, action, status) {
|
|
@@ -3104,6 +3512,7 @@
|
|
|
3104
3512
|
}
|
|
3105
3513
|
});
|
|
3106
3514
|
var linkedCount = memoryEntities.filter(function (entity) { return (memoryLinkCounts.get(entity.id) || 0) > 0; }).length;
|
|
3515
|
+
var accessTotals = state.reports.memoryAccess && state.reports.memoryAccess.totals;
|
|
3107
3516
|
var query = parseSearchQuery(els.memorySearch ? els.memorySearch.value : "");
|
|
3108
3517
|
var filter = els.memoryFilter ? els.memoryFilter.value : "all";
|
|
3109
3518
|
var filtered = memoryEntities.filter(function (entity) {
|
|
@@ -3118,10 +3527,17 @@
|
|
|
3118
3527
|
els.memoryStats.innerHTML = [
|
|
3119
3528
|
memoryStat("Reusable", memoryEntities.length),
|
|
3120
3529
|
memoryStat("Code-linked", linkedCount),
|
|
3530
|
+
memoryStat("Reused 30d", accessTotals ? Number(accessTotals.uses_30d || 0) : "n/a"),
|
|
3121
3531
|
memoryStat("Needs paths", memoryEntities.length - linkedCount)
|
|
3122
3532
|
].join("");
|
|
3123
3533
|
}
|
|
3124
3534
|
if (els.memoryOverview) renderMemoryOverview(memoryEntities, linkedCount);
|
|
3535
|
+
if (els.lifecycleList) renderMemoryLifecycle(memoryEntities, memoryLinkCounts);
|
|
3536
|
+
if (els.memoryReviewActions) renderMemoryReviewActions();
|
|
3537
|
+
if (els.memoryTimelineList) renderMemoryTimeline();
|
|
3538
|
+
if (els.memoryAuditList) renderMemoryAudit();
|
|
3539
|
+
if (els.memoryLineageList) renderMemoryLineage();
|
|
3540
|
+
if (els.sessionCaptureList) renderSessionCapture();
|
|
3125
3541
|
els.memoryList.textContent = "";
|
|
3126
3542
|
if (!memoryEntities.length) {
|
|
3127
3543
|
els.memoryList.className = "memory-list details-empty";
|
|
@@ -3142,6 +3558,7 @@
|
|
|
3142
3558
|
filtered.slice(0, 60).forEach(function (entity) {
|
|
3143
3559
|
var links = memoryCodeLinksForEntity(entity.id);
|
|
3144
3560
|
var firstCodeTarget = primaryCodeTargetForMemory(entity.id, links);
|
|
3561
|
+
var access = memoryAccessForEntity(entity);
|
|
3145
3562
|
var item = document.createElement("button");
|
|
3146
3563
|
item.type = "button";
|
|
3147
3564
|
var selected = state.selected && state.selected.kind === "entity" && state.selected.id === entity.id;
|
|
@@ -3158,6 +3575,9 @@
|
|
|
3158
3575
|
item.querySelector(".memory-row-target").textContent = links.length
|
|
3159
3576
|
? links.length + " code link" + (links.length === 1 ? "" : "s") + (firstCodeTarget ? " | " + trimIntelText(codeTargetLabel(firstCodeTarget), 64) : "")
|
|
3160
3577
|
: "needs code paths";
|
|
3578
|
+
if (access && access.total_uses) {
|
|
3579
|
+
item.querySelector(".memory-row-target").textContent += " | reused " + Number(access.uses_30d || 0) + "x in 30d";
|
|
3580
|
+
}
|
|
3161
3581
|
item.addEventListener("click", function () {
|
|
3162
3582
|
selectEntity(entity.id, true);
|
|
3163
3583
|
render();
|
|
@@ -3171,6 +3591,13 @@
|
|
|
3171
3591
|
return "<div><strong>" + escapeHtml(String(value)) + "</strong><span>" + escapeHtml(label) + "</span></div>";
|
|
3172
3592
|
}
|
|
3173
3593
|
|
|
3594
|
+
function memoryAccessForEntity(entity) {
|
|
3595
|
+
var report = state.reports && state.reports.memoryAccess;
|
|
3596
|
+
if (!report || !Array.isArray(report.entries)) return null;
|
|
3597
|
+
var ids = new Set([entity.id].concat(entity.aliases || []));
|
|
3598
|
+
return report.entries.find(function (entry) { return ids.has(entry.packet_id); }) || null;
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3174
3601
|
function renderMemoryOverview(memoryEntities, linkedCount) {
|
|
3175
3602
|
els.memoryOverview.textContent = "";
|
|
3176
3603
|
var total = memoryEntities.length;
|
|
@@ -3198,6 +3625,592 @@
|
|
|
3198
3625
|
}), "A healthy repo has decisions, bug fixes, runbooks, gotchas, and code explanations.", "ok"));
|
|
3199
3626
|
}
|
|
3200
3627
|
|
|
3628
|
+
function renderMemoryReviewActions() {
|
|
3629
|
+
var lifecycle = state.reports && state.reports.lifecycle;
|
|
3630
|
+
var access = state.reports && state.reports.memoryAccess;
|
|
3631
|
+
var recommendations = lifecycle && Array.isArray(lifecycle.recommendations)
|
|
3632
|
+
? lifecycle.recommendations
|
|
3633
|
+
: (access && Array.isArray(access.recommendations) ? access.recommendations : []);
|
|
3634
|
+
if (els.memoryReviewStatus) {
|
|
3635
|
+
els.memoryReviewStatus.textContent = recommendations.length ? recommendations.length + " action" + (recommendations.length === 1 ? "" : "s") : "clear";
|
|
3636
|
+
}
|
|
3637
|
+
els.memoryReviewActions.textContent = "";
|
|
3638
|
+
if (!lifecycle && !access) {
|
|
3639
|
+
els.memoryReviewActions.className = "memory-action-list details-empty";
|
|
3640
|
+
els.memoryReviewActions.textContent = "No lifecycle report loaded. Run kage lifecycle or open the local viewer after refreshing memory.";
|
|
3641
|
+
return;
|
|
3642
|
+
}
|
|
3643
|
+
if (!recommendations.length) {
|
|
3644
|
+
els.memoryReviewActions.className = "memory-action-list details-empty";
|
|
3645
|
+
els.memoryReviewActions.textContent = "No memory review actions. Recent recall usage does not show obvious hot or cold packets.";
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3648
|
+
els.memoryReviewActions.className = "memory-action-list";
|
|
3649
|
+
recommendations.slice(0, 6).forEach(function (item) {
|
|
3650
|
+
var card = document.createElement("article");
|
|
3651
|
+
card.className = classNames("memory-action", item.severity && "memory-action-" + item.severity);
|
|
3652
|
+
card.innerHTML = [
|
|
3653
|
+
"<div class=\"memory-action-head\"><span></span><strong></strong></div>",
|
|
3654
|
+
"<p></p>",
|
|
3655
|
+
"<em></em>"
|
|
3656
|
+
].join("");
|
|
3657
|
+
card.querySelector(".memory-action-head span").textContent = memoryActionLabel(item.kind);
|
|
3658
|
+
card.querySelector(".memory-action-head strong").textContent = item.summary || item.title || "Memory action";
|
|
3659
|
+
card.querySelector("p").textContent = item.reason || "";
|
|
3660
|
+
card.querySelector("em").textContent = item.action || "";
|
|
3661
|
+
var entity = item.packet_id ? findMemoryEntityByPacketId(item.packet_id) : null;
|
|
3662
|
+
if (entity) {
|
|
3663
|
+
card.tabIndex = 0;
|
|
3664
|
+
card.setAttribute("role", "button");
|
|
3665
|
+
card.addEventListener("click", function () {
|
|
3666
|
+
selectEntity(entity.id, true);
|
|
3667
|
+
render();
|
|
3668
|
+
});
|
|
3669
|
+
card.addEventListener("keydown", function (event) {
|
|
3670
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
3671
|
+
event.preventDefault();
|
|
3672
|
+
selectEntity(entity.id, true);
|
|
3673
|
+
render();
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
3676
|
+
els.memoryReviewActions.appendChild(card);
|
|
3677
|
+
});
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
function renderMemoryTimeline() {
|
|
3681
|
+
var report = state.reports && state.reports.timeline;
|
|
3682
|
+
var entries = Array.isArray(report && report.entries) ? report.entries : [];
|
|
3683
|
+
var totals = report && report.totals ? report.totals : {};
|
|
3684
|
+
if (els.memoryTimelineStatus) {
|
|
3685
|
+
els.memoryTimelineStatus.textContent = report ? String(Number(totals.total || entries.length)) + " recent" : "waiting";
|
|
3686
|
+
}
|
|
3687
|
+
if (els.memoryTimelineSummary) {
|
|
3688
|
+
els.memoryTimelineSummary.textContent = "";
|
|
3689
|
+
[
|
|
3690
|
+
lifecycleSummaryStep("+", "Added", Number(totals.added || 0), "New repo memories captured for future agents."),
|
|
3691
|
+
lifecycleSummaryStep("~", "Updated", Number(totals.updated || 0), "Packets with changed rationale, evidence, or paths."),
|
|
3692
|
+
lifecycleSummaryStep("?", "Pending", Number(totals.pending || 0), "Memory waiting for teammate review.")
|
|
3693
|
+
].forEach(function (step) { els.memoryTimelineSummary.appendChild(step); });
|
|
3694
|
+
}
|
|
3695
|
+
els.memoryTimelineList.textContent = "";
|
|
3696
|
+
if (!report) {
|
|
3697
|
+
els.memoryTimelineList.className = "session-capture-list details-empty";
|
|
3698
|
+
els.memoryTimelineList.textContent = "No memory timeline report loaded. Run kage timeline or open the local viewer after refresh.";
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
if (!entries.length) {
|
|
3702
|
+
els.memoryTimelineList.className = "session-capture-list details-empty";
|
|
3703
|
+
els.memoryTimelineList.textContent = "No recent memory activity. Capture durable repo decisions, bugs, runbooks, or gotchas as work happens.";
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
els.memoryTimelineList.className = "session-capture-list";
|
|
3707
|
+
entries.slice(0, 8).forEach(function (entry) {
|
|
3708
|
+
var card = document.createElement("article");
|
|
3709
|
+
card.className = "session-capture-card";
|
|
3710
|
+
card.innerHTML = [
|
|
3711
|
+
"<div class=\"session-capture-head\"><div><strong></strong><span></span></div><em></em></div>",
|
|
3712
|
+
"<p></p>",
|
|
3713
|
+
"<div class=\"session-capture-meta\"></div>"
|
|
3714
|
+
].join("");
|
|
3715
|
+
card.querySelector("strong").textContent = entry.title || "Memory packet";
|
|
3716
|
+
card.querySelector("span").textContent = [entry.type, timelineDateLabel(entry.date), entry.source_kind].filter(Boolean).join(" | ");
|
|
3717
|
+
card.querySelector("em").textContent = timelineKindLabel(entry.kind);
|
|
3718
|
+
card.querySelector("p").textContent = entry.action || entry.summary || "Review this memory activity.";
|
|
3719
|
+
var meta = card.querySelector(".session-capture-meta");
|
|
3720
|
+
[
|
|
3721
|
+
["status", entry.status || "unknown"],
|
|
3722
|
+
["paths", Array.isArray(entry.paths) ? entry.paths.slice(0, 2).join(", ") || "none" : "none"],
|
|
3723
|
+
["tags", Array.isArray(entry.tags) ? entry.tags.slice(0, 3).join(", ") || "none" : "none"]
|
|
3724
|
+
].forEach(function (row) {
|
|
3725
|
+
var chip = document.createElement("span");
|
|
3726
|
+
chip.textContent = row[0] + ": " + row[1];
|
|
3727
|
+
meta.appendChild(chip);
|
|
3728
|
+
});
|
|
3729
|
+
var entity = entry.packet_id ? findMemoryEntityByPacketId(entry.packet_id) : null;
|
|
3730
|
+
if (entity) {
|
|
3731
|
+
card.tabIndex = 0;
|
|
3732
|
+
card.setAttribute("role", "button");
|
|
3733
|
+
card.addEventListener("click", function () {
|
|
3734
|
+
selectEntity(entity.id, true);
|
|
3735
|
+
render();
|
|
3736
|
+
});
|
|
3737
|
+
card.addEventListener("keydown", function (event) {
|
|
3738
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
3739
|
+
event.preventDefault();
|
|
3740
|
+
selectEntity(entity.id, true);
|
|
3741
|
+
render();
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
els.memoryTimelineList.appendChild(card);
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
function renderMemoryAudit() {
|
|
3749
|
+
var report = state.reports && state.reports.memoryAudit;
|
|
3750
|
+
var entries = Array.isArray(report && report.entries) ? report.entries : [];
|
|
3751
|
+
var totals = report && report.totals ? report.totals : {};
|
|
3752
|
+
if (els.memoryAuditStatus) {
|
|
3753
|
+
els.memoryAuditStatus.textContent = report ? String(Number(totals.total || entries.length)) + " mutations" : "waiting";
|
|
3754
|
+
}
|
|
3755
|
+
if (els.memoryAuditSummary) {
|
|
3756
|
+
els.memoryAuditSummary.textContent = "";
|
|
3757
|
+
[
|
|
3758
|
+
lifecycleSummaryStep("1", "Captured", Number(totals.capture || 0), "New packets written to repo memory."),
|
|
3759
|
+
lifecycleSummaryStep("2", "Reviewed", Number(totals.approve || 0) + Number(totals.reject || 0), "Pending packets approved or rejected."),
|
|
3760
|
+
lifecycleSummaryStep("3", "Replaced", Number(totals.supersede || 0), "Old knowledge linked to current replacements.")
|
|
3761
|
+
].forEach(function (step) { els.memoryAuditSummary.appendChild(step); });
|
|
3762
|
+
}
|
|
3763
|
+
els.memoryAuditList.textContent = "";
|
|
3764
|
+
if (!report) {
|
|
3765
|
+
els.memoryAuditList.className = "session-capture-list details-empty";
|
|
3766
|
+
els.memoryAuditList.textContent = "No memory audit report loaded. Run kage memory-audit or open the local viewer after refresh.";
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
if (!entries.length) {
|
|
3770
|
+
els.memoryAuditList.className = "session-capture-list details-empty";
|
|
3771
|
+
els.memoryAuditList.textContent = "No audited memory mutations yet. Captures, feedback, reviews, and supersedes will appear here.";
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
els.memoryAuditList.className = "session-capture-list";
|
|
3775
|
+
entries.slice(0, 8).forEach(function (entry) {
|
|
3776
|
+
var card = document.createElement("article");
|
|
3777
|
+
card.className = "session-capture-card";
|
|
3778
|
+
card.innerHTML = [
|
|
3779
|
+
"<div class=\"session-capture-head\"><div><strong></strong><span></span></div><em></em></div>",
|
|
3780
|
+
"<p></p>",
|
|
3781
|
+
"<div class=\"session-capture-meta\"></div>"
|
|
3782
|
+
].join("");
|
|
3783
|
+
card.querySelector("strong").textContent = (entry.packet_titles || []).join(", ") || (entry.packet_ids || []).join(", ") || "Memory mutation";
|
|
3784
|
+
card.querySelector("span").textContent = [entry.actor, entry.branch, timelineDateLabel(entry.timestamp)].filter(Boolean).join(" | ");
|
|
3785
|
+
card.querySelector("em").textContent = entry.operation || "audit";
|
|
3786
|
+
card.querySelector("p").textContent = memoryAuditAction(entry);
|
|
3787
|
+
var meta = card.querySelector(".session-capture-meta");
|
|
3788
|
+
[
|
|
3789
|
+
["packets", Array.isArray(entry.packet_ids) ? String(entry.packet_ids.length) : "0"],
|
|
3790
|
+
["head", entry.head ? String(entry.head).slice(0, 8) : "none"],
|
|
3791
|
+
["details", auditDetailsLabel(entry.details)]
|
|
3792
|
+
].forEach(function (row) {
|
|
3793
|
+
var chip = document.createElement("span");
|
|
3794
|
+
chip.textContent = row[0] + ": " + row[1];
|
|
3795
|
+
meta.appendChild(chip);
|
|
3796
|
+
});
|
|
3797
|
+
els.memoryAuditList.appendChild(card);
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
function memoryAuditAction(entry) {
|
|
3802
|
+
if (!entry) return "Review this memory mutation before handoff.";
|
|
3803
|
+
if (entry.operation === "capture") return "New repo knowledge was captured for future agents.";
|
|
3804
|
+
if (entry.operation === "feedback") return "A recalled memory received explicit usefulness or stale feedback.";
|
|
3805
|
+
if (entry.operation === "approve") return "A pending packet became shared repo memory.";
|
|
3806
|
+
if (entry.operation === "reject") return "A pending packet was rejected instead of becoming shared memory.";
|
|
3807
|
+
if (entry.operation === "supersede") return "Old repo knowledge now points at a current replacement packet.";
|
|
3808
|
+
if (entry.operation === "deprecate") return "Stale memory was retired from active use.";
|
|
3809
|
+
if (entry.operation === "delete") return "Memory was removed by an explicit cleanup action.";
|
|
3810
|
+
return "Review this memory mutation before handoff.";
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
function auditDetailsLabel(details) {
|
|
3814
|
+
if (!details || typeof details !== "object") return "none";
|
|
3815
|
+
if (details.feedback) return "feedback " + details.feedback;
|
|
3816
|
+
if (details.reason) return String(details.reason).slice(0, 80);
|
|
3817
|
+
if (details.type) return "type " + details.type;
|
|
3818
|
+
return Object.keys(details).slice(0, 3).join(", ") || "none";
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
function renderMemoryLineage() {
|
|
3822
|
+
var report = state.reports && state.reports.lineage;
|
|
3823
|
+
var chains = Array.isArray(report && report.chains) ? report.chains : [];
|
|
3824
|
+
var orphans = Array.isArray(report && report.orphans) ? report.orphans : [];
|
|
3825
|
+
var totals = report && report.totals ? report.totals : {};
|
|
3826
|
+
if (els.memoryLineageStatus) {
|
|
3827
|
+
els.memoryLineageStatus.textContent = report ? String(Number(totals.chains || chains.length)) + " chains" : "waiting";
|
|
3828
|
+
}
|
|
3829
|
+
if (els.memoryLineageSummary) {
|
|
3830
|
+
els.memoryLineageSummary.textContent = "";
|
|
3831
|
+
[
|
|
3832
|
+
lifecycleSummaryStep("1", "Current", Number(totals.chains || chains.length), "Replacement packets future agents should trust."),
|
|
3833
|
+
lifecycleSummaryStep("2", "Retired", Number(totals.superseded || 0), "Old packets kept as audit history, not active recall."),
|
|
3834
|
+
lifecycleSummaryStep("3", "Repair", Number(totals.orphans || orphans.length), "Superseded packets missing a replacement link.")
|
|
3835
|
+
].forEach(function (step) { els.memoryLineageSummary.appendChild(step); });
|
|
3836
|
+
}
|
|
3837
|
+
els.memoryLineageList.textContent = "";
|
|
3838
|
+
if (!report) {
|
|
3839
|
+
els.memoryLineageList.className = "session-capture-list details-empty";
|
|
3840
|
+
els.memoryLineageList.textContent = "No lineage report loaded. Run kage lineage or open the local viewer after refresh.";
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
if (!chains.length && !orphans.length) {
|
|
3844
|
+
els.memoryLineageList.className = "session-capture-list details-empty";
|
|
3845
|
+
els.memoryLineageList.textContent = "No retired memory chains yet. Use kage supersede when a newer packet replaces old repo knowledge.";
|
|
3846
|
+
return;
|
|
3847
|
+
}
|
|
3848
|
+
els.memoryLineageList.className = "session-capture-list";
|
|
3849
|
+
chains.slice(0, 6).forEach(function (chain) {
|
|
3850
|
+
var card = document.createElement("article");
|
|
3851
|
+
card.className = "session-capture-card";
|
|
3852
|
+
card.innerHTML = [
|
|
3853
|
+
"<div class=\"session-capture-head\"><div><strong></strong><span></span></div><em>current</em></div>",
|
|
3854
|
+
"<p></p>",
|
|
3855
|
+
"<div class=\"session-capture-meta\"></div>"
|
|
3856
|
+
].join("");
|
|
3857
|
+
card.querySelector("strong").textContent = chain.current_title || "Replacement memory";
|
|
3858
|
+
card.querySelector("span").textContent = "replaces " + Number((chain.superseded_packet_ids || []).length) + " packet" + ((chain.superseded_packet_ids || []).length === 1 ? "" : "s");
|
|
3859
|
+
card.querySelector("p").textContent = chain.action || "Use the current replacement packet in recall.";
|
|
3860
|
+
var meta = card.querySelector(".session-capture-meta");
|
|
3861
|
+
[
|
|
3862
|
+
["reason", chain.reason || "replacement linked"],
|
|
3863
|
+
["paths", Array.isArray(chain.paths) ? chain.paths.slice(0, 2).join(", ") || "none" : "none"],
|
|
3864
|
+
["updated", timelineDateLabel(chain.updated_at)]
|
|
3865
|
+
].forEach(function (row) {
|
|
3866
|
+
var chip = document.createElement("span");
|
|
3867
|
+
chip.textContent = row[0] + ": " + row[1];
|
|
3868
|
+
meta.appendChild(chip);
|
|
3869
|
+
});
|
|
3870
|
+
var entity = chain.current_packet_id ? findMemoryEntityByPacketId(chain.current_packet_id) : null;
|
|
3871
|
+
if (entity) {
|
|
3872
|
+
card.tabIndex = 0;
|
|
3873
|
+
card.setAttribute("role", "button");
|
|
3874
|
+
card.addEventListener("click", function () {
|
|
3875
|
+
selectEntity(entity.id, true);
|
|
3876
|
+
render();
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
els.memoryLineageList.appendChild(card);
|
|
3880
|
+
});
|
|
3881
|
+
orphans.slice(0, 4).forEach(function (orphan) {
|
|
3882
|
+
var card = document.createElement("article");
|
|
3883
|
+
card.className = "session-capture-card memory-action-danger";
|
|
3884
|
+
card.innerHTML = [
|
|
3885
|
+
"<div class=\"session-capture-head\"><div><strong></strong><span></span></div><em>repair</em></div>",
|
|
3886
|
+
"<p></p>"
|
|
3887
|
+
].join("");
|
|
3888
|
+
card.querySelector("strong").textContent = orphan.title || "Superseded memory";
|
|
3889
|
+
card.querySelector("span").textContent = timelineDateLabel(orphan.updated_at);
|
|
3890
|
+
card.querySelector("p").textContent = orphan.action || "Add a replacement link before trusting this old context.";
|
|
3891
|
+
els.memoryLineageList.appendChild(card);
|
|
3892
|
+
});
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
function timelineKindLabel(kind) {
|
|
3896
|
+
if (kind === "added") return "added";
|
|
3897
|
+
if (kind === "updated") return "updated";
|
|
3898
|
+
if (kind === "pending") return "pending";
|
|
3899
|
+
if (kind === "deprecated") return "retired";
|
|
3900
|
+
return "activity";
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
function timelineDateLabel(date) {
|
|
3904
|
+
return date ? String(date).slice(0, 10) : "";
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
function memoryActionLabel(kind) {
|
|
3908
|
+
if (kind === "promote_hot") return "Hot";
|
|
3909
|
+
if (kind === "review_cold") return "Cold";
|
|
3910
|
+
if (kind === "connect_paths") return "Ground";
|
|
3911
|
+
if (kind === "add_grounding") return "Ground";
|
|
3912
|
+
if (kind === "review_stale") return "Stale";
|
|
3913
|
+
if (kind === "resolve_feedback") return "Feedback";
|
|
3914
|
+
if (kind === "archive_generated") return "Archive";
|
|
3915
|
+
if (kind === "review_pending") return "Pending";
|
|
3916
|
+
if (kind === "keep_verified") return "Keep";
|
|
3917
|
+
if (kind === "seed_usage") return "Start";
|
|
3918
|
+
return "Review";
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
function findMemoryEntityByPacketId(packetId) {
|
|
3922
|
+
return state.entities.find(function (entity) {
|
|
3923
|
+
if (!isMemoryPacketEntity(entity)) return false;
|
|
3924
|
+
if (entity.id === packetId || entity.packet_id === packetId) return true;
|
|
3925
|
+
return Array.isArray(entity.aliases) && entity.aliases.indexOf(packetId) !== -1;
|
|
3926
|
+
}) || null;
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
function renderMemoryLifecycle(memoryEntities, memoryLinkCounts) {
|
|
3930
|
+
if (!els.lifecycleList) return;
|
|
3931
|
+
var report = state.reports && state.reports.lifecycle;
|
|
3932
|
+
if (report && Array.isArray(report.items)) {
|
|
3933
|
+
renderMemoryLifecycleReport(report);
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
var proofPackets = memoryEntities
|
|
3937
|
+
.map(function (entity) {
|
|
3938
|
+
var links = memoryCodeLinksForEntity(entity.id);
|
|
3939
|
+
return {
|
|
3940
|
+
entity: entity,
|
|
3941
|
+
links: links,
|
|
3942
|
+
score: lifecyclePacketScore(entity, links, memoryLinkCounts.get(entity.id) || 0)
|
|
3943
|
+
};
|
|
3944
|
+
})
|
|
3945
|
+
.sort(function (a, b) {
|
|
3946
|
+
return b.score - a.score ||
|
|
3947
|
+
lifecycleTimestamp(b.entity).localeCompare(lifecycleTimestamp(a.entity)) ||
|
|
3948
|
+
displayName(a.entity).localeCompare(displayName(b.entity));
|
|
3949
|
+
});
|
|
3950
|
+
var grounded = proofPackets.filter(function (item) { return item.links.length; }).length;
|
|
3951
|
+
var recallReady = proofPackets.filter(function (item) {
|
|
3952
|
+
return item.links.length && lifecycleEvidence(item.entity).length && lifecycleRecallQuery(item.entity);
|
|
3953
|
+
}).length;
|
|
3954
|
+
if (els.lifecycleStatus) {
|
|
3955
|
+
els.lifecycleStatus.textContent = proofPackets.length ? recallReady + " recall-ready" : "empty";
|
|
3956
|
+
}
|
|
3957
|
+
if (els.lifecycleSummary) {
|
|
3958
|
+
els.lifecycleSummary.textContent = "";
|
|
3959
|
+
[
|
|
3960
|
+
lifecycleSummaryStep("1", "Captured", proofPackets.length, "Reusable facts agents saved instead of rediscovering."),
|
|
3961
|
+
lifecycleSummaryStep("2", "Grounded", grounded, "Packets linked to files, symbols, routes, or tests."),
|
|
3962
|
+
lifecycleSummaryStep("3", "Recall-ready", recallReady, "Grounded packets with evidence and a likely future query.")
|
|
3963
|
+
].forEach(function (step) { els.lifecycleSummary.appendChild(step); });
|
|
3964
|
+
}
|
|
3965
|
+
els.lifecycleList.textContent = "";
|
|
3966
|
+
if (!proofPackets.length) {
|
|
3967
|
+
els.lifecycleList.className = "lifecycle-list details-empty";
|
|
3968
|
+
els.lifecycleList.textContent = "No memory lifecycle to show yet. Capture reusable repo knowledge with kage learn or kage propose.";
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3971
|
+
els.lifecycleList.className = "lifecycle-list";
|
|
3972
|
+
proofPackets.slice(0, 5).forEach(function (item) {
|
|
3973
|
+
els.lifecycleList.appendChild(renderLifecycleCard(item.entity, item.links));
|
|
3974
|
+
});
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
function renderMemoryLifecycleReport(report) {
|
|
3978
|
+
var totals = report.totals || {};
|
|
3979
|
+
var needsReview = Number(totals.stale || 0) + Number(totals.ungrounded || 0) + Number(totals.disputed || 0) + Number(totals.pending || 0);
|
|
3980
|
+
if (els.lifecycleStatus) {
|
|
3981
|
+
els.lifecycleStatus.textContent = needsReview ? needsReview + " need review" : "healthy";
|
|
3982
|
+
}
|
|
3983
|
+
if (els.lifecycleSummary) {
|
|
3984
|
+
els.lifecycleSummary.textContent = "";
|
|
3985
|
+
[
|
|
3986
|
+
lifecycleSummaryStep("1", "Approved", Number(totals.approved || 0), "Repo-local packets available to future agents."),
|
|
3987
|
+
lifecycleSummaryStep("2", "Hot/healthy", Number(totals.hot || 0) + Number(totals.healthy || 0), "Memories that are grounded, fresh, or repeatedly recalled."),
|
|
3988
|
+
lifecycleSummaryStep("3", "Needs action", needsReview, "Packets to verify, ground, review, or resolve before trusting.")
|
|
3989
|
+
].forEach(function (step) { els.lifecycleSummary.appendChild(step); });
|
|
3990
|
+
}
|
|
3991
|
+
els.lifecycleList.textContent = "";
|
|
3992
|
+
var items = Array.isArray(report.items) ? report.items : [];
|
|
3993
|
+
if (!items.length) {
|
|
3994
|
+
els.lifecycleList.className = "lifecycle-list details-empty";
|
|
3995
|
+
els.lifecycleList.textContent = "No memory lifecycle to show yet. Capture reusable repo knowledge with kage learn or kage propose.";
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
3998
|
+
els.lifecycleList.className = "lifecycle-list";
|
|
3999
|
+
items.slice(0, 6).forEach(function (item) {
|
|
4000
|
+
els.lifecycleList.appendChild(renderLifecycleReportCard(item));
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
|
|
4004
|
+
function renderLifecycleReportCard(item) {
|
|
4005
|
+
var card = document.createElement("article");
|
|
4006
|
+
card.className = classNames("lifecycle-card", item.severity && "memory-action-" + item.severity);
|
|
4007
|
+
card.innerHTML = [
|
|
4008
|
+
"<div class=\"lifecycle-card-head\"><div><strong></strong><span></span></div><button type=\"button\">Inspect</button></div>",
|
|
4009
|
+
"<p></p>",
|
|
4010
|
+
"<div class=\"lifecycle-flow\"></div>"
|
|
4011
|
+
].join("");
|
|
4012
|
+
card.querySelector("strong").textContent = item.title || "Memory packet";
|
|
4013
|
+
card.querySelector(".lifecycle-card-head span").textContent = [
|
|
4014
|
+
memoryActionLabel(item.recommended_action),
|
|
4015
|
+
item.health,
|
|
4016
|
+
item.type
|
|
4017
|
+
].filter(Boolean).join(" | ");
|
|
4018
|
+
card.querySelector("p").textContent = item.reason || item.action || "Lifecycle action";
|
|
4019
|
+
var entity = item.packet_id ? findMemoryEntityByPacketId(item.packet_id) : null;
|
|
4020
|
+
var button = card.querySelector("button");
|
|
4021
|
+
if (entity) {
|
|
4022
|
+
button.addEventListener("click", function () {
|
|
4023
|
+
selectEntity(entity.id, true);
|
|
4024
|
+
render();
|
|
4025
|
+
});
|
|
4026
|
+
} else {
|
|
4027
|
+
button.disabled = true;
|
|
4028
|
+
button.textContent = "No node";
|
|
4029
|
+
}
|
|
4030
|
+
var flow = card.querySelector(".lifecycle-flow");
|
|
4031
|
+
[
|
|
4032
|
+
lifecycleFlowCell("Evidence", Number(item.source_refs || 0) + " source ref" + (Number(item.source_refs || 0) === 1 ? "" : "s")),
|
|
4033
|
+
lifecycleFlowCell("Grounding", item.paths && item.paths.length ? trimIntelText(item.paths[0], 100) : "Needs code path or symbol link"),
|
|
4034
|
+
lifecycleFlowCell("Recall", Number(item.uses_30d || 0) + " use" + (Number(item.uses_30d || 0) === 1 ? "" : "s") + " in 30d"),
|
|
4035
|
+
lifecycleFlowCell("Action", item.action || "Keep verified")
|
|
4036
|
+
].forEach(function (cell) { flow.appendChild(cell); });
|
|
4037
|
+
return card;
|
|
4038
|
+
}
|
|
4039
|
+
|
|
4040
|
+
function lifecycleSummaryStep(number, label, value, detail) {
|
|
4041
|
+
var item = document.createElement("article");
|
|
4042
|
+
item.className = "lifecycle-step";
|
|
4043
|
+
item.innerHTML = "<strong></strong><span></span><em></em><p></p>";
|
|
4044
|
+
item.querySelector("strong").textContent = number;
|
|
4045
|
+
item.querySelector("span").textContent = label;
|
|
4046
|
+
item.querySelector("em").textContent = formatDashboardValue(value);
|
|
4047
|
+
item.querySelector("p").textContent = detail;
|
|
4048
|
+
return item;
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
function renderLifecycleCard(entity, links) {
|
|
4052
|
+
var card = document.createElement("article");
|
|
4053
|
+
card.className = "lifecycle-card";
|
|
4054
|
+
var codeTargets = links.map(function (edge) {
|
|
4055
|
+
return state.entityById.get(edge.from === entity.id ? edge.to : edge.from);
|
|
4056
|
+
}).filter(Boolean).sort(function (a, b) {
|
|
4057
|
+
return codeTargetScore(b) - codeTargetScore(a) || codeTargetLabel(a).localeCompare(codeTargetLabel(b));
|
|
4058
|
+
});
|
|
4059
|
+
var evidence = lifecycleEvidence(entity);
|
|
4060
|
+
var query = lifecycleRecallQuery(entity);
|
|
4061
|
+
card.innerHTML = [
|
|
4062
|
+
"<div class=\"lifecycle-card-head\"><div><strong></strong><span></span></div><button type=\"button\">Inspect</button></div>",
|
|
4063
|
+
"<p></p>",
|
|
4064
|
+
"<div class=\"lifecycle-flow\"></div>"
|
|
4065
|
+
].join("");
|
|
4066
|
+
card.querySelector("strong").textContent = displayName(entity);
|
|
4067
|
+
card.querySelector(".lifecycle-card-head span").textContent = [
|
|
4068
|
+
entity.type || "memory",
|
|
4069
|
+
lifecycleTimestampLabel(entity)
|
|
4070
|
+
].filter(Boolean).join(" | ");
|
|
4071
|
+
card.querySelector("p").textContent = trimIntelText(entity.summary || entity.description || entity.path || "No summary", 220);
|
|
4072
|
+
card.querySelector("button").addEventListener("click", function () {
|
|
4073
|
+
selectEntity(entity.id, true);
|
|
4074
|
+
render();
|
|
4075
|
+
});
|
|
4076
|
+
var flow = card.querySelector(".lifecycle-flow");
|
|
4077
|
+
[
|
|
4078
|
+
lifecycleFlowCell("Captured", evidence.length ? trimIntelText(evidence[0], 100) : "No source evidence loaded"),
|
|
4079
|
+
lifecycleFlowCell("Grounded", codeTargets.length ? trimIntelText(codeTargetLabel(codeTargets[0]), 100) : "Needs code path or symbol link"),
|
|
4080
|
+
lifecycleFlowCell("Recall", query ? "\"" + query + "\"" : "Search by title, summary, tag, or path"),
|
|
4081
|
+
lifecycleFlowCell("Handoff", lifecycleHandoffText(entity, codeTargets.length))
|
|
4082
|
+
].forEach(function (cell) { flow.appendChild(cell); });
|
|
4083
|
+
return card;
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
function lifecycleFlowCell(label, value) {
|
|
4087
|
+
var cell = document.createElement("div");
|
|
4088
|
+
cell.className = "lifecycle-cell";
|
|
4089
|
+
cell.innerHTML = "<span></span><strong></strong>";
|
|
4090
|
+
cell.querySelector("span").textContent = label;
|
|
4091
|
+
cell.querySelector("strong").textContent = value;
|
|
4092
|
+
return cell;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
function lifecyclePacketScore(entity, links, linkCount) {
|
|
4096
|
+
var score = 0;
|
|
4097
|
+
score += Math.min(80, (links.length || linkCount) * 24);
|
|
4098
|
+
score += lifecycleEvidence(entity).length ? 35 : 0;
|
|
4099
|
+
score += lifecycleRecallQuery(entity) ? 25 : 0;
|
|
4100
|
+
score += ["decision", "bug_fix", "runbook", "gotcha", "code_explanation", "workflow"].indexOf(entity.type) !== -1 ? 20 : 0;
|
|
4101
|
+
score += lifecycleTimestamp(entity) ? 8 : 0;
|
|
4102
|
+
return score;
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
function lifecycleEvidence(entity) {
|
|
4106
|
+
var evidence = [];
|
|
4107
|
+
if (Array.isArray(entity.evidence)) evidence = evidence.concat(entity.evidence);
|
|
4108
|
+
if (Array.isArray(entity.source_refs)) {
|
|
4109
|
+
entity.source_refs.forEach(function (ref) {
|
|
4110
|
+
evidence.push([ref.kind, ref.captured_at || ref.path || ref.source].filter(Boolean).join(" "));
|
|
4111
|
+
});
|
|
4112
|
+
}
|
|
4113
|
+
if (entity.freshness && entity.freshness.verification) evidence.push(entity.freshness.verification);
|
|
4114
|
+
return evidence.map(function (item) { return String(item || "").trim(); }).filter(Boolean);
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
function lifecycleTimestamp(entity) {
|
|
4118
|
+
return String(entity.updated_at || entity.created_at || entity.last_seen_at || entity.first_seen_at || "");
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
function lifecycleTimestampLabel(entity) {
|
|
4122
|
+
var value = lifecycleTimestamp(entity);
|
|
4123
|
+
return value ? value.slice(0, 10) : "";
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
function lifecycleRecallQuery(entity) {
|
|
4127
|
+
var text = String(entity.summary || entity.description || displayName(entity) || "").trim();
|
|
4128
|
+
var path = Array.isArray(entity.paths) && entity.paths.length ? entity.paths[0] : entity.path;
|
|
4129
|
+
if (entity.type === "runbook") return "how to run " + trimIntelText(displayName(entity), 54);
|
|
4130
|
+
if (entity.type === "bug_fix") return "why did " + trimIntelText(displayName(entity), 54) + " break";
|
|
4131
|
+
if (entity.type === "decision") return "why was " + trimIntelText(displayName(entity), 54) + " decided";
|
|
4132
|
+
if (path) return "what should I know before changing " + path;
|
|
4133
|
+
return text ? trimIntelText(text, 76) : "";
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
function lifecycleHandoffText(entity, linkedTargetCount) {
|
|
4137
|
+
if (linkedTargetCount) return "Future agents touching this area can recall the reason before editing.";
|
|
4138
|
+
if (entity.type === "reference") return "Useful context, but add code paths if it should guide edits.";
|
|
4139
|
+
return "Add paths so teammates and agents can use it at change time.";
|
|
4140
|
+
}
|
|
4141
|
+
|
|
4142
|
+
function renderSessionCapture() {
|
|
4143
|
+
if (!els.sessionCaptureList) return;
|
|
4144
|
+
var report = state.reports && state.reports.sessions;
|
|
4145
|
+
var replay = state.reports && state.reports.replay;
|
|
4146
|
+
var totals = report && report.totals ? report.totals : {};
|
|
4147
|
+
var sessions = Array.isArray(report && report.sessions) ? report.sessions : [];
|
|
4148
|
+
var replayEvents = Array.isArray(replay && replay.events) ? replay.events : [];
|
|
4149
|
+
if (els.sessionCaptureStatus) {
|
|
4150
|
+
els.sessionCaptureStatus.textContent = sessions.length ? String(totals.sessions || sessions.length) + " sessions" : "no sessions";
|
|
4151
|
+
}
|
|
4152
|
+
if (els.sessionCaptureSummary) {
|
|
4153
|
+
els.sessionCaptureSummary.textContent = "";
|
|
4154
|
+
[
|
|
4155
|
+
lifecycleSummaryStep("1", "Observed", Number(totals.observations || 0), "Privacy-scanned local events from agent sessions."),
|
|
4156
|
+
lifecycleSummaryStep("2", "Distillable", Number(totals.durable_observations || 0), "Reusable command, workflow, decision, or issue signals."),
|
|
4157
|
+
lifecycleSummaryStep("3", "Needs review", Number(totals.sessions_with_candidates || 0), "Sessions ready for packet review with kage distill.")
|
|
4158
|
+
].forEach(function (step) { els.sessionCaptureSummary.appendChild(step); });
|
|
4159
|
+
}
|
|
4160
|
+
els.sessionCaptureList.textContent = "";
|
|
4161
|
+
if (!sessions.length) {
|
|
4162
|
+
els.sessionCaptureList.className = "session-capture-list details-empty";
|
|
4163
|
+
els.sessionCaptureList.textContent = "No observed sessions yet. Agents can call kage_observe, then kage_distill turns durable observations into reviewable memory packets.";
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
els.sessionCaptureList.className = "session-capture-list";
|
|
4167
|
+
sessions.slice(0, 6).forEach(function (session) {
|
|
4168
|
+
var card = document.createElement("article");
|
|
4169
|
+
card.className = "session-capture-card";
|
|
4170
|
+
var candidates = Array.isArray(session.candidate_types) && session.candidate_types.length ? session.candidate_types.join(", ") : "none";
|
|
4171
|
+
var agents = Array.isArray(session.agents) && session.agents.length ? session.agents.join(", ") : "unknown agent";
|
|
4172
|
+
card.innerHTML = [
|
|
4173
|
+
"<div class=\"session-capture-head\"><div><strong></strong><span></span></div><em></em></div>",
|
|
4174
|
+
"<p></p>",
|
|
4175
|
+
"<div class=\"session-capture-meta\"></div>"
|
|
4176
|
+
].join("");
|
|
4177
|
+
card.querySelector("strong").textContent = session.session_id || "default";
|
|
4178
|
+
card.querySelector("span").textContent = [agents, session.last_at || session.first_at || ""].filter(Boolean).join(" · ");
|
|
4179
|
+
card.querySelector("em").textContent = String(session.durable_observations || 0) + " distillable";
|
|
4180
|
+
card.querySelector("p").textContent = session.next_action || "Review this session before saving durable memory.";
|
|
4181
|
+
var meta = card.querySelector(".session-capture-meta");
|
|
4182
|
+
[
|
|
4183
|
+
["events", session.observations || 0],
|
|
4184
|
+
["candidates", candidates],
|
|
4185
|
+
["commands", Array.isArray(session.commands) ? session.commands.slice(0, 2).join(", ") || "none" : "none"],
|
|
4186
|
+
["paths", Array.isArray(session.paths) ? session.paths.slice(0, 2).join(", ") || "none" : "none"]
|
|
4187
|
+
].forEach(function (item) {
|
|
4188
|
+
var chip = document.createElement("span");
|
|
4189
|
+
chip.textContent = item[0] + ": " + item[1];
|
|
4190
|
+
meta.appendChild(chip);
|
|
4191
|
+
});
|
|
4192
|
+
els.sessionCaptureList.appendChild(card);
|
|
4193
|
+
});
|
|
4194
|
+
if (replayEvents.length) {
|
|
4195
|
+
var replayCard = document.createElement("article");
|
|
4196
|
+
replayCard.className = "session-capture-card";
|
|
4197
|
+
replayCard.innerHTML = [
|
|
4198
|
+
"<div class=\"session-capture-head\"><div><strong>Replay digest</strong><span>raw transcript text excluded</span></div><em></em></div>",
|
|
4199
|
+
"<p></p>",
|
|
4200
|
+
"<div class=\"session-capture-meta\"></div>"
|
|
4201
|
+
].join("");
|
|
4202
|
+
replayCard.querySelector("em").textContent = String(replay && replay.totals ? replay.totals.durable_candidates || 0 : 0) + " candidates";
|
|
4203
|
+
replayCard.querySelector("p").textContent = replay && replay.next_action ? replay.next_action : "Review the digest, then distill durable observations into memory packets.";
|
|
4204
|
+
var replayMeta = replayCard.querySelector(".session-capture-meta");
|
|
4205
|
+
replayEvents.slice(0, 6).forEach(function (event) {
|
|
4206
|
+
var chip = document.createElement("span");
|
|
4207
|
+
chip.textContent = (event.durable_candidate ? "candidate: " : "event: ") + (event.label || event.type || "observation") + " · " + trimIntelText(event.summary || "", 90);
|
|
4208
|
+
replayMeta.appendChild(chip);
|
|
4209
|
+
});
|
|
4210
|
+
els.sessionCaptureList.appendChild(replayCard);
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
|
|
3201
4214
|
function memoryCodeLinksForEntity(entityId) {
|
|
3202
4215
|
return state.edges.filter(function (edge) {
|
|
3203
4216
|
if ((edge.from !== entityId && edge.to !== entityId) || !isMemoryCodeEdge(edge)) return false;
|
|
@@ -3357,17 +4370,29 @@
|
|
|
3357
4370
|
var packets = state.pendingPackets || [];
|
|
3358
4371
|
var inbox = state.inbox;
|
|
3359
4372
|
var inboxItems = inbox && Array.isArray(inbox.items) ? inbox.items : [];
|
|
4373
|
+
var handoff = state.reports && state.reports.handoff;
|
|
4374
|
+
var handoffItems = handoff && Array.isArray(handoff.items) ? handoff.items : [];
|
|
3360
4375
|
var counts = inbox && inbox.counts ? inbox.counts : {};
|
|
3361
4376
|
var openCount = reviewOpenCount(counts, packets, inboxItems);
|
|
4377
|
+
if (handoff && handoff.totals) openCount = Number(firstNumber(handoff.totals.open_items, openCount));
|
|
3362
4378
|
els.reviewCount.textContent = String(openCount);
|
|
3363
4379
|
els.reviewList.textContent = "";
|
|
3364
|
-
if (els.reviewOverview) renderReviewOverview(inbox, packets, inboxItems);
|
|
3365
|
-
|
|
4380
|
+
if (els.reviewOverview) renderReviewOverview(inbox, packets, inboxItems, handoff);
|
|
4381
|
+
renderMemoryHandoff(handoff, handoffItems);
|
|
4382
|
+
if (!packets.length && !inboxItems.length && !handoffItems.length && !state.reviewText) {
|
|
3366
4383
|
els.reviewList.className = "review-list details-empty";
|
|
3367
4384
|
els.reviewList.textContent = "No pending packets loaded. Launch with `kage viewer --project <repo>` to load review context automatically.";
|
|
3368
4385
|
return;
|
|
3369
4386
|
}
|
|
3370
4387
|
els.reviewList.className = "review-list";
|
|
4388
|
+
handoffItems.slice(0, 10).forEach(function (entry) {
|
|
4389
|
+
els.reviewList.appendChild(reviewCard({
|
|
4390
|
+
title: entry.title || entry.summary || entry.kind,
|
|
4391
|
+
meta: [entry.kind, entry.severity, entry.date && timelineDateLabel(entry.date)].filter(Boolean).join(" | "),
|
|
4392
|
+
summary: entry.summary || "",
|
|
4393
|
+
risk: entry.action || "Review before handoff"
|
|
4394
|
+
}, "handoff"));
|
|
4395
|
+
});
|
|
3371
4396
|
if (inbox) {
|
|
3372
4397
|
var summary = document.createElement("div");
|
|
3373
4398
|
summary.className = "review-item";
|
|
@@ -3434,7 +4459,7 @@
|
|
|
3434
4459
|
}
|
|
3435
4460
|
}
|
|
3436
4461
|
|
|
3437
|
-
function renderReviewOverview(inbox, packets, inboxItems) {
|
|
4462
|
+
function renderReviewOverview(inbox, packets, inboxItems, handoff) {
|
|
3438
4463
|
els.reviewOverview.textContent = "";
|
|
3439
4464
|
var counts = inbox && inbox.counts ? inbox.counts : {};
|
|
3440
4465
|
var pending = Number(firstNumber(counts.pending, packets.length, 0));
|
|
@@ -3442,19 +4467,74 @@
|
|
|
3442
4467
|
var duplicates = Number(firstNumber(counts.duplicates, 0));
|
|
3443
4468
|
var missingContext = Number(firstNumber(counts.missing_context, 0));
|
|
3444
4469
|
var blockers = reviewOpenCount(counts, packets, inboxItems);
|
|
4470
|
+
if (handoff && handoff.totals) blockers = Number(firstNumber(handoff.totals.open_items, blockers));
|
|
4471
|
+
var mutations = handoff && handoff.totals ? Number(firstNumber(handoff.totals.recent_mutations, 0)) : 0;
|
|
3445
4472
|
els.reviewOverview.appendChild(metricDonut(
|
|
3446
4473
|
"Handoff readiness",
|
|
3447
4474
|
blockers ? 0 : 100,
|
|
3448
|
-
blockers ? blockers + "
|
|
3449
|
-
blockers ? "Resolve
|
|
4475
|
+
blockers ? blockers + " memory handoff item(s) need attention" : "No pending, stale, duplicate, or missing-context memory",
|
|
4476
|
+
blockers ? "Resolve the handoff queue before trusting branch memory." : "Ready to hand work to another agent or teammate.",
|
|
3450
4477
|
blockers ? "warn" : "ok"
|
|
3451
4478
|
));
|
|
3452
|
-
els.reviewOverview.appendChild(metricBars("Inbox breakdown", blockers ? blockers + " open" : "clear", [
|
|
4479
|
+
els.reviewOverview.appendChild(metricBars(handoff ? "Handoff queue" : "Inbox breakdown", blockers ? blockers + " open" : "clear", [
|
|
3453
4480
|
{ label: "Pending", value: pending, score: Math.min(100, pending * 24), status: pending ? "warn" : "ok" },
|
|
3454
4481
|
{ label: "Stale", value: stale, score: Math.min(100, stale * 24), status: stale ? "warn" : "ok" },
|
|
3455
4482
|
{ label: "Duplicates", value: duplicates, score: Math.min(100, duplicates * 24), status: duplicates ? "warn" : "ok" },
|
|
3456
|
-
{ label: "
|
|
3457
|
-
], "These are the
|
|
4483
|
+
{ label: "Mutations", value: mutations, score: Math.min(100, mutations * 16), status: mutations ? "ok" : "warn" }
|
|
4484
|
+
], handoff ? (handoff.summary || "Combined inbox, lifecycle, audit, timeline, and lineage.") : "These are the review metrics that should block merge or handoff.", blockers ? "warn" : "ok"));
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
function renderMemoryHandoff(handoff, items) {
|
|
4488
|
+
if (!els.handoffList) return;
|
|
4489
|
+
var totals = handoff && handoff.totals ? handoff.totals : {};
|
|
4490
|
+
if (els.handoffStatus) {
|
|
4491
|
+
els.handoffStatus.textContent = handoff ? (Number(totals.open_items || 0) ? Number(totals.open_items || 0) + " open" : "ready") : "waiting";
|
|
4492
|
+
}
|
|
4493
|
+
if (els.handoffSummary) {
|
|
4494
|
+
els.handoffSummary.textContent = "";
|
|
4495
|
+
[
|
|
4496
|
+
lifecycleSummaryStep("1", "Open", Number(totals.open_items || 0), "Blockers and warnings to resolve before another agent trusts memory."),
|
|
4497
|
+
lifecycleSummaryStep("2", "Distill", Number(totals.distillable_sessions || 0), "Observed sessions with reusable learnings that should become memory packets."),
|
|
4498
|
+
lifecycleSummaryStep("3", "Mutations", Number(totals.recent_mutations || 0), "Memory captures, reviews, feedback, and supersedes since audit logging began."),
|
|
4499
|
+
lifecycleSummaryStep("4", "Lineage", Number(totals.supersession_orphans || 0), "Retired packets missing a current replacement.")
|
|
4500
|
+
].forEach(function (step) { els.handoffSummary.appendChild(step); });
|
|
4501
|
+
}
|
|
4502
|
+
els.handoffList.textContent = "";
|
|
4503
|
+
if (!handoff) {
|
|
4504
|
+
els.handoffList.className = "session-capture-list details-empty";
|
|
4505
|
+
els.handoffList.textContent = "No handoff report loaded. Run kage handoff or open the local viewer after refresh.";
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4508
|
+
if (!items.length) {
|
|
4509
|
+
els.handoffList.className = "session-capture-list details-empty";
|
|
4510
|
+
els.handoffList.textContent = "No memory handoff actions are open.";
|
|
4511
|
+
return;
|
|
4512
|
+
}
|
|
4513
|
+
els.handoffList.className = "session-capture-list";
|
|
4514
|
+
items.slice(0, 8).forEach(function (entry) {
|
|
4515
|
+
els.handoffList.appendChild(reviewCard({
|
|
4516
|
+
title: entry.title || entry.summary || entry.kind,
|
|
4517
|
+
meta: [entry.kind, entry.severity, entry.date && timelineDateLabel(entry.date)].filter(Boolean).join(" | "),
|
|
4518
|
+
summary: entry.summary || "",
|
|
4519
|
+
risk: entry.action || "Review before handoff"
|
|
4520
|
+
}, "handoff"));
|
|
4521
|
+
});
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4524
|
+
function reviewCard(entry, extraClass) {
|
|
4525
|
+
var item = document.createElement("div");
|
|
4526
|
+
item.className = "review-item" + (extraClass ? " " + extraClass : "");
|
|
4527
|
+
item.innerHTML = [
|
|
4528
|
+
"<div class=\"review-title\"></div>",
|
|
4529
|
+
"<div class=\"review-meta\"></div>",
|
|
4530
|
+
"<div class=\"review-summary\"></div>",
|
|
4531
|
+
"<div class=\"review-risks\"></div>"
|
|
4532
|
+
].join("");
|
|
4533
|
+
item.querySelector(".review-title").textContent = entry.title || "";
|
|
4534
|
+
item.querySelector(".review-meta").textContent = entry.meta || "";
|
|
4535
|
+
item.querySelector(".review-summary").textContent = entry.summary || "";
|
|
4536
|
+
item.querySelector(".review-risks").textContent = entry.risk || "";
|
|
4537
|
+
return item;
|
|
3458
4538
|
}
|
|
3459
4539
|
|
|
3460
4540
|
function reviewOpenCount(counts, packets, inboxItems) {
|
|
@@ -3481,6 +4561,7 @@
|
|
|
3481
4561
|
els.proofStatus.textContent = "loaded";
|
|
3482
4562
|
els.proofList.className = "proof-list";
|
|
3483
4563
|
if (els.proofOverview) renderProofOverview(metrics, state.reports || {});
|
|
4564
|
+
renderProofLedger(state.reports && state.reports.benchmark);
|
|
3484
4565
|
var rows = [
|
|
3485
4566
|
["Validation", metrics.harness && metrics.harness.validation_ok ? "clean" : "check"],
|
|
3486
4567
|
["Evidence", metrics.memory_graph ? metrics.memory_graph.evidence_coverage_percent + "%" : "n/a"],
|
|
@@ -3497,15 +4578,58 @@
|
|
|
3497
4578
|
});
|
|
3498
4579
|
}
|
|
3499
4580
|
|
|
4581
|
+
function renderProofLedger(benchmark) {
|
|
4582
|
+
var ledger = benchmark && Array.isArray(benchmark.proof_ledger) ? benchmark.proof_ledger : [];
|
|
4583
|
+
ledger.forEach(function (entry) {
|
|
4584
|
+
var item = document.createElement("div");
|
|
4585
|
+
item.className = "proof-item proof-ledger-item " + (entry.pass ? "is-ok" : "is-warn");
|
|
4586
|
+
item.innerHTML = [
|
|
4587
|
+
"<strong></strong>",
|
|
4588
|
+
"<span></span>",
|
|
4589
|
+
"<code></code>",
|
|
4590
|
+
"<p></p>"
|
|
4591
|
+
].join("");
|
|
4592
|
+
item.querySelector("strong").textContent = entry.metric || (entry.pass ? "passing" : "check");
|
|
4593
|
+
item.querySelector("span").textContent = [entry.label, entry.target].filter(Boolean).join(" | ");
|
|
4594
|
+
item.querySelector("code").textContent = entry.command || "";
|
|
4595
|
+
item.querySelector("p").textContent = entry.next_action || "Review this proof before publishing benchmark claims.";
|
|
4596
|
+
els.proofList.appendChild(item);
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
|
|
3500
4600
|
function renderProofOverview(metrics, reports) {
|
|
3501
4601
|
els.proofOverview.textContent = "";
|
|
3502
4602
|
var quality = reports.quality || {};
|
|
3503
4603
|
var benchmark = reports.benchmark || {};
|
|
3504
4604
|
var gates = Array.isArray(benchmark.gates) ? benchmark.gates : [];
|
|
4605
|
+
var retrieval = benchmarkRetrievalSummary(benchmark);
|
|
4606
|
+
var sourceDiversity = benchmarkSourceDiversitySummary(benchmark);
|
|
4607
|
+
var scale = benchmarkScaleSummary(benchmark);
|
|
3505
4608
|
var passingGates = gates.filter(function (gate) { return gate.pass; }).length;
|
|
3506
4609
|
var gatePercent = gates.length ? Math.round(passingGates / gates.length * 100) : (benchmark.ok ? 100 : 0);
|
|
3507
4610
|
var evidence = Number(firstNumber(metrics.memory_graph && metrics.memory_graph.evidence_coverage_percent, quality.evidence_coverage_percent, 0));
|
|
3508
4611
|
var pathGrounding = Number(firstNumber(quality.path_grounding_coverage_percent, 0));
|
|
4612
|
+
if (retrieval) {
|
|
4613
|
+
els.proofOverview.appendChild(metricBars("Retrieval proof", retrieval.r10 + "% R@10", [
|
|
4614
|
+
{ label: "R@5", value: retrieval.r5 != null ? retrieval.r5 + "%" : "n/a", score: retrieval.r5 || 0, status: retrieval.r5 >= 95 ? "ok" : "warn" },
|
|
4615
|
+
{ label: "R@10", value: retrieval.r10 + "%", score: retrieval.r10, status: retrieval.r10 >= 95 ? "ok" : "warn" },
|
|
4616
|
+
{ label: "NDCG@10", value: retrieval.ndcg10 != null ? retrieval.ndcg10 : "n/a", score: retrieval.ndcg10 != null ? retrieval.ndcg10 * 100 : 0, status: retrieval.ndcg10 >= 0.85 ? "ok" : "warn" }
|
|
4617
|
+
], retrieval.label + ". Measures memory retrieval proof, not answer accuracy.", retrieval.r10 >= 95 ? "ok" : "warn"));
|
|
4618
|
+
}
|
|
4619
|
+
if (sourceDiversity) {
|
|
4620
|
+
els.proofOverview.appendChild(metricBars("Source diversity proof", sourceDiversity.uniqueSources + " sources", [
|
|
4621
|
+
{ label: "Sources", value: sourceDiversity.uniqueSources, score: Math.min(100, sourceDiversity.uniqueSources * 50), status: sourceDiversity.uniqueSources >= 2 ? "ok" : "warn" },
|
|
4622
|
+
{ label: "Max/session", value: sourceDiversity.maxFromOneSource, score: sourceDiversity.maxFromOneSource <= 3 ? 100 : 45, status: sourceDiversity.maxFromOneSource <= 3 ? "ok" : "warn" },
|
|
4623
|
+
{ label: "Independent rank", value: sourceDiversity.independentRank != null ? "#" + sourceDiversity.independentRank : "miss", score: sourceDiversity.independentRank != null && sourceDiversity.independentRank <= sourceDiversity.topK ? 100 : 35, status: sourceDiversity.pass ? "ok" : "warn" }
|
|
4624
|
+
], "Noisy observed sessions should not crowd out independent teammate memory.", sourceDiversity.pass ? "ok" : "warn"));
|
|
4625
|
+
}
|
|
4626
|
+
if (scale) {
|
|
4627
|
+
els.proofOverview.appendChild(metricBars("Scale proof", scale.packets + " packets", [
|
|
4628
|
+
{ label: "Hit rate", value: scale.hitRate + "%", score: scale.hitRate, status: scale.hitRate >= 95 ? "ok" : "warn" },
|
|
4629
|
+
{ label: "Median", value: scale.medianLatency + "ms", score: Math.max(0, 100 - scale.medianLatency), status: scale.medianLatency <= 50 ? "ok" : "warn" },
|
|
4630
|
+
{ label: "Context cut", value: scale.contextReduction + "%", score: scale.contextReduction, status: scale.contextReduction >= 80 ? "ok" : "warn" }
|
|
4631
|
+
], "Scale benchmark checks large repo-memory retrieval cost.", scale.hitRate >= 95 ? "ok" : "warn"));
|
|
4632
|
+
}
|
|
3509
4633
|
els.proofOverview.appendChild(metricDonut(
|
|
3510
4634
|
"Trust gate",
|
|
3511
4635
|
gatePercent,
|
|
@@ -3520,6 +4644,70 @@
|
|
|
3520
4644
|
], "Trust memory only when it is evidence-backed and path-grounded.", evidence >= 80 ? "ok" : "warn"));
|
|
3521
4645
|
}
|
|
3522
4646
|
|
|
4647
|
+
function benchmarkRetrievalSummary(report) {
|
|
4648
|
+
if (!report || !report.summary) return null;
|
|
4649
|
+
var summary = report.summary;
|
|
4650
|
+
var r10 = Number(summary.recall_at_10_percent);
|
|
4651
|
+
if (!Number.isFinite(r10)) return null;
|
|
4652
|
+
var mode = summary.retrieval_mode || report.retrieval_mode || "";
|
|
4653
|
+
var embeddings = summary.embeddings || report.embeddings || null;
|
|
4654
|
+
var modeLabel = mode === "kage-recall-with-dense-local-embeddings"
|
|
4655
|
+
? "dense local embeddings"
|
|
4656
|
+
: mode === "kage-recall-default"
|
|
4657
|
+
? "default recall"
|
|
4658
|
+
: mode;
|
|
4659
|
+
var modelLabel = embeddings && embeddings.model ? " · " + embeddings.model : "";
|
|
4660
|
+
return {
|
|
4661
|
+
label: [summary.benchmark || report.benchmark || "Retrieval benchmark", modeLabel].filter(Boolean).join(" · "),
|
|
4662
|
+
mode: modeLabel,
|
|
4663
|
+
model: modelLabel ? modelLabel.slice(3) : "",
|
|
4664
|
+
r5: numberOrNull(summary.recall_at_5_percent),
|
|
4665
|
+
r10: r10,
|
|
4666
|
+
r20: numberOrNull(summary.recall_at_20_percent),
|
|
4667
|
+
mrr: numberOrNull(summary.mrr),
|
|
4668
|
+
ndcg10: numberOrNull(summary.ndcg_at_10),
|
|
4669
|
+
contextReduction: numberOrNull(summary.context_reduction_percent)
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
function benchmarkSourceDiversitySummary(report) {
|
|
4674
|
+
var diversity = report && report.memory_quality && report.memory_quality.source_diversity
|
|
4675
|
+
? report.memory_quality.source_diversity
|
|
4676
|
+
: report && report.source_diversity;
|
|
4677
|
+
if (!diversity) return null;
|
|
4678
|
+
var uniqueSources = numberOrNull(diversity.unique_sources);
|
|
4679
|
+
var maxFromOneSource = numberOrNull(diversity.max_results_from_one_source);
|
|
4680
|
+
var topK = numberOrNull(diversity.top_k) || 4;
|
|
4681
|
+
if (uniqueSources == null || maxFromOneSource == null) return null;
|
|
4682
|
+
return {
|
|
4683
|
+
uniqueSources: uniqueSources,
|
|
4684
|
+
maxFromOneSource: maxFromOneSource,
|
|
4685
|
+
independentRank: numberOrNull(diversity.independent_source_rank),
|
|
4686
|
+
topK: topK,
|
|
4687
|
+
pass: Boolean(diversity.pass)
|
|
4688
|
+
};
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
function benchmarkScaleSummary(report) {
|
|
4692
|
+
var scale = report && report.memory_scale;
|
|
4693
|
+
var summary = scale && scale.summary;
|
|
4694
|
+
if (!summary) return null;
|
|
4695
|
+
var hitRate = numberOrNull(summary.largest_hit_rate_percent);
|
|
4696
|
+
var packets = numberOrNull(summary.largest_packets);
|
|
4697
|
+
if (hitRate == null || packets == null) return null;
|
|
4698
|
+
return {
|
|
4699
|
+
packets: packets,
|
|
4700
|
+
hitRate: hitRate,
|
|
4701
|
+
medianLatency: numberOrNull(summary.largest_median_recall_latency_ms) || 0,
|
|
4702
|
+
contextReduction: numberOrNull(summary.largest_context_reduction_percent) || 0
|
|
4703
|
+
};
|
|
4704
|
+
}
|
|
4705
|
+
|
|
4706
|
+
function numberOrNull(value) {
|
|
4707
|
+
var number = Number(value);
|
|
4708
|
+
return Number.isFinite(number) ? number : null;
|
|
4709
|
+
}
|
|
4710
|
+
|
|
3523
4711
|
function renderIntelligence() {
|
|
3524
4712
|
if (!els.intelligenceList) return;
|
|
3525
4713
|
var reports = state.reports || {};
|
|
@@ -3532,7 +4720,11 @@
|
|
|
3532
4720
|
return;
|
|
3533
4721
|
}
|
|
3534
4722
|
els.intelligenceList.className = "intelligence-list";
|
|
3535
|
-
normalizeIntelCards(cards)
|
|
4723
|
+
var normalizedCards = normalizeIntelCards(cards);
|
|
4724
|
+
var riskFirst = new Set(["Change Risk", "Module Health", "Decision Memory", "Graph Insights", "Memory Quality"]);
|
|
4725
|
+
var primaryCards = normalizedCards.filter(function (card) { return riskFirst.has(card.title); }).slice(0, 3);
|
|
4726
|
+
if (!primaryCards.length) primaryCards = normalizedCards.slice(0, 3);
|
|
4727
|
+
primaryCards.forEach(function (card) {
|
|
3536
4728
|
var item = document.createElement("article");
|
|
3537
4729
|
item.className = "intel-card";
|
|
3538
4730
|
item.innerHTML = [
|
|
@@ -3549,7 +4741,7 @@
|
|
|
3549
4741
|
item.querySelector(".intel-highlight").textContent = card.highlight || card.summary || "";
|
|
3550
4742
|
item.querySelector(".intel-action span").textContent = card.action || "Review this signal before changing related code.";
|
|
3551
4743
|
var list = item.querySelector("ul");
|
|
3552
|
-
card.rows.slice(0,
|
|
4744
|
+
card.rows.slice(0, 2).forEach(function (row) {
|
|
3553
4745
|
var li = document.createElement("li");
|
|
3554
4746
|
li.innerHTML = "<strong></strong> <span></span>";
|
|
3555
4747
|
li.querySelector("strong").textContent = row[0];
|
|
@@ -3558,14 +4750,18 @@
|
|
|
3558
4750
|
});
|
|
3559
4751
|
els.intelligenceList.appendChild(item);
|
|
3560
4752
|
});
|
|
3561
|
-
var sections = rankIntelligenceSections(buildIntelligenceSections(reports)).slice(0,
|
|
4753
|
+
var sections = rankIntelligenceSections(buildIntelligenceSections(reports)).slice(0, 2);
|
|
3562
4754
|
if (sections.length) {
|
|
4755
|
+
var details = document.createElement("details");
|
|
4756
|
+
details.className = "intel-deep-drawer";
|
|
4757
|
+
details.innerHTML = "<summary><span>More repo signals</span><em>cycles, owners, modules</em></summary>";
|
|
3563
4758
|
var deepGrid = document.createElement("div");
|
|
3564
4759
|
deepGrid.className = "intel-deep-grid";
|
|
3565
4760
|
sections.forEach(function (section) {
|
|
3566
4761
|
deepGrid.appendChild(renderIntelligenceSection(section));
|
|
3567
4762
|
});
|
|
3568
|
-
|
|
4763
|
+
details.appendChild(deepGrid);
|
|
4764
|
+
els.intelligenceList.appendChild(details);
|
|
3569
4765
|
}
|
|
3570
4766
|
}
|
|
3571
4767
|
|
|
@@ -4020,13 +5216,26 @@
|
|
|
4020
5216
|
var benchmark = reports.benchmark;
|
|
4021
5217
|
if (benchmark) {
|
|
4022
5218
|
var checks = Array.isArray(benchmark.checks) ? benchmark.checks : [];
|
|
5219
|
+
var retrieval = benchmarkRetrievalSummary(benchmark);
|
|
5220
|
+
var scale = benchmarkScaleSummary(benchmark);
|
|
4023
5221
|
cards.push({
|
|
4024
|
-
title: "Benchmark",
|
|
4025
|
-
kicker: "local proof",
|
|
4026
|
-
summary:
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
5222
|
+
title: retrieval ? "Retrieval Proof" : "Benchmark",
|
|
5223
|
+
kicker: retrieval ? (retrieval.mode || "retrieval proof") : "local proof",
|
|
5224
|
+
summary: retrieval
|
|
5225
|
+
? retrieval.label + (scale ? " plus " + scale.packets + "-packet scale proof." : "") + " Evidence retrieval only, not answer accuracy."
|
|
5226
|
+
: benchmark.summary || "Local memory and graph benchmark signals.",
|
|
5227
|
+
rows: retrieval
|
|
5228
|
+
? [
|
|
5229
|
+
["R@5", retrieval.r5 != null ? retrieval.r5 + "%" : "n/a"],
|
|
5230
|
+
["R@10", retrieval.r10 + "%"],
|
|
5231
|
+
["MRR", retrieval.mrr != null ? String(retrieval.mrr) : "n/a"],
|
|
5232
|
+
["Context cut", retrieval.contextReduction != null ? retrieval.contextReduction + "%" : "n/a"],
|
|
5233
|
+
["Scale", scale ? scale.hitRate + "% hit / " + scale.medianLatency + "ms" : "not loaded"],
|
|
5234
|
+
["Model", retrieval.model || "dependency-free default"]
|
|
5235
|
+
]
|
|
5236
|
+
: checks.slice(0, 5).map(function (item) {
|
|
5237
|
+
return [item.name || "check", (item.pass ? "pass" : "check") + " - " + item.actual + "/" + item.target];
|
|
5238
|
+
})
|
|
4030
5239
|
});
|
|
4031
5240
|
}
|
|
4032
5241
|
return cards;
|