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
@@ -211,6 +211,9 @@ export type { MemoryStatusResponse, MemoryStatsTopMemory } from "./memory.js";
211
211
  // Setup wizard — embedder configuration + backend detection
212
212
  export type { SetupStatusResponse, SetupDetectResponse, SetupConfigureRequest, SetupConfigureResponse, DetectResult, OllamaDetectResult } from "./setup.js";
213
213
 
214
+ // Embedder health probe
215
+ export type { EmbedderHealthResponse } from "./embedder-health.js";
216
+
214
217
  // S46/D3 — memory graph (visual memory map) + validation report
215
218
  export type {
216
219
  MemoryMapResponse,
@@ -220,6 +223,9 @@ export type {
220
223
  GraphValidationReport,
221
224
  } from "./memory-map.js";
222
225
 
226
+ // Part B — RAPTOR tree
227
+ export type { RaptorNodeDTO, RaptorTreeResponse } from "./raptor.js";
228
+
223
229
  // A3 — cache stripe distribution + health
224
230
  export type {
225
231
  StripeBucket,
@@ -0,0 +1,49 @@
1
+ /**
2
+ * api-contracts/raptor.ts — RAPTOR tree API contract.
3
+ *
4
+ * Response shape for GET /api/raptor-tree: the hierarchical RAPTOR tree
5
+ * (summary nodes by level) for a session. Client-safe — the raw 512-dim
6
+ * embedding vector is stripped before sending.
7
+ */
8
+
9
+ /**
10
+ * A single RAPTOR tree node (client-safe — no `embedding` vector).
11
+ * Mirrors src/store/sqlite/raptor.ts StoredRaptorNode minus the embedding.
12
+ */
13
+ export interface RaptorNodeDTO {
14
+ /** Stable node id (persisted in raptor_nodes.id). */
15
+ readonly id: string;
16
+ /** Session this node belongs to. */
17
+ readonly sessionId: string;
18
+ /** Tree depth level (0 = root summary, higher = more granular). */
19
+ readonly level: number;
20
+ /** Parent node id, or null for the root of the tree. */
21
+ readonly parentId: string | null;
22
+ /** Child node ids. */
23
+ readonly children: string[];
24
+ /** Summarized content of this cluster. */
25
+ readonly summary: string;
26
+ /** Quality marker of the summary (e.g. "high" | "med" | "low"). */
27
+ readonly qualityMarker: string;
28
+ /** Estimated token count of the summary. */
29
+ readonly tokenEstimate: number;
30
+ /** Epoch ms when the tree containing this node was built. */
31
+ readonly builtAt: number;
32
+ }
33
+
34
+ /**
35
+ * Response body for GET /api/raptor-tree.
36
+ * `empty: true` when no RAPTOR nodes exist for the resolved session.
37
+ */
38
+ export interface RaptorTreeResponse {
39
+ /** Flat list of all nodes (tree shape recoverable via parentId/children). */
40
+ readonly nodes: RaptorNodeDTO[];
41
+ /** Max level across nodes (0 when empty). */
42
+ readonly levels: number;
43
+ /** The root node id (node with parentId === null), or null when empty. */
44
+ readonly rootId: string | null;
45
+ /** Max builtAt across nodes (epoch ms), or null when empty. */
46
+ readonly builtAt: number | null;
47
+ /** True when there are no RAPTOR nodes for the resolved session. */
48
+ readonly empty: boolean;
49
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared runtime-helpers for the api-contracts split test files.
3
+ *
4
+ * Extracted from api-contracts.test.ts so each per-endpoint describe file can
5
+ * reuse the same field/type assertion primitives without duplicating them.
6
+ * Only runtime helpers live here — compile-time `satisfies` checks and the
7
+ * ENDPOINTS registry assertions stay in endpoints-registry.test.ts.
8
+ */
9
+ import assert from "node:assert/strict";
10
+
11
+ /** Primitive type name from a JSON-parsed value (matches typeof for JSON types). */
12
+ export function primitiveType(v: unknown): string {
13
+ if (v === null) return "null";
14
+ if (Array.isArray(v)) return "array";
15
+ return typeof v;
16
+ }
17
+
18
+ /** Assert that a field exists on an object and has one of the expected primitive types. */
19
+ export function assertField(
20
+ obj: Record<string, unknown>,
21
+ field: string,
22
+ expected: string[],
23
+ ): void {
24
+ assert.ok(field in obj, `field "${field}" must exist`);
25
+ const actual = primitiveType(obj[field]);
26
+ assert.ok(
27
+ expected.includes(actual),
28
+ `field "${field}" expected type ${expected.join("|")}, got ${actual}`,
29
+ );
30
+ }
31
+
32
+ /** Assert that a nested object field exists and is an object (or null if allowed). */
33
+ export function assertObject(
34
+ obj: Record<string, unknown>,
35
+ field: string,
36
+ allowNull: boolean = false,
37
+ ): Record<string, unknown> | null {
38
+ assert.ok(field in obj, `field "${field}" must exist`);
39
+ const val = obj[field];
40
+ if (allowNull && val === null) return null;
41
+ assert.equal(
42
+ primitiveType(val),
43
+ "object",
44
+ `field "${field}" must be an object${allowNull ? " or null" : ""}`,
45
+ );
46
+ return val as Record<string, unknown>;
47
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Shared harness + types for the routes-cache split files.
3
+ * Extracted from routes-cache.test.ts: spawn-and-fetch server harness + DTOs.
4
+ */
5
+ import { mkdtempSync, rmSync, readFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { spawn } from "node:child_process";
9
+
10
+ const SERVER_ENTRY = new URL("../server.js", import.meta.url).pathname;
11
+
12
+ export function waitFor(
13
+ cond: () => boolean | Promise<boolean>,
14
+ timeoutMs = 6000,
15
+ ): Promise<void> {
16
+ const start = Date.now();
17
+ return new Promise((resolve, reject) => {
18
+ const tick = async () => {
19
+ if (await cond()) return resolve();
20
+ if (Date.now() - start > timeoutMs) return reject(new Error("timeout"));
21
+ setTimeout(tick, 50);
22
+ };
23
+ tick();
24
+ });
25
+ }
26
+
27
+ export function freshDir(prefix: string): string {
28
+ return mkdtempSync(join(tmpdir(), prefix));
29
+ }
30
+
31
+ export async function withServer<T>(
32
+ port: string,
33
+ dir: string,
34
+ fn: (port: number) => Promise<T>,
35
+ ): Promise<T> {
36
+ process.env.MEGACOMPACT_DASHBOARD_PORT = port;
37
+ const child = spawn(process.execPath, [SERVER_ENTRY, dir], { stdio: "ignore" });
38
+ try {
39
+ await waitFor(async () => {
40
+ try {
41
+ const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
42
+ const res = await fetch(`http://localhost:${raw.port}/api/version`);
43
+ return res.ok;
44
+ } catch {
45
+ return false;
46
+ }
47
+ });
48
+ const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
49
+ return await fn(raw.port);
50
+ } finally {
51
+ child.kill("SIGTERM");
52
+ delete process.env.MEGACOMPACT_DASHBOARD_PORT;
53
+ rmSync(dir, { recursive: true, force: true });
54
+ }
55
+ }
56
+
57
+ export interface ProviderCacheByModel {
58
+ model: string;
59
+ hitPct: number;
60
+ totalCacheRead: number;
61
+ totalCacheWrite: number;
62
+ sampleCount: number;
63
+ }
64
+
65
+ export interface ProviderCacheResponse {
66
+ cache: {
67
+ avgHitPct: number;
68
+ turnCount: number;
69
+ totalCacheRead: number;
70
+ totalCacheWrite: number;
71
+ totalInput: number;
72
+ firstTurnAt: string | null;
73
+ latestTurnAt: string | null;
74
+ byModel: ProviderCacheByModel[];
75
+ };
76
+ savings: {
77
+ cacheReadSaved: number;
78
+ cacheWriteCost: number;
79
+ netSaved: number;
80
+ model: string;
81
+ inputRate: number;
82
+ } | null;
83
+ updatedAt: string;
84
+ windowMinutes?: number | null;
85
+ prefixBreaks: Array<{
86
+ id: number;
87
+ ts: number;
88
+ cause: string;
89
+ confidence: number;
90
+ prevHitPct: number;
91
+ currHitPct: number;
92
+ breakAt: number;
93
+ }>;
94
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * dashboard-server/routes-embedder-health.ts — Embedder health probe route.
3
+ *
4
+ * GET /api/embedder-health — Round-trips a test embed through the active
5
+ * embedder and reports reachability + latency + dimensions.
6
+ *
7
+ * PREVENT-PI-004: probes the active embedder, which is either the built-in
8
+ * TrigramEmbedder (pure local math) or the HttpEmbedder pointed at a
9
+ * user-spawned localhost server — the existing audited loopback exception
10
+ * (see httpEmbedder.ts). No new network paths are introduced here.
11
+ */
12
+
13
+ import { performance } from "node:perf_hooks";
14
+ import type { IncomingMessage, ServerResponse } from "node:http";
15
+ import type { RouteContext } from "./routes-core.js";
16
+ import { detectCurrentEmbedder } from "./routes-setup.js";
17
+ import { defaultEmbedder } from "../../src/embedder.js";
18
+ import type { EmbedderHealthResponse } from "./api-contracts/embedder-health.js";
19
+
20
+ /** Mask an embedding URL down to scheme://hostname:port, stripping path +
21
+ * any embedded credentials. Returns null when the env URL is unset/empty. */
22
+ function maskEmbeddingUrl(): string | null {
23
+ const raw = process.env["MEGACOMPACT_EMBEDDING_URL"];
24
+ if (!raw || raw.trim().length === 0) return null;
25
+ try {
26
+ const u = new URL(raw);
27
+ return `${u.protocol}//${u.hostname}${u.port ? `:${u.port}` : ""}`;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ export function handleEmbedderHealth(
34
+ req: IncomingMessage,
35
+ res: ServerResponse,
36
+ _ctx: RouteContext,
37
+ ): boolean {
38
+ if (req.url !== "/api/embedder-health") return false;
39
+ if (req.method !== "GET") {
40
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
41
+ res.writeHead(405, { "Content-Type": "application/json" });
42
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
43
+ return true;
44
+ }
45
+
46
+ const activeEmbedder = detectCurrentEmbedder();
47
+ const url = maskEmbeddingUrl();
48
+
49
+ let status: "ok" | "unreachable" | "error" = "ok";
50
+ let dim = 0;
51
+ let error: string | undefined;
52
+ const start = performance.now();
53
+ try {
54
+ const embedder = defaultEmbedder();
55
+ embedder.embed("mega-compact health probe");
56
+ dim = embedder.dim;
57
+ } catch (e) {
58
+ const msg = e instanceof Error ? e.message : String(e);
59
+ status = /unreachable/i.test(msg) ? "unreachable" : "error";
60
+ error = msg;
61
+ }
62
+ const latencyMs = performance.now() - start;
63
+
64
+ const body: EmbedderHealthResponse = {
65
+ activeEmbedder,
66
+ status,
67
+ latencyMs,
68
+ dim,
69
+ url,
70
+ ...(error !== undefined ? { error } : {}),
71
+ };
72
+
73
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
74
+ res.writeHead(200, { "Content-Type": "application/json" });
75
+ res.end(JSON.stringify(body));
76
+ return true;
77
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * routes-raptor.ts — GET /api/raptor-tree route handler (Part B).
3
+ *
4
+ * Returns the hierarchical RAPTOR tree (summary nodes by level) for a session.
5
+ * When no sessionId is given, resolves to the most recent session that has
6
+ * raptor nodes. Reads only from the local SQLite store (PREVENT-PI-004 OK).
7
+ * Uses an inline require to the pi-agnostic src/store/sqlite/raptor.ts reader.
8
+ */
9
+ import { createRequire } from "node:module";
10
+ import type { ServerResponse, IncomingMessage } from "node:http";
11
+ import { parse as parseUrl } from "node:url";
12
+ import type { RouteContext } from "./routes-core.js";
13
+ import type { RaptorTreeResponse, RaptorNodeDTO } from "./api-contracts/raptor.js";
14
+ import { openStore } from "../../src/store/sqlite.js";
15
+
16
+ const _req = createRequire(import.meta.url);
17
+
18
+ // guardrails-allow PREVENT-PI-004: local loopback-only dashboard endpoint — reads the local SQLite store for RAPTOR tree data, zero remote network calls.
19
+
20
+ /** Full node shape returned by listRaptorNodes (includes the embedding vector). */
21
+ type StoredRaptorNode = {
22
+ id: string;
23
+ sessionId: string;
24
+ level: number;
25
+ parentId: string | null;
26
+ children: string[];
27
+ summary: string;
28
+ embedding: number[];
29
+ qualityMarker: string;
30
+ tokenEstimate: number;
31
+ builtAt: number;
32
+ };
33
+
34
+ /** Find the most recent sessionId that has raptor nodes (else null). */
35
+ function latestRaptorSession(stateDir: string): string | null {
36
+ try {
37
+ const db = openStore(stateDir);
38
+ const row = db
39
+ .prepare(
40
+ "SELECT DISTINCT session_id FROM raptor_nodes ORDER BY built_at DESC LIMIT 1",
41
+ )
42
+ .get() as { session_id: string } | undefined;
43
+ return row?.session_id ?? null;
44
+ } catch {
45
+ // no raptor_nodes table yet (fresh DB) — treat as empty
46
+ return null;
47
+ }
48
+ }
49
+
50
+ function toDTO(node: StoredRaptorNode): RaptorNodeDTO {
51
+ return {
52
+ id: node.id,
53
+ sessionId: node.sessionId,
54
+ level: node.level,
55
+ parentId: node.parentId,
56
+ children: node.children,
57
+ summary: node.summary,
58
+ qualityMarker: node.qualityMarker,
59
+ tokenEstimate: node.tokenEstimate,
60
+ builtAt: node.builtAt,
61
+ };
62
+ }
63
+
64
+ function emptyResponse(): RaptorTreeResponse {
65
+ return { nodes: [], levels: 0, rootId: null, builtAt: null, empty: true };
66
+ }
67
+
68
+ export function handleRaptorTree(
69
+ req: IncomingMessage,
70
+ res: ServerResponse,
71
+ ctx: RouteContext,
72
+ ): boolean {
73
+ if (req.method !== "GET") return false;
74
+ const parsed = parseUrl(req.url ?? "", true);
75
+ if (parsed.pathname !== "/api/raptor-tree") return false;
76
+
77
+ try {
78
+ const { listRaptorNodes } = _req(
79
+ "../../src/store/sqlite/raptor.js",
80
+ ) as {
81
+ listRaptorNodes: (
82
+ sessionId: string,
83
+ stateDir: string,
84
+ ) => StoredRaptorNode[];
85
+ };
86
+
87
+ let sessionId =
88
+ typeof parsed.query.sessionId === "string" ? parsed.query.sessionId : "";
89
+
90
+ if (!sessionId) {
91
+ sessionId = latestRaptorSession(ctx.stateDir) ?? "";
92
+ if (!sessionId) {
93
+ const body = emptyResponse();
94
+ res.writeHead(200, { "Content-Type": "application/json" });
95
+ res.end(JSON.stringify(body));
96
+ return true;
97
+ }
98
+ }
99
+
100
+ const nodes = listRaptorNodes(sessionId, ctx.stateDir);
101
+ if (nodes.length === 0) {
102
+ const body = emptyResponse();
103
+ res.writeHead(200, { "Content-Type": "application/json" });
104
+ res.end(JSON.stringify(body));
105
+ return true;
106
+ }
107
+
108
+ const dtos: RaptorNodeDTO[] = nodes.map(toDTO);
109
+ const levels = dtos.reduce((m, n) => Math.max(m, n.level), 0);
110
+ const root = dtos.find((n) => n.parentId === null);
111
+ const builtAt = dtos.reduce((m, n) => Math.max(m, n.builtAt), 0);
112
+ const body: RaptorTreeResponse = {
113
+ nodes: dtos,
114
+ levels,
115
+ rootId: root?.id ?? null,
116
+ builtAt,
117
+ empty: false,
118
+ };
119
+
120
+ res.writeHead(200, { "Content-Type": "application/json" });
121
+ res.end(JSON.stringify(body));
122
+ return true;
123
+ } catch (e) {
124
+ res.writeHead(500, { "Content-Type": "application/json" });
125
+ res.end(JSON.stringify({ error: String(e) }));
126
+ return true;
127
+ }
128
+ }
@@ -28,7 +28,7 @@ import type {
28
28
  // handleSetupStatus — "/api/setup-status"
29
29
  // ---------------------------------------------------------------------------
30
30
 
31
- function detectCurrentEmbedder(): SetupStatusResponse["currentEmbedder"] {
31
+ export function detectCurrentEmbedder(): SetupStatusResponse["currentEmbedder"] {
32
32
  const url = process.env["MEGACOMPACT_EMBEDDING_URL"];
33
33
  const minilm = process.env["MEGACOMPACT_MINILM"];
34
34
  if (url && url.trim().length > 0) return "http";
@@ -23,5 +23,7 @@ export { handleProviderCache } from "./routes-cache.js";
23
23
  export { handleMemoryStatus } from "./routes-memory.js";
24
24
  export { handleSetupStatus, handleSetupDetect, handleSetupConfigure } from "./routes-setup.js";
25
25
  export { handleMemoryMap } from "./routes-memory-map.js";
26
+ export { handleRaptorTree } from "./routes-raptor.js";
26
27
  export { handleCacheStripes } from "./routes-cache.js";
27
28
  export { handleContextHealth, handleCachePoison, handleHealthSettings } from "./routes-health.js";
29
+ export { handleEmbedderHealth } from "./routes-embedder-health.js";
@@ -44,9 +44,11 @@ import {
44
44
  handleSetupDetect,
45
45
  handleSetupConfigure,
46
46
  handleMemoryMap,
47
+ handleRaptorTree,
47
48
  handleContextHealth,
48
49
  handleCachePoison,
49
50
  handleHealthSettings,
51
+ handleEmbedderHealth,
50
52
  handleStatic,
51
53
  } from "./routes.js";
52
54
 
@@ -242,9 +244,11 @@ export async function launchDashboardServer(
242
244
  if (handleSetupDetect(req, res, ctx)) return;
243
245
  if (handleSetupConfigure(req, res, ctx)) return;
244
246
  if (handleMemoryMap(req, res, ctx)) return;
247
+ if (handleRaptorTree(req, res, ctx)) return;
245
248
  if (handleContextHealth(req, res, ctx)) return;
246
249
  if (handleCachePoison(req, res, ctx)) return;
247
250
  if (handleHealthSettings(req, res, ctx)) return;
251
+ if (handleEmbedderHealth(req, res, ctx)) return;
248
252
  handleStatic(req, res, ctx);
249
253
  });
250
254
 
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Shared helpers for the mega-cache-replay.test split files.
3
+ * Extracted from extensions/mega-cache-replay.test.ts: baseTmp, env, harness().
4
+ */
5
+ import { mkdtempSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { createRequire } from "node:module";
9
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
10
+
11
+ export const baseTmp = mkdtempSync(join(tmpdir(), "mc-cache-"));
12
+
13
+ let counter = 0;
14
+
15
+ export function setupEnv(): void {
16
+ process.env.MEGACOMPACT_INDEX_DIR = join(baseTmp, "index");
17
+ process.env.MEGACOMPACT_PGLITE_DISABLED = "true";
18
+ }
19
+
20
+ export function harness() {
21
+ const stateDir = join(baseTmp, `run-${counter++}`);
22
+ process.env.MEGACOMPACT_STATE_DIR = stateDir;
23
+ process.env.MEGACOMPACT_DEBUG = "true";
24
+ process.env.MEGACOMPACT_THRESHOLD_TOKENS = "50";
25
+ process.env.MEGACOMPACT_FAST_GATE_PCT = "1";
26
+ process.env.MEGACOMPACT_ANCHOR_USER_MESSAGES = "1";
27
+ process.env.MEGACOMPACT_DURABLE_TRIM_FLOOR = "0";
28
+ process.env.MEGACOMPACT_MEMORY_AUTO_REVIEW = "false";
29
+ process.env.MEGACOMPACT_RAPTOR_ENABLED = "false";
30
+ process.env.MEGACOMPACT_L1_ENABLED = "false";
31
+ process.env.MEGACOMPACT_L2_ENABLED = "false";
32
+ delete process.env.MEGACOMPACT_LEGACY_DURABLE_TRIM;
33
+
34
+ const usage = {
35
+ tokens: 200000,
36
+ contextWindow: 200000,
37
+ percent: 100 as number | null,
38
+ };
39
+
40
+ const handlers: Record<string, Function[]> = {};
41
+ const compactCalls: any[] = [];
42
+
43
+ function msg(role: string, text: string, toolName?: string): AgentMessage {
44
+ if (role === "assistant" && toolName) {
45
+ return {
46
+ role: "assistant",
47
+ content: [
48
+ { type: "toolCall", name: toolName, id: "c1", arguments: {} },
49
+ ],
50
+ api: "anthropic-messages",
51
+ provider: "anthropic",
52
+ model: "m",
53
+ usage: {
54
+ inputTokens: 1,
55
+ outputTokens: 1,
56
+ cacheReadTokens: 0,
57
+ cacheWriteTokens: 0,
58
+ },
59
+ stopReason: "tool_use",
60
+ timestamp: 0,
61
+ } as unknown as AgentMessage;
62
+ }
63
+ if (role === "toolResult" && toolName) {
64
+ return {
65
+ role: "toolResult",
66
+ content: [{ type: "text", text }],
67
+ toolCallId: "c1",
68
+ toolName,
69
+ isError: false,
70
+ timestamp: 0,
71
+ } as unknown as AgentMessage;
72
+ }
73
+ return {
74
+ role: "user",
75
+ content: text,
76
+ timestamp: 0,
77
+ } as unknown as AgentMessage;
78
+ }
79
+
80
+ function buildSession(tag: string, n: number): AgentMessage[] {
81
+ const s: AgentMessage[] = [];
82
+ for (let i = 0; i < n; i++) {
83
+ s.push(
84
+ msg("user", `[${tag}] we decided to use approach ${i} for module ${i}`),
85
+ );
86
+ s.push(msg("assistant", `[${tag}] edited module ${i}`, "Edit"));
87
+ s.push(msg("toolResult", `[${tag}] edited module ${i}`, "Edit"));
88
+ }
89
+ return s;
90
+ }
91
+
92
+ const toEntry = (m: AgentMessage, i: number): any => ({
93
+ type: "message",
94
+ id: `e${i}`,
95
+ parentId: null,
96
+ timestamp: String(i),
97
+ message: m,
98
+ });
99
+ const sessionManager = {
100
+ getSessionId: () => "sess_cache_001",
101
+ getEntries: () => buildSession("A", 14).map(toEntry),
102
+ getBranch: () => buildSession("A", 14).map(toEntry),
103
+ };
104
+
105
+ function makeCtx(over: Partial<any> = {}) {
106
+ return {
107
+ ui: {
108
+ setStatus: () => {},
109
+ notify: () => {},
110
+ select: () => {},
111
+ confirm: async () => true,
112
+ input: async () => "",
113
+ setWidget: () => {},
114
+ },
115
+ mode: "tui" as any,
116
+ hasUI: true,
117
+ cwd: stateDir,
118
+ sessionManager,
119
+ modelRegistry: {} as any,
120
+ model: undefined,
121
+ isIdle: () => true,
122
+ isProjectTrusted: () => true,
123
+ signal: undefined,
124
+ abort: () => {},
125
+ hasPendingMessages: () => false,
126
+ shutdown: () => {},
127
+ getContextUsage: () => ({ ...usage }),
128
+ compact: (opts?: any) => {
129
+ compactCalls.push(opts);
130
+ return undefined;
131
+ },
132
+ getSystemPrompt: () => "system base",
133
+ ...over,
134
+ } as any;
135
+ }
136
+
137
+ const pi = {
138
+ on: (ev: string, h: Function) => {
139
+ if (!handlers[ev]) handlers[ev] = [];
140
+ handlers[ev].push(h);
141
+ },
142
+ registerCommand: () => {},
143
+ registerTool: () => {},
144
+ registerShortcut: () => {},
145
+ registerFlag: () => {},
146
+ getFlag: () => undefined,
147
+ registerMessageRenderer: () => {},
148
+ registerEntryRenderer: () => {},
149
+ sendMessage: () => {},
150
+ sendUserMessage: () => {},
151
+ appendEntry: () => {},
152
+ setSessionName: () => {},
153
+ getSessionName: () => undefined,
154
+ setLabel: () => {},
155
+ exec: async () => ({ stdout: "", stderr: "", code: 0 }),
156
+ getActiveTools: () => [],
157
+ getAllTools: () => [],
158
+ setActiveTools: () => {},
159
+ getCommands: () => [],
160
+ setModel: async () => false,
161
+ getThinkingLevel: () => "off" as any,
162
+ setThinkingLevel: () => {},
163
+ } as any;
164
+
165
+ const require = createRequire(import.meta.url);
166
+ const mod = require("../mega-compact.js") as { default: (p: any) => void };
167
+ mod.default(pi);
168
+ const { lastRuntime } = require("../mega-events.js") as { lastRuntime: any };
169
+
170
+ const fire = async (ev: string, event: any, ctx: any) => {
171
+ let r: any;
172
+ for (const h of handlers[ev] || []) r = await h(event, ctx);
173
+ return r;
174
+ };
175
+ return {
176
+ stateDir,
177
+ handlers,
178
+ compactCalls,
179
+ fire,
180
+ ctx: makeCtx,
181
+ usage,
182
+ buildSession,
183
+ runtime: lastRuntime,
184
+ clearDebounce: () => {
185
+ if (lastRuntime) lastRuntime.debounceUntil = 0;
186
+ },
187
+ };
188
+ }