@kage-core/kage-graph-mcp 1.1.8 → 1.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "Local-first repo memory, code graph, and recall MCP server for coding agents",
5
5
  "main": "dist/index.js",
6
6
  "files": [
package/viewer/app.js CHANGED
@@ -7,6 +7,8 @@
7
7
  edges: [],
8
8
  episodesById: new Map(),
9
9
  entityById: new Map(),
10
+ edgeById: new Map(),
11
+ degreeById: new Map(),
10
12
  positions: new Map(),
11
13
  visibleEntityIds: new Set(),
12
14
  visibleEdgeIds: new Set(),
@@ -27,8 +29,12 @@
27
29
  panning: null,
28
30
  hoverNode: null,
29
31
  raf: null,
32
+ drawRaf: null,
30
33
  running: false,
31
- lastSignature: ""
34
+ lastSignature: "",
35
+ tick: 0,
36
+ idleFrames: 0,
37
+ adjacency: new Map()
32
38
  },
33
39
  pan: null
34
40
  };
@@ -104,7 +110,7 @@
104
110
  };
105
111
 
106
112
  els.graphFile.addEventListener("change", handleFile);
107
- els.searchInput.addEventListener("input", render);
113
+ els.searchInput.addEventListener("input", scheduleRender);
108
114
  els.viewMode.addEventListener("change", render);
109
115
  els.typeFilter.addEventListener("change", render);
110
116
  els.relationFilter.addEventListener("change", render);
@@ -183,6 +189,10 @@
183
189
  state.entityById = new Map(entities.map(function (entity) {
184
190
  return [entity.id, entity];
185
191
  }));
192
+ state.edgeById = new Map(edges.map(function (edge) {
193
+ return [edge.id, edge];
194
+ }));
195
+ state.degreeById = buildDegreeMap(edges);
186
196
  state.episodesById = new Map(episodes.map(function (episode) {
187
197
  return [episode.id, episode];
188
198
  }));
@@ -195,6 +205,15 @@
195
205
  render();
196
206
  }
197
207
 
208
+ function buildDegreeMap(edges) {
209
+ var degrees = new Map();
210
+ edges.forEach(function (edge) {
211
+ degrees.set(edge.from, (degrees.get(edge.from) || 0) + 1);
212
+ degrees.set(edge.to, (degrees.get(edge.to) || 0) + 1);
213
+ });
214
+ return degrees;
215
+ }
216
+
198
217
  function loadFromUrlParams() {
199
218
  var params = new URLSearchParams(window.location.search);
200
219
  var graphPaths = []
@@ -214,7 +233,7 @@
214
233
  if (reviewPath) jobs.push(fetchText(reviewPath).then(function (text) { state.reviewText = text; }).catch(function () { state.reviewText = ""; }));
215
234
  if (pendingPath) jobs.push(loadPending(pendingPath).then(function (packets) { state.pendingPackets = packets; }));
216
235
  if (!graphPaths.length && !jobs.length) {
217
- setAutoLoad("manual mode", false);
236
+ loadHostedDemo();
218
237
  return;
219
238
  }
220
239
  setAutoLoad("loading project graph", false);
@@ -239,6 +258,21 @@
239
258
  });
240
259
  }
241
260
 
261
+ function loadHostedDemo() {
262
+ setAutoLoad("loading hosted demo graph", false);
263
+ Promise.all([
264
+ fetchJson("./demo/graph.json"),
265
+ fetchJson("./demo/metrics.json").catch(function () { return null; })
266
+ ]).then(function (items) {
267
+ var graph = items[0];
268
+ state.metrics = items[1];
269
+ loadNormalizedGraph(normalizeGraph(graph), "hosted demo graph");
270
+ setAutoLoad("hosted demo graph loaded", true);
271
+ }).catch(function () {
272
+ setAutoLoad("manual mode", false);
273
+ });
274
+ }
275
+
242
276
  function inferMemoryRoot(path) {
243
277
  var marker = "/.agent_memory/";
244
278
  var index = path.indexOf(marker);
@@ -471,6 +505,7 @@
471
505
  if (!state.graph) return;
472
506
 
473
507
  var query = parseSearchQuery(els.searchInput.value);
508
+ state.renderQuery = query;
474
509
  var mode = els.viewMode.value;
475
510
  var type = els.typeFilter.value;
476
511
  var relation = els.relationFilter.value;
@@ -509,6 +544,7 @@
509
544
  type: type,
510
545
  relation: relation,
511
546
  scope: els.scopeFilter.value,
547
+ mode: mode,
512
548
  maxNodes: Number(els.maxNodes.value || 90),
513
549
  showDependencies: els.showDependencies.checked
514
550
  });
