pi-mega-compact 0.13.0 → 0.13.2

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.
Files changed (212) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/embedder-health.js +11 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +225 -0
  3. package/dist/extensions/dashboard-server/api-contracts/endpoints/types.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +5 -224
  5. package/dist/extensions/dashboard-server/api-contracts/raptor.js +8 -0
  6. package/dist/extensions/dashboard-server/api-contracts.test/_helpers.js +32 -0
  7. package/dist/extensions/dashboard-server/routes-cache.test/_helpers.js +48 -0
  8. package/dist/extensions/dashboard-server/routes-embedder-health.js +67 -0
  9. package/dist/extensions/dashboard-server/routes-raptor.js +88 -0
  10. package/dist/extensions/dashboard-server/routes-setup.js +1 -1
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +5 -1
  13. package/dist/extensions/mega-cache-replay.test/_helpers.js +175 -0
  14. package/dist/extensions/mega-commands/dataCommands.js +151 -0
  15. package/dist/extensions/mega-commands/helpers.js +61 -0
  16. package/dist/extensions/mega-commands/historyCommands.js +97 -0
  17. package/dist/extensions/mega-commands/setupCommand.js +200 -0
  18. package/dist/extensions/mega-commands.js +14 -499
  19. package/dist/extensions/mega-compact-s38.test/_helpers.js +194 -0
  20. package/dist/extensions/mega-compact.js +12 -2
  21. package/dist/extensions/mega-compact.test/_helpers.js +225 -0
  22. package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +211 -0
  23. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +49 -0
  24. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.js +33 -0
  25. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +387 -0
  26. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/event.js +7 -0
  27. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.js +55 -0
  28. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.js +12 -0
  29. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.js +22 -0
  30. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +38 -0
  31. package/dist/extensions/mega-events/agent-handlers/turnEndHandler.js +36 -0
  32. package/dist/extensions/mega-events/agent-handlers.js +4 -799
  33. package/dist/extensions/mega-events/context-handler/afterCompact.js +163 -0
  34. package/dist/extensions/mega-events/context-handler/messageText.js +12 -0
  35. package/dist/extensions/mega-events/context-handler/tailResult.js +46 -0
  36. package/dist/extensions/mega-events/context-handler.js +11 -198
  37. package/dist/extensions/mega-events/separated-prompt.test/_helpers.js +25 -0
  38. package/dist/src/config.js +2 -0
  39. package/dist/src/dedup-engine.test/_helpers.js +55 -0
  40. package/dist/src/e2e.test/_helpers.js +52 -0
  41. package/dist/src/embedder.js +1 -0
  42. package/dist/src/httpEmbedder.js +6 -0
  43. package/dist/src/hyde.js +146 -0
  44. package/dist/src/importance.test/_helpers.js +5 -0
  45. package/dist/src/memoryGraph/sources/checkpoints.js +78 -0
  46. package/dist/src/memoryGraph/sources/edges.js +17 -0
  47. package/dist/src/memoryGraph/sources/flags.js +22 -0
  48. package/dist/src/memoryGraph/sources/helpers.js +14 -0
  49. package/dist/src/memoryGraph/sources/memories.js +88 -0
  50. package/dist/src/memoryGraph/sources/raptor.js +26 -0
  51. package/dist/src/memoryGraph/sources/turns.js +176 -0
  52. package/dist/src/memoryGraph/sources.js +15 -441
  53. package/dist/src/memoryGraph-gates.test/_helpers.js +120 -0
  54. package/dist/src/ratio.bench.test/_helpers.js +198 -0
  55. package/dist/src/recall/async.js +112 -0
  56. package/dist/src/recall/format.js +149 -0
  57. package/dist/src/recall/memory.js +62 -0
  58. package/dist/src/recall/reformulate.js +82 -0
  59. package/dist/src/recall/sync.js +169 -0
  60. package/dist/src/recall/types.js +1 -0
  61. package/dist/src/recall.js +13 -530
  62. package/dist/src/store/sqlite/schema/core.js +232 -0
  63. package/dist/src/store/sqlite/schema/game.js +65 -0
  64. package/dist/src/store/sqlite/schema/plan-v2.js +107 -0
  65. package/dist/src/store/sqlite/schema/turns.js +59 -0
  66. package/dist/src/store/sqlite/schema.js +8 -441
  67. package/dist/src/store/turns/contract-compliance.test/_helpers.js +21 -0
  68. package/dist/src/store/turns/sqlite-store/admin.js +171 -0
  69. package/dist/src/store/turns/sqlite-store/ctx.js +1 -0
  70. package/dist/src/store/turns/sqlite-store/read.js +111 -0
  71. package/dist/src/store/turns/sqlite-store/rows.js +42 -0
  72. package/dist/src/store/turns/sqlite-store/write.js +102 -0
  73. package/dist/src/store/turns/sqlite-store.js +31 -367
  74. package/dist/src/vectorStore/class.js +356 -0
  75. package/dist/src/vectorStore/hash.js +10 -0
  76. package/dist/src/vectorStore/types.js +2 -0
  77. package/dist/src/vectorStore.js +8 -359
  78. package/dist/src/vectorStore.test/_helpers.js +17 -0
  79. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js → CacheTab-DPRHjYZ0.js} +2 -2
  80. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js.map → CacheTab-DPRHjYZ0.js.map} +1 -1
  81. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js → EventsTab-CgwYq-Qf.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js.map → EventsTab-CgwYq-Qf.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js → HealthTab-BgOCjVzR.js} +2 -2
  84. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js.map → HealthTab-BgOCjVzR.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js +2 -0
  86. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js.map +1 -0
  87. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js +2 -0
  88. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js.map +1 -0
  89. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js → MetricsTab-CcH9fP8P.js} +2 -2
  90. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js.map → MetricsTab-CcH9fP8P.js.map} +1 -1
  91. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js → OverviewTab-BOs8ZNev.js} +2 -2
  92. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js.map → OverviewTab-BOs8ZNev.js.map} +1 -1
  93. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js → ReposTab-B-MfSPvg.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js.map → ReposTab-B-MfSPvg.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js → SessionsTab-BmeN649Q.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js.map → SessionsTab-BmeN649Q.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js +2 -0
  98. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js.map +1 -0
  99. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js → TimeSavedCard-DnPg0L0z.js} +2 -2
  100. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js.map → TimeSavedCard-DnPg0L0z.js.map} +1 -1
  101. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js → TopicsTab-BODHOg_b.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js.map → TopicsTab-BODHOg_b.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js → TurnsTab-BK0I7hKr.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js.map → TurnsTab-BK0I7hKr.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{index-D-lPLRWB.js → index-mOlljTFI.js} +10 -10
  106. package/extensions/dashboard-client/dist/assets/index-mOlljTFI.js.map +1 -0
  107. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js → useSSE-BYOtv1-3.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js.map → useSSE-BYOtv1-3.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/index.html +1 -1
  110. package/extensions/dashboard-client/src/App.tsx +0 -4
  111. package/extensions/dashboard-client/src/api/client.ts +16 -0
  112. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +178 -0
  113. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +140 -0
  114. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +109 -0
  115. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +98 -0
  116. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +108 -0
  117. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +12 -598
  118. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +476 -0
  119. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +163 -0
  120. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +56 -466
  121. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderHealthCard.tsx +109 -0
  122. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +468 -0
  123. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +23 -484
  124. package/extensions/dashboard-server/api-contracts/embedder-health.ts +32 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +360 -0
  126. package/extensions/dashboard-server/api-contracts/endpoints/types.ts +350 -0
  127. package/extensions/dashboard-server/api-contracts/endpoints.ts +27 -648
  128. package/extensions/dashboard-server/api-contracts/index.ts +6 -0
  129. package/extensions/dashboard-server/api-contracts/raptor.ts +49 -0
  130. package/extensions/dashboard-server/api-contracts.test/_helpers.ts +47 -0
  131. package/extensions/dashboard-server/routes-cache.test/_helpers.ts +94 -0
  132. package/extensions/dashboard-server/routes-embedder-health.ts +77 -0
  133. package/extensions/dashboard-server/routes-raptor.ts +128 -0
  134. package/extensions/dashboard-server/routes-setup.ts +1 -1
  135. package/extensions/dashboard-server/routes.ts +2 -0
  136. package/extensions/dashboard-server/server.ts +4 -0
  137. package/extensions/mega-cache-replay.test/_helpers.ts +188 -0
  138. package/extensions/mega-commands/dataCommands.ts +169 -0
  139. package/extensions/mega-commands/helpers.ts +62 -0
  140. package/extensions/mega-commands/historyCommands.ts +117 -0
  141. package/extensions/mega-commands/setupCommand.ts +235 -0
  142. package/extensions/mega-commands.ts +25 -538
  143. package/extensions/mega-compact-s38.test/_helpers.ts +201 -0
  144. package/extensions/mega-compact.test/_helpers.ts +237 -0
  145. package/extensions/mega-compact.ts +12 -2
  146. package/extensions/mega-events/agent-handlers/agentEndHandler.ts +238 -0
  147. package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +74 -0
  148. package/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.ts +50 -0
  149. package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +421 -0
  150. package/extensions/mega-events/agent-handlers/turnEndHandler/event.ts +16 -0
  151. package/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.ts +73 -0
  152. package/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.ts +29 -0
  153. package/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.ts +42 -0
  154. package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +61 -0
  155. package/extensions/mega-events/agent-handlers/turnEndHandler.ts +64 -0
  156. package/extensions/mega-events/agent-handlers.ts +15 -861
  157. package/extensions/mega-events/context-handler/afterCompact.ts +213 -0
  158. package/extensions/mega-events/context-handler/messageText.ts +20 -0
  159. package/extensions/mega-events/context-handler/tailResult.ts +63 -0
  160. package/extensions/mega-events/context-handler.ts +17 -235
  161. package/extensions/mega-events/separated-prompt.test/_helpers.ts +35 -0
  162. package/package.json +1 -1
  163. package/src/config.ts +4 -0
  164. package/src/dedup-engine.test/_helpers.ts +79 -0
  165. package/src/e2e.test/_helpers.ts +59 -0
  166. package/src/embedder.ts +4 -0
  167. package/src/httpEmbedder.ts +7 -0
  168. package/src/hyde.ts +162 -0
  169. package/src/importance.test/_helpers.ts +5 -0
  170. package/src/memoryGraph/sources/checkpoints.ts +99 -0
  171. package/src/memoryGraph/sources/edges.ts +23 -0
  172. package/src/memoryGraph/sources/flags.ts +26 -0
  173. package/src/memoryGraph/sources/helpers.ts +13 -0
  174. package/src/memoryGraph/sources/memories.ts +108 -0
  175. package/src/memoryGraph/sources/raptor.ts +37 -0
  176. package/src/memoryGraph/sources/turns.ts +226 -0
  177. package/src/memoryGraph/sources.ts +19 -544
  178. package/src/memoryGraph-gates.test/_helpers.ts +164 -0
  179. package/src/ratio.bench.test/_helpers.ts +268 -0
  180. package/src/recall/async.ts +142 -0
  181. package/src/recall/format.ts +173 -0
  182. package/src/recall/memory.ts +90 -0
  183. package/src/recall/reformulate.ts +111 -0
  184. package/src/recall/sync.ts +196 -0
  185. package/src/recall/types.ts +63 -0
  186. package/src/recall.ts +25 -712
  187. package/src/store/sqlite/schema/core.ts +233 -0
  188. package/src/store/sqlite/schema/game.ts +66 -0
  189. package/src/store/sqlite/schema/plan-v2.ts +108 -0
  190. package/src/store/sqlite/schema/turns.ts +60 -0
  191. package/src/store/sqlite/schema.ts +13 -441
  192. package/src/store/turns/contract-compliance.test/_helpers.ts +38 -0
  193. package/src/store/turns/sqlite-store/admin.ts +249 -0
  194. package/src/store/turns/sqlite-store/ctx.ts +14 -0
  195. package/src/store/turns/sqlite-store/read.ts +174 -0
  196. package/src/store/turns/sqlite-store/rows.ts +55 -0
  197. package/src/store/turns/sqlite-store/write.ts +193 -0
  198. package/src/store/turns/sqlite-store.ts +33 -514
  199. package/src/vectorStore/class.ts +433 -0
  200. package/src/vectorStore/hash.ts +11 -0
  201. package/src/vectorStore/types.ts +58 -0
  202. package/src/vectorStore.test/_helpers.ts +20 -0
  203. package/src/vectorStore.ts +13 -489
  204. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-D-lPLRWB.js.map +0 -1
@@ -0,0 +1,476 @@
1
+ /**
2
+ * MemoryMapTab/MemoryMapView.tsx — D3 memory map with force-directed graph.
3
+ * Node shape encodes nodeType: checkpoint=filled circle, turn=hollow circle,
4
+ * turn-content=hollow+ring, memory=diamond.
5
+ * Fetches from /api/memory-map, renders SVG force layout.
6
+ */
7
+ import type React from "react";
8
+ import { useEffect, useRef, useState, useCallback, useMemo } from "react";
9
+ import { renderNodeShape, NODE_COLORS, NODE_TYPE_LABELS } from "../../memory-map-shapes.js";
10
+ import type { NodeType } from "../../memory-map-shapes.js";
11
+ import { buildLayout, applyForces } from "../../memory-map-layout.js";
12
+ import type { LayoutNode, LayoutEdge } from "../../memory-map-layout.js";
13
+
14
+ // Types (mirror of api-contracts/memory-map.ts — client-side only)
15
+
16
+ interface GraphNode {
17
+ id: string;
18
+ sessionId: string;
19
+ label: string;
20
+ summaryTruncated: string;
21
+ tokenEstimate: number;
22
+ timestamp: number;
23
+ dedupStatus: string | undefined;
24
+ raptorLevel: number;
25
+ topicSummary: string | undefined;
26
+ decisionCount: number;
27
+ textSnippet: string;
28
+ /** Source type discriminator for UI node-shape encoding. */
29
+ nodeType: "checkpoint" | "turn" | "turn-content" | "memory";
30
+ }
31
+
32
+ interface GraphEdge {
33
+ source: string;
34
+ target: string;
35
+ weight: number;
36
+ type: "temporal" | "semantic" | "raptor_parent";
37
+ }
38
+
39
+ interface GraphValidationReport {
40
+ readonly gatesRun: number;
41
+ readonly gatesPassed: number;
42
+ readonly dropped: { nodes: number; edges: number };
43
+ readonly warnings: Array<{ gate: string; code: string; count: number }>;
44
+ readonly sources: {
45
+ checkpoint: number;
46
+ turn: number;
47
+ turnContent: number;
48
+ memory: number;
49
+ };
50
+ readonly builtAt: number;
51
+ }
52
+
53
+ interface GraphMetadata {
54
+ totalNodes: number;
55
+ totalEdges: number;
56
+ avgWeight: number;
57
+ nodeTypeBreakdown: Record<string, number>;
58
+ edgeTypeBreakdown: Record<string, number>;
59
+ }
60
+
61
+ interface MemoryMapResponse {
62
+ nodes: GraphNode[];
63
+ edges: GraphEdge[];
64
+ metadata: GraphMetadata;
65
+ /** Validation report from the 9-gate pipeline. Optional for backward compat. */
66
+ validation?: GraphValidationReport;
67
+ }
68
+
69
+ // Simulation rendering constants (layout math is in memory-map-layout.ts)
70
+
71
+ const MAX_ITERATIONS = 300;
72
+ const EDGE_ALPHA = 0.3;
73
+ const SVG_WIDTH = 900;
74
+ const SVG_HEIGHT = 600;
75
+ const PADDING = 40;
76
+
77
+ // Helpers
78
+
79
+ function edgeColor(type: GraphEdge["type"]): string {
80
+ switch (type) {
81
+ case "temporal":
82
+ return "#6366f1"; // indigo
83
+ case "semantic":
84
+ return "#22c55e"; // green
85
+ case "raptor_parent":
86
+ return "#f59e0b"; // amber
87
+ }
88
+ }
89
+
90
+ // Minimal legend swatch component
91
+ interface SwatchProps {
92
+ type: "node" | "edge";
93
+ color: string;
94
+ shape: "filled-circle" | "hollow-circle" | "hollow-ring" | "diamond" | "line";
95
+ label: string;
96
+ }
97
+ const Swatch: React.FC<SwatchProps> = ({ color, shape, label }) => {
98
+ const base: React.CSSProperties = {
99
+ display: "inline-block",
100
+ width: "10px",
101
+ height: "10px",
102
+ verticalAlign: "middle",
103
+ marginRight: "3px",
104
+ borderRadius: shape.startsWith("hollow") || shape === "filled-circle" ? "50%" : undefined,
105
+ };
106
+ let swatch: JSX.Element;
107
+ if (shape === "line") {
108
+ return <span style={{ color, marginRight: "6px" }}>{label}</span>;
109
+ } else if (shape === "filled-circle") {
110
+ swatch = <span style={{ ...base, backgroundColor: color }} />;
111
+ } else if (shape === "hollow-circle") {
112
+ swatch = <span style={{ ...base, border: `2px solid ${color}`, backgroundColor: "transparent" }} />;
113
+ } else if (shape === "hollow-ring") {
114
+ swatch = (
115
+ <span style={{ ...base, border: `2px solid ${color}`, backgroundColor: "transparent", position: "relative" }}>
116
+ <span style={{ display: "block", width: "4px", height: "4px", borderRadius: "50%", backgroundColor: color, margin: "2px auto" }} />
117
+ </span>
118
+ );
119
+ } else {
120
+ swatch = <span style={{ ...base, clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)", backgroundColor: color }} />;
121
+ }
122
+ return <><span className="memory-map-legend-item">{swatch}{label}</span> </>;
123
+ };
124
+
125
+ // Force simulation engine (delegated to memory-map-layout.ts)
126
+ const MemoryMapView: React.FC = () => {
127
+ const [data, setData] = useState<MemoryMapResponse | null>(null);
128
+ const [loading, setLoading] = useState<boolean>(true);
129
+ const [error, setError] = useState<string | null>(null);
130
+ const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
131
+ const [searchQuery, setSearchQuery] = useState<string>("");
132
+ const [filterSession] = useState<string>("");
133
+ const [frame, setFrame] = useState<number>(0);
134
+
135
+ const layoutRef = useRef<{ nodes: LayoutNode[]; edges: LayoutEdge[] } | null>(null);
136
+ const animRef = useRef<number>(0);
137
+
138
+ // Fetch graph data when filterSession changes
139
+ useEffect(() => {
140
+ let cancelled = false;
141
+ const params = new URLSearchParams();
142
+ if (filterSession) params.set("sessionId", filterSession);
143
+ params.set("threshold", "0.7");
144
+ params.set("maxEdgesPerNode", "4");
145
+
146
+ setLoading(true);
147
+ setError(null);
148
+
149
+ fetch(`/api/memory-map?${params.toString()}`)
150
+ .then((r) => {
151
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
152
+ return r.json() as Promise<MemoryMapResponse>;
153
+ })
154
+ .then((d) => {
155
+ if (!cancelled) {
156
+ setData(d);
157
+ layoutRef.current = buildLayout(d);
158
+ setFrame(1);
159
+ setLoading(false);
160
+ }
161
+ })
162
+ .catch((e: unknown) => {
163
+ if (!cancelled) {
164
+ setError(e instanceof Error ? e.message : "Unknown error");
165
+ setLoading(false);
166
+ }
167
+ });
168
+
169
+ return () => {
170
+ cancelled = true;
171
+ cancelAnimationFrame(animRef.current);
172
+ };
173
+ }, [filterSession]);
174
+
175
+ // Run force simulation via requestAnimationFrame
176
+ useEffect(() => {
177
+ if (!layoutRef.current || frame === 0) return;
178
+
179
+ const layout = layoutRef.current;
180
+ let iter = 0;
181
+
182
+ const tick = () => {
183
+ if (iter >= MAX_ITERATIONS) return;
184
+ applyForces(layout);
185
+ iter++;
186
+ setFrame(iter);
187
+ animRef.current = requestAnimationFrame(tick);
188
+ };
189
+
190
+ animRef.current = requestAnimationFrame(tick);
191
+ return () => cancelAnimationFrame(animRef.current);
192
+ }, [frame]);
193
+
194
+ // Toggle pin / select a node
195
+ const handleNodeClick = useCallback((node: LayoutNode, index: number) => {
196
+ const layout = layoutRef.current;
197
+ if (!layout) return;
198
+ layout.nodes[index].pinned = !layout.nodes[index].pinned;
199
+ if (!layout.nodes[index].pinned) {
200
+ layout.nodes[index].vx = 0;
201
+ layout.nodes[index].vy = 0;
202
+ }
203
+ setSelectedNode((prev) => (prev?.id === node.id ? null : (node as unknown as GraphNode)));
204
+ setFrame((s) => s + 1);
205
+ }, []);
206
+
207
+ // Filtered node indices for search
208
+ const filteredSet = useMemo<Set<number> | null>(() => {
209
+ const layout = layoutRef.current;
210
+ if (!layout || !searchQuery) return null;
211
+ const q = searchQuery.toLowerCase();
212
+ return new Set(
213
+ layout.nodes
214
+ .map((n, i) =>
215
+ n.summaryTruncated.toLowerCase().includes(q) || n.label.toLowerCase().includes(q) ? i : -1,
216
+ )
217
+ .filter((i) => i >= 0),
218
+ );
219
+ }, [searchQuery]);
220
+
221
+ // Health indicator helpers (D3)
222
+
223
+ /**
224
+ * Derive graph health from validation report:
225
+ * green = all gates passed + no warnings
226
+ * yellow = warnings present but no critical gate failed
227
+ * red = a critical gate (gatesRun > gatesPassed) failed
228
+ */
229
+ const graphHealth = useMemo<{ level: "green" | "yellow" | "red"; label: string }>(() => {
230
+ const v = data?.validation;
231
+ if (!v) return { level: "yellow", label: "No validation" };
232
+ if (v.gatesPassed < v.gatesRun) return { level: "red", label: "Gate failure" };
233
+ if (v.warnings.length > 0) return { level: "yellow", label: `${v.warnings.length} warning(s)` };
234
+ return { level: "green", label: "Healthy" };
235
+ }, [data?.validation]);
236
+
237
+ const healthColor: Record<string, string> = {
238
+ green: "#22c55e",
239
+ yellow: "#eab308",
240
+ red: "#ef4444",
241
+ };
242
+
243
+ // Source availability indicators (D3)
244
+
245
+ /** Count badge fragments from validation.sources. */
246
+ const countBadge = useMemo<string>(() => {
247
+ const v = data?.validation;
248
+ if (!v) {
249
+ const totalNodes = data?.metadata.totalNodes ?? 0;
250
+ return `${totalNodes} nodes`;
251
+ }
252
+ const parts: string[] = [];
253
+ if (v.sources.checkpoint > 0) parts.push(`${v.sources.checkpoint} checkpoints`);
254
+ if (v.sources.turn > 0) parts.push(`${v.sources.turn} turns`);
255
+ if (v.sources.turnContent > 0) parts.push(`${v.sources.turnContent} turn-content`);
256
+ if (v.sources.memory > 0) parts.push(`${v.sources.memory} memories`);
257
+ if (parts.length === 0) {
258
+ parts.push(`${data?.metadata.totalNodes ?? 0} nodes`);
259
+ }
260
+ return parts.join(" · ");
261
+ }, [data?.validation, data?.metadata.totalNodes]);
262
+
263
+ /** Per-source availability: ✓ for available sources, ✗ for empty ones. */
264
+ const sourceAvail = useMemo<string>(() => {
265
+ const v = data?.validation;
266
+ if (!v) return "";
267
+ const parts: string[] = [];
268
+ parts.push(v.sources.checkpoint > 0 ? "✓ checkpoints" : "✗ checkpoints");
269
+ parts.push(v.sources.turn > 0 ? "✓ turns" : "✗ turns");
270
+ parts.push(v.sources.turnContent > 0 ? "✓ turn-content" : "✗ turn-content");
271
+ parts.push(v.sources.memory > 0 ? "✓ memories" : "✗ memories");
272
+ return parts.join(" | ");
273
+ }, [data?.validation]);
274
+
275
+ /** Whether all four sources are empty (true empty state). */
276
+ const allSourcesEmpty = useMemo<boolean>(() => {
277
+ const v = data?.validation;
278
+ if (!v) return data?.metadata.totalNodes === 0;
279
+ return (
280
+ v.sources.checkpoint === 0 &&
281
+ v.sources.turn === 0 &&
282
+ v.sources.turnContent === 0 &&
283
+ v.sources.memory === 0
284
+ );
285
+ }, [data?.validation, data?.metadata.totalNodes]);
286
+
287
+ // Loading state
288
+
289
+ if (loading) {
290
+ return <div className="memory-map-loading">Loading memory graph...</div>;
291
+ }
292
+
293
+ // Error state
294
+
295
+ if (error) {
296
+ return (
297
+ <div className="memory-map-error">
298
+ <p>Failed to load memory map: {error}</p>
299
+ <button
300
+ className="memory-map-retry-btn"
301
+ onClick={() => setFrame((s) => s + 1)}
302
+ >
303
+ Retry
304
+ </button>
305
+ </div>
306
+ );
307
+ }
308
+
309
+ // Empty state (D2)
310
+
311
+ if (!data || !layoutRef.current || allSourcesEmpty) {
312
+ return (
313
+ <div className="memory-map-empty">
314
+ <p>Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner.</p>
315
+ </div>
316
+ );
317
+ }
318
+
319
+ const layout = layoutRef.current;
320
+
321
+ // Render
322
+
323
+ return (
324
+ <div className="memory-map-container">
325
+ {/* Toolbar */}
326
+ <div className="memory-map-toolbar">
327
+ <input
328
+ type="text"
329
+ placeholder="Search memories..."
330
+ value={searchQuery}
331
+ onInput={(e: React.FormEvent<HTMLInputElement>) =>
332
+ setSearchQuery((e.target as HTMLInputElement).value)
333
+ }
334
+ className="memory-map-search"
335
+ />
336
+ <span className="memory-map-stats">
337
+ {/* Count badge */}
338
+ <span className="memory-map-count-badge">{countBadge}</span>
339
+ <br />
340
+ <span className="memory-map-edges">{data.edges.length} edges</span>
341
+ </span>
342
+ {/* Graph-health indicator */}
343
+ <span
344
+ className="memory-map-health-badge"
345
+ style={{
346
+ backgroundColor: healthColor[graphHealth.level] ?? "#6b7280",
347
+ color: "#fff",
348
+ padding: "2px 8px",
349
+ borderRadius: "10px",
350
+ fontSize: "11px",
351
+ fontWeight: 600,
352
+ marginLeft: "8px",
353
+ }}
354
+ title={`Validation: ${graphHealth.label}`}
355
+ >
356
+ {graphHealth.level === "green" ? "Healthy" : graphHealth.level === "yellow" ? "Warnings" : "Critical"}
357
+ </span>
358
+ </div>
359
+
360
+ {/* Source availability indicators */}
361
+ {sourceAvail ? (
362
+ <div className="memory-map-source-avail" style={{ fontSize: "11px", padding: "2px 12px", color: "#6b7280" }}>
363
+ {sourceAvail}
364
+ </div>
365
+ ) : null}
366
+
367
+ {/* Legend — node types + edge types */}
368
+ <div className="memory-map-legend">
369
+ <span className="memory-map-legend-section">Nodes:</span>
370
+ <Swatch type="node" color={NODE_COLORS.checkpoint} shape="filled-circle" label="Checkpoint" />
371
+ <Swatch type="node" color={NODE_COLORS.turn} shape="hollow-circle" label="Turn" />
372
+ <Swatch type="node" color={NODE_COLORS["turn-content"]} shape="hollow-ring" label="Turn Content" />
373
+ <Swatch type="node" color={NODE_COLORS.memory} shape="diamond" label="Memory" />
374
+ <span className="memory-map-legend-sep">|</span>
375
+ <span className="memory-map-legend-section">Edges:</span>
376
+ <Swatch type="edge" color={edgeColor("semantic")} shape="line" label="Semantic" />
377
+ <Swatch type="edge" color={edgeColor("temporal")} shape="line" label="Temporal" />
378
+ <Swatch type="edge" color={edgeColor("raptor_parent")} shape="line" label="Raptor" />
379
+ </div>
380
+
381
+ {/* SVG graph */}
382
+ <svg
383
+ width={SVG_WIDTH}
384
+ height={SVG_HEIGHT}
385
+ viewBox={`0 0 ${SVG_WIDTH} ${SVG_HEIGHT}`}
386
+ className="memory-map-svg"
387
+ >
388
+ {/* Edges */}
389
+ {layout.edges.map((e, i) => {
390
+ const src = layout.nodes[e.source];
391
+ const tgt = layout.nodes[e.target];
392
+ const color = edgeColor(e.type);
393
+ const strokeW = Math.max(1, e.weight * 2.5);
394
+ return (
395
+ <line
396
+ key={`edge-${i}`}
397
+ x1={PADDING + (src.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}
398
+ y1={PADDING + (src.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}
399
+ x2={PADDING + (tgt.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}
400
+ y2={PADDING + (tgt.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}
401
+ stroke={color}
402
+ strokeWidth={strokeW}
403
+ opacity={EDGE_ALPHA}
404
+ />
405
+ );
406
+ })}
407
+
408
+ {/* Nodes — shape encodes nodeType */}
409
+ {layout.nodes.map((n, i) => {
410
+ const cx =
411
+ PADDING + (n.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING);
412
+ const cy =
413
+ PADDING + (n.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING);
414
+ const radius = Math.min(20, 8 + n.tokenEstimate / 500);
415
+ const isFiltered = filteredSet ? filteredSet.has(i) : true;
416
+ const isSelected = selectedNode?.id === n.id;
417
+ const nodeLabel = n.label.length > 20 ? n.label.slice(0, 18) + "..." : n.label;
418
+
419
+ return (
420
+ <g
421
+ key={`node-${i}`}
422
+ className="memory-map-node-group"
423
+ onClick={() => handleNodeClick(n, i)}
424
+ style={{ cursor: "pointer" }}
425
+ >
426
+ {renderNodeShape(cx, cy, radius, n.nodeType as NodeType, isSelected, isFiltered, n.dedupStatus)}
427
+ <text
428
+ x={cx}
429
+ y={cy + radius + 12}
430
+ textAnchor="middle"
431
+ fontSize="9px"
432
+ fill="#374151"
433
+ className="memory-map-label"
434
+ >
435
+ {nodeLabel}
436
+ </text>
437
+ </g>
438
+ );
439
+ })}
440
+ </svg>
441
+
442
+ {/* Node detail panel */}
443
+ {selectedNode ? (
444
+ <div className="memory-map-detail">
445
+ <h3>
446
+ <span
447
+ className="memory-map-detail-type"
448
+ style={{
449
+ display: "inline-block",
450
+ fontSize: "12px",
451
+ fontWeight: 400,
452
+ color: "#6b7280",
453
+ marginRight: "6px",
454
+ }}
455
+ >
456
+ {NODE_TYPE_LABELS[selectedNode.nodeType]}
457
+ </span>
458
+ {selectedNode.label}
459
+ </h3>
460
+ <p>{selectedNode.summaryTruncated}</p>
461
+ {selectedNode.topicSummary ? (
462
+ <p className="memory-map-topic">Topic: {selectedNode.topicSummary}</p>
463
+ ) : null}
464
+ <p>
465
+ Tokens: {selectedNode.tokenEstimate} | Decisions: {selectedNode.decisionCount}
466
+ </p>
467
+ <p>Session: {selectedNode.sessionId}</p>
468
+ <p>Raptor Level: {selectedNode.raptorLevel}</p>
469
+ <p className="memory-map-snippet">{selectedNode.textSnippet}</p>
470
+ </div>
471
+ ) : null}
472
+ </div>
473
+ );
474
+ };
475
+
476
+ export default MemoryMapView;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * MemoryMapTab/RaptorTreeView.tsx — RAPTOR tree sub-tab (Part B).
3
+ *
4
+ * Fetches the hierarchical RAPTOR tree from /api/raptor-tree (defaults to the
5
+ * most recent session with nodes) and renders it as level-indented summary
6
+ * cards. No D3 needed — CSS indentation encodes hierarchy.
7
+ */
8
+ import type React from "react";
9
+ import { useEffect, useState } from "react";
10
+ import { fetchRaptorTree } from "../../api/client";
11
+ import type { RaptorTreeResponse, RaptorNodeDTO } from "@contracts";
12
+
13
+ /** Per-level border color for node cards. */
14
+ function levelColor(level: number): string {
15
+ if (level === 0) return "#d4a017"; // gold — root
16
+ if (level === 1) return "#3b82f6"; // blue
17
+ return "#14b8a6"; // teal — level 2+
18
+ }
19
+
20
+ function formatDate(ts: number): string {
21
+ if (!ts) return "—";
22
+ return new Date(ts).toLocaleString();
23
+ }
24
+
25
+ const containerStyle: React.CSSProperties = {
26
+ padding: "1rem",
27
+ width: "100%",
28
+ boxSizing: "border-box",
29
+ };
30
+
31
+ const headerStyle: React.CSSProperties = {
32
+ display: "flex",
33
+ gap: "1.5rem",
34
+ alignItems: "center",
35
+ marginBottom: "1rem",
36
+ color: "#a0a0c0",
37
+ fontSize: "0.9rem",
38
+ };
39
+
40
+ const emptyStyle: React.CSSProperties = {
41
+ padding: "2rem",
42
+ border: "1px dashed #2a2a4e",
43
+ borderRadius: "8px",
44
+ color: "#a0a0c0",
45
+ textAlign: "center",
46
+ };
47
+
48
+ const loadingStyle: React.CSSProperties = {
49
+ padding: "1rem",
50
+ color: "#a0a0c0",
51
+ };
52
+
53
+ function NodeCard({ node }: { node: RaptorNodeDTO }): React.ReactElement {
54
+ const border = levelColor(node.level);
55
+ const childrenLabel =
56
+ node.children.length > 0
57
+ ? `${node.children.length} child${node.children.length === 1 ? "" : "ren"}`
58
+ : "leaf";
59
+ return (
60
+ <div
61
+ style={{
62
+ background: "#14142b",
63
+ border: `1px solid ${border}`,
64
+ borderRadius: "8px",
65
+ padding: "0.75rem 1rem",
66
+ marginBottom: "0.6rem",
67
+ marginLeft: node.level * 24,
68
+ maxWidth: "90%",
69
+ }}
70
+ >
71
+ <div style={{ display: "flex", gap: "0.75rem", alignItems: "center", marginBottom: "0.35rem" }}>
72
+ <span
73
+ style={{
74
+ display: "inline-block",
75
+ background: border,
76
+ color: "#fff",
77
+ borderRadius: "10px",
78
+ padding: "1px 8px",
79
+ fontSize: "11px",
80
+ fontWeight: 600,
81
+ }}
82
+ >
83
+ L{node.level}
84
+ </span>
85
+ <span style={{ fontSize: "12px", color: "#6b7280" }}>
86
+ {childrenLabel} · {node.tokenEstimate} tok · {node.qualityMarker}
87
+ </span>
88
+ <span style={{ fontSize: "12px", color: "#6b7280", marginLeft: "auto" }}>
89
+ {formatDate(node.builtAt)}
90
+ </span>
91
+ </div>
92
+ <div style={{ fontSize: "0.92rem", color: "#e5e7eb" }}>{node.summary}</div>
93
+ </div>
94
+ );
95
+ }
96
+
97
+ export default function RaptorTreeView(): React.ReactElement {
98
+ const [data, setData] = useState<RaptorTreeResponse | null>(null);
99
+ const [loading, setLoading] = useState<boolean>(true);
100
+ const [error, setError] = useState<string | null>(null);
101
+
102
+ useEffect(() => {
103
+ let cancelled = false;
104
+ setLoading(true);
105
+ setError(null);
106
+ fetchRaptorTree()
107
+ .then((d) => {
108
+ if (!cancelled) {
109
+ setData(d);
110
+ setLoading(false);
111
+ }
112
+ })
113
+ .catch((e: unknown) => {
114
+ if (!cancelled) {
115
+ setError(e instanceof Error ? e.message : "Unknown error");
116
+ setLoading(false);
117
+ }
118
+ });
119
+ return () => {
120
+ cancelled = true;
121
+ };
122
+ }, []);
123
+
124
+ if (loading) {
125
+ return <div style={loadingStyle}>Loading RAPTOR tree...</div>;
126
+ }
127
+
128
+ if (error) {
129
+ return (
130
+ <div style={emptyStyle}>
131
+ <p>Failed to load RAPTOR tree: {error}</p>
132
+ </div>
133
+ );
134
+ }
135
+
136
+ if (!data || data.empty || data.nodes.length === 0) {
137
+ return (
138
+ <div style={emptyStyle}>
139
+ <p>
140
+ No RAPTOR tree built yet — run a compaction (the tree builds during
141
+ compaction).
142
+ </p>
143
+ </div>
144
+ );
145
+ }
146
+
147
+ return (
148
+ <div style={containerStyle}>
149
+ <div style={headerStyle}>
150
+ <span>
151
+ <strong>{data.nodes.length}</strong> nodes
152
+ </span>
153
+ <span>
154
+ <strong>{data.levels + 1}</strong> levels
155
+ </span>
156
+ <span>Built {formatDate(data.builtAt ?? 0)}</span>
157
+ </div>
158
+ {data.nodes.map((node) => (
159
+ <NodeCard key={node.id} node={node} />
160
+ ))}
161
+ </div>
162
+ );
163
+ }