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
@@ -1,4 +1,4 @@
1
- import { openStore, appendRawTranscript, writeCheckpointEpoch, } from "../../src/store/sqlite.js";
1
+ import { openStore, writeCheckpointEpoch, } from "../../src/store/sqlite.js";
2
2
  import { epochIdFor } from "../../src/mirror/epoch.js";
3
3
  import { autoCompactCheck } from "../../src/compact.js";
4
4
  import { estimateSessionTokens } from "../../src/tokens.js";
@@ -9,75 +9,9 @@ import { openTurnStore } from "../../src/store/turns/connection.js";
9
9
  import { buildTopicModel, createTopicStore, bumpWikiCompactCounter, } from "../../src/topics/index.js";
10
10
  import { computeLiveTrimCut, liveTrimSummaryMessage } from "../mega-trim.js";
11
11
  import { pressureFromPct, pressureRatio, } from "../mega-config.js";
12
- import { computeContentDigest } from "../../src/dedup/digest.js";
13
- /**
14
- * Recursively canonicalize a value for deterministic JSON serialization:
15
- * - Objects: keys sorted alphabetically, values recursively canonicalized
16
- * - Arrays: elements recursively canonicalized (array ORDER is preserved)
17
- * - Primitives: returned as-is
18
- *
19
- * F5 fix: shallow-sorted JSON.stringify(content, Object.keys(content).sort())
20
- * omitted nested keys not in the top-level keys array and only sorted one level.
21
- * This recursively sorts every object at every depth so semantically-equal
22
- * differently-ordered content hashes identically.
23
- */
24
- function canonicalize(value) {
25
- if (value === null || value === undefined)
26
- return value;
27
- if (typeof value !== "object")
28
- return value;
29
- if (Array.isArray(value))
30
- return value.map(canonicalize);
31
- // Plain object: sort keys, recursively canonicalize values.
32
- const sorted = Object.keys(value).sort();
33
- const out = {};
34
- for (const k of sorted) {
35
- out[k] = canonicalize(value[k]);
36
- }
37
- return out;
38
- }
39
- /**
40
- * Convert a pi AgentMessage to a RawTranscriptRow for the DB mirror.
41
- * content_bytes is canonical JSON (sorted keys, recursive) for deterministic
42
- * storage; contentHash is the canonical digest (normalize + hash) that matches
43
- * what dedupTranscript computes so the two pipelines use the same linkage key.
44
- *
45
- * F4 fix: the hash key MUST match on both sides of the append/dedup split.
46
- * Using computeContentDigest here (normalize → hash) ensures content_ref set
47
- * by dedupTranscript always resolves to an existing dedup_mirror row even for
48
- * whitespace/case-variant content. F5 fix: recursive canonicalize replaces the
49
- * broken shallow-sorted JSON.stringify replacer.
50
- */
51
- function toRawTranscriptRow(msg, sessionId, epochId, currentTurn) {
52
- // Narrow to Message union (has content + timestamp).
53
- const m = msg;
54
- const content = m.content;
55
- if (content == null || content === "")
56
- return null;
57
- // Canonical bytes: string content → normalize for consistent byte content;
58
- // object content → recursive canonicalize then JSON (no replacer).
59
- const contentBytes = typeof content === "string"
60
- ? content // F5: strings are primitives; their byte content is fixed
61
- : JSON.stringify(canonicalize(content));
62
- // F4 fix: use the same digest as dedupTranscript so contentHash is consistent
63
- // on both sides of the append/dedup split. computeContentDigest normalizes
64
- // (strip ANSI, NFC, case-fold, collapse whitespace) then hashes → the same
65
- // key is stored in raw_transcript.content_hash AND used as the dedup link.
66
- const { contentHash } = computeContentDigest(contentBytes);
67
- return {
68
- contentHash,
69
- sessionId,
70
- seq: 0, // assigned by appendRawTranscript (COALESCE(MAX(seq),0)+1)
71
- role: m.role ?? "unknown",
72
- contentBytes,
73
- toolName: m.toolName ?? null,
74
- messageTimestamp: m.timestamp ?? null,
75
- checkpointEpoch: epochId,
76
- // S50: label the row with the turn that produced it (per-turn dedup /
77
- // compression-by-turn metrics). Null when the writer omits it (back-compat).
78
- turnIndex: currentTurn ?? null,
79
- };
80
- }
12
+ import { appendMirrorMessages } from "./mirror-append.js";
13
+ import { stagedForTail, withRecallTail } from "./recall-tail.js";
14
+ import { buildSeparatedPrompt } from "./separated-prompt.js";
81
15
  /** Register the context event handler (live-trim auto-trigger). */
82
16
  export function registerContextHandler(pi, runtime, config) {
83
17
  // ---- Auto-trigger: live trim (compact and continue) + native durable ----
@@ -98,6 +32,24 @@ export function registerContextHandler(pi, runtime, config) {
98
32
  const usage = ctx.getContextUsage();
99
33
  const pct = usage?.percent;
100
34
  const messages = event.messages;
35
+ // S53: helper to inject the staged recall/memory block as a user-role
36
+ // tail message at any view-return point. Returns undefined when nothing
37
+ // is staged (or the flag is OFF) so the caller falls through to its
38
+ // normal return. The F3 mirror append (above the gates) runs on the
39
+ // REAL transcript before any view is built, so the injected tail never
40
+ // reaches raw_transcript (PREVENT-PI: append-only, view-only).
41
+ const tailResult = (msgs) => {
42
+ const base = msgs ?? messages;
43
+ if (!stagedForTail(runtime, config) && !config.messageSeparation)
44
+ return undefined;
45
+ let result = stagedForTail(runtime, config)
46
+ ? withRecallTail(base, runtime, config)
47
+ : base;
48
+ if (config.messageSeparation) {
49
+ result = buildSeparatedPrompt(result);
50
+ }
51
+ return { messages: result };
52
+ };
101
53
  // Always track context for the dashboard/widget, even when auto is off.
102
54
  // (v0.8 regression: !config.auto gate sat above this, leaving ctx stats
103
55
  // null -> widget '?% / ?/?' when auto disabled. Track first, THEN gate.)
@@ -118,21 +70,24 @@ export function registerContextHandler(pi, runtime, config) {
118
70
  runtime.lastCtxPercent = pct ?? null;
119
71
  runtime.lastCtxWindow = usage?.contextWindow ?? 0;
120
72
  runtime.snapshot(ctx);
121
- if (!config.auto)
73
+ if (!config.auto) {
74
+ const tailed = tailResult();
75
+ if (tailed)
76
+ return tailed;
122
77
  return;
78
+ }
123
79
  const view = viewForFallback ?? runtime.engineView(messages);
124
- // S27 DB-mirror: append ALL incoming messages to raw_transcript.
80
+ // S27 DB-mirror: append incoming messages to raw_transcript.
125
81
  // Runs BEFORE fast-gate so every message is captured, even if we
126
82
  // don't compact this turn. Append is idempotent (content_hash PK).
83
+ // F3: high-water mark (mirror-append.ts) skips already-processed
84
+ // messages on subsequent events. On fork/rewind (shorter list or
85
+ // boundary hash mismatch) the mark is dropped, falling back to a
86
+ // full reprocess.
127
87
  if (config.dbMirror) {
128
88
  try {
129
89
  const db = openStore(runtime.currentStateDir);
130
- const epochId = epochIdFor(runtime.rt.sessionId);
131
- for (const msg of messages) {
132
- const raw = toRawTranscriptRow(msg, runtime.rt.sessionId, epochId, runtime.currentTurn);
133
- if (raw)
134
- appendRawTranscript(db, raw);
135
- }
90
+ appendMirrorMessages(db, messages, runtime.rt.sessionId, epochIdFor(runtime.rt.sessionId), runtime.currentTurn);
136
91
  }
137
92
  catch (e) {
138
93
  runtime.logger.warn("db-mirror-append-fail", { error: String(e) });
@@ -157,18 +112,18 @@ export function registerContextHandler(pi, runtime, config) {
157
112
  // custom tier OR tiered-but-pct-unavailable → token gate (S27 fallback).
158
113
  if (currentTokens < runtime.effectiveThreshold) {
159
114
  runtime.diagCtxFastGate++;
160
- return;
115
+ return tailResult() ?? undefined;
161
116
  }
162
117
  const check = autoCompactCheck(currentTokens, runtime.effectiveThreshold); // SERVER-STYLE CONFIRM (local)
163
118
  if (!check.shouldCompact) {
164
119
  runtime.diagCtxNoCompact++;
165
- return;
120
+ return tailResult() ?? undefined;
166
121
  }
167
122
  gatePassed = true;
168
123
  }
169
124
  if (!gatePassed) {
170
125
  runtime.diagCtxFastGate++;
171
- return;
126
+ return tailResult() ?? undefined;
172
127
  }
173
128
  // D.2: Replay MUST be exempt from debounce — replay is free (no compute,
174
129
  // no re-write) and prevents unnecessary KV-cache invalidation. Check
@@ -197,9 +152,8 @@ export function registerContextHandler(pi, runtime, config) {
197
152
  runtime.snapshot(ctx);
198
153
  // v0.8.7: shallow-copy the cached summary so pi's transformContext can't
199
154
  // mutate the shared reference across replays (audit P3).
200
- return {
201
- messages: [{ ...runtime.trimCache.summaryAgentMsg }, ...recent],
202
- };
155
+ const replayView = [{ ...runtime.trimCache.summaryAgentMsg }, ...recent];
156
+ return tailResult(replayView) ?? { messages: replayView };
203
157
  }
204
158
  // else: context grew enough → fall through to re-compact (cache is stale)
205
159
  }
@@ -208,7 +162,7 @@ export function registerContextHandler(pi, runtime, config) {
208
162
  const now = Date.now();
209
163
  if (now < runtime.debounceUntil) {
210
164
  runtime.diagCtxDebounce++;
211
- return;
165
+ return tailResult() ?? undefined;
212
166
  }
213
167
  runtime.debounceUntil = now + 2000;
214
168
  // Adaptive compression (Fix E): scale compression strength + keepFrom depth
@@ -233,11 +187,10 @@ export function registerContextHandler(pi, runtime, config) {
233
187
  runtime.diagLiveTrimFires++;
234
188
  runtime.diagLiveTrimReplays++;
235
189
  runtime.snapshot(ctx);
236
- return {
237
- messages: [{ ...runtime.trimCache.summaryAgentMsg }, ...recent],
238
- };
190
+ const skipView = [{ ...runtime.trimCache.summaryAgentMsg }, ...recent];
191
+ return tailResult(skipView) ?? { messages: skipView };
239
192
  }
240
- return;
193
+ return tailResult() ?? undefined;
241
194
  }
242
195
  // S27 DB-mirror: write checkpoint_epoch with deterministic nonce.
243
196
  // This makes the cache key stable across identical compactions.
@@ -392,7 +345,7 @@ export function registerContextHandler(pi, runtime, config) {
392
345
  viewLen: view.length,
393
346
  anchorUserMessages,
394
347
  });
395
- return; // unsafe / below anchor floor — no trim this call
348
+ return tailResult() ?? undefined; // unsafe / below anchor floor — no trim this call
396
349
  }
397
350
  const summaryMsg = liveTrimSummaryMessage({
398
351
  compactedFrom: ran.result.compactedFrom,
@@ -446,11 +399,11 @@ export function registerContextHandler(pi, runtime, config) {
446
399
  ctxPct: pct,
447
400
  ctxTokens: usage?.tokens ?? null,
448
401
  });
449
- return { messages: [summaryAgentMsg, ...recent] };
402
+ return tailResult([summaryAgentMsg, ...recent]) ?? { messages: [summaryAgentMsg, ...recent] };
450
403
  }
451
404
  catch {
452
405
  runtime.diagCtxThrown++;
453
- return; // non-fatal: no trim this call; the next context event retries
406
+ return tailResult() ?? undefined; // non-fatal: no trim this call; the next context event retries
454
407
  }
455
408
  });
456
409
  }
@@ -0,0 +1,51 @@
1
+ const TABLE_DDL = `CREATE TABLE IF NOT EXISTS context_hwm (
2
+ session_id TEXT PRIMARY KEY,
3
+ last_seq INTEGER NOT NULL,
4
+ last_content_hash TEXT NOT NULL
5
+ )`;
6
+ /** Ensure the context_hwm table exists. Idempotent. */
7
+ export function ensureHwmTable(db) {
8
+ try {
9
+ db.exec(TABLE_DDL);
10
+ }
11
+ catch {
12
+ // non-fatal
13
+ }
14
+ }
15
+ /** Read the high-water mark for a session. Returns undefined if none. */
16
+ export function readHwm(db, sessionId) {
17
+ try {
18
+ const row = db
19
+ .prepare("SELECT session_id, last_seq, last_content_hash FROM context_hwm WHERE session_id = ?")
20
+ .get(sessionId);
21
+ if (!row)
22
+ return undefined;
23
+ return {
24
+ sessionId: row.session_id,
25
+ lastSeq: Number(row.last_seq),
26
+ lastContentHash: row.last_content_hash,
27
+ };
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ /** Write (INSERT OR REPLACE) the high-water mark for a session. */
34
+ export function writeHwm(db, hwm) {
35
+ try {
36
+ db.prepare("INSERT OR REPLACE INTO context_hwm (session_id, last_seq, last_content_hash) VALUES (?, ?, ?)").run(hwm.sessionId, hwm.lastSeq, hwm.lastContentHash);
37
+ }
38
+ catch {
39
+ // non-fatal
40
+ }
41
+ }
42
+ /** Drop the high-water mark for a session (invalidation). */
43
+ export function dropHwm(db, sessionId) {
44
+ try {
45
+ db.prepare("DELETE FROM context_hwm WHERE session_id = ?").run(sessionId);
46
+ }
47
+ catch {
48
+ // non-fatal
49
+ }
50
+ }
51
+ export const TEST_ONLY = { TABLE_DDL };
@@ -63,7 +63,7 @@ export function classifyErrorDetailed(message) {
63
63
  return { category: null, signal: 'length-guard' };
64
64
  if (sr === 'aborted')
65
65
  return { category: 'cancelled', signal: 'cancelled' };
66
- if (sr === 'stop' || sr === 'toolUse' || sr === 'tool_use')
66
+ if (/^(stop|tool_?use|end_?turn|max_?tokens|complete|finished|done)$/i.test(sr))
67
67
  return { category: null, signal: 'success' };
68
68
  const usage = m.usage;
69
69
  usagePresent = usage != null && typeof usage === 'object';
@@ -100,10 +100,21 @@ export function classifyErrorDetailed(message) {
100
100
  }
101
101
  }
102
102
  if (!sr) {
103
- const r = { category: 'transient', signal: 'stream-death-no-stopreason' };
104
- if (httpStatus !== undefined)
105
- r.httpStatus = httpStatus;
106
- return r;
103
+ // A missing stopReason alone is not sufficient evidence of stream death.
104
+ // Require at least one corroborating signal: an error field, or 0 total tokens
105
+ // (indicating the stream died before producing anything). A normal completion
106
+ // from a runtime that omits stopReason has content and tokens — not an error.
107
+ const hasError = (m.error != null && m.error !== undefined && m.error !== '') ||
108
+ (typeof m.errorMessage === 'string' && m.errorMessage !== '');
109
+ if (hasError || (usagePresent && totalTokens === 0)) {
110
+ const r = { category: 'transient', signal: 'stream-death-no-stopreason' };
111
+ if (httpStatus !== undefined)
112
+ r.httpStatus = httpStatus;
113
+ return r;
114
+ }
115
+ // No stopReason, no error field, tokens present — likely a normal completion
116
+ // from a runtime that omits stopReason (pi-crew, etc.). Not an error.
117
+ return { category: null, signal: 'success' };
107
118
  }
108
119
  text = parts.join(' ');
109
120
  }
@@ -0,0 +1,107 @@
1
+ import { appendRawTranscript } from "../../src/store/sqlite.js";
2
+ import { computeContentDigest } from "../../src/dedup/digest.js";
3
+ import { ensureHwmTable, readHwm, writeHwm, dropHwm, } from "./context-hwm.js";
4
+ /**
5
+ * Append incoming messages to raw_transcript with HWM incremental support.
6
+ *
7
+ * On fork/rewind (shorter message list or boundary hash mismatch), the HWM
8
+ * is dropped and all messages are reprocessed.
9
+ */
10
+ export function appendMirrorMessages(db, messages, sessionId, epochId, currentTurn) {
11
+ ensureHwmTable(db);
12
+ const hwm = readHwm(db, sessionId);
13
+ let tailStart = 0;
14
+ let needDrop = false;
15
+ if (hwm) {
16
+ if (messages.length <= hwm.lastSeq) {
17
+ // Session was shortened (fork/rewind/clear) — full reprocess.
18
+ needDrop = true;
19
+ }
20
+ else {
21
+ // Verify boundary integrity: hash of message at hwm.lastSeq-1
22
+ // must match the stored hash.
23
+ const boundaryMsg = messages[hwm.lastSeq - 1];
24
+ const boundaryHash = contentHashOf(boundaryMsg.content);
25
+ if (boundaryHash === hwm.lastContentHash) {
26
+ tailStart = hwm.lastSeq;
27
+ }
28
+ else {
29
+ needDrop = true;
30
+ }
31
+ }
32
+ }
33
+ if (needDrop) {
34
+ dropHwm(db, sessionId);
35
+ }
36
+ // Process tail (or all if tailStart === 0).
37
+ let rowsProcessed = 0;
38
+ for (let i = tailStart; i < messages.length; i++) {
39
+ const raw = toRawRow(messages[i], sessionId, epochId, currentTurn);
40
+ if (raw) {
41
+ appendRawTranscript(db, raw);
42
+ rowsProcessed++;
43
+ }
44
+ }
45
+ // Write new HWM.
46
+ if (messages.length > 0) {
47
+ const lastMsg = messages[messages.length - 1];
48
+ writeHwm(db, {
49
+ sessionId,
50
+ lastSeq: messages.length,
51
+ lastContentHash: contentHashOf(lastMsg.content),
52
+ });
53
+ }
54
+ return { rowsProcessed };
55
+ }
56
+ /** Single rule for hashing message content on BOTH sides of the HWM
57
+ * (write + boundary check) — a mismatch here silently defeats the
58
+ * incremental path (every event would full-reprocess). null/undefined → "". */
59
+ function contentHashOf(content) {
60
+ if (content == null)
61
+ return "";
62
+ const bytes = typeof content === "string"
63
+ ? content
64
+ : JSON.stringify(canonicalize(content));
65
+ return computeContentDigest(bytes).contentHash;
66
+ }
67
+ /**
68
+ * Canonicalize a value for deterministic JSON serialization (keys sorted).
69
+ * This is the same logic as the inline function in context-handler.ts.
70
+ */
71
+ function canonicalize(value) {
72
+ if (value === null || value === undefined)
73
+ return value;
74
+ if (typeof value !== "object")
75
+ return value;
76
+ if (Array.isArray(value))
77
+ return value.map(canonicalize);
78
+ const sorted = Object.keys(value).sort();
79
+ const out = {};
80
+ for (const k of sorted) {
81
+ out[k] = canonicalize(value[k]);
82
+ }
83
+ return out;
84
+ }
85
+ /** Convert an AgentMessage to a RawTranscriptRow (same logic as toRawTranscriptRow in context-handler.ts). */
86
+ function toRawRow(msg, sessionId, epochId, currentTurn) {
87
+ const m = msg;
88
+ const content = m.content;
89
+ if (content == null || content === "")
90
+ return null;
91
+ const contentBytes = typeof content === "string"
92
+ ? content
93
+ : JSON.stringify(canonicalize(content));
94
+ return {
95
+ contentHash: computeContentDigest(contentBytes).contentHash,
96
+ sessionId,
97
+ seq: 0, // assigned by appendRawTranscript
98
+ role: m.role ?? "unknown",
99
+ contentBytes,
100
+ toolName: m.toolName ?? null,
101
+ messageTimestamp: m.timestamp ?? null,
102
+ checkpointEpoch: epochId,
103
+ turnIndex: currentTurn ?? null,
104
+ };
105
+ }
106
+ // Re-export for convenience when the handler only needs the table ensure.
107
+ export { ensureHwmTable, readHwm, writeHwm, dropHwm };
@@ -49,5 +49,11 @@ export async function maybeSendProviderOutageAdvisory(effectiveCategory, runtime
49
49
  ...(detail?.signal !== undefined ? { signal: detail.signal } : {}),
50
50
  ...(detail?.rawText !== undefined ? { rawText: detail.rawText } : {}),
51
51
  });
52
- await safeSendUserMessage(pi, `[mega-compact] the provider is having issues (${runtime.rt.consecutiveErrors} consecutive failures — timeouts/5xx/rate-limits). Retries are bounded and continue automatically; your context is fine do NOT clear or reset it. Work resumes as soon as the provider recovers.`);
52
+ // Advisory is dashboard-only (events tab + log). No user-visible message
53
+ // injecting into the conversation scares users and triggers false-positive
54
+ // re-classification of the resulting turn (2026-07-31 incident).
55
+ if (!config.advisoryChannel) {
56
+ // Legacy path (flag OFF): user-role message injection.
57
+ await safeSendUserMessage(pi, `[mega-compact] the provider is having issues (${runtime.rt.consecutiveErrors} consecutive failures — timeouts/5xx/rate-limits). Retries are bounded and continue automatically; your context is fine — do NOT clear or reset it. Work resumes as soon as the provider recovers.`);
58
+ }
53
59
  }
@@ -1,10 +1,126 @@
1
- import { recordPerfSample } from "../../src/store/sqlite.js";
1
+ import { recordPerfSample, readPerfSamples, } from "../../src/store/sqlite.js";
2
+ import { classifyPrefixBreak } from "../../src/prefix-break.js";
2
3
  /** Narrow a turn_end message to its usage block when it is an assistant msg. */
3
4
  function usageOf(msg) {
4
5
  if (msg.role !== "assistant" || !msg.usage)
5
6
  return null;
6
7
  return msg.usage;
7
8
  }
9
+ /**
10
+ * Attempt to record a prefix_break sample when a significant cache_hit_pct
11
+ * drop is detected between two consecutive turns.
12
+ *
13
+ * Drop threshold: 20 percentage points. When triggered, calls
14
+ * classifyPrefixBreak with the three candidate event timestamps from the
15
+ * runtime and records a `prefix_break` sample. Best-effort + non-fatal.
16
+ *
17
+ * Also evaluates rolling cache health and emits a degradation event when the
18
+ * composite score falls below 0.6 (A3, PLAN_V2 Phase 4).
19
+ *
20
+ * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite write only.
21
+ * PREVENT-002: SQL is fully parameterized via recordPerfSample.
22
+ */
23
+ function tryRecordPrefixBreak(stateDir, prevHitPct, currHitPct, breakAt, runtime) {
24
+ try {
25
+ if (prevHitPct - currHitPct < 20)
26
+ return; // no significant drop
27
+ const events = {
28
+ lastRecallAt: runtime.rt.lastInjectAt, // S53: inject fires alongside recall
29
+ lastCompactAt: runtime.rt.lastCompactAt,
30
+ lastInjectAt: runtime.rt.lastInjectAt,
31
+ };
32
+ const { cause, confidence } = classifyPrefixBreak(breakAt, events);
33
+ recordPerfSample(stateDir, "prefix_break", 0, {
34
+ cause,
35
+ confidence,
36
+ prevHitPct,
37
+ currHitPct,
38
+ breakAt,
39
+ }); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback perf instrumentation)
40
+ // A3: compute rolling cache health score and emit alert on degradation.
41
+ tryComputeCacheHealth(stateDir, currHitPct, runtime);
42
+ }
43
+ catch {
44
+ /* non-fatal: prefix-break detection never blocks the agent loop */
45
+ }
46
+ }
47
+ /**
48
+ * Compute a rolling cache health score and emit a dashboard event when it
49
+ * falls below the degradation threshold (0.6 / "yellow").
50
+ *
51
+ * The score is a weighted composite of:
52
+ * - hitRateScore (0-1): recent cache_hit_pct sample average / 100
53
+ * - stabilityScore (0-1): 1 - recent prefix_break count / maxExpectedBreaks
54
+ * - churnScore (0-1): 1 - stripe re-assignment rate (derived from prefix_break classifications)
55
+ *
56
+ * Weights: hit rate 50%, prefix stability 30%, churn 20%.
57
+ *
58
+ * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite read, no network.
59
+ * PREVENT-001: readPerfSamples returns empty array when no data; handled.
60
+ * PREVENT-011: no `any` — all arrays are typed PerfSampleRow[].
61
+ */
62
+ function tryComputeCacheHealth(stateDir, currHitPct, runtime) {
63
+ try {
64
+ const now = Date.now();
65
+ const windowMs = 300_000; // 5-minute lookback
66
+ const sinceTs = now - windowMs;
67
+ // Read recent cache_hit_pct and prefix_break samples.
68
+ const hitSamples = readPerfSamples(stateDir, sinceTs, "cache_hit_pct");
69
+ const breakSamples = readPerfSamples(stateDir, sinceTs, "prefix_break");
70
+ // hitRateScore: average of recent cache hit percentages / 100.
71
+ let hitRateAvg = currHitPct;
72
+ if (hitSamples.length > 0) {
73
+ const sum = hitSamples.reduce((a, r) => a + r.value, 0);
74
+ hitRateAvg = sum / hitSamples.length;
75
+ }
76
+ const hitRateScore = Math.max(0, Math.min(1, hitRateAvg / 100));
77
+ // stabilityScore: penalize for frequent prefix breaks.
78
+ // Assume at most 2 breaks in 5min is tolerable.
79
+ const maxExpectedBreaks = 2;
80
+ const stabilityScore = Math.max(0, 1 - breakSamples.length / maxExpectedBreaks);
81
+ // churnScore: if recent breaks have varied causes, penalize.
82
+ const causeVariety = new Set(breakSamples.map((r) => {
83
+ const m = typeof r.meta === "string" ? JSON.parse(r.meta) : null;
84
+ return typeof m?.cause === "string" ? m.cause : "other";
85
+ }));
86
+ const churnPenalty = Math.min(1, causeVariety.size / 4); // 4 possible causes
87
+ const churnScore = 1 - churnPenalty;
88
+ // Composite: hitRate dominates.
89
+ const composite = hitRateScore * 0.5 + stabilityScore * 0.3 + churnScore * 0.2;
90
+ // Emit degradation event when score < 0.6.
91
+ if (composite < 0.6) {
92
+ const prevScore = runtime.rt._lastCacheHealthScore ?? 1;
93
+ runtime.rt._lastCacheHealthScore = composite;
94
+ recordPerfSample(stateDir, "cache_health", composite, {
95
+ hitRateScore: Math.round(hitRateScore * 1000) / 1000,
96
+ stabilityScore: Math.round(stabilityScore * 1000) / 1000,
97
+ churnScore: Math.round(churnScore * 1000) / 1000,
98
+ breakCount: breakSamples.length,
99
+ hitSampleCount: hitSamples.length,
100
+ currHitPct,
101
+ trend: composite < prevScore ? "falling" : "stable",
102
+ }); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback dashboard event)
103
+ // Structured log line for the host to pick up.
104
+ const level = composite < 0.4 ? "error" : "warn";
105
+ // eslint-disable-next-line no-console
106
+ console.log(JSON.stringify({
107
+ ts: now,
108
+ event: `cache_health_${level}`,
109
+ composite: Math.round(composite * 1000) / 1000,
110
+ hitRateAvg: Math.round(hitRateAvg * 10) / 10,
111
+ breakCount: breakSamples.length,
112
+ trend: composite < prevScore ? "falling" : "stable",
113
+ }));
114
+ }
115
+ else {
116
+ // Still healthy — just update the stored score.
117
+ runtime.rt._lastCacheHealthScore = composite;
118
+ }
119
+ }
120
+ catch {
121
+ /* non-fatal: cache health computation never blocks */
122
+ }
123
+ }
8
124
  /** Register perf instrumentation handlers + start the 5s cpu/mem interval. */
9
125
  export function registerPerfHandler(pi, runtime) {
10
126
  // turn_start: record the wall-clock start of the turn. Using Date.now() (not
@@ -23,6 +139,7 @@ export function registerPerfHandler(pi, runtime) {
23
139
  });
24
140
  // turn_end: compute turn latency + TPS + cache hit % from the assistant
25
141
  // message's usage block. One perf_samples row per metric per turn.
142
+ // S53A: tracks prevHitPct across turns to detect prefix breaks.
26
143
  pi.on("turn_end", async (event) => {
27
144
  try {
28
145
  if (runtime.perfTurnStart > 0) {
@@ -36,7 +153,22 @@ export function registerPerfHandler(pi, runtime) {
36
153
  recordPerfSample(runtime.currentStateDir, "tps", u.output / durSec, { outputTokens: u.output });
37
154
  const denom = u.cacheRead + u.input + u.cacheWrite;
38
155
  const hitPct = denom > 0 ? (u.cacheRead / denom) * 100 : 0;
39
- recordPerfSample(runtime.currentStateDir, "cache_hit_pct", hitPct, { input: u.input, cacheRead: u.cacheRead, cacheWrite: u.cacheWrite });
156
+ recordPerfSample(runtime.currentStateDir, "cache_hit_pct", hitPct, {
157
+ input: u.input,
158
+ cacheRead: u.cacheRead,
159
+ cacheWrite: u.cacheWrite,
160
+ modelName: runtime.currentModel?.modelName,
161
+ modelId: runtime.currentModel?.modelId,
162
+ });
163
+ // S53A: detect significant cache hit % drop → record prefix_break.
164
+ // Only trigger when we have a previous value AND the drop is >20pp.
165
+ if (runtime.rt._prevCacheHitPct != null &&
166
+ runtime.rt._prevCacheHitPct - hitPct >= 20) {
167
+ const breakAt = runtime.perfTurnStart;
168
+ tryRecordPrefixBreak(runtime.currentStateDir, runtime.rt._prevCacheHitPct, hitPct, breakAt, runtime);
169
+ }
170
+ // Stamp for the next turn's comparison (even when no break fires).
171
+ runtime.rt._prevCacheHitPct = hitPct;
40
172
  }
41
173
  }
42
174
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * True when recall blocks are staged AND the feature flag is ON. Called by the
3
+ * context handler to decide whether to call withRecallTail.
4
+ */
5
+ export function stagedForTail(runtime, config) {
6
+ if (!config.recallTailInject)
7
+ return false;
8
+ return (runtime.pendingRecallBlock != null ||
9
+ runtime.pendingMemoryRecallBlock != null);
10
+ }
11
+ /**
12
+ * Append staged recall/memory blocks as a single user-role message at the very
13
+ * tail of the messages array. Returns a NEW array (does not mutate input).
14
+ *
15
+ * Sets `runtime.recallInjectedThisTurn = true` when it actually appends, so the
16
+ * turn_end handler (agent-handlers.ts) consumes the staged blocks only if they
17
+ * reached a view this turn. If a turn ends with no context event, the flag
18
+ * stays false and the blocks remain staged for the next turn.
19
+ *
20
+ * Stable timestamp (runtime.perfTurnStart ?? Date.now()) so replays within the
21
+ * turn are byte-identical (mirrors the v0.8.6 summaryAgentMsg rationale).
22
+ *
23
+ * Non-fatal: on any failure, returns messages unmodified and does NOT set the
24
+ * injected flag. PREVENT-PI-003: the injected message has role "user", never
25
+ * "system".
26
+ */
27
+ export function withRecallTail(messages, runtime, config) {
28
+ if (!config.recallTailInject)
29
+ return messages;
30
+ try {
31
+ const blocks = [];
32
+ if (runtime.pendingRecallBlock != null) {
33
+ blocks.push(runtime.pendingRecallBlock);
34
+ }
35
+ if (runtime.pendingMemoryRecallBlock != null) {
36
+ blocks.push(runtime.pendingMemoryRecallBlock);
37
+ }
38
+ if (blocks.length === 0)
39
+ return messages;
40
+ const tailText = blocks.join("\n\n");
41
+ const timestamp = runtime.perfTurnStart ?? // stable within a turn
42
+ Date.now();
43
+ const tailMsg = {
44
+ role: "user",
45
+ content: tailText,
46
+ timestamp,
47
+ };
48
+ runtime.rt.recallInjectedThisTurn = true;
49
+ // Append at the very tail — never splits toolCall/toolResult.
50
+ // PREVENT-PI-001/002: a single message appended after a complete
51
+ // transcript prefix cannot split a toolCall/toolResult pair.
52
+ return [...messages, tailMsg];
53
+ }
54
+ catch {
55
+ // Non-fatal: leave blocks staged (flag stays unset) and return the
56
+ // unmodified view. The context event still proceeds normally.
57
+ return messages;
58
+ }
59
+ }