@@ -528,6 +564,14 @@
528
564
  renderProof();
529
565
  }
530
566
 
567
+ function scheduleRender() {
568
+ if (state.renderRaf) return;
569
+ state.renderRaf = window.requestAnimationFrame(function () {
570
+ state.renderRaf = null;
571
+ render();
572
+ });
573
+ }
574
+
531
575
  function refineVisibleGraph(entityIds, edgeIds, options) {
532
576
  var entities = new Set(entityIds);
533
577
  var edges = new Set(edgeIds);
@@ -552,7 +596,9 @@
552
596
  return entityImportance(state.entityById.get(b)) - entityImportance(state.entityById.get(a)) ||
553
597
  displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
554
598
  });
555
- entities = new Set(ranked.slice(0, options.maxNodes));
599
+ entities = options.mode === "combined"
600
+ ? balancedSignalEntities(ranked, options.maxNodes)
601
+ : new Set(ranked.slice(0, options.maxNodes));
556
602
  if (state.selected && state.selected.kind === "entity") entities.add(state.selected.id);
557
603
  edges = edgesWithVisibleEndpoints(edges, entities);
558
604
  }
@@ -568,6 +614,33 @@
568
614
  return { entities: entities, edges: edgesWithVisibleEndpoints(edges, entities) };
569
615
  }
570
616
 
617
+ function balancedSignalEntities(rankedIds, maxNodes) {
618
+ var result = new Set();
619
+ var memoryIds = rankedIds.filter(function (id) {
620
+ var entity = state.entityById.get(id);
621
+ return entity && entity.graph_kind === "memory" && ["memory", "repo", "memory_type", "command"].indexOf(entity.type) !== -1;
622
+ });
623
+ var codeIds = rankedIds.filter(function (id) {
624
+ var entity = state.entityById.get(id);
625
+ return entity && entity.graph_kind === "code";
626
+ });
627
+ var otherMemoryIds = rankedIds.filter(function (id) {
628
+ var entity = state.entityById.get(id);
629
+ return entity && entity.graph_kind === "memory" && !memoryIds.includes(id);
630
+ });
631
+
632
+ var memoryBudget = clamp(Math.round(maxNodes * 0.34), 18, Math.min(44, maxNodes));
633
+ memoryIds.slice(0, memoryBudget).forEach(function (id) { result.add(id); });
634
+ otherMemoryIds.slice(0, Math.max(0, Math.round(memoryBudget * 0.35))).forEach(function (id) { result.add(id); });
635
+ codeIds.forEach(function (id) {
636
+ if (result.size < maxNodes) result.add(id);
637
+ });
638
+ rankedIds.forEach(function (id) {
639
+ if (result.size < maxNodes) result.add(id);
640
+ });
641
+ return result;
642
+ }
643
+
571
644
  function focusSelection(entityIds, edgeIds, selection) {
572
645
  var entities = new Set();
573
646
  var edges = new Set();
@@ -599,7 +672,7 @@
599
672
 
600
673
  function edgesWithVisibleEndpoints(edgeIds, entityIds) {
601
674
  return new Set(Array.from(edgeIds).filter(function (id) {
602
- var edge = state.edges.find(function (candidate) { return candidate.id === id; });
675
+ var edge = state.edgeById.get(id);
603
676
  return edge && entityIds.has(edge.from) && entityIds.has(edge.to);
604
677
  }));
605
678
  }
@@ -656,6 +729,22 @@
656
729
  state.sim.edges = state.edges.filter(function (edge) {
657
730
  return state.visibleEdgeIds.has(edge.id) && state.sim.nodeById.has(edge.from) && state.sim.nodeById.has(edge.to);
658
731
  });
732
+ state.sim.adjacency = buildAdjacency(state.sim.edges);
733
+ if (forceReset) {
734
+ state.sim.tick = 0;
735
+ state.sim.idleFrames = 0;
736
+ }
737
+ }
738
+
739
+ function buildAdjacency(edges) {
740
+ var adjacency = new Map();
741
+ edges.forEach(function (edge) {
742
+ if (!adjacency.has(edge.from)) adjacency.set(edge.from, new Set());
743
+ if (!adjacency.has(edge.to)) adjacency.set(edge.to, new Set());
744
+ adjacency.get(edge.from).add(edge.to);
745
+ adjacency.get(edge.to).add(edge.from);
746
+ });
747
+ return adjacency;
659
748
  }
660
749
 
661
750
  function seededPosition(index, total) {
@@ -673,16 +762,28 @@
673
762
  state.sim.raf = window.requestAnimationFrame(simulationTick);
674
763
  }
675
764
 
765
+ function stopSimulation() {
766
+ state.sim.running = false;
767
+ if (state.sim.raf) window.cancelAnimationFrame(state.sim.raf);
768
+ state.sim.raf = null;
769
+ }
770
+
676
771
  function simulationTick() {
677
772
  if (!state.sim.running) return;
678
- stepSimulation();
773
+ var maxVelocity = stepSimulation();
679
774
  drawCanvasGraph();
775
+ state.sim.tick += 1;
776
+ state.sim.idleFrames = maxVelocity < 0.035 ? state.sim.idleFrames + 1 : 0;
777
+ if (!state.sim.dragNode && (state.sim.idleFrames > 24 || state.sim.tick > 220)) {
778
+ stopSimulation();
779
+ return;
780
+ }
680
781
  state.sim.raf = window.requestAnimationFrame(simulationTick);
681
782
  }
682
783
 
683
784
  function stepSimulation() {
684
785
  var nodes = state.sim.nodes;
685
- if (!nodes.length) return;
786
+ if (!nodes.length) return 0;
686
787
  var nodeMap = state.sim.nodeById;
687
788
  var count = nodes.length;
688
789
  var repulsion = count > 120 ? 3600 : count > 70 ? 2500 : 1700;
@@ -736,11 +837,16 @@
736
837
  }
737
838
  });
738
839
 
840
+ var maxVelocity = 0;
739
841
  nodes.forEach(function (node) {
740
842
  if (state.sim.dragNode === node) return;
741
- node.x += clamp(node.vx, -8, 8);
742
- node.y += clamp(node.vy, -8, 8);
843
+ var vx = clamp(node.vx, -8, 8);
844
+ var vy = clamp(node.vy, -8, 8);
845
+ node.x += vx;
846
+ node.y += vy;
847
+ maxVelocity = Math.max(maxVelocity, Math.abs(vx), Math.abs(vy));
743
848
  });
849
+ return maxVelocity;
744
850
  }
745
851
 
746
852
  function drawCanvasGraph() {
@@ -803,7 +909,7 @@
803
909
  function drawCanvasEdges(ctx) {
804
910
  var nodeMap = state.sim.nodeById;
805
911
  var focusId = focusedCanvasNodeId();
806
- var query = parseSearchQuery(els.searchInput.value);
912
+ var query = state.renderQuery || parseSearchQuery(els.searchInput.value);
807
913
  var dense = state.sim.nodes.length > 55;
808
914
  state.sim.edges.forEach(function (edge) {
809
915
  var from = nodeMap.get(edge.from);
@@ -832,15 +938,14 @@
832
938
 
833
939
  function drawCanvasNodes(ctx) {
834
940
  var focusId = focusedCanvasNodeId();
835
- var query = parseSearchQuery(els.searchInput.value);
941
+ var query = state.renderQuery || parseSearchQuery(els.searchInput.value);
836
942
  var dense = state.sim.nodes.length > 55;
943
+ var focusNeighbors = focusId ? state.sim.adjacency.get(focusId) : null;
837
944
  state.sim.nodes.forEach(function (node) {
838
945
  var entity = node.entity;
839
946
  var selected = state.selected && state.selected.kind === "entity" && state.selected.id === node.id;
840
947
  var hovered = state.sim.hoverNode && state.sim.hoverNode.id === node.id;
841
- var connected = focusId && (node.id === focusId || state.sim.edges.some(function (edge) {
842
- return (edge.from === focusId && edge.to === node.id) || (edge.to === focusId && edge.from === node.id);
843
- }));
948
+ var connected = focusId && (node.id === focusId || (focusNeighbors && focusNeighbors.has(node.id)));
844
949
  var matches = matchesSearchQuery(entity, query);
845
950
  var alpha = !matches ? 0.12 : focusId && !connected ? 0.20 : 1;
846
951
  var color = nodeThemeColor(entity);
@@ -1376,9 +1481,7 @@
1376
1481
  }
1377
1482
 
1378
1483
  function degreeOf(id) {
1379
- return state.edges.reduce(function (sum, edge) {
1380
- return sum + (edge.from === id || edge.to === id ? 1 : 0);
1381
- }, 0);
1484
+ return state.degreeById.get(id) || 0;
1382
1485
  }
1383
1486
 
1384
1487
  function entityImportance(entity) {
@@ -1442,6 +1545,9 @@
1442
1545
  state.sim.dragNode.y = world.y;
1443
1546
  state.sim.dragNode.vx = 0;
1444
1547
  state.sim.dragNode.vy = 0;
1548
+ state.sim.tick = 0;
1549
+ state.sim.idleFrames = 0;
1550
+ startSimulation();
1445
1551
  } else if (state.sim.panning) {
1446
1552
  var dx = event.clientX - state.sim.panning.x;
1447
1553
  var dy = event.clientY - state.sim.panning.y;
@@ -1451,7 +1557,7 @@
1451
1557
  }
1452
1558
  state.sim.hoverNode = findCanvasNode(world.x, world.y);
1453
1559
  updateCanvasTooltip(event);
1454
- drawCanvasGraph();
1560
+ scheduleCanvasDraw();
1455
1561
  }
1456
1562
 
1457
1563
  function endCanvasPointer() {
@@ -1471,7 +1577,7 @@
1471
1577
  state.sim.dragNode = null;
1472
1578
  state.sim.panning = null;
1473
1579
  if (els.tooltip) els.tooltip.classList.remove("visible");
1474
- drawCanvasGraph();
1580
+ scheduleCanvasDraw();
1475
1581
  }
1476
1582
 
1477
1583
  function handleCanvasWheel(event) {
@@ -1482,7 +1588,15 @@
1482
1588
  state.sim.zoom = clamp(state.sim.zoom * factor, 0.14, 4.5);
1483
1589
  state.sim.panX = event.clientX - rect.left - before.x * state.sim.zoom;
1484
1590
  state.sim.panY = event.clientY - rect.top - before.y * state.sim.zoom;
1485
- drawCanvasGraph();
1591
+ scheduleCanvasDraw();
1592
+ }
1593
+
1594
+ function scheduleCanvasDraw() {
1595
+ if (state.sim.drawRaf) return;
1596
+ state.sim.drawRaf = window.requestAnimationFrame(function () {
1597
+ state.sim.drawRaf = null;
1598
+ drawCanvasGraph();
1599
+ });
1486
1600
  }
1487
1601
 
1488
1602
  function handleCanvasDoubleClick(event) {
@@ -0,0 +1,267 @@
1
+ {
2
+ "entities": [
3
+ {
4
+ "id": "repo:kage",
5
+ "type": "repo",
6
+ "name": "Kage",
7
+ "summary": "Repo memory and source graph for coding agents.",
8
+ "aliases": ["kage-core/Kage"],
9
+ "evidence": ["episode:demo-launch"]
10
+ },
11
+ {
12
+ "id": "memory:viewer-balance",
13
+ "type": "memory",
14
+ "name": "Viewer balances memory and code",
15
+ "summary": "Combined mode reserves visible space for memory packets before filling the rest with code graph nodes.",
16
+ "aliases": ["viewer", "memory graph", "code graph"],
17
+ "evidence": ["episode:demo-viewer"]
18
+ },
19
+ {
20
+ "id": "memory:kage-context",
21
+ "type": "memory",
22
+ "name": "kage_context starts agent sessions",
23
+ "summary": "Agents should call kage_context at session start to receive repo memory, code graph, metrics, and policy hints.",
24
+ "aliases": ["codex", "claude code", "mcp"],
25
+ "evidence": ["episode:demo-context"]
26
+ },
27
+ {
28
+ "id": "memory:repo-local",
29
+ "type": "memory",
30
+ "name": "Repo-local memory is git-native",
31
+ "summary": "Approved repo memory is written as JSON packets under .agent_memory/packets and travels with the repo.",
32
+ "aliases": ["git", "packets"],
33
+ "evidence": ["episode:demo-repo-memory"]
34
+ },
35
+ {
36
+ "id": "memory:review-boundary",
37
+ "type": "memory",
38
+ "name": "Org/global promotion is reviewed",
39
+ "summary": "Agents can write repo-local memory, but org/global/public promotion requires explicit human review.",
40
+ "aliases": ["trust", "review"],
41
+ "evidence": ["episode:demo-trust"]
42
+ },
43
+ {
44
+ "id": "type:decision",
45
+ "type": "memory_type",
46
+ "name": "decision",
47
+ "summary": "Durable architectural or product choice.",
48
+ "aliases": [],
49
+ "evidence": ["episode:demo-viewer"]
50
+ },
51
+ {
52
+ "id": "type:runbook",
53
+ "type": "memory_type",
54
+ "name": "runbook",
55
+ "summary": "How to run, test, debug, or operate the repo.",
56
+ "aliases": [],
57
+ "evidence": ["episode:demo-context"]
58
+ },
59
+ {
60
+ "id": "command:kage-viewer",
61
+ "type": "command",
62
+ "name": "kage viewer --project .",
63
+ "summary": "Launches the local auto-loaded Memory Terminal for a repo.",
64
+ "aliases": ["viewer"],
65
+ "evidence": ["episode:demo-viewer"]
66
+ },
67
+ {
68
+ "id": "command:kage-refresh",
69
+ "type": "command",
70
+ "name": "kage refresh --project .",
71
+ "summary": "Rebuilds indexes, code graph, memory graph, metrics, and stale memory metadata.",
72
+ "aliases": ["refresh"],
73
+ "evidence": ["episode:demo-context"]
74
+ },
75
+ {
76
+ "id": "path:mcp-viewer-app",
77
+ "type": "path",
78
+ "name": "mcp/viewer/app.js",
79
+ "summary": "Static Memory Terminal canvas graph and inspector.",
80
+ "aliases": ["viewer app"],
81
+ "evidence": ["episode:demo-viewer"]
82
+ },
83
+ {
84
+ "id": "path:mcp-kernel",
85
+ "type": "path",
86
+ "name": "mcp/kernel.ts",
87
+ "summary": "Memory kernel, validation, indexing, graph, recall, setup, metrics, and PR checks.",
88
+ "aliases": ["kernel"],
89
+ "evidence": ["episode:demo-context"]
90
+ },
91
+ {
92
+ "id": "tag:viewer",
93
+ "type": "tag",
94
+ "name": "viewer",
95
+ "summary": "Graph and memory terminal experience.",
96
+ "aliases": [],
97
+ "evidence": ["episode:demo-viewer"]
98
+ },
99
+ {
100
+ "id": "tag:repo-memory",
101
+ "type": "tag",
102
+ "name": "repo-memory",
103
+ "summary": "Git-native memory packets shared across agents.",
104
+ "aliases": [],
105
+ "evidence": ["episode:demo-repo-memory"]
106
+ },
107
+ {
108
+ "id": "tag:mcp",
109
+ "type": "tag",
110
+ "name": "mcp",
111
+ "summary": "Model Context Protocol integration for coding agents.",
112
+ "aliases": [],
113
+ "evidence": ["episode:demo-context"]
114
+ }
115
+ ],
116
+ "edges": [
117
+ {
118
+ "id": "edge:repo:viewer",
119
+ "from": "repo:kage",
120
+ "to": "memory:viewer-balance",
121
+ "relation": "contains_memory",
122
+ "fact": "Kage contains a viewer memory about balancing memory and code graph nodes.",
123
+ "confidence": 0.95,
124
+ "evidence": ["episode:demo-viewer"]
125
+ },
126
+ {
127
+ "id": "edge:repo:context",
128
+ "from": "repo:kage",
129
+ "to": "memory:kage-context",
130
+ "relation": "contains_memory",
131
+ "fact": "Kage contains a session-start memory about kage_context.",
132
+ "confidence": 0.95,
133
+ "evidence": ["episode:demo-context"]
134
+ },
135
+ {
136
+ "id": "edge:repo:local",
137
+ "from": "repo:kage",
138
+ "to": "memory:repo-local",
139
+ "relation": "contains_memory",
140
+ "fact": "Kage contains a memory about git-native repo-local packets.",
141
+ "confidence": 0.95,
142
+ "evidence": ["episode:demo-repo-memory"]
143
+ },
144
+ {
145
+ "id": "edge:repo:review",
146
+ "from": "repo:kage",
147
+ "to": "memory:review-boundary",
148
+ "relation": "contains_memory",
149
+ "fact": "Kage contains a trust-boundary memory for org/global promotion.",
150
+ "confidence": 0.95,
151
+ "evidence": ["episode:demo-trust"]
152
+ },
153
+ {
154
+ "id": "edge:viewer:type",
155
+ "from": "memory:viewer-balance",
156
+ "to": "type:decision",
157
+ "relation": "has_type",
158
+ "fact": "Viewer balance is a product decision.",
159
+ "confidence": 0.9,
160
+ "evidence": ["episode:demo-viewer"]
161
+ },
162
+ {
163
+ "id": "edge:context:type",
164
+ "from": "memory:kage-context",
165
+ "to": "type:runbook",
166
+ "relation": "has_type",
167
+ "fact": "kage_context is operational runbook memory.",
168
+ "confidence": 0.9,
169
+ "evidence": ["episode:demo-context"]
170
+ },
171
+ {
172
+ "id": "edge:viewer:path",
173
+ "from": "memory:viewer-balance",
174
+ "to": "path:mcp-viewer-app",
175
+ "relation": "affects_path",
176
+ "fact": "Viewer balance applies to the static viewer app.",
177
+ "confidence": 0.92,
178
+ "evidence": ["episode:demo-viewer"]
179
+ },
180
+ {
181
+ "id": "edge:context:path",
182
+ "from": "memory:kage-context",
183
+ "to": "path:mcp-kernel",
184
+ "relation": "affects_path",
185
+ "fact": "kage_context is implemented through the memory kernel and MCP server.",
186
+ "confidence": 0.88,
187
+ "evidence": ["episode:demo-context"]
188
+ },
189
+ {
190
+ "id": "edge:viewer:command",
191
+ "from": "memory:viewer-balance",
192
+ "to": "command:kage-viewer",
193
+ "relation": "documents_command",
194
+ "fact": "The viewer memory documents the kage viewer command.",
195
+ "confidence": 0.9,
196
+ "evidence": ["episode:demo-viewer"]
197
+ },
198
+ {
199
+ "id": "edge:context:refresh",
200
+ "from": "memory:kage-context",
201
+ "to": "command:kage-refresh",
202
+ "relation": "documents_command",
203
+ "fact": "Agent context should be refreshed after meaningful changes.",
204
+ "confidence": 0.86,
205
+ "evidence": ["episode:demo-context"]
206
+ },
207
+ {
208
+ "id": "edge:viewer:tag",
209
+ "from": "memory:viewer-balance",
210
+ "to": "tag:viewer",
211
+ "relation": "mentions_tag",
212
+ "fact": "Viewer balance is tagged viewer.",
213
+ "confidence": 0.9,
214
+ "evidence": ["episode:demo-viewer"]
215
+ },
216
+ {
217
+ "id": "edge:local:tag",
218
+ "from": "memory:repo-local",
219
+ "to": "tag:repo-memory",
220
+ "relation": "mentions_tag",
221
+ "fact": "Repo-local memory is tagged repo-memory.",
222
+ "confidence": 0.9,
223
+ "evidence": ["episode:demo-repo-memory"]
224
+ },
225
+ {
226
+ "id": "edge:context:tag",
227
+ "from": "memory:kage-context",
228
+ "to": "tag:mcp",
229
+ "relation": "mentions_tag",
230
+ "fact": "kage_context is exposed through MCP.",
231
+ "confidence": 0.9,
232
+ "evidence": ["episode:demo-context"]
233
+ }
234
+ ],
235
+ "episodes": [
236
+ {
237
+ "id": "episode:demo-launch",
238
+ "summary": "Kage demo graph for the hosted Memory Terminal.",
239
+ "source": "hosted_demo",
240
+ "created_at": "2026-05-03T00:00:00.000Z"
241
+ },
242
+ {
243
+ "id": "episode:demo-viewer",
244
+ "summary": "The viewer should show memory and code graph together, not an empty shell.",
245
+ "source": "hosted_demo",
246
+ "created_at": "2026-05-03T00:00:00.000Z"
247
+ },
248
+ {
249
+ "id": "episode:demo-context",
250
+ "summary": "Agents use kage_context and refresh to avoid rediscovery.",
251
+ "source": "hosted_demo",
252
+ "created_at": "2026-05-03T00:00:00.000Z"
253
+ },
254
+ {
255
+ "id": "episode:demo-repo-memory",
256
+ "summary": "Repo-local packets are git-native durable memory.",
257
+ "source": "hosted_demo",
258
+ "created_at": "2026-05-03T00:00:00.000Z"
259
+ },
260
+ {
261
+ "id": "episode:demo-trust",
262
+ "summary": "Org/global sharing is review-gated.",
263
+ "source": "hosted_demo",
264
+ "created_at": "2026-05-03T00:00:00.000Z"
265
+ }
266
+ ]
267
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "schema_version": 1,
3
+ "project_dir": "hosted-demo",
4
+ "repo_key": "kage-core-kage",
5
+ "generated_at": "2026-05-03T00:00:00.000Z",
6
+ "code_graph": {
7
+ "files": 12,
8
+ "symbols": 1894,
9
+ "imports": 37,
10
+ "calls": 2317,
11
+ "routes": 3,
12
+ "tests": 64,
13
+ "packages_and_scripts": 9,
14
+ "languages": {
15
+ "typescript": 6,
16
+ "javascript": 3,
17
+ "markdown": 3
18
+ },
19
+ "parsers": {
20
+ "typescript": 6,
21
+ "javascript": 3,
22
+ "metadata": 3
23
+ },
24
+ "source_symbols_by_parser": {
25
+ "typescript": 1620,
26
+ "javascript": 274
27
+ },
28
+ "indexer_coverage_percent": 100
29
+ },
30
+ "memory_graph": {
31
+ "approved_packets": 22,
32
+ "pending_packets": 0,
33
+ "episodes": 5,
34
+ "entities": 14,
35
+ "edges": 13,
36
+ "evidence_backed_edges": 13,
37
+ "evidence_coverage_percent": 100,
38
+ "average_quality_score": 97,
39
+ "duplicate_candidate_pairs": 0
40
+ },
41
+ "savings": {
42
+ "estimated_indexed_source_tokens": 113882,
43
+ "estimated_memory_tokens": 1800,
44
+ "estimated_recall_context_tokens": 1800,
45
+ "estimated_tokens_saved_per_recall": 112082
46
+ },
47
+ "harness": {
48
+ "policy_installed": true,
49
+ "validation_ok": true,
50
+ "warnings": 0,
51
+ "errors": 0,
52
+ "readiness_score": 100
53
+ },
54
+ "pain": {
55
+ "setup_runbook_coverage_percent": 100,
56
+ "bug_fix_coverage_percent": 80,
57
+ "decision_coverage_percent": 100,
58
+ "code_flow_coverage_percent": 100,
59
+ "recall_hit_rate_percent": 100,
60
+ "estimated_rediscovery_avoided": 9,
61
+ "estimated_tokens_saved": 112082,
62
+ "time_to_first_use_seconds": 60
63
+ },
64
+ "quality": {
65
+ "totals": {
66
+ "approved": 22,
67
+ "pending": 0,
68
+ "high_signal": 21,
69
+ "needs_review": 1,
70
+ "duplicate": 0,
71
+ "stale": 0,
72
+ "too_generic": 0
73
+ },
74
+ "memory_type_coverage": {
75
+ "decision": 10,
76
+ "runbook": 5,
77
+ "gotcha": 3,
78
+ "reference": 4
79
+ },
80
+ "useful_memory_ratio_percent": 97,
81
+ "duplicate_burden": 0,
82
+ "stale_wrong_feedback_rate_percent": 0,
83
+ "evidence_coverage_percent": 100,
84
+ "path_grounding_coverage_percent": 100,
85
+ "approved_to_pending_ratio": 22
86
+ }
87
+ }