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
@@ -1,96 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { mkdtempSync, rmSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import { VectorStore } from "./vectorStore.js";
7
- import { compactSession } from "./engine.js";
8
- import { recallAndInline } from "./recall.js";
9
- import type { EngineMessage } from "./types.js";
10
-
11
- const baseTmp = mkdtempSync(join(tmpdir(), "mc-resume-"));
12
- let counter = 0;
13
- /** Two instances, SAME disk dir — simulates a fresh process / resumed session. */
14
- function storeForDir(dir: string) {
15
- return new VectorStore({ dedupSim: 0.9, stateDir: dir });
16
- }
17
- function msg(role: EngineMessage["role"], text: string, toolName?: string): EngineMessage {
18
- return toolName ? { role, text, toolName, input: text, output: text } : { role, text };
19
- }
20
- const SESS = "sess_resume";
21
-
22
- /**
23
- * Simulate the extension's `recentUserQuery`: the resume query is built from the
24
- * newest user message in the (re-loaded) session. We model "newest user msg"
25
- * directly rather than going through pi's session manager.
26
- */
27
- function latestUserQuery(messages: EngineMessage[]): string {
28
- for (let i = messages.length - 1; i >= 0; i--) {
29
- if (messages[i].role === "user") return messages[i].text;
30
- }
31
- return "";
32
- }
33
-
34
- test("resume contract: compact in one process, recall in a fresh one from disk", () => {
35
- const dir = join(baseTmp, `run-${counter++}`);
36
-
37
- // --- Process 1: the original session compacts ---
38
- const writer = storeForDir(dir);
39
- const session: EngineMessage[] = [
40
- msg("user", "investigated src/compact.ts and added a truncate helper"),
41
- msg("assistant", "added truncate", "Edit"),
42
- msg("user", "then wired it into the summary pipeline"),
43
- msg("assistant", "wired it in", "Edit"),
44
- ];
45
- const ran = compactSession(
46
- { sessionId: SESS, messages: session, keepFrom: session.length, timestamp: 1 },
47
- writer,
48
- );
49
- assert.equal(ran.skipped, false);
50
- assert.ok(ran.checkpointId, "a checkpoint was persisted");
51
-
52
- // --- Process 2: pi restarts, session resumes from disk ---
53
- const reader = storeForDir(dir); // brand new instance, same dir
54
- const resumeQuery = latestUserQuery(session); // newest user msg
55
- const r = recallAndInline(
56
- { sessionId: SESS, query: resumeQuery, limit: 3, source: "resume" },
57
- reader,
58
- );
59
-
60
- assert.equal(r.empty, false, "resume must re-surface the compacted context");
61
- assert.equal(r.toInject.length, 1);
62
- assert.equal(r.toInject[0].checkpoint.checkpointId, ran.checkpointId);
63
- assert.ok(r.block.includes("Recalled context"), "block is model-visible system-prompt text");
64
- });
65
-
66
- test("resume contract: nothing to recall for a brand-new session", () => {
67
- const dir = join(baseTmp, `run-${counter++}`);
68
- const fresh = storeForDir(dir);
69
- const r = recallAndInline(
70
- { sessionId: "sess_never_seen", query: "anything at all", limit: 3, source: "resume" },
71
- fresh,
72
- );
73
- assert.equal(r.empty, true);
74
- assert.equal(r.block, "");
75
- });
76
-
77
- test("resume contract: re-inject is deduped after the first recall", () => {
78
- const dir = join(baseTmp, `run-${counter++}`);
79
- const writer = storeForDir(dir);
80
- const session: EngineMessage[] = [
81
- msg("user", "built the trigram embedder for the vector store"),
82
- msg("assistant", "built it", "Edit"),
83
- ];
84
- compactSession({ sessionId: SESS, messages: session, keepFrom: 2, timestamp: 1 }, writer);
85
-
86
- const reader = storeForDir(dir);
87
- const q = latestUserQuery(session);
88
- const first = recallAndInline({ sessionId: SESS, query: q, limit: 3, source: "resume" }, reader);
89
- const second = recallAndInline({ sessionId: SESS, query: q, limit: 3, source: "resume" }, reader);
90
- assert.equal(first.empty, false);
91
- assert.equal(second.empty, true, "second resume does not re-inject (sentinel)");
92
- });
93
-
94
- test("cleanup", () => {
95
- rmSync(baseTmp, { recursive: true, force: true });
96
- });
@@ -1,467 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { mkdtempSync, rmSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import { VectorStore } from "./vectorStore.js";
7
- import { compactSession } from "./engine.js";
8
- import { recallAndInline, recallAndInlineAsync, formatRecallBlock } from "./recall.js";
9
- import { vectorList, vectorWasInjected } from "./vectorStore.js";
10
- import { markInjectedGlobal, wasInjectedGlobal, closeIndexStore } from "./store/sqlite.js";
11
- import {
12
- closeVectorIndex,
13
- initVectorIndex,
14
- rebuildFromSqlite,
15
- } from "./store/vectorIndex.js";
16
- import type { EngineMessage } from "./types.js";
17
-
18
- const baseTmp = mkdtempSync(join(tmpdir(), "mc-recall-"));
19
- let counter = 0;
20
- function store() {
21
- return new VectorStore({ dedupSim: 0.9, stateDir: join(baseTmp, `run-${counter++}`) });
22
- }
23
- function msg(role: EngineMessage["role"], text: string, toolName?: string): EngineMessage {
24
- return toolName ? { role, text, toolName, input: text, output: text } : { role, text };
25
- }
26
- const SESS = "sess_recall";
27
-
28
- test("recallAndInline injects new hits and marks them injected", () => {
29
- const s = store();
30
- compactSession({ sessionId: SESS, messages: [msg("user", "investigated src/vectorStore.ts embedding"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
31
- compactSession({ sessionId: SESS, messages: [msg("user", "fixed the dedupe race in store.ts"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 2 }, s);
32
-
33
- const r1 = recallAndInline({ sessionId: SESS, query: "vectorStore embedding", limit: 1, source: "command" }, s as any);
34
- assert.equal(r1.empty, false);
35
- assert.equal(r1.toInject.length, 1);
36
- assert.ok(r1.block.includes("Recalled context"));
37
-
38
- // Second call with the same query must NOT re-inject (shared dedup).
39
- const r2 = recallAndInline({ sessionId: SESS, query: "vectorStore embedding", limit: 1, source: "command" }, s as any);
40
- assert.equal(r2.empty, true);
41
- assert.equal(r2.toInject.length, 0);
42
- });
43
-
44
- test("recallAndInline skipInjected=false re-returns hits", () => {
45
- const s = store();
46
- compactSession({ sessionId: SESS, messages: [msg("user", "configured the fast gate threshold"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
47
- const r1 = recallAndInline({ sessionId: SESS, query: "fast gate threshold", limit: 5, source: "resume" }, s as any);
48
- const r2 = recallAndInline({ sessionId: SESS, query: "fast gate threshold", limit: 5, source: "resume", skipInjected: false }, s as any);
49
- assert.equal(r1.toInject.length, 1);
50
- assert.equal(r2.toInject.length, 1);
51
- });
52
-
53
- test("formatRecallBlock is empty for no hits", () => {
54
- assert.equal(formatRecallBlock([]), "");
55
- });
56
-
57
- test("formatRecallBlock (S17): labels a cross-repo hit with its source repo", () => {
58
- const hit = {
59
- checkpoint: { checkpointId: "chkpt_x", summary: "did thing Y", filesModified: ["a.ts"] },
60
- score: 0.91,
61
- repoId: "/home/u/rad-gateway",
62
- } as any;
63
- const block = formatRecallBlock([hit]);
64
- assert.ok(block.includes("from repo"), "labels cross-repo source");
65
- assert.ok(block.includes("rad-gateway"), "includes the repo display name");
66
- });
67
-
68
- test("formatRecallBlock (S17): omits the label for same-repo hits (no repoId)", () => {
69
- const hit = {
70
- checkpoint: { checkpointId: "c1", summary: "s", filesModified: [] },
71
- score: 0.9,
72
- } as any;
73
- const block = formatRecallBlock([hit]);
74
- assert.ok(!block.includes("from repo"), "no source label for same-repo hits");
75
- });
76
-
77
- test("recallAndInline empty when store has nothing for query", () => {
78
- const s = store();
79
- const r = recallAndInline({ sessionId: SESS, query: "no such topic exists here", limit: 5, source: "command" }, s as any);
80
- assert.equal(r.empty, true);
81
- assert.equal(r.block, "");
82
- });
83
-
84
- test("Fix C: recallMaxTokens caps the injected block", () => {
85
- const s = store();
86
- // Three distinct checkpoints so we can observe the cap bite mid-stream.
87
- compactSession({ sessionId: SESS, messages: [msg("user", "alpha module wiring and bootstrap sequence"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
88
- compactSession({ sessionId: SESS, messages: [msg("user", "beta module config and env resolution"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 2 }, s);
89
- compactSession({ sessionId: SESS, messages: [msg("user", "gamma module shutdown and cleanup hooks"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 3 }, s);
90
-
91
- // A ceiling of 50 tokens fits the first checkpoint (~33 body tokens) but
92
- // stops before the second (~65 cumulative) — proving the cap bites mid-stream.
93
- // (F3: the cap now counts body tokens only — one preamble at format time, not
94
- // N — matching the async path. The old per-hit preamble counting needed ~100.)
95
- const r = recallAndInline(
96
- { sessionId: SESS, query: "module wiring config shutdown", limit: 5, source: "command", recallMaxTokens: 50, skipInjected: false },
97
- s as any,
98
- );
99
- assert.ok(r.toInject.length >= 1, "at least one injected under the cap");
100
- assert.ok(r.toInject.length < 3, "cap prevented all three from injecting");
101
- assert.ok(r.block.length > 0, "block non-empty");
102
- });
103
-
104
- test("Fix C: inline dedupe drops a hit already resident in the live window", () => {
105
- const s = store();
106
- const resident = "alpha module wiring and bootstrap sequence";
107
- compactSession({ sessionId: SESS, messages: [msg("user", resident), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
108
- compactSession({ sessionId: SESS, messages: [msg("user", "omega module telemetry and tracing spans"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 2 }, s);
109
-
110
- // Baseline: with dedupe OFF, both checkpoints are candidates.
111
- const rNoDedup = recallAndInline(
112
- { sessionId: SESS, query: "module wiring telemetry", limit: 5, source: "command", skipInjected: false },
113
- s as any,
114
- );
115
- // The live window contains the exact summary of the first checkpoint — as it
116
- // would be if a prior recall already injected it. Inline dedupe must drop it
117
- // (strictly fewer injected than the no-dedupe baseline).
118
- const residentSummary = rNoDedup.toInject[0].checkpoint.summary;
119
- const rDedup = recallAndInline(
120
- { sessionId: SESS, query: "module wiring telemetry", limit: 5, source: "command", skipInjected: false, windowDedupe: true, liveWindow: [residentSummary], dedupSim: 0.9 },
121
- s as any,
122
- );
123
- assert.ok(rDedup.toInject.length <= rNoDedup.toInject.length, "dedupe never adds hits");
124
- assert.ok(
125
- rDedup.toInject.length < rNoDedup.toInject.length,
126
- "inline dedupe dropped a resident hit",
127
- );
128
- });
129
-
130
- // ---- S18 cross-repo global injected-set (real-data, no mocks) ----------------
131
- //
132
- // Earlier versions of these tests used `as any` mock stores with canned
133
- // `searchAsync` returns. That was mock data: it asserted recall's orchestration
134
- // against a fake search result, not the real embed → HNSW → hydrate → inject
135
- // path. Per the no-mock-data principle, both tests now seed a REAL foreign
136
- // VectorStore (real checkpoint + real TrigramEmbedder embedding persisted to
137
- // SQLite), rebuild the real PGlite index from it via `rebuildFromSqlite`, and
138
- // run the full `recallAndInlineAsync` cross-repo path against a separate self
139
- // store. The foreign checkpoint hydrates from the foreign store via its real
140
- // repoId (== stateDir, per VectorStore's repoId convention).
141
-
142
- async function seedForeignRepo(foreignStateDir: string): Promise<{ sessionId: string; checkpointId: string; summary: string }> {
143
- // A real VectorStore at the foreign repo's stateDir. repoId == stateDir.
144
- const foreign = new VectorStore({ stateDir: foreignStateDir, dedupSim: 0.9 });
145
- const sess = "sess_foreign";
146
- // Seed a real checkpoint via the real compactSession pipeline. The summary
147
- // text determines the embedding; the query below must land near it.
148
- const summary = "foreign repo authentication jwt token validation";
149
- const result = compactSession(
150
- {
151
- sessionId: sess,
152
- messages: [msg("user", summary), msg("assistant", "ok", "Edit")],
153
- keepFrom: 2,
154
- timestamp: 1,
155
- },
156
- foreign,
157
- );
158
- return { sessionId: sess, checkpointId: result.checkpointId ?? "", summary };
159
- }
160
-
161
- test("S18: global injected-set skips a foreign checkpoint already injected machine-wide", async () => {
162
- if (process.env.MEGACOMPACT_PGLITE_DISABLED === "true") { return; } // skip when WASM index is off
163
- const indexDir = mkdtempSync(join(tmpdir(), "mc-gi-"));
164
- const foreignStateDir = mkdtempSync(join(tmpdir(), "mc-gi-foreign-"));
165
- const selfStateDir = mkdtempSync(join(tmpdir(), "mc-gi-self-"));
166
- process.env.MEGACOMPACT_VECTOR_INDEX_DIR = mkdtempSync(join(tmpdir(), "mc-gi-vidx-"));
167
- try {
168
- await closeVectorIndex(); // fresh singleton per this index dir
169
- const seed = await seedForeignRepo(foreignStateDir);
170
- // Populate the real PGlite index from the foreign store's real checkpoints.
171
- await rebuildFromSqlite(
172
- () => [{ repoId: foreignStateDir, stateDir: foreignStateDir }],
173
- (sd) => {
174
- // readCheckpoints: yield (sessionId, checkpointId, embedding) for every
175
- // real checkpoint in this store. Mirrors the production enumerator.
176
- const store = new VectorStore({ stateDir: sd, dedupSim: 0.9 });
177
- return vectorList(store, seed.sessionId).map((cp) => ({
178
- sessionId: seed.sessionId,
179
- checkpointId: cp.checkpointId,
180
- embedding: cp.embedding,
181
- }));
182
- },
183
- );
184
- const pg = await initVectorIndex();
185
- assert.ok(pg, "PGlite index should initialize (WASM available)");
186
-
187
- const sess = "sess_cross";
188
- // Pre-mark the foreign checkpoint as already injected machine-wide.
189
- markInjectedGlobal(seed.checkpointId, foreignStateDir, sess, indexDir);
190
- assert.equal(wasInjectedGlobal(seed.checkpointId, sess, indexDir), true);
191
-
192
- // Self store (different repo) — the cross-repo query runs against the index.
193
- const selfStore = new VectorStore({ stateDir: selfStateDir, dedupSim: 0.9 });
194
- const r = await recallAndInlineAsync(
195
- { sessionId: sess, query: "foreign repo authentication jwt", limit: 3, source: "command", crossRepo: true, globalIndexDir: indexDir },
196
- selfStore,
197
- );
198
- assert.equal(r.toInject.length, 0, "globally-injected foreign checkpoint skipped");
199
- } finally {
200
- await closeVectorIndex();
201
- closeIndexStore();
202
- delete process.env.MEGACOMPACT_VECTOR_INDEX_DIR;
203
- rmSync(indexDir, { recursive: true, force: true });
204
- rmSync(foreignStateDir, { recursive: true, force: true });
205
- rmSync(selfStateDir, { recursive: true, force: true });
206
- }
207
- });
208
-
209
- test("S18: a fresh foreign checkpoint is injected AND recorded globally", async () => {
210
- if (process.env.MEGACOMPACT_PGLITE_DISABLED === "true") { return; } // skip when WASM index is off
211
- const indexDir = mkdtempSync(join(tmpdir(), "mc-gi2-"));
212
- const foreignStateDir = mkdtempSync(join(tmpdir(), "mc-gi2-foreign-"));
213
- const selfStateDir = mkdtempSync(join(tmpdir(), "mc-gi2-self-"));
214
- process.env.MEGACOMPACT_VECTOR_INDEX_DIR = mkdtempSync(join(tmpdir(), "mc-gi2-vidx-"));
215
- try {
216
- await closeVectorIndex(); // fresh singleton per this index dir
217
- const seed = await seedForeignRepo(foreignStateDir);
218
- assert.equal(wasInjectedGlobal(seed.checkpointId, "sess_fresh", indexDir), false);
219
- await rebuildFromSqlite(
220
- () => [{ repoId: foreignStateDir, stateDir: foreignStateDir }],
221
- (sd) => {
222
- const store = new VectorStore({ stateDir: sd, dedupSim: 0.9 });
223
- return vectorList(store, seed.sessionId).map((cp) => ({
224
- sessionId: seed.sessionId,
225
- checkpointId: cp.checkpointId,
226
- embedding: cp.embedding,
227
- }));
228
- },
229
- );
230
- const pg = await initVectorIndex();
231
- assert.ok(pg, "PGlite index should initialize (WASM available)");
232
-
233
- const sess = "sess_fresh";
234
- const selfStore = new VectorStore({ stateDir: selfStateDir, dedupSim: 0.9 });
235
- const r = await recallAndInlineAsync(
236
- { sessionId: sess, query: "foreign repo authentication jwt", limit: 3, source: "command", crossRepo: true, globalIndexDir: indexDir },
237
- selfStore,
238
- );
239
- assert.equal(r.toInject.length, 1, "fresh foreign checkpoint injected");
240
- assert.equal(wasInjectedGlobal(seed.checkpointId, sess, indexDir), true, "recorded machine-wide");
241
- } finally {
242
- await closeVectorIndex();
243
- closeIndexStore();
244
- delete process.env.MEGACOMPACT_VECTOR_INDEX_DIR;
245
- rmSync(indexDir, { recursive: true, force: true });
246
- rmSync(foreignStateDir, { recursive: true, force: true });
247
- rmSync(selfStateDir, { recursive: true, force: true });
248
- }
249
- });
250
-
251
- // ---- F3: format-once — one preamble, [1..n] numbering, cap respected ----
252
- //
253
- // The fix (landed async, extended to sync here) accumulates the hit list and
254
- // calls formatRecallBlock ONCE at the end, so a multi-hit block carries exactly
255
- // one preamble and contiguous [1..n] labels instead of one preamble per hit.
256
-
257
- test("F3 (sync): multi-hit injection has exactly one preamble and [1..n] numbering", () => {
258
- const s = store();
259
- compactSession({ sessionId: SESS, messages: [msg("user", "alpha module wiring and bootstrap sequence"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
260
- compactSession({ sessionId: SESS, messages: [msg("user", "beta module config and env resolution"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 2 }, s);
261
- compactSession({ sessionId: SESS, messages: [msg("user", "gamma module shutdown and cleanup hooks"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 3 }, s);
262
-
263
- const r = recallAndInline(
264
- { sessionId: SESS, query: "module wiring config shutdown", limit: 5, source: "command", skipInjected: false },
265
- s as any,
266
- );
267
- assert.ok(r.toInject.length >= 2, "at least two hits injected (got " + r.toInject.length + ")");
268
- // Exactly one preamble (the old per-hit format produced one per hit).
269
- const preamble = "The following compacted context was recalled";
270
- assert.equal(r.block.split(preamble).length - 1, 1, "exactly one preamble for a multi-hit block");
271
- // Contiguous [1..n] numbering.
272
- for (let i = 1; i <= r.toInject.length; i++) {
273
- assert.ok(r.block.includes(`[${i}]`), `block includes [${i}]`);
274
- }
275
- // No out-of-range label (e.g. [n+1]) leaks in.
276
- assert.ok(!r.block.includes(`[${r.toInject.length + 1}]`), "no extra-numbered label");
277
- });
278
-
279
- test("F3 (sync): recallMaxTokens caps mid-stream with exactly one preamble", () => {
280
- const s = store();
281
- compactSession({ sessionId: SESS, messages: [msg("user", "alpha module wiring and bootstrap sequence"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
282
- compactSession({ sessionId: SESS, messages: [msg("user", "beta module config and env resolution"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 2 }, s);
283
- compactSession({ sessionId: SESS, messages: [msg("user", "gamma module shutdown and cleanup hooks"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 3 }, s);
284
-
285
- // A tight ceiling that fits the first checkpoint (~33 body tokens) but stops
286
- // before the second (~65 cumulative). F3: body-only counting (one preamble at
287
- // format time, not per hit), matching the async path.
288
- const r = recallAndInline(
289
- { sessionId: SESS, query: "module wiring config shutdown", limit: 5, source: "command", skipInjected: false, recallMaxTokens: 50 },
290
- s as any,
291
- );
292
- assert.ok(r.toInject.length < 3, "cap stopped before all three injected");
293
- assert.ok(r.toInject.length >= 1, "at least one injected under the cap");
294
- assert.equal(
295
- r.block.split("The following compacted context was recalled").length - 1,
296
- 1,
297
- "still exactly one preamble under the cap",
298
- );
299
- });
300
-
301
- test("F3 (async): multi-hit cross-repo injection has exactly one preamble and [1..n] numbering", async () => {
302
- if (process.env.MEGACOMPACT_PGLITE_DISABLED === "true") { return; } // skip when WASM index is off
303
- const indexDir = mkdtempSync(join(tmpdir(), "mc-f3a-"));
304
- const foreignStateDir = mkdtempSync(join(tmpdir(), "mc-f3a-foreign-"));
305
- const selfStateDir = mkdtempSync(join(tmpdir(), "mc-f3a-self-"));
306
- process.env.MEGACOMPACT_VECTOR_INDEX_DIR = mkdtempSync(join(tmpdir(), "mc-f3a-vidx-"));
307
- try {
308
- await closeVectorIndex();
309
- // Seed multiple distinct foreign checkpoints so the cross-repo query yields
310
- // more than one hit (needed to assert [1..n] numbering).
311
- const foreign = new VectorStore({ stateDir: foreignStateDir, dedupSim: 0.9 });
312
- const fsess = "sess_foreign_f3";
313
- const summaries = [
314
- "foreign repo authentication jwt token validation",
315
- "foreign repo database connection pooling and retry",
316
- "foreign repo logging telemetry and tracing spans",
317
- ];
318
- const cids: string[] = [];
319
- for (let i = 0; i < summaries.length; i++) {
320
- const res = compactSession(
321
- { sessionId: fsess, messages: [msg("user", summaries[i]), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: i + 1 },
322
- foreign,
323
- );
324
- if (res.checkpointId) cids.push(res.checkpointId);
325
- }
326
- await rebuildFromSqlite(
327
- () => [{ repoId: foreignStateDir, stateDir: foreignStateDir }],
328
- (sd) => {
329
- const st = new VectorStore({ stateDir: sd, dedupSim: 0.9 });
330
- return vectorList(st, fsess).map((cp) => ({
331
- sessionId: fsess, checkpointId: cp.checkpointId, embedding: cp.embedding,
332
- }));
333
- },
334
- );
335
- const pg = await initVectorIndex();
336
- assert.ok(pg, "PGlite index should initialize");
337
-
338
- const selfStore = new VectorStore({ stateDir: selfStateDir, dedupSim: 0.9 });
339
- const r = await recallAndInlineAsync(
340
- { sessionId: "sess_f3", query: "foreign repo", limit: 5, source: "command", crossRepo: true, skipInjected: false, globalIndexDir: indexDir },
341
- selfStore,
342
- );
343
- assert.ok(r.toInject.length >= 2, "at least two cross-repo hits (got " + r.toInject.length + ")");
344
- assert.equal(
345
- r.block.split("The following compacted context was recalled").length - 1,
346
- 1,
347
- "exactly one preamble for a multi-hit async block",
348
- );
349
- for (let i = 1; i <= r.toInject.length; i++) {
350
- assert.ok(r.block.includes(`[${i}]`), `async block includes [${i}]`);
351
- }
352
- assert.ok(!r.block.includes(`[${r.toInject.length + 1}]`), "no extra-numbered label (async)");
353
- } finally {
354
- await closeVectorIndex();
355
- closeIndexStore();
356
- delete process.env.MEGACOMPACT_VECTOR_INDEX_DIR;
357
- rmSync(indexDir, { recursive: true, force: true });
358
- rmSync(foreignStateDir, { recursive: true, force: true });
359
- rmSync(selfStateDir, { recursive: true, force: true });
360
- }
361
- });
362
-
363
- // ---- F2: cross-repo injected-set bypass ----------------------------------
364
- //
365
- // When the same session resumes in a DIFFERENT repo, the per-repo session_state
366
- // has no injection marker for the foreign checkpoint (different stateDir), so
367
- // only the machine-wide injected-set (shared globalIndexDir) can block a
368
- // re-inject. The F2 fix ensures globalIndexDir is always resolved (via
369
- // getIndexDir in the extension), so wasInjectedGlobal is consulted; without
370
- // the fix, globalIndexDir was undefined and foreign checkpoints re-injected
371
- // every resume. This test passes globalIndexDir explicitly (simulating the
372
- // resolver default) and does NOT set MEGACOMPACT_INDEX_DIR.
373
-
374
- test("F2: cross-repo hit is not re-injected when resuming the same session in a different repo (shared index dir)", async () => {
375
- if (process.env.MEGACOMPACT_PGLITE_DISABLED === "true") { return; } // skip when WASM index is off
376
- const indexDir = mkdtempSync(join(tmpdir(), "mc-f2-"));
377
- const foreignStateDir = mkdtempSync(join(tmpdir(), "mc-f2-foreign-"));
378
- const selfStateDir1 = mkdtempSync(join(tmpdir(), "mc-f2-self1-"));
379
- const selfStateDir2 = mkdtempSync(join(tmpdir(), "mc-f2-self2-"));
380
- process.env.MEGACOMPACT_VECTOR_INDEX_DIR = mkdtempSync(join(tmpdir(), "mc-f2-vidx-"));
381
- try {
382
- await closeVectorIndex();
383
- const seed = await seedForeignRepo(foreignStateDir);
384
- await rebuildFromSqlite(
385
- () => [{ repoId: foreignStateDir, stateDir: foreignStateDir }],
386
- (sd) => {
387
- const st = new VectorStore({ stateDir: sd, dedupSim: 0.9 });
388
- return vectorList(st, seed.sessionId).map((cp) => ({
389
- sessionId: seed.sessionId, checkpointId: cp.checkpointId, embedding: cp.embedding,
390
- }));
391
- },
392
- );
393
- const pg = await initVectorIndex();
394
- assert.ok(pg, "PGlite index should initialize");
395
-
396
- const sess = "sess_resume";
397
- // Repo B: first recall — fresh foreign checkpoint is injected AND recorded
398
- // in the shared machine-wide index.
399
- const storeB = new VectorStore({ stateDir: selfStateDir1, dedupSim: 0.9 });
400
- const r1 = await recallAndInlineAsync(
401
- { sessionId: sess, query: "foreign repo authentication jwt", limit: 3, source: "command", crossRepo: true, globalIndexDir: indexDir },
402
- storeB,
403
- );
404
- assert.equal(r1.toInject.length, 1, "first recall (repo B) injects the foreign checkpoint");
405
- assert.equal(wasInjectedGlobal(seed.checkpointId, sess, indexDir), true, "recorded in the shared index");
406
-
407
- // Repo C: SAME session resumed in a DIFFERENT repo. The per-session injected
408
- // set in repo C's store is empty (different stateDir), so only the shared
409
- // global injected-set can block re-injection.
410
- const storeC = new VectorStore({ stateDir: selfStateDir2, dedupSim: 0.9 });
411
- assert.equal(vectorWasInjected(storeC, sess, seed.checkpointId), false, "repo C per-session set is empty (different stateDir)");
412
- const r2 = await recallAndInlineAsync(
413
- { sessionId: sess, query: "foreign repo authentication jwt", limit: 3, source: "command", crossRepo: true, globalIndexDir: indexDir },
414
- storeC,
415
- );
416
- assert.equal(r2.toInject.length, 0, "second recall (repo C) does NOT re-inject (machine-wide global dedup)");
417
- } finally {
418
- await closeVectorIndex();
419
- closeIndexStore();
420
- delete process.env.MEGACOMPACT_VECTOR_INDEX_DIR;
421
- rmSync(indexDir, { recursive: true, force: true });
422
- rmSync(foreignStateDir, { recursive: true, force: true });
423
- rmSync(selfStateDir1, { recursive: true, force: true });
424
- rmSync(selfStateDir2, { recursive: true, force: true });
425
- }
426
- });
427
-
428
- test("F2: without globalIndexDir, cross-repo hits are skipped (not injected undeduped)", async () => {
429
- if (process.env.MEGACOMPACT_PGLITE_DISABLED === "true") { return; } // skip when WASM index is off
430
- const foreignStateDir = mkdtempSync(join(tmpdir(), "mc-f2b-foreign-"));
431
- const selfStateDir = mkdtempSync(join(tmpdir(), "mc-f2b-self-"));
432
- process.env.MEGACOMPACT_VECTOR_INDEX_DIR = mkdtempSync(join(tmpdir(), "mc-f2b-vidx-"));
433
- try {
434
- await closeVectorIndex();
435
- const seed = await seedForeignRepo(foreignStateDir);
436
- await rebuildFromSqlite(
437
- () => [{ repoId: foreignStateDir, stateDir: foreignStateDir }],
438
- (sd) => {
439
- const st = new VectorStore({ stateDir: sd, dedupSim: 0.9 });
440
- return vectorList(st, seed.sessionId).map((cp) => ({
441
- sessionId: seed.sessionId, checkpointId: cp.checkpointId, embedding: cp.embedding,
442
- }));
443
- },
444
- );
445
- const pg = await initVectorIndex();
446
- assert.ok(pg, "PGlite index should initialize");
447
-
448
- // No globalIndexDir → the F2 guard skips foreign hits rather than injecting
449
- // them undeduped. Same-repo hits (none here) would still pass.
450
- const selfStore = new VectorStore({ stateDir: selfStateDir, dedupSim: 0.9 });
451
- const r = await recallAndInlineAsync(
452
- { sessionId: "sess_nodir", query: "foreign repo authentication jwt", limit: 3, source: "command", crossRepo: true },
453
- selfStore,
454
- );
455
- assert.equal(r.toInject.length, 0, "foreign hit skipped when globalIndexDir is unset (no undeduped injection)");
456
- } finally {
457
- await closeVectorIndex();
458
- closeIndexStore();
459
- delete process.env.MEGACOMPACT_VECTOR_INDEX_DIR;
460
- rmSync(foreignStateDir, { recursive: true, force: true });
461
- rmSync(selfStateDir, { recursive: true, force: true });
462
- }
463
- });
464
-
465
- test("cleanup", () => {
466
- rmSync(baseTmp, { recursive: true, force: true });
467
- });