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,85 @@
1
+ /**
2
+ * mega-events/context-hwm.ts — high-water mark for DB-mirror raw_transcript
3
+ * incremental append (F3 sprint).
4
+ *
5
+ * Stores the last-processed seq + content_hash per session so the context
6
+ * event only appends the tail on subsequent events. On fork/rewind (shorter
7
+ * message list or hash mismatch at the boundary), the mark is dropped so the
8
+ * next event falls back to a full reprocess.
9
+ *
10
+ * Non-fatal: every function logs and swallows errors; the agent loop is never
11
+ * broken (repo invariant).
12
+ *
13
+ * All queries parameterized (PREVENT-002); no `any` (PREVENT-011).
14
+ */
15
+ import type { DatabaseSync } from "node:sqlite";
16
+
17
+ export interface ContextHwm {
18
+ sessionId: string;
19
+ lastSeq: number;
20
+ lastContentHash: string;
21
+ }
22
+
23
+ const TABLE_DDL = `CREATE TABLE IF NOT EXISTS context_hwm (
24
+ session_id TEXT PRIMARY KEY,
25
+ last_seq INTEGER NOT NULL,
26
+ last_content_hash TEXT NOT NULL
27
+ )`;
28
+
29
+ /** Ensure the context_hwm table exists. Idempotent. */
30
+ export function ensureHwmTable(db: DatabaseSync): void {
31
+ try {
32
+ db.exec(TABLE_DDL);
33
+ } catch {
34
+ // non-fatal
35
+ }
36
+ }
37
+
38
+ /** Read the high-water mark for a session. Returns undefined if none. */
39
+ export function readHwm(
40
+ db: DatabaseSync,
41
+ sessionId: string,
42
+ ): ContextHwm | undefined {
43
+ try {
44
+ const row = db
45
+ .prepare(
46
+ "SELECT session_id, last_seq, last_content_hash FROM context_hwm WHERE session_id = ?",
47
+ )
48
+ .get(sessionId) as
49
+ | { session_id: string; last_seq: number; last_content_hash: string }
50
+ | undefined;
51
+ if (!row) return undefined;
52
+ return {
53
+ sessionId: row.session_id,
54
+ lastSeq: Number(row.last_seq),
55
+ lastContentHash: row.last_content_hash,
56
+ };
57
+ } catch {
58
+ return undefined;
59
+ }
60
+ }
61
+
62
+ /** Write (INSERT OR REPLACE) the high-water mark for a session. */
63
+ export function writeHwm(
64
+ db: DatabaseSync,
65
+ hwm: ContextHwm,
66
+ ): void {
67
+ try {
68
+ db.prepare(
69
+ "INSERT OR REPLACE INTO context_hwm (session_id, last_seq, last_content_hash) VALUES (?, ?, ?)",
70
+ ).run(hwm.sessionId, hwm.lastSeq, hwm.lastContentHash);
71
+ } catch {
72
+ // non-fatal
73
+ }
74
+ }
75
+
76
+ /** Drop the high-water mark for a session (invalidation). */
77
+ export function dropHwm(db: DatabaseSync, sessionId: string): void {
78
+ try {
79
+ db.prepare("DELETE FROM context_hwm WHERE session_id = ?").run(sessionId);
80
+ } catch {
81
+ // non-fatal
82
+ }
83
+ }
84
+
85
+ export const TEST_ONLY = { TABLE_DDL };
@@ -102,7 +102,7 @@ export function classifyErrorDetailed(message: unknown): ClassifyErrorDetail {
102
102
  const sr = typeof m.stopReason === 'string' ? m.stopReason : '';
103
103
  if (sr === 'length') return { category: null, signal: 'length-guard' };
104
104
  if (sr === 'aborted') return { category: 'cancelled', signal: 'cancelled' };
105
- if (sr === 'stop' || sr === 'toolUse' || sr === 'tool_use') return { category: null, signal: 'success' };
105
+ if (/^(stop|tool_?use|end_?turn|max_?tokens|complete|finished|done)$/i.test(sr)) return { category: null, signal: 'success' };
106
106
  const usage = m.usage;
107
107
  usagePresent = usage != null && typeof usage === 'object';
108
108
  if (usagePresent) {
@@ -133,9 +133,20 @@ export function classifyErrorDetailed(message: unknown): ClassifyErrorDetail {
133
133
  }
134
134
  }
135
135
  if (!sr) {
136
- const r: ClassifyErrorDetail = { category: 'transient', signal: 'stream-death-no-stopreason' };
137
- if (httpStatus !== undefined) r.httpStatus = httpStatus;
138
- return r;
136
+ // A missing stopReason alone is not sufficient evidence of stream death.
137
+ // Require at least one corroborating signal: an error field, or 0 total tokens
138
+ // (indicating the stream died before producing anything). A normal completion
139
+ // from a runtime that omits stopReason has content and tokens — not an error.
140
+ const hasError = (m.error != null && m.error !== undefined && m.error !== '') ||
141
+ (typeof (m as { errorMessage?: unknown }).errorMessage === 'string' && (m as { errorMessage?: string }).errorMessage !== '');
142
+ if (hasError || (usagePresent && totalTokens === 0)) {
143
+ const r: ClassifyErrorDetail = { category: 'transient', signal: 'stream-death-no-stopreason' };
144
+ if (httpStatus !== undefined) r.httpStatus = httpStatus;
145
+ return r;
146
+ }
147
+ // No stopReason, no error field, tokens present — likely a normal completion
148
+ // from a runtime that omits stopReason (pi-crew, etc.). Not an error.
149
+ return { category: null, signal: 'success' };
139
150
  }
140
151
  text = parts.join(' ');
141
152
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * mirror-append.ts — extracted DB-mirror append logic for context-handler.ts
3
+ * (F3 sprint: high-water mark incremental append).
4
+ *
5
+ * Follows the repo's delegate-shell pattern: the context handler shell delegates
6
+ * to this impl module for the mirror-append concern. Returns `rowsProcessed`
7
+ * so callers (including tests) can observe how many new rows were written
8
+ * without adding test-only hooks.
9
+ */
10
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
11
+ import type { DatabaseSync } from "node:sqlite";
12
+ import { appendRawTranscript, type RawTranscriptRow } from "../../src/store/sqlite.js";
13
+ import { computeContentDigest } from "../../src/dedup/digest.js";
14
+ import {
15
+ ensureHwmTable,
16
+ readHwm,
17
+ writeHwm,
18
+ dropHwm,
19
+ } from "./context-hwm.js";
20
+
21
+ export interface MirrorAppendResult {
22
+ rowsProcessed: number;
23
+ }
24
+
25
+ /**
26
+ * Append incoming messages to raw_transcript with HWM incremental support.
27
+ *
28
+ * On fork/rewind (shorter message list or boundary hash mismatch), the HWM
29
+ * is dropped and all messages are reprocessed.
30
+ */
31
+ export function appendMirrorMessages(
32
+ db: DatabaseSync,
33
+ messages: AgentMessage[],
34
+ sessionId: string,
35
+ epochId: string,
36
+ currentTurn: number | undefined,
37
+ ): MirrorAppendResult {
38
+ ensureHwmTable(db);
39
+ const hwm = readHwm(db, sessionId);
40
+
41
+ let tailStart = 0;
42
+ let needDrop = false;
43
+
44
+ if (hwm) {
45
+ if (messages.length <= hwm.lastSeq) {
46
+ // Session was shortened (fork/rewind/clear) — full reprocess.
47
+ needDrop = true;
48
+ } else {
49
+ // Verify boundary integrity: hash of message at hwm.lastSeq-1
50
+ // must match the stored hash.
51
+ const boundaryMsg = messages[hwm.lastSeq - 1];
52
+ const boundaryHash = contentHashOf(
53
+ (boundaryMsg as { content?: unknown }).content,
54
+ );
55
+ if (boundaryHash === hwm.lastContentHash) {
56
+ tailStart = hwm.lastSeq;
57
+ } else {
58
+ needDrop = true;
59
+ }
60
+ }
61
+ }
62
+
63
+ if (needDrop) {
64
+ dropHwm(db, sessionId);
65
+ }
66
+
67
+ // Process tail (or all if tailStart === 0).
68
+ let rowsProcessed = 0;
69
+ for (let i = tailStart; i < messages.length; i++) {
70
+ const raw = toRawRow(messages[i], sessionId, epochId, currentTurn);
71
+ if (raw) {
72
+ appendRawTranscript(db, raw);
73
+ rowsProcessed++;
74
+ }
75
+ }
76
+
77
+ // Write new HWM.
78
+ if (messages.length > 0) {
79
+ const lastMsg = messages[messages.length - 1];
80
+ writeHwm(db, {
81
+ sessionId,
82
+ lastSeq: messages.length,
83
+ lastContentHash: contentHashOf(
84
+ (lastMsg as { content?: unknown }).content,
85
+ ),
86
+ });
87
+ }
88
+
89
+ return { rowsProcessed };
90
+ }
91
+
92
+ /** Single rule for hashing message content on BOTH sides of the HWM
93
+ * (write + boundary check) — a mismatch here silently defeats the
94
+ * incremental path (every event would full-reprocess). null/undefined → "". */
95
+ function contentHashOf(content: unknown): string {
96
+ if (content == null) return "";
97
+ const bytes =
98
+ typeof content === "string"
99
+ ? content
100
+ : JSON.stringify(canonicalize(content));
101
+ return computeContentDigest(bytes).contentHash;
102
+ }
103
+
104
+ /**
105
+ * Canonicalize a value for deterministic JSON serialization (keys sorted).
106
+ * This is the same logic as the inline function in context-handler.ts.
107
+ */
108
+ function canonicalize(value: unknown): unknown {
109
+ if (value === null || value === undefined) return value;
110
+ if (typeof value !== "object") return value;
111
+ if (Array.isArray(value)) return value.map(canonicalize);
112
+ const sorted = Object.keys(value as Record<string, unknown>).sort();
113
+ const out: Record<string, unknown> = {};
114
+ for (const k of sorted) {
115
+ (out as Record<string, unknown>)[k] = canonicalize(
116
+ (value as Record<string, unknown>)[k],
117
+ );
118
+ }
119
+ return out;
120
+ }
121
+
122
+ /** Convert an AgentMessage to a RawTranscriptRow (same logic as toRawTranscriptRow in context-handler.ts). */
123
+ function toRawRow(
124
+ msg: AgentMessage,
125
+ sessionId: string,
126
+ epochId: string,
127
+ currentTurn: number | undefined,
128
+ ): RawTranscriptRow | null {
129
+ const m = msg as {
130
+ role?: string;
131
+ content?: unknown;
132
+ timestamp?: number;
133
+ toolName?: string;
134
+ };
135
+ const content = m.content;
136
+ if (content == null || content === "") return null;
137
+ const contentBytes =
138
+ typeof content === "string"
139
+ ? content
140
+ : JSON.stringify(canonicalize(content));
141
+ return {
142
+ contentHash: computeContentDigest(contentBytes).contentHash,
143
+ sessionId,
144
+ seq: 0, // assigned by appendRawTranscript
145
+ role: m.role ?? "unknown",
146
+ contentBytes,
147
+ toolName: m.toolName ?? null,
148
+ messageTimestamp: m.timestamp ?? null,
149
+ checkpointEpoch: epochId,
150
+ turnIndex: currentTurn ?? null,
151
+ };
152
+ }
153
+
154
+ // Re-export for convenience when the handler only needs the table ensure.
155
+ export { ensureHwmTable, readHwm, writeHwm, dropHwm };
@@ -39,7 +39,7 @@ export async function maybeSendProviderOutageAdvisory(
39
39
  effectiveCategory: string,
40
40
  runtime: MegaRuntime,
41
41
  pi: ExtensionAPI,
42
- config: { providerOutageAdviseThreshold: number },
42
+ config: { providerOutageAdviseThreshold: number; advisoryChannel?: boolean },
43
43
  detail?: OutageDetail,
44
44
  ): Promise<void> {
45
45
  if (effectiveCategory !== "transient") return;
@@ -66,8 +66,14 @@ export async function maybeSendProviderOutageAdvisory(
66
66
  ...(detail?.rawText !== undefined ? { rawText: detail.rawText } : {}),
67
67
  });
68
68
 
69
- await safeSendUserMessage(
70
- pi,
71
- `[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.`,
72
- );
69
+ // Advisory is dashboard-only (events tab + log). No user-visible message —
70
+ // injecting into the conversation scares users and triggers false-positive
71
+ // re-classification of the resulting turn (2026-07-31 incident).
72
+ if (!config.advisoryChannel) {
73
+ // Legacy path (flag OFF): user-role message injection.
74
+ await safeSendUserMessage(
75
+ pi,
76
+ `[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.`,
77
+ );
78
+ }
73
79
  }
@@ -3,8 +3,11 @@
3
3
  *
4
4
  * Captures cheap, local-only telemetry into the `perf_samples` SQLite table for
5
5
  * the dashboard's Perf tab: turn + provider latency, TPS, cache hit %, and (via
6
- * MegaRuntime.ensurePerfInterval) a 5s cpu/mem interval. All capture is wrapped in
7
- * try/catch — instrumentation NEVER blocks the agent loop (non-fatal).
6
+ * MegaRuntime.ensurePerfInterval) a 5s cpu/mem interval. All capture is wrapped
7
+ * in try/catch — instrumentation NEVER blocks the agent loop (non-fatal).
8
+ *
9
+ * S53A: detects significant cache_hit_pct drops (delta > 20 pp) between
10
+ * consecutive turns and records a `prefix_break` sample with a classified cause.
8
11
  *
9
12
  * PREVENT-PI-004: Date.now / process.cpuUsage / process.memoryUsage + local
10
13
  * SQLite only, zero network.
@@ -12,7 +15,11 @@
12
15
  */
13
16
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
14
17
  import { type MegaRuntime } from "../mega-runtime.js";
15
- import { recordPerfSample } from "../../src/store/sqlite.js";
18
+ import {
19
+ recordPerfSample,
20
+ readPerfSamples,
21
+ } from "../../src/store/sqlite.js";
22
+ import { classifyPrefixBreak, type EventTimestamps } from "../../src/prefix-break.js";
16
23
 
17
24
  /** Structural view of an AssistantMessage usage block (no pi-ai import). */
18
25
  interface UsageBlock {
@@ -30,6 +37,158 @@ function usageOf(
30
37
  return msg.usage;
31
38
  }
32
39
 
40
+ /**
41
+ * Attempt to record a prefix_break sample when a significant cache_hit_pct
42
+ * drop is detected between two consecutive turns.
43
+ *
44
+ * Drop threshold: 20 percentage points. When triggered, calls
45
+ * classifyPrefixBreak with the three candidate event timestamps from the
46
+ * runtime and records a `prefix_break` sample. Best-effort + non-fatal.
47
+ *
48
+ * Also evaluates rolling cache health and emits a degradation event when the
49
+ * composite score falls below 0.6 (A3, PLAN_V2 Phase 4).
50
+ *
51
+ * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite write only.
52
+ * PREVENT-002: SQL is fully parameterized via recordPerfSample.
53
+ */
54
+ function tryRecordPrefixBreak(
55
+ stateDir: string,
56
+ prevHitPct: number,
57
+ currHitPct: number,
58
+ breakAt: number,
59
+ runtime: MegaRuntime,
60
+ ): void {
61
+ try {
62
+ if (prevHitPct - currHitPct < 20) return; // no significant drop
63
+
64
+ const events: EventTimestamps = {
65
+ lastRecallAt: runtime.rt.lastInjectAt, // S53: inject fires alongside recall
66
+ lastCompactAt: runtime.rt.lastCompactAt,
67
+ lastInjectAt: runtime.rt.lastInjectAt,
68
+ };
69
+ const { cause, confidence } = classifyPrefixBreak(breakAt, events);
70
+
71
+ recordPerfSample(
72
+ stateDir,
73
+ "prefix_break",
74
+ 0,
75
+ {
76
+ cause,
77
+ confidence,
78
+ prevHitPct,
79
+ currHitPct,
80
+ breakAt,
81
+ },
82
+ ); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback perf instrumentation)
83
+
84
+ // A3: compute rolling cache health score and emit alert on degradation.
85
+ tryComputeCacheHealth(stateDir, currHitPct, runtime);
86
+ } catch {
87
+ /* non-fatal: prefix-break detection never blocks the agent loop */
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Compute a rolling cache health score and emit a dashboard event when it
93
+ * falls below the degradation threshold (0.6 / "yellow").
94
+ *
95
+ * The score is a weighted composite of:
96
+ * - hitRateScore (0-1): recent cache_hit_pct sample average / 100
97
+ * - stabilityScore (0-1): 1 - recent prefix_break count / maxExpectedBreaks
98
+ * - churnScore (0-1): 1 - stripe re-assignment rate (derived from prefix_break classifications)
99
+ *
100
+ * Weights: hit rate 50%, prefix stability 30%, churn 20%.
101
+ *
102
+ * PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite read, no network.
103
+ * PREVENT-001: readPerfSamples returns empty array when no data; handled.
104
+ * PREVENT-011: no `any` — all arrays are typed PerfSampleRow[].
105
+ */
106
+ function tryComputeCacheHealth(
107
+ stateDir: string,
108
+ currHitPct: number,
109
+ runtime: MegaRuntime,
110
+ ): void {
111
+ try {
112
+ const now = Date.now();
113
+ const windowMs = 300_000; // 5-minute lookback
114
+ const sinceTs = now - windowMs;
115
+
116
+ // Read recent cache_hit_pct and prefix_break samples.
117
+ const hitSamples = readPerfSamples(stateDir, sinceTs, "cache_hit_pct");
118
+ const breakSamples = readPerfSamples(stateDir, sinceTs, "prefix_break");
119
+
120
+ // hitRateScore: average of recent cache hit percentages / 100.
121
+ let hitRateAvg = currHitPct;
122
+ if (hitSamples.length > 0) {
123
+ const sum = hitSamples.reduce((a, r) => a + r.value, 0);
124
+ hitRateAvg = sum / hitSamples.length;
125
+ }
126
+ const hitRateScore = Math.max(0, Math.min(1, hitRateAvg / 100));
127
+
128
+ // stabilityScore: penalize for frequent prefix breaks.
129
+ // Assume at most 2 breaks in 5min is tolerable.
130
+ const maxExpectedBreaks = 2;
131
+ const stabilityScore = Math.max(
132
+ 0,
133
+ 1 - breakSamples.length / maxExpectedBreaks,
134
+ );
135
+
136
+ // churnScore: if recent breaks have varied causes, penalize.
137
+ const causeVariety = new Set(
138
+ breakSamples.map((r) => {
139
+ const m = typeof r.meta === "string" ? (JSON.parse(r.meta) as Record<string, unknown>) : null;
140
+ return typeof m?.cause === "string" ? m.cause : "other";
141
+ }),
142
+ );
143
+ const churnPenalty = Math.min(1, causeVariety.size / 4); // 4 possible causes
144
+ const churnScore = 1 - churnPenalty;
145
+
146
+ // Composite: hitRate dominates.
147
+ const composite =
148
+ hitRateScore * 0.5 + stabilityScore * 0.3 + churnScore * 0.2;
149
+
150
+ // Emit degradation event when score < 0.6.
151
+ if (composite < 0.6) {
152
+ const prevScore = runtime.rt._lastCacheHealthScore ?? 1;
153
+ runtime.rt._lastCacheHealthScore = composite;
154
+
155
+ recordPerfSample(
156
+ stateDir,
157
+ "cache_health",
158
+ composite,
159
+ {
160
+ hitRateScore: Math.round(hitRateScore * 1000) / 1000,
161
+ stabilityScore: Math.round(stabilityScore * 1000) / 1000,
162
+ churnScore: Math.round(churnScore * 1000) / 1000,
163
+ breakCount: breakSamples.length,
164
+ hitSampleCount: hitSamples.length,
165
+ currHitPct,
166
+ trend: composite < prevScore ? "falling" : "stable",
167
+ },
168
+ ); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback dashboard event)
169
+
170
+ // Structured log line for the host to pick up.
171
+ const level = composite < 0.4 ? "error" : "warn";
172
+ // eslint-disable-next-line no-console
173
+ console.log(
174
+ JSON.stringify({
175
+ ts: now,
176
+ event: `cache_health_${level}`,
177
+ composite: Math.round(composite * 1000) / 1000,
178
+ hitRateAvg: Math.round(hitRateAvg * 10) / 10,
179
+ breakCount: breakSamples.length,
180
+ trend: composite < prevScore ? "falling" : "stable",
181
+ }),
182
+ );
183
+ } else {
184
+ // Still healthy — just update the stored score.
185
+ runtime.rt._lastCacheHealthScore = composite;
186
+ }
187
+ } catch {
188
+ /* non-fatal: cache health computation never blocks */
189
+ }
190
+ }
191
+
33
192
  /** Register perf instrumentation handlers + start the 5s cpu/mem interval. */
34
193
  export function registerPerfHandler(
35
194
  pi: ExtensionAPI,
@@ -51,6 +210,7 @@ export function registerPerfHandler(
51
210
 
52
211
  // turn_end: compute turn latency + TPS + cache hit % from the assistant
53
212
  // message's usage block. One perf_samples row per metric per turn.
213
+ // S53A: tracks prevHitPct across turns to detect prefix breaks.
54
214
  pi.on("turn_end", async (event) => {
55
215
  try {
56
216
  if (runtime.perfTurnStart > 0) {
@@ -73,10 +233,34 @@ export function registerPerfHandler(
73
233
  runtime.currentStateDir,
74
234
  "cache_hit_pct",
75
235
  hitPct,
76
- { input: u.input, cacheRead: u.cacheRead, cacheWrite: u.cacheWrite },
236
+ {
237
+ input: u.input,
238
+ cacheRead: u.cacheRead,
239
+ cacheWrite: u.cacheWrite,
240
+ modelName: runtime.currentModel?.modelName,
241
+ modelId: runtime.currentModel?.modelId,
242
+ },
77
243
  );
244
+
245
+ // S53A: detect significant cache hit % drop → record prefix_break.
246
+ // Only trigger when we have a previous value AND the drop is >20pp.
247
+ if (
248
+ runtime.rt._prevCacheHitPct != null &&
249
+ runtime.rt._prevCacheHitPct - hitPct >= 20
250
+ ) {
251
+ const breakAt = runtime.perfTurnStart;
252
+ tryRecordPrefixBreak(
253
+ runtime.currentStateDir,
254
+ runtime.rt._prevCacheHitPct,
255
+ hitPct,
256
+ breakAt,
257
+ runtime,
258
+ );
259
+ }
260
+ // Stamp for the next turn's comparison (even when no break fires).
261
+ runtime.rt._prevCacheHitPct = hitPct;
262
+ }
78
263
  }
79
- }
80
264
  } catch {
81
265
  /* non-fatal: instrumentation must never break the agent loop */
82
266
  }
@@ -110,4 +294,4 @@ export function registerPerfHandler(
110
294
  // Start the 5s cpu/mem sampling interval (one per MegaRuntime; cleared in
111
295
  // runtime.dispose()). Idempotent — safe to call again after a dispose().
112
296
  runtime.ensurePerfInterval();
113
- }
297
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * recall-tail.ts — S53 Recall Tail Injection: append staged recall/memory blocks
3
+ * as a single user-role message at the very tail of the context view, instead
4
+ * of prepending them as part of systemPrompt (the pre-S53 behavior).
5
+ *
6
+ * Key invariants (enforced / tested):
7
+ * - PREVENT-PI-001/002: append-only at the very tail; never splits a
8
+ * toolCall/toolResult pair.
9
+ * - PREVENT-PI-003: user-role message, never system role.
10
+ * - Turn-scoped pin: staged blocks inject on EVERY context event of the turn
11
+ * (tool loops); consumed at turn_end ONLY if actually injected.
12
+ * - Non-fatal: any failure returns the unmodified messages array.
13
+ * - Flag OFF: no-op (returns messages unchanged — byte-identical pre-sprint;
14
+ * session-handlers.ts keeps the legacy systemPrompt prepend path).
15
+ */
16
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
17
+ import type { MegaRuntime } from "../mega-runtime.js";
18
+ import type { MegaConfig } from "../mega-config.js";
19
+
20
+ /**
21
+ * True when recall blocks are staged AND the feature flag is ON. Called by the
22
+ * context handler to decide whether to call withRecallTail.
23
+ */
24
+ export function stagedForTail(
25
+ runtime: MegaRuntime,
26
+ config: MegaConfig,
27
+ ): boolean {
28
+ if (!config.recallTailInject) return false;
29
+ return (
30
+ runtime.pendingRecallBlock != null ||
31
+ runtime.pendingMemoryRecallBlock != null
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Append staged recall/memory blocks as a single user-role message at the very
37
+ * tail of the messages array. Returns a NEW array (does not mutate input).
38
+ *
39
+ * Sets `runtime.recallInjectedThisTurn = true` when it actually appends, so the
40
+ * turn_end handler (agent-handlers.ts) consumes the staged blocks only if they
41
+ * reached a view this turn. If a turn ends with no context event, the flag
42
+ * stays false and the blocks remain staged for the next turn.
43
+ *
44
+ * Stable timestamp (runtime.perfTurnStart ?? Date.now()) so replays within the
45
+ * turn are byte-identical (mirrors the v0.8.6 summaryAgentMsg rationale).
46
+ *
47
+ * Non-fatal: on any failure, returns messages unmodified and does NOT set the
48
+ * injected flag. PREVENT-PI-003: the injected message has role "user", never
49
+ * "system".
50
+ */
51
+ export function withRecallTail(
52
+ messages: readonly AgentMessage[],
53
+ runtime: MegaRuntime,
54
+ config: MegaConfig,
55
+ ): AgentMessage[] {
56
+ if (!config.recallTailInject) return messages as AgentMessage[];
57
+ try {
58
+ const blocks: string[] = [];
59
+ if (runtime.pendingRecallBlock != null) {
60
+ blocks.push(runtime.pendingRecallBlock);
61
+ }
62
+ if (runtime.pendingMemoryRecallBlock != null) {
63
+ blocks.push(runtime.pendingMemoryRecallBlock);
64
+ }
65
+ if (blocks.length === 0) return messages as AgentMessage[];
66
+
67
+ const tailText = blocks.join("\n\n");
68
+ const timestamp =
69
+ runtime.perfTurnStart ?? // stable within a turn
70
+ Date.now();
71
+
72
+ const tailMsg = {
73
+ role: "user" as const,
74
+ content: tailText,
75
+ timestamp,
76
+ } as unknown as AgentMessage;
77
+
78
+ runtime.rt.recallInjectedThisTurn = true;
79
+ // Append at the very tail — never splits toolCall/toolResult.
80
+ // PREVENT-PI-001/002: a single message appended after a complete
81
+ // transcript prefix cannot split a toolCall/toolResult pair.
82
+ return [...messages, tailMsg];
83
+ } catch {
84
+ // Non-fatal: leave blocks staged (flag stays unset) and return the
85
+ // unmodified view. The context event still proceeds normally.
86
+ return messages as AgentMessage[];
87
+ }
88
+ }