pi-mega-compact 0.11.5 → 0.11.6

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 (145) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/cache-stripes.js +8 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +13 -0
  3. package/dist/extensions/dashboard-server/api-contracts/memory-map.js +1 -0
  4. package/dist/extensions/dashboard-server/routes-cache.js +28 -0
  5. package/dist/extensions/dashboard-server/routes-game.js +3 -0
  6. package/dist/extensions/dashboard-server/routes-memory-map.js +50 -0
  7. package/dist/extensions/dashboard-server/routes-setup.js +105 -0
  8. package/dist/extensions/dashboard-server/routes.js +3 -1
  9. package/dist/extensions/dashboard-server/server.js +7 -1
  10. package/dist/extensions/mega-compact.js +5 -0
  11. package/dist/extensions/mega-config.js +5 -0
  12. package/dist/extensions/mega-events/context-handler.js +10 -2
  13. package/dist/extensions/mega-events/perf-handler.js +83 -1
  14. package/dist/extensions/mega-events/separated-prompt.js +248 -0
  15. package/dist/extensions/mega-runtime/env-loader.js +56 -0
  16. package/dist/extensions/mega-runtime/reset-runtime.js +1 -0
  17. package/dist/extensions/mega-runtime/runtime-snapshot.js +1 -0
  18. package/dist/extensions/mega-runtime/runtime.js +1 -0
  19. package/dist/extensions/mega-runtime/snapshot.js +1 -0
  20. package/dist/extensions/mega-runtime/widget.js +5 -0
  21. package/dist/src/cache-stripe-impl.js +288 -0
  22. package/dist/src/cache-stripe.js +11 -0
  23. package/dist/src/config/stopwords.js +24 -0
  24. package/dist/src/config.js +17 -0
  25. package/dist/src/memoryGraph.js +299 -0
  26. package/dist/src/monitoring.js +13 -0
  27. package/dist/src/queryExpansion.js +163 -0
  28. package/dist/src/queryReformulation/cache.js +123 -0
  29. package/dist/src/queryReformulation/rrf.js +29 -0
  30. package/dist/src/queryReformulation/tfidf.js +61 -0
  31. package/dist/src/queryReformulation/vagueness.js +33 -0
  32. package/dist/src/queryReformulation.js +225 -0
  33. package/dist/src/recallMetrics.js +258 -0
  34. package/dist/src/store/sqlite/cache-stripes.js +133 -0
  35. package/dist/src/store/sqlite/fts5-search.js +68 -0
  36. package/dist/src/store/sqlite/perf-samples.js +1 -0
  37. package/dist/src/store/sqlite/schema.js +60 -1
  38. package/dist/src/store/sqlite.js +1 -0
  39. package/dist/src/store/vectorIndex.js +29 -5
  40. package/dist/src/tiered-router/types.js +18 -0
  41. package/dist/src/tieredRouter.js +421 -0
  42. package/dist/src/vectorStore.js +8 -6
  43. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BoL7qBOJ.js → AchievementTiles-DR9cLGDL.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BoL7qBOJ.js.map → AchievementTiles-DR9cLGDL.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BFDE3HYc.js → AchievementsTab-svNUgJ1e.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BFDE3HYc.js.map → AchievementsTab-svNUgJ1e.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js +2 -0
  48. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js.map +1 -0
  49. package/extensions/dashboard-client/dist/assets/{ConfigTab-DDFE-ckA.js → ConfigTab-CmmWheuW.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{ConfigTab-DDFE-ckA.js.map → ConfigTab-CmmWheuW.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/{EventsTab-B6UxZ7PM.js → EventsTab-CFE7jLsa.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{EventsTab-B6UxZ7PM.js.map → EventsTab-CFE7jLsa.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/GameTab-CJAX_th_.js +2 -0
  54. package/extensions/dashboard-client/dist/assets/{GameTab-DehUX-KE.js.map → GameTab-CJAX_th_.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-0GWRt4wn.js → MaintenanceTab-P4SUsvMf.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-0GWRt4wn.js.map → MaintenanceTab-P4SUsvMf.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +2 -0
  58. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +1 -0
  59. package/extensions/dashboard-client/dist/assets/{MetricsTab-C3rwMVsN.js → MetricsTab-CHQscixy.js} +2 -2
  60. package/extensions/dashboard-client/dist/assets/{MetricsTab-C3rwMVsN.js.map → MetricsTab-CHQscixy.js.map} +1 -1
  61. package/extensions/dashboard-client/dist/assets/{OverviewTab-39O9_x5j.js → OverviewTab-DBs4DJdd.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{OverviewTab-39O9_x5j.js.map → OverviewTab-DBs4DJdd.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{ReposTab-Cfj6Q5sH.js → ReposTab-DeFz76L5.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{ReposTab-Cfj6Q5sH.js.map → ReposTab-DeFz76L5.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{SessionsTab-DXDTCi3r.js → SessionsTab-BWNsj1OB.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{SessionsTab-DXDTCi3r.js.map → SessionsTab-BWNsj1OB.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js +2 -0
  68. package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js.map +1 -0
  69. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cw1dJ4e9.js → TimeSavedCard-CiqCOQuL.js} +2 -2
  70. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cw1dJ4e9.js.map → TimeSavedCard-CiqCOQuL.js.map} +1 -1
  71. package/extensions/dashboard-client/dist/assets/{TopicsTab-BAw972BV.js → TopicsTab-BWWQxltv.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{TopicsTab-BAw972BV.js.map → TopicsTab-BWWQxltv.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cf5mGIdl.js → TurnsTab-CVz4f4-o.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cf5mGIdl.js.map → TurnsTab-CVz4f4-o.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +1 -0
  76. package/extensions/dashboard-client/dist/assets/{index-4gQ7auad.js → index-DrktNOoT.js} +11 -11
  77. package/extensions/dashboard-client/dist/assets/index-DrktNOoT.js.map +1 -0
  78. package/extensions/dashboard-client/dist/assets/{useSSE-DNgMuyP6.js → useSSE-Bf5LOLSi.js} +2 -2
  79. package/extensions/dashboard-client/dist/assets/{useSSE-DNgMuyP6.js.map → useSSE-Bf5LOLSi.js.map} +1 -1
  80. package/extensions/dashboard-client/dist/index.html +2 -2
  81. package/extensions/dashboard-client/src/App.tsx +24 -8
  82. package/extensions/dashboard-client/src/api/client.ts +15 -0
  83. package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +100 -0
  84. package/extensions/dashboard-client/src/components/StripeDistributionCard.tsx +110 -0
  85. package/extensions/dashboard-client/src/components/TabBar.tsx +49 -4
  86. package/extensions/dashboard-client/src/main.tsx +1 -0
  87. package/extensions/dashboard-client/src/styles/base.css +30 -0
  88. package/extensions/dashboard-client/src/styles/cache.css +167 -0
  89. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +75 -13
  90. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +440 -0
  91. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +65 -2
  92. package/extensions/dashboard-server/api-contracts/cache-stripes.ts +49 -0
  93. package/extensions/dashboard-server/api-contracts/endpoints.ts +31 -1
  94. package/extensions/dashboard-server/api-contracts/index.ts +16 -1
  95. package/extensions/dashboard-server/api-contracts/memory-map.ts +50 -0
  96. package/extensions/dashboard-server/api-contracts/setup.ts +22 -0
  97. package/extensions/dashboard-server/routes-cache.ts +36 -1
  98. package/extensions/dashboard-server/routes-game.ts +3 -0
  99. package/extensions/dashboard-server/routes-memory-map.ts +105 -0
  100. package/extensions/dashboard-server/routes-setup.ts +112 -0
  101. package/extensions/dashboard-server/routes.ts +3 -1
  102. package/extensions/dashboard-server/server.ts +6 -0
  103. package/extensions/mega-compact.ts +5 -0
  104. package/extensions/mega-config.ts +17 -0
  105. package/extensions/mega-events/context-handler.ts +10 -2
  106. package/extensions/mega-events/perf-handler.ts +111 -1
  107. package/extensions/mega-events/separated-prompt.ts +329 -0
  108. package/extensions/mega-runtime/env-loader.ts +55 -0
  109. package/extensions/mega-runtime/helpers.ts +2 -0
  110. package/extensions/mega-runtime/reset-runtime.ts +1 -0
  111. package/extensions/mega-runtime/runtime-snapshot.ts +1 -0
  112. package/extensions/mega-runtime/runtime.ts +1 -0
  113. package/extensions/mega-runtime/snapshot.ts +6 -0
  114. package/extensions/mega-runtime/widget-types.ts +3 -0
  115. package/extensions/mega-runtime/widget.ts +5 -0
  116. package/package.json +1 -1
  117. package/src/cache-stripe-impl.ts +385 -0
  118. package/src/cache-stripe.ts +18 -0
  119. package/src/config/stopwords.ts +25 -0
  120. package/src/config.ts +26 -0
  121. package/src/memoryGraph.ts +438 -0
  122. package/src/monitoring.ts +35 -0
  123. package/src/queryExpansion.ts +206 -0
  124. package/src/queryReformulation/cache.ts +169 -0
  125. package/src/queryReformulation/rrf.ts +51 -0
  126. package/src/queryReformulation/tfidf.ts +79 -0
  127. package/src/queryReformulation/vagueness.ts +38 -0
  128. package/src/queryReformulation.ts +355 -0
  129. package/src/recallMetrics.ts +388 -0
  130. package/src/store/sqlite/cache-stripes.ts +196 -0
  131. package/src/store/sqlite/fts5-search.ts +90 -0
  132. package/src/store/sqlite/perf-samples.ts +3 -1
  133. package/src/store/sqlite/schema.ts +60 -1
  134. package/src/store/sqlite.ts +1 -0
  135. package/src/store/vectorIndex.ts +38 -7
  136. package/src/tiered-router/types.ts +64 -0
  137. package/src/tieredRouter.ts +489 -0
  138. package/src/vectorStore.ts +9 -5
  139. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +0 -2
  140. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +0 -1
  141. package/extensions/dashboard-client/dist/assets/GameTab-DehUX-KE.js +0 -2
  142. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +0 -2
  143. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +0 -1
  144. package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +0 -1
  145. package/extensions/dashboard-client/dist/assets/index-BEdKlpAB.css +0 -1
@@ -0,0 +1,49 @@
1
+ /**
2
+ * api-contracts/cache-stripes.ts — Cache stripe API contract (A3, PLAN_V2 Phase 4).
3
+ *
4
+ * Response shape for GET /api/cache-stripes: per-stripe distribution counts,
5
+ * average stability, and a top-level health score. Loopback-only data;
6
+ * zero network (PREVENT-PI-004).
7
+ */
8
+
9
+ /** A single stripe bucket with aggregate metadata. */
10
+ export interface StripeBucket {
11
+ /** Stripe index 0–4 (0=permanent, 1=epoch, 2=topic, 3=thread, 4=volatile). */
12
+ readonly stripe: number;
13
+ /** Human-readable label for this stripe. */
14
+ readonly label: string;
15
+ /** Number of chunks assigned to this stripe. */
16
+ readonly count: number;
17
+ /** Average stability score (0.0–1.0) within this stripe. */
18
+ readonly avgStability: number;
19
+ /** Minimum stability in this stripe. */
20
+ readonly minStability: number;
21
+ /** Maximum stability in this stripe. */
22
+ readonly maxStability: number;
23
+ }
24
+
25
+ /** Overall cache health assessment. */
26
+ export interface CacheHealthScore {
27
+ /** Composite health score 0.0–1.0 (1.0 = perfect). */
28
+ readonly score: number;
29
+ /** Human-readable label: "good" | "fair" | "degraded" | "poor". */
30
+ readonly label: string;
31
+ /** Dominant stability tier fraction. */
32
+ readonly dominantTier: number;
33
+ /** Stripe churn rate (0.0–1.0) — fraction of assignments changed since last epoch. */
34
+ readonly churnRate: number;
35
+ }
36
+
37
+ /** Response body for GET /api/cache-stripes. */
38
+ export interface CacheStripesResponse {
39
+ /** Per-stripe bucket arrays (always 5 entries, stripes 0–4). */
40
+ readonly buckets: StripeBucket[];
41
+ /** Overall health assessment. */
42
+ readonly health: CacheHealthScore;
43
+ /** Epoch id these assignments belong to. */
44
+ readonly epochId: string | null;
45
+ /** Timestamp (epoch ms) of the last refresh. */
46
+ readonly lastRefreshAt: number;
47
+ /** Total chunk count across all stripes. */
48
+ readonly totalChunks: number;
49
+ }
@@ -16,7 +16,7 @@ import type { GameStateResponse } from "./game.js";
16
16
  import type { SseEvent } from "./index.js";
17
17
  import type { ProviderCacheResponse } from "./provider-cache.js";
18
18
  import type { MemoryStatusResponse } from "./memory.js";
19
- import type { SetupStatusResponse, SetupDetectResponse } from "./setup.js";
19
+ import type { SetupStatusResponse, SetupDetectResponse, SetupConfigureRequest, SetupConfigureResponse } from "./setup.js";
20
20
  import type {
21
21
  TurnsResponse,
22
22
  ConversationTurnsResponse,
@@ -36,6 +36,8 @@ import type {
36
36
  SchemaHealthResponse,
37
37
  } from "./maintenance.js";
38
38
 
39
+ import type { CacheStripesResponse } from "./cache-stripes.js";
40
+
39
41
  import type {
40
42
  GameScoreRow,
41
43
  GameScoresQuery,
@@ -300,6 +302,14 @@ export interface PerfLatest {
300
302
  readonly n: number;
301
303
  }
302
304
 
305
+ /** A single cache hit rate sample with timestamp. */
306
+ export interface CacheHitSample {
307
+ /** Cache hit percentage (0–100). */
308
+ readonly pct: number;
309
+ /** Epoch ms timestamp of the sample. */
310
+ readonly ts: number;
311
+ }
312
+
303
313
  /** Average + latest statistics for cache hit percentage. */
304
314
  export interface PerfCacheHit {
305
315
  /** Average cache hit percentage (0–100). */
@@ -308,6 +318,8 @@ export interface PerfCacheHit {
308
318
  readonly latest: number;
309
319
  /** Number of samples in the window. */
310
320
  readonly n: number;
321
+ /** Raw cache hit samples with timestamps for time-series charting. */
322
+ readonly samples: CacheHitSample[];
311
323
  }
312
324
 
313
325
  /** Diagnostic counters from the live dashboard snapshot. */
@@ -614,6 +626,16 @@ export const ENDPOINTS = {
614
626
  "Lifetime provider prompt cache hit-rate aggregates + dollar savings estimate.",
615
627
  } as const satisfies EndpointDef<"GET", undefined, ProviderCacheResponse>,
616
628
 
629
+
630
+ // ─── A3 Cache Stripes (PLAN_V2 Phase 4) ───────────────────────────
631
+
632
+ /** GET /api/cache-stripes — Per-stripe distribution + health score. */
633
+ cacheStripes: {
634
+ method: "GET",
635
+ path: "/api/cache-stripes",
636
+ description:
637
+ "Per-stripe distribution counts, average stability scores, and composite cache health.",
638
+ } as const satisfies EndpointDef<"GET", undefined, CacheStripesResponse>,
617
639
  // ─── Memory Effectiveness (S53B) ───────────────────────────────────────
618
640
 
619
641
  /** GET /api/memory-status — Memory store aggregate statistics. */
@@ -639,4 +661,12 @@ export const ENDPOINTS = {
639
661
  description:
640
662
  "Best-effort detection of local embedder backends (ollama, llama.cpp, ONNX) available on the machine.",
641
663
  } as const satisfies EndpointDef<"GET", undefined, SetupDetectResponse>,
664
+
665
+ /** POST /api/setup-configure — Write embedder config to .mega-compact.env. */
666
+ setupConfigure: {
667
+ method: "POST",
668
+ path: "/api/setup-configure",
669
+ description:
670
+ "Write the chosen embedder configuration to .mega-compact.env (loaded at next startup). Returns whether a restart is required.",
671
+ } as const satisfies EndpointDef<"POST", SetupConfigureRequest, SetupConfigureResponse>,
642
672
  } as const;
@@ -208,4 +208,19 @@ export type {
208
208
  export type { MemoryStatusResponse, MemoryStatsTopMemory } from "./memory.js";
209
209
 
210
210
  // Setup wizard — embedder configuration + backend detection
211
- export type { SetupStatusResponse, SetupDetectResponse, DetectResult, OllamaDetectResult } from "./setup.js";
211
+ export type { SetupStatusResponse, SetupDetectResponse, SetupConfigureRequest, SetupConfigureResponse, DetectResult, OllamaDetectResult } from "./setup.js";
212
+
213
+ // S46 — memory graph (visual memory map)
214
+ export type {
215
+ MemoryMapResponse,
216
+ MemoryMapNode,
217
+ MemoryMapEdgeEntry,
218
+ MemoryMapQuery,
219
+ } from "./memory-map.js";
220
+
221
+ // A3 — cache stripe distribution + health
222
+ export type {
223
+ StripeBucket,
224
+ CacheHealthScore,
225
+ CacheStripesResponse,
226
+ } from "./cache-stripes.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * api-contracts/memory-map.ts — Memory graph API contract (S46).
3
+ *
4
+ * Response shape for GET /api/memory-map: graph nodes, edges, and metadata.
5
+ * This is a read-only JSON payload consumed by the MemoryMapTab React component.
6
+ */
7
+ import type { GraphMetadata } from "../../../src/memoryGraph.js";
8
+
9
+ /**
10
+ * Query parameters for GET /api/memory-map.
11
+ * All fields are optional.
12
+ */
13
+ export interface MemoryMapQuery {
14
+ /** Filter to a single session ID. */
15
+ readonly sessionId?: string;
16
+ /** Cosine similarity threshold (0..1). Default 0.7. */
17
+ readonly threshold?: number;
18
+ /** Max semantic edges per node. Default 3. */
19
+ readonly maxEdgesPerNode?: number;
20
+ }
21
+
22
+ /** Response body for GET /api/memory-map. */
23
+ export interface MemoryMapResponse {
24
+ readonly nodes: MemoryMapNode[];
25
+ readonly edges: MemoryMapEdgeEntry[];
26
+ readonly metadata: GraphMetadata;
27
+ }
28
+
29
+ /** A node in the response (client-safe subset — no full embedding vector). */
30
+ export interface MemoryMapNode {
31
+ readonly id: string;
32
+ readonly sessionId: string;
33
+ readonly label: string;
34
+ readonly summaryTruncated: string;
35
+ readonly tokenEstimate: number;
36
+ readonly timestamp: number;
37
+ readonly dedupStatus: string | undefined;
38
+ readonly raptorLevel: number;
39
+ readonly topicSummary: string | undefined;
40
+ readonly decisionCount: number;
41
+ readonly textSnippet: string;
42
+ }
43
+
44
+ /** An edge entry in the response. */
45
+ export interface MemoryMapEdgeEntry {
46
+ readonly source: string;
47
+ readonly target: string;
48
+ readonly weight: number;
49
+ readonly type: "temporal" | "semantic" | "raptor_parent";
50
+ }
@@ -56,3 +56,25 @@ export interface SetupDetectResponse {
56
56
  /** Error message if detection itself failed, or null. */
57
57
  readonly error: string | null;
58
58
  }
59
+
60
+ /** Request body for POST /api/setup-configure. */
61
+ export interface SetupConfigureRequest {
62
+ /** Which embedder to configure. "trigram" clears the URL (uses built-in). */
63
+ readonly embedder: "ollama" | "llama" | "trigram";
64
+ /** Optional override URL (for non-default Ollama ports or llama.cpp). */
65
+ readonly url?: string;
66
+ }
67
+
68
+ /** Response for POST /api/setup-configure. */
69
+ export interface SetupConfigureResponse {
70
+ /** The embedder that was configured. */
71
+ readonly embedder: string;
72
+ /** The URL that was written (null for trigram). */
73
+ readonly url: string | null;
74
+ /** Path to the .mega-compact.env file that was written. */
75
+ readonly envPath: string;
76
+ /** Whether the user needs to restart pi to activate. */
77
+ readonly restartRequired: boolean;
78
+ /** The already-active embedder, if the new config matches (no restart needed). */
79
+ readonly alreadyActive: boolean;
80
+ }
@@ -196,4 +196,39 @@ export function handleProviderCache(
196
196
  } catch {
197
197
  return false;
198
198
  }
199
- }
199
+ }
200
+ // ---------------------------------------------------------------------------
201
+ // handleCacheStripes — "GET /api/cache-stripes"
202
+ // ---------------------------------------------------------------------------
203
+
204
+ export function handleCacheStripes(
205
+ req: IncomingMessage,
206
+ res: ServerResponse,
207
+ ctx: RouteContext,
208
+ ): boolean {
209
+ if (!req.url?.startsWith("/api/cache-stripes")) return false;
210
+ if (req.method !== "GET") {
211
+ res.writeHead(405, { "Content-Type": "application/json" });
212
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
213
+ return true;
214
+ }
215
+ try {
216
+ const stateDir = ctx.stateDir;
217
+ const pfReq = createRequire(import.meta.url);
218
+ const { readCacheStripes } = pfReq(
219
+ "../../src/store/sqlite/cache-stripes.js",
220
+ ) as typeof import("../../src/store/sqlite/cache-stripes.js");
221
+ const result = readCacheStripes(stateDir);
222
+ res.writeHead(200, { "Content-Type": "application/json" });
223
+ res.end(JSON.stringify(result));
224
+ } catch (e) {
225
+ res.writeHead(500, { "Content-Type": "application/json" });
226
+ res.end(
227
+ JSON.stringify({
228
+ error: "cache_stripes_unavailable",
229
+ detail: String(e),
230
+ }),
231
+ );
232
+ }
233
+ return true;
234
+ }
@@ -307,6 +307,9 @@ export function handlePerf(
307
307
  avg: avg(get("cache_hit_pct")),
308
308
  latest: latest(get("cache_hit_pct")),
309
309
  n: get("cache_hit_pct").length,
310
+ samples: rows
311
+ .filter((r) => r.kind === "cache_hit_pct")
312
+ .map((r) => ({ pct: r.value, ts: r.ts })),
310
313
  },
311
314
  db_recompute_ms: {
312
315
  p50: pct(get("db_recompute_ms"), 50),
@@ -0,0 +1,105 @@
1
+ /**
2
+ * routes-memory-map.ts — GET /api/memory-map route handler (S46).
3
+ *
4
+ * Builds a memory graph from the local SQLite store and returns it as JSON.
5
+ * Uses an inline require to the pi-agnostic src/memoryGraph.ts builder.
6
+ */
7
+ import { createRequire } from "node:module";
8
+ import type { ServerResponse, IncomingMessage } from "node:http";
9
+ import { parse as parseUrl } from "node:url";
10
+ import type { RouteContext } from "./routes-core.js";
11
+ import type { MemoryMapResponse, MemoryMapNode, MemoryMapEdgeEntry } from "./api-contracts/memory-map.js";
12
+
13
+ const _req = createRequire(import.meta.url);
14
+
15
+ // guardrails-allow PREVENT-PI-004: localhost-only dashboard API endpoint that reads from the local SQLite store — zero remote network calls.
16
+
17
+ export function handleMemoryMap(
18
+ req: IncomingMessage,
19
+ res: ServerResponse,
20
+ ctx: RouteContext,
21
+ ): boolean {
22
+ if (req.method !== "GET") return false;
23
+ const parsed = parseUrl(req.url ?? "", true);
24
+ if (parsed.pathname !== "/api/memory-map") return false;
25
+
26
+ // guardrails-allow PREVENT-PI-004: local loopback-only dashboard server — reads the local SQLite store for graph data.
27
+ const { buildMemoryGraph } = _req("../../src/memoryGraph.js") as {
28
+ buildMemoryGraph: (opts?: {
29
+ sessionId?: string;
30
+ similarityThreshold?: number;
31
+ maxEdgesPerNode?: number;
32
+ stateDir?: string;
33
+ includeRaptorEdges?: boolean;
34
+ includeTemporalEdges?: boolean;
35
+ }) => MemoryGraphReturn;
36
+ };
37
+
38
+ type MemoryGraphReturn = {
39
+ nodes: Array<{
40
+ id: string;
41
+ sessionId: string;
42
+ label: string;
43
+ summaryTruncated: string;
44
+ tokenEstimate: number;
45
+ timestamp: number;
46
+ dedupStatus: string | undefined;
47
+ raptorLevel: number;
48
+ topicSummary: string | undefined;
49
+ decisionCount: number;
50
+ textSnippet: string;
51
+ }>;
52
+ edges: Array<{
53
+ source: string;
54
+ target: string;
55
+ weight: number;
56
+ type: "temporal" | "semantic" | "raptor_parent";
57
+ }>;
58
+ metadata: {
59
+ sessionCount: number;
60
+ totalCheckpoints: number;
61
+ totalEdges: number;
62
+ semanticEdgeCount: number;
63
+ temporalEdgeCount: number;
64
+ raptorEdgeCount: number;
65
+ similarityThresholdUsed: number;
66
+ builtAt: number;
67
+ };
68
+ };
69
+
70
+ const sessionId = typeof parsed.query.sessionId === "string" ? parsed.query.sessionId : undefined;
71
+ const threshold = typeof parsed.query.threshold === "string" ? Number.parseFloat(parsed.query.threshold) : undefined;
72
+ const maxEdges = typeof parsed.query.maxEdgesPerNode === "string" ? Number.parseInt(parsed.query.maxEdgesPerNode, 10) : undefined;
73
+ const raptorEdges = parsed.query.includeRaptorEdges === "false" ? false : true;
74
+ const temporalEdges = parsed.query.includeTemporalEdges === "false" ? false : true;
75
+
76
+ const graph = buildMemoryGraph({
77
+ sessionId,
78
+ similarityThreshold: threshold,
79
+ maxEdgesPerNode: maxEdges,
80
+ stateDir: ctx.stateDir,
81
+ includeRaptorEdges: raptorEdges,
82
+ includeTemporalEdges: temporalEdges,
83
+ });
84
+
85
+ const nodes: MemoryMapNode[] = graph.nodes.map((n) => ({
86
+ id: n.id,
87
+ sessionId: n.sessionId,
88
+ label: n.label,
89
+ summaryTruncated: n.summaryTruncated,
90
+ tokenEstimate: n.tokenEstimate,
91
+ timestamp: n.timestamp,
92
+ dedupStatus: n.dedupStatus,
93
+ raptorLevel: n.raptorLevel,
94
+ topicSummary: n.topicSummary,
95
+ decisionCount: n.decisionCount,
96
+ textSnippet: n.textSnippet,
97
+ }));
98
+
99
+ const edges: MemoryMapEdgeEntry[] = graph.edges as MemoryMapEdgeEntry[];
100
+ const response: MemoryMapResponse = { nodes, edges, metadata: graph.metadata };
101
+
102
+ res.writeHead(200, { "Content-Type": "application/json" });
103
+ res.end(JSON.stringify(response));
104
+ return true;
105
+ }
@@ -11,11 +11,15 @@
11
11
 
12
12
  import { spawnSync } from "node:child_process"; // guardrails-allow PREVENT-PI-004: local subprocess detection only
13
13
  import { createRequire } from "node:module";
14
+ import { writeFileSync, mkdirSync } from "node:fs";
15
+ import { join } from "node:path";
14
16
  import type { IncomingMessage, ServerResponse } from "node:http";
15
17
  import type { RouteContext } from "./routes-core.js";
16
18
  import type {
17
19
  SetupStatusResponse,
18
20
  SetupDetectResponse,
21
+ SetupConfigureRequest,
22
+ SetupConfigureResponse,
19
23
  DetectResult,
20
24
  OllamaDetectResult,
21
25
  } from "./api-contracts/setup.js";
@@ -179,3 +183,111 @@ export function handleSetupDetect(
179
183
  res.end(JSON.stringify(body));
180
184
  return true;
181
185
  }
186
+
187
+ // ---------------------------------------------------------------------------
188
+ // handleSetupConfigure — POST "/api/setup-configure"
189
+ // ---------------------------------------------------------------------------
190
+
191
+ const OLLAMA_DEFAULT_URL = "http://localhost:11434/api/embeddings"; // guardrails-allow PREVENT-PI-004: localhost-only config string, not a runtime fetch
192
+ const LLAMA_DEFAULT_URL = "http://localhost:8080/v1/embeddings"; // guardrails-allow PREVENT-PI-004: localhost-only config string, not a runtime fetch
193
+
194
+ function readJsonBody(
195
+ req: IncomingMessage,
196
+ cb: (
197
+ result:
198
+ | { ok: true; value: Record<string, unknown> }
199
+ | { ok: false; error: string },
200
+ ) => void,
201
+ ): void {
202
+ let body = "";
203
+ let tooBig = false;
204
+ req.on("data", (chunk: Buffer) => {
205
+ if (body.length > 65536) { tooBig = true; return; }
206
+ body += chunk.toString();
207
+ });
208
+ req.on("end", () => {
209
+ if (tooBig) return cb({ ok: false, error: "body_too_large" });
210
+ try {
211
+ const v = body ? JSON.parse(body) : {}; // PREVENT-001: parsed value type-checked below
212
+ if (typeof v !== "object" || v === null || Array.isArray(v)) {
213
+ return cb({ ok: false, error: "invalid_object" });
214
+ }
215
+ cb({ ok: true, value: v as Record<string, unknown> });
216
+ } catch {
217
+ cb({ ok: false, error: "invalid_json" });
218
+ }
219
+ });
220
+ req.on("error", () => cb({ ok: false, error: "read_error" }));
221
+ }
222
+
223
+ export function handleSetupConfigure(
224
+ req: IncomingMessage,
225
+ res: ServerResponse,
226
+ ctx: RouteContext,
227
+ ): boolean {
228
+ if (req.url !== "/api/setup-configure") return false;
229
+ if (req.method !== "POST") {
230
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
231
+ res.writeHead(405, { "Content-Type": "application/json" });
232
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
233
+ return true;
234
+ }
235
+ readJsonBody(req, (parsed) => {
236
+ if (!parsed.ok) {
237
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
238
+ res.writeHead(400, { "Content-Type": "application/json" });
239
+ res.end(JSON.stringify({ error: parsed.error }));
240
+ return;
241
+ }
242
+ const body = parsed.value as unknown as SetupConfigureRequest;
243
+ const embedder = body.embedder;
244
+ if (embedder !== "ollama" && embedder !== "llama" && embedder !== "trigram") {
245
+ res.writeHead(400, { "Content-Type": "application/json" });
246
+ res.end(JSON.stringify({ error: "invalid_embedder" }));
247
+ return;
248
+ }
249
+ const url = body.url;
250
+ let resolvedUrl: string | null;
251
+ if (embedder === "ollama") resolvedUrl = typeof url === "string" && url ? url : OLLAMA_DEFAULT_URL;
252
+ else if (embedder === "llama") resolvedUrl = typeof url === "string" && url ? url : LLAMA_DEFAULT_URL;
253
+ else resolvedUrl = null;
254
+
255
+ // Write .mega-compact.env to the state dir (loaded by env-loader at next startup).
256
+ const stateDir = ctx.stateDir;
257
+ const envPath = join(stateDir, ".mega-compact.env");
258
+ const lines: string[] = [
259
+ "# Mega-Compact Embedder Configuration",
260
+ `# Configured via dashboard Setup tab at ${new Date().toISOString()}`,
261
+ ];
262
+ if (resolvedUrl) {
263
+ lines.push(`export MEGACOMPACT_EMBEDDING_URL="${resolvedUrl}"`);
264
+ } else {
265
+ lines.push("# trigram: built-in embedder, no URL needed");
266
+ lines.push("# unset MEGACOMPACT_EMBEDDING_URL (commented to override any shell-set value)");
267
+ lines.push("# export MEGACOMPACT_EMBEDDING_URL=");
268
+ }
269
+ lines.push("");
270
+ try {
271
+ mkdirSync(stateDir, { recursive: true });
272
+ writeFileSync(envPath, lines.join("\n"), "utf-8");
273
+ } catch (e) {
274
+ res.writeHead(500, { "Content-Type": "application/json" });
275
+ res.end(JSON.stringify({ error: `write_failed: ${e instanceof Error ? e.message : String(e)}` }));
276
+ return;
277
+ }
278
+ // Detect if the new config matches what's already active (no restart needed).
279
+ const currentUrl = process.env["MEGACOMPACT_EMBEDDING_URL"];
280
+ const alreadyActive = (resolvedUrl === null && !currentUrl) || (resolvedUrl !== null && currentUrl === resolvedUrl);
281
+ const resp: SetupConfigureResponse = {
282
+ embedder,
283
+ url: resolvedUrl,
284
+ envPath,
285
+ restartRequired: !alreadyActive,
286
+ alreadyActive,
287
+ };
288
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
289
+ res.writeHead(200, { "Content-Type": "application/json" });
290
+ res.end(JSON.stringify(resp));
291
+ });
292
+ return true;
293
+ }
@@ -21,4 +21,6 @@ export { handleTurns } from "./routes-turns.js";
21
21
  export { handleMaintenance } from "./routes-maintenance.js";
22
22
  export { handleProviderCache } from "./routes-cache.js";
23
23
  export { handleMemoryStatus } from "./routes-memory.js";
24
- export { handleSetupStatus, handleSetupDetect } from "./routes-setup.js";
24
+ export { handleSetupStatus, handleSetupDetect, handleSetupConfigure } from "./routes-setup.js";
25
+ export { handleMemoryMap } from "./routes-memory-map.js";
26
+ export { handleCacheStripes } from "./routes-cache.js";
@@ -38,9 +38,12 @@ import {
38
38
  handleTurns,
39
39
  handleMaintenance,
40
40
  handleProviderCache,
41
+ handleCacheStripes,
41
42
  handleMemoryStatus,
42
43
  handleSetupStatus,
43
44
  handleSetupDetect,
45
+ handleSetupConfigure,
46
+ handleMemoryMap,
44
47
  handleStatic,
45
48
  } from "./routes.js";
46
49
 
@@ -231,8 +234,11 @@ export async function launchDashboardServer(
231
234
  if (handleMaintenance(req, res, ctx)) return;
232
235
  if (handleProviderCache(req, res, ctx)) return;
233
236
  if (handleMemoryStatus(req, res, ctx)) return;
237
+ if (handleCacheStripes(req, res, ctx)) return;
234
238
  if (handleSetupStatus(req, res, ctx)) return;
235
239
  if (handleSetupDetect(req, res, ctx)) return;
240
+ if (handleSetupConfigure(req, res, ctx)) return;
241
+ if (handleMemoryMap(req, res, ctx)) return;
236
242
  handleStatic(req, res, ctx);
237
243
  });
238
244
 
@@ -30,6 +30,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
30
30
  import { closeVectorIndex } from "../src/store/vectorIndex.js";
31
31
  import { closeMemoryIndex } from "../src/store/memoryIndex.js";
32
32
  import { loadConfig } from "./mega-config.js";
33
+ import { loadMegaEnv } from "./mega-runtime/env-loader.js";
33
34
  import { MegaRuntime } from "./mega-runtime.js";
34
35
  import { registerEventHandlers } from "./mega-events.js";
35
36
  import { registerCommands } from "./mega-commands.js";
@@ -41,6 +42,10 @@ import { registerMetricsCommands } from "./mega-metrics-cmds.js";
41
42
  import { registerTopicsCommands } from "./mega-topics-cmds.js";
42
43
 
43
44
  export default function (pi: ExtensionAPI) {
45
+ // Load .mega-compact.env (written by /mega-setup + dashboard Setup tab) BEFORE
46
+ // config resolution so the wizard's choices take effect. Shell env wins over
47
+ // the file (see env-loader.ts). Non-fatal: missing/malformed file is a no-op.
48
+ loadMegaEnv(loadConfig().stateDir);
44
49
  const config = loadConfig();
45
50
  // S38.9: preflight env validation — check for obviously invalid values at startup.
46
51
  // Non-fatal: log warnings and fall back to defaults.
@@ -168,9 +168,21 @@ export interface MegaConfig {
168
168
  * the tail of the view when auto is OFF AND no trim action is needed. Default ON
169
169
  * (true). When false, restores the pre-sprint systemPrompt prepend behavior. */
170
170
  recallTailInject: boolean;
171
+ /** A1 PLAN_V2 Phase 2: Message Separation — isolate user/assistant turns
172
+ * from volatile tool results so the prompt-cache prefix stays stable.
173
+ * Default OFF (flag-OFF = byte-identical pre-sprint). */
174
+ messageSeparation: boolean;
171
175
  debug: boolean;
172
176
  /** Master reconciliation: TUI shutdown widget (MEGACOMPACT_TUI_WIDGET=0 to disable). */
173
177
  tuiWidget: boolean;
178
+ /** S57 B1: Query reformulation via embedding-neighbor keyword expansion. */
179
+ ragQueryReformulation: boolean;
180
+ /** S57 B2: Tiered recall router (L0 cache -> L1 FTS5 -> L2). */
181
+ ragTieredRouter: boolean;
182
+ /** S57 B3: Recall quality metrics (precision/recall scoring + logging). */
183
+ ragRecallMetrics: boolean;
184
+ /** S57 B4: Memory graph traversal (dashboard-oriented). */
185
+ ragMemoryGraph: boolean;
174
186
  }
175
187
 
176
188
  function envFlag(name: string, fallback: number): number {
@@ -332,7 +344,12 @@ export function loadConfig(): MegaConfig {
332
344
  recallMaxTokens: envFlag("MEGACOMPACT_RECALL_MAX_TOKENS", 1500),
333
345
  windowDedupe: envBool("MEGACOMPACT_WINDOW_DEDUPE", true),
334
346
  recallTailInject: envBool("MEGACOMPACT_RECALL_TAIL_INJECT", true),
347
+ messageSeparation: envBool("MEGACOMPACT_MESSAGE_SEPARATION", false),
335
348
  tuiWidget: envBool("MEGACOMPACT_TUI_WIDGET", true),
349
+ ragQueryReformulation: envBool("MEGACOMPACT_QUERY_REFORMULATION", false),
350
+ ragTieredRouter: envBool("MEGACOMPACT_TIERED_ROUTER", false),
351
+ ragRecallMetrics: envBool("MEGACOMPACT_RECALL_METRICS", false),
352
+ ragMemoryGraph: envBool("MEGACOMPACT_MEMORY_GRAPH", false),
336
353
  // D.1: env-overridable recompact delta (minimum context growth % before
337
354
  // re-compacting instead of replaying the cached live trim). Default 50.
338
355
  recompactPctDelta: envFlag("MEGACOMPACT_RECOMPACT_PCT_DELTA", 50),
@@ -37,6 +37,7 @@ import {
37
37
  } from "../mega-config.js";
38
38
  import { appendMirrorMessages } from "./mirror-append.js";
39
39
  import { stagedForTail, withRecallTail } from "./recall-tail.js";
40
+ import { buildSeparatedPrompt } from "./separated-prompt.js";
40
41
 
41
42
  /** Register the context event handler (live-trim auto-trigger). */
42
43
  export function registerContextHandler(
@@ -69,8 +70,15 @@ export function registerContextHandler(
69
70
  // REAL transcript before any view is built, so the injected tail never
70
71
  // reaches raw_transcript (PREVENT-PI: append-only, view-only).
71
72
  const tailResult = (msgs?: readonly AgentMessage[]) => {
72
- if (!stagedForTail(runtime, config)) return undefined;
73
- return { messages: withRecallTail(msgs ?? messages, runtime, config) };
73
+ const base = msgs ?? messages;
74
+ if (!stagedForTail(runtime, config) && !config.messageSeparation) return undefined;
75
+ let result: AgentMessage[] = stagedForTail(runtime, config)
76
+ ? withRecallTail(base, runtime, config)
77
+ : (base as AgentMessage[]);
78
+ if (config.messageSeparation) {
79
+ result = buildSeparatedPrompt(result);
80
+ }
81
+ return { messages: result };
74
82
  };
75
83
  // Always track context for the dashboard/widget, even when auto is off.
76
84
  // (v0.8 regression: !config.auto gate sat above this, leaving ctx stats