@remnic/core 10.0.83 → 10.0.85

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 (413) hide show
  1. package/dist/access-admin-ops-surface.js +67 -66
  2. package/dist/access-authorization-probe.js +68 -67
  3. package/dist/access-boundary.js +67 -66
  4. package/dist/access-cli.js +148 -146
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +67 -66
  7. package/dist/access-http-briefing.js +68 -67
  8. package/dist/access-http-query.js +69 -68
  9. package/dist/access-http.js +91 -89
  10. package/dist/access-identity-continuity-surface.js +67 -66
  11. package/dist/access-lcm-surface.js +67 -66
  12. package/dist/access-mcp-frames.d.ts +29 -0
  13. package/dist/access-mcp-frames.d.ts.map +1 -0
  14. package/dist/access-mcp-frames.js +10 -0
  15. package/dist/access-mcp.d.ts +1 -1
  16. package/dist/access-mcp.d.ts.map +1 -1
  17. package/dist/access-mcp.js +82 -80
  18. package/dist/access-namespace-preflight.js +67 -66
  19. package/dist/access-observe-write-category.js +9 -9
  20. package/dist/access-observe-write-surface.d.ts.map +1 -1
  21. package/dist/access-observe-write-surface.js +67 -66
  22. package/dist/access-offline-manifest.js +3 -3
  23. package/dist/access-operations-batch.js +71 -70
  24. package/dist/access-operations.d.ts +1 -1
  25. package/dist/access-operations.js +78 -77
  26. package/dist/access-recall-concurrency.js +67 -66
  27. package/dist/access-recall-response.js +67 -66
  28. package/dist/access-recall-surface.js +67 -66
  29. package/dist/access-schema.js +4 -4
  30. package/dist/access-service.js +67 -66
  31. package/dist/access-surface-catalog.js +69 -68
  32. package/dist/access-surface-catalog.js.map +1 -1
  33. package/dist/active-recall.js +31 -31
  34. package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
  35. package/dist/briefing.js +25 -25
  36. package/dist/buffer-persistence.d.ts +8 -4
  37. package/dist/buffer-persistence.d.ts.map +1 -1
  38. package/dist/buffer-persistence.js +1 -1
  39. package/dist/buffer-turn-helpers.d.ts +5 -0
  40. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  41. package/dist/buffer-turn-helpers.js +1 -1
  42. package/dist/buffer.d.ts +4 -1
  43. package/dist/buffer.d.ts.map +1 -1
  44. package/dist/buffer.js +9 -9
  45. package/dist/calibration.js +2 -2
  46. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  47. package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
  48. package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
  49. package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
  50. package/dist/causal-behavior.js +2 -2
  51. package/dist/causal-chain.js +2 -2
  52. package/dist/causal-consolidation.js +32 -32
  53. package/dist/causal-retrieval.js +2 -2
  54. package/dist/causal-trajectory-graph.js +2 -2
  55. package/dist/causal-trajectory.js +1 -1
  56. package/dist/{chunk-T6T2RMWW.js → chunk-23B4HLWG.js} +5 -5
  57. package/dist/{chunk-LECSS7EE.js → chunk-2ASEDFOA.js} +2 -2
  58. package/dist/{chunk-TPX7XXY4.js → chunk-2JAT4S7N.js} +2 -2
  59. package/dist/{chunk-XRBHIR7Q.js → chunk-2URKYLMK.js} +2 -2
  60. package/dist/{chunk-IN4OFAHX.js → chunk-2XYDL2FG.js} +2 -2
  61. package/dist/{chunk-HQGATKG2.js → chunk-3KGCHTTK.js} +2 -2
  62. package/dist/{chunk-YAUHSNPB.js → chunk-3LHPTBWG.js} +2 -2
  63. package/dist/{chunk-7PMOCQOL.js → chunk-3ODZLCHC.js} +192 -63
  64. package/dist/chunk-3ODZLCHC.js.map +1 -0
  65. package/dist/{chunk-XT45WCKX.js → chunk-3VU2FDTU.js} +8 -8
  66. package/dist/{chunk-CIIHIAJ2.js → chunk-433EXTC4.js} +3 -3
  67. package/dist/{chunk-QZSIDSB2.js → chunk-44FQCC5D.js} +2 -2
  68. package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
  69. package/dist/{chunk-54FDG7K7.js → chunk-4DPLCYEK.js} +2 -2
  70. package/dist/{chunk-KO3YBHOV.js → chunk-4IWEPQEV.js} +2 -2
  71. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  72. package/dist/{chunk-K6RTQZA4.js → chunk-6DANGCMO.js} +2 -2
  73. package/dist/{chunk-2IW5PT7U.js → chunk-6KFFRMPQ.js} +2 -2
  74. package/dist/{chunk-MSIRVXQ3.js → chunk-73YVMZ7D.js} +4 -4
  75. package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
  76. package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
  77. package/dist/{chunk-YOEYL6CH.js → chunk-AP3TSV6S.js} +2 -2
  78. package/dist/{chunk-GQZSGXR6.js → chunk-BATPQNRG.js} +2 -2
  79. package/dist/{chunk-D5SSBCYY.js → chunk-BLGQUZFB.js} +2 -2
  80. package/dist/{chunk-BDN3TDGV.js → chunk-BLMTOIBK.js} +2 -2
  81. package/dist/{chunk-B74NPUHT.js → chunk-BMIM6ESC.js} +43 -43
  82. package/dist/chunk-BMIM6ESC.js.map +1 -0
  83. package/dist/{chunk-R45L4TV6.js → chunk-BUKK5SWA.js} +6 -6
  84. package/dist/{chunk-TFLPDNQV.js → chunk-CCU7URRP.js} +2 -2
  85. package/dist/{chunk-2XJZLSXE.js → chunk-CMRHXPLQ.js} +20 -20
  86. package/dist/{chunk-BK4FBHZ3.js → chunk-D6THW7PA.js} +3 -3
  87. package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
  88. package/dist/{chunk-VKDAJJQP.js → chunk-DI5ET2IE.js} +5 -5
  89. package/dist/{chunk-QZBTIYKD.js → chunk-DM4RRTN7.js} +3 -3
  90. package/dist/{chunk-S57QBPTA.js → chunk-DPPLNZ3A.js} +4 -6
  91. package/dist/chunk-DPPLNZ3A.js.map +1 -0
  92. package/dist/{chunk-EBOHFZI4.js → chunk-E5QEF3JO.js} +2 -2
  93. package/dist/{chunk-DD6OIFDG.js → chunk-E7V5AK7I.js} +3 -3
  94. package/dist/{chunk-WAD32CZG.js → chunk-ELWI2R6H.js} +26 -13
  95. package/dist/chunk-ELWI2R6H.js.map +1 -0
  96. package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
  97. package/dist/{chunk-HPFVZPRH.js → chunk-FK4WC6VK.js} +3 -3
  98. package/dist/{chunk-VLMBSGQF.js → chunk-FRM4UTDR.js} +6 -6
  99. package/dist/{chunk-LTDLJCMQ.js → chunk-FZPDRCOB.js} +2 -2
  100. package/dist/{chunk-ZJJFUEPO.js → chunk-GBOYOL5L.js} +2 -2
  101. package/dist/{chunk-YUTG35M4.js → chunk-GJVGFKOU.js} +2 -2
  102. package/dist/{chunk-77R7BQZ2.js → chunk-GKYIRTNX.js} +33 -11
  103. package/dist/{chunk-77R7BQZ2.js.map → chunk-GKYIRTNX.js.map} +1 -1
  104. package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
  105. package/dist/{chunk-HWHNQVRV.js → chunk-GYNRNKMW.js} +2 -2
  106. package/dist/{chunk-LU66I56Q.js → chunk-H34INI3P.js} +29 -3
  107. package/dist/chunk-H34INI3P.js.map +1 -0
  108. package/dist/{chunk-AYTDCMEL.js → chunk-H4D5YURJ.js} +80 -94
  109. package/dist/chunk-H4D5YURJ.js.map +1 -0
  110. package/dist/{chunk-6LCWKS62.js → chunk-HVFX2N2G.js} +60 -59
  111. package/dist/chunk-HVFX2N2G.js.map +1 -0
  112. package/dist/chunk-IGLJNHOX.js +62 -0
  113. package/dist/chunk-IGLJNHOX.js.map +1 -0
  114. package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
  115. package/dist/{chunk-YK7GOPIE.js → chunk-JFH6JAGY.js} +6 -4
  116. package/dist/chunk-JFH6JAGY.js.map +1 -0
  117. package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
  118. package/dist/{chunk-FJ2VJTGA.js → chunk-KHZVAISH.js} +2 -2
  119. package/dist/{chunk-WHQXWFH7.js → chunk-LT3CMO65.js} +2 -2
  120. package/dist/{chunk-NUSAAN75.js → chunk-MHSBOEH7.js} +2 -2
  121. package/dist/{chunk-KJCL2FZO.js → chunk-MNYQVYJ4.js} +2 -2
  122. package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
  123. package/dist/{chunk-FVRTCYZ3.js → chunk-MVOJDLXS.js} +2 -2
  124. package/dist/{chunk-KESJ5XX3.js → chunk-MYCCO6AA.js} +2 -2
  125. package/dist/{chunk-66JOLMWS.js → chunk-MZCGHPIJ.js} +2 -2
  126. package/dist/{chunk-B4ZU34JQ.js → chunk-N6II66E5.js} +3 -3
  127. package/dist/{chunk-BK7AJUWB.js → chunk-NIV7RPWQ.js} +6 -6
  128. package/dist/{chunk-TLHF75OR.js → chunk-O55AGDTI.js} +10 -10
  129. package/dist/{chunk-GGJFRVRD.js → chunk-OMYRPMQC.js} +2 -2
  130. package/dist/{chunk-SVCHO337.js → chunk-ONN2E5QD.js} +32 -23
  131. package/dist/chunk-ONN2E5QD.js.map +1 -0
  132. package/dist/{chunk-XHGYZRVW.js → chunk-OSTFDWZJ.js} +4 -4
  133. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  134. package/dist/{chunk-WC475JOG.js → chunk-P225W4RQ.js} +6 -6
  135. package/dist/{chunk-TFA6HPOA.js → chunk-QCP75JGO.js} +5 -5
  136. package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
  137. package/dist/chunk-QV2VZKD5.js +31 -0
  138. package/dist/chunk-QV2VZKD5.js.map +1 -0
  139. package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
  140. package/dist/{chunk-VUROWXFL.js → chunk-RNGDKFY2.js} +2 -2
  141. package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
  142. package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
  143. package/dist/{chunk-XYP5WBDO.js → chunk-SGHV6LZ7.js} +13 -13
  144. package/dist/chunk-SGHV6LZ7.js.map +1 -0
  145. package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
  146. package/dist/{chunk-T77AX6DG.js → chunk-TQVECGUQ.js} +4 -4
  147. package/dist/{chunk-OQSSQOKQ.js → chunk-TV65NR7L.js} +2 -2
  148. package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
  149. package/dist/chunk-UDKWFDV2.js.map +1 -0
  150. package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
  151. package/dist/{chunk-4EACCIPO.js → chunk-UHPHH74F.js} +2 -2
  152. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  153. package/dist/{chunk-6QYPL6NI.js → chunk-V45TEVDA.js} +3 -3
  154. package/dist/{chunk-GCAQ3VUJ.js → chunk-VCVEMQJK.js} +2 -2
  155. package/dist/{chunk-6L63IN3M.js → chunk-VEFGHRKV.js} +1 -1
  156. package/dist/{chunk-E33UJHY3.js → chunk-WFDWLWDK.js} +2 -2
  157. package/dist/{chunk-4KLQI3L2.js → chunk-WLNWFHX3.js} +1 -1
  158. package/dist/chunk-WLNWFHX3.js.map +1 -0
  159. package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
  160. package/dist/{chunk-MPGLM2VU.js → chunk-X7REO4CH.js} +2 -2
  161. package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
  162. package/dist/chunk-XO23OKMK.js.map +1 -0
  163. package/dist/{chunk-Z3DDQXM4.js → chunk-XSFQN7TP.js} +35 -36
  164. package/dist/chunk-XSFQN7TP.js.map +1 -0
  165. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  166. package/dist/{chunk-WV254W4V.js → chunk-Y3IYKNBY.js} +2 -2
  167. package/dist/{chunk-FUOAIJKQ.js → chunk-Y43X3E24.js} +2 -2
  168. package/dist/{chunk-3T3VHXUH.js → chunk-YAAKVTZA.js} +2 -2
  169. package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
  170. package/dist/{chunk-65SKM4BF.js → chunk-YNBMBL4C.js} +2 -2
  171. package/dist/{chunk-PJYK7HH2.js → chunk-ZWTHVUT5.js} +2 -2
  172. package/dist/cli.js +110 -108
  173. package/dist/coding/export-okf-codegraph.js +25 -25
  174. package/dist/coding/pre-action-gate.js +2 -2
  175. package/dist/compounding/engine.js +27 -27
  176. package/dist/config.js +28 -28
  177. package/dist/connectors/codex-materialize-runner.js +25 -25
  178. package/dist/connectors/index.js +25 -25
  179. package/dist/contradiction/index.js +4 -4
  180. package/dist/convergence-refresh.js +27 -27
  181. package/dist/conversation-index/backend.js +2 -2
  182. package/dist/corpus-watermark.js +27 -27
  183. package/dist/deep-recall-http-glue.js +5 -5
  184. package/dist/entity-mention-index-cache.d.ts.map +1 -1
  185. package/dist/entity-mention-index-cache.js +2 -1
  186. package/dist/entity-retrieval.js +27 -26
  187. package/dist/event-order-recall.js +2 -2
  188. package/dist/explicit-capture.js +5 -5
  189. package/dist/external-wiki-access.js +68 -67
  190. package/dist/extraction-evidence-class.js +2 -2
  191. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  192. package/dist/extraction-quote-grounding.js +1 -1
  193. package/dist/extraction-source-grounding-rules.js +2 -2
  194. package/dist/extraction-source-grounding.js +5 -5
  195. package/dist/extraction-span-materialize.js +2 -2
  196. package/dist/extraction.js +40 -40
  197. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  198. package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
  199. package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
  200. package/dist/graph-jsonl.js +3 -3
  201. package/dist/graph-snapshot.js +3 -3
  202. package/dist/graph.js +2 -2
  203. package/dist/harmonic-retrieval.js +25 -25
  204. package/dist/index.js +205 -203
  205. package/dist/lcm/engine.js +2 -2
  206. package/dist/lcm/index.js +2 -2
  207. package/dist/local-llm.js +2 -2
  208. package/dist/location/index.js +28 -28
  209. package/dist/maintenance/memory-governance.js +25 -25
  210. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +25 -25
  211. package/dist/maintenance/rebuild-memory-projection-runner.js +27 -27
  212. package/dist/maintenance/rebuild-memory-projection.js +26 -26
  213. package/dist/memory-browse-operations.js +68 -67
  214. package/dist/memory-subject.js +2 -2
  215. package/dist/namespaces/migrate.js +26 -26
  216. package/dist/namespaces/search.d.ts +8 -0
  217. package/dist/namespaces/search.d.ts.map +1 -1
  218. package/dist/namespaces/search.js +10 -6
  219. package/dist/namespaces/storage.js +25 -25
  220. package/dist/operator-doctor-corpus.js +28 -28
  221. package/dist/operator-toolkit.js +39 -39
  222. package/dist/orchestration/backlog-scheduler.d.ts +3 -2
  223. package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
  224. package/dist/orchestration/backlog-scheduler.js +159 -157
  225. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  226. package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
  227. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
  228. package/dist/orchestration/buffer-drain-runner.js +149 -147
  229. package/dist/orchestration/extraction-persist.d.ts.map +1 -1
  230. package/dist/orchestration/fact-dedup-window.d.ts +1 -4
  231. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  232. package/dist/orchestration/maintenance.js +39 -39
  233. package/dist/orchestrator.js +148 -146
  234. package/dist/provenance.js +2 -2
  235. package/dist/recall-navigation-operations.js +68 -67
  236. package/dist/recall-navigation.js +67 -66
  237. package/dist/reconcile/manifest.js +2 -2
  238. package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
  239. package/dist/resolution-23ZBDM6A.js.map +1 -0
  240. package/dist/resume-bundles.js +29 -29
  241. package/dist/salvage-envelope.js +4 -4
  242. package/dist/search/factory.js +5 -5
  243. package/dist/search/index.js +9 -9
  244. package/dist/semantic-consolidation.js +26 -26
  245. package/dist/semantic-rule-promotion.js +25 -25
  246. package/dist/semantic-rule-verifier.js +25 -25
  247. package/dist/source-agent-qualifier.js +31 -31
  248. package/dist/standup/build.js +25 -25
  249. package/dist/storage/buffer-drain-claims.d.ts +18 -6
  250. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  251. package/dist/storage/buffer-drain-claims.js +2 -2
  252. package/dist/storage/buffer-session-files.js +2 -2
  253. package/dist/storage/content-hash-index.d.ts +8 -0
  254. package/dist/storage/content-hash-index.d.ts.map +1 -1
  255. package/dist/storage.d.ts +10 -1
  256. package/dist/storage.d.ts.map +1 -1
  257. package/dist/storage.js +24 -24
  258. package/dist/subject-config.js +3 -3
  259. package/dist/summarizer.js +3 -3
  260. package/dist/support-passport/index.js +70 -69
  261. package/dist/transfer/autodetect.js +1 -1
  262. package/dist/transfer/backup.js +2 -2
  263. package/dist/transfer/capsule-export.js +3 -3
  264. package/dist/transfer/capsule-gunzip.d.ts +14 -0
  265. package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
  266. package/dist/transfer/capsule-import.d.ts.map +1 -1
  267. package/dist/transfer/capsule-import.js +3 -2
  268. package/dist/transfer/capsule-merge.d.ts.map +1 -1
  269. package/dist/transfer/export-okf.js +25 -25
  270. package/dist/verified-recall.js +25 -25
  271. package/dist/who-knows-cli.js +26 -26
  272. package/dist/who-knows.js +25 -25
  273. package/dist/write-envelope.js +3 -3
  274. package/package.json +2 -2
  275. package/src/access-mcp-correction-tools.test.ts +45 -0
  276. package/src/access-mcp-frames.test.ts +47 -0
  277. package/src/access-mcp-frames.ts +74 -0
  278. package/src/access-mcp.test.ts +146 -0
  279. package/src/access-mcp.ts +30 -30
  280. package/src/access-observe-write-surface.ts +4 -4
  281. package/src/backlog-scheduler.test.ts +443 -3
  282. package/src/buffer-persistence.ts +15 -6
  283. package/src/buffer-turn-helpers.ts +6 -0
  284. package/src/buffer.ts +6 -6
  285. package/src/cli.ts +4 -4
  286. package/src/compounding/weekly-evidence.test.ts +237 -0
  287. package/src/entity-mention-index-cache.ts +1 -6
  288. package/src/extraction-quote-grounding.test.ts +49 -0
  289. package/src/extraction-quote-grounding.ts +50 -12
  290. package/src/fact-dedup-window.test.ts +103 -20
  291. package/src/maintenance/rebuild-memory-projection-runner.test.ts +90 -0
  292. package/src/namespaces/search.test.ts +339 -0
  293. package/src/namespaces/search.ts +54 -1
  294. package/src/orchestration/backlog-scheduler.ts +13 -12
  295. package/src/orchestration/buffer-drain-runner.ts +40 -16
  296. package/src/orchestration/extraction-persist.ts +2 -10
  297. package/src/orchestration/fact-dedup-window.ts +5 -12
  298. package/src/orchestration/full-corpus-heap-guard.test.ts +52 -0
  299. package/src/projection-rebuild-config.test.ts +79 -0
  300. package/src/storage/buffer-drain-claims.test.ts +330 -6
  301. package/src/storage/buffer-drain-claims.ts +226 -80
  302. package/src/storage/buffer-session-files.ts +2 -2
  303. package/src/storage/content-hash-index.ts +9 -0
  304. package/src/storage/entity-store-symlink.test.ts +44 -0
  305. package/src/storage.ts +15 -0
  306. package/src/transfer/capsule-gunzip.ts +39 -0
  307. package/src/transfer/capsule-import.test.ts +73 -0
  308. package/src/transfer/capsule-import.ts +2 -2
  309. package/src/transfer/capsule-merge.test.ts +34 -0
  310. package/src/transfer/capsule-merge.ts +2 -10
  311. package/src/utils/malformed-entity-record.test.ts +69 -0
  312. package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
  313. package/dist/chunk-4KLQI3L2.js.map +0 -1
  314. package/dist/chunk-6LCWKS62.js.map +0 -1
  315. package/dist/chunk-7PMOCQOL.js.map +0 -1
  316. package/dist/chunk-AYTDCMEL.js.map +0 -1
  317. package/dist/chunk-B74NPUHT.js.map +0 -1
  318. package/dist/chunk-KQUSP6LB.js.map +0 -1
  319. package/dist/chunk-LU66I56Q.js.map +0 -1
  320. package/dist/chunk-MPDLLLOG.js.map +0 -1
  321. package/dist/chunk-S57QBPTA.js.map +0 -1
  322. package/dist/chunk-SVCHO337.js.map +0 -1
  323. package/dist/chunk-WAD32CZG.js.map +0 -1
  324. package/dist/chunk-XYP5WBDO.js.map +0 -1
  325. package/dist/chunk-YK7GOPIE.js.map +0 -1
  326. package/dist/chunk-Z3DDQXM4.js.map +0 -1
  327. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
  328. /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
  329. /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  330. /package/dist/{resolution-BNWE5Y25.js.map → capsule-gunzip-KGBDGF7P.js.map} +0 -0
  331. /package/dist/{chunk-T6T2RMWW.js.map → chunk-23B4HLWG.js.map} +0 -0
  332. /package/dist/{chunk-LECSS7EE.js.map → chunk-2ASEDFOA.js.map} +0 -0
  333. /package/dist/{chunk-TPX7XXY4.js.map → chunk-2JAT4S7N.js.map} +0 -0
  334. /package/dist/{chunk-XRBHIR7Q.js.map → chunk-2URKYLMK.js.map} +0 -0
  335. /package/dist/{chunk-IN4OFAHX.js.map → chunk-2XYDL2FG.js.map} +0 -0
  336. /package/dist/{chunk-HQGATKG2.js.map → chunk-3KGCHTTK.js.map} +0 -0
  337. /package/dist/{chunk-YAUHSNPB.js.map → chunk-3LHPTBWG.js.map} +0 -0
  338. /package/dist/{chunk-XT45WCKX.js.map → chunk-3VU2FDTU.js.map} +0 -0
  339. /package/dist/{chunk-CIIHIAJ2.js.map → chunk-433EXTC4.js.map} +0 -0
  340. /package/dist/{chunk-QZSIDSB2.js.map → chunk-44FQCC5D.js.map} +0 -0
  341. /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
  342. /package/dist/{chunk-54FDG7K7.js.map → chunk-4DPLCYEK.js.map} +0 -0
  343. /package/dist/{chunk-KO3YBHOV.js.map → chunk-4IWEPQEV.js.map} +0 -0
  344. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  345. /package/dist/{chunk-K6RTQZA4.js.map → chunk-6DANGCMO.js.map} +0 -0
  346. /package/dist/{chunk-2IW5PT7U.js.map → chunk-6KFFRMPQ.js.map} +0 -0
  347. /package/dist/{chunk-MSIRVXQ3.js.map → chunk-73YVMZ7D.js.map} +0 -0
  348. /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
  349. /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
  350. /package/dist/{chunk-YOEYL6CH.js.map → chunk-AP3TSV6S.js.map} +0 -0
  351. /package/dist/{chunk-GQZSGXR6.js.map → chunk-BATPQNRG.js.map} +0 -0
  352. /package/dist/{chunk-D5SSBCYY.js.map → chunk-BLGQUZFB.js.map} +0 -0
  353. /package/dist/{chunk-BDN3TDGV.js.map → chunk-BLMTOIBK.js.map} +0 -0
  354. /package/dist/{chunk-R45L4TV6.js.map → chunk-BUKK5SWA.js.map} +0 -0
  355. /package/dist/{chunk-TFLPDNQV.js.map → chunk-CCU7URRP.js.map} +0 -0
  356. /package/dist/{chunk-2XJZLSXE.js.map → chunk-CMRHXPLQ.js.map} +0 -0
  357. /package/dist/{chunk-BK4FBHZ3.js.map → chunk-D6THW7PA.js.map} +0 -0
  358. /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
  359. /package/dist/{chunk-VKDAJJQP.js.map → chunk-DI5ET2IE.js.map} +0 -0
  360. /package/dist/{chunk-QZBTIYKD.js.map → chunk-DM4RRTN7.js.map} +0 -0
  361. /package/dist/{chunk-EBOHFZI4.js.map → chunk-E5QEF3JO.js.map} +0 -0
  362. /package/dist/{chunk-DD6OIFDG.js.map → chunk-E7V5AK7I.js.map} +0 -0
  363. /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
  364. /package/dist/{chunk-HPFVZPRH.js.map → chunk-FK4WC6VK.js.map} +0 -0
  365. /package/dist/{chunk-VLMBSGQF.js.map → chunk-FRM4UTDR.js.map} +0 -0
  366. /package/dist/{chunk-LTDLJCMQ.js.map → chunk-FZPDRCOB.js.map} +0 -0
  367. /package/dist/{chunk-ZJJFUEPO.js.map → chunk-GBOYOL5L.js.map} +0 -0
  368. /package/dist/{chunk-YUTG35M4.js.map → chunk-GJVGFKOU.js.map} +0 -0
  369. /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
  370. /package/dist/{chunk-HWHNQVRV.js.map → chunk-GYNRNKMW.js.map} +0 -0
  371. /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
  372. /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
  373. /package/dist/{chunk-FJ2VJTGA.js.map → chunk-KHZVAISH.js.map} +0 -0
  374. /package/dist/{chunk-WHQXWFH7.js.map → chunk-LT3CMO65.js.map} +0 -0
  375. /package/dist/{chunk-NUSAAN75.js.map → chunk-MHSBOEH7.js.map} +0 -0
  376. /package/dist/{chunk-KJCL2FZO.js.map → chunk-MNYQVYJ4.js.map} +0 -0
  377. /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
  378. /package/dist/{chunk-FVRTCYZ3.js.map → chunk-MVOJDLXS.js.map} +0 -0
  379. /package/dist/{chunk-KESJ5XX3.js.map → chunk-MYCCO6AA.js.map} +0 -0
  380. /package/dist/{chunk-66JOLMWS.js.map → chunk-MZCGHPIJ.js.map} +0 -0
  381. /package/dist/{chunk-B4ZU34JQ.js.map → chunk-N6II66E5.js.map} +0 -0
  382. /package/dist/{chunk-BK7AJUWB.js.map → chunk-NIV7RPWQ.js.map} +0 -0
  383. /package/dist/{chunk-TLHF75OR.js.map → chunk-O55AGDTI.js.map} +0 -0
  384. /package/dist/{chunk-GGJFRVRD.js.map → chunk-OMYRPMQC.js.map} +0 -0
  385. /package/dist/{chunk-XHGYZRVW.js.map → chunk-OSTFDWZJ.js.map} +0 -0
  386. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  387. /package/dist/{chunk-WC475JOG.js.map → chunk-P225W4RQ.js.map} +0 -0
  388. /package/dist/{chunk-TFA6HPOA.js.map → chunk-QCP75JGO.js.map} +0 -0
  389. /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
  390. /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
  391. /package/dist/{chunk-VUROWXFL.js.map → chunk-RNGDKFY2.js.map} +0 -0
  392. /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
  393. /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
  394. /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
  395. /package/dist/{chunk-T77AX6DG.js.map → chunk-TQVECGUQ.js.map} +0 -0
  396. /package/dist/{chunk-OQSSQOKQ.js.map → chunk-TV65NR7L.js.map} +0 -0
  397. /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
  398. /package/dist/{chunk-4EACCIPO.js.map → chunk-UHPHH74F.js.map} +0 -0
  399. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  400. /package/dist/{chunk-6QYPL6NI.js.map → chunk-V45TEVDA.js.map} +0 -0
  401. /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-VCVEMQJK.js.map} +0 -0
  402. /package/dist/{chunk-6L63IN3M.js.map → chunk-VEFGHRKV.js.map} +0 -0
  403. /package/dist/{chunk-E33UJHY3.js.map → chunk-WFDWLWDK.js.map} +0 -0
  404. /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
  405. /package/dist/{chunk-MPGLM2VU.js.map → chunk-X7REO4CH.js.map} +0 -0
  406. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  407. /package/dist/{chunk-WV254W4V.js.map → chunk-Y3IYKNBY.js.map} +0 -0
  408. /package/dist/{chunk-FUOAIJKQ.js.map → chunk-Y43X3E24.js.map} +0 -0
  409. /package/dist/{chunk-3T3VHXUH.js.map → chunk-YAAKVTZA.js.map} +0 -0
  410. /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
  411. /package/dist/{chunk-65SKM4BF.js.map → chunk-YNBMBL4C.js.map} +0 -0
  412. /package/dist/{chunk-PJYK7HH2.js.map → chunk-ZWTHVUT5.js.map} +0 -0
  413. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -11,6 +11,7 @@
11
11
  import assert from "node:assert/strict";
12
12
  import test from "node:test";
13
13
 
14
+ import { hostname } from "node:os";
14
15
  import {
15
16
  BacklogScheduler,
16
17
  type BacklogSchedulerDeps,
@@ -23,10 +24,12 @@ import { Orchestrator } from "./orchestrator.js";
23
24
  import {
24
25
  cleanupDir,
25
26
  makeHarmonicLifecycleConfig,
27
+ memoryFilesContaining,
26
28
  mkTempMemoryDir,
27
29
  singleFactResult,
28
30
  stubExtraction,
29
31
  } from "./testing/orchestrator-lite.js";
32
+ import type { DrainLease } from "./storage/buffer-drain-claims.js";
30
33
  import type { BufferTurn } from "./types.js";
31
34
 
32
35
  interface StubState {
@@ -79,10 +82,10 @@ function makeDeps(state: StubState, queue: ExtractionQueueCoordinator): BacklogS
79
82
  },
80
83
  },
81
84
  liveBufferCounts: () => state.counts,
82
- lease: () => ({
83
- ownerId: "daemon-drain-test",
85
+ lease: (ownerId) => ({
86
+ ownerId,
84
87
  pid: process.pid,
85
- host: "test",
88
+ host: hostname(),
86
89
  acquiredAt: new Date().toISOString(),
87
90
  heartbeatAt: new Date().toISOString(),
88
91
  leaseMs: 60_000,
@@ -113,6 +116,50 @@ function turn(content: string, sessionKey: string, timestamp = "2026-11-01T00:00
113
116
  return { role: "user", content, timestamp, sessionKey };
114
117
  }
115
118
 
119
+ test("scheduler instances use distinct lease tokens through claim and drain", async () => {
120
+ const issuedOwners: string[] = [];
121
+ const drainedOwners: string[] = [];
122
+ for (let attempt = 0; attempt < 2; attempt += 1) {
123
+ const key = `token-session-${attempt}`;
124
+ const queue = new ExtractionQueueCoordinator(1);
125
+ const state: StubState = {
126
+ counts: new Map([[key, { turns: 1, retainedTurns: 0 }]]),
127
+ claimed: new Set(),
128
+ claimTurns: new Map(),
129
+ claimedThisRun: new Set(),
130
+ outcomes: [emptyOutcome(key)],
131
+ drained: [],
132
+ };
133
+ const deps = makeDeps(state, queue);
134
+ deps.lease = (ownerId) => {
135
+ issuedOwners.push(ownerId);
136
+ return {
137
+ ownerId,
138
+ pid: process.pid,
139
+ host: hostname(),
140
+ acquiredAt: new Date().toISOString(),
141
+ heartbeatAt: new Date().toISOString(),
142
+ leaseMs: 60_000,
143
+ };
144
+ };
145
+ deps.drainKey = async (_drainKey, drainOptions) => {
146
+ drainedOwners.push(drainOptions.ownerId);
147
+ return emptyOutcome(key);
148
+ };
149
+ const scheduler = new BacklogScheduler(deps, { ownerId: "shared-label", pollMs: 3_600_000 });
150
+ scheduler.start();
151
+ try {
152
+ await scheduler.tickForTest();
153
+ await scheduler.settleForTest();
154
+ } finally {
155
+ await scheduler.stop();
156
+ }
157
+ }
158
+
159
+ assert.notEqual(issuedOwners[0], issuedOwners[1]);
160
+ assert.deepEqual([...new Set(drainedOwners)], [...new Set(issuedOwners)]);
161
+ });
162
+
116
163
  test("startup drains existing claims and newly buffered sessions within the queue's concurrency", async () => {
117
164
  const queue = new ExtractionQueueCoordinator(2);
118
165
  const state: StubState = {
@@ -464,6 +511,399 @@ test("a wired scheduler drains real backlog and resumes after tombstone-consumin
464
511
  }
465
512
  });
466
513
 
514
+ // ── Real wiring: full cap cycle (fill → refuse → drain → admit) ─────────────
515
+
516
+ test("a full session cap admits a new durable session after the backlog drains", async () => {
517
+ // Real production SmartBuffer admission, real backlog scheduler/drain and
518
+ // the real persistence path (session files → drain claims → memory files).
519
+ // The extraction MODEL is a test double: it records the exact turn slice it
520
+ // is asked to extract and derives one fact from the first turn's content —
521
+ // every durability, admission and clearing decision below is production code.
522
+ const memoryDir = await mkTempMemoryDir("backlog-cap-cycle");
523
+ let orchestrator: Orchestrator | undefined;
524
+ try {
525
+ orchestrator = new Orchestrator(
526
+ makeHarmonicLifecycleConfig(memoryDir, {
527
+ extractionMinChars: 0,
528
+ extractionMinUserTurns: 1,
529
+ bufferMaxSessions: 4, // + the legacy `default` sentinel entry: 3 durable slots
530
+ })
531
+ );
532
+ const calls = stubExtraction(orchestrator, (turns) => singleFactResult(`cap-cycle fact for ${turns[0]?.content ?? "empty"}`));
533
+ const buffer = orchestrator.buffer;
534
+ const defaultNamespace = orchestrator.config.defaultNamespace;
535
+
536
+ // Mirrors production ingestion (turn-ingestion.ts): accepted captures are
537
+ // pinned BEFORE bounded admission, so the cap applies to real durable work.
538
+ const pinned = (key: string, content: string): BufferTurn => ({
539
+ ...turn(content, key),
540
+ acceptedCapture: true,
541
+ writeNamespaceOverride: defaultNamespace,
542
+ });
543
+ const capture = async (key: string, content: string): Promise<void> => {
544
+ const outcome = await buffer.addTurnBounded(key, pinned(key, content));
545
+ assert.notEqual(outcome, null, `${key} admitted below the cap`);
546
+ await buffer.flushPendingSave({ throwOnFailure: true });
547
+ };
548
+ await capture("cap-1", "alpha one");
549
+ await capture("cap-2", "beta two");
550
+ await capture("cap-3", "gamma three");
551
+
552
+ // The cap genuinely bites: a fourth durable session is refused, never
553
+ // evicted (#3251).
554
+ assert.equal(
555
+ await orchestrator.buffer.addTurnBounded("cap-4", turn("delta four", "cap-4")),
556
+ null,
557
+ "a fourth session is refused at the cap"
558
+ );
559
+
560
+ const scheduler = wireBacklogScheduler(orchestrator, { pollMs: 3_600_000 });
561
+ try {
562
+ scheduler.start();
563
+ for (let attempt = 0; attempt < 6 && scheduler.snapshot().counters.processed < 3; attempt += 1) {
564
+ await scheduler.tickForTest();
565
+ await scheduler.settleForTest();
566
+ }
567
+ assert.equal(scheduler.snapshot().counters.processed, 3, "all three sessions drained");
568
+ assert.equal(scheduler.snapshot().counters.durableOutputs, 3, "one durable output per session");
569
+ assert.equal(calls.length, 3, "the model double saw one turn slice per session");
570
+
571
+ // Durable extraction kept the content: each session's fact is on disk.
572
+ for (const source of ["alpha one", "beta two", "gamma three"]) {
573
+ const hits = await memoryFilesContaining(memoryDir, `cap-cycle fact for ${source}`);
574
+ assert.ok(hits.length > 0, `content of the drained session was lost: ${source}`);
575
+ }
576
+
577
+
578
+ // The source turns are cleared from both the durable claim and the live
579
+ // SmartBuffer; the claim tombstones remain until a later owner save.
580
+ const claims = orchestrator.storage.bufferDrainClaims();
581
+ assert.deepEqual(
582
+ (await claims.list()).map((l) => l.turnCount).sort(),
583
+ [0, 0, 0],
584
+ "all drained claims are empty tombstones"
585
+ );
586
+ for (const key of ["cap-1", "cap-2", "cap-3"]) {
587
+ assert.deepEqual(orchestrator.buffer.getTurns(key), [], `${key} stale source turns cleared in memory`);
588
+ }
589
+
590
+ const admitted = await orchestrator.buffer.addTurnBounded("cap-4", pinned("cap-4", "delta four"));
591
+ assert.notEqual(admitted, null, "a fully drained backlog frees cap slots for a new session");
592
+ } finally {
593
+ await scheduler.stop();
594
+ }
595
+ } finally {
596
+ await orchestrator?.destroy().catch(() => undefined);
597
+ await cleanupDir(memoryDir);
598
+ }
599
+ });
600
+
601
+
602
+ test("same-key accepted ingress during extraction survives owner-fenced reconciliation", async () => {
603
+ const memoryDir = await mkTempMemoryDir("backlog-drain-ingress-race");
604
+ let orchestrator: Orchestrator | undefined;
605
+ try {
606
+ orchestrator = new Orchestrator(
607
+ makeHarmonicLifecycleConfig(memoryDir, { extractionMinChars: 0, extractionMinUserTurns: 1 })
608
+ );
609
+ const buffer = orchestrator.buffer;
610
+ const namespace = orchestrator.config.defaultNamespace;
611
+ const key = "race-session";
612
+ const oldTurn: BufferTurn = {
613
+ ...turn("original captured turn", key),
614
+ acceptedCapture: true,
615
+ writeNamespaceOverride: namespace,
616
+ };
617
+ const newerTurn: BufferTurn = {
618
+ ...turn("new accepted turn during drain", key, "2026-11-02T00:00:00.000Z"),
619
+ acceptedCapture: true,
620
+ writeNamespaceOverride: namespace,
621
+ principalOverride: "principal-race",
622
+ };
623
+ const entered = Promise.withResolvers<void>();
624
+ const release = Promise.withResolvers<void>();
625
+ let firstExtraction = true;
626
+ const calls = stubExtraction(orchestrator, async (turns) => {
627
+ if (firstExtraction) {
628
+ firstExtraction = false;
629
+ entered.resolve();
630
+ await release.promise;
631
+ }
632
+ return singleFactResult(`race fact for ${turns[0]?.content ?? "empty"}`);
633
+ });
634
+
635
+ assert.notEqual(await buffer.addTurnBounded(key, oldTurn), null);
636
+ await buffer.flushPendingSave({ throwOnFailure: true });
637
+ let now = 0;
638
+ const scheduler = wireBacklogScheduler(orchestrator, {
639
+ ownerId: "race-owner",
640
+ pollMs: 3_600_000,
641
+ retryBaseMs: 1,
642
+ retryMaxMs: 1,
643
+ now: () => now,
644
+ });
645
+ try {
646
+ scheduler.start();
647
+ await entered.promise;
648
+ assert.equal(
649
+ await orchestrator.storage.bufferDrainClaims().drainedTurnsForOwner(key, "not-race-owner"),
650
+ null,
651
+ "another owner cannot authorize live-buffer reconciliation"
652
+ );
653
+
654
+ // Live ingress saves into the owned claim while the model is still
655
+ // extracting its original snapshot.
656
+ assert.notEqual(await buffer.addTurnBounded(key, newerTurn), null);
657
+ await buffer.flushPendingSave({ throwOnFailure: true });
658
+ release.resolve();
659
+ await scheduler.settleForTest();
660
+
661
+ const pending = await orchestrator.storage.bufferDrainClaims().load(key);
662
+ assert.deepEqual(
663
+ pending?.entry.turns.map((item) => item.content),
664
+ ["new accepted turn during drain"],
665
+ "the extracted source is removed while newer accepted ingress remains durable"
666
+ );
667
+ assert.equal(pending?.entry.turns[0]?.principalOverride, "principal-race");
668
+ assert.equal(pending?.entry.turns[0]?.writeNamespaceOverride, namespace);
669
+ assert.equal((await memoryFilesContaining(memoryDir, "race fact for original captured turn")).length, 1);
670
+
671
+ now = 2;
672
+ await scheduler.tickForTest();
673
+ await scheduler.settleForTest();
674
+ assert.equal(scheduler.snapshot().counters.processed, 1, "the newer turn also drains");
675
+ assert.deepEqual(calls.map((batch) => batch.map((item) => item.content)), [
676
+ ["original captured turn"],
677
+ ["new accepted turn during drain"],
678
+ ]);
679
+ assert.equal(calls[1]?.[0]?.principalOverride, "principal-race");
680
+ assert.equal(calls[1]?.[0]?.writeNamespaceOverride, namespace);
681
+ assert.equal((await memoryFilesContaining(memoryDir, "race fact for original captured turn")).length, 1);
682
+ assert.equal((await memoryFilesContaining(memoryDir, "race fact for new accepted turn during drain")).length, 1);
683
+ assert.equal(scheduler.snapshot().counters.durableOutputs, 1);
684
+ } finally {
685
+ release.resolve();
686
+ await scheduler.stop();
687
+ }
688
+ } finally {
689
+ await orchestrator?.destroy().catch(() => undefined);
690
+ await cleanupDir(memoryDir);
691
+ }
692
+ });
693
+
694
+
695
+
696
+ test("a drain tombstones every identical accepted occurrence before live-buffer reconciliation", async () => {
697
+ const memoryDir = await mkTempMemoryDir("buffer-reconcile-identical-turns");
698
+ let orchestrator: Orchestrator | undefined;
699
+ try {
700
+ orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
701
+ const buffer = orchestrator.buffer;
702
+ const key = "duplicate-session";
703
+ const duplicate = turn("identical accepted occurrence", key);
704
+ const newer = {
705
+ ...turn("new live turn after duplicate drain", key, "2026-11-05T00:00:00.000Z"),
706
+ acceptedCapture: true,
707
+ writeNamespaceOverride: orchestrator.config.defaultNamespace,
708
+ };
709
+ const claims = orchestrator.storage.bufferDrainClaims();
710
+ const now = new Date().toISOString();
711
+ const lease: DrainLease = {
712
+ ownerId: "duplicate-owner",
713
+ pid: process.pid,
714
+ host: hostname(),
715
+ acquiredAt: now,
716
+ heartbeatAt: now,
717
+ leaseMs: 60_000,
718
+ };
719
+
720
+ await buffer.addTurn(key, duplicate);
721
+ await buffer.addTurn(key, duplicate);
722
+ await buffer.flushPendingSave({ throwOnFailure: true });
723
+ assert.deepEqual(buffer.getTurns(key), [duplicate, duplicate]);
724
+ assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
725
+ const claimed = await claims.load(key);
726
+ assert.ok(claimed);
727
+ assert.deepEqual(claimed.entry.turns, [duplicate, duplicate]);
728
+
729
+ await claims.writeEntry(key, lease.ownerId, { turns: [], lastExtractionAt: null, extractionCount: 0 }, claimed.entry);
730
+ assert.deepEqual(await claims.drainedTurns(key), { turns: [duplicate, duplicate], retainedTurns: [] });
731
+ assert.equal(await claims.ack(key, lease.ownerId), "acked");
732
+
733
+ assert.notEqual(await buffer.addTurnBounded(key, newer), null);
734
+ await buffer.flushPendingSave({ throwOnFailure: true });
735
+ assert.deepEqual(buffer.getTurns(key), [newer]);
736
+ assert.deepEqual((await orchestrator.storage.loadBuffer()).entries?.[key]?.turns, [newer]);
737
+ } finally {
738
+ await orchestrator?.destroy().catch(() => undefined);
739
+ await cleanupDir(memoryDir);
740
+ }
741
+ });
742
+
743
+ test("ambiguous identical active and retained turns remain pending after cross-list progress", async () => {
744
+ const memoryDir = await mkTempMemoryDir("buffer-reconcile-cross-list-duplicates");
745
+ let orchestrator: Orchestrator | undefined;
746
+ try {
747
+ orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
748
+ const buffer = orchestrator.buffer;
749
+ const claims = orchestrator.storage.bufferDrainClaims();
750
+ const key = "cross-list-duplicates";
751
+ const same = turn("same occurrence identity in both lists", key);
752
+ const now = new Date().toISOString();
753
+ const lease: DrainLease = {
754
+ ownerId: "cross-list-owner",
755
+ pid: process.pid,
756
+ host: hostname(),
757
+ acquiredAt: now,
758
+ heartbeatAt: now,
759
+ leaseMs: 60_000,
760
+ };
761
+
762
+ await buffer.addTurn(key, same);
763
+ await buffer.retainDeferredTurns(key, [same], 10, [same]);
764
+ await buffer.flushPendingSave({ throwOnFailure: true });
765
+ assert.deepEqual(buffer.getTurns(key), [same, same]);
766
+ assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
767
+ const claim = await claims.load(key);
768
+ assert.ok(claim);
769
+ assert.deepEqual(claim.entry.turns, [same]);
770
+ assert.deepEqual(claim.entry.retainedTurns, [same]);
771
+
772
+ // One indistinguishable copy survives in the opposite list. Progress
773
+ // alone cannot prove whether that is the active or retained occurrence.
774
+ await claims.writeEntry(
775
+ key,
776
+ lease.ownerId,
777
+ { turns: [], retainedTurns: [same], lastExtractionAt: null, extractionCount: 0 },
778
+ claim.entry,
779
+ );
780
+ const drained = await claims.drainedTurnsForOwner(key, lease.ownerId);
781
+ assert.deepEqual(drained, { turns: [], retainedTurns: [] });
782
+ assert.deepEqual(
783
+ (await claims.load(key))?.entry,
784
+ { turns: [same], retainedTurns: [same], lastExtractionAt: null, extractionCount: 0 },
785
+ "both original source-list occurrences remain pending when provenance is ambiguous",
786
+ );
787
+ assert.ok(drained);
788
+ await buffer.save({ key, drained });
789
+ assert.deepEqual(buffer.getTurns(key), [same, same]);
790
+ } finally {
791
+ await orchestrator?.destroy().catch(() => undefined);
792
+ await cleanupDir(memoryDir);
793
+ }
794
+ });
795
+
796
+ test("claim tombstones clear identical active and retained copies on live save", async () => {
797
+ const memoryDir = await mkTempMemoryDir("buffer-reconcile-retained-provenance");
798
+ let orchestrator: Orchestrator | undefined;
799
+ try {
800
+ orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
801
+ const buffer = orchestrator.buffer;
802
+ const key = "retained-session";
803
+ const source = turn("same source identity", key);
804
+ const newer = {
805
+ ...turn("new live turn", key, "2026-11-04T00:00:00.000Z"),
806
+ acceptedCapture: true,
807
+ writeNamespaceOverride: orchestrator.config.defaultNamespace,
808
+ };
809
+ const claims = orchestrator.storage.bufferDrainClaims();
810
+ const now = new Date().toISOString();
811
+ const lease: DrainLease = {
812
+ ownerId: "retained-owner",
813
+ pid: process.pid,
814
+ host: hostname(),
815
+ acquiredAt: now,
816
+ heartbeatAt: now,
817
+ leaseMs: 60_000,
818
+ };
819
+
820
+ await buffer.addTurn(key, source);
821
+ await buffer.retainDeferredTurns(key, [source]);
822
+ assert.deepEqual(buffer.getTurns(key), [source, source]);
823
+ assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
824
+ const claimed = await claims.load(key);
825
+ assert.ok(claimed);
826
+ assert.deepEqual(claimed.entry, {
827
+ turns: [source],
828
+ retainedTurns: [source],
829
+ lastExtractionAt: null,
830
+ extractionCount: 0,
831
+ });
832
+ await claims.writeEntry(key, lease.ownerId, { turns: [], lastExtractionAt: null, extractionCount: 0 }, claimed.entry);
833
+ assert.deepEqual(await claims.drainedTurns(key), { turns: [source], retainedTurns: [source] });
834
+ assert.equal(await claims.ack(key, lease.ownerId), "acked");
835
+
836
+ // A new same-key save consumes the tombstone through BufferSessionFiles.
837
+ assert.notEqual(await buffer.addTurnBounded(key, newer), null);
838
+ await buffer.flushPendingSave({ throwOnFailure: true });
839
+ assert.deepEqual(buffer.getTurns(key), [newer]);
840
+ assert.deepEqual(buffer.getRetainedDeferredTurns(key), []);
841
+ const persisted = await orchestrator.storage.loadBuffer();
842
+ assert.deepEqual(persisted.entries?.[key]?.turns, [newer]);
843
+ assert.equal(persisted.entries?.[key]?.retainedTurns, undefined);
844
+ } finally {
845
+ await orchestrator?.destroy().catch(() => undefined);
846
+ await cleanupDir(memoryDir);
847
+ }
848
+ });
849
+ test("a lease takeover after the drained snapshot cannot erase newer scoped ingress", async () => {
850
+ const memoryDir = await mkTempMemoryDir("backlog-takeover-reconcile");
851
+ let orchestrator: Orchestrator | undefined;
852
+ try {
853
+ orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
854
+ const buffer = orchestrator.buffer;
855
+ const claims = orchestrator.storage.bufferDrainClaims();
856
+ const key = "takeover-session";
857
+ const oldTurn: BufferTurn = {
858
+ ...turn("old drained turn", key),
859
+ acceptedCapture: true,
860
+ writeNamespaceOverride: orchestrator.config.defaultNamespace,
861
+ };
862
+ const newerTurn: BufferTurn = {
863
+ ...turn("new accepted turn", key, "2026-11-03T00:00:00.000Z"),
864
+ acceptedCapture: true,
865
+ writeNamespaceOverride: orchestrator.config.defaultNamespace,
866
+ principalOverride: "new-owner-principal",
867
+ };
868
+ const lease = (ownerId: string): DrainLease => {
869
+ const now = new Date().toISOString();
870
+ return { ownerId, pid: process.pid, host: hostname(), acquiredAt: now, heartbeatAt: now, leaseMs: 60_000 };
871
+ };
872
+
873
+ assert.notEqual(await buffer.addTurnBounded(key, oldTurn), null);
874
+ await buffer.flushPendingSave({ throwOnFailure: true });
875
+ assert.equal(await claims.claimFromSessionFile(key, lease("owner-one")), "claimed");
876
+ const claimed = await claims.load(key);
877
+ assert.ok(claimed);
878
+ await claims.writeEntry(
879
+ key,
880
+ "owner-one",
881
+ { turns: [], lastExtractionAt: null, extractionCount: 0 },
882
+ claimed.entry
883
+ );
884
+ const ownerOneSnapshot = await claims.drainedTurnsForOwner(key, "owner-one");
885
+ assert.deepEqual(ownerOneSnapshot, { turns: [oldTurn], retainedTurns: [] });
886
+ assert.ok(ownerOneSnapshot);
887
+
888
+ // Simulate takeover after the old owner's fenced read but before its
889
+ // serialized live-buffer mutation. Progress is monotonic and identities
890
+ // are list-specific, so applying this snapshot cannot erase newer work.
891
+ await claims.releaseLease(key, "owner-one");
892
+ assert.equal(await claims.adopt(key, lease("owner-two")), "adopted");
893
+ assert.notEqual(await buffer.addTurnBounded(key, newerTurn), null);
894
+ await buffer.flushPendingSave({ throwOnFailure: true });
895
+ await buffer.save({ key, drained: ownerOneSnapshot });
896
+ assert.deepEqual(buffer.getTurns(key), [], "the old in-memory entry remains handed to the claim");
897
+
898
+ const afterIngress = await claims.load(key);
899
+ assert.deepEqual(afterIngress?.entry.turns, [newerTurn]);
900
+ assert.equal(afterIngress?.entry.turns[0]?.principalOverride, "new-owner-principal");
901
+ assert.equal(afterIngress?.entry.turns[0]?.writeNamespaceOverride, orchestrator.config.defaultNamespace);
902
+ } finally {
903
+ await orchestrator?.destroy().catch(() => undefined);
904
+ await cleanupDir(memoryDir);
905
+ }
906
+ });
467
907
  test("a claim decision unsettled mid-rename keeps the key reserved until it settles", async () => {
468
908
  const queue = new ExtractionQueueCoordinator(1);
469
909
  const state: StubState = {
@@ -1,4 +1,5 @@
1
- import type { BufferEntryState, BufferState, BufferSurpriseEvent, BufferTurn } from "./types.js";
1
+ import type { DrainedTurnSets } from "./buffer-turn-helpers.js";
2
+ import type { BufferEntryState, BufferState, BufferSurpriseEvent } from "./types.js";
2
3
  import type { BufferSessionSaveOutcome } from "./storage/buffer-drain-claims.js";
3
4
 
4
5
  /**
@@ -26,8 +27,8 @@ export interface BufferStorage {
26
27
  export interface BufferPersistOutcome {
27
28
  /** Sessions whose in-memory entry moved into a drain claim: the caller must evict them (a claimed session no longer lives in this process). */
28
29
  handedOver: string[];
29
- /** Per session, the turns a drain already extracted: the caller subtracts them from its in-memory copy. */
30
- drainedTurns: Map<string, BufferTurn[]>;
30
+ /** Per session, the turns a drain extracted, kept in their original lists. */
31
+ drainedTurns: Map<string, DrainedTurnSets>;
31
32
  }
32
33
 
33
34
  /**
@@ -73,17 +74,20 @@ export class BufferSessionPersistence {
73
74
  storage: BufferStorage,
74
75
  state: BufferState,
75
76
  peekEntry: (key: string) => BufferEntryState | undefined,
77
+ reconcile?: { key: string; drained: DrainedTurnSets },
76
78
  ): Promise<BufferPersistOutcome> {
79
+ const drainedTurns = new Map<string, DrainedTurnSets>(
80
+ reconcile ? [[reconcile.key, reconcile.drained]] : [],
81
+ );
77
82
  const saveSession = storage.saveBufferSession?.bind(storage);
78
83
  const deleteSession = storage.deleteBufferSession?.bind(storage);
79
84
  if (!saveSession || !deleteSession) {
80
85
  await storage.saveBuffer(state);
81
86
  this.dirty.clear();
82
87
  this.deleted.clear();
83
- return { handedOver: [], drainedTurns: new Map() };
88
+ return { handedOver: [], drainedTurns };
84
89
  }
85
90
  const handedOver: string[] = [];
86
- const drainedTurns = new Map<string, BufferTurn[]>();
87
91
  for (const key of [...this.deleted]) {
88
92
  await deleteSession(key);
89
93
  this.deleted.delete(key);
@@ -104,7 +108,12 @@ export class BufferSessionPersistence {
104
108
  if (outcome?.status === "handed_over") handedOver.push(key);
105
109
  // A drain tombstone: the turns it extracted must leave this process's
106
110
  // copy too, or the owner would extract them a second time.
107
- if (outcome?.drainedTurns && outcome.drainedTurns.length > 0) drainedTurns.set(key, outcome.drainedTurns);
111
+ if (
112
+ outcome?.drainedTurns &&
113
+ (outcome.drainedTurns.turns.length > 0 || outcome.drainedTurns.retainedTurns.length > 0)
114
+ ) {
115
+ drainedTurns.set(key, outcome.drainedTurns);
116
+ }
108
117
  }
109
118
  return { handedOver, drainedTurns };
110
119
  }
@@ -1,5 +1,11 @@
1
1
  import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
2
2
 
3
+ /** Extracted identities retain whether they came from active or retained turns. */
4
+ export interface DrainedTurnSets {
5
+ turns: BufferTurn[];
6
+ retainedTurns: BufferTurn[];
7
+ }
8
+
3
9
  export interface BufferTurnOwner {
4
10
  /**
5
11
  * Server-resolved principal that owns this session at the authenticated
package/src/buffer.ts CHANGED
@@ -300,16 +300,16 @@ export class SmartBuffer {
300
300
  this.loaded = true;
301
301
  }
302
302
 
303
- private async saveUnlocked(): Promise<void> {
304
- const { handedOver, drainedTurns } = await this.persistence.persist(this.storage, this.state, (key) => this.peekEntry(key) ?? undefined);
303
+ private async saveUnlocked(reconcile?: { key: string; drained: import("./buffer-turn-helpers.js").DrainedTurnSets }): Promise<void> {
304
+ const { handedOver, drainedTurns } = await this.persistence.persist(this.storage, this.state, (key) => this.peekEntry(key) ?? undefined, reconcile);
305
305
  // A drain tombstone: the turns the drain extracted leave this copy too,
306
306
  // so a stale owner never extracts (or persists) them a second time.
307
307
  for (const [key, drained] of drainedTurns) {
308
308
  const entry = this.peekEntry(key);
309
309
  if (!entry) continue;
310
- entry.turns = subtractBufferTurns(entry.turns, drained);
310
+ entry.turns = subtractBufferTurns(entry.turns, drained.turns);
311
311
  if (entry.retainedTurns && entry.retainedTurns.length > 0) {
312
- entry.retainedTurns = subtractBufferTurns(entry.retainedTurns, drained);
312
+ entry.retainedTurns = subtractBufferTurns(entry.retainedTurns, drained.retainedTurns);
313
313
  if (entry.retainedTurns.length === 0) delete entry.retainedTurns;
314
314
  }
315
315
  if (key === "default") this.state.turns = entry.turns;
@@ -340,8 +340,8 @@ export class SmartBuffer {
340
340
  return bufferEntryCounts(this.state);
341
341
  }
342
342
 
343
- async save(): Promise<void> {
344
- await this.enqueueMutation(async () => this.saveUnlocked());
343
+ async save(reconcile?: { key: string; drained: import("./buffer-turn-helpers.js").DrainedTurnSets }): Promise<void> {
344
+ await this.enqueueMutation(async () => this.saveUnlocked(reconcile));
345
345
  }
346
346
 
347
347
  /**
package/src/cli.ts CHANGED
@@ -4895,7 +4895,7 @@ export function registerCli(
4895
4895
  // No sidecar — attempt in-memory decryption if the secure-store is
4896
4896
  // unlocked. decryptCapsuleFileInMemory returns the plaintext gzip
4897
4897
  // bytes; we then gunzip and parse exactly like the plaintext path.
4898
- const { gunzipSync } = await import("node:zlib");
4898
+ const { gunzipCapsuleArchive } = await import("./transfer/capsule-gunzip.js");
4899
4899
  const { parseExportBundle } = await import("./transfer/types.js");
4900
4900
  let decryptedBuf: Buffer;
4901
4901
  try {
@@ -4917,7 +4917,7 @@ export function registerCli(
4917
4917
  process.exitCode = 1;
4918
4918
  return;
4919
4919
  }
4920
- const json = gunzipSync(decryptedBuf).toString("utf-8");
4920
+ const json = gunzipCapsuleArchive(decryptedBuf, "capsule inspect", archivePath);
4921
4921
  const parsed2 = parseExportBundle(JSON.parse(json));
4922
4922
  if (parsed2.capsuleVersion !== 2) {
4923
4923
  process.stderr.write(
@@ -4929,10 +4929,10 @@ export function registerCli(
4929
4929
  manifest = (parsed2.bundle as { manifest: Record<string, unknown> }).manifest;
4930
4930
  } else {
4931
4931
  const { readFile } = await import("node:fs/promises");
4932
- const { gunzipSync } = await import("node:zlib");
4932
+ const { gunzipCapsuleArchive } = await import("./transfer/capsule-gunzip.js");
4933
4933
  const { parseExportBundle } = await import("./transfer/types.js");
4934
4934
  const buf = await readFile(archivePath);
4935
- const json = gunzipSync(buf).toString("utf-8");
4935
+ const json = gunzipCapsuleArchive(buf, "capsule inspect", archivePath);
4936
4936
  const parsed2 = parseExportBundle(JSON.parse(json));
4937
4937
  if (parsed2.capsuleVersion !== 2) {
4938
4938
  process.stderr.write(