pi-mega-compact 0.11.4 → 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 (381) 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 +32 -0
  3. package/dist/extensions/dashboard-server/api-contracts/memory-map.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/memory.js +8 -0
  5. package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +1 -0
  6. package/dist/extensions/dashboard-server/api-contracts/setup.js +10 -0
  7. package/dist/extensions/dashboard-server/index-reader.js +6 -2
  8. package/dist/extensions/dashboard-server/routes-cache.js +135 -47
  9. package/dist/extensions/dashboard-server/routes-game.js +3 -0
  10. package/dist/extensions/dashboard-server/routes-memory-map.js +50 -0
  11. package/dist/extensions/dashboard-server/routes-memory.js +51 -0
  12. package/dist/extensions/dashboard-server/routes-repo.js +2 -2
  13. package/dist/extensions/dashboard-server/routes-setup.js +267 -0
  14. package/dist/extensions/dashboard-server/routes.js +4 -0
  15. package/dist/extensions/dashboard-server/server.js +13 -1
  16. package/dist/extensions/mega-commands.js +189 -1
  17. package/dist/extensions/mega-compact.js +5 -0
  18. package/dist/extensions/mega-config.js +7 -0
  19. package/dist/extensions/mega-events/agent-handlers.js +19 -4
  20. package/dist/extensions/mega-events/compact-handlers.js +1 -0
  21. package/dist/extensions/mega-events/context-handler.js +45 -92
  22. package/dist/extensions/mega-events/context-hwm.js +51 -0
  23. package/dist/extensions/mega-events/error-classifier.js +16 -5
  24. package/dist/extensions/mega-events/mirror-append.js +107 -0
  25. package/dist/extensions/mega-events/outage-advisor.js +7 -1
  26. package/dist/extensions/mega-events/perf-handler.js +134 -2
  27. package/dist/extensions/mega-events/recall-tail.js +59 -0
  28. package/dist/extensions/mega-events/separated-prompt.js +248 -0
  29. package/dist/extensions/mega-events/session-handlers.js +15 -8
  30. package/dist/extensions/mega-runtime/env-loader.js +56 -0
  31. package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
  32. package/dist/extensions/mega-runtime/runtime-snapshot.js +1 -0
  33. package/dist/extensions/mega-runtime/runtime.js +7 -0
  34. package/dist/extensions/mega-runtime/snapshot.js +1 -0
  35. package/dist/extensions/mega-runtime/widget.js +5 -0
  36. package/dist/src/cache-stripe-impl.js +288 -0
  37. package/dist/src/cache-stripe.js +11 -0
  38. package/dist/src/config/stopwords.js +24 -0
  39. package/dist/src/config.js +17 -0
  40. package/dist/src/embedder.js +41 -2
  41. package/dist/src/memoryGraph.js +299 -0
  42. package/dist/src/memoryStats.js +75 -0
  43. package/dist/src/monitoring.js +13 -0
  44. package/dist/src/prefix-break.js +43 -0
  45. package/dist/src/pricing.js +1 -1
  46. package/dist/src/queryExpansion.js +163 -0
  47. package/dist/src/queryReformulation/cache.js +123 -0
  48. package/dist/src/queryReformulation/rrf.js +29 -0
  49. package/dist/src/queryReformulation/tfidf.js +61 -0
  50. package/dist/src/queryReformulation/vagueness.js +33 -0
  51. package/dist/src/queryReformulation.js +225 -0
  52. package/dist/src/recallMetrics.js +258 -0
  53. package/dist/src/store/sqlite/cache-stripes.js +133 -0
  54. package/dist/src/store/sqlite/fts5-search.js +68 -0
  55. package/dist/src/store/sqlite/perf-samples.js +145 -16
  56. package/dist/src/store/sqlite/schema.js +60 -1
  57. package/dist/src/store/sqlite.js +1 -0
  58. package/dist/src/store/turns/memory-store.js +8 -0
  59. package/dist/src/store/turns/sqlite-store.js +14 -3
  60. package/dist/src/store/turns/types.js +18 -1
  61. package/dist/src/store/vectorIndex.js +55 -10
  62. package/dist/src/tiered-router/types.js +18 -0
  63. package/dist/src/tieredRouter.js +421 -0
  64. package/dist/src/vectorStore.js +8 -6
  65. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-DR9cLGDL.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-DR9cLGDL.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/AchievementsTab-svNUgJ1e.js +2 -0
  68. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-svNUgJ1e.js.map} +1 -1
  69. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-CmmWheuW.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-CmmWheuW.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-CFE7jLsa.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-CFE7jLsa.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/assets/GameTab-CJAX_th_.js +2 -0
  76. package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-CJAX_th_.js.map} +1 -1
  77. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-P4SUsvMf.js} +2 -2
  78. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-P4SUsvMf.js.map} +1 -1
  79. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +2 -0
  80. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +1 -0
  81. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-CHQscixy.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-CHQscixy.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/OverviewTab-DBs4DJdd.js +2 -0
  84. package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-DBs4DJdd.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/ReposTab-DeFz76L5.js +2 -0
  86. package/extensions/dashboard-client/dist/assets/ReposTab-DeFz76L5.js.map +1 -0
  87. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-BWNsj1OB.js} +2 -2
  88. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-BWNsj1OB.js.map} +1 -1
  89. package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js +2 -0
  90. package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js.map +1 -0
  91. package/extensions/dashboard-client/dist/assets/TimeSavedCard-CiqCOQuL.js +2 -0
  92. package/extensions/dashboard-client/dist/assets/TimeSavedCard-CiqCOQuL.js.map +1 -0
  93. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BWWQxltv.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BWWQxltv.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-CVz4f4-o.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-CVz4f4-o.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js +2 -0
  98. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js.map +1 -0
  99. package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +1 -0
  100. package/extensions/dashboard-client/dist/assets/index-DrktNOoT.js +42 -0
  101. package/extensions/dashboard-client/dist/assets/index-DrktNOoT.js.map +1 -0
  102. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +2 -0
  103. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +1 -0
  104. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js → useSSE-Bf5LOLSi.js} +2 -2
  105. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-Bf5LOLSi.js.map} +1 -1
  106. package/extensions/dashboard-client/dist/index.html +2 -2
  107. package/extensions/dashboard-client/src/App.tsx +28 -8
  108. package/extensions/dashboard-client/src/api/client.ts +29 -0
  109. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
  110. package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +100 -0
  111. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +56 -0
  112. package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +38 -57
  113. package/extensions/dashboard-client/src/components/StripeDistributionCard.tsx +110 -0
  114. package/extensions/dashboard-client/src/components/TabBar.tsx +49 -4
  115. package/extensions/dashboard-client/src/main.tsx +1 -0
  116. package/extensions/dashboard-client/src/styles/base.css +30 -0
  117. package/extensions/dashboard-client/src/styles/cache.css +167 -0
  118. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +75 -12
  119. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +440 -0
  120. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +418 -0
  121. package/extensions/dashboard-client/tsconfig.json +3 -2
  122. package/extensions/dashboard-client/vite.config.ts +2 -0
  123. package/extensions/dashboard-server/api-contracts/cache-stripes.ts +49 -0
  124. package/extensions/dashboard-server/api-contracts/endpoints.ts +66 -3
  125. package/extensions/dashboard-server/api-contracts/index.ts +22 -0
  126. package/extensions/dashboard-server/api-contracts/memory-map.ts +50 -0
  127. package/extensions/dashboard-server/api-contracts/memory.ts +27 -0
  128. package/extensions/dashboard-server/api-contracts/multi-repo.ts +231 -231
  129. package/extensions/dashboard-server/api-contracts/provider-cache.ts +37 -0
  130. package/extensions/dashboard-server/api-contracts/setup.ts +80 -0
  131. package/extensions/dashboard-server/index-reader.ts +138 -121
  132. package/extensions/dashboard-server/routes-cache.ts +186 -75
  133. package/extensions/dashboard-server/routes-game.ts +3 -0
  134. package/extensions/dashboard-server/routes-memory-map.ts +105 -0
  135. package/extensions/dashboard-server/routes-memory.ts +63 -0
  136. package/extensions/dashboard-server/routes-repo.ts +36 -36
  137. package/extensions/dashboard-server/routes-setup.ts +293 -0
  138. package/extensions/dashboard-server/routes.ts +4 -0
  139. package/extensions/dashboard-server/server.ts +12 -0
  140. package/extensions/mega-commands.ts +211 -1
  141. package/extensions/mega-compact.ts +5 -0
  142. package/extensions/mega-config.ts +27 -0
  143. package/extensions/mega-events/agent-handlers.ts +20 -4
  144. package/extensions/mega-events/compact-handlers.ts +1 -0
  145. package/extensions/mega-events/context-handler.ts +53 -110
  146. package/extensions/mega-events/context-hwm.ts +85 -0
  147. package/extensions/mega-events/error-classifier.ts +15 -4
  148. package/extensions/mega-events/mirror-append.ts +155 -0
  149. package/extensions/mega-events/outage-advisor.ts +11 -5
  150. package/extensions/mega-events/perf-handler.ts +190 -6
  151. package/extensions/mega-events/recall-tail.ts +88 -0
  152. package/extensions/mega-events/separated-prompt.ts +329 -0
  153. package/extensions/mega-events/session-handlers.ts +14 -7
  154. package/extensions/mega-runtime/env-loader.ts +55 -0
  155. package/extensions/mega-runtime/helpers.ts +14 -0
  156. package/extensions/mega-runtime/reset-runtime.ts +6 -0
  157. package/extensions/mega-runtime/runtime-snapshot.ts +1 -0
  158. package/extensions/mega-runtime/runtime.ts +7 -0
  159. package/extensions/mega-runtime/snapshot.ts +6 -0
  160. package/extensions/mega-runtime/widget-types.ts +3 -0
  161. package/extensions/mega-runtime/widget.ts +5 -0
  162. package/package.json +5 -2
  163. package/src/cache-stripe-impl.ts +385 -0
  164. package/src/cache-stripe.ts +18 -0
  165. package/src/config/stopwords.ts +25 -0
  166. package/src/config.ts +26 -0
  167. package/src/embedder.ts +42 -2
  168. package/src/memoryGraph.ts +438 -0
  169. package/src/memoryStats.ts +107 -0
  170. package/src/monitoring.ts +35 -0
  171. package/src/prefix-break.ts +83 -0
  172. package/src/pricing.ts +2 -2
  173. package/src/queryExpansion.ts +206 -0
  174. package/src/queryReformulation/cache.ts +169 -0
  175. package/src/queryReformulation/rrf.ts +51 -0
  176. package/src/queryReformulation/tfidf.ts +79 -0
  177. package/src/queryReformulation/vagueness.ts +38 -0
  178. package/src/queryReformulation.ts +355 -0
  179. package/src/recallMetrics.ts +388 -0
  180. package/src/store/sqlite/cache-stripes.ts +196 -0
  181. package/src/store/sqlite/fts5-search.ts +90 -0
  182. package/src/store/sqlite/perf-samples.ts +218 -23
  183. package/src/store/sqlite/schema.ts +60 -1
  184. package/src/store/sqlite.ts +1 -0
  185. package/src/store/turns/memory-store.ts +10 -1
  186. package/src/store/turns/sqlite-store.ts +27 -15
  187. package/src/store/turns/types.ts +21 -0
  188. package/src/store/vectorIndex.ts +71 -11
  189. package/src/tiered-router/types.ts +64 -0
  190. package/src/tieredRouter.ts +489 -0
  191. package/src/vectorStore.ts +9 -5
  192. package/dist/extensions/conflict-scan.test.js +0 -115
  193. package/dist/extensions/dashboard-server/api-contracts.test.js +0 -884
  194. package/dist/extensions/dashboard-server/html.test.js +0 -52
  195. package/dist/extensions/dashboard-server/perf-server.test.js +0 -80
  196. package/dist/extensions/dashboard-server/routes-cache.test.js +0 -248
  197. package/dist/extensions/dashboard-server/server.test.js +0 -120
  198. package/dist/extensions/dashboard-server-s32.test.js +0 -181
  199. package/dist/extensions/dashboard-server.test.js +0 -339
  200. package/dist/extensions/mega-cache-replay.test.js +0 -400
  201. package/dist/extensions/mega-compact-s38.test.js +0 -1383
  202. package/dist/extensions/mega-compact.test.js +0 -1112
  203. package/dist/extensions/mega-events.test.js +0 -47
  204. package/dist/extensions/mega-game-cmds.test.js +0 -163
  205. package/dist/extensions/mega-runtime/snapshot.test.js +0 -127
  206. package/dist/extensions/mega-runtime/state.test.js +0 -266
  207. package/dist/extensions/mega-runtime/widget.test.js +0 -343
  208. package/dist/extensions/mega-shutdown-widget.test.js +0 -121
  209. package/dist/extensions/mega-teamrun.test.js +0 -156
  210. package/dist/extensions/mega-trim.test.js +0 -58
  211. package/dist/extensions/mega-turn-store.test.js +0 -68
  212. package/dist/src/boundary.test.js +0 -170
  213. package/dist/src/compact.test.js +0 -78
  214. package/dist/src/config/themes.test.js +0 -94
  215. package/dist/src/dedup/dedup.test.js +0 -41
  216. package/dist/src/dedup/l1.test.js +0 -50
  217. package/dist/src/dedup/raptor/buildHistory.test.js +0 -292
  218. package/dist/src/dedup/raptor/multilevel-serve.test.js +0 -229
  219. package/dist/src/dedup/raptor/multilevel.test.js +0 -238
  220. package/dist/src/dedup/raptor/promote.test.js +0 -69
  221. package/dist/src/dedup/raptor/raptor.test.js +0 -248
  222. package/dist/src/dedup/raptor/retrieval.test.js +0 -95
  223. package/dist/src/dedup/raptor/serve-gate.test.js +0 -298
  224. package/dist/src/dedup/sprint12.test.js +0 -219
  225. package/dist/src/dedup-engine.test.js +0 -472
  226. package/dist/src/driftDetection.test.js +0 -87
  227. package/dist/src/e2e.test.js +0 -698
  228. package/dist/src/engine.test.js +0 -111
  229. package/dist/src/extractive.test.js +0 -147
  230. package/dist/src/fork.test.js +0 -81
  231. package/dist/src/game/scoring.test.js +0 -98
  232. package/dist/src/httpEmbedder.test.js +0 -277
  233. package/dist/src/importance.test.js +0 -372
  234. package/dist/src/intent.test.js +0 -78
  235. package/dist/src/log.test.js +0 -42
  236. package/dist/src/mechanical-fix.test.js +0 -65
  237. package/dist/src/memory.test.js +0 -70
  238. package/dist/src/memoryConsolidate.test.js +0 -38
  239. package/dist/src/memoryOps.test.js +0 -129
  240. package/dist/src/memoryRecall.test.js +0 -171
  241. package/dist/src/memoryRoundtrip.test.js +0 -137
  242. package/dist/src/metrics/turns.test.js +0 -152
  243. package/dist/src/mirror/mirror.test.js +0 -185
  244. package/dist/src/raptor-inject-summaries.test.js +0 -162
  245. package/dist/src/ratio.bench.test.js +0 -897
  246. package/dist/src/recall.integration.test.js +0 -77
  247. package/dist/src/recall.test.js +0 -379
  248. package/dist/src/sprint14.test.js +0 -219
  249. package/dist/src/sprint4x-rag-verification.test.js +0 -93
  250. package/dist/src/store/compression.test.js +0 -139
  251. package/dist/src/store/memoryIndex.test.js +0 -51
  252. package/dist/src/store/migrate.test.js +0 -139
  253. package/dist/src/store/pgOpenGuard.test.js +0 -74
  254. package/dist/src/store/phase01.test.js +0 -81
  255. package/dist/src/store/phase04.test.js +0 -54
  256. package/dist/src/store/sprint10.test.js +0 -186
  257. package/dist/src/store/sqlite/game-achievements.test.js +0 -67
  258. package/dist/src/store/sqlite/game-scores.test.js +0 -106
  259. package/dist/src/store/sqlite/game-state.test.js +0 -76
  260. package/dist/src/store/sqlite/global-index.test.js +0 -136
  261. package/dist/src/store/sqlite/mechanical-fix.test.js +0 -146
  262. package/dist/src/store/sqlite/perf-samples.test.js +0 -146
  263. package/dist/src/store/sqlite/raptor.test.js +0 -106
  264. package/dist/src/store/sqlite/turns.test.js +0 -183
  265. package/dist/src/store/sqlite.cachehit.test.js +0 -55
  266. package/dist/src/store/sqlite.dbmaint.test.js +0 -158
  267. package/dist/src/store/sqlite.dbmirror.test.js +0 -184
  268. package/dist/src/store/turns/contract-compliance.test.js +0 -428
  269. package/dist/src/store/turns/memory-store.test.js +0 -9
  270. package/dist/src/store/turns/migrations.test.js +0 -138
  271. package/dist/src/store/turns/sqlite-store.test.js +0 -144
  272. package/dist/src/store/vectorIndex.test.js +0 -123
  273. package/dist/src/store.test.js +0 -161
  274. package/dist/src/supersede.test.js +0 -36
  275. package/dist/src/topics/cluster.test.js +0 -150
  276. package/dist/src/topics/store.test.js +0 -117
  277. package/dist/src/types.test.js +0 -9
  278. package/dist/src/vector-search-cache.test.js +0 -157
  279. package/dist/src/vectorStore.test.js +0 -479
  280. package/dist/src/wiki.test.js +0 -171
  281. package/extensions/conflict-scan.test.ts +0 -129
  282. package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +0 -2
  283. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +0 -2
  284. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +0 -1
  285. package/extensions/dashboard-client/dist/assets/GameTab-BLDudcO5.js +0 -2
  286. package/extensions/dashboard-client/dist/assets/OverviewTab-D4kGvsiQ.js +0 -2
  287. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +0 -2
  288. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +0 -1
  289. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +0 -2
  290. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +0 -1
  291. package/extensions/dashboard-client/dist/assets/index-BEdKlpAB.css +0 -1
  292. package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js +0 -42
  293. package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js.map +0 -1
  294. package/extensions/dashboard-server/api-contracts.test.ts +0 -1065
  295. package/extensions/dashboard-server/html.test.ts +0 -62
  296. package/extensions/dashboard-server/perf-server.test.ts +0 -101
  297. package/extensions/dashboard-server/routes-cache.test.ts +0 -302
  298. package/extensions/dashboard-server/server.test.ts +0 -131
  299. package/extensions/dashboard-server-s32.test.ts +0 -195
  300. package/extensions/dashboard-server.test.ts +0 -382
  301. package/extensions/mega-cache-replay.test.ts +0 -526
  302. package/extensions/mega-compact-s38.test.ts +0 -1433
  303. package/extensions/mega-compact.test.ts +0 -1509
  304. package/extensions/mega-events.test.ts +0 -55
  305. package/extensions/mega-game-cmds.test.ts +0 -197
  306. package/extensions/mega-runtime/snapshot.test.ts +0 -139
  307. package/extensions/mega-runtime/state.test.ts +0 -262
  308. package/extensions/mega-runtime/widget.test.ts +0 -468
  309. package/extensions/mega-shutdown-widget.test.ts +0 -141
  310. package/extensions/mega-teamrun.test.ts +0 -176
  311. package/extensions/mega-trim.test.ts +0 -64
  312. package/extensions/mega-turn-store.test.ts +0 -108
  313. package/src/boundary.test.ts +0 -187
  314. package/src/compact.test.ts +0 -99
  315. package/src/config/themes.test.ts +0 -116
  316. package/src/dedup/dedup.test.ts +0 -46
  317. package/src/dedup/l1.test.ts +0 -57
  318. package/src/dedup/raptor/buildHistory.test.ts +0 -353
  319. package/src/dedup/raptor/multilevel-serve.test.ts +0 -273
  320. package/src/dedup/raptor/multilevel.test.ts +0 -325
  321. package/src/dedup/raptor/promote.test.ts +0 -82
  322. package/src/dedup/raptor/raptor.test.ts +0 -297
  323. package/src/dedup/raptor/retrieval.test.ts +0 -118
  324. package/src/dedup/raptor/serve-gate.test.ts +0 -348
  325. package/src/dedup/sprint12.test.ts +0 -242
  326. package/src/dedup-engine.test.ts +0 -670
  327. package/src/driftDetection.test.ts +0 -100
  328. package/src/e2e.test.ts +0 -843
  329. package/src/engine.test.ts +0 -123
  330. package/src/extractive.test.ts +0 -174
  331. package/src/fork.test.ts +0 -94
  332. package/src/game/scoring.test.ts +0 -103
  333. package/src/httpEmbedder.test.ts +0 -286
  334. package/src/importance.test.ts +0 -538
  335. package/src/intent.test.ts +0 -87
  336. package/src/log.test.ts +0 -47
  337. package/src/mechanical-fix.test.ts +0 -70
  338. package/src/memory.test.ts +0 -92
  339. package/src/memoryConsolidate.test.ts +0 -47
  340. package/src/memoryOps.test.ts +0 -148
  341. package/src/memoryRecall.test.ts +0 -194
  342. package/src/memoryRoundtrip.test.ts +0 -155
  343. package/src/metrics/turns.test.ts +0 -165
  344. package/src/mirror/mirror.test.ts +0 -240
  345. package/src/raptor-inject-summaries.test.ts +0 -228
  346. package/src/ratio.bench.test.ts +0 -1316
  347. package/src/recall.integration.test.ts +0 -96
  348. package/src/recall.test.ts +0 -467
  349. package/src/sprint14.test.ts +0 -245
  350. package/src/sprint4x-rag-verification.test.ts +0 -119
  351. package/src/store/compression.test.ts +0 -168
  352. package/src/store/memoryIndex.test.ts +0 -61
  353. package/src/store/migrate.test.ts +0 -158
  354. package/src/store/pgOpenGuard.test.ts +0 -89
  355. package/src/store/phase01.test.ts +0 -88
  356. package/src/store/phase04.test.ts +0 -58
  357. package/src/store/sprint10.test.ts +0 -214
  358. package/src/store/sqlite/game-achievements.test.ts +0 -80
  359. package/src/store/sqlite/game-scores.test.ts +0 -132
  360. package/src/store/sqlite/game-state.test.ts +0 -89
  361. package/src/store/sqlite/global-index.test.ts +0 -222
  362. package/src/store/sqlite/mechanical-fix.test.ts +0 -162
  363. package/src/store/sqlite/perf-samples.test.ts +0 -169
  364. package/src/store/sqlite/raptor.test.ts +0 -139
  365. package/src/store/sqlite/turns.test.ts +0 -218
  366. package/src/store/sqlite.cachehit.test.ts +0 -68
  367. package/src/store/sqlite.dbmaint.test.ts +0 -193
  368. package/src/store/sqlite.dbmirror.test.ts +0 -229
  369. package/src/store/turns/contract-compliance.test.ts +0 -562
  370. package/src/store/turns/memory-store.test.ts +0 -15
  371. package/src/store/turns/migrations.test.ts +0 -161
  372. package/src/store/turns/sqlite-store.test.ts +0 -161
  373. package/src/store/vectorIndex.test.ts +0 -140
  374. package/src/store.test.ts +0 -191
  375. package/src/supersede.test.ts +0 -42
  376. package/src/topics/cluster.test.ts +0 -176
  377. package/src/topics/store.test.ts +0 -132
  378. package/src/types.test.ts +0 -10
  379. package/src/vector-search-cache.test.ts +0 -190
  380. package/src/vectorStore.test.ts +0 -533
  381. package/src/wiki.test.ts +0 -217
@@ -0,0 +1,80 @@
1
+ /**
2
+ * api-contracts/setup.ts — Setup wizard API response types.
3
+ *
4
+ * Types for the /api/setup-status and /api/setup-detect endpoints used
5
+ * by the /megasetup command and dashboard Setup tab.
6
+ *
7
+ * PREVENT-PI-004: type definitions only, no network code.
8
+ * PREVENT-011: no `any` type — all types are explicit.
9
+ */
10
+
11
+ /**
12
+ * Response for GET /api/setup-status.
13
+ * Returns the current embedder configuration read from the environment.
14
+ */
15
+ export interface SetupStatusResponse {
16
+ /** Which embedder implementation is currently active. */
17
+ readonly currentEmbedder: "trigram" | "http" | "minilm" | "unknown";
18
+ /** The embedding URL if httpEmbedder is active, otherwise null. */
19
+ readonly embeddingUrl: string | null;
20
+ /** The MEGACOMPACT_EMBED_CACHE value (number as string), or null. */
21
+ readonly embedCache: string | null;
22
+ /** Whether MEGACOMPACT_MINILM is set to a truthy value. */
23
+ readonly minilm: boolean;
24
+ }
25
+
26
+ /**
27
+ * A single detection result for one embedder backend.
28
+ */
29
+ export interface DetectResult {
30
+ readonly installed: boolean;
31
+ readonly detail: string | null;
32
+ }
33
+
34
+ /**
35
+ * Ollama-specific detection result with model listing and runtime status.
36
+ */
37
+ export interface OllamaDetectResult extends DetectResult {
38
+ readonly installed: boolean;
39
+ readonly models: string[];
40
+ readonly running: boolean;
41
+ readonly detail: string | null;
42
+ }
43
+
44
+ /**
45
+ * Response for GET /api/setup-detect.
46
+ * Best-effort detection of local embedder backends available on the machine.
47
+ * All fields are best-effort; failures are non-fatal.
48
+ */
49
+ export interface SetupDetectResponse {
50
+ /** Ollama detection: installed, available models, whether the server is running. */
51
+ readonly ollama: OllamaDetectResult | null;
52
+ /** llama.cpp / llama-server detection on PATH. */
53
+ readonly llamaCpp: DetectResult | null;
54
+ /** ONNX runtime detection (onnxruntime-node in node_modules). */
55
+ readonly onnx: DetectResult | null;
56
+ /** Error message if detection itself failed, or null. */
57
+ readonly error: string | null;
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
+ }
@@ -16,138 +16,155 @@ import { DatabaseSync } from "node:sqlite";
16
16
  import type { IndexRepo, IndexIndex } from "./types.js";
17
17
 
18
18
  export function getIndexDir(): string {
19
- const override = process.env.MEGACOMPACT_INDEX_DIR;
20
- if (override && override.trim() !== "") return override;
21
- try {
22
- return join(homedir(), ".mega-compact-index");
23
- } catch {
24
- return join("/tmp", ".mega-compact-index");
25
- }
19
+ const override = process.env.MEGACOMPACT_INDEX_DIR;
20
+ if (override && override.trim() !== "") return override;
21
+ try {
22
+ return join(homedir(), ".mega-compact-index");
23
+ } catch {
24
+ return join("/tmp", ".mega-compact-index");
25
+ }
26
26
  }
27
27
 
28
28
  /** Read the machine-wide repo registry from SQLite (read-only, single shot). */
29
29
  export function readIndex(): IndexIndex | null {
30
- const indexPath = join(getIndexDir(), "index.sqlite");
31
- if (!existsSync(indexPath)) return null;
32
- let db: DatabaseSync | undefined;
33
- try {
34
- // Read-only + immutable WAL so a concurrent writer's WAL never blocks us.
35
- db = new DatabaseSync(indexPath, { readOnly: true });
36
- db.exec("PRAGMA journal_mode = WAL");
37
- const rows = db
38
- .prepare("SELECT * FROM repo_registry ORDER BY last_seen DESC")
39
- .all() as Record<string, unknown>[];
40
- const mapped: IndexRepo[] = rows.map((r) => ({
41
- repoRoot: String(r.repo_root ?? ""),
42
- displayName: String(r.display_name ?? ""),
43
- stateDir: String(r.state_dir ?? ""),
44
- checkpointCount: Number(r.checkpoint_count ?? 0),
45
- tokensSaved: Number(r.tokens_saved ?? 0),
46
- compressedOriginalBytes: Number(r.compressed_original_bytes ?? 0),
47
- lastCompactedAt: (r.last_compacted_at as number | null) ?? null,
48
- provider: (r.provider as string | null) ?? null,
49
- providerName: (r.provider_name as string | null) ?? null,
50
- modelName: (r.model_name as string | null) ?? null,
51
- inputRate: (r.input_rate as number | null) ?? null,
52
- outputRate: (r.output_rate as number | null) ?? null,
53
- lastSeen: Number(r.last_seen ?? 0),
54
- // Defaults — enriched below from each repo's own store.
55
- tokensKept: 0,
56
- tokensDropped: 0,
57
- sessions: 0,
58
- contextWindow: null,
59
- maxTokens: null,
60
- reasoning: null,
61
- providerCachePct: null,
62
- providerCacheRead: null,
63
- providerCacheWrite: null,
64
- }));
65
- // Enrich each repo with per-store token + model detail read directly via
66
- // node:sqlite (same zero-dependency invariant as readIndex; no store graph
67
- // import). Best-effort: a missing/corrupt store degrades to the defaults
68
- // above so the dashboard never fails to render.
69
- for (const repo of mapped) {
70
- try {
71
- const storePath = join(repo.stateDir, "sqlite.db");
72
- if (existsSync(storePath)) {
73
- const sdb = new DatabaseSync(storePath, { readOnly: true });
74
- try {
75
- const tok = sdb
76
- .prepare(
77
- `SELECT COALESCE(SUM(token_estimate),0) AS kept,
30
+ const indexPath = join(getIndexDir(), "index.sqlite");
31
+ if (!existsSync(indexPath)) return null;
32
+ let db: DatabaseSync | undefined;
33
+ try {
34
+ // Read-only + immutable WAL so a concurrent writer's WAL never blocks us.
35
+ db = new DatabaseSync(indexPath, { readOnly: true });
36
+ db.exec("PRAGMA journal_mode = WAL");
37
+ const rows = db
38
+ .prepare("SELECT * FROM repo_registry ORDER BY last_seen DESC")
39
+ .all() as Record<string, unknown>[];
40
+ const mapped: IndexRepo[] = rows.map((r) => ({
41
+ repoRoot: String(r.repo_root ?? ""),
42
+ displayName: String(r.display_name ?? ""),
43
+ stateDir: String(r.state_dir ?? ""),
44
+ checkpointCount: Number(r.checkpoint_count ?? 0),
45
+ tokensSaved: Number(r.tokens_saved ?? 0),
46
+ compressedOriginalBytes: Number(r.compressed_original_bytes ?? 0),
47
+ lastCompactedAt: (r.last_compacted_at as number | null) ?? null,
48
+ provider: (r.provider as string | null) ?? null,
49
+ providerName: (r.provider_name as string | null) ?? null,
50
+ modelName: (r.model_name as string | null) ?? null,
51
+ inputRate: (r.input_rate as number | null) ?? null,
52
+ outputRate: (r.output_rate as number | null) ?? null,
53
+ lastSeen: Number(r.last_seen ?? 0),
54
+ // Defaults — enriched below from each repo's own store.
55
+ tokensKept: 0,
56
+ tokensDropped: 0,
57
+ sessions: 0,
58
+ contextWindow: null,
59
+ maxTokens: null,
60
+ reasoning: null,
61
+ providerCachePct: null,
62
+ providerCacheRead: null,
63
+ providerCacheWrite: null,
64
+ }));
65
+ // Enrich each repo with per-store token + model detail read directly via
66
+ // node:sqlite (same zero-dependency invariant as readIndex; no store graph
67
+ // import). Best-effort: a missing/corrupt store degrades to the defaults
68
+ // above so the dashboard never fails to render.
69
+ for (const repo of mapped) {
70
+ try {
71
+ const storePath = join(repo.stateDir, "sqlite.db");
72
+ if (existsSync(storePath)) {
73
+ const sdb = new DatabaseSync(storePath, { readOnly: true });
74
+ try {
75
+ const tok = sdb
76
+ .prepare(
77
+ `SELECT COALESCE(SUM(token_estimate),0) AS kept,
78
78
  COALESCE(SUM(original_token_estimate),0) AS dropped,
79
79
  COUNT(DISTINCT session_id) AS sess
80
80
  FROM context_chunks WHERE dedup_status != 'removed'`,
81
- )
82
- .get() as { kept: number; dropped: number; sess: number };
83
- repo.tokensKept = Number(tok.kept ?? 0);
84
- repo.tokensDropped = Number(tok.dropped ?? 0);
85
- repo.sessions = Number(tok.sess ?? 0);
86
- const mrow = sdb
87
- .prepare(
88
- `SELECT context_window, max_tokens, reasoning
81
+ )
82
+ .get() as { kept: number; dropped: number; sess: number };
83
+ repo.tokensKept = Number(tok.kept ?? 0);
84
+ repo.tokensDropped = Number(tok.dropped ?? 0);
85
+ repo.sessions = Number(tok.sess ?? 0);
86
+ const mrow = sdb
87
+ .prepare(
88
+ `SELECT context_window, max_tokens, reasoning
89
89
  FROM model_snapshots ORDER BY captured_at DESC LIMIT 1`,
90
- )
91
- .get() as { context_window: number; max_tokens: number; reasoning: number } | undefined;
92
- if (mrow) {
93
- repo.contextWindow = Number(mrow.context_window ?? 0) || null;
94
- repo.maxTokens = Number(mrow.max_tokens ?? 0) || null;
95
- repo.reasoning = Number(mrow.reasoning ?? 0) === 1;
96
- }
97
- // Provider prompt-cache stats (E.3)
98
- try {
99
- const pRow = sdb
100
- .prepare(
101
- `SELECT
90
+ )
91
+ .get() as
92
+ | {
93
+ context_window: number;
94
+ max_tokens: number;
95
+ reasoning: number;
96
+ }
97
+ | undefined;
98
+ if (mrow) {
99
+ repo.contextWindow = Number(mrow.context_window ?? 0) || null;
100
+ repo.maxTokens = Number(mrow.max_tokens ?? 0) || null;
101
+ repo.reasoning = Number(mrow.reasoning ?? 0) === 1;
102
+ }
103
+ // Provider prompt-cache stats (E.3)
104
+ try {
105
+ const pRow = sdb
106
+ .prepare(
107
+ `SELECT
102
108
  SUM(cache_read) AS totalRead,
103
109
  SUM(cache_write) AS totalWrite,
104
110
  SUM(cache_read + cache_write) * 1.0 / MAX(SUM(cache_read + cache_write + tokens_in), 1) AS hitPct
105
111
  FROM perf_samples
106
112
  WHERE cache_read > 0 OR cache_write > 0`,
107
- )
108
- .get() as { totalRead: number; totalWrite: number; hitPct: number } | undefined;
109
- if (pRow && pRow.totalRead > 0) {
110
- repo.providerCacheRead = Number(pRow.totalRead);
111
- repo.providerCacheWrite = Number(pRow.totalWrite);
112
- repo.providerCachePct = Number((pRow.hitPct * 100).toFixed(1));
113
- }
114
- } catch {
115
- /* best-effort — perf_samples may not exist */
116
- }
117
- } finally {
118
- sdb.close();
119
- }
120
- }
121
- } catch {
122
- /* best-effort — keep the defaults */
123
- }
124
- }
125
- // Defensive display hygiene (belt-and-suspenders — the real fix is that
126
- // tests now isolate via MEGACOMPACT_INDEX_DIR): drop transient test/temp
127
- // paths that should never have been real repos, and collapse duplicate
128
- // display names to the most-recently-seen row (rows are last_seen DESC, so
129
- // the first occurrence wins). Keeps the All-repos list readable.
130
- const isTransient = (p: string) =>
131
- /^\/tmp\//.test(p) || /^\/private\/tmp\//.test(p) || /^\/var\/folders\//.test(p) ||
132
- /\/mc-(ext|e2e|resume|recall)-/.test(p);
133
- const seenName = new Set<string>();
134
- const repos: IndexRepo[] = [];
135
- for (const r of mapped) {
136
- if (isTransient(r.repoRoot)) continue;
137
- if (seenName.has(r.displayName)) continue;
138
- seenName.add(r.displayName);
139
- repos.push(r);
140
- }
141
- const summary = {
142
- totalRepos: repos.length,
143
- totalCheckpoints: repos.reduce((a, r) => a + r.checkpointCount, 0),
144
- totalTokensSaved: repos.reduce((a, r) => a + r.tokensSaved, 0),
145
- totalCompressedOriginalBytes: repos.reduce((a, r) => a + r.compressedOriginalBytes, 0),
146
- };
147
- return { updatedAt: new Date().toISOString(), summary, repos };
148
- } catch {
149
- return null;
150
- } finally {
151
- try { db?.close(); } catch { /* ignore */ }
152
- }
113
+ )
114
+ .get() as
115
+ | { totalRead: number; totalWrite: number; hitPct: number }
116
+ | undefined;
117
+ if (pRow && pRow.totalRead > 0) {
118
+ repo.providerCacheRead = Number(pRow.totalRead);
119
+ repo.providerCacheWrite = Number(pRow.totalWrite);
120
+ repo.providerCachePct = Number((pRow.hitPct * 100).toFixed(1));
121
+ }
122
+ } catch {
123
+ /* best-effort — perf_samples may not exist */
124
+ }
125
+ } finally {
126
+ sdb.close();
127
+ }
128
+ }
129
+ } catch {
130
+ /* best-effort — keep the defaults */
131
+ }
132
+ }
133
+ // Defensive display hygiene (belt-and-suspenders the real fix is that
134
+ // tests now isolate via MEGACOMPACT_INDEX_DIR): drop transient test/temp
135
+ // paths that should never have been real repos, and collapse duplicate
136
+ // display names to the most-recently-seen row (rows are last_seen DESC, so
137
+ // the first occurrence wins). Keeps the All-repos list readable.
138
+ const isTransient = (p: string) =>
139
+ /^\/tmp\//.test(p) ||
140
+ /^\/private\/tmp\//.test(p) ||
141
+ /^\/var\/folders\//.test(p) ||
142
+ /\/mc-(ext|e2e|resume|recall)-/.test(p);
143
+ const seenName = new Set<string>();
144
+ const repos: IndexRepo[] = [];
145
+ for (const r of mapped) {
146
+ if (isTransient(r.repoRoot)) continue;
147
+ if (seenName.has(r.displayName)) continue;
148
+ seenName.add(r.displayName);
149
+ repos.push(r);
150
+ }
151
+ const summary = {
152
+ totalRepos: repos.length,
153
+ totalCheckpoints: repos.reduce((a, r) => a + r.checkpointCount, 0),
154
+ totalTokensSaved: repos.reduce((a, r) => a + r.tokensSaved, 0),
155
+ totalCompressedOriginalBytes: repos.reduce(
156
+ (a, r) => a + r.compressedOriginalBytes,
157
+ 0,
158
+ ),
159
+ };
160
+ return { updatedAt: new Date().toISOString(), summary, repos };
161
+ } catch {
162
+ return null;
163
+ } finally {
164
+ try {
165
+ db?.close();
166
+ } catch {
167
+ /* ignore */
168
+ }
169
+ }
153
170
  }
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * GET /api/provider-cache — Lifetime provider prompt cache hit-rate aggregates
5
5
  * plus dollar savings estimate (priced from latest model snapshot).
6
+ * Accepts optional ?minutes=N, ?since=<epoch-ms>, ?until=<epoch-ms> (S53A).
6
7
  *
7
8
  * Guardrails: PREVENT-PI-004 (loopback-only), PREVENT-001 (null-safe JSON parse),
8
9
  * PREVENT-002 (parameterized SQL). Loopback-only localhost dashboard endpoint.
@@ -24,97 +25,207 @@ export function handleProviderCache(
24
25
  ctx: RouteContext,
25
26
  ): boolean {
26
27
  if (!req.url?.startsWith("/api/provider-cache")) return false;
27
-
28
- const { stateDir } = ctx;
29
-
30
- // /api/provider-cache — GET returns lifetime provider prompt cache aggregates
31
- // from perf_samples (cache_hit_pct rows with meta JSON) plus an optional
32
- // dollar savings estimate priced from the latest model snapshot. The dashboard
33
- // server is a detached child with no MegaRuntime ref, so it reads via a
34
- // require()'d sqlite helper (same pattern as /api/perf). Non-GET → 405.
35
- // PREVENT-PI-004: loopback.
36
- const pfReq = createRequire(import.meta.url);
37
- const { readProviderCacheLifetime, latestModelSnapshot } = pfReq(
38
- "../../src/store/sqlite.js",
39
- ) as typeof import("../../src/store/sqlite.js");
40
- if (req.method !== "GET") {
41
- // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
42
- res.writeHead(405, { "Content-Type": "application/json" });
43
- res.end(JSON.stringify({ error: "method_not_allowed" }));
44
- return true;
45
- }
46
28
  try {
47
- const lifetime = readProviderCacheLifetime(stateDir); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
29
+ const stateDir = ctx.stateDir;
30
+ // PREVENT-PI-004: loopback.
31
+ const pfReq = createRequire(import.meta.url);
32
+ const { readProviderCacheLifetime, readProviderCacheWindow, latestModelSnapshot } = pfReq(
33
+ "../../src/store/sqlite.js",
34
+ ) as typeof import("../../src/store/sqlite.js");
35
+ if (req.method !== "GET") {
36
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
37
+ res.writeHead(405, { "Content-Type": "application/json" });
38
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
39
+ return true;
40
+ }
48
41
 
49
- // D3: compute dollar savings from the latest model snapshot. Null-safe:
50
- // when no snapshot or zero input rate, savings stays null (UI shows "—").
51
- let savings: {
52
- cacheReadSaved: number;
53
- cacheWriteCost: number;
54
- netSaved: number;
55
- model: string;
56
- inputRate: number;
57
- } | null = null;
58
- try {
59
- const snap = latestModelSnapshot(stateDir); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
60
- if (snap && snap.inputRate > 0) {
61
- const s = computeCacheSavings(
62
- lifetime.totalCacheRead,
63
- lifetime.totalCacheWrite,
64
- snap.inputRate,
65
- );
66
- savings = {
67
- cacheReadSaved: s.cacheReadSaved,
68
- cacheWriteCost: s.cacheWriteCost,
69
- netSaved: s.netSaved,
70
- model: snap.modelName ?? snap.modelId ?? "unknown",
71
- inputRate: snap.inputRate,
72
- };
42
+ // Parse query params: ?minutes=N | ?since=&until= (mutually exclusive).
43
+ let windowMinutes: number | null = null;
44
+ let sinceTs = 0;
45
+ let untilTs = 0;
46
+ if (req.url) {
47
+ const qIdx = req.url.indexOf("?");
48
+ if (qIdx !== -1) {
49
+ const params = new URLSearchParams(req.url.slice(qIdx));
50
+ const m = params.get("minutes");
51
+ if (m != null) {
52
+ const n = parseInt(m, 10);
53
+ if (Number.isFinite(n) && n > 0 && String(n) === m.trim()) {
54
+ windowMinutes = n;
55
+ }
56
+ }
57
+ // S53A: ?since=&until= epoch-ms timestamps.
58
+ const s = params.get("since");
59
+ const u = params.get("until");
60
+ if (s != null) {
61
+ const sn = Number(s);
62
+ if (Number.isFinite(sn) && sn >= 0) sinceTs = sn;
63
+ }
64
+ if (u != null) {
65
+ const un = Number(u);
66
+ if (Number.isFinite(un) && un > 0) untilTs = un;
67
+ }
73
68
  }
69
+ }
70
+
71
+ // S53A: read prefix_break samples in the same window as the aggregates.
72
+ // Import lazily to avoid a circular dependency in the test harness.
73
+ let prefixBreaks: Array<{
74
+ id: number;
75
+ ts: number;
76
+ cause: "recall" | "compaction" | "inject" | "other";
77
+ confidence: number;
78
+ prevHitPct: number;
79
+ currHitPct: number;
80
+ breakAt: number;
81
+ }> = [];
82
+ try {
83
+ // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
84
+ const { readPrefixBreaks } = pfReq(
85
+ "../../src/store/sqlite/perf-samples.js",
86
+ ) as typeof import("../../src/store/sqlite/perf-samples.js");
87
+ // When ?since=&until= is used, pass those directly; otherwise derive
88
+ // from windowMinutes or 0 (no bounds = lifetime).
89
+ const pSince = sinceTs > 0 ? sinceTs : (windowMinutes != null ? Date.now() - windowMinutes * 60_000 : 0);
90
+ const pUntil = untilTs > 0 ? untilTs : (windowMinutes != null ? Date.now() : 0);
91
+ const rows = readPrefixBreaks(stateDir, pSince, pUntil);
92
+ prefixBreaks = rows.map((r) => ({
93
+ id: r.id,
94
+ ts: r.ts,
95
+ cause: r.meta.cause,
96
+ confidence: r.meta.confidence,
97
+ prevHitPct: r.meta.prevHitPct,
98
+ currHitPct: r.meta.currHitPct,
99
+ breakAt: r.meta.breakAt,
100
+ }));
74
101
  } catch {
75
- /* pricing unavailable — savings stays null */
102
+ /* prefix-break read unavailable — response still returns empty array */
76
103
  }
77
104
 
78
- const body: {
79
- cache: {
80
- avgHitPct: number;
81
- turnCount: number;
82
- totalCacheRead: number;
83
- totalCacheWrite: number;
84
- totalInput: number;
85
- firstTurnAt: string | null;
86
- latestTurnAt: string | null;
87
- };
88
- savings: {
105
+ try {
106
+ const lifetime = windowMinutes != null
107
+ ? readProviderCacheWindow(stateDir, windowMinutes)
108
+ : readProviderCacheLifetime(stateDir); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
109
+
110
+ // D3: compute dollar savings from the latest model snapshot. Null-safe:
111
+ // when no snapshot or zero input rate, savings stays null (UI shows "—").
112
+ let savings: {
89
113
  cacheReadSaved: number;
90
114
  cacheWriteCost: number;
91
115
  netSaved: number;
92
116
  model: string;
93
117
  inputRate: number;
94
- } | null;
95
- updatedAt: string;
96
- } = {
97
- cache: {
98
- avgHitPct: lifetime.avgHitPct,
99
- turnCount: lifetime.sampleCount,
100
- totalCacheRead: lifetime.totalCacheRead,
101
- totalCacheWrite: lifetime.totalCacheWrite,
102
- totalInput: lifetime.totalInput,
103
- firstTurnAt: lifetime.firstSampleAt,
104
- latestTurnAt: lifetime.latestSampleAt,
105
- },
106
- savings,
107
- updatedAt: new Date().toISOString(),
108
- };
109
- // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
118
+ } | null = null;
119
+ try {
120
+ const snap = latestModelSnapshot(stateDir); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
121
+ if (snap && snap.inputRate > 0) {
122
+ const s = computeCacheSavings(
123
+ lifetime.totalCacheRead,
124
+ lifetime.totalCacheWrite,
125
+ snap.inputRate,
126
+ );
127
+ savings = {
128
+ cacheReadSaved: s.cacheReadSaved,
129
+ cacheWriteCost: s.cacheWriteCost,
130
+ netSaved: s.netSaved,
131
+ model: snap.modelName ?? snap.modelId ?? "unknown",
132
+ inputRate: snap.inputRate,
133
+ };
134
+ }
135
+ } catch {
136
+ /* pricing unavailable — savings stays null */
137
+ }
138
+
139
+ const body: {
140
+ cache: {
141
+ avgHitPct: number;
142
+ turnCount: number;
143
+ totalCacheRead: number;
144
+ totalCacheWrite: number;
145
+ totalInput: number;
146
+ firstTurnAt: string | null;
147
+ latestTurnAt: string | null;
148
+ byModel: Array<{
149
+ model: string;
150
+ hitPct: number;
151
+ totalCacheRead: number;
152
+ totalCacheWrite: number;
153
+ sampleCount: number;
154
+ }>;
155
+ };
156
+ savings: {
157
+ cacheReadSaved: number;
158
+ cacheWriteCost: number;
159
+ netSaved: number;
160
+ model: string;
161
+ inputRate: number;
162
+ } | null;
163
+ updatedAt: string;
164
+ windowMinutes?: number | null;
165
+ prefixBreaks: typeof prefixBreaks;
166
+ } = {
167
+ cache: {
168
+ avgHitPct: lifetime.avgHitPct,
169
+ turnCount: lifetime.sampleCount,
170
+ totalCacheRead: lifetime.totalCacheRead,
171
+ totalCacheWrite: lifetime.totalCacheWrite,
172
+ totalInput: lifetime.totalInput,
173
+ firstTurnAt: lifetime.firstSampleAt,
174
+ latestTurnAt: lifetime.latestSampleAt,
175
+ byModel: lifetime.byModel,
176
+ },
177
+ savings,
178
+ updatedAt: new Date().toISOString(),
179
+ windowMinutes,
180
+ prefixBreaks,
181
+ };
182
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
183
+ res.writeHead(200, { "Content-Type": "application/json" });
184
+ res.end(JSON.stringify(body));
185
+ } catch (e) {
186
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
187
+ res.writeHead(500, { "Content-Type": "application/json" });
188
+ res.end(
189
+ JSON.stringify({
190
+ error: "provider_cache_unavailable",
191
+ detail: String(e),
192
+ }),
193
+ );
194
+ }
195
+ return true;
196
+ } catch {
197
+ return false;
198
+ }
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);
110
222
  res.writeHead(200, { "Content-Type": "application/json" });
111
- res.end(JSON.stringify(body));
223
+ res.end(JSON.stringify(result));
112
224
  } catch (e) {
113
- // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
114
225
  res.writeHead(500, { "Content-Type": "application/json" });
115
226
  res.end(
116
227
  JSON.stringify({
117
- error: "provider_cache_unavailable",
228
+ error: "cache_stripes_unavailable",
118
229
  detail: String(e),
119
230
  }),
120
231
  );
@@ -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),