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,8 @@
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
+ export {};
@@ -196,6 +196,13 @@ export const ENDPOINTS = {
196
196
  path: "/api/provider-cache",
197
197
  description: "Lifetime provider prompt cache hit-rate aggregates + dollar savings estimate.",
198
198
  },
199
+ // ─── A3 Cache Stripes (PLAN_V2 Phase 4) ───────────────────────────
200
+ /** GET /api/cache-stripes — Per-stripe distribution + health score. */
201
+ cacheStripes: {
202
+ method: "GET",
203
+ path: "/api/cache-stripes",
204
+ description: "Per-stripe distribution counts, average stability scores, and composite cache health.",
205
+ },
199
206
  // ─── Memory Effectiveness (S53B) ───────────────────────────────────────
200
207
  /** GET /api/memory-status — Memory store aggregate statistics. */
201
208
  memoryStatus: {
@@ -215,4 +222,10 @@ export const ENDPOINTS = {
215
222
  path: "/api/setup-detect",
216
223
  description: "Best-effort detection of local embedder backends (ollama, llama.cpp, ONNX) available on the machine.",
217
224
  },
225
+ /** POST /api/setup-configure — Write embedder config to .mega-compact.env. */
226
+ setupConfigure: {
227
+ method: "POST",
228
+ path: "/api/setup-configure",
229
+ description: "Write the chosen embedder configuration to .mega-compact.env (loaded at next startup). Returns whether a restart is required.",
230
+ },
218
231
  };
@@ -138,3 +138,31 @@ export function handleProviderCache(req, res, ctx) {
138
138
  return false;
139
139
  }
140
140
  }
141
+ // ---------------------------------------------------------------------------
142
+ // handleCacheStripes — "GET /api/cache-stripes"
143
+ // ---------------------------------------------------------------------------
144
+ export function handleCacheStripes(req, res, ctx) {
145
+ if (!req.url?.startsWith("/api/cache-stripes"))
146
+ return false;
147
+ if (req.method !== "GET") {
148
+ res.writeHead(405, { "Content-Type": "application/json" });
149
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
150
+ return true;
151
+ }
152
+ try {
153
+ const stateDir = ctx.stateDir;
154
+ const pfReq = createRequire(import.meta.url);
155
+ const { readCacheStripes } = pfReq("../../src/store/sqlite/cache-stripes.js");
156
+ const result = readCacheStripes(stateDir);
157
+ res.writeHead(200, { "Content-Type": "application/json" });
158
+ res.end(JSON.stringify(result));
159
+ }
160
+ catch (e) {
161
+ res.writeHead(500, { "Content-Type": "application/json" });
162
+ res.end(JSON.stringify({
163
+ error: "cache_stripes_unavailable",
164
+ detail: String(e),
165
+ }));
166
+ }
167
+ return true;
168
+ }
@@ -256,6 +256,9 @@ export function handlePerf(req, res, ctx) {
256
256
  avg: avg(get("cache_hit_pct")),
257
257
  latest: latest(get("cache_hit_pct")),
258
258
  n: get("cache_hit_pct").length,
259
+ samples: rows
260
+ .filter((r) => r.kind === "cache_hit_pct")
261
+ .map((r) => ({ pct: r.value, ts: r.ts })),
259
262
  },
260
263
  db_recompute_ms: {
261
264
  p50: pct(get("db_recompute_ms"), 50),
@@ -0,0 +1,50 @@
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 { parse as parseUrl } from "node:url";
9
+ const _req = createRequire(import.meta.url);
10
+ // guardrails-allow PREVENT-PI-004: localhost-only dashboard API endpoint that reads from the local SQLite store — zero remote network calls.
11
+ export function handleMemoryMap(req, res, ctx) {
12
+ if (req.method !== "GET")
13
+ return false;
14
+ const parsed = parseUrl(req.url ?? "", true);
15
+ if (parsed.pathname !== "/api/memory-map")
16
+ return false;
17
+ // guardrails-allow PREVENT-PI-004: local loopback-only dashboard server — reads the local SQLite store for graph data.
18
+ const { buildMemoryGraph } = _req("../../src/memoryGraph.js");
19
+ const sessionId = typeof parsed.query.sessionId === "string" ? parsed.query.sessionId : undefined;
20
+ const threshold = typeof parsed.query.threshold === "string" ? Number.parseFloat(parsed.query.threshold) : undefined;
21
+ const maxEdges = typeof parsed.query.maxEdgesPerNode === "string" ? Number.parseInt(parsed.query.maxEdgesPerNode, 10) : undefined;
22
+ const raptorEdges = parsed.query.includeRaptorEdges === "false" ? false : true;
23
+ const temporalEdges = parsed.query.includeTemporalEdges === "false" ? false : true;
24
+ const graph = buildMemoryGraph({
25
+ sessionId,
26
+ similarityThreshold: threshold,
27
+ maxEdgesPerNode: maxEdges,
28
+ stateDir: ctx.stateDir,
29
+ includeRaptorEdges: raptorEdges,
30
+ includeTemporalEdges: temporalEdges,
31
+ });
32
+ const nodes = graph.nodes.map((n) => ({
33
+ id: n.id,
34
+ sessionId: n.sessionId,
35
+ label: n.label,
36
+ summaryTruncated: n.summaryTruncated,
37
+ tokenEstimate: n.tokenEstimate,
38
+ timestamp: n.timestamp,
39
+ dedupStatus: n.dedupStatus,
40
+ raptorLevel: n.raptorLevel,
41
+ topicSummary: n.topicSummary,
42
+ decisionCount: n.decisionCount,
43
+ textSnippet: n.textSnippet,
44
+ }));
45
+ const edges = graph.edges;
46
+ const response = { nodes, edges, metadata: graph.metadata };
47
+ res.writeHead(200, { "Content-Type": "application/json" });
48
+ res.end(JSON.stringify(response));
49
+ return true;
50
+ }
@@ -10,6 +10,8 @@
10
10
  */
11
11
  import { spawnSync } from "node:child_process"; // guardrails-allow PREVENT-PI-004: local subprocess detection only
12
12
  import { createRequire } from "node:module";
13
+ import { writeFileSync, mkdirSync } from "node:fs";
14
+ import { join } from "node:path";
13
15
  // ---------------------------------------------------------------------------
14
16
  // handleSetupStatus — "/api/setup-status"
15
17
  // ---------------------------------------------------------------------------
@@ -160,3 +162,106 @@ export function handleSetupDetect(req, res, _ctx) {
160
162
  res.end(JSON.stringify(body));
161
163
  return true;
162
164
  }
165
+ // ---------------------------------------------------------------------------
166
+ // handleSetupConfigure — POST "/api/setup-configure"
167
+ // ---------------------------------------------------------------------------
168
+ const OLLAMA_DEFAULT_URL = "http://localhost:11434/api/embeddings"; // guardrails-allow PREVENT-PI-004: localhost-only config string, not a runtime fetch
169
+ const LLAMA_DEFAULT_URL = "http://localhost:8080/v1/embeddings"; // guardrails-allow PREVENT-PI-004: localhost-only config string, not a runtime fetch
170
+ function readJsonBody(req, cb) {
171
+ let body = "";
172
+ let tooBig = false;
173
+ req.on("data", (chunk) => {
174
+ if (body.length > 65536) {
175
+ tooBig = true;
176
+ return;
177
+ }
178
+ body += chunk.toString();
179
+ });
180
+ req.on("end", () => {
181
+ if (tooBig)
182
+ return cb({ ok: false, error: "body_too_large" });
183
+ try {
184
+ const v = body ? JSON.parse(body) : {}; // PREVENT-001: parsed value type-checked below
185
+ if (typeof v !== "object" || v === null || Array.isArray(v)) {
186
+ return cb({ ok: false, error: "invalid_object" });
187
+ }
188
+ cb({ ok: true, value: v });
189
+ }
190
+ catch {
191
+ cb({ ok: false, error: "invalid_json" });
192
+ }
193
+ });
194
+ req.on("error", () => cb({ ok: false, error: "read_error" }));
195
+ }
196
+ export function handleSetupConfigure(req, res, ctx) {
197
+ if (req.url !== "/api/setup-configure")
198
+ return false;
199
+ if (req.method !== "POST") {
200
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
201
+ res.writeHead(405, { "Content-Type": "application/json" });
202
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
203
+ return true;
204
+ }
205
+ readJsonBody(req, (parsed) => {
206
+ if (!parsed.ok) {
207
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
208
+ res.writeHead(400, { "Content-Type": "application/json" });
209
+ res.end(JSON.stringify({ error: parsed.error }));
210
+ return;
211
+ }
212
+ const body = parsed.value;
213
+ const embedder = body.embedder;
214
+ if (embedder !== "ollama" && embedder !== "llama" && embedder !== "trigram") {
215
+ res.writeHead(400, { "Content-Type": "application/json" });
216
+ res.end(JSON.stringify({ error: "invalid_embedder" }));
217
+ return;
218
+ }
219
+ const url = body.url;
220
+ let resolvedUrl;
221
+ if (embedder === "ollama")
222
+ resolvedUrl = typeof url === "string" && url ? url : OLLAMA_DEFAULT_URL;
223
+ else if (embedder === "llama")
224
+ resolvedUrl = typeof url === "string" && url ? url : LLAMA_DEFAULT_URL;
225
+ else
226
+ resolvedUrl = null;
227
+ // Write .mega-compact.env to the state dir (loaded by env-loader at next startup).
228
+ const stateDir = ctx.stateDir;
229
+ const envPath = join(stateDir, ".mega-compact.env");
230
+ const lines = [
231
+ "# Mega-Compact Embedder Configuration",
232
+ `# Configured via dashboard Setup tab at ${new Date().toISOString()}`,
233
+ ];
234
+ if (resolvedUrl) {
235
+ lines.push(`export MEGACOMPACT_EMBEDDING_URL="${resolvedUrl}"`);
236
+ }
237
+ else {
238
+ lines.push("# trigram: built-in embedder, no URL needed");
239
+ lines.push("# unset MEGACOMPACT_EMBEDDING_URL (commented to override any shell-set value)");
240
+ lines.push("# export MEGACOMPACT_EMBEDDING_URL=");
241
+ }
242
+ lines.push("");
243
+ try {
244
+ mkdirSync(stateDir, { recursive: true });
245
+ writeFileSync(envPath, lines.join("\n"), "utf-8");
246
+ }
247
+ catch (e) {
248
+ res.writeHead(500, { "Content-Type": "application/json" });
249
+ res.end(JSON.stringify({ error: `write_failed: ${e instanceof Error ? e.message : String(e)}` }));
250
+ return;
251
+ }
252
+ // Detect if the new config matches what's already active (no restart needed).
253
+ const currentUrl = process.env["MEGACOMPACT_EMBEDDING_URL"];
254
+ const alreadyActive = (resolvedUrl === null && !currentUrl) || (resolvedUrl !== null && currentUrl === resolvedUrl);
255
+ const resp = {
256
+ embedder,
257
+ url: resolvedUrl,
258
+ envPath,
259
+ restartRequired: !alreadyActive,
260
+ alreadyActive,
261
+ };
262
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
263
+ res.writeHead(200, { "Content-Type": "application/json" });
264
+ res.end(JSON.stringify(resp));
265
+ });
266
+ return true;
267
+ }
@@ -13,4 +13,6 @@ export { handleTurns } from "./routes-turns.js";
13
13
  export { handleMaintenance } from "./routes-maintenance.js";
14
14
  export { handleProviderCache } from "./routes-cache.js";
15
15
  export { handleMemoryStatus } from "./routes-memory.js";
16
- export { handleSetupStatus, handleSetupDetect } from "./routes-setup.js";
16
+ export { handleSetupStatus, handleSetupDetect, handleSetupConfigure } from "./routes-setup.js";
17
+ export { handleMemoryMap } from "./routes-memory-map.js";
18
+ export { handleCacheStripes } from "./routes-cache.js";
@@ -12,7 +12,7 @@ import { join, dirname } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { createRequire } from "node:module";
14
14
  import { log, setLogPath, setDashboardServerVersion } from "./state.js";
15
- import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleStatic, } from "./routes.js";
15
+ import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleStatic, } from "./routes.js";
16
16
  export async function launchDashboardServer(stateDir) {
17
17
  // Our own package version — exposed at /api/version so the launcher can
18
18
  // detect a stale server (started by an older build) and replace it on
@@ -197,10 +197,16 @@ export async function launchDashboardServer(stateDir) {
197
197
  return;
198
198
  if (handleMemoryStatus(req, res, ctx))
199
199
  return;
200
+ if (handleCacheStripes(req, res, ctx))
201
+ return;
200
202
  if (handleSetupStatus(req, res, ctx))
201
203
  return;
202
204
  if (handleSetupDetect(req, res, ctx))
203
205
  return;
206
+ if (handleSetupConfigure(req, res, ctx))
207
+ return;
208
+ if (handleMemoryMap(req, res, ctx))
209
+ return;
204
210
  handleStatic(req, res, ctx);
205
211
  });
206
212
  // Bind base + range are env-configurable so tests can use a private,
@@ -28,6 +28,7 @@
28
28
  import { closeVectorIndex } from "../src/store/vectorIndex.js";
29
29
  import { closeMemoryIndex } from "../src/store/memoryIndex.js";
30
30
  import { loadConfig } from "./mega-config.js";
31
+ import { loadMegaEnv } from "./mega-runtime/env-loader.js";
31
32
  import { MegaRuntime } from "./mega-runtime.js";
32
33
  import { registerEventHandlers } from "./mega-events.js";
33
34
  import { registerCommands } from "./mega-commands.js";
@@ -38,6 +39,10 @@ import { registerGameCommands } from "./mega-game-cmds.js";
38
39
  import { registerMetricsCommands } from "./mega-metrics-cmds.js";
39
40
  import { registerTopicsCommands } from "./mega-topics-cmds.js";
40
41
  export default function (pi) {
42
+ // Load .mega-compact.env (written by /mega-setup + dashboard Setup tab) BEFORE
43
+ // config resolution so the wizard's choices take effect. Shell env wins over
44
+ // the file (see env-loader.ts). Non-fatal: missing/malformed file is a no-op.
45
+ loadMegaEnv(loadConfig().stateDir);
41
46
  const config = loadConfig();
42
47
  // S38.9: preflight env validation — check for obviously invalid values at startup.
43
48
  // Non-fatal: log warnings and fall back to defaults.
@@ -167,7 +167,12 @@ export function loadConfig() {
167
167
  recallMaxTokens: envFlag("MEGACOMPACT_RECALL_MAX_TOKENS", 1500),
168
168
  windowDedupe: envBool("MEGACOMPACT_WINDOW_DEDUPE", true),
169
169
  recallTailInject: envBool("MEGACOMPACT_RECALL_TAIL_INJECT", true),
170
+ messageSeparation: envBool("MEGACOMPACT_MESSAGE_SEPARATION", false),
170
171
  tuiWidget: envBool("MEGACOMPACT_TUI_WIDGET", true),
172
+ ragQueryReformulation: envBool("MEGACOMPACT_QUERY_REFORMULATION", false),
173
+ ragTieredRouter: envBool("MEGACOMPACT_TIERED_ROUTER", false),
174
+ ragRecallMetrics: envBool("MEGACOMPACT_RECALL_METRICS", false),
175
+ ragMemoryGraph: envBool("MEGACOMPACT_MEMORY_GRAPH", false),
171
176
  // D.1: env-overridable recompact delta (minimum context growth % before
172
177
  // re-compacting instead of replaying the cached live trim). Default 50.
173
178
  recompactPctDelta: envFlag("MEGACOMPACT_RECOMPACT_PCT_DELTA", 50),
@@ -11,6 +11,7 @@ import { computeLiveTrimCut, liveTrimSummaryMessage } from "../mega-trim.js";
11
11
  import { pressureFromPct, pressureRatio, } from "../mega-config.js";
12
12
  import { appendMirrorMessages } from "./mirror-append.js";
13
13
  import { stagedForTail, withRecallTail } from "./recall-tail.js";
14
+ import { buildSeparatedPrompt } from "./separated-prompt.js";
14
15
  /** Register the context event handler (live-trim auto-trigger). */
15
16
  export function registerContextHandler(pi, runtime, config) {
16
17
  // ---- Auto-trigger: live trim (compact and continue) + native durable ----
@@ -38,9 +39,16 @@ export function registerContextHandler(pi, runtime, config) {
38
39
  // REAL transcript before any view is built, so the injected tail never
39
40
  // reaches raw_transcript (PREVENT-PI: append-only, view-only).
40
41
  const tailResult = (msgs) => {
41
- if (!stagedForTail(runtime, config))
42
+ const base = msgs ?? messages;
43
+ if (!stagedForTail(runtime, config) && !config.messageSeparation)
42
44
  return undefined;
43
- return { messages: withRecallTail(msgs ?? messages, runtime, config) };
45
+ let result = stagedForTail(runtime, config)
46
+ ? withRecallTail(base, runtime, config)
47
+ : base;
48
+ if (config.messageSeparation) {
49
+ result = buildSeparatedPrompt(result);
50
+ }
51
+ return { messages: result };
44
52
  };
45
53
  // Always track context for the dashboard/widget, even when auto is off.
46
54
  // (v0.8 regression: !config.auto gate sat above this, leaving ctx stats
@@ -1,4 +1,4 @@
1
- import { recordPerfSample } from "../../src/store/sqlite.js";
1
+ import { recordPerfSample, readPerfSamples, } from "../../src/store/sqlite.js";
2
2
  import { classifyPrefixBreak } from "../../src/prefix-break.js";
3
3
  /** Narrow a turn_end message to its usage block when it is an assistant msg. */
4
4
  function usageOf(msg) {
@@ -14,6 +14,9 @@ function usageOf(msg) {
14
14
  * classifyPrefixBreak with the three candidate event timestamps from the
15
15
  * runtime and records a `prefix_break` sample. Best-effort + non-fatal.
16
16
  *
17
+ * Also evaluates rolling cache health and emits a degradation event when the
18
+ * composite score falls below 0.6 (A3, PLAN_V2 Phase 4).
19
+ *
17
20
  * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite write only.
18
21
  * PREVENT-002: SQL is fully parameterized via recordPerfSample.
19
22
  */
@@ -34,11 +37,90 @@ function tryRecordPrefixBreak(stateDir, prevHitPct, currHitPct, breakAt, runtime
34
37
  currHitPct,
35
38
  breakAt,
36
39
  }); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback perf instrumentation)
40
+ // A3: compute rolling cache health score and emit alert on degradation.
41
+ tryComputeCacheHealth(stateDir, currHitPct, runtime);
37
42
  }
38
43
  catch {
39
44
  /* non-fatal: prefix-break detection never blocks the agent loop */
40
45
  }
41
46
  }
47
+ /**
48
+ * Compute a rolling cache health score and emit a dashboard event when it
49
+ * falls below the degradation threshold (0.6 / "yellow").
50
+ *
51
+ * The score is a weighted composite of:
52
+ * - hitRateScore (0-1): recent cache_hit_pct sample average / 100
53
+ * - stabilityScore (0-1): 1 - recent prefix_break count / maxExpectedBreaks
54
+ * - churnScore (0-1): 1 - stripe re-assignment rate (derived from prefix_break classifications)
55
+ *
56
+ * Weights: hit rate 50%, prefix stability 30%, churn 20%.
57
+ *
58
+ * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite read, no network.
59
+ * PREVENT-001: readPerfSamples returns empty array when no data; handled.
60
+ * PREVENT-011: no `any` — all arrays are typed PerfSampleRow[].
61
+ */
62
+ function tryComputeCacheHealth(stateDir, currHitPct, runtime) {
63
+ try {
64
+ const now = Date.now();
65
+ const windowMs = 300_000; // 5-minute lookback
66
+ const sinceTs = now - windowMs;
67
+ // Read recent cache_hit_pct and prefix_break samples.
68
+ const hitSamples = readPerfSamples(stateDir, sinceTs, "cache_hit_pct");
69
+ const breakSamples = readPerfSamples(stateDir, sinceTs, "prefix_break");
70
+ // hitRateScore: average of recent cache hit percentages / 100.
71
+ let hitRateAvg = currHitPct;
72
+ if (hitSamples.length > 0) {
73
+ const sum = hitSamples.reduce((a, r) => a + r.value, 0);
74
+ hitRateAvg = sum / hitSamples.length;
75
+ }
76
+ const hitRateScore = Math.max(0, Math.min(1, hitRateAvg / 100));
77
+ // stabilityScore: penalize for frequent prefix breaks.
78
+ // Assume at most 2 breaks in 5min is tolerable.
79
+ const maxExpectedBreaks = 2;
80
+ const stabilityScore = Math.max(0, 1 - breakSamples.length / maxExpectedBreaks);
81
+ // churnScore: if recent breaks have varied causes, penalize.
82
+ const causeVariety = new Set(breakSamples.map((r) => {
83
+ const m = typeof r.meta === "string" ? JSON.parse(r.meta) : null;
84
+ return typeof m?.cause === "string" ? m.cause : "other";
85
+ }));
86
+ const churnPenalty = Math.min(1, causeVariety.size / 4); // 4 possible causes
87
+ const churnScore = 1 - churnPenalty;
88
+ // Composite: hitRate dominates.
89
+ const composite = hitRateScore * 0.5 + stabilityScore * 0.3 + churnScore * 0.2;
90
+ // Emit degradation event when score < 0.6.
91
+ if (composite < 0.6) {
92
+ const prevScore = runtime.rt._lastCacheHealthScore ?? 1;
93
+ runtime.rt._lastCacheHealthScore = composite;
94
+ recordPerfSample(stateDir, "cache_health", composite, {
95
+ hitRateScore: Math.round(hitRateScore * 1000) / 1000,
96
+ stabilityScore: Math.round(stabilityScore * 1000) / 1000,
97
+ churnScore: Math.round(churnScore * 1000) / 1000,
98
+ breakCount: breakSamples.length,
99
+ hitSampleCount: hitSamples.length,
100
+ currHitPct,
101
+ trend: composite < prevScore ? "falling" : "stable",
102
+ }); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback dashboard event)
103
+ // Structured log line for the host to pick up.
104
+ const level = composite < 0.4 ? "error" : "warn";
105
+ // eslint-disable-next-line no-console
106
+ console.log(JSON.stringify({
107
+ ts: now,
108
+ event: `cache_health_${level}`,
109
+ composite: Math.round(composite * 1000) / 1000,
110
+ hitRateAvg: Math.round(hitRateAvg * 10) / 10,
111
+ breakCount: breakSamples.length,
112
+ trend: composite < prevScore ? "falling" : "stable",
113
+ }));
114
+ }
115
+ else {
116
+ // Still healthy — just update the stored score.
117
+ runtime.rt._lastCacheHealthScore = composite;
118
+ }
119
+ }
120
+ catch {
121
+ /* non-fatal: cache health computation never blocks */
122
+ }
123
+ }
42
124
  /** Register perf instrumentation handlers + start the 5s cpu/mem interval. */
43
125
  export function registerPerfHandler(pi, runtime) {
44
126
  // turn_start: record the wall-clock start of the turn. Using Date.now() (not