pi-mega-compact 0.11.3 → 0.11.5

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 (307) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +19 -0
  2. package/dist/extensions/dashboard-server/api-contracts/memory.js +8 -0
  3. package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/setup.js +10 -0
  5. package/dist/extensions/dashboard-server/index-reader.js +6 -2
  6. package/dist/extensions/dashboard-server/routes-cache.js +115 -55
  7. package/dist/extensions/dashboard-server/routes-memory.js +51 -0
  8. package/dist/extensions/dashboard-server/routes-repo.js +2 -2
  9. package/dist/extensions/dashboard-server/routes-setup.js +162 -0
  10. package/dist/extensions/dashboard-server/routes.js +2 -0
  11. package/dist/extensions/dashboard-server/server.js +7 -1
  12. package/dist/extensions/mega-commands.js +189 -1
  13. package/dist/extensions/mega-config.js +3 -0
  14. package/dist/extensions/mega-events/agent-handlers.js +41 -6
  15. package/dist/extensions/mega-events/compact-handlers.js +1 -0
  16. package/dist/extensions/mega-events/context-handler.js +37 -92
  17. package/dist/extensions/mega-events/context-hwm.js +51 -0
  18. package/dist/extensions/mega-events/error-classifier.js +118 -128
  19. package/dist/extensions/mega-events/mirror-append.js +107 -0
  20. package/dist/extensions/mega-events/outage-advisor.js +59 -0
  21. package/dist/extensions/mega-events/perf-handler.js +51 -1
  22. package/dist/extensions/mega-events/recall-tail.js +59 -0
  23. package/dist/extensions/mega-events/session-handlers.js +15 -8
  24. package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
  25. package/dist/extensions/mega-runtime/runtime.js +7 -0
  26. package/dist/src/embedder.js +41 -2
  27. package/dist/src/memoryStats.js +75 -0
  28. package/dist/src/prefix-break.js +43 -0
  29. package/dist/src/pricing.js +1 -1
  30. package/dist/src/store/sqlite/perf-samples.js +144 -16
  31. package/dist/src/store/turns/memory-store.js +8 -0
  32. package/dist/src/store/turns/sqlite-store.js +14 -3
  33. package/dist/src/store/turns/types.js +18 -1
  34. package/dist/src/store/vectorIndex.js +26 -5
  35. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-BoL7qBOJ.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-BoL7qBOJ.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/AchievementsTab-BFDE3HYc.js +2 -0
  38. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-BFDE3HYc.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +2 -0
  40. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +1 -0
  41. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-DDFE-ckA.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-DDFE-ckA.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-B6UxZ7PM.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-B6UxZ7PM.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js → GameTab-DehUX-KE.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-DehUX-KE.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-0GWRt4wn.js} +2 -2
  48. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-0GWRt4wn.js.map} +1 -1
  49. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-C3rwMVsN.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-C3rwMVsN.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/OverviewTab-39O9_x5j.js +2 -0
  52. package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-39O9_x5j.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js +2 -0
  54. package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js.map +1 -0
  55. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-DXDTCi3r.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-DXDTCi3r.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +2 -0
  58. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +1 -0
  59. package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js +2 -0
  60. package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js.map +1 -0
  61. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BAw972BV.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BAw972BV.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-Cf5mGIdl.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-Cf5mGIdl.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js +2 -0
  66. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js.map +1 -0
  67. package/extensions/dashboard-client/dist/assets/{index-BX0bcgLu.js → index-4gQ7auad.js} +10 -10
  68. package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +1 -0
  69. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js → useSSE-DNgMuyP6.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-DNgMuyP6.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/index.html +1 -1
  74. package/extensions/dashboard-client/src/App.tsx +4 -0
  75. package/extensions/dashboard-client/src/api/client.ts +14 -0
  76. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
  77. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +56 -0
  78. package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +38 -57
  79. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +1 -0
  80. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +355 -0
  81. package/extensions/dashboard-client/tsconfig.json +3 -2
  82. package/extensions/dashboard-client/vite.config.ts +2 -0
  83. package/extensions/dashboard-server/api-contracts/endpoints.ts +36 -3
  84. package/extensions/dashboard-server/api-contracts/index.ts +7 -0
  85. package/extensions/dashboard-server/api-contracts/memory.ts +27 -0
  86. package/extensions/dashboard-server/api-contracts/multi-repo.ts +231 -231
  87. package/extensions/dashboard-server/api-contracts/provider-cache.ts +37 -0
  88. package/extensions/dashboard-server/api-contracts/setup.ts +58 -0
  89. package/extensions/dashboard-server/index-reader.ts +138 -121
  90. package/extensions/dashboard-server/routes-cache.ts +161 -85
  91. package/extensions/dashboard-server/routes-memory.ts +63 -0
  92. package/extensions/dashboard-server/routes-repo.ts +36 -36
  93. package/extensions/dashboard-server/routes-setup.ts +181 -0
  94. package/extensions/dashboard-server/routes.ts +2 -0
  95. package/extensions/dashboard-server/server.ts +6 -0
  96. package/extensions/mega-commands.ts +211 -1
  97. package/extensions/mega-config.ts +18 -0
  98. package/extensions/mega-events/agent-handlers.ts +44 -5
  99. package/extensions/mega-events/compact-handlers.ts +1 -0
  100. package/extensions/mega-events/context-handler.ts +45 -110
  101. package/extensions/mega-events/context-hwm.ts +85 -0
  102. package/extensions/mega-events/error-classifier.ts +156 -124
  103. package/extensions/mega-events/mirror-append.ts +155 -0
  104. package/extensions/mega-events/outage-advisor.ts +79 -0
  105. package/extensions/mega-events/perf-handler.ts +79 -5
  106. package/extensions/mega-events/recall-tail.ts +88 -0
  107. package/extensions/mega-events/session-handlers.ts +14 -7
  108. package/extensions/mega-runtime/helpers.ts +14 -0
  109. package/extensions/mega-runtime/reset-runtime.ts +6 -0
  110. package/extensions/mega-runtime/runtime.ts +7 -0
  111. package/package.json +5 -2
  112. package/src/embedder.ts +42 -2
  113. package/src/memoryStats.ts +107 -0
  114. package/src/prefix-break.ts +83 -0
  115. package/src/pricing.ts +2 -2
  116. package/src/store/sqlite/perf-samples.ts +215 -22
  117. package/src/store/turns/memory-store.ts +10 -1
  118. package/src/store/turns/sqlite-store.ts +27 -15
  119. package/src/store/turns/types.ts +21 -0
  120. package/src/store/vectorIndex.ts +33 -4
  121. package/dist/extensions/conflict-scan.test.js +0 -115
  122. package/dist/extensions/dashboard-server/api-contracts.test.js +0 -884
  123. package/dist/extensions/dashboard-server/html.test.js +0 -52
  124. package/dist/extensions/dashboard-server/perf-server.test.js +0 -80
  125. package/dist/extensions/dashboard-server/routes-cache.test.js +0 -248
  126. package/dist/extensions/dashboard-server/server.test.js +0 -120
  127. package/dist/extensions/dashboard-server-s32.test.js +0 -181
  128. package/dist/extensions/dashboard-server.test.js +0 -339
  129. package/dist/extensions/mega-cache-replay.test.js +0 -400
  130. package/dist/extensions/mega-compact-s38.test.js +0 -820
  131. package/dist/extensions/mega-compact.test.js +0 -1112
  132. package/dist/extensions/mega-events.test.js +0 -47
  133. package/dist/extensions/mega-game-cmds.test.js +0 -163
  134. package/dist/extensions/mega-runtime/snapshot.test.js +0 -127
  135. package/dist/extensions/mega-runtime/state.test.js +0 -266
  136. package/dist/extensions/mega-runtime/widget.test.js +0 -343
  137. package/dist/extensions/mega-shutdown-widget.test.js +0 -121
  138. package/dist/extensions/mega-teamrun.test.js +0 -156
  139. package/dist/extensions/mega-trim.test.js +0 -58
  140. package/dist/extensions/mega-turn-store.test.js +0 -68
  141. package/dist/src/boundary.test.js +0 -170
  142. package/dist/src/compact.test.js +0 -78
  143. package/dist/src/config/themes.test.js +0 -94
  144. package/dist/src/dedup/dedup.test.js +0 -41
  145. package/dist/src/dedup/l1.test.js +0 -50
  146. package/dist/src/dedup/raptor/buildHistory.test.js +0 -292
  147. package/dist/src/dedup/raptor/multilevel-serve.test.js +0 -229
  148. package/dist/src/dedup/raptor/multilevel.test.js +0 -238
  149. package/dist/src/dedup/raptor/promote.test.js +0 -69
  150. package/dist/src/dedup/raptor/raptor.test.js +0 -248
  151. package/dist/src/dedup/raptor/retrieval.test.js +0 -95
  152. package/dist/src/dedup/raptor/serve-gate.test.js +0 -298
  153. package/dist/src/dedup/sprint12.test.js +0 -219
  154. package/dist/src/dedup-engine.test.js +0 -472
  155. package/dist/src/driftDetection.test.js +0 -87
  156. package/dist/src/e2e.test.js +0 -698
  157. package/dist/src/engine.test.js +0 -111
  158. package/dist/src/extractive.test.js +0 -147
  159. package/dist/src/fork.test.js +0 -81
  160. package/dist/src/game/scoring.test.js +0 -98
  161. package/dist/src/httpEmbedder.test.js +0 -277
  162. package/dist/src/importance.test.js +0 -372
  163. package/dist/src/intent.test.js +0 -78
  164. package/dist/src/log.test.js +0 -42
  165. package/dist/src/mechanical-fix.test.js +0 -65
  166. package/dist/src/memory.test.js +0 -70
  167. package/dist/src/memoryConsolidate.test.js +0 -38
  168. package/dist/src/memoryOps.test.js +0 -129
  169. package/dist/src/memoryRecall.test.js +0 -171
  170. package/dist/src/memoryRoundtrip.test.js +0 -137
  171. package/dist/src/metrics/turns.test.js +0 -152
  172. package/dist/src/mirror/mirror.test.js +0 -185
  173. package/dist/src/raptor-inject-summaries.test.js +0 -162
  174. package/dist/src/ratio.bench.test.js +0 -897
  175. package/dist/src/recall.integration.test.js +0 -77
  176. package/dist/src/recall.test.js +0 -379
  177. package/dist/src/sprint14.test.js +0 -219
  178. package/dist/src/sprint4x-rag-verification.test.js +0 -93
  179. package/dist/src/store/compression.test.js +0 -139
  180. package/dist/src/store/memoryIndex.test.js +0 -51
  181. package/dist/src/store/migrate.test.js +0 -139
  182. package/dist/src/store/pgOpenGuard.test.js +0 -74
  183. package/dist/src/store/phase01.test.js +0 -81
  184. package/dist/src/store/phase04.test.js +0 -54
  185. package/dist/src/store/sprint10.test.js +0 -186
  186. package/dist/src/store/sqlite/game-achievements.test.js +0 -67
  187. package/dist/src/store/sqlite/game-scores.test.js +0 -106
  188. package/dist/src/store/sqlite/game-state.test.js +0 -76
  189. package/dist/src/store/sqlite/global-index.test.js +0 -136
  190. package/dist/src/store/sqlite/mechanical-fix.test.js +0 -146
  191. package/dist/src/store/sqlite/perf-samples.test.js +0 -146
  192. package/dist/src/store/sqlite/raptor.test.js +0 -106
  193. package/dist/src/store/sqlite/turns.test.js +0 -183
  194. package/dist/src/store/sqlite.cachehit.test.js +0 -55
  195. package/dist/src/store/sqlite.dbmaint.test.js +0 -158
  196. package/dist/src/store/sqlite.dbmirror.test.js +0 -184
  197. package/dist/src/store/turns/contract-compliance.test.js +0 -428
  198. package/dist/src/store/turns/memory-store.test.js +0 -9
  199. package/dist/src/store/turns/migrations.test.js +0 -138
  200. package/dist/src/store/turns/sqlite-store.test.js +0 -144
  201. package/dist/src/store/vectorIndex.test.js +0 -123
  202. package/dist/src/store.test.js +0 -161
  203. package/dist/src/supersede.test.js +0 -36
  204. package/dist/src/topics/cluster.test.js +0 -150
  205. package/dist/src/topics/store.test.js +0 -117
  206. package/dist/src/types.test.js +0 -9
  207. package/dist/src/vector-search-cache.test.js +0 -157
  208. package/dist/src/vectorStore.test.js +0 -479
  209. package/dist/src/wiki.test.js +0 -171
  210. package/extensions/conflict-scan.test.ts +0 -129
  211. package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +0 -2
  212. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +0 -2
  213. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +0 -1
  214. package/extensions/dashboard-client/dist/assets/OverviewTab-D4kGvsiQ.js +0 -2
  215. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +0 -2
  216. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +0 -1
  217. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +0 -2
  218. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +0 -1
  219. package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js.map +0 -1
  220. package/extensions/dashboard-server/api-contracts.test.ts +0 -1065
  221. package/extensions/dashboard-server/html.test.ts +0 -62
  222. package/extensions/dashboard-server/perf-server.test.ts +0 -101
  223. package/extensions/dashboard-server/routes-cache.test.ts +0 -302
  224. package/extensions/dashboard-server/server.test.ts +0 -131
  225. package/extensions/dashboard-server-s32.test.ts +0 -195
  226. package/extensions/dashboard-server.test.ts +0 -382
  227. package/extensions/mega-cache-replay.test.ts +0 -526
  228. package/extensions/mega-compact-s38.test.ts +0 -872
  229. package/extensions/mega-compact.test.ts +0 -1509
  230. package/extensions/mega-events.test.ts +0 -55
  231. package/extensions/mega-game-cmds.test.ts +0 -197
  232. package/extensions/mega-runtime/snapshot.test.ts +0 -139
  233. package/extensions/mega-runtime/state.test.ts +0 -262
  234. package/extensions/mega-runtime/widget.test.ts +0 -468
  235. package/extensions/mega-shutdown-widget.test.ts +0 -141
  236. package/extensions/mega-teamrun.test.ts +0 -176
  237. package/extensions/mega-trim.test.ts +0 -64
  238. package/extensions/mega-turn-store.test.ts +0 -108
  239. package/src/boundary.test.ts +0 -187
  240. package/src/compact.test.ts +0 -99
  241. package/src/config/themes.test.ts +0 -116
  242. package/src/dedup/dedup.test.ts +0 -46
  243. package/src/dedup/l1.test.ts +0 -57
  244. package/src/dedup/raptor/buildHistory.test.ts +0 -353
  245. package/src/dedup/raptor/multilevel-serve.test.ts +0 -273
  246. package/src/dedup/raptor/multilevel.test.ts +0 -325
  247. package/src/dedup/raptor/promote.test.ts +0 -82
  248. package/src/dedup/raptor/raptor.test.ts +0 -297
  249. package/src/dedup/raptor/retrieval.test.ts +0 -118
  250. package/src/dedup/raptor/serve-gate.test.ts +0 -348
  251. package/src/dedup/sprint12.test.ts +0 -242
  252. package/src/dedup-engine.test.ts +0 -670
  253. package/src/driftDetection.test.ts +0 -100
  254. package/src/e2e.test.ts +0 -843
  255. package/src/engine.test.ts +0 -123
  256. package/src/extractive.test.ts +0 -174
  257. package/src/fork.test.ts +0 -94
  258. package/src/game/scoring.test.ts +0 -103
  259. package/src/httpEmbedder.test.ts +0 -286
  260. package/src/importance.test.ts +0 -538
  261. package/src/intent.test.ts +0 -87
  262. package/src/log.test.ts +0 -47
  263. package/src/mechanical-fix.test.ts +0 -70
  264. package/src/memory.test.ts +0 -92
  265. package/src/memoryConsolidate.test.ts +0 -47
  266. package/src/memoryOps.test.ts +0 -148
  267. package/src/memoryRecall.test.ts +0 -194
  268. package/src/memoryRoundtrip.test.ts +0 -155
  269. package/src/metrics/turns.test.ts +0 -165
  270. package/src/mirror/mirror.test.ts +0 -240
  271. package/src/raptor-inject-summaries.test.ts +0 -228
  272. package/src/ratio.bench.test.ts +0 -1316
  273. package/src/recall.integration.test.ts +0 -96
  274. package/src/recall.test.ts +0 -467
  275. package/src/sprint14.test.ts +0 -245
  276. package/src/sprint4x-rag-verification.test.ts +0 -119
  277. package/src/store/compression.test.ts +0 -168
  278. package/src/store/memoryIndex.test.ts +0 -61
  279. package/src/store/migrate.test.ts +0 -158
  280. package/src/store/pgOpenGuard.test.ts +0 -89
  281. package/src/store/phase01.test.ts +0 -88
  282. package/src/store/phase04.test.ts +0 -58
  283. package/src/store/sprint10.test.ts +0 -214
  284. package/src/store/sqlite/game-achievements.test.ts +0 -80
  285. package/src/store/sqlite/game-scores.test.ts +0 -132
  286. package/src/store/sqlite/game-state.test.ts +0 -89
  287. package/src/store/sqlite/global-index.test.ts +0 -222
  288. package/src/store/sqlite/mechanical-fix.test.ts +0 -162
  289. package/src/store/sqlite/perf-samples.test.ts +0 -169
  290. package/src/store/sqlite/raptor.test.ts +0 -139
  291. package/src/store/sqlite/turns.test.ts +0 -218
  292. package/src/store/sqlite.cachehit.test.ts +0 -68
  293. package/src/store/sqlite.dbmaint.test.ts +0 -193
  294. package/src/store/sqlite.dbmirror.test.ts +0 -229
  295. package/src/store/turns/contract-compliance.test.ts +0 -562
  296. package/src/store/turns/memory-store.test.ts +0 -15
  297. package/src/store/turns/migrations.test.ts +0 -161
  298. package/src/store/turns/sqlite-store.test.ts +0 -161
  299. package/src/store/vectorIndex.test.ts +0 -140
  300. package/src/store.test.ts +0 -191
  301. package/src/supersede.test.ts +0 -42
  302. package/src/topics/cluster.test.ts +0 -176
  303. package/src/topics/store.test.ts +0 -132
  304. package/src/types.test.ts +0 -10
  305. package/src/vector-search-cache.test.ts +0 -190
  306. package/src/vectorStore.test.ts +0 -533
  307. package/src/wiki.test.ts +0 -217
package/src/e2e.test.ts DELETED
@@ -1,843 +0,0 @@
1
- /**
2
- * e2e.test.ts — comprehensive end-to-end pipeline tests.
3
- *
4
- * Exercises the full compaction → storage → dedup → recall pipeline across
5
- * 12 scenarios. Each test is self-contained with its own temp store.
6
- */
7
-
8
- import { test } from "node:test";
9
- import assert from "node:assert/strict";
10
- import { mkdtempSync, rmSync } from "node:fs";
11
- import { tmpdir } from "node:os";
12
- import { join } from "node:path";
13
- import { VectorStore, computeRegionHash, vectorList, vectorSemDedup, vectorWasInjected, vectorMarkInjected, vectorTopSimilar, vectorStats, vectorSearch } from "./vectorStore.js";
14
- import { compactSession, recall, supersededCount } from "./engine.js";
15
- import { loadDedupConfig, type DedupConfigShape } from "./config/dedup.js";
16
- import { defaultEmbedder } from "./embedder.js";
17
- import { decompressSmart } from "./store.js";
18
- import {
19
- upsertCheckpoint,
20
- closeStore,
21
- } from "./store/sqlite.js";
22
- import type { EngineMessage } from "./types.js";
23
- import type { StoredCheckpoint } from "./store.js";
24
-
25
- // ---------------------------------------------------------------------------
26
- // Temp dir management — each store() gets its own isolated state dir.
27
- // ---------------------------------------------------------------------------
28
-
29
- const baseTmp = mkdtempSync(join(tmpdir(), "mc-e2e-"));
30
- let counter = 0;
31
-
32
- function storeDir(): string {
33
- return join(baseTmp, `run-${counter++}`);
34
- }
35
-
36
- function store(over: Partial<DedupConfigShape> = {}): VectorStore {
37
- const dir = storeDir();
38
- const config: DedupConfigShape = { ...loadDedupConfig(), ...over };
39
- return new VectorStore({ stateDir: dir, config });
40
- }
41
-
42
- function msg(role: EngineMessage["role"], text: string, toolName?: string, input?: string, output?: string): EngineMessage {
43
- return toolName
44
- ? { role, text, toolName, input: input ?? text, output: output ?? text }
45
- : { role, text };
46
- }
47
-
48
- /** Seed a checkpoint directly into SQLite (bypasses online dedup tiers). */
49
- function seedDirect(dir: string, sessionId: string, rows: { id: string; text: string; tok: number; ts?: number }[]): void {
50
- const e = defaultEmbedder();
51
- for (const r of rows) {
52
- const cp: StoredCheckpoint = {
53
- checkpointId: r.id,
54
- sessionId,
55
- summary: r.text,
56
- keyDecisions: [],
57
- nextSteps: [],
58
- filesModified: [],
59
- tokenEstimate: r.tok,
60
- regionHash: computeRegionHash(r.text),
61
- embedding: e.embed(r.text),
62
- timestamp: r.ts ?? 1,
63
- };
64
- upsertCheckpoint(cp, dir);
65
- }
66
- }
67
-
68
- // ---------------------------------------------------------------------------
69
- // 1. Full Compaction Pipeline (SUPERSEDE → COLLAPSE → CLUSTER)
70
- // ---------------------------------------------------------------------------
71
-
72
- test("1. Full compaction pipeline: SUPERSEDE → COLLAPSE → CLUSTER", () => {
73
- const s = store();
74
- const SESS = "sess_full_pipeline";
75
-
76
- // Build a realistic 20+ message conversation with file reads, edits,
77
- // user questions, and assistant responses with tool calls.
78
- const messages: EngineMessage[] = [
79
- msg("user", "read src/server.ts to understand the current setup"),
80
- msg("assistant", "Reading src/server.ts", "Read", "src/server.ts", "const server = createServer(...)"),
81
- msg("user", "the server has a memory leak, let's fix it"),
82
- msg("assistant", "I see the leak in src/server.ts:42 — the event listeners are not cleaned up", "Edit"),
83
- msg("user", "edit src/server.ts to remove the listeners on cleanup"),
84
- msg("assistant", "Edited src/server.ts:42 — added cleanup() to remove all listeners", "Edit"),
85
- msg("user", "now read src/router.ts"),
86
- msg("assistant", "Reading src/router.ts", "Read", "src/router.ts", "export const router = new Map()"),
87
- msg("user", "the router is missing the /health endpoint"),
88
- msg("assistant", "I'll add a /health endpoint to src/router.ts", "Edit"),
89
- msg("user", "also read src/middleware.ts for the auth check"),
90
- msg("assistant", "Reading src/middleware.ts", "Read", "src/middleware.ts", "function authMiddleware(req, res, next) {...}"),
91
- msg("user", "the auth middleware should use JWT instead of session cookies"),
92
- msg("assistant", "Refactoring src/middleware.ts to use JWT verification", "Edit"),
93
- msg("user", "add tests for the new JWT auth in src/auth.test.ts"),
94
- msg("assistant", "Created src/auth.test.ts with 5 test cases for JWT validation", "Edit"),
95
- msg("user", "run the tests to make sure everything passes"),
96
- msg("assistant", "Running npm test", "Bash", "npm test", "All 5 auth tests passed"),
97
- msg("user", "great, now let's deploy this"),
98
- msg("assistant", "Deploying to staging environment", "Bash", "npm run deploy", "Deployed to staging"),
99
- msg("user", "check the staging logs for any errors"),
100
- msg("assistant", "Checking staging logs", "Bash", "kubectl logs", "No errors detected"),
101
- ];
102
-
103
- // Compact the older portion (keep last 4 messages verbatim).
104
- const r = compactSession({ sessionId: SESS, messages, keepFrom: 18, timestamp: 100 }, s);
105
-
106
- // Verify: not skipped
107
- assert.equal(r.skipped, false, "compaction should not be skipped");
108
-
109
- // Verify: summary is non-empty
110
- assert.ok(r.summary.length > 0, "summary should be produced by COLLAPSE");
111
-
112
- // Verify: regionHash is computed
113
- assert.ok(r.regionHash.length > 0, "regionHash should be computed");
114
- assert.match(r.regionHash, /^[0-9a-f]{16}$/, "regionHash is 16-hex SHA-256 prefix");
115
-
116
- // Verify: SUPERSEDE dropped obsolete file reads (the first reads of
117
- // server.ts, router.ts, middleware.ts are superseded by later edits)
118
- const compactable = messages.slice(0, 18);
119
- const superseded = supersededCount(compactable);
120
- assert.ok(superseded >= 1, `at least 1 superseded file read, got ${superseded}`);
121
-
122
- // Verify: checkpoint persisted
123
- assert.ok(r.checkpointId, "checkpointId should be set");
124
- assert.match(r.checkpointId!, /^chkpt_001$/);
125
-
126
- // Verify: checkpoint is searchable via vectorSearch(store, )
127
- const hits = vectorSearch(s, SESS, "server.ts memory leak JWT auth", 5);
128
- assert.ok(hits.length > 0, "checkpoint should be searchable");
129
- assert.equal(hits[0].checkpoint.checkpointId, r.checkpointId);
130
- });
131
-
132
- // ---------------------------------------------------------------------------
133
- // 2. Multi-Session Dedup (L0 exact hash)
134
- // ---------------------------------------------------------------------------
135
-
136
- test("2. Multi-session dedup: identical content in same session detected by L0", () => {
137
- const s = store();
138
-
139
- // First compaction — stores a checkpoint
140
- const messagesA: EngineMessage[] = [
141
- msg("user", "read src/database.ts and fix the connection pooling"),
142
- msg("assistant", "Fixed connection pool in src/database.ts", "Edit"),
143
- msg("user", "add a retry mechanism for failed queries"),
144
- msg("assistant", "Added retry logic in src/database.ts:85", "Edit"),
145
- ];
146
-
147
- const rA = compactSession({ sessionId: "sess_multi_a", messages: messagesA, keepFrom: 4, timestamp: 1 }, s);
148
- assert.equal(rA.deduped, false, "first compaction should not be deduped");
149
- assert.ok(rA.checkpointId);
150
-
151
- // Same session, identical content — L0 exact hash should detect the duplicate
152
- const messagesB: EngineMessage[] = [
153
- msg("user", "read src/database.ts and fix the connection pooling"),
154
- msg("assistant", "Fixed connection pool in src/database.ts", "Edit"),
155
- msg("user", "add a retry mechanism for failed queries"),
156
- msg("assistant", "Added retry logic in src/database.ts:85", "Edit"),
157
- ];
158
-
159
- const rB = compactSession({ sessionId: "sess_multi_a", messages: messagesB, keepFrom: 4, timestamp: 2 }, s);
160
-
161
- // L0 exact hash dedup should catch this — same regionText → same regionHash
162
- assert.equal(rB.deduped, true, "second identical compaction should be deduped by L0");
163
- assert.ok(rB.dedupReason === "regionHash" || rB.dedupReason === "contentHash",
164
- `dedup reason should be L0 (regionHash or contentHash), got ${rB.dedupReason}`);
165
-
166
- // Search should still return the checkpoint
167
- const hits = vectorSearch(s, "sess_multi_a", "database connection pooling", 5);
168
- assert.ok(hits.length > 0, "search returns the deduped result");
169
- assert.equal(hits[0].checkpoint.checkpointId, rA.checkpointId);
170
- });
171
-
172
- // ---------------------------------------------------------------------------
173
- // 3. Near-Duplicate Detection (L1 MinHash/LSH)
174
- // ---------------------------------------------------------------------------
175
-
176
- test("3. Near-duplicate detection: L1 MinHash/LSH catches one-word edits that L0 misses", () => {
177
- // Disable L2 so we isolate L1 behavior
178
- const s = store({ L2_ENABLED: false });
179
- const SESS = "sess_l1_e2e";
180
-
181
- // First conversation
182
- const r1 = s.add({
183
- sessionId: SESS,
184
- summary: "user reviewed the authentication module and merged the pull request",
185
- regionText: "user reviewed the authentication module and merged the pull request on github after ci passed",
186
- timestamp: 1,
187
- });
188
- assert.equal(r1.deduped, false);
189
-
190
- // Near-duplicate: a few word changes that L0 exact hash misses
191
- const r2 = s.add({
192
- sessionId: SESS,
193
- summary: "user reviewed the authentication module and merged the pull request",
194
- regionText: "user reviewed the authentication module and merged the pull requests on github after ci passed",
195
- timestamp: 2,
196
- });
197
-
198
- // L1 should catch the near-dup
199
- assert.equal(r2.deduped, true, "L1 should catch the near-duplicate");
200
- assert.equal(r2.reason, "l1MinHash", "dedup reason should be l1MinHash");
201
- assert.equal(vectorList(s,SESS).length, 1, "only one checkpoint stored");
202
- });
203
-
204
- test("3b. Negative: L1 does NOT collapse genuinely different content", () => {
205
- const s = store({ L2_ENABLED: false });
206
- const SESS = "sess_l1_neg";
207
-
208
- s.add({
209
- sessionId: SESS,
210
- summary: "database migration",
211
- regionText: "the database migration added three new indexes to the users table for faster lookups",
212
- timestamp: 1,
213
- });
214
-
215
- const r2 = s.add({
216
- sessionId: SESS,
217
- summary: "frontend dark mode",
218
- regionText: "the frontend added a dark mode toggle with css custom properties for theming",
219
- timestamp: 2,
220
- });
221
-
222
- assert.equal(r2.deduped, false, "distinct content should not be deduped");
223
- assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored");
224
- });
225
-
226
- // ---------------------------------------------------------------------------
227
- // 4. Semantic Dedup (L2 cosine)
228
- // ---------------------------------------------------------------------------
229
-
230
- test("4. Semantic dedup: L2 cosine catches highly similar content that L0/L1 miss", () => {
231
- const s = store();
232
- const SESS = "sess_l2_e2e";
233
-
234
- // First conversation about authentication and session tokens
235
- const r1 = s.add({
236
- sessionId: SESS,
237
- summary: "user authentication and session token management",
238
- regionText: "user authentication and session token management login validation session expiry handling secure cookie management",
239
- timestamp: 1,
240
- });
241
- assert.equal(r1.deduped, false);
242
-
243
- // Second conversation — nearly identical with a few word swaps
244
- // L0 won't catch (different text), L1 might not catch (too many word changes)
245
- // L2 cosine on trigram embeddings should catch this high-similarity paraphrase
246
- const r2 = s.add({
247
- sessionId: SESS,
248
- summary: "user authentication and session token management login validation",
249
- regionText: "user authentication and session token management login validation session expiry handling secure cookie configuration",
250
- timestamp: 2,
251
- });
252
-
253
- // L2 semantic cosine should catch this high-similarity paraphrase
254
- assert.equal(r2.deduped, true, "L2 should catch the high-similarity paraphrase");
255
- assert.ok(r2.reason === "contentSimilarity" || r2.reason === "l1MinHash",
256
- `dedup reason should be L2 contentSimilarity or L1 l1MinHash, got ${r2.reason}`);
257
- assert.equal(vectorList(s,SESS).length, 1, "only one checkpoint after semantic dedup");
258
- });
259
-
260
- test("4b. MMR diversifies search results", () => {
261
- const s = store();
262
- const SESS = "sess_mmr_e2e";
263
-
264
- // Store several checkpoints — some near-identical, some distinct.
265
- // Use sufficiently different text to avoid L1/L2 dedup collapse.
266
- const regions = [
267
- "the parser handles tokenization of source code files with lexical analysis",
268
- "the database uses a b-tree index for fast lookups on the users table",
269
- "the frontend renders a virtualized list for large datasets with lazy loading",
270
- "the api gateway implements rate limiting with a token bucket throttling algorithm",
271
- ];
272
-
273
- for (let i = 0; i < regions.length; i++) {
274
- s.add({
275
- sessionId: SESS,
276
- summary: `topic_${i}`,
277
- regionText: regions[i],
278
- timestamp: i + 1,
279
- });
280
- }
281
-
282
- // Search for parser-related content — should return relevant results
283
- const hits = vectorSearch(s, SESS, "parser tokenization source code files", 4);
284
- assert.ok(hits.length >= 1, "should return at least one result");
285
-
286
- // The top hit should be the parser checkpoint
287
- assert.ok(hits[0].checkpoint.summary.includes("topic_0"), "top hit is the parser topic");
288
-
289
- // MMR should provide diversity — not all results should be near-identical
290
- // (with 4 distinct topics, we should get variety in the results)
291
- const summaries = hits.map((h) => h.checkpoint.summary);
292
- const uniqueSummaries = new Set(summaries);
293
- assert.ok(uniqueSummaries.size >= 2, "MMR provides diverse results");
294
- });
295
-
296
- // ---------------------------------------------------------------------------
297
- // 5. SemDeDup Offline Cleanup
298
- // ---------------------------------------------------------------------------
299
-
300
- test("5. SemDeDup offline cleanup: redundant rows marked removed, not deleted", () => {
301
- const dir = storeDir();
302
- const s = new VectorStore({ stateDir: dir, config: { ...loadDedupConfig() } });
303
- const SESS = "sess_semdedup";
304
-
305
- // Seed multiple checkpoints with varying similarity directly into SQLite
306
- // to bypass online dedup (SemDeDup's real job is offline cleanup)
307
- seedDirect(dir, SESS, [
308
- { id: "chkpt_001", text: "the cache stores parsed ast nodes for fast lookup and retrieval", tok: 100, ts: 1 },
309
- { id: "chkpt_002", text: "the cache stores parsed ast nodes for fast lookup and retrieval and reuse", tok: 900, ts: 2 },
310
- { id: "chkpt_003", text: "the frontend uses a virtualized list for rendering large datasets efficiently", tok: 500, ts: 3 },
311
- ]);
312
-
313
- // Run SemDeDup with threshold that catches the near-identical pair
314
- const removed = vectorSemDedup(s,SESS, 0.85);
315
- assert.equal(removed, 1, "one redundant row should be marked removed");
316
-
317
- // Verify: the lower-tokenEstimate row is the one removed
318
- const all = vectorList(s,SESS);
319
- const dropped = all.find((c) => c.dedupStatus === "removed");
320
- assert.ok(dropped, "a row should have dedup_status='removed'");
321
- assert.equal(dropped!.checkpointId, "chkpt_001", "lower tokenEstimate row removed");
322
- assert.equal(dropped!.dedupStatus, "removed");
323
-
324
- // Verify: search excludes removed rows (3 total, 1 removed → 2 active)
325
- const hits = vectorSearch(s, SESS, "cache parsed ast nodes frontend virtualized list", 10);
326
- assert.equal(hits.length, 2, "search excludes the removed row, returns 2 active");
327
- assert.ok(hits.every((h) => h.checkpoint.dedupStatus !== "removed"), "no removed rows in search");
328
-
329
- // Verify: idempotent re-run removes nothing new
330
- const secondRun = vectorSemDedup(s,SESS, 0.85);
331
- assert.equal(secondRun, 0, "idempotent re-run removes nothing new");
332
-
333
- closeStore(dir);
334
- });
335
-
336
- // ---------------------------------------------------------------------------
337
- // 6. Recall + Inline with Dedup Sentinel
338
- // ---------------------------------------------------------------------------
339
-
340
- test("6. Recall + inline with dedup sentinel: injected checkpoints are skipped", () => {
341
- const s = store();
342
- const SESS = "sess_recall";
343
-
344
- // Compact a session
345
- const messages: EngineMessage[] = [
346
- msg("user", "investigated src/vectorStore.ts to understand the dedup cascade"),
347
- msg("assistant", "Found the L0, L1, L2 tiers in src/vectorStore.ts", "Read"),
348
- msg("user", "now document the dedup pipeline in README.md"),
349
- msg("assistant", "Updated README.md with full dedup documentation", "Edit"),
350
- ];
351
-
352
- const r = compactSession({ sessionId: SESS, messages, keepFrom: 4, timestamp: 1 }, s);
353
- assert.ok(r.checkpointId);
354
-
355
- // Recall with a relevant query — should return the checkpoint
356
- const first = recall({ sessionId: SESS, query: "vectorStore dedup cascade", limit: 5, skipInjected: true }, s);
357
- assert.equal(first.newHits.length, 1, "first recall should find the checkpoint");
358
- assert.equal(first.hits.length, 1);
359
-
360
- // Mark it injected
361
- const cpId = first.hits[0].checkpoint.checkpointId;
362
- vectorMarkInjected(s,SESS, cpId);
363
-
364
- // Recall again — should skip the injected checkpoint
365
- const second = recall({ sessionId: SESS, query: "vectorStore dedup cascade", limit: 5, skipInjected: true }, s);
366
- assert.equal(second.newHits.length, 0, "injected checkpoint should be skipped (dedup sentinel)");
367
-
368
- // Without skipInjected, hits still surface
369
- assert.equal(second.hits.length, 1, "hits still surface without skipInjected");
370
-
371
- // Verify wasInjected
372
- assert.equal(vectorWasInjected(s,SESS, cpId), true, "wasInjected returns true for injected checkpoint");
373
- assert.equal(vectorWasInjected(s,SESS, "chkpt_999"), false, "wasInjected returns false for non-injected");
374
- });
375
-
376
- // ---------------------------------------------------------------------------
377
- // 7. topSimilar
378
- // ---------------------------------------------------------------------------
379
-
380
- test("7. topSimilar: returns n most cosine-similar checkpoints", () => {
381
- const s = store();
382
- const SESS = "sess_topsim";
383
-
384
- // Store several checkpoints with varying topics
385
- const regions = [
386
- "the compiler optimized the hot loop with loop unrolling and inlining",
387
- "the database added a covering index to speed up queries on the users table",
388
- "the frontend introduced a virtualized list for large tables with lazy rendering",
389
- "the api added rate limiting using a token bucket algorithm for throttling",
390
- "the worker pool now backpressures when the queue is overloaded with tasks",
391
- ];
392
-
393
- for (let i = 0; i < regions.length; i++) {
394
- s.add({
395
- sessionId: SESS,
396
- summary: `topic_${i}`,
397
- regionText: regions[i],
398
- timestamp: i + 1,
399
- });
400
- }
401
-
402
- // Call topSimilar(3)
403
- const hits = vectorTopSimilar(s,SESS, 3);
404
- assert.ok(hits.length <= 3, "should respect n limit");
405
- assert.ok(hits.length > 0, "should return results");
406
-
407
- // Verify self-exclusion: the most recent checkpoint should not be in results
408
- const all = vectorList(s,SESS);
409
- const ordered = [...all].sort((a, b) => a.checkpointId.localeCompare(b.checkpointId));
410
- const current = ordered[ordered.length - 1];
411
- assert.ok(!hits.some((h) => h.checkpoint.checkpointId === current.checkpointId), "current checkpoint excluded");
412
-
413
- // Verify scores are descending
414
- for (let i = 1; i < hits.length; i++) {
415
- assert.ok(hits[i - 1].score >= hits[i].score, "scores should be descending");
416
- }
417
- });
418
-
419
- test("7b. topSimilar respects n limit strictly", () => {
420
- const s = store();
421
- const SESS = "sess_topsim_limit";
422
-
423
- // Use sufficiently distinct texts to avoid L1 dedup collapse
424
- const regions = [
425
- "the compiler optimizer unrolls loops and inlines function calls for speed",
426
- "the database engine uses a covering b-tree index to accelerate lookups",
427
- "the frontend framework virtualizes large lists with lazy rendering and recycling",
428
- "the api gateway throttles requests using a token bucket rate limiting algorithm",
429
- "the worker pool applies backpressure when the task queue exceeds capacity limits",
430
- ];
431
-
432
- for (let i = 0; i < regions.length; i++) {
433
- s.add({
434
- sessionId: SESS,
435
- summary: `c${i}`,
436
- regionText: regions[i],
437
- timestamp: i + 1,
438
- });
439
- }
440
-
441
- const hits = vectorTopSimilar(s,SESS, 2);
442
- assert.equal(hits.length, 2, "n limit respected");
443
- });
444
-
445
- test("7c. topSimilar returns empty for sessions with 0 or 1 checkpoints", () => {
446
- const s = store();
447
- assert.deepEqual(vectorTopSimilar(s,"sess_empty", 5), []);
448
- s.add({ sessionId: "sess_one", summary: "solo", regionText: "only checkpoint here", timestamp: 1 });
449
- assert.deepEqual(vectorTopSimilar(s,"sess_one", 5), []);
450
- });
451
-
452
- // ---------------------------------------------------------------------------
453
- // 8. Compression Round-Trip
454
- // ---------------------------------------------------------------------------
455
-
456
- test("8. Compression round-trip: decompression produces original content", () => {
457
- const s = store();
458
- const SESS = "sess_compress";
459
-
460
- // Store a checkpoint with a realistic region text
461
- const originalText = [
462
- "user asked to read src/server.ts",
463
- "assistant read src/server.ts and found a memory leak at line 42",
464
- "user asked to fix the leak by removing unused event listeners",
465
- "assistant edited src/server.ts to add cleanup() call in the shutdown handler",
466
- "user asked to add tests for the cleanup logic",
467
- "assistant created src/server.test.ts with three test cases",
468
- ].join("\n");
469
-
470
- s.add({
471
- sessionId: SESS,
472
- summary: "fixed memory leak in server.ts and added tests",
473
- regionText: originalText,
474
- tokenEstimate: 500,
475
- timestamp: 1,
476
- });
477
-
478
- // Retrieve and verify decompression
479
- const cp = vectorList(s,SESS)[0];
480
- assert.ok(cp.compressedOriginal instanceof Buffer, "compressedOriginal is a Buffer");
481
-
482
- const restored = decompressSmart(cp.compressedOriginal as Buffer).toString("utf-8");
483
- assert.equal(restored, originalText, "decompression produces the original content");
484
- });
485
-
486
- test("8b. Compression round-trip with different content sizes", () => {
487
- const s = store();
488
- const SESS = "sess_compress_sizes";
489
-
490
- // Small text (under 512 bytes → raw tier)
491
- const small = "small region text about a minor fix";
492
- s.add({ sessionId: SESS, summary: "small", regionText: small, timestamp: 1 });
493
-
494
- // Medium text (512B–4KB → gzip level 1)
495
- const medium = "medium region text with more detail. ".repeat(50);
496
- s.add({ sessionId: SESS, summary: "medium", regionText: medium, timestamp: 2 });
497
-
498
- // Large text (>4KB → gzip level 6 or brotli)
499
- const large = "detailed region text with lots of context about the codebase. ".repeat(200);
500
- s.add({ sessionId: SESS, summary: "large", regionText: large, timestamp: 3 });
501
-
502
- const checkpoints = vectorList(s,SESS);
503
- assert.equal(checkpoints.length, 3);
504
-
505
- // Verify each round-trips correctly
506
- const texts = [small, medium, large];
507
- for (let i = 0; i < 3; i++) {
508
- const cp = checkpoints[i];
509
- assert.ok(cp.compressedOriginal instanceof Buffer);
510
- const restored = decompressSmart(cp.compressedOriginal as Buffer).toString("utf-8");
511
- assert.equal(restored, texts[i], `content ${i} round-trips correctly`);
512
- }
513
- });
514
-
515
- // ---------------------------------------------------------------------------
516
- // 9. Store Stats and Metrics
517
- // ---------------------------------------------------------------------------
518
-
519
- test("9. Store stats and metrics reflect actual state after compactions", () => {
520
- const s = store();
521
- const SESS = "sess_stats_e2e";
522
-
523
- // Compact three different conversations
524
- const conv1: EngineMessage[] = [
525
- msg("user", "read src/index.ts and refactor the imports"),
526
- msg("assistant", "Refactored imports in src/index.ts", "Edit"),
527
- ];
528
- const r1 = compactSession({ sessionId: SESS, messages: conv1, keepFrom: 2, timestamp: 1 }, s);
529
- assert.equal(r1.deduped, false);
530
-
531
- const conv2: EngineMessage[] = [
532
- msg("user", "fix the type errors in src/types.ts"),
533
- msg("assistant", "Fixed type errors in src/types.ts", "Edit"),
534
- ];
535
- const r2 = compactSession({ sessionId: SESS, messages: conv2, keepFrom: 2, timestamp: 2 }, s);
536
- assert.equal(r2.deduped, false);
537
-
538
- const conv3: EngineMessage[] = [
539
- msg("user", "add unit tests for the new utility functions"),
540
- msg("assistant", "Added tests in src/utils.test.ts", "Edit"),
541
- ];
542
- const r3 = compactSession({ sessionId: SESS, messages: conv3, keepFrom: 2, timestamp: 3 }, s);
543
- assert.equal(r3.deduped, false);
544
-
545
- // Get stats
546
- const st = vectorStats(s,SESS);
547
- assert.equal(st.checkpointCount, 3, "three checkpoints stored");
548
- assert.ok(st.totalTokenEstimate > 0, "totalTokenEstimate should be positive");
549
- assert.equal(st.lastCheckpointId, "chkpt_003", "last checkpoint id correct");
550
- assert.ok(st.lastSummary && st.lastSummary.length > 0, "last summary is non-empty");
551
- assert.equal(st.injectedCount, 0, "no injections yet");
552
- assert.equal(st.dedupHitRate, 0, "dedupHitRate is 0 with no injections");
553
-
554
- // Mark one injected and re-check
555
- vectorMarkInjected(s,SESS, "chkpt_001");
556
- const st2 = vectorStats(s,SESS);
557
- assert.equal(st2.injectedCount, 1, "one injection tracked");
558
- assert.ok(Math.abs(st2.dedupHitRate - 1 / 3) < 1e-9, "dedupHitRate = injected/checkpoints");
559
- });
560
-
561
- test("9b. Stats on empty session returns zeros", () => {
562
- const s = store();
563
- const st = vectorStats(s,"sess_nothing");
564
- assert.equal(st.checkpointCount, 0);
565
- assert.equal(st.totalTokenEstimate, 0);
566
- assert.equal(st.lastCheckpointId, undefined);
567
- assert.equal(st.lastSummary, undefined);
568
- assert.equal(st.dedupHitRate, 0);
569
- });
570
-
571
- // ---------------------------------------------------------------------------
572
- // 10. Concurrent Sessions
573
- // ---------------------------------------------------------------------------
574
-
575
- test("10. Concurrent sessions: no cross-contamination", () => {
576
- const s = store();
577
-
578
- // Three different sessions with distinct content
579
- const sessions = [
580
- { id: "sess_concurrent_a", msgs: [
581
- msg("user", "work on the authentication module in src/auth.ts"),
582
- msg("assistant", "Updated src/auth.ts with JWT validation", "Edit"),
583
- ]},
584
- { id: "sess_concurrent_b", msgs: [
585
- msg("user", "optimize the database queries in src/db.ts"),
586
- msg("assistant", "Added covering indexes in src/db.ts", "Edit"),
587
- ]},
588
- { id: "sess_concurrent_c", msgs: [
589
- msg("user", "fix the rendering bug in src/canvas.ts"),
590
- msg("assistant", "Fixed the canvas rendering loop in src/canvas.ts", "Edit"),
591
- ]},
592
- ];
593
-
594
- // Compact all three "simultaneously" (sequentially but back-to-back)
595
- const results = sessions.map((sess) =>
596
- compactSession({ sessionId: sess.id, messages: sess.msgs, keepFrom: 2, timestamp: 1 }, s),
597
- );
598
-
599
- // Each session has its own checkpoint
600
- results.forEach((r, i) => {
601
- assert.equal(r.skipped, false, `session ${i} should compact`);
602
- assert.equal(r.deduped, false, `session ${i} should not be deduped (distinct content)`);
603
- assert.ok(r.checkpointId);
604
- });
605
-
606
- // Verify: each session has exactly one checkpoint
607
- sessions.forEach((sess) => {
608
- const cps = vectorList(s,sess.id);
609
- assert.equal(cps.length, 1, `${sess.id} should have 1 checkpoint`);
610
- });
611
-
612
- // Verify: search in one session doesn't return another session's checkpoints
613
- const hitsA = vectorSearch(s, "sess_concurrent_a", "authentication JWT", 5);
614
- assert.ok(hitsA.every((h) => h.checkpoint.sessionId === "sess_concurrent_a"),
615
- "search in A returns only A's checkpoints");
616
-
617
- const hitsB = vectorSearch(s, "sess_concurrent_b", "database queries indexes", 5);
618
- assert.ok(hitsB.every((h) => h.checkpoint.sessionId === "sess_concurrent_b"),
619
- "search in B returns only B's checkpoints");
620
-
621
- const hitsC = vectorSearch(s, "sess_concurrent_c", "canvas rendering bug", 5);
622
- assert.ok(hitsC.every((h) => h.checkpoint.sessionId === "sess_concurrent_c"),
623
- "search in C returns only C's checkpoints");
624
- });
625
-
626
- // ---------------------------------------------------------------------------
627
- // 11. Edge Cases
628
- // ---------------------------------------------------------------------------
629
-
630
- test("11a. Empty session (no messages) — compactSession should skip", () => {
631
- const s = store();
632
- const r = compactSession({ sessionId: "sess_empty_msgs", messages: [], keepFrom: 0, timestamp: 1 }, s);
633
- assert.equal(r.skipped, true, "empty session should be skipped");
634
- assert.equal(r.summary, "");
635
- assert.equal(r.regionHash, "");
636
- assert.equal(vectorList(s,"sess_empty_msgs").length, 0);
637
- });
638
-
639
- test("11b. Single message session — should skip or handle gracefully", () => {
640
- const s = store();
641
- const r = compactSession({
642
- sessionId: "sess_single_msg",
643
- messages: [msg("user", "hello world")],
644
- keepFrom: 0,
645
- timestamp: 1,
646
- }, s);
647
- // With keepFrom=0, the single message is compactable, so it should work
648
- // but produce a minimal summary
649
- assert.ok(r.skipped === true || r.skipped === false, "should not throw");
650
- if (!r.skipped) {
651
- assert.ok(r.checkpointId);
652
- }
653
- });
654
-
655
- test("11c. Very large region text — should still compact and store", () => {
656
- const s = store();
657
- const SESS = "sess_large";
658
-
659
- // Build a large conversation with many messages
660
- const messages: EngineMessage[] = [];
661
- for (let i = 0; i < 50; i++) {
662
- messages.push(msg("user", `read src/module_${i}.ts and review the implementation of feature ${i}`));
663
- messages.push(msg("assistant", `Reviewed src/module_${i}.ts — feature ${i} looks good with minor issues`, "Read"));
664
- messages.push(msg("user", `fix the issues in src/module_${i}.ts`));
665
- messages.push(msg("assistant", `Fixed issues in src/module_${i}.ts`, "Edit"));
666
- }
667
-
668
- const r = compactSession({ sessionId: SESS, messages, keepFrom: 8, timestamp: 1 }, s);
669
- assert.equal(r.skipped, false, "large conversation should compact");
670
- assert.ok(r.checkpointId, "checkpoint created");
671
- assert.ok(r.summary.length > 0, "summary produced");
672
-
673
- // Verify it's searchable
674
- const hits = vectorSearch(s, SESS, "module feature fix review", 3);
675
- assert.ok(hits.length > 0, "large checkpoint is searchable");
676
- });
677
-
678
- test("11d. Unicode and emoji content — should normalize and hash correctly", () => {
679
- const s = store();
680
- const SESS = "sess_unicode";
681
-
682
- const messages: EngineMessage[] = [
683
- msg("user", "read src/本地化.ts and fix the 中文 translation issues 🌏"),
684
- msg("assistant", "Fixed translations in src/本地化.ts — updated 中文 strings and emoji handling 🌏✅", "Edit"),
685
- msg("user", "add support for 日本語 and 한국어 locales too"),
686
- msg("assistant", "Added 日本語 and 한국어 locale support in src/本地化.ts 🌏✅🇯🇵🇰🇷", "Edit"),
687
- ];
688
-
689
- const r = compactSession({ sessionId: SESS, messages, keepFrom: 4, timestamp: 1 }, s);
690
- assert.equal(r.skipped, false, "unicode conversation should compact");
691
- assert.ok(r.regionHash.length > 0, "regionHash computed for unicode content");
692
-
693
- // Verify the checkpoint exists and is searchable
694
- const hits = vectorSearch(s, SESS, "中文 translation 本地化", 3);
695
- assert.ok(hits.length > 0, "unicode checkpoint is searchable");
696
-
697
- // Verify regionHash is deterministic for the same unicode content
698
- const r2 = compactSession({ sessionId: SESS, messages, keepFrom: 4, timestamp: 2 }, s);
699
- assert.equal(r2.deduped, true, "identical unicode content should be deduped");
700
- assert.equal(r.regionHash, r2.regionHash, "regionHash is deterministic for unicode");
701
- });
702
-
703
- test("11e. All messages in preserve-recent window — nothing to compact", () => {
704
- const s = store();
705
- const messages: EngineMessage[] = [
706
- msg("user", "first message"),
707
- msg("assistant", "first response", "Edit"),
708
- msg("user", "second message"),
709
- msg("assistant", "second response", "Edit"),
710
- ];
711
-
712
- // keepFrom = 0 → compactable slice is empty (everything is "preserved")
713
- const r = compactSession({
714
- sessionId: "sess_all_preserved",
715
- messages,
716
- keepFrom: 0,
717
- timestamp: 1,
718
- }, s);
719
- assert.equal(r.skipped, true, "should skip when keepFrom=0 (nothing to compact)");
720
- assert.equal(vectorList(s,"sess_all_preserved").length, 0, "no checkpoints stored");
721
- });
722
-
723
- // ---------------------------------------------------------------------------
724
- // 12. Feature Flag Toggling
725
- // ---------------------------------------------------------------------------
726
-
727
- test("12a. L0_ENABLED=false — L0 does not collapse exact dups", () => {
728
- // Disable all tiers so no dedup happens at all
729
- const s = store({ L0_ENABLED: false, L1_ENABLED: false, L2_ENABLED: false });
730
- const SESS = "sess_l0_off";
731
-
732
- // Use different regionText for each add to avoid the SQLite content_hash
733
- // unique constraint (which fires regardless of L0 flag since the hash is
734
- // always computed). When L0 is disabled, the dedup cascade skips the
735
- // hash check, but the DB constraint is structural.
736
- const r1 = s.add({
737
- sessionId: SESS,
738
- summary: "first checkpoint about the parser",
739
- regionText: "the parser tokenizes the input into a stream of tokens for the compiler version one",
740
- timestamp: 1,
741
- });
742
- const r2 = s.add({
743
- sessionId: SESS,
744
- summary: "second checkpoint about the parser",
745
- regionText: "the parser tokenizes the input into a stream of tokens for the compiler version two",
746
- timestamp: 2,
747
- });
748
-
749
- assert.equal(r1.deduped, false);
750
- // With L0 off (and L1/L2 off too), near-dup should NOT be collapsed
751
- assert.equal(r2.deduped, false, "L0 disabled → near-dup not collapsed");
752
- assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored when L0 is off");
753
- });
754
-
755
- test("12b. MARK_ONLY_L1=true — L1 records but does not collapse", () => {
756
- // Disable L2 to isolate L1 behavior
757
- const s = store({ L1_ENABLED: true, MARK_ONLY_L1: true, L2_ENABLED: false });
758
- const SESS = "sess_mark_only_l1";
759
-
760
- const r1 = s.add({
761
- sessionId: SESS,
762
- summary: "user worked on the parser optimization",
763
- regionText: "the parser optimized the hot loop with aggressive inlining",
764
- timestamp: 1,
765
- });
766
- const r2 = s.add({
767
- sessionId: SESS,
768
- summary: "user worked on the parser optimization",
769
- regionText: "the parser optimized the hot loop with aggressive inlinings",
770
- timestamp: 2,
771
- });
772
-
773
- assert.equal(r1.deduped, false);
774
- // MARK_ONLY_L1 → L1 detects the near-dup but does NOT collapse
775
- assert.equal(r2.deduped, false, "MARK_ONLY_L1 → not collapsed");
776
- assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored (mark only)");
777
-
778
- // Both should be active (not removed)
779
- const all = vectorList(s,SESS);
780
- assert.ok(all.every((c) => c.dedupStatus === "active"), "both rows active under MARK_ONLY");
781
- });
782
-
783
- test("12c. L2_ENABLED=false — L2 skipped but L0/L1 still work", () => {
784
- const s = store({ L2_ENABLED: false });
785
- const SESS = "sess_l2_off";
786
-
787
- // L0 should still catch exact dups
788
- s.add({
789
- sessionId: SESS,
790
- summary: "auth module work",
791
- regionText: "the auth module validates the session token and refreshes it on each request",
792
- timestamp: 1,
793
- });
794
- const r2 = s.add({
795
- sessionId: SESS,
796
- summary: "auth module work",
797
- regionText: "the auth module validates the session token and refreshes it on each request",
798
- timestamp: 2,
799
- });
800
- assert.equal(r2.deduped, true, "L0 still catches exact dup with L2 off");
801
- assert.equal(r2.reason, "contentHash", "L0 contentHash still fires");
802
-
803
- // L1 should still catch near-dups
804
- const r3 = s.add({
805
- sessionId: SESS,
806
- summary: "auth module work variant",
807
- regionText: "the auth module validates the session tokens and refreshes them on each requests",
808
- timestamp: 3,
809
- });
810
- assert.equal(r3.deduped, true, "L1 still catches near-dup with L2 off");
811
- assert.equal(r3.reason, "l1MinHash", "L1 MinHash still fires");
812
- });
813
-
814
- test("12d. All tiers disabled — nothing deduped, everything stored", () => {
815
- const s = store({ L0_ENABLED: false, L1_ENABLED: false, L2_ENABLED: false });
816
- const SESS = "sess_all_off";
817
-
818
- // Use different regionText to avoid SQLite content_hash unique constraint
819
- const r1 = s.add({
820
- sessionId: SESS,
821
- summary: "alpha",
822
- regionText: "alpha region text about the compiler optimization pipeline first pass",
823
- timestamp: 1,
824
- });
825
- const r2 = s.add({
826
- sessionId: SESS,
827
- summary: "alpha",
828
- regionText: "alpha region text about the compiler optimization pipeline second pass",
829
- timestamp: 2,
830
- });
831
-
832
- assert.equal(r1.deduped, false);
833
- assert.equal(r2.deduped, false, "all tiers off → nothing deduped");
834
- assert.equal(vectorList(s,SESS).length, 2, "both stored");
835
- });
836
-
837
- // ---------------------------------------------------------------------------
838
- // Cleanup
839
- // ---------------------------------------------------------------------------
840
-
841
- test("E2E cleanup", () => {
842
- rmSync(baseTmp, { recursive: true, force: true });
843
- });