pi-mega-compact 0.13.4 → 0.13.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 (217) hide show
  1. package/README.md +11 -8
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
  3. package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
  5. package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
  6. package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
  7. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
  8. package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
  9. package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
  10. package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +17 -3
  13. package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
  14. package/dist/extensions/mega-pipeline/recall.js +31 -0
  15. package/dist/extensions/mega-turn-store.js +2 -1
  16. package/dist/src/config/turns.js +10 -0
  17. package/dist/src/config.js +2 -0
  18. package/dist/src/recall/async.js +10 -1
  19. package/dist/src/recall/format.js +10 -1
  20. package/dist/src/recall/hydeTelemetry.js +22 -0
  21. package/dist/src/recall/sync.js +28 -2
  22. package/dist/src/store/turns/hydeStore.js +32 -0
  23. package/dist/src/store/turns/index.js +1 -0
  24. package/dist/src/store/turns/schema.js +102 -1
  25. package/dist/src/store/turns/sqlite-store/rows.js +23 -0
  26. package/dist/src/store/turns/sqlite-store/write.js +7 -3
  27. package/dist/src/topics/durability.js +187 -0
  28. package/dist/src/topics/index.js +2 -1
  29. package/dist/src/topics/store.js +28 -9
  30. package/dist/src/wiki/curation-helpers.js +71 -0
  31. package/dist/src/wiki/curation.js +147 -0
  32. package/dist/src/wiki/incremental.js +126 -0
  33. package/dist/src/wiki/index.js +5 -0
  34. package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
  35. package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
  37. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
  38. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
  39. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
  40. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
  41. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
  42. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
  43. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
  44. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
  45. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
  46. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
  47. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
  48. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
  49. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
  50. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
  51. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
  52. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
  53. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
  54. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
  56. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
  57. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
  58. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
  59. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
  61. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
  62. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
  63. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
  64. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
  65. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
  66. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
  67. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
  68. package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
  69. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
  72. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
  73. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/index.html +2 -2
  76. package/extensions/dashboard-client/package-lock.json +1995 -191
  77. package/extensions/dashboard-client/package.json +12 -1
  78. package/extensions/dashboard-client/postcss.config.js +6 -0
  79. package/extensions/dashboard-client/src/App.tsx +127 -88
  80. package/extensions/dashboard-client/src/api/client.ts +109 -10
  81. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
  82. package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
  83. package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
  84. package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
  85. package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
  86. package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
  87. package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
  88. package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
  89. package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
  90. package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
  91. package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
  92. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
  93. package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
  94. package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
  95. package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
  96. package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
  97. package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
  98. package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
  99. package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
  100. package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
  101. package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
  102. package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
  103. package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
  104. package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
  105. package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
  106. package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
  107. package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
  108. package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
  109. package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
  110. package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
  111. package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
  112. package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
  113. package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
  114. package/extensions/dashboard-client/src/config.ts +10 -0
  115. package/extensions/dashboard-client/src/main.tsx +2 -2
  116. package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
  117. package/extensions/dashboard-client/src/styles/index.css +79 -0
  118. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
  119. package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
  120. package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
  121. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
  122. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
  123. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
  124. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
  125. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
  126. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
  127. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
  128. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
  129. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
  130. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
  131. package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
  132. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
  133. package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
  134. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
  135. package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
  136. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
  137. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
  138. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
  139. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
  140. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
  141. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
  142. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
  143. package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
  144. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
  145. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
  146. package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
  147. package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
  148. package/extensions/dashboard-client/src/utils/cn.ts +7 -0
  149. package/extensions/dashboard-client/tailwind.config.js +46 -0
  150. package/extensions/dashboard-server/api-contracts/core.ts +59 -0
  151. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
  152. package/extensions/dashboard-server/api-contracts/index.ts +48 -2
  153. package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
  154. package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
  155. package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
  156. package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
  157. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
  158. package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
  159. package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
  160. package/extensions/dashboard-server/routes-wiki.ts +376 -0
  161. package/extensions/dashboard-server/routes.ts +2 -0
  162. package/extensions/dashboard-server/server.ts +18 -2
  163. package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
  164. package/extensions/mega-pipeline/recall.ts +31 -0
  165. package/extensions/mega-turn-store.ts +13 -0
  166. package/package.json +1 -1
  167. package/src/config/turns.ts +17 -0
  168. package/src/config.ts +3 -0
  169. package/src/recall/async.ts +10 -1
  170. package/src/recall/format.ts +15 -2
  171. package/src/recall/hydeTelemetry.ts +44 -0
  172. package/src/recall/sync.ts +34 -2
  173. package/src/recall/types.ts +36 -0
  174. package/src/store/turns/hydeStore.ts +82 -0
  175. package/src/store/turns/index.ts +4 -0
  176. package/src/store/turns/schema.ts +126 -1
  177. package/src/store/turns/sqlite-store/rows.ts +25 -0
  178. package/src/store/turns/sqlite-store/write.ts +20 -2
  179. package/src/store/turns/types.ts +33 -0
  180. package/src/topics/durability.ts +231 -0
  181. package/src/topics/index.ts +7 -1
  182. package/src/topics/store.ts +35 -9
  183. package/src/wiki/curation-helpers.ts +140 -0
  184. package/src/wiki/curation.ts +217 -0
  185. package/src/wiki/incremental.ts +164 -0
  186. package/src/wiki/index.ts +13 -0
  187. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
  188. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
  189. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
  190. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
  191. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
  192. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
  193. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
  194. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
  195. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
  196. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
  197. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
  198. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
  199. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
  200. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
  201. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
  202. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
  203. package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
  204. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
  213. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
  214. package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
  215. package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
  216. package/extensions/dashboard-client/src/styles/turns.css +0 -142
  217. package/extensions/dashboard-client/src/tabs/SetupTab/RagSettingsCard.tsx +0 -160
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-mega-compact
2
2
 
3
- A local context compressor for the [pi coding agent](https://github.com/earendil-works/pi). Keeps long sessions running without overflowing the context window. All on your machine — no cloud, no API calls, no telemetry.
3
+ A local-first context compressor for the [pi coding agent](https://github.com/earendil-works/pi). Keeps long sessions running without overflowing the context window. Local by default — no cloud, no API calls, no telemetry. Bring your own localhost embedder (Ollama, ONNX, TEI) for better semantic matches, or opt in to a remote endpoint if you need to.
4
4
 
5
5
  ## Features
6
6
 
@@ -13,10 +13,10 @@ A local context compressor for the [pi coding agent](https://github.com/earendil
13
13
  - **Durable memory** — on a cadence the store auto-reviews and safe-keeps decisions, facts, and preferences as first-class RAG memories, so long-running projects remember what mattered.
14
14
  - **Prompt-cache optimization** — message separation + cache striping (default OFF, opt-in). Targets 82-90% cache hit rate by structuring context around provider cache boundaries. Enables with `MEGACOMPACT_MESSAGE_SEPARATION` and `MEGACOMPACT_CACHE_STRIPING`.
15
15
  - **Context health + KV cache poison validation** (v0.12) — a real-time composite 0-1 health score per turn from five sub-scores (drift, output quality, error rate, cache health, cache poison). Catches garbled/hallucinated output and provider-side KV-cache corruption *before* they waste tokens — the failure mode where a large-context model (e.g. DeepSeek V4 Flash, 1M window) degrades at <1% usage. Tri-layer cache poison validation: prefix hash (L1 FNV-1a), output-quality-by-cache-hit (L2 semantic), error-rate correlation (L3 behavioral). The dashboard **Health tab** shows a gauge, sparkline, sub-score bars, alerts, and per-model breakdown. Auto-mitigation (force compaction on degraded context, prefix break on poisoned cache) is default OFF — toggle it in the Maintenance tab.
16
- - **RAG suite** — query reformulation (TF-IDF + RRF), tiered routing (L0 cache -> L1 FTS5 -> L2 PGlite), and recall-quality metrics (CRAG). All default OFF, opt-in via flags `MEGACOMPACT_QUERY_REFORMULATION`, `MEGACOMPACT_TIERED_ROUTER`, `MEGACOMPACT_RECALL_METRICS`.
16
+ - **RAG suite** — query reformulation (TF-IDF + RRF), tiered routing (L0 cache -> L1 FTS5 -> L2 PGlite), recall-quality metrics (CRAG), memory graph, and HyDE (hypothetical document embeddings). All default ON with graceful fallback — opt out via `MEGACOMPACT_<NAME>_DISABLED=true`. HyDE auto-activates only when an LLM embedder (Ollama/HTTP) is configured — it's a no-op with the default TrigramEmbedder. Toggle any flag from the dashboard Setup tab.
17
17
  - **Stacked memory graph** — the dashboard shows memory composition over time from 3 content sources (turns, durable memories, wiki) with a 9-gate validation system and a graph-health indicator. Per-model provider cache breakdown in the Cache tab.
18
18
  - **Debug bundle** — Maintenance tab in the dashboard has a **Gather Debug Logs** button that collects events, config, and store state into a shareable archive for bug reports.
19
- - **Fully local** — node:sqlite + trigram embeddings by default. Bring your own localhost embedder (ONNX, Ollama, TEI) for better semantic matches. Zero calls off your machine except the optional, localhost-only dashboard.
19
+ - **Local-first by default** — node:sqlite + trigram embeddings by default, zero calls off your machine. Bring your own localhost embedder (Ollama, ONNX, TEI) for better semantic matches the embedder endpoint is loopback-only by default (`MEGACOMPACT_ALLOW_REMOTE_EMBEDDER=1` opts in to a remote/third-party endpoint). The optional dashboard is localhost-only. No cloud, no API calls, no telemetry.
20
20
  - **Team-run aware** — fine-grained durable trim fires at agent settle during sub-agent runs, so long multi-agent work doesn't just collapse at the end.
21
21
  - **Multi-pi dashboard** — one dashboard tab per active pi process with the context stack, per-repo stats, and a live SSE feed across all of them. The React SPA has 15 lazy-loaded tabs — **Overview**, **Cache**, **Sessions**, **Turns** (per-turn memory + recall + rewind), and **Health** (context-health gauge + cache-poison alerts) are primary; **Repos**, **Events**, **Config**, **Setup** (embedding wizard), **Metrics** (perf latency/TPS/CPU), **Game**, **Achievements**, **Topics** (wiki), **Memory Map** (D3 graph), and **Maintenance** (debug bundle) are advanced.
22
22
  - **Auto-categorizing wiki.** Every 3 compactions (seeds from turns before that), the store clusters your real memory embeddings (k-means) and labels each cluster with its most discriminative terms (TF-IDF) — no LLM, no Ollama, fully local. The dashboard **Wiki tab** browses topics, searches by label or term, and drills down into the member memories of each cluster.
@@ -67,13 +67,16 @@ Set env vars before starting pi. Defaults are in `src/config/dedup.ts`.
67
67
  | `MEGACOMPACT_AUTO` | `true` | Enable auto-compaction |
68
68
  | `MEGACOMPACT_DEDUP_SIM` | `0.90` | Cosine threshold for near-dup collapse |
69
69
  | `MEGACOMPACT_CROSSREPO_ENABLED` | `true` | Cross-repo recall on resume |
70
- | `MEGACOMPACT_EMBEDDING_URL` | _(unset)_ | BYO localhost embedder endpoint |
70
+ | `MEGACOMPACT_EMBEDDING_URL` | _(unset)_ | BYO localhost embedder endpoint (loopback-only by default) |
71
+ | `MEGACOMPACT_ALLOW_REMOTE_EMBEDDER` | `false` | Opt-in: allow non-loopback embedder endpoint (e.g. hosted API) |
71
72
  | `MEGACOMPACT_TUI_WIDGET` | `true` | Render the above-editor panel |
72
73
  | `MEGACOMPACT_MESSAGE_SEPARATION` | `false` | Opt-in: separate messages at provider cache boundaries |
73
74
  | `MEGACOMPACT_CACHE_STRIPING` | `false` | Opt-in: stripe cache stripes across conversation |
74
- | `MEGACOMPACT_QUERY_REFORMULATION` | `false` | Opt-in: TF-IDF + RRF query reformulation |
75
- | `MEGACOMPACT_TIERED_ROUTER` | `false` | Opt-in: L0->L1->L2 recall routing |
76
- | `MEGACOMPACT_RECALL_METRICS` | `false` | Opt-in: CRAG recall quality metrics |
75
+ | `MEGACOMPACT_QUERY_REFORMULATION_DISABLED` | `false` | Default ON — set `true` to disable TF-IDF + RRF query reformulation |
76
+ | `MEGACOMPACT_TIERED_ROUTER_DISABLED` | `false` | Default ON — set `true` to disable L0->L1->L2 recall routing |
77
+ | `MEGACOMPACT_RECALL_METRICS_DISABLED` | `false` | Default ON — set `true` to disable CRAG recall quality metrics |
78
+ | `MEGACOMPACT_MEMORY_GRAPH_DISABLED` | `false` | Default ON — set `true` to disable memory graph |
79
+ | `MEGACOMPACT_HYDE_DISABLED` | `false` | Default ON — set `true` to disable HyDE (auto-activates only with LLM embedder) |
77
80
  | `MEGACOMPACT_MEMORY_GRAPH_SEED_TURNS` | `true` | Seed memory graph from turns (default ON) |
78
81
  | `MEGACOMPACT_WIKI_SEED_FROM_TURNS` | `true` | Seed wiki from turns (default ON) |
79
82
  | `MEGACOMPACT_RAPTOR_INCREMENTAL` | `true` | Incremental RAPTOR updates (default ON) |
@@ -104,7 +107,7 @@ Detailed architecture: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
104
107
 
105
108
  ```bash
106
109
  npm run build # TypeScript compile
107
- npm test # Build + 1088 tests
110
+ npm test # Build + 1145 tests
108
111
  npm run lint # Type check + guardrails scan
109
112
  ```
110
113
 
@@ -151,6 +151,49 @@ export const ENDPOINTS = {
151
151
  path: "/api/topics/:topicId/memories",
152
152
  description: "Member memories assigned to a wiki topic (drill-down).",
153
153
  },
154
+ // ─── Wiki Revival (W2) ───────────────────────────────────────────
155
+ /** GET /api/wiki/index — wiki landing page with resolved labels. */
156
+ wikiIndex: {
157
+ method: "GET",
158
+ path: "/api/wiki/index",
159
+ description: "Wiki index: auto-categorized topics with resolved (override-aware) labels.",
160
+ },
161
+ /** GET /api/wiki/topic/:topicId — single topic page + provenance. */
162
+ wikiTopic: {
163
+ method: "GET",
164
+ path: "/api/wiki/topic/:topicId",
165
+ description: "Single wiki topic page: summary, key memories, and memory provenance.",
166
+ },
167
+ /** PUT /api/wiki/topic/:topicId/label — rename a topic (user curation). */
168
+ renameTopic: {
169
+ method: "PUT",
170
+ path: "/api/wiki/topic/:topicId/label",
171
+ description: "Rename a wiki topic (persists a label override; emits wiki_topic_renamed).",
172
+ },
173
+ /** POST /api/wiki/merge — merge one topic into another. */
174
+ mergeTopics: {
175
+ method: "POST",
176
+ path: "/api/wiki/merge",
177
+ description: "Merge source topic into target (reassigns memories; emits wiki_topics_merged).",
178
+ },
179
+ /** POST /api/wiki/topic/:topicId/split — split listed memories into a new topic. */
180
+ splitTopic: {
181
+ method: "POST",
182
+ path: "/api/wiki/topic/:topicId/split",
183
+ description: "Split listed member memories into a new topic (emits wiki_topic_split).",
184
+ },
185
+ /** GET /api/wiki/topic/:topicId/timeline — per-topic time buckets. */
186
+ topicTimeline: {
187
+ method: "GET",
188
+ path: "/api/wiki/topic/:topicId/timeline",
189
+ description: "Per-topic memory-assignment timeline buckets for charts.",
190
+ },
191
+ /** GET /api/wiki/evolution — node/edge D3 evolution graph. */
192
+ topicEvolution: {
193
+ method: "GET",
194
+ path: "/api/wiki/evolution",
195
+ description: "Topic evolution nodes + edges + time buckets from topic_evolution.",
196
+ },
154
197
  // ─── S49B Maintenance Tab ─────────────────────────────────────────
155
198
  /** GET /api/maintenance — DB stats (table row counts + storage). */
156
199
  maintenanceStats: {
@@ -235,4 +278,11 @@ export const ENDPOINTS = {
235
278
  path: "/api/rag-settings",
236
279
  description: "Update RAG feature flag states by writing _DISABLED env vars to .mega-compact.env.",
237
280
  },
281
+ // ─── RAG Metrics (Sprint H2) ──────────────────────────────────────
282
+ /** GET /api/rag-metrics — HyDE + recall-quality telemetry aggregates. */
283
+ ragMetrics: {
284
+ method: "GET",
285
+ path: "/api/rag-metrics",
286
+ description: "HyDE invocation + recall-quality telemetry: flags, totals, recent turns, and daily series.",
287
+ },
238
288
  };
@@ -1,8 +1,8 @@
1
1
  /**
2
- * api-contracts/rag-settings.ts — RAG feature flag API response types.
2
+ * api-contracts/rag-settings.ts — comprehensive settings API response types.
3
3
  *
4
- * Types for the /api/rag-settings endpoint used by the dashboard RAG Settings
5
- * panel to read and toggle the five RAG feature flags (B1–B5).
4
+ * Types for the /api/rag-settings endpoint used by the dashboard Setup panel to
5
+ * read and update every adjustable MEGACOMPACT_* setting, grouped by category.
6
6
  *
7
7
  * PREVENT-PI-004: type definitions only, no network code.
8
8
  * PREVENT-011: no `any` type — all types are explicit.
@@ -0,0 +1,60 @@
1
+ /**
2
+ * dashboard-server/routes-rag-metrics.ts — GET /api/rag-metrics route (H2).
3
+ *
4
+ * Aggregates the H1 HyDE + recall-quality telemetry columns from turns.db into
5
+ * a compact response (flags, totals, recent rows, daily series).
6
+ *
7
+ * Guardrails: PREVENT-PI-004 (loopback-only), PREVENT-002 (parameterized),
8
+ * PREVENT-011 (no `any`).
9
+ */
10
+ import { openTurnStore } from "../../src/store/turns/connection.js";
11
+ import { listTelemetryTurns, aggregateDailyTelemetry, } from "../../src/store/turns/hydeStore.js";
12
+ import { RAG_HYDE_ENABLED, RAG_RECALL_METRICS } from "../../src/config.js";
13
+ function sendJson(res, status, body) {
14
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
15
+ res.writeHead(status, { "Content-Type": "application/json" });
16
+ res.end(JSON.stringify(body));
17
+ }
18
+ export function handleRagMetrics(req, res, ctx) {
19
+ const url = req.url ?? "";
20
+ if (url !== "/api/rag-metrics" || req.method !== "GET")
21
+ return false;
22
+ let db;
23
+ try {
24
+ db = openTurnStore(ctx.stateDir);
25
+ }
26
+ catch {
27
+ db = null;
28
+ }
29
+ const recent = db ? listTelemetryTurns(db, { limit: 200 }) : [];
30
+ const daily = db ? aggregateDailyTelemetry(db, 14) : [];
31
+ const hydeRanTurns = recent.filter((t) => t.hydeRan === 1).length;
32
+ const liftSum = recent.reduce((s, t) => s + t.hydeLift, 0);
33
+ const genSum = recent.reduce((s, t) => s + t.hydeGenerationMs, 0);
34
+ const scored = recent.filter((t) => t.recallScore > 0 || t.recallPass === 1);
35
+ const avgScore = scored.length > 0
36
+ ? Math.round((scored.reduce((s, t) => s + t.recallScore, 0) / scored.length) * 100) / 100
37
+ : null;
38
+ const passCount = scored.filter((t) => t.recallPass === 1).length;
39
+ const recentPassRate = scored.length > 0 ? Math.round((passCount / scored.length) * 100) / 100 : 0;
40
+ const body = {
41
+ flags: {
42
+ hydeEnabled: RAG_HYDE_ENABLED(),
43
+ recallMetricsEnabled: RAG_RECALL_METRICS(),
44
+ },
45
+ totals: {
46
+ telemetryTurns: recent.length,
47
+ hydeRanTurns,
48
+ avgLift: recent.length > 0
49
+ ? Math.round((liftSum / recent.length) * 100) / 100
50
+ : 0,
51
+ avgScore,
52
+ avgGenerationMs: recent.length > 0 ? Math.round(genSum / recent.length) : 0,
53
+ recentPassRate,
54
+ },
55
+ recent: recent.slice(0, 50),
56
+ daily,
57
+ };
58
+ sendJson(res, 200, body);
59
+ return true;
60
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * dashboard-server/routes-rag-settings-helpers.ts — SETTINGS inventory.
3
+ *
4
+ * The full catalog of user-adjustable MEGACOMPACT_* settings surfaced by the
5
+ * dashboard Setup panel, grouped by category. Kept separate from the route
6
+ * handler so the inventory can grow without bloating the handler file.
7
+ *
8
+ * PREVENT-011: no `any` type.
9
+ */
10
+ // Shorthand builders to keep the inventory terse and unambiguous.
11
+ const boolFlag = (key, label, description, def, requiresLlm = false) => ({
12
+ key,
13
+ label,
14
+ description,
15
+ type: "boolean",
16
+ default: def,
17
+ disabledConvention: true,
18
+ requiresLlm,
19
+ });
20
+ const boolDirect = (key, label, description, def) => ({
21
+ key,
22
+ label,
23
+ description,
24
+ type: "boolean",
25
+ default: def,
26
+ disabledConvention: false,
27
+ requiresLlm: false,
28
+ });
29
+ const num = (key, label, description, def, min, max, unit) => ({
30
+ key,
31
+ label,
32
+ description,
33
+ type: "number",
34
+ default: def,
35
+ disabledConvention: false,
36
+ requiresLlm: false,
37
+ min,
38
+ max,
39
+ ...(unit ? { unit } : {}),
40
+ });
41
+ const str = (key, label, description, def) => ({
42
+ key,
43
+ label,
44
+ description,
45
+ type: "string",
46
+ default: def,
47
+ disabledConvention: false,
48
+ requiresLlm: false,
49
+ });
50
+ /** Every adjustable setting, grouped by category. Read-only after module load. */
51
+ export const SETTINGS = [
52
+ {
53
+ name: "RAG Pipeline",
54
+ settings: [
55
+ boolFlag("MEGACOMPACT_QUERY_REFORMULATION", "Query Reformulation", "TF-IDF keyword expansion for vague recall queries", true),
56
+ boolFlag("MEGACOMPACT_TIERED_ROUTER", "Tiered Recall Router", "L0 cache → L1 FTS5 → L2 HNSW routing", true),
57
+ boolFlag("MEGACOMPACT_RECALL_METRICS", "Recall Quality Metrics", "Precision/recall scoring and logging", true),
58
+ boolFlag("MEGACOMPACT_MEMORY_GRAPH", "Memory Graph", "Dashboard-oriented memory graph traversal", true),
59
+ boolFlag("MEGACOMPACT_HYDE", "HyDE", "Generate hypothetical answer via LLM, embed it, RRF-fuse", true, true),
60
+ boolFlag("MEGACOMPACT_NEW_UI", "New Dashboard UI", "Tailwind + shadcn visual design (sidebar, glass panels)", true),
61
+ ],
62
+ },
63
+ {
64
+ name: "Wiki / Turns",
65
+ settings: [
66
+ boolFlag("MEGACOMPACT_WIKI_ENHANCED", "Wiki Enhanced", "User curation (rename/merge/split) + topic evolution", true),
67
+ boolFlag("MEGACOMPACT_WIKI_INCREMENTAL", "Wiki Incremental", "Assign new memories to nearest centroid", true),
68
+ boolDirect("MEGACOMPACT_AUTO_WIKI", "Auto Wiki", "Auto-categorizing wiki (k-means + TF-IDF)", true),
69
+ boolDirect("MEGACOMPACT_WIKI_SEED_FROM_TURNS", "Wiki Seed from Turns", "Seed topic model from raw_transcript when chunks thin", true),
70
+ boolDirect("MEGACOMPACT_TURNS_DB", "Turns DB", "Isolated turns.db store", true),
71
+ num("MEGACOMPACT_WIKI_SILHOUETTE_MIN", "Silhouette Minimum", "Full rebuild trigger threshold", 0.2, 0, 1),
72
+ num("MEGACOMPACT_TURNS_RETENTION_DAYS", "Turn Retention", "Per-turn provenance retention window", 30, 1, 365, "days"),
73
+ num("MEGACOMPACT_TURNS_KEEP_MIN", "Turns Keep Minimum", "Min turns kept per conversation during prune", 5, 1, 100),
74
+ num("MEGACOMPACT_WIKI_LABEL_TOP_TERMS", "Wiki Label Terms", "How many TF-IDF terms form a topic label", 5, 1, 20),
75
+ num("MEGACOMPACT_WIKI_REBUILD_EVERY", "Wiki Rebuild Every", "Rebuild topic model every Nth compaction", 3, 1, 50),
76
+ ],
77
+ },
78
+ {
79
+ name: "Dedup Tiers",
80
+ settings: [
81
+ boolDirect("MEGACOMPACT_L0_ENABLED", "L0 Exact Match", "Exact-hash dedup tier", true),
82
+ boolDirect("MEGACOMPACT_L1_ENABLED", "L1 MinHash/LSH", "Near-dup tier", true),
83
+ boolDirect("MEGACOMPACT_L2_ENABLED", "L2 Semantic", "Cosine similarity dedup tier", true),
84
+ boolDirect("MEGACOMPACT_RAPTOR_ENABLED", "RAPTOR", "Hierarchical clustering tier", true),
85
+ boolDirect("MEGACOMPACT_RAPTOR_MULTILEVEL", "RAPTOR Multi-level", "Multi-level retrieval (score all tree levels)", true),
86
+ boolDirect("MEGACOMPACT_RAPTOR_LEAF_EXPANSION", "RAPTOR Leaf Expansion", "Leaf node expansion in retrieval", true),
87
+ boolDirect("MEGACOMPACT_RAPTOR_INCREMENTAL", "RAPTOR Incremental", "Incremental rebuild mode", true),
88
+ boolDirect("MEGACOMPACT_RAPTOR_INJECT_SUMMARIES", "RAPTOR Inject Summaries", "Inject top-level summary nodes into recall", true),
89
+ boolDirect("MEGACOMPACT_MARK_ONLY_L0", "Mark Only L0", "L0 runs but does not collapse", false),
90
+ boolDirect("MEGACOMPACT_MARK_ONLY_L1", "Mark Only L1", "L1 runs but does not collapse", false),
91
+ boolDirect("MEGACOMPACT_MARK_ONLY_L2", "Mark Only L2", "L2 runs but does not collapse", false),
92
+ boolDirect("MEGACOMPACT_MINILM", "MiniLM Embedder", "Use MiniLM instead of trigram", false),
93
+ ],
94
+ },
95
+ {
96
+ name: "Dedup Thresholds",
97
+ settings: [
98
+ num("MEGACOMPACT_L2_THRESHOLD", "L2 Cosine Threshold", "L2 semantic dedup firing point", 0.85, 0, 1),
99
+ num("MEGACOMPACT_L1_JACCARD", "L1 Jaccard Threshold", "L1 MinHash near-dup threshold", 0.8, 0, 1),
100
+ num("MEGACOMPACT_DEDUP_SIM", "Dedup Similarity", "Legacy content-similarity fallback", 0.9, 0, 1),
101
+ num("MEGACOMPACT_MMR_LAMBDA", "MMR Lambda", "Maximal Marginal Relevance diversity", 0.5, 0, 1),
102
+ num("MEGACOMPACT_SEMDEDUP_COSINE", "SemDeDup Cosine", "Offline SemDeDup pair threshold", 0.95, 0, 1),
103
+ num("MEGACOMPACT_CONSOLIDATE_COSINE", "Consolidate Cosine", "Memory consolidation merge threshold", 0.7, 0, 1),
104
+ num("MEGACOMPACT_SIMILARITY_BUDGET_MS", "Similarity Budget", "Time budget for similarity computation", 50, 1, 10000, "ms"),
105
+ num("MEGACOMPACT_L1_VERIFY_BUDGET_MS", "L1 Verify Budget", "L1 verification time budget", 20, 1, 10000, "ms"),
106
+ num("MEGACOMPACT_L1_CANDIDATE_CAP", "L1 Candidate Cap", "L1 candidate cap", 100, 1, 10000),
107
+ ],
108
+ },
109
+ {
110
+ name: "RAPTOR Tuning",
111
+ settings: [
112
+ num("MEGACOMPACT_RAPTOR_BUDGET_MS", "RAPTOR Budget", "RAPTOR time budget", 5000, 100, 60000, "ms"),
113
+ num("MEGACOMPACT_RAPTOR_CLUSTERS", "RAPTOR Clusters", "Clusters per level", 5, 1, 50),
114
+ num("MEGACOMPACT_RAPTOR_CONSISTENCY", "RAPTOR Consistency", "Consistency threshold", 0.6, 0, 1),
115
+ num("MEGACOMPACT_RAPTOR_MAX_LEAF_EXP", "RAPTOR Max Leaf Expansion", "Max leaf expansion nodes", 10, 1, 100),
116
+ num("MEGACOMPACT_RAPTOR_FRESHNESS_HOURS", "RAPTOR Freshness", "Skip rebuild when tree is fresh", 4, 1, 168, "hours"),
117
+ num("MEGACOMPACT_FP_RATE_L0", "FP Rate L0", "FP alert threshold for exact tier", 0.01, 0, 1),
118
+ num("MEGACOMPACT_FP_RATE_L1L2", "FP Rate L1/L2", "FP alert threshold for fuzzy tiers", 0.05, 0, 1),
119
+ num("MEGACOMPACT_ALERT_WINDOW_MS", "Alert Window", "Alert evaluation window", 600000, 1000, 3600000, "ms"),
120
+ num("MEGACOMPACT_P95_BUDGET_MS", "P95 Budget", "Canary p95 budget per tier", 100, 1, 10000, "ms"),
121
+ ],
122
+ },
123
+ {
124
+ name: "Models",
125
+ settings: [
126
+ str("MEGACOMPACT_HYDE_MODEL", "HyDE Model", "LLM model name for HyDE generation", "llama3.2"),
127
+ str("MEGACOMPACT_RAPTOR_MODEL", "RAPTOR Summary Model", "Ollama model for cluster summarization (empty = extractive)", ""),
128
+ str("MEGACOMPACT_RAPTOR_URL", "RAPTOR Ollama URL", "Ollama endpoint for RAPTOR summarization", "http://127.0.0.1:11434"),
129
+ num("MEGACOMPACT_EMBED_CACHE", "Embed Cache Size", "Embedding cache entries (0 = disabled)", 256, 0, 10000),
130
+ ],
131
+ },
132
+ ];
133
+ /** Flat index of every setting by key for O(1) lookup + validation. */
134
+ export const SETTING_BY_KEY = new Map(SETTINGS.flatMap((c) => c.settings).map((s) => [s.key, s]));
135
+ /**
136
+ * Adjustable MEGACOMPACT_* env vars intentionally NOT surfaced in the settings
137
+ * UI. Handled by other panels, install-time paths, dev-only, internal tuning,
138
+ * or special string formats the generic form cannot represent.
139
+ * Kept as string literals so regression_check.py's coverage scan sees them.
140
+ */
141
+ export const EXCLUDED_SETTINGS = [
142
+ // Embedder telemetry — handled by EmbedderSetup.
143
+ "MEGACOMPACT_EMBEDDING_URL",
144
+ "MEGACOMPACT_EMBEDDING_KEY",
145
+ "MEGACOMPACT_EMBEDDING_HEADERS",
146
+ "MEGACOMPACT_EMBEDDING_DIM",
147
+ "MEGACOMPACT_OLLAMA_MODEL",
148
+ "MEGACOMPACT_ALLOW_REMOTE_EMBEDDER",
149
+ // Install-time paths.
150
+ "MEGACOMPACT_STATE_DIR",
151
+ "MEGACOMPACT_INDEX_DIR",
152
+ "MEGACOMPACT_VECTOR_INDEX_DIR",
153
+ "MEGACOMPACT_TURNS_DB_PATH",
154
+ // Dev-only.
155
+ "MEGACOMPACT_EXT_SCAN_DIR",
156
+ "MEGACOMPACT_EXT_USER_DIR",
157
+ // PGlite internal tuning.
158
+ "MEGACOMPACT_PGLITE_DISABLED",
159
+ "MEGACOMPACT_PGLITE_OPEN_TIMEOUT_MS",
160
+ "MEGACOMPACT_PGLITE_CLOSE_TIMEOUT_MS",
161
+ "MEGACOMPACT_PGLITE_QUERY_TIMEOUT_MS",
162
+ // Special string formats the generic form cannot represent.
163
+ "MEGACOMPACT_WIKI_K_RANGE",
164
+ "MEGACOMPACT_RAPTOR_LEVEL_WEIGHTS",
165
+ // Internal calibration, not user-facing.
166
+ "MEGACOMPACT_FTS5_MAX_BM25",
167
+ ];
@@ -1,9 +1,13 @@
1
1
  /**
2
- * dashboard-server/routes-rag-settings.ts — RAG Settings route handler.
2
+ * dashboard-server/routes-rag-settings.ts — comprehensive settings route handler.
3
3
  *
4
- * GET /api/rag-settings — Returns the state of all RAG feature flags (B1–B5).
5
- * POST /api/rag-settings Toggles flags by writing MEGACOMPACT_*_DISABLED
6
- * lines to the per-repo .mega-compact.env file.
4
+ * GET /api/rag-settings — Returns every adjustable MEGACOMPACT_* setting,
5
+ * grouped by category, with live values from env.
6
+ * POST /api/rag-settings Updates a single setting by writing its line to the
7
+ * per-repo .mega-compact.env file.
8
+ *
9
+ * The SETTINGS inventory lives in routes-rag-settings-helpers.ts so this file
10
+ * stays within the line budget.
7
11
  *
8
12
  * Guardrails: PREVENT-PI-004 (loopback-only), PREVENT-001 (null-safe JSON),
9
13
  * PREVENT-011 (no `any`). Each JSON write carries a guardrails-allow annotation.
@@ -11,47 +15,83 @@
11
15
  import { readFileSync, existsSync, writeFileSync, mkdirSync } from "node:fs";
12
16
  import { join } from "node:path";
13
17
  import { detectCurrentEmbedder } from "./routes-setup.js";
18
+ import { SETTINGS, SETTING_BY_KEY } from "./routes-rag-settings-helpers.js";
14
19
  // ---------------------------------------------------------------------------
15
- // RAG_FLAGSthe five feature flags surfaced by this panel.
20
+ // Env-reading helpers one per setting type.
16
21
  // ---------------------------------------------------------------------------
17
- const RAG_FLAGS = [
18
- {
19
- key: "MEGACOMPACT_QUERY_REFORMULATION",
20
- label: "Query Reformulation",
21
- description: "TF-IDF keyword expansion for vague recall queries (TrigramEmbedder path)",
22
- requiresLlm: false,
23
- },
24
- {
25
- key: "MEGACOMPACT_TIERED_ROUTER",
26
- label: "Tiered Recall Router",
27
- description: "L0 cache → L1 FTS5 → L2 HNSW routing for faster recall",
28
- requiresLlm: false,
29
- },
30
- {
31
- key: "MEGACOMPACT_RECALL_METRICS",
32
- label: "Recall Quality Metrics",
33
- description: "Precision/recall scoring and logging for recall evaluation",
34
- requiresLlm: false,
35
- },
36
- {
37
- key: "MEGACOMPACT_MEMORY_GRAPH",
38
- label: "Memory Graph",
39
- description: "Dashboard-oriented memory graph traversal across sessions",
40
- requiresLlm: false,
41
- },
42
- {
43
- key: "MEGACOMPACT_HYDE",
44
- label: "HyDE (Hypothetical Document Embeddings)",
45
- description: "Generate hypothetical answer via LLM, embed it, RRF-fuse with raw-query results",
46
- requiresLlm: true,
47
- },
48
- ];
49
- /** Strip any MEGACOMPACT_*_DISABLED assignment lines from env file content. */
50
- const DISABLED_LINE = /^export\s+MEGACOMPACT_\w+_DISABLED=/;
51
22
  function isDisabled(key) {
52
23
  const v = process.env[key + "_DISABLED"];
53
24
  return v === "true" || v === "1";
54
25
  }
26
+ function isEnabled(key, def) {
27
+ const v = process.env[key];
28
+ if (v === undefined)
29
+ return def;
30
+ return v === "true" || v === "1";
31
+ }
32
+ function readNumber(key, def) {
33
+ const v = process.env[key];
34
+ if (v === undefined)
35
+ return def;
36
+ const n = Number(v);
37
+ return Number.isNaN(n) ? def : n;
38
+ }
39
+ function resolveValue(spec) {
40
+ if (spec.type === "boolean") {
41
+ return spec.disabledConvention
42
+ ? !isDisabled(spec.key)
43
+ : isEnabled(spec.key, spec.default);
44
+ }
45
+ if (spec.type === "number") {
46
+ return readNumber(spec.key, spec.default);
47
+ }
48
+ return process.env[spec.key] ?? spec.default;
49
+ }
50
+ function toSettingState(spec, category) {
51
+ return {
52
+ key: spec.key,
53
+ label: spec.label,
54
+ description: spec.description,
55
+ category,
56
+ type: spec.type,
57
+ value: resolveValue(spec),
58
+ default: spec.default,
59
+ disabledConvention: spec.disabledConvention,
60
+ requiresLlm: spec.requiresLlm,
61
+ ...(spec.unit !== undefined ? { unit: spec.unit } : {}),
62
+ ...(spec.min !== undefined ? { min: spec.min } : {}),
63
+ ...(spec.max !== undefined ? { max: spec.max } : {}),
64
+ };
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Env file I/O.
68
+ // ---------------------------------------------------------------------------
69
+ /** Strip lines assigning KEY or KEY_DISABLED from env file content. */
70
+ const keyLinePattern = (key) => new RegExp(`^export\\s+${key}(_DISABLED)?=`);
71
+ function envPathOf(ctx) {
72
+ return join(ctx.stateDir, ".mega-compact.env");
73
+ }
74
+ function readEnvLines(envPath, key) {
75
+ if (!existsSync(envPath))
76
+ return [];
77
+ return readFileSync(envPath, "utf-8")
78
+ .split("\n")
79
+ .filter((l) => !keyLinePattern(key).test(l));
80
+ }
81
+ function writeSetting(envPath, key, line) {
82
+ const lines = readEnvLines(envPath, key);
83
+ lines.push(line);
84
+ if (lines[lines.length - 1] !== "")
85
+ lines.push("");
86
+ writeFileSync(envPath, lines.join("\n"), "utf-8");
87
+ }
88
+ /** Strip KEY/KEY_DISABLED lines, leaving the file otherwise intact. */
89
+ function removeSetting(envPath, key) {
90
+ writeFileSync(envPath, readEnvLines(envPath, key).join("\n"), "utf-8");
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Request body parsing.
94
+ // ---------------------------------------------------------------------------
55
95
  function readJsonBody(req, cb) {
56
96
  let body = "";
57
97
  let tooBig = false;
@@ -85,15 +125,12 @@ export function handleRagSettings(req, res, ctx) {
85
125
  if (req.url !== "/api/rag-settings")
86
126
  return false;
87
127
  if (req.method === "GET") {
88
- const flags = RAG_FLAGS.map((f) => ({
89
- key: f.key,
90
- label: f.label,
91
- description: f.description,
92
- enabled: !isDisabled(f.key),
93
- requiresLlm: f.requiresLlm,
128
+ const categories = SETTINGS.map((cat) => ({
129
+ name: cat.name,
130
+ settings: cat.settings.map((s) => toSettingState(s, cat.name)),
94
131
  }));
95
132
  const body = {
96
- flags,
133
+ categories,
97
134
  llmActive: detectCurrentEmbedder() === "http",
98
135
  };
99
136
  // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
@@ -109,47 +146,63 @@ export function handleRagSettings(req, res, ctx) {
109
146
  res.end(JSON.stringify({ error: parsed.error }));
110
147
  return;
111
148
  }
112
- const body = parsed.value;
113
- const desired = body.flags;
114
- if (typeof desired !== "object" || desired === null || Array.isArray(desired)) {
149
+ const reqBody = parsed.value;
150
+ const key = reqBody.key;
151
+ const value = reqBody.value;
152
+ if (typeof key !== "string" || typeof value !== "string") {
115
153
  // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
116
154
  res.writeHead(400, { "Content-Type": "application/json" });
117
- res.end(JSON.stringify({ error: "invalid_flags" }));
155
+ res.end(JSON.stringify({ error: "invalid_body" }));
118
156
  return;
119
157
  }
120
- // Only accept known RAG flag keys.
121
- const known = new Set(RAG_FLAGS.map((f) => f.key));
122
- for (const key of Object.keys(desired)) {
123
- if (!known.has(key)) {
124
- // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
125
- res.writeHead(400, { "Content-Type": "application/json" });
126
- res.end(JSON.stringify({ error: `unknown_flag: ${key}` }));
127
- return;
128
- }
129
- }
130
- const envPath = join(ctx.stateDir, ".mega-compact.env");
131
- let lines = [];
132
- if (existsSync(envPath)) {
133
- const content = readFileSync(envPath, "utf-8");
134
- lines = content.split("\n").filter((line) => !DISABLED_LINE.test(line));
135
- }
136
- for (const key of Object.keys(desired)) {
137
- if (desired[key] === false) {
138
- lines.push(`export ${key}_DISABLED="true"`);
139
- }
158
+ const spec = SETTING_BY_KEY.get(key);
159
+ if (!spec) {
160
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
161
+ res.writeHead(400, { "Content-Type": "application/json" });
162
+ res.end(JSON.stringify({ error: `unknown_setting: ${key}` }));
163
+ return;
140
164
  }
141
- if (lines.length > 0 && lines[lines.length - 1] !== "")
142
- lines.push("");
165
+ const envPath = envPathOf(ctx);
143
166
  try {
144
167
  mkdirSync(ctx.stateDir, { recursive: true });
145
- writeFileSync(envPath, lines.join("\n"), "utf-8");
168
+ if (spec.type === "boolean") {
169
+ if (value !== "true" && value !== "false") {
170
+ throw new SettingError("boolean_value_required");
171
+ }
172
+ if (spec.disabledConvention) {
173
+ // false → opt out via KEY_DISABLED="true"; true → strip the line.
174
+ if (value === "false") {
175
+ writeSetting(envPath, spec.key, `export ${spec.key}_DISABLED="true"`);
176
+ }
177
+ else {
178
+ removeSetting(envPath, spec.key);
179
+ }
180
+ }
181
+ else {
182
+ writeSetting(envPath, spec.key, `export ${spec.key}="${value}"`);
183
+ }
184
+ }
185
+ else if (spec.type === "number") {
186
+ const n = Number(value);
187
+ if (Number.isNaN(n))
188
+ throw new SettingError("invalid_number");
189
+ if (spec.min !== undefined && n < spec.min)
190
+ throw new SettingError("below_min");
191
+ if (spec.max !== undefined && n > spec.max)
192
+ throw new SettingError("above_max");
193
+ writeSetting(envPath, spec.key, `export ${spec.key}="${n}"`);
194
+ }
195
+ else {
196
+ writeSetting(envPath, spec.key, `export ${spec.key}="${value}"`);
197
+ }
146
198
  }
147
199
  catch (e) {
200
+ const msg = e instanceof SettingError
201
+ ? e.message
202
+ : `write_failed: ${e instanceof Error ? e.message : String(e)}`;
148
203
  // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
149
- res.writeHead(500, { "Content-Type": "application/json" });
150
- res.end(JSON.stringify({
151
- error: `write_failed: ${e instanceof Error ? e.message : String(e)}`,
152
- }));
204
+ res.writeHead(400, { "Content-Type": "application/json" });
205
+ res.end(JSON.stringify({ error: msg }));
153
206
  return;
154
207
  }
155
208
  const resp = {
@@ -167,3 +220,9 @@ export function handleRagSettings(req, res, ctx) {
167
220
  res.end(JSON.stringify({ error: "method_not_allowed" }));
168
221
  return true;
169
222
  }
223
+ class SettingError extends Error {
224
+ constructor(message) {
225
+ super(message);
226
+ this.name = "SettingError";
227
+ }
228
+ }