pi-mega-compact 0.11.7 → 0.11.9

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 (93) hide show
  1. package/dist/extensions/dashboard-server/gather-debug-keys.js +18 -0
  2. package/dist/extensions/dashboard-server/gather-debug.js +129 -0
  3. package/dist/extensions/dashboard-server/routes-maintenance.js +6 -0
  4. package/dist/extensions/dashboard-server/routes-memory-map.js +8 -15
  5. package/dist/extensions/mega-commands.js +65 -5
  6. package/dist/extensions/mega-config.js +7 -0
  7. package/dist/extensions/mega-events/agent-handlers.js +74 -5
  8. package/dist/extensions/mega-events/context-handler.js +132 -3
  9. package/dist/extensions/mega-runtime/snapshot.js +1 -1
  10. package/dist/extensions/mega-trim.js +10 -0
  11. package/dist/src/config/turns.js +2 -1
  12. package/dist/src/memoryGraph/embedding.js +50 -0
  13. package/dist/src/memoryGraph/gates.js +379 -0
  14. package/dist/src/memoryGraph/sources.js +390 -0
  15. package/dist/src/memoryGraph.js +133 -259
  16. package/dist/src/store/sqlite/embedding-cache.js +71 -0
  17. package/dist/src/store/sqlite.js +1 -0
  18. package/dist/src/topics/cluster.js +10 -2
  19. package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js → AchievementTiles-DFitrb4Y.js} +2 -2
  20. package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js.map → AchievementTiles-DFitrb4Y.js.map} +1 -1
  21. package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js → AchievementsTab-PJrmq8pa.js} +2 -2
  22. package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js.map → AchievementsTab-PJrmq8pa.js.map} +1 -1
  23. package/extensions/dashboard-client/dist/assets/CacheTab-C1WE7dEx.js +2 -0
  24. package/extensions/dashboard-client/dist/assets/CacheTab-C1WE7dEx.js.map +1 -0
  25. package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js → ConfigTab-Dx-RpO8C.js} +2 -2
  26. package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js.map → ConfigTab-Dx-RpO8C.js.map} +1 -1
  27. package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js → EventsTab-C63BEWEs.js} +2 -2
  28. package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js.map → EventsTab-C63BEWEs.js.map} +1 -1
  29. package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js → GameTab-DHpJE6km.js} +2 -2
  30. package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js.map → GameTab-DHpJE6km.js.map} +1 -1
  31. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BSv0GfBb.js +2 -0
  32. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BSv0GfBb.js.map +1 -0
  33. package/extensions/dashboard-client/dist/assets/MemoryMapTab-Cg1fgEx_.js +2 -0
  34. package/extensions/dashboard-client/dist/assets/MemoryMapTab-Cg1fgEx_.js.map +1 -0
  35. package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js → MetricsTab-DCahxNOQ.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js.map → MetricsTab-DCahxNOQ.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js → OverviewTab-RYvO1SZL.js} +2 -2
  38. package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js.map → OverviewTab-RYvO1SZL.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js → ReposTab-BDGr_UXb.js} +2 -2
  40. package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js.map → ReposTab-BDGr_UXb.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js → SessionsTab-ByrTnHaB.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js.map → SessionsTab-ByrTnHaB.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js → SetupTab-CELafKis.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js.map → SetupTab-CELafKis.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js → TimeSavedCard-BLWtmfr_.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js.map → TimeSavedCard-BLWtmfr_.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/TopicsTab-8Fsh_Igx.js +2 -0
  48. package/extensions/dashboard-client/dist/assets/TopicsTab-8Fsh_Igx.js.map +1 -0
  49. package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js → TurnsTab-D7mgXDe2.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js.map → TurnsTab-D7mgXDe2.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js → index-h3jgEs6G.js} +10 -10
  52. package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js.map → index-h3jgEs6G.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js → useSSE-C5lmipBF.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js.map → useSSE-C5lmipBF.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/index.html +1 -1
  56. package/extensions/dashboard-client/src/api/client.ts +5 -0
  57. package/extensions/dashboard-client/src/memory-map-layout.ts +161 -0
  58. package/extensions/dashboard-client/src/memory-map-shapes.tsx +126 -0
  59. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +29 -2
  60. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +101 -0
  61. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +201 -165
  62. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +21 -0
  63. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +1 -1
  64. package/extensions/dashboard-server/api-contracts/index.ts +3 -1
  65. package/extensions/dashboard-server/api-contracts/maintenance.ts +13 -0
  66. package/extensions/dashboard-server/api-contracts/memory-map.ts +14 -6
  67. package/extensions/dashboard-server/gather-debug-keys.ts +18 -0
  68. package/extensions/dashboard-server/gather-debug.ts +136 -0
  69. package/extensions/dashboard-server/routes-maintenance.ts +7 -0
  70. package/extensions/dashboard-server/routes-memory-map.ts +21 -29
  71. package/extensions/mega-commands.ts +63 -6
  72. package/extensions/mega-config.ts +22 -0
  73. package/extensions/mega-events/agent-handlers.ts +88 -5
  74. package/extensions/mega-events/context-handler.ts +152 -3
  75. package/extensions/mega-runtime/snapshot.ts +1 -1
  76. package/extensions/mega-trim.ts +15 -0
  77. package/package.json +1 -1
  78. package/src/config/turns.ts +5 -2
  79. package/src/memoryGraph/embedding.ts +63 -0
  80. package/src/memoryGraph/gates.ts +451 -0
  81. package/src/memoryGraph/sources.ts +484 -0
  82. package/src/memoryGraph.ts +188 -350
  83. package/src/store/sqlite/embedding-cache.ts +81 -0
  84. package/src/store/sqlite.ts +1 -0
  85. package/src/topics/cluster.ts +11 -1
  86. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js +0 -2
  87. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js.map +0 -1
  88. package/extensions/dashboard-client/dist/assets/MaintenanceTab-P4SUsvMf.js +0 -2
  89. package/extensions/dashboard-client/dist/assets/MaintenanceTab-P4SUsvMf.js.map +0 -1
  90. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +0 -2
  91. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +0 -1
  92. package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js +0 -2
  93. package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js.map +0 -1
@@ -1,438 +1,276 @@
1
1
  /**
2
- * memoryGraph.ts — S46 visual memory map: build a graph of graphs.
2
+ * memoryGraph.ts — S46/D3 visual memory map: build a graph of graphs.
3
3
  *
4
- * Nodes = checkpoints (with optional RAPTOR level annotations).
5
- * Edges = temporal adjacency, semantic similarity (cosine above threshold), and
6
- * RAPTOR parent–child links.
4
+ * Delegated-shell pattern: implementation lives in memoryGraph/gates.ts,
5
+ * memoryGraph/sources.ts, and memoryGraph/embedding.ts. This file owns
6
+ * the public types, the entry point (buildMemoryGraph), and exports.
7
7
  *
8
8
  * pi-agnostic (no pi runtime types). Zero network calls (PREVENT-PI-004).
9
- * Synchronous read-only interface non-fatal on store errors.
9
+ * Synchronous read-only interface -- non-fatal on store errors.
10
10
  *
11
11
  * Re-exports the public types as both MemoryGraph* and shorter aliases
12
12
  * (GraphNode, GraphEdge, GraphMetadata) so dashboard api-contracts can import
13
13
  * from the canonical source.
14
14
  */
15
- import { getStateDir, normalizeSessionId } from "./store.js";
16
- import { openStore, decodeEmbedding } from "./store/sqlite/utils.js";
17
- import { listRaptorNodes } from "./store/sqlite/raptor.js";
15
+ import { getStateDir } from "./store.js";
16
+ import { openStore } from "./store/sqlite/utils.js";
17
+ import type { DatabaseSync } from "node:sqlite";
18
18
  import { Logger } from "./log.js";
19
+ import { runValidationPipeline } from "./memoryGraph/gates.js";
20
+ import {
21
+ buildCheckpointNodes,
22
+ buildTurnNodes,
23
+ buildTurnContentNodes,
24
+ buildMemoryNodes,
25
+ addRaptorAnnotations,
26
+ deduplicateEdges,
27
+ areTurnsEnabled,
28
+ isTurnContentEnabled,
29
+ isTurnContentFlaggedOn,
30
+ areMemoriesEnabled,
31
+ } from "./memoryGraph/sources.js";
19
32
  const log = new Logger();
20
33
 
21
34
  // ---------------------------------------------------------------------------
22
35
  // Types
23
- // ---------------------------------------------------------------------------
36
+ // ------
24
37
 
25
- /** A node in the memory graph represents one checkpoint (potentially with
26
- * RAPTOR level information if it participates in a RAPTOR tree). */
38
+ /** A single node in the memory graph. */
27
39
  export interface MemoryGraphNode {
28
40
  id: string;
29
41
  sessionId: string;
30
42
  label: string;
31
43
  summaryTruncated: string;
32
44
  tokenEstimate: number;
33
- /** Unix-epoch ms (from checkpoint timestamp or created_at). */
34
45
  timestamp: number;
35
46
  dedupStatus: string | undefined;
36
- /** 0 = no RAPTOR parent; 1+ = RAPTOR cluster level. */
37
47
  raptorLevel: number;
38
48
  topicSummary: string | undefined;
39
49
  decisionCount: number;
40
- /** First ~200 chars of the checkpoint summary/text for inline preview. */
41
50
  textSnippet: string;
51
+ /** D3: node type discriminator for validation and UI styling. */
52
+ nodeType: "checkpoint" | "turn" | "turn-content" | "memory";
53
+ /** D3: if this turn node has an associated checkpoint epoch. */
54
+ epochId?: string;
42
55
  }
43
56
 
44
- /** Edge types indicate the *reason* two nodes are connected. */
45
- export type MemoryGraphEdgeType = "temporal" | "semantic" | "raptor_parent";
46
-
57
+ /** A directed edge between two graph nodes. */
47
58
  export interface MemoryGraphEdge {
48
59
  source: string;
49
60
  target: string;
50
61
  weight: number;
51
- type: MemoryGraphEdgeType;
62
+ type: "temporal" | "semantic" | "raptor_parent";
52
63
  }
53
64
 
54
- export interface MemoryGraphMetadata {
55
- sessionCount: number;
56
- totalCheckpoints: number;
57
- totalEdges: number;
58
- semanticEdgeCount: number;
59
- temporalEdgeCount: number;
60
- raptorEdgeCount: number;
61
- similarityThresholdUsed: number;
65
+ /** D3: validation report baked into every graph. */
66
+ export interface GraphValidationReport {
67
+ gatesRun: number;
68
+ gatesPassed: number;
69
+ dropped: { nodes: number; edges: number };
70
+ warnings: Array<{ gate: string; code: string; count: number }>;
71
+ sources: {
72
+ checkpoint: number;
73
+ turn: number;
74
+ turnContent: number;
75
+ memory: number;
76
+ };
62
77
  builtAt: number;
63
78
  }
64
79
 
65
- /** The complete memory graph returned by buildMemoryGraph. */
80
+ /** The full graph structure returned to consumers. */
66
81
  export interface MemoryGraph {
67
82
  nodes: MemoryGraphNode[];
68
83
  edges: MemoryGraphEdge[];
69
- metadata: MemoryGraphMetadata;
84
+ metadata: {
85
+ totalNodes: number;
86
+ totalEdges: number;
87
+ avgWeight: number;
88
+ nodeTypeBreakdown: Record<string, number>;
89
+ edgeTypeBreakdown: Record<string, number>;
90
+ };
91
+ /** D3: validation report from the 9-gate pipeline. */
92
+ validation: GraphValidationReport;
70
93
  }
71
94
 
72
95
  // ---------------------------------------------------------------------------
73
- // Short aliases for dashboard api-contracts import convenience
96
+ // Short aliases (dashboard api-contracts import these)
74
97
  // ---------------------------------------------------------------------------
75
98
 
76
99
  export type GraphNode = MemoryGraphNode;
77
100
  export type GraphEdge = MemoryGraphEdge;
78
- export type GraphMetadata = MemoryGraphMetadata;
79
-
80
- /** Options to configure graph construction. */
81
- export interface MemoryGraphOptions {
82
- /** Similarity threshold for semantic edges (cosine). Default 0.7. */
83
- similarityThreshold?: number;
84
- /** Maximum number of semantic edges per node (avoids dense clique). Default 4. */
85
- maxEdgesPerNode?: number;
86
- /** Optional session filter — undefined = all sessions. */
87
- sessionId?: string;
88
- /** State directory (defaults to getStateDir()). */
89
- stateDir?: string;
90
- /** Include RAPTOR parent–child edges when available. Default true. */
91
- includeRaptorEdges?: boolean;
92
- /** Include temporal adjacency edges (consecutive checkpoints in same session). Default true. */
93
- includeTemporalEdges?: boolean;
94
- }
101
+ export type GraphMetadata = MemoryGraph["metadata"];
95
102
 
96
103
  // ---------------------------------------------------------------------------
97
- // Defaults
98
- // ---------------------------------------------------------------------------
99
-
100
- const DEFAULT_SIMILARITY_THRESHOLD = 0.7;
101
- const DEFAULT_MAX_EDGES_PER_NODE = 4;
102
-
103
- // ---------------------------------------------------------------------------
104
- // Public API
104
+ // Public entry point
105
105
  // ---------------------------------------------------------------------------
106
106
 
107
107
  /**
108
- * Build a memory graph from the store.
108
+ * Build a memory graph from three sources (checkpoints / turns / memories)
109
+ * wired through a 9-gate validation pipeline.
109
110
  *
110
- * Opens the SQLite store, reads all (or session-filtered) checkpoints,
111
- * computes cosine similarity edges, temporal edges, and RAPTOR parent edges.
112
- * Returns a MemoryGraph or an empty graph on failure (non-fatal).
111
+ * Returns an empty graph (byte-identical pre-change for checkpoint-only) when
112
+ * no sources are available.
113
113
  */
114
- export function buildMemoryGraph(opts: MemoryGraphOptions = {}): MemoryGraph {
115
- const {
116
- similarityThreshold = DEFAULT_SIMILARITY_THRESHOLD,
117
- maxEdgesPerNode = DEFAULT_MAX_EDGES_PER_NODE,
118
- sessionId,
119
- stateDir = getStateDir(),
120
- includeRaptorEdges = true,
121
- includeTemporalEdges = true,
122
- } = opts;
123
-
124
- const builtAt = Date.now();
125
-
126
- try {
127
- const nodes = loadNodes(sessionId, stateDir);
128
- const nodeIndex = buildNodeIndex(nodes);
129
- const edges: MemoryGraphEdge[] = [];
130
-
131
- // Temporal edges: consecutive checkpoints within the same session
132
- if (includeTemporalEdges) {
133
- edges.push(...buildTemporalEdges(nodes));
134
- }
135
-
136
- // Semantic edges: cosine similarity above threshold
137
- edges.push(...buildSemanticEdges(nodes, nodeIndex, similarityThreshold, maxEdgesPerNode));
138
-
139
- // RAPTOR parent–child edges
140
- if (includeRaptorEdges) {
141
- edges.push(...buildRaptorEdges(nodes, sessionId, stateDir, nodeIndex));
142
- }
143
-
144
- // Deduplicate edges (keep highest weight for each source-target pair)
145
- const dedupedEdges = deduplicateEdges(edges);
146
-
147
- // Count sessions
148
- const sessionSet = new Set<string>();
149
- for (const n of nodes) {
150
- sessionSet.add(n.sessionId);
151
- }
152
-
153
- const meta: MemoryGraphMetadata = {
154
- sessionCount: sessionSet.size,
155
- totalCheckpoints: nodes.length,
156
- totalEdges: dedupedEdges.length,
157
- semanticEdgeCount: dedupedEdges.filter((e) => e.type === "semantic").length,
158
- temporalEdgeCount: dedupedEdges.filter((e) => e.type === "temporal").length,
159
- raptorEdgeCount: dedupedEdges.filter((e) => e.type === "raptor_parent").length,
160
- similarityThresholdUsed: similarityThreshold,
161
- builtAt,
162
- };
163
-
164
- return { nodes, edges: dedupedEdges, metadata: meta };
165
- } catch (err) {
166
- log.warn("buildMemoryGraph failed", { error: String(err) });
167
- return {
168
- nodes: [],
169
- edges: [],
170
- metadata: {
171
- sessionCount: 0,
172
- totalCheckpoints: 0,
173
- totalEdges: 0,
174
- semanticEdgeCount: 0,
175
- temporalEdgeCount: 0,
176
- raptorEdgeCount: 0,
177
- similarityThresholdUsed: similarityThreshold,
178
- builtAt,
179
- },
180
- };
114
+ export function buildMemoryGraph(
115
+ sessionId: string,
116
+ stateDir: string = getStateDir(),
117
+ ): MemoryGraph {
118
+ const { nodes, edges } = buildUnvalidatedGraph(sessionId, stateDir);
119
+
120
+ // Gate pipeline -- filters, deduplicates, and validates
121
+ const { ws, stats } = runValidationPipeline({ nodes, edges });
122
+
123
+ const nodeTypeBreakdown: Record<string, number> = {};
124
+ const edgeTypeBreakdown: Record<string, number> = {};
125
+ for (const n of ws.nodes) {
126
+ nodeTypeBreakdown[n.nodeType] = (nodeTypeBreakdown[n.nodeType] ?? 0) + 1;
127
+ }
128
+ for (const e of ws.edges) {
129
+ edgeTypeBreakdown[e.type] = (edgeTypeBreakdown[e.type] ?? 0) + 1;
181
130
  }
182
- }
183
-
184
- // ---------------------------------------------------------------------------
185
- // Node loading
186
- // ---------------------------------------------------------------------------
187
-
188
- function loadNodes(sessionId: string | undefined, stateDir: string): MemoryGraphNode[] {
189
- const db = openStore(stateDir);
190
-
191
- const sql = sessionId
192
- ? "SELECT * FROM context_chunks WHERE session_id = ? ORDER BY id ASC"
193
- : "SELECT * FROM context_chunks ORDER BY session_id, id ASC";
194
-
195
- const rows = sessionId
196
- ? (db.prepare(sql).all(normalizeSessionId(sessionId)) as any[])
197
- : (db.prepare(sql).all() as any[]);
198
131
 
199
- return rows.map(rowToGraphNode);
200
- }
132
+ const avgWeight =
133
+ ws.edges.length > 0
134
+ ? ws.edges.reduce((s, e) => s + e.weight, 0) / ws.edges.length
135
+ : 0;
136
+
137
+ const sources = countSourcesByType(ws.nodes);
138
+
139
+ const validation: GraphValidationReport = {
140
+ gatesRun: stats.gatesRun.length,
141
+ gatesPassed: stats.gatesPassed.length,
142
+ dropped: stats.dropped,
143
+ warnings: stats.warnings.map((w) => ({
144
+ gate: w.gate,
145
+ code: w.code,
146
+ count: w.count,
147
+ })),
148
+ sources,
149
+ builtAt: Date.now(),
150
+ };
201
151
 
202
- function rowToGraphNode(row: any): MemoryGraphNode {
203
- const summary = row.summary ?? "";
204
152
  return {
205
- id: row.id,
206
- sessionId: row.session_id,
207
- label: row.id,
208
- summaryTruncated: summary.length > 200 ? summary.slice(0, 197) + "..." : summary,
209
- tokenEstimate: row.token_estimate ?? 0,
210
- timestamp: row.created_at ?? Date.now(),
211
- dedupStatus: row.dedup_status ?? undefined,
212
- raptorLevel: 0, // enriched below if RAPTOR data exists
213
- topicSummary: row.topic_summary ?? undefined,
214
- decisionCount: row.key_decisions
215
- ? safeJsonCount(row.key_decisions)
216
- : 0,
217
- textSnippet: summary.length > 200 ? summary.slice(0, 197) + "..." : summary,
153
+ nodes: ws.nodes,
154
+ edges: ws.edges,
155
+ metadata: {
156
+ totalNodes: ws.nodes.length,
157
+ totalEdges: ws.edges.length,
158
+ avgWeight,
159
+ nodeTypeBreakdown,
160
+ edgeTypeBreakdown,
161
+ },
162
+ validation,
218
163
  };
219
164
  }
220
165
 
221
- /** Parse a JSON array stored in a text column and return its length. */
222
- function safeJsonCount(raw: string | null | undefined): number {
223
- if (!raw) return 0;
224
- try {
225
- const arr = JSON.parse(raw);
226
- return Array.isArray(arr) ? arr.length : 0;
227
- } catch {
228
- return 0;
229
- }
230
- }
231
-
232
166
  // ---------------------------------------------------------------------------
233
- // Node index building
167
+ // Source orchestrator
234
168
  // ---------------------------------------------------------------------------
235
169
 
236
- function buildNodeIndex(nodes: MemoryGraphNode[]): Map<string, number> {
237
- const idx = new Map<string, number>();
238
- nodes.forEach((n, i) => idx.set(n.id, i));
239
- return idx;
240
- }
241
-
242
- // ---------------------------------------------------------------------------
243
- // Edges: temporal
244
- // ---------------------------------------------------------------------------
245
-
246
- function buildTemporalEdges(nodes: MemoryGraphNode[]): MemoryGraphEdge[] {
170
+ function buildUnvalidatedGraph(
171
+ sessionId: string,
172
+ stateDir: string,
173
+ ): { nodes: MemoryGraphNode[]; edges: MemoryGraphEdge[] } {
174
+ const nodes: MemoryGraphNode[] = [];
247
175
  const edges: MemoryGraphEdge[] = [];
248
- if (nodes.length < 2) return edges;
249
176
 
250
- let prev = nodes[0];
251
- for (let i = 1; i < nodes.length; i++) {
252
- const cur = nodes[i];
253
- if (cur.sessionId === prev.sessionId) {
254
- edges.push({
255
- source: prev.id,
256
- target: cur.id,
257
- weight: 1.0,
258
- type: "temporal",
259
- });
260
- }
261
- prev = cur;
262
- }
263
- return edges;
264
- }
265
-
266
- // ---------------------------------------------------------------------------
267
- // Edges: semantic (cosine similarity)
268
- // ---------------------------------------------------------------------------
269
-
270
- function buildSemanticEdges(
271
- nodes: MemoryGraphNode[],
272
- _nodeIndex: Map<string, number>,
273
- threshold: number,
274
- maxPerNode: number,
275
- ): MemoryGraphEdge[] {
276
- if (nodes.length < 2) return [];
277
- const db = openStore();
278
-
279
- // Retrieve embeddings from DB for each node
280
- const embeddings = new Map<string, number[]>();
281
- const placeholders = nodes.map(() => "?").join(",");
282
- const ids = nodes.map((n) => n.id);
283
- const rows = db
284
- .prepare(`SELECT id, embedding_blob FROM context_chunks WHERE id IN (${placeholders})`)
285
- .all(...ids) as unknown as Array<{ id: string; embedding_blob: Uint8Array | null }>;
286
-
287
- for (const row of rows) {
288
- const emb = decodeEmbedding(row.embedding_blob as Uint8Array | null | undefined);
289
- if (emb.length > 0) {
290
- embeddings.set(row.id, emb);
291
- }
177
+ let db: DatabaseSync;
178
+ try {
179
+ db = openStore(stateDir);
180
+ } catch {
181
+ log.warn("graph_source_unavailable", { detail: "Cannot open store" });
182
+ return { nodes: [], edges: [] };
292
183
  }
293
184
 
294
- type EdgeCandidate = { target: string; weight: number };
295
- const edges: MemoryGraphEdge[] = [];
296
-
297
- for (const node of nodes) {
298
- const aId = node.id;
299
- const aEmb = embeddings.get(aId);
300
- if (!aEmb) continue;
301
-
302
- const candidates: EdgeCandidate[] = [];
303
-
304
- for (const other of nodes) {
305
- if (other.id === aId) continue;
306
- // Skip if already connected via temporal or raptor edge
307
- const bEmb = embeddings.get(other.id);
308
- if (!bEmb) continue;
309
-
310
- const sim = cosineSimilarity(aEmb, bEmb);
311
- if (sim >= threshold) {
312
- candidates.push({ target: other.id, weight: sim });
185
+ try {
186
+ // Source: checkpoints (always on)
187
+ buildCheckpointNodes(db, sessionId, nodes, edges);
188
+
189
+ // Source A: turns (structural, metadata-only)
190
+ if (areTurnsEnabled()) {
191
+ try {
192
+ buildTurnNodes(db, sessionId, nodes, edges);
193
+ } catch (err) {
194
+ log.warn("graph_source_unavailable", {
195
+ source: "turns",
196
+ detail: String(err),
197
+ });
313
198
  }
314
199
  }
315
200
 
316
- // Keep top-K highest-weight semantic neighbors
317
- candidates.sort((a, b) => b.weight - a.weight);
318
- const top = candidates.slice(0, maxPerNode);
319
- for (const c of top) {
320
- edges.push({
321
- source: aId,
322
- target: c.target,
323
- weight: c.weight,
324
- type: "semantic",
201
+ // Source B: turn content (raw_transcript join, embedding-based)
202
+ if (isTurnContentEnabled()) {
203
+ try {
204
+ buildTurnContentNodes(db, sessionId, nodes, edges);
205
+ } catch (err) {
206
+ log.warn("graph_source_unavailable", {
207
+ source: "turn-content",
208
+ detail: String(err),
209
+ });
210
+ }
211
+ } else if (isTurnContentFlaggedOn() && !isTurnContentEnabled()) {
212
+ log.warn("graph_source_unavailable", {
213
+ source: "turn-content",
214
+ detail: "dbMirror is OFF -- no raw_transcript available",
325
215
  });
326
216
  }
327
- }
328
-
329
- return edges;
330
- }
331
-
332
- // ---------------------------------------------------------------------------
333
- // Edges: RAPTOR parent–child
334
- // ---------------------------------------------------------------------------
335
-
336
- function buildRaptorEdges(
337
- nodes: MemoryGraphNode[],
338
- sessionId: string | undefined,
339
- stateDir: string,
340
- nodeIndex: Map<string, number>,
341
- ): MemoryGraphEdge[] {
342
- const edges: MemoryGraphEdge[] = [];
343
217
 
344
- try {
345
- if (sessionId) {
346
- enrichRaptorForSession(sessionId, stateDir, nodeIndex, nodes, edges);
347
- } else {
348
- // Load all sessions from DB
349
- const db = openStore(stateDir);
350
- const sessionRows = db
351
- .prepare("SELECT DISTINCT session_id FROM raptor_nodes ORDER BY session_id")
352
- .all() as any[];
353
- for (const row of sessionRows) {
354
- enrichRaptorForSession(row.session_id as string, stateDir, nodeIndex, nodes, edges);
218
+ // Source C: memories
219
+ if (areMemoriesEnabled()) {
220
+ try {
221
+ buildMemoryNodes(db, sessionId, nodes, edges);
222
+ } catch (err) {
223
+ log.warn("graph_source_unavailable", {
224
+ source: "memories",
225
+ detail: String(err),
226
+ });
355
227
  }
356
228
  }
357
- } catch (err) {
358
- log.warn("RAPTOR edge build failed (non-fatal)", { error: String(err) });
359
- }
360
-
361
- return edges;
362
- }
363
-
364
- function enrichRaptorForSession(
365
- sessId: string,
366
- stateDir: string,
367
- nodeIndex: Map<string, number>,
368
- nodes: MemoryGraphNode[],
369
- edges: MemoryGraphEdge[],
370
- ): void {
371
- const raptorNodes = listRaptorNodes(sessId, stateDir);
372
-
373
- for (const rn of raptorNodes) {
374
- const idx = nodeIndex.get(rn.id);
375
- if (idx !== undefined) {
376
- // Annotate the graph node with the RAPTOR level
377
- nodes[idx].raptorLevel = rn.level;
378
- }
379
229
 
380
- if (!rn.parentId) continue;
381
- // Only create edge if both nodes are in the graph
382
- if (nodeIndex.has(rn.id) && nodeIndex.has(rn.parentId)) {
383
- edges.push({
384
- source: rn.id,
385
- target: rn.parentId,
386
- weight: Math.max(0.1, 1.0 - rn.level * 0.1), // higher levels = weaker link
387
- type: "raptor_parent",
388
- });
230
+ // RAPTOR annotations on checkpoint nodes
231
+ try {
232
+ addRaptorAnnotations(sessionId, stateDir, nodes, edges);
233
+ } catch {
234
+ // non-fatal
389
235
  }
236
+ } finally {
237
+ // DatabaseSync has no close in Node 22 -- leave GC to clean up
390
238
  }
391
- }
392
-
393
- // ---------------------------------------------------------------------------
394
- // Edge deduplication
395
- // ---------------------------------------------------------------------------
396
-
397
- function deduplicateEdges(edges: MemoryGraphEdge[]): MemoryGraphEdge[] {
398
- const seen = new Map<string, MemoryGraphEdge>();
399
239
 
400
- for (const e of edges) {
401
- const keyA = `${e.source}|${e.target}`;
402
- const keyB = `${e.target}|${e.source}`;
403
- const existing = seen.get(keyA) ?? seen.get(keyB);
404
-
405
- if (!existing) {
406
- seen.set(keyA, e);
407
- } else if (e.weight > existing.weight) {
408
- // Replace with higher-weight edge
409
- seen.delete(keyA);
410
- seen.delete(keyB);
411
- seen.set(keyA, e);
412
- }
413
- }
240
+ // Pre-gate edge deduplication
241
+ const deduped = deduplicateEdges(edges);
242
+ edges.length = 0;
243
+ edges.push(...deduped);
414
244
 
415
- return Array.from(seen.values());
245
+ return { nodes, edges };
416
246
  }
417
247
 
418
248
  // ---------------------------------------------------------------------------
419
- // Cosine similarity
249
+ // Helpers
420
250
  // ---------------------------------------------------------------------------
421
251
 
422
- function cosineSimilarity(a: number[], b: number[]): number {
423
- const len = Math.min(a.length, b.length);
424
- if (len === 0) return 0;
425
-
426
- let dot = 0;
427
- let normA = 0;
428
- let normB = 0;
429
-
430
- for (let i = 0; i < len; i++) {
431
- dot += a[i] * b[i];
432
- normA += a[i] * a[i];
433
- normB += b[i] * b[i];
252
+ function countSourcesByType(
253
+ nodes: MemoryGraphNode[],
254
+ ): GraphValidationReport["sources"] {
255
+ let checkpoint = 0;
256
+ let turn = 0;
257
+ let turnContent = 0;
258
+ let memory = 0;
259
+ for (const n of nodes) {
260
+ switch (n.nodeType) {
261
+ case "checkpoint":
262
+ checkpoint++;
263
+ break;
264
+ case "turn":
265
+ turn++;
266
+ break;
267
+ case "turn-content":
268
+ turnContent++;
269
+ break;
270
+ case "memory":
271
+ memory++;
272
+ break;
273
+ }
434
274
  }
435
-
436
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
437
- return denom === 0 ? 0 : dot / denom;
438
- }
275
+ return { checkpoint, turn, turnContent, memory };
276
+ }