@remnic/core 10.0.84 → 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 (399) 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 +146 -144
  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.js +78 -77
  25. package/dist/access-recall-concurrency.js +67 -66
  26. package/dist/access-recall-response.js +67 -66
  27. package/dist/access-recall-surface.js +67 -66
  28. package/dist/access-schema.js +4 -4
  29. package/dist/access-service.js +67 -66
  30. package/dist/access-surface-catalog.js +69 -68
  31. package/dist/access-surface-catalog.js.map +1 -1
  32. package/dist/active-recall.js +31 -31
  33. package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
  34. package/dist/briefing.js +25 -25
  35. package/dist/buffer-persistence.d.ts +8 -4
  36. package/dist/buffer-persistence.d.ts.map +1 -1
  37. package/dist/buffer-persistence.js +1 -1
  38. package/dist/buffer-turn-helpers.d.ts +5 -0
  39. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  40. package/dist/buffer-turn-helpers.js +1 -1
  41. package/dist/buffer.d.ts +4 -1
  42. package/dist/buffer.d.ts.map +1 -1
  43. package/dist/buffer.js +9 -9
  44. package/dist/calibration.js +2 -2
  45. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  46. package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
  47. package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
  48. package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
  49. package/dist/causal-behavior.js +2 -2
  50. package/dist/causal-chain.js +2 -2
  51. package/dist/causal-consolidation.js +32 -32
  52. package/dist/causal-retrieval.js +2 -2
  53. package/dist/causal-trajectory-graph.js +2 -2
  54. package/dist/causal-trajectory.js +1 -1
  55. package/dist/{chunk-T6T2RMWW.js → chunk-23B4HLWG.js} +5 -5
  56. package/dist/{chunk-LECSS7EE.js → chunk-2ASEDFOA.js} +2 -2
  57. package/dist/{chunk-TPX7XXY4.js → chunk-2JAT4S7N.js} +2 -2
  58. package/dist/{chunk-XRBHIR7Q.js → chunk-2URKYLMK.js} +2 -2
  59. package/dist/{chunk-IN4OFAHX.js → chunk-2XYDL2FG.js} +2 -2
  60. package/dist/{chunk-HQGATKG2.js → chunk-3KGCHTTK.js} +2 -2
  61. package/dist/{chunk-YAUHSNPB.js → chunk-3LHPTBWG.js} +2 -2
  62. package/dist/{chunk-7PMOCQOL.js → chunk-3ODZLCHC.js} +192 -63
  63. package/dist/chunk-3ODZLCHC.js.map +1 -0
  64. package/dist/{chunk-XT45WCKX.js → chunk-3VU2FDTU.js} +8 -8
  65. package/dist/{chunk-CIIHIAJ2.js → chunk-433EXTC4.js} +3 -3
  66. package/dist/{chunk-QZSIDSB2.js → chunk-44FQCC5D.js} +2 -2
  67. package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
  68. package/dist/{chunk-54FDG7K7.js → chunk-4DPLCYEK.js} +2 -2
  69. package/dist/{chunk-KO3YBHOV.js → chunk-4IWEPQEV.js} +2 -2
  70. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  71. package/dist/{chunk-K6RTQZA4.js → chunk-6DANGCMO.js} +2 -2
  72. package/dist/{chunk-2IW5PT7U.js → chunk-6KFFRMPQ.js} +2 -2
  73. package/dist/{chunk-MSIRVXQ3.js → chunk-73YVMZ7D.js} +4 -4
  74. package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
  75. package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
  76. package/dist/{chunk-YOEYL6CH.js → chunk-AP3TSV6S.js} +2 -2
  77. package/dist/{chunk-GQZSGXR6.js → chunk-BATPQNRG.js} +2 -2
  78. package/dist/{chunk-D5SSBCYY.js → chunk-BLGQUZFB.js} +2 -2
  79. package/dist/{chunk-BDN3TDGV.js → chunk-BLMTOIBK.js} +2 -2
  80. package/dist/{chunk-B74NPUHT.js → chunk-BMIM6ESC.js} +43 -43
  81. package/dist/chunk-BMIM6ESC.js.map +1 -0
  82. package/dist/{chunk-R45L4TV6.js → chunk-BUKK5SWA.js} +6 -6
  83. package/dist/{chunk-TFLPDNQV.js → chunk-CCU7URRP.js} +2 -2
  84. package/dist/{chunk-2XJZLSXE.js → chunk-CMRHXPLQ.js} +20 -20
  85. package/dist/{chunk-BK4FBHZ3.js → chunk-D6THW7PA.js} +3 -3
  86. package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
  87. package/dist/{chunk-VKDAJJQP.js → chunk-DI5ET2IE.js} +5 -5
  88. package/dist/{chunk-QZBTIYKD.js → chunk-DM4RRTN7.js} +3 -3
  89. package/dist/{chunk-EBOHFZI4.js → chunk-E5QEF3JO.js} +2 -2
  90. package/dist/{chunk-K3B72DKJ.js → chunk-E7V5AK7I.js} +2 -2
  91. package/dist/{chunk-WAD32CZG.js → chunk-ELWI2R6H.js} +26 -13
  92. package/dist/chunk-ELWI2R6H.js.map +1 -0
  93. package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
  94. package/dist/{chunk-HPFVZPRH.js → chunk-FK4WC6VK.js} +3 -3
  95. package/dist/{chunk-VLMBSGQF.js → chunk-FRM4UTDR.js} +6 -6
  96. package/dist/{chunk-LTDLJCMQ.js → chunk-FZPDRCOB.js} +2 -2
  97. package/dist/{chunk-ZJJFUEPO.js → chunk-GBOYOL5L.js} +2 -2
  98. package/dist/{chunk-YUTG35M4.js → chunk-GJVGFKOU.js} +2 -2
  99. package/dist/{chunk-77R7BQZ2.js → chunk-GKYIRTNX.js} +33 -11
  100. package/dist/{chunk-77R7BQZ2.js.map → chunk-GKYIRTNX.js.map} +1 -1
  101. package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
  102. package/dist/{chunk-HWHNQVRV.js → chunk-GYNRNKMW.js} +2 -2
  103. package/dist/{chunk-LU66I56Q.js → chunk-H34INI3P.js} +29 -3
  104. package/dist/chunk-H34INI3P.js.map +1 -0
  105. package/dist/{chunk-HLKUZW42.js → chunk-H4D5YURJ.js} +80 -94
  106. package/dist/chunk-H4D5YURJ.js.map +1 -0
  107. package/dist/{chunk-6LCWKS62.js → chunk-HVFX2N2G.js} +60 -59
  108. package/dist/chunk-HVFX2N2G.js.map +1 -0
  109. package/dist/chunk-IGLJNHOX.js +62 -0
  110. package/dist/chunk-IGLJNHOX.js.map +1 -0
  111. package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
  112. package/dist/{chunk-YK7GOPIE.js → chunk-JFH6JAGY.js} +6 -4
  113. package/dist/chunk-JFH6JAGY.js.map +1 -0
  114. package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
  115. package/dist/{chunk-FJ2VJTGA.js → chunk-KHZVAISH.js} +2 -2
  116. package/dist/{chunk-WHQXWFH7.js → chunk-LT3CMO65.js} +2 -2
  117. package/dist/{chunk-NUSAAN75.js → chunk-MHSBOEH7.js} +2 -2
  118. package/dist/{chunk-KJCL2FZO.js → chunk-MNYQVYJ4.js} +2 -2
  119. package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
  120. package/dist/{chunk-FVRTCYZ3.js → chunk-MVOJDLXS.js} +2 -2
  121. package/dist/{chunk-KESJ5XX3.js → chunk-MYCCO6AA.js} +2 -2
  122. package/dist/{chunk-66JOLMWS.js → chunk-MZCGHPIJ.js} +2 -2
  123. package/dist/{chunk-B4ZU34JQ.js → chunk-N6II66E5.js} +3 -3
  124. package/dist/{chunk-BK7AJUWB.js → chunk-NIV7RPWQ.js} +6 -6
  125. package/dist/{chunk-TLHF75OR.js → chunk-O55AGDTI.js} +10 -10
  126. package/dist/{chunk-GGJFRVRD.js → chunk-OMYRPMQC.js} +2 -2
  127. package/dist/{chunk-3JPTUCD7.js → chunk-ONN2E5QD.js} +32 -23
  128. package/dist/chunk-ONN2E5QD.js.map +1 -0
  129. package/dist/{chunk-XHGYZRVW.js → chunk-OSTFDWZJ.js} +4 -4
  130. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  131. package/dist/{chunk-WC475JOG.js → chunk-P225W4RQ.js} +6 -6
  132. package/dist/{chunk-TFA6HPOA.js → chunk-QCP75JGO.js} +5 -5
  133. package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
  134. package/dist/chunk-QV2VZKD5.js +31 -0
  135. package/dist/chunk-QV2VZKD5.js.map +1 -0
  136. package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
  137. package/dist/{chunk-VUROWXFL.js → chunk-RNGDKFY2.js} +2 -2
  138. package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
  139. package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
  140. package/dist/{chunk-XYP5WBDO.js → chunk-SGHV6LZ7.js} +13 -13
  141. package/dist/chunk-SGHV6LZ7.js.map +1 -0
  142. package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
  143. package/dist/{chunk-T77AX6DG.js → chunk-TQVECGUQ.js} +4 -4
  144. package/dist/{chunk-OQSSQOKQ.js → chunk-TV65NR7L.js} +2 -2
  145. package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
  146. package/dist/chunk-UDKWFDV2.js.map +1 -0
  147. package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
  148. package/dist/{chunk-4EACCIPO.js → chunk-UHPHH74F.js} +2 -2
  149. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  150. package/dist/{chunk-6QYPL6NI.js → chunk-V45TEVDA.js} +3 -3
  151. package/dist/{chunk-GCAQ3VUJ.js → chunk-VCVEMQJK.js} +2 -2
  152. package/dist/{chunk-6L63IN3M.js → chunk-VEFGHRKV.js} +1 -1
  153. package/dist/{chunk-E33UJHY3.js → chunk-WFDWLWDK.js} +2 -2
  154. package/dist/{chunk-4KLQI3L2.js → chunk-WLNWFHX3.js} +1 -1
  155. package/dist/chunk-WLNWFHX3.js.map +1 -0
  156. package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
  157. package/dist/{chunk-MPGLM2VU.js → chunk-X7REO4CH.js} +2 -2
  158. package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
  159. package/dist/chunk-XO23OKMK.js.map +1 -0
  160. package/dist/{chunk-Z3DDQXM4.js → chunk-XSFQN7TP.js} +35 -36
  161. package/dist/chunk-XSFQN7TP.js.map +1 -0
  162. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  163. package/dist/{chunk-WV254W4V.js → chunk-Y3IYKNBY.js} +2 -2
  164. package/dist/{chunk-FUOAIJKQ.js → chunk-Y43X3E24.js} +2 -2
  165. package/dist/{chunk-3T3VHXUH.js → chunk-YAAKVTZA.js} +2 -2
  166. package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
  167. package/dist/{chunk-65SKM4BF.js → chunk-YNBMBL4C.js} +2 -2
  168. package/dist/{chunk-PJYK7HH2.js → chunk-ZWTHVUT5.js} +2 -2
  169. package/dist/cli.js +110 -108
  170. package/dist/coding/export-okf-codegraph.js +25 -25
  171. package/dist/coding/pre-action-gate.js +2 -2
  172. package/dist/compounding/engine.js +27 -27
  173. package/dist/config.js +28 -28
  174. package/dist/connectors/codex-materialize-runner.js +25 -25
  175. package/dist/connectors/index.js +25 -25
  176. package/dist/contradiction/index.js +4 -4
  177. package/dist/convergence-refresh.js +27 -27
  178. package/dist/conversation-index/backend.js +2 -2
  179. package/dist/corpus-watermark.js +27 -27
  180. package/dist/deep-recall-http-glue.js +5 -5
  181. package/dist/entity-retrieval.js +25 -25
  182. package/dist/event-order-recall.js +2 -2
  183. package/dist/explicit-capture.js +5 -5
  184. package/dist/external-wiki-access.js +68 -67
  185. package/dist/extraction-evidence-class.js +2 -2
  186. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  187. package/dist/extraction-quote-grounding.js +1 -1
  188. package/dist/extraction-source-grounding-rules.js +2 -2
  189. package/dist/extraction-source-grounding.js +5 -5
  190. package/dist/extraction-span-materialize.js +2 -2
  191. package/dist/extraction.js +40 -40
  192. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  193. package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
  194. package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
  195. package/dist/graph-jsonl.js +3 -3
  196. package/dist/graph-snapshot.js +3 -3
  197. package/dist/graph.js +2 -2
  198. package/dist/harmonic-retrieval.js +25 -25
  199. package/dist/index.js +195 -193
  200. package/dist/lcm/engine.js +2 -2
  201. package/dist/lcm/index.js +2 -2
  202. package/dist/local-llm.js +2 -2
  203. package/dist/location/index.js +28 -28
  204. package/dist/maintenance/memory-governance.js +25 -25
  205. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +25 -25
  206. package/dist/maintenance/rebuild-memory-projection-runner.js +27 -27
  207. package/dist/maintenance/rebuild-memory-projection.js +26 -26
  208. package/dist/memory-browse-operations.js +68 -67
  209. package/dist/memory-subject.js +2 -2
  210. package/dist/namespaces/migrate.js +26 -26
  211. package/dist/namespaces/search.d.ts +8 -0
  212. package/dist/namespaces/search.d.ts.map +1 -1
  213. package/dist/namespaces/search.js +10 -6
  214. package/dist/namespaces/storage.js +25 -25
  215. package/dist/operator-doctor-corpus.js +28 -28
  216. package/dist/operator-toolkit.js +39 -39
  217. package/dist/orchestration/backlog-scheduler.d.ts +3 -2
  218. package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
  219. package/dist/orchestration/backlog-scheduler.js +157 -155
  220. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  221. package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
  222. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
  223. package/dist/orchestration/buffer-drain-runner.js +147 -145
  224. package/dist/orchestration/extraction-persist.d.ts.map +1 -1
  225. package/dist/orchestration/fact-dedup-window.d.ts +1 -4
  226. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  227. package/dist/orchestration/maintenance.js +39 -39
  228. package/dist/orchestrator.js +146 -144
  229. package/dist/provenance.js +2 -2
  230. package/dist/recall-navigation-operations.js +68 -67
  231. package/dist/recall-navigation.js +67 -66
  232. package/dist/reconcile/manifest.js +2 -2
  233. package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
  234. package/dist/resolution-23ZBDM6A.js.map +1 -0
  235. package/dist/resume-bundles.js +29 -29
  236. package/dist/salvage-envelope.js +4 -4
  237. package/dist/search/factory.js +5 -5
  238. package/dist/search/index.js +9 -9
  239. package/dist/semantic-consolidation.js +26 -26
  240. package/dist/semantic-rule-promotion.js +25 -25
  241. package/dist/semantic-rule-verifier.js +25 -25
  242. package/dist/source-agent-qualifier.js +31 -31
  243. package/dist/standup/build.js +25 -25
  244. package/dist/storage/buffer-drain-claims.d.ts +18 -6
  245. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  246. package/dist/storage/buffer-drain-claims.js +2 -2
  247. package/dist/storage/buffer-session-files.js +2 -2
  248. package/dist/storage/content-hash-index.d.ts +8 -0
  249. package/dist/storage/content-hash-index.d.ts.map +1 -1
  250. package/dist/storage.d.ts +10 -1
  251. package/dist/storage.d.ts.map +1 -1
  252. package/dist/storage.js +24 -24
  253. package/dist/subject-config.js +3 -3
  254. package/dist/summarizer.js +3 -3
  255. package/dist/support-passport/index.js +70 -69
  256. package/dist/transfer/autodetect.js +1 -1
  257. package/dist/transfer/backup.js +2 -2
  258. package/dist/transfer/capsule-export.js +3 -3
  259. package/dist/transfer/capsule-gunzip.d.ts +14 -0
  260. package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
  261. package/dist/transfer/capsule-import.d.ts.map +1 -1
  262. package/dist/transfer/capsule-import.js +3 -2
  263. package/dist/transfer/capsule-merge.d.ts.map +1 -1
  264. package/dist/transfer/export-okf.js +25 -25
  265. package/dist/verified-recall.js +25 -25
  266. package/dist/who-knows-cli.js +26 -26
  267. package/dist/who-knows.js +25 -25
  268. package/dist/write-envelope.js +3 -3
  269. package/package.json +2 -2
  270. package/src/access-mcp-frames.test.ts +47 -0
  271. package/src/access-mcp-frames.ts +74 -0
  272. package/src/access-mcp.test.ts +146 -0
  273. package/src/access-mcp.ts +30 -30
  274. package/src/access-observe-write-surface.ts +4 -4
  275. package/src/backlog-scheduler.test.ts +443 -3
  276. package/src/buffer-persistence.ts +15 -6
  277. package/src/buffer-turn-helpers.ts +6 -0
  278. package/src/buffer.ts +6 -6
  279. package/src/cli.ts +4 -4
  280. package/src/extraction-quote-grounding.test.ts +49 -0
  281. package/src/extraction-quote-grounding.ts +50 -12
  282. package/src/fact-dedup-window.test.ts +103 -20
  283. package/src/namespaces/search.test.ts +339 -0
  284. package/src/namespaces/search.ts +54 -1
  285. package/src/orchestration/backlog-scheduler.ts +13 -12
  286. package/src/orchestration/buffer-drain-runner.ts +40 -16
  287. package/src/orchestration/extraction-persist.ts +2 -10
  288. package/src/orchestration/fact-dedup-window.ts +5 -12
  289. package/src/storage/buffer-drain-claims.test.ts +330 -6
  290. package/src/storage/buffer-drain-claims.ts +226 -80
  291. package/src/storage/buffer-session-files.ts +2 -2
  292. package/src/storage/content-hash-index.ts +9 -0
  293. package/src/storage.ts +15 -0
  294. package/src/transfer/capsule-gunzip.ts +39 -0
  295. package/src/transfer/capsule-import.test.ts +73 -0
  296. package/src/transfer/capsule-import.ts +2 -2
  297. package/src/transfer/capsule-merge.test.ts +34 -0
  298. package/src/transfer/capsule-merge.ts +2 -10
  299. package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
  300. package/dist/chunk-3JPTUCD7.js.map +0 -1
  301. package/dist/chunk-4KLQI3L2.js.map +0 -1
  302. package/dist/chunk-6LCWKS62.js.map +0 -1
  303. package/dist/chunk-7PMOCQOL.js.map +0 -1
  304. package/dist/chunk-B74NPUHT.js.map +0 -1
  305. package/dist/chunk-HLKUZW42.js.map +0 -1
  306. package/dist/chunk-KQUSP6LB.js.map +0 -1
  307. package/dist/chunk-LU66I56Q.js.map +0 -1
  308. package/dist/chunk-MPDLLLOG.js.map +0 -1
  309. package/dist/chunk-WAD32CZG.js.map +0 -1
  310. package/dist/chunk-XYP5WBDO.js.map +0 -1
  311. package/dist/chunk-YK7GOPIE.js.map +0 -1
  312. package/dist/chunk-Z3DDQXM4.js.map +0 -1
  313. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
  314. /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
  315. /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  316. /package/dist/{resolution-BNWE5Y25.js.map → capsule-gunzip-KGBDGF7P.js.map} +0 -0
  317. /package/dist/{chunk-T6T2RMWW.js.map → chunk-23B4HLWG.js.map} +0 -0
  318. /package/dist/{chunk-LECSS7EE.js.map → chunk-2ASEDFOA.js.map} +0 -0
  319. /package/dist/{chunk-TPX7XXY4.js.map → chunk-2JAT4S7N.js.map} +0 -0
  320. /package/dist/{chunk-XRBHIR7Q.js.map → chunk-2URKYLMK.js.map} +0 -0
  321. /package/dist/{chunk-IN4OFAHX.js.map → chunk-2XYDL2FG.js.map} +0 -0
  322. /package/dist/{chunk-HQGATKG2.js.map → chunk-3KGCHTTK.js.map} +0 -0
  323. /package/dist/{chunk-YAUHSNPB.js.map → chunk-3LHPTBWG.js.map} +0 -0
  324. /package/dist/{chunk-XT45WCKX.js.map → chunk-3VU2FDTU.js.map} +0 -0
  325. /package/dist/{chunk-CIIHIAJ2.js.map → chunk-433EXTC4.js.map} +0 -0
  326. /package/dist/{chunk-QZSIDSB2.js.map → chunk-44FQCC5D.js.map} +0 -0
  327. /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
  328. /package/dist/{chunk-54FDG7K7.js.map → chunk-4DPLCYEK.js.map} +0 -0
  329. /package/dist/{chunk-KO3YBHOV.js.map → chunk-4IWEPQEV.js.map} +0 -0
  330. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  331. /package/dist/{chunk-K6RTQZA4.js.map → chunk-6DANGCMO.js.map} +0 -0
  332. /package/dist/{chunk-2IW5PT7U.js.map → chunk-6KFFRMPQ.js.map} +0 -0
  333. /package/dist/{chunk-MSIRVXQ3.js.map → chunk-73YVMZ7D.js.map} +0 -0
  334. /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
  335. /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
  336. /package/dist/{chunk-YOEYL6CH.js.map → chunk-AP3TSV6S.js.map} +0 -0
  337. /package/dist/{chunk-GQZSGXR6.js.map → chunk-BATPQNRG.js.map} +0 -0
  338. /package/dist/{chunk-D5SSBCYY.js.map → chunk-BLGQUZFB.js.map} +0 -0
  339. /package/dist/{chunk-BDN3TDGV.js.map → chunk-BLMTOIBK.js.map} +0 -0
  340. /package/dist/{chunk-R45L4TV6.js.map → chunk-BUKK5SWA.js.map} +0 -0
  341. /package/dist/{chunk-TFLPDNQV.js.map → chunk-CCU7URRP.js.map} +0 -0
  342. /package/dist/{chunk-2XJZLSXE.js.map → chunk-CMRHXPLQ.js.map} +0 -0
  343. /package/dist/{chunk-BK4FBHZ3.js.map → chunk-D6THW7PA.js.map} +0 -0
  344. /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
  345. /package/dist/{chunk-VKDAJJQP.js.map → chunk-DI5ET2IE.js.map} +0 -0
  346. /package/dist/{chunk-QZBTIYKD.js.map → chunk-DM4RRTN7.js.map} +0 -0
  347. /package/dist/{chunk-EBOHFZI4.js.map → chunk-E5QEF3JO.js.map} +0 -0
  348. /package/dist/{chunk-K3B72DKJ.js.map → chunk-E7V5AK7I.js.map} +0 -0
  349. /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
  350. /package/dist/{chunk-HPFVZPRH.js.map → chunk-FK4WC6VK.js.map} +0 -0
  351. /package/dist/{chunk-VLMBSGQF.js.map → chunk-FRM4UTDR.js.map} +0 -0
  352. /package/dist/{chunk-LTDLJCMQ.js.map → chunk-FZPDRCOB.js.map} +0 -0
  353. /package/dist/{chunk-ZJJFUEPO.js.map → chunk-GBOYOL5L.js.map} +0 -0
  354. /package/dist/{chunk-YUTG35M4.js.map → chunk-GJVGFKOU.js.map} +0 -0
  355. /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
  356. /package/dist/{chunk-HWHNQVRV.js.map → chunk-GYNRNKMW.js.map} +0 -0
  357. /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
  358. /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
  359. /package/dist/{chunk-FJ2VJTGA.js.map → chunk-KHZVAISH.js.map} +0 -0
  360. /package/dist/{chunk-WHQXWFH7.js.map → chunk-LT3CMO65.js.map} +0 -0
  361. /package/dist/{chunk-NUSAAN75.js.map → chunk-MHSBOEH7.js.map} +0 -0
  362. /package/dist/{chunk-KJCL2FZO.js.map → chunk-MNYQVYJ4.js.map} +0 -0
  363. /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
  364. /package/dist/{chunk-FVRTCYZ3.js.map → chunk-MVOJDLXS.js.map} +0 -0
  365. /package/dist/{chunk-KESJ5XX3.js.map → chunk-MYCCO6AA.js.map} +0 -0
  366. /package/dist/{chunk-66JOLMWS.js.map → chunk-MZCGHPIJ.js.map} +0 -0
  367. /package/dist/{chunk-B4ZU34JQ.js.map → chunk-N6II66E5.js.map} +0 -0
  368. /package/dist/{chunk-BK7AJUWB.js.map → chunk-NIV7RPWQ.js.map} +0 -0
  369. /package/dist/{chunk-TLHF75OR.js.map → chunk-O55AGDTI.js.map} +0 -0
  370. /package/dist/{chunk-GGJFRVRD.js.map → chunk-OMYRPMQC.js.map} +0 -0
  371. /package/dist/{chunk-XHGYZRVW.js.map → chunk-OSTFDWZJ.js.map} +0 -0
  372. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  373. /package/dist/{chunk-WC475JOG.js.map → chunk-P225W4RQ.js.map} +0 -0
  374. /package/dist/{chunk-TFA6HPOA.js.map → chunk-QCP75JGO.js.map} +0 -0
  375. /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
  376. /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
  377. /package/dist/{chunk-VUROWXFL.js.map → chunk-RNGDKFY2.js.map} +0 -0
  378. /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
  379. /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
  380. /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
  381. /package/dist/{chunk-T77AX6DG.js.map → chunk-TQVECGUQ.js.map} +0 -0
  382. /package/dist/{chunk-OQSSQOKQ.js.map → chunk-TV65NR7L.js.map} +0 -0
  383. /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
  384. /package/dist/{chunk-4EACCIPO.js.map → chunk-UHPHH74F.js.map} +0 -0
  385. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  386. /package/dist/{chunk-6QYPL6NI.js.map → chunk-V45TEVDA.js.map} +0 -0
  387. /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-VCVEMQJK.js.map} +0 -0
  388. /package/dist/{chunk-6L63IN3M.js.map → chunk-VEFGHRKV.js.map} +0 -0
  389. /package/dist/{chunk-E33UJHY3.js.map → chunk-WFDWLWDK.js.map} +0 -0
  390. /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
  391. /package/dist/{chunk-MPGLM2VU.js.map → chunk-X7REO4CH.js.map} +0 -0
  392. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  393. /package/dist/{chunk-WV254W4V.js.map → chunk-Y3IYKNBY.js.map} +0 -0
  394. /package/dist/{chunk-FUOAIJKQ.js.map → chunk-Y43X3E24.js.map} +0 -0
  395. /package/dist/{chunk-3T3VHXUH.js.map → chunk-YAAKVTZA.js.map} +0 -0
  396. /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
  397. /package/dist/{chunk-65SKM4BF.js.map → chunk-YNBMBL4C.js.map} +0 -0
  398. /package/dist/{chunk-PJYK7HH2.js.map → chunk-ZWTHVUT5.js.map} +0 -0
  399. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -1,6 +1,5 @@
1
1
  import { lstat, mkdir, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { gunzipSync } from "node:zlib";
4
3
  import { bumpMemoryCorpusVersionForDir } from "../memory-corpus-version.js";
5
4
  import {
6
5
  HistoricalEntityCanonicalIdCache,
@@ -29,6 +28,7 @@ import {
29
28
  type ExportManifestV2,
30
29
  type ExportMemoryRecordV1,
31
30
  } from "./types.js";
31
+ import { gunzipCapsuleArchive } from "./capsule-gunzip.js";
32
32
 
33
33
  /**
34
34
  * Three-way conflict-resolution mode for {@link mergeCapsule}.
@@ -190,15 +190,7 @@ export async function mergeCapsule(
190
190
  // ---------------------------------------------------------------------------
191
191
 
192
192
  const raw = await readFile(archiveAbs);
193
- let json: string;
194
- try {
195
- json = gunzipSync(raw).toString("utf-8");
196
- } catch (cause) {
197
- throw new Error(
198
- `mergeCapsule: archive is not a valid gzip file: ${archiveAbs}`,
199
- { cause: cause as Error },
200
- );
201
- }
193
+ const json = gunzipCapsuleArchive(raw, "mergeCapsule", archiveAbs);
202
194
 
203
195
  let parsedJson: unknown;
204
196
  try {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/transfer/capsule-merge.ts"],"sourcesContent":["import { lstat, mkdir, readFile, realpath, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { gunzipSync } from \"node:zlib\";\nimport { bumpMemoryCorpusVersionForDir } from \"../memory-corpus-version.js\";\nimport {\n HistoricalEntityCanonicalIdCache,\n canonicalizeEntityRefFrontmatter,\n readEntityRefFromFrontmatter,\n classifyEntityRefWritePath,\n repairContentAfterJournalMove,\n requestEntityCanonicalIdReconcile,\n} from \"../storage/entity-canonical-id-references.js\";\nimport { withRawEntityPageMutation } from \"../storage/entity-canonical-id-lock.js\";\nimport {\n createVersion,\n type VersioningConfig,\n type VersioningLogger,\n} from \"../page-versioning.js\";\nimport {\n assertIsDirectoryNotSymlink,\n assertRealpathInsideRoot,\n fromPosixRelPath,\n isPathInsideRoot,\n sha256String,\n} from \"./fs-utils.js\";\nimport {\n parseExportBundle,\n type CapsuleBlock,\n type ExportManifestV2,\n type ExportMemoryRecordV1,\n} from \"./types.js\";\n\n/**\n * Three-way conflict-resolution mode for {@link mergeCapsule}.\n *\n * A \"conflict\" is defined as: the same memory-file path exists in both the\n * source archive and the target directory AND the content hash of the local\n * file differs from the archive's manifest entry for that path.\n *\n * Files that exist only in the archive (no local counterpart) are always\n * written regardless of mode — there is no conflict to resolve.\n *\n * Files that are byte-identical (same content hash in both locations) are\n * recorded as {@link MergeCapsuleResult.skipped} with reason `\"identical\"` and\n * are never re-written regardless of mode; this is a no-op optimisation rather\n * than a conflict.\n *\n * - `\"skip-conflicts\"` (default) — log the conflict, skip the conflicting\n * archive entries, but continue importing non-conflicting entries. The\n * resulting merge is the union of:\n * - all non-conflicting archive files (written to target)\n * - all pre-existing local files (left unchanged)\n *\n * - `\"prefer-source\"` — for conflicting files, snapshot the local content via\n * page-versioning (gotcha #54: snapshot before overwrite) then overwrite\n * with the archive content.\n *\n * - `\"prefer-local\"` — for conflicting files, keep the local content; the\n * archive entry is skipped.\n */\nexport type MergeCapsuleConflictMode =\n | \"skip-conflicts\"\n | \"prefer-source\"\n | \"prefer-local\";\n\n/**\n * Options accepted by {@link mergeCapsule}.\n *\n * `sourceArchive` — absolute or cwd-relative path to a `.capsule.json.gz`\n * archive produced by `exportCapsule`. Must be a V2 bundle.\n *\n * `targetRoot` — absolute or cwd-relative path to the memory directory that\n * receives the merged records. Must be an existing, non-symlink directory.\n *\n * `conflictMode` — see {@link MergeCapsuleConflictMode}. Defaults to\n * `\"skip-conflicts\"`.\n *\n * `versioning` — optional page-versioning config forwarded to\n * {@link createVersion} in `\"prefer-source\"` mode. When omitted or disabled,\n * overwrites proceed without snapshotting (not recommended for production).\n *\n * `log` — optional logger forwarded to {@link createVersion}.\n */\nexport interface MergeCapsuleOptions {\n sourceArchive: string;\n targetRoot: string;\n conflictMode?: MergeCapsuleConflictMode;\n versioning?: VersioningConfig;\n log?: VersioningLogger;\n}\n\nexport interface MergeCapsuleWrittenRecord {\n /** Capsule-relative posix path. */\n sourcePath: string;\n /** Memory-dir-relative posix path written on disk. */\n targetPath: string;\n /** Whether a page-versioning snapshot was taken before overwriting. */\n snapshotted: boolean;\n}\n\nexport interface MergeCapsuleSkippedRecord {\n /** Capsule-relative posix path. */\n path: string;\n /**\n * Why the archive entry was not written.\n *\n * `\"conflict\"` — the entry existed locally with different content and the\n * active mode did not resolve the conflict with a write (`\"skip-conflicts\"` /\n * `\"prefer-local\"`).\n *\n * `\"identical\"` — the entry's content hash matches what is already on disk;\n * no write is needed.\n */\n reason: \"conflict\" | \"identical\";\n}\n\nexport interface MergeCapsuleConflictRecord {\n /** Capsule-relative posix path of the conflicting entry. */\n path: string;\n /** SHA-256 of the archive's copy. */\n archiveSha256: string;\n /** SHA-256 of the local copy. */\n localSha256: string;\n}\n\nexport interface MergeCapsuleResult {\n /** Records that were written to the target directory. */\n merged: MergeCapsuleWrittenRecord[];\n /**\n * Records that were NOT written (conflict skipped, or byte-identical).\n * Includes conflicts that were resolved by `\"prefer-local\"`.\n */\n skipped: MergeCapsuleSkippedRecord[];\n /**\n * Metadata about every detected conflict, regardless of which mode resolved\n * it. Callers can use this to report \"N conflicts encountered; M overwritten\".\n */\n conflicts: MergeCapsuleConflictRecord[];\n /** The manifest decoded from the archive. */\n manifest: ExportManifestV2;\n}\n\n// ---------------------------------------------------------------------------\n// Main entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Merge a V2 capsule archive into an existing memory directory using\n * three-way conflict semantics.\n *\n * Sequence:\n * 1. Read + gunzip + JSON.parse the archive.\n * 2. Validate through `parseExportBundle` (V1 rejected).\n * 3. Verify every record's content sha256 against the manifest.\n * Any mismatch aborts BEFORE any file is written (gotcha #25).\n * 4. Classify each record as: new (no local copy), identical (same hash),\n * or conflicting (different hash).\n * 5. Apply the selected {@link MergeCapsuleConflictMode} to conflicting\n * entries; always write new entries; always skip identical entries.\n *\n * Determinism: `merged`, `skipped`, and `conflicts` are all returned sorted\n * by `path`/`sourcePath` so callers get stable output regardless of bundle\n * order.\n */\nexport async function mergeCapsule(\n opts: MergeCapsuleOptions,\n): Promise<MergeCapsuleResult> {\n const archiveAbs = path.resolve(opts.sourceArchive);\n const rootAbs = path.resolve(opts.targetRoot);\n\n await assertIsDirectoryNotSymlink(rootAbs, \"mergeCapsule\", \"targetRoot\");\n\n const conflictMode: MergeCapsuleConflictMode =\n opts.conflictMode ?? \"skip-conflicts\";\n\n // Rule 51: reject invalid conflictMode values up-front before any I/O.\n if (\n conflictMode !== \"skip-conflicts\" &&\n conflictMode !== \"prefer-source\" &&\n conflictMode !== \"prefer-local\"\n ) {\n throw new Error(\n `mergeCapsule: unknown conflictMode ${JSON.stringify(conflictMode)}; ` +\n `expected \"skip-conflicts\", \"prefer-source\", or \"prefer-local\"`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Parse + validate archive\n // ---------------------------------------------------------------------------\n\n const raw = await readFile(archiveAbs);\n let json: string;\n try {\n json = gunzipSync(raw).toString(\"utf-8\");\n } catch (cause) {\n throw new Error(\n `mergeCapsule: archive is not a valid gzip file: ${archiveAbs}`,\n { cause: cause as Error },\n );\n }\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(json);\n } catch (cause) {\n throw new Error(\n `mergeCapsule: archive is not valid JSON after gunzip: ${archiveAbs}`,\n { cause: cause as Error },\n );\n }\n\n const parsed = parseExportBundle(parsedJson);\n if (parsed.capsuleVersion !== 2) {\n throw new Error(\n \"mergeCapsule: archive is V1; only V2 capsule archives are supported\",\n );\n }\n\n const bundle = parsed.bundle as {\n manifest: ExportManifestV2;\n records: ExportMemoryRecordV1[];\n };\n const manifest = bundle.manifest;\n const capsule = manifest.capsule;\n\n // Build path → manifest entry index for O(1) checksum lookup.\n const manifestIndex = new Map<string, ExportManifestV2[\"files\"][number]>();\n for (const f of manifest.files) {\n manifestIndex.set(f.path, f);\n }\n if (manifestIndex.size !== manifest.files.length) {\n throw new Error(\"mergeCapsule: manifest contains duplicate file paths\");\n }\n\n const recordPaths = new Set<string>();\n for (const rec of bundle.records) {\n if (recordPaths.has(rec.path)) {\n throw new Error(\n `mergeCapsule: bundle contains duplicate record path: ${rec.path}`,\n );\n }\n recordPaths.add(rec.path);\n }\n\n // ---------------------------------------------------------------------------\n // Phase 1: verify checksums + validate paths before ANY filesystem mutation.\n // (gotcha #25: don't destroy old state before confirming new state succeeds)\n // ---------------------------------------------------------------------------\n\n const rootReal = await realpath(rootAbs).catch(() => rootAbs);\n\n // Tracks normalized, case-folded target paths seen so far in phase 1. Maps\n // targetAbs.toLowerCase() → first source path so the collision error can name\n // both offending entries. Two manifest entries whose computed target paths\n // normalise to the same absolute path (e.g. `subdir/file.md` and\n // `subdir/./file.md`, or differing case on case-insensitive filesystems such\n // as macOS and Windows) would both refer to the same inode. In\n // `skip-conflicts`/`prefer-local` modes one entry would be misclassified as a\n // local conflict against the OTHER entry's just-written content; in\n // `prefer-source` the second entry would silently overwrite the first. We\n // reject the import up-front before any write (Codex P2 thread on PR #748,\n // mirroring `capsule-import.ts`).\n const seenTargetPaths = new Map<string, string>();\n\n for (const rec of bundle.records) {\n // Checksum validation.\n const entry = manifestIndex.get(rec.path);\n if (!entry) {\n throw new Error(\n `mergeCapsule: archive checksum mismatch (record without manifest entry: ${rec.path})`,\n );\n }\n const { sha256, bytes } = sha256String(rec.content);\n if (sha256 !== entry.sha256 || bytes !== entry.bytes) {\n throw new Error(\n `mergeCapsule: archive checksum mismatch for ${rec.path}: ` +\n `expected sha256=${entry.sha256} bytes=${entry.bytes}, ` +\n `got sha256=${sha256} bytes=${bytes}`,\n );\n }\n\n // Path-traversal validation (mirrors capsule-import.ts).\n if (rec.path.includes(\"\\\\\")) {\n throw new Error(\n `mergeCapsule: record path contains backslash separators (Windows-style paths are not allowed): ${rec.path}`,\n );\n }\n const posixNormalized = path.posix.normalize(rec.path);\n if (\n rec.path.startsWith(\"/\") ||\n rec.path.split(\"/\").some((seg) => seg === \"..\") ||\n posixNormalized.startsWith(\"..\") ||\n posixNormalized.startsWith(\"/\")\n ) {\n throw new Error(\n `mergeCapsule: record path escapes target root: ${rec.path}`,\n );\n }\n\n // Lexical root containment check.\n const targetAbs = path.join(rootReal, fromPosixRelPath(rec.path));\n if (!isPathInsideRoot(rootReal, targetAbs)) {\n throw new Error(\n `mergeCapsule: record path escapes target root: ${rec.path}`,\n );\n }\n\n // Symlink-aware containment check (shared helper from fs-utils).\n await assertRealpathInsideRoot(rootReal, targetAbs, rec.path, \"mergeCapsule\");\n\n // Target-file symlink guard: if the target already exists as a symlink,\n // reject — writes through symlinks can redirect to unexpected locations.\n const targetLstat = await lstat(targetAbs).catch(() => null);\n if (targetLstat !== null && targetLstat.isSymbolicLink()) {\n throw new Error(\n `mergeCapsule: record target is a symlink and cannot be written to safely: ${rec.path}`,\n );\n }\n\n // Duplicate normalized target path detection (Codex P2 #748, mirrors\n // capsule-import.ts). `path.join` already normalises `.` segments\n // (e.g. `subdir/./file.md` → `subdir/file.md`). On case-insensitive\n // filesystems (macOS default, Windows), two paths that differ only in case\n // would resolve to the same inode. We fold the dedup key to lowercase so\n // that `subdir/File.md` and `subdir/file.md` are detected as duplicates\n // before any write occurs. This is intentionally unconditional: the cost\n // of an extra `.toLowerCase()` on case-sensitive filesystems is negligible,\n // and a defensive lowercase is far simpler than probing filesystem\n // case-sensitivity at runtime. Without this guard, prefer-source mode\n // would silently overwrite one entry with the other, and skip-conflicts /\n // prefer-local would misclassify the second entry as a local conflict\n // against the first entry's freshly written content.\n const dedupKey = targetAbs.toLowerCase();\n const firstSourcePath = seenTargetPaths.get(dedupKey);\n if (firstSourcePath !== undefined) {\n throw new Error(\n `mergeCapsule: manifest contains two entries that resolve to the same target path: ` +\n `\"${firstSourcePath}\" and \"${rec.path}\" both map to \"${rec.path}\"`,\n );\n }\n seenTargetPaths.set(dedupKey, rec.path);\n }\n\n // Detect manifest-only entries (missing record). Treat as corruption.\n for (const f of manifest.files) {\n if (!recordPaths.has(f.path)) {\n throw new Error(\n `mergeCapsule: archive checksum mismatch (manifest entry without record: ${f.path})`,\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Phase 2: classify records and apply conflict mode.\n // ---------------------------------------------------------------------------\n\n const merged: MergeCapsuleWrittenRecord[] = [];\n const skipped: MergeCapsuleSkippedRecord[] = [];\n const conflicts: MergeCapsuleConflictRecord[] = [];\n // Same write-boundary rule as capsule-import (issue #2213): records land\n // with their entityRef resolved through the target's migration journal,\n // read through the identity-keyed cache PER RECORD so a peer migration\n // completing mid-merge is picked up for every record written after it.\n const journalStateDir = path.join(rootReal, \"state\");\n const historicalIdCache = new HistoricalEntityCanonicalIdCache();\n // Entity pages in THIS capsule can re-establish collisions the target's\n // journal already resolved (Codex P1): once the next migration parks the\n // mapping, a memory canonicalized onto the canonical claimant is\n // permanently misattributed. Memories whose ref targets a capsule-supplied\n // entity id merge byte-faithful behind the reconcile marker instead.\n const capsuleEntityIds = new Set(\n bundle.records\n .filter((record) =>\n classifyEntityRefWritePath(rootReal, path.join(rootReal, fromPosixRelPath(record.path))) === \"entity\")\n .map((record) => path.basename(record.path, \".md\")),\n );\n // Write a record with its entityRef resolved AT the write, then repair if\n // the journal moved across the write itself — a parked mapping cannot be\n // fixed after the fact by the reconcile pass (issue #2213). Only memory-tier\n // records canonicalize; entities/ pages carry relationship TARGETS only\n // rewriteRelationshipTargets understands, so they merge byte-faithful with\n // a reconcile marker; everything else merges losslessly. The corpus bump\n // lands AFTER the repair so a warm cache rescan never captures pre-repair\n // bytes.\n // The reconcile marker is published ONCE, after every record landed\n // (Codex P1, round 21): a per-record marker could be consumed by a peer\n // between a legacy-preserving memory write and its colliding entity page,\n // canonicalizing the memory onto the wrong claimant irreversibly.\n let reconcileNeeded = false;\n const writeCanonicalRecord = async (targetAbs: string, rawContent: string): Promise<void> => {\n const kind = classifyEntityRefWritePath(rootReal, targetAbs);\n const rawRef = kind === \"memory\" ? readEntityRefFromFrontmatter(rawContent) : null;\n if (kind === \"memory\" && rawRef !== null && capsuleEntityIds.has(rawRef)) {\n // Ref targets a capsule-supplied entity page (potential collision\n // claimant): keep the legacy bytes; the deferred marker lets the\n // migration settle the collision and rewrite per its verdict (Codex P1).\n await writeFile(targetAbs, rawContent, \"utf-8\");\n reconcileNeeded = true;\n bumpMemoryCorpusVersionForDir(rootReal); // per-write bump (Cursor Medium, #1902)\n return;\n }\n if (kind !== \"memory\") {\n await writeFile(targetAbs, rawContent, \"utf-8\");\n if (kind === \"entity\") {\n reconcileNeeded = true;\n }\n bumpMemoryCorpusVersionForDir(rootReal); // per-write bump (Cursor Medium, #1902)\n return;\n }\n const idsAtWrite = historicalIdCache.get(journalStateDir);\n const written = canonicalizeEntityRefFrontmatter(rawContent, idsAtWrite);\n // Snapshot the pre-write bytes: a repair failure after the write must\n // restore them (or remove a newly created target) before propagating —\n // the merge reports failure, so disk must return to its pre-write state\n // and warm caches stay consistent (AGENTS.md §14).\n const originalBytes = await readLocalFile(targetAbs);\n await writeFile(targetAbs, written, \"utf-8\");\n try {\n await repairContentAfterJournalMove({\n stateDir: journalStateDir,\n cache: historicalIdCache,\n idsAtWrite,\n rawContent,\n lastWritten: written,\n rewrite: (content) => writeFile(targetAbs, content, \"utf-8\"),\n });\n } catch (err) {\n if (originalBytes === null) {\n await unlink(targetAbs).catch(() => undefined);\n } else {\n await writeFile(targetAbs, originalBytes, \"utf-8\").catch(() => undefined);\n }\n throw err;\n }\n bumpMemoryCorpusVersionForDir(rootReal); // per-write bump (Cursor Medium, #1902)\n };\n\n // Sort by source path for deterministic output (mirrors capsule-import.ts).\n const sortedRecords = [...bundle.records].sort((a, b) =>\n a.path.localeCompare(b.path),\n );\n\n const applyRecords = async (): Promise<void> => {\n for (const rec of sortedRecords) {\n const targetAbs = path.join(rootReal, fromPosixRelPath(rec.path));\n const entry = manifestIndex.get(rec.path)!; // validated above\n\n const localContent = await readLocalFile(targetAbs);\n\n if (localContent === null) {\n // No local copy — always write regardless of mode. Resolve at the write\n // itself and verify the journal afterwards: a peer migration completing\n // during any of these awaits (or the write) would otherwise strand the\n // reference — capsule writes bump only the corpus version, which the\n // migration fingerprint deliberately ignores.\n await mkdir(path.dirname(targetAbs), { recursive: true });\n await writeCanonicalRecord(targetAbs, rec.content);\n merged.push({ sourcePath: rec.path, targetPath: rec.path, snapshotted: false });\n continue;\n }\n\n // Local file exists. A local copy already holding the CANONICALIZED form\n // (computed AFTER the local read, so a journal published during that\n // await is reflected) is done. A local copy matching the RAW archive\n // bytes while a mapping applies is UPGRADED in place — skipping would\n // leave the legacy entityRef on disk forever, since corpus bumps no\n // longer trigger migration rewrites (issue #2213).\n const idsAtCompare = historicalIdCache.get(journalStateDir);\n const compareRef = readEntityRefFromFrontmatter(rec.content);\n const canonicalContent =\n classifyEntityRefWritePath(rootReal, targetAbs) === \"memory\"\n && !(compareRef !== null && capsuleEntityIds.has(compareRef))\n ? canonicalizeEntityRefFrontmatter(rec.content, idsAtCompare)\n : rec.content;\n const { sha256: localSha256 } = sha256String(localContent);\n const canonicalSha256 =\n canonicalContent === rec.content ? entry.sha256 : sha256String(canonicalContent).sha256;\n\n // The identical verdict is only valid for the snapshot it was computed\n // under (Codex P1): a mapping parked/removed mid-compare can make the\n // local copy's formerly-canonical ref stale with nothing left to repair\n // it. A moved journal falls through to the write path, which resolves at\n // the write and repairs after it.\n if (localSha256 === canonicalSha256 && historicalIdCache.get(journalStateDir) === idsAtCompare) {\n skipped.push({ path: rec.path, reason: \"identical\" });\n continue;\n }\n if (localSha256 === canonicalSha256 || localSha256 === entry.sha256) {\n await writeCanonicalRecord(targetAbs, rec.content);\n merged.push({ sourcePath: rec.path, targetPath: rec.path, snapshotted: false });\n continue;\n }\n\n // Content differs → conflict.\n const { sha256: archiveSha256 } = sha256String(rec.content);\n conflicts.push({\n path: rec.path,\n archiveSha256,\n localSha256,\n });\n\n if (conflictMode === \"skip-conflicts\" || conflictMode === \"prefer-local\") {\n // Keep local copy, skip archive entry.\n skipped.push({ path: rec.path, reason: \"conflict\" });\n continue;\n }\n\n // conflictMode === \"prefer-source\": snapshot local then overwrite.\n let snapshotted = false;\n if (opts.versioning && opts.versioning.enabled) {\n // Gotcha #54: snapshot BEFORE overwriting.\n await createVersion(\n targetAbs,\n localContent,\n \"manual\",\n opts.versioning,\n opts.log,\n `capsule-merge: ${capsule.id}`,\n rootReal,\n );\n snapshotted = true;\n }\n\n // Same write-time re-resolve + post-write repair as above — the\n // snapshot/read awaits give a peer migration a real window.\n await writeCanonicalRecord(targetAbs, rec.content);\n merged.push({ sourcePath: rec.path, targetPath: rec.path, snapshotted });\n }\n\n // Every record — including every collision claimant entity page — is on\n // disk; NOW the reconcile pass can settle collisions safely.\n if (reconcileNeeded) {\n await requestEntityCanonicalIdReconcile(journalStateDir);\n }\n };\n const firstEntityId = capsuleEntityIds.values().next().value;\n if (firstEntityId === undefined) {\n await applyRecords();\n } else {\n await withRawEntityPageMutation(\n rootReal,\n path.join(rootReal, \"entities\", `${firstEntityId}.md`),\n applyRecords,\n );\n }\n\n // Sort output lists for determinism.\n merged.sort((a, b) => a.sourcePath.localeCompare(b.sourcePath));\n skipped.sort((a, b) => a.path.localeCompare(b.path));\n conflicts.sort((a, b) => a.path.localeCompare(b.path));\n\n // Per-write corpus bumps happen inside the loop above (Cursor Medium, #1902):\n // a concurrent readAllMemories during the merge rescans and never serves the\n // pre-merge corpus mid-batch.\n\n return { merged, skipped, conflicts, manifest };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nasync function readLocalFile(absPath: string): Promise<string | null> {\n const st = await stat(absPath).catch(() => null);\n if (!st || !st.isFile()) return null;\n return readFile(absPath, \"utf-8\");\n}\n\n// Re-export CapsuleBlock so callers don't need a deep import from types.ts.\nexport type { CapsuleBlock };\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/orchestration/buffer-drain-runner.ts"],"sourcesContent":["/** Claim-backed extraction-buffer drain (`remnic buffer drain` worker AND the */\n\nimport { hostname } from \"node:os\";\nimport type { BufferStorage } from \"../buffer-persistence.js\";\nimport { type ExtractionScopeGroup, describeError, groupTurnsByExtractionScope } from \"../buffer-turn-helpers.js\";\nimport { SmartBuffer } from \"../buffer.js\";\nimport { resolveMemoryLifecycleCapabilities } from \"../capabilities.js\";\nimport { shouldSkipImplicitExtraction } from \"../explicit-capture.js\";\nimport { log } from \"../logger.js\";\nimport type { ResolvedScopeProfilePlan } from \"../namespaces/scope-profiles.js\";\nimport type { Orchestrator } from \"../orchestrator.js\";\nimport { headerConnector, renderExtractionConversation, resolveSourceConnector } from \"../source-agent-qualifier.js\";\nimport type { BufferDrainClaims, DrainLease } from \"../storage/buffer-drain-claims.js\";\nimport type { BufferEntryState, BufferState, BufferTurn, PluginConfig } from \"../types.js\";\nimport { ExtractionDrain } from \"./extraction-drain.js\";\nimport type { ExtractionRunResult } from \"./extraction-run.js\";\nimport { selfDeps } from \"./self-deps.js\";\nimport { TurnIngestionCoordinator, type TurnIngestionDeps } from \"./turn-ingestion.js\";\n\nexport type DrainSessionStatus = \"drained\" | \"deferred\" | \"failed\" | \"skipped\";\n\nexport interface DrainSessionOutcome {\n key: string;\n status: DrainSessionStatus;\n /** Turns the claim held when the drain started. */\n turnsAtStart: number;\n /** Turns still in the claim when the drain stopped. */\n turnsRemaining: number;\n /** Facts the extraction persisted during this run. */\n persistedCount: number;\n /** Durable outputs (memories + entities) the extraction wrote. */\n durableOutputCount: number;\n /** Why the session was skipped or deferred (machine-readable). */\n reason?: string;\n /** Current-policy denials: scope groups whose authorization no longer holds. */\n denied?: { reason: string; turns: number }[];\n}\n\n/** Extraction results that mean \"current policy refuses this scope\": the\n * turns are valid backlog whose captured scope is no longer writable. */\nconst DENIED_SCOPE_REASONS: ReadonlySet<string> = new Set([\n \"pinned_namespace_unavailable\",\n \"namespace_write_revoked\",\n \"scope_profile_revoked\",\n // The resolved profile has no writable layer: the same current-policy\n // refusal family, surfaced from inside the shared runExtraction gate.\n \"scope_profile_no_writable_layer\",\n // captureMode=explicit: ordinary buffered conversation turns never create\n // memories; only deliberately accepted captures drain.\n \"capture_mode_explicit\",\n]);\n\nexport interface BufferDrainRunnerOptions {\n /** Identity of this drainer; owns the lease it takes. */\n ownerId: string;\n /** Lease lifetime in ms. The heartbeat keeps it alive while draining. */\n leaseMs?: number;\n /** Cancels the run at the next chunk boundary. */\n signal?: AbortSignal;\n /** Progress callback (per session, and per claim transition). */\n onEvent?: (event: DrainRunnerEvent) => void;\n}\n\n/** A failed drain run: the reason a chunk or the session could not finish. */\nexport type DrainExtractionError = Error;\n\nexport type DrainRunnerEvent =\n | { type: \"session_started\"; key: string; turns: number }\n | { type: \"session_finished\"; key: string; outcome: DrainSessionOutcome };\n\nconst DEFAULT_LEASE_MS = 120_000;\n\n/**\n * A `BufferStorage` view of one drain claim: the claim file IS the buffer.\n * Writes are the drain's progress (what is left to extract), so every\n * successfully extracted chunk is durably removed before the next one runs.\n */\nexport class ClaimBufferStorage implements BufferStorage {\n constructor(\n private readonly claims: BufferDrainClaims,\n private readonly key: string,\n private readonly ownerId: string,\n private readonly entry: BufferEntryState\n ) {}\n\n async loadBuffer(): Promise<BufferState> {\n const entries: NonNullable<BufferState[\"entries\"]> = Object.create(null);\n entries[this.key] = {\n turns: this.entry.turns,\n retainedTurns: this.entry.retainedTurns,\n lastExtractionAt: this.entry.lastExtractionAt,\n extractionCount: this.entry.extractionCount,\n };\n return {\n turns: this.entry.turns,\n lastExtractionAt: this.entry.lastExtractionAt,\n extractionCount: this.entry.extractionCount,\n entries,\n };\n }\n\n async saveBuffer(state: BufferState): Promise<void> {\n // Whole-state fallback (the claim covers one session): persist the entry.\n const entry = state.entries?.[this.key];\n await this.claims.writeEntry(\n this.key,\n this.ownerId,\n entry ?? { turns: [], lastExtractionAt: null, extractionCount: 0 },\n this.entry\n );\n }\n\n async saveBufferSession(key: string, entry: BufferEntryState): Promise<void> {\n await this.claims.writeEntry(key, this.ownerId, entry, key === this.key ? this.entry : undefined);\n }\n\n async deleteBufferSession(): Promise<void> {\n // The claim is removed by the ack path, never by a buffer eviction: the\n // drain decides when the session is finished.\n }\n}\n\n/** Per-invocation isolated execution context for one claim drain. */\ninterface ClaimDrainExecution {\n claimBuffer: SmartBuffer;\n coordinator: TurnIngestionCoordinator;\n executionDrain: ExtractionDrain;\n}\n\n/** The scopeProfileWritePlan option a scope group forwards to the queue. */\ninterface GroupPlanOption {\n scopeProfileWritePlan?: ResolvedScopeProfilePlan | null;\n}\n\n/** Durable counters a drain pass contributed. */\ninterface GroupCounts {\n persistedCount: number;\n durableOutputCount: number;\n}\n\n/** Pending turns across canonical + retained copies (the honest backlog count). */\nfunction pendingTurnCount(entry: BufferEntryState | undefined): number {\n if (!entry) return 0;\n return entry.turns.length + (entry.retainedTurns?.length ?? 0);\n}\n\n/** Load one claimed session. An unclaimed key is nothing to drain. */\nasync function loadClaimedSession(\n claims: BufferDrainClaims,\n key: string\n): Promise<\n | { kind: \"notClaimed\"; notClaimed: DrainSessionOutcome }\n | { kind: \"claimed\"; claim: { entry: BufferEntryState }; turnsAtStart: number }\n> {\n const claim = await claims.load(key);\n if (!claim) {\n return {\n kind: \"notClaimed\",\n notClaimed: {\n key,\n status: \"skipped\",\n turnsAtStart: 0,\n turnsRemaining: 0,\n persistedCount: 0,\n durableOutputCount: 0,\n reason: \"not_claimed\",\n },\n };\n }\n return { kind: \"claimed\", claim, turnsAtStart: pendingTurnCount(claim.entry) };\n}\n\nfunction busyLeaseOutcome(key: string, turnsAtStart: number): DrainSessionOutcome {\n return {\n key,\n status: \"skipped\",\n turnsAtStart,\n turnsRemaining: turnsAtStart,\n persistedCount: 0,\n durableOutputCount: 0,\n reason: \"claimed_by_another_drain\",\n };\n}\n\n/** Take the lease unless this drainer already holds it. A busy lease means\n * another drainer owns the claim — skip without touching content. */\nasync function adoptLease(\n claims: BufferDrainClaims,\n key: string,\n ownerId: string,\n leaseMs: number\n): Promise<DrainSessionOutcome | null> {\n const held = await claims.adopt(key, {\n ownerId,\n pid: process.pid,\n host: hostname(),\n acquiredAt: new Date().toISOString(),\n heartbeatAt: new Date().toISOString(),\n leaseMs,\n });\n return held === \"busy\" ? busyLeaseOutcome(key, pendingTurnCount((await claims.load(key))?.entry)) : null;\n}\n\nfunction assembleOutcome(\n key: string,\n turnsAtStart: number,\n verdict: { status: DrainSessionStatus; reason?: string; turnsRemaining: number },\n counts: { persistedCount: number; durableOutputCount: number },\n denied: Map<string, number>\n): DrainSessionOutcome {\n const outcome: DrainSessionOutcome = {\n key,\n status: verdict.status,\n turnsAtStart,\n turnsRemaining: verdict.turnsRemaining,\n persistedCount: counts.persistedCount,\n durableOutputCount: counts.durableOutputCount,\n };\n if (denied.size > 0) outcome.denied = [...denied].map(([deniedReason, turns]) => ({ reason: deniedReason, turns }));\n if (verdict.reason !== undefined) outcome.reason = verdict.reason;\n return outcome;\n}\n\n/** Ack an EMPTY claim. The ack verdict is the truth: live ingress can merge\n * between the empty read and the ack — a not_empty ack means pending work\n * exists; reload it, release OUR lease, report deferred, never a false\n * drained. */\nasync function ackEmptyClaim(\n claims: BufferDrainClaims,\n key: string,\n ownerId: string,\n turnsAtStart: number\n): Promise<DrainSessionOutcome> {\n const ackOutcome = await claims.ack(key, ownerId);\n if (ackOutcome !== \"acked\") {\n const reloaded = await claims.load(key);\n const turnsRemaining = pendingTurnCount(reloaded?.entry);\n await claims.releaseLease(key, ownerId);\n return {\n key,\n status: \"deferred\",\n turnsAtStart,\n turnsRemaining,\n persistedCount: 0,\n durableOutputCount: 0,\n reason: ackOutcome === \"not_empty\" ? \"ack_not_empty\" : \"ack_not_owner\",\n };\n }\n return { key, status: \"drained\", turnsAtStart, turnsRemaining: 0, persistedCount: 0, durableOutputCount: 0 };\n}\n\n/** another drainer took the claim, so this run must stop instead of writing */\nfunction startLeaseHeartbeat(\n claims: BufferDrainClaims,\n key: string,\n ownerId: string,\n leaseMs: number,\n abort: AbortController\n): NodeJS.Timeout {\n const heartbeat = setInterval(\n () => {\n void claims\n .heartbeat(key, ownerId, leaseMs)\n .then((state) => {\n if (state === \"lost\") abort.abort(new Error(`buffer drain: lease lost for session ${key}`));\n })\n .catch((err: Error) => {\n // A heartbeat that cannot be renewed is a lease that cannot be held:\n // stop this run cleanly at the next chunk boundary (the next run\n // resumes it).\n abort.abort(new Error(`buffer drain: lease heartbeat failed for session ${key}: ${describeError(err)}`));\n });\n },\n Math.max(1_000, Math.floor(leaseMs / 3))\n );\n heartbeat.unref?.();\n return heartbeat;\n}\n\n/** coordinator bound to it, and the no-drop chunked drain. Built per */\nfunction buildClaimExecution(\n orchestrator: Orchestrator,\n claims: BufferDrainClaims,\n key: string,\n ownerId: string,\n claim: { entry: BufferEntryState }\n): ClaimDrainExecution {\n const claimBuffer = new SmartBuffer(orchestrator.config, new ClaimBufferStorage(claims, key, ownerId, claim.entry));\n const isolatedRun = orchestrator.extractionRunCoordinatorForBuffer(claimBuffer);\n const coordinator = new TurnIngestionCoordinator(selfDeps<TurnIngestionDeps>(orchestrator));\n const config = orchestrator.config;\n const ingestionDeps = selfDeps<TurnIngestionDeps>(orchestrator);\n // No-drop: a repeatedly failing chunk stays in the claim as durable\n // retryable backlog — never side-filed, never counted as processed.\n const executionDrain = new ExtractionDrain(\n {\n config: {\n extractionMinUserTurns: config.extractionMinUserTurns,\n extractionMinChars: config.extractionMinChars,\n extractionMaxTurnChars: config.extractionMaxTurnChars,\n extractionChunkDropAfterFailures: config.extractionChunkDropAfterFailures,\n },\n extractionWindowBudgetChars: () => ingestionDeps.extractionWindowBudgetChars?.() ?? Number.POSITIVE_INFINITY,\n // Render-exact chunk sizing: the model clamp bounds the CONVERSATION\n // (truncateLocalExtractionConversation), so measure exactly that —\n // with the SAME source-classification gate extract() applies when it\n // prepends the connector header (header margin included when the\n // engine would render it).\n measureRenderedTurns: (turns) => {\n const sourceConnector = resolveMemoryLifecycleCapabilities(config).extractionScopeClassification\n ? resolveSourceConnector(turns)\n : undefined;\n return renderExtractionConversation(\n turns.map((turn) => ({ ...turn, content: turn.content.trim().slice(0, config.extractionMaxTurnChars) })),\n headerConnector(sourceConnector)\n ).conversation.length;\n },\n runExtraction: (turns, runOptions) => isolatedRun.runExtraction(turns, runOptions),\n getStorage: () => ingestionDeps.getStorage(),\n buffer: claimBuffer,\n },\n { dropFailedChunks: false }\n );\n return { claimBuffer, coordinator, executionDrain };\n}\n\n/** Plan semantics for one scope group: a pinned plan forwards as-is; a\n * planless ACCEPTED group pins null (existing flush behavior); unpinned\n * legacy groups pass NO option so runExtraction resolves the CURRENT\n * profile. */\nfunction planOptionForGroup(group: ExtractionScopeGroup): GroupPlanOption {\n if (group.scopeProfileWritePlan !== undefined) return { scopeProfileWritePlan: group.scopeProfileWritePlan };\n if (group.turns.some((turn) => turn.acceptedCapture === true)) return { scopeProfileWritePlan: null };\n return {};\n}\n\n/** captureMode=explicit: ordinary buffered conversation turns never create\n * memories — only deliberately accepted captures drain. */\nfunction explicitModeDenied(group: ExtractionScopeGroup, config: PluginConfig): boolean {\n return shouldSkipImplicitExtraction(config) && !group.turns.some((turn) => turn.acceptedCapture === true);\n}\n\ninterface GroupOutcome {\n turns: number;\n result?: ExtractionRunResult;\n}\n\ninterface ScopeDrainContext {\n config: PluginConfig;\n key: string;\n signal: AbortSignal;\n claimBuffer: SmartBuffer;\n coordinator: TurnIngestionCoordinator;\n executionDrain: ExtractionDrain;\n turns: BufferTurn[];\n}\n\n/** Drain a claimed session's turns ONE captured-scope group at a time: each\n * group keeps its own namespace/principal/plan pins, and the shared\n * extraction boundary re-applies CURRENT authorization from the turn's\n * ORIGINAL coding binding before any write. Groups run sequentially (FIFO\n * per key — the queue's same-key exclusion would serialize them anyway); a\n * denied group never blocks or clears the other groups. */\nasync function drainScopeGroups(\n ctx: ScopeDrainContext\n): Promise<{ groupOutcomes: GroupOutcome[]; groupError?: DrainExtractionError }> {\n const groupOutcomes: GroupOutcome[] = [];\n let groupError: DrainExtractionError | undefined;\n for (const group of groupTurnsByExtractionScope(ctx.turns)) {\n if (ctx.signal.aborted) break;\n if (explicitModeDenied(group, ctx.config)) {\n log.warn(\n `buffer drain: session ${ctx.key} denied capture_mode_explicit (${group.turns.length} ordinary turn(s) retained)`\n );\n groupOutcomes.push({\n turns: group.turns.length,\n result: { status: \"skipped\", reason: \"capture_mode_explicit\", persistedCount: 0, durableOutputCount: 0 },\n });\n continue;\n }\n const settleGate = Promise.withResolvers<{ cause?: unknown; result?: ExtractionRunResult }>();\n let done = false;\n const onSettle = (cause: unknown, result?: ExtractionRunResult): void => {\n if (done) return;\n done = true;\n settleGate.resolve({ cause, result });\n };\n // Settlement and callback EXIT are tracked separately: the lease is\n // released only after BOTH the queued closure settled and fully exited\n // (an abort settles through its listener while the closure may still be\n // persisting).\n const exited = Promise.withResolvers<void>();\n void ctx.coordinator\n .queueBufferedExtraction(group.turns, \"trigger_mode\", {\n bufferKey: ctx.key,\n clearBufferAfterExtraction: true,\n clearMatchingTurns: true,\n skipDedupeCheck: true,\n skipCharThreshold: true,\n skipUserTurnThreshold: true,\n // Automatic background work yields to fail-closed foreground flushes\n // (preemptRunning), same as the live trigger path.\n preemptable: true,\n abortSignal: ctx.signal,\n writeNamespaceOverride: group.writeNamespaceOverride,\n principalOverride: group.principalOverride,\n ...planOptionForGroup(group),\n execution: { buffer: ctx.claimBuffer, drain: ctx.executionDrain },\n onTaskSettled: (error, result) => settleGate.resolve({ cause: error, result }),\n onTaskExit: () => exited.resolve(),\n })\n .catch((cause: DrainExtractionError) => onSettle(cause));\n const [verdict] = await Promise.all([settleGate.promise, exited.promise]);\n const settled = verdict;\n if (settled.cause !== undefined) {\n // SAFETY: settle captures whatever the queued closure rejected with;\n // every drain rejection site throws Error values, and a rejection is\n // always a DrainExtractionError in this path.\n groupError = settled.cause as Error;\n break;\n }\n groupOutcomes.push({ turns: group.turns.length, result: settled.result });\n }\n return { groupOutcomes, groupError };\n}\n\n/** Denied scope groups: policy refusals leave their turns pending (the group\n * result's turns were NOT cleared), aggregated per reason so a drain is\n * never reported complete while denied backlog remains. */\nfunction deniedByReason(groupOutcomes: readonly GroupOutcome[]): Map<string, number> {\n const denied = new Map<string, number>();\n for (const entry of groupOutcomes) {\n if (entry.result?.status !== \"skipped\" || !DENIED_SCOPE_REASONS.has(entry.result.reason ?? \"\")) continue;\n denied.set(entry.result.reason!, (denied.get(entry.result.reason!) ?? 0) + entry.turns);\n }\n return denied;\n}\n\nfunction lastGroupReason(groupOutcomes: readonly GroupOutcome[]): string | undefined {\n for (let index = groupOutcomes.length - 1; index >= 0; index -= 1) {\n const reason = groupOutcomes[index]?.result?.reason;\n if (reason !== undefined) return reason;\n }\n return undefined;\n}\n\n/** the session file is gone for good — but the ack verdict is the truth (live */\nasync function settleDrainVerdict(\n claims: BufferDrainClaims,\n key: string,\n ownerId: string,\n groupOutcomes: readonly GroupOutcome[],\n groupError: DrainExtractionError | undefined,\n turnsRemaining: number\n): Promise<{ status: DrainSessionStatus; reason?: string; turnsRemaining: number }> {\n if (turnsRemaining === 0) return await ackOrReload(claims, key, ownerId);\n if (groupError) {\n await claims.releaseLease(key, ownerId);\n return { status: \"failed\", reason: describeError(groupError), turnsRemaining };\n }\n await claims.releaseLease(key, ownerId);\n const reason =\n groupOutcomes.find((entry) => entry.result?.reason !== undefined && DENIED_SCOPE_REASONS.has(entry.result.reason!))\n ?.result?.reason ??\n lastGroupReason(groupOutcomes) ??\n \"deferred\";\n return { status: \"deferred\", reason, turnsRemaining };\n}\n\n/** Ack an emptied claim; a not-acked verdict means live ingress merged (or\n * the lease moved) — reload the honest pending count and release OUR lease,\n * keeping the source. */\nasync function ackOrReload(\n claims: BufferDrainClaims,\n key: string,\n ownerId: string\n): Promise<{ status: DrainSessionStatus; reason?: string; turnsRemaining: number }> {\n const ackOutcome = await claims.ack(key, ownerId);\n if (ackOutcome === \"acked\") return { status: \"drained\", turnsRemaining: 0 };\n const reloaded = await claims.load(key);\n const turnsRemaining = pendingTurnCount(reloaded?.entry);\n await claims.releaseLease(key, ownerId);\n return { status: \"deferred\", reason: ackOutcome === \"not_empty\" ? \"ack_not_empty\" : \"ack_not_owner\", turnsRemaining };\n}\n\nfunction groupCounts(groupOutcomes: readonly GroupOutcome[]): GroupCounts {\n return {\n persistedCount: groupOutcomes.reduce((sum, entry) => sum + (entry.result?.persistedCount ?? 0), 0),\n durableOutputCount: groupOutcomes.reduce((sum, entry) => sum + (entry.result?.durableOutputCount ?? 0), 0),\n };\n}\n\nexport interface DrainScanEntry {\n key: string;\n turns: number;\n claimed: boolean;\n}\n\n/** Session-buffer listings: a claimed session reports its CLAIM's pending\n * count (the live buffer copy may lag the claim), an unclaimed one its\n * buffer entry. */\nasync function bufferSessionListings(\n claims: BufferDrainClaims,\n entries: Record<string, BufferEntryState>\n): Promise<{ listings: DrainScanEntry[]; seen: Set<string> }> {\n const listings: DrainScanEntry[] = [];\n const seen = new Set<string>();\n for (const [key, entry] of Object.entries(entries)) {\n seen.add(key);\n const claim = claims.isClaimed(key) ? await claims.load(key) : null;\n listings.push({\n key,\n turns: claim ? pendingTurnCount(claim.entry) : pendingTurnCount(entry),\n claimed: claim !== null,\n });\n }\n return { listings, seen };\n}\n\n/** Claims the buffer state no longer lists (drained by an earlier run whose\n * claim file outlived its session file). */\nasync function orphanClaimListings(claims: BufferDrainClaims, seen: Set<string>): Promise<DrainScanEntry[]> {\n const out: DrainScanEntry[] = [];\n for (const listed of await claims.list()) {\n if (seen.has(listed.key)) continue;\n out.push({ key: listed.key, turns: listed.turnCount, claimed: true });\n }\n return out;\n}\n\n/** The drain runs on the caller's abort signal combined with this run's own\n * lease-loss controller. */\nfunction drainAbortSignal(callerSignal: AbortSignal | undefined, abort: AbortController): AbortSignal {\n return callerSignal ? AbortSignal.any([callerSignal, abort.signal]) : abort.signal;\n}\n\n/** One heartbeat-protected claim drain: run every scope group, settle the\n * verdict against the claim store, and assemble the honest outcome. The\n * heartbeat is cleared by the caller on every exit path. */\nasync function runClaimDrain(\n ctx: {\n orchestrator: Orchestrator;\n claims: BufferDrainClaims;\n key: string;\n ownerId: string;\n claimBuffer: SmartBuffer;\n coordinator: TurnIngestionCoordinator;\n executionDrain: ExtractionDrain;\n turns: BufferTurn[];\n turnsAtStart: number;\n config: PluginConfig;\n signal: AbortSignal;\n },\n options: BufferDrainRunnerOptions,\n heartbeat: NodeJS.Timeout\n): Promise<DrainSessionOutcome> {\n // An emptied claim acks here: the ack verdict is the truth (drained), or\n // live ingress merged between the read and the ack — deferred, source kept.\n if (ctx.turns.length === 0) {\n const outcome = await ackEmptyClaim(ctx.claims, ctx.key, ctx.ownerId, ctx.turnsAtStart);\n options.onEvent?.({ type: \"session_finished\", key: ctx.key, outcome });\n return outcome;\n }\n const { groupOutcomes, groupError } = await drainScopeGroups(ctx);\n // Heartbeat cleared on EVERY exit path: a rejected drain must not keep\n // renewing the lease forever (cleared in the caller's finally).\n const remaining = await ctx.claims.load(ctx.key);\n const { status, reason, turnsRemaining } = await settleDrainVerdict(\n ctx.claims,\n ctx.key,\n ctx.ownerId,\n groupOutcomes,\n groupError,\n pendingTurnCount(remaining?.entry)\n );\n const outcome = assembleOutcome(\n ctx.key,\n ctx.turnsAtStart,\n { status, reason, turnsRemaining },\n groupCounts(groupOutcomes),\n deniedByReason(groupOutcomes)\n );\n log.info(`buffer drain: session ${ctx.key} ${status} (${outcome.turnsRemaining} turn(s) remaining)`);\n options.onEvent?.({ type: \"session_finished\", key: ctx.key, outcome });\n return outcome;\n}\n\nexport class BufferDrainRunner {\n constructor(private readonly orchestrator: Orchestrator) {}\n\n /** The claim store of this orchestrator's memory root. */\n get claims(): BufferDrainClaims {\n return this.orchestrator.storage.bufferDrainClaims();\n }\n\n /**\n * Drain one claimed session. The caller owns the lease (it may have taken\n * it already); this method verifies ownership before touching content.\n */\n async drainSession(key: string, options: BufferDrainRunnerOptions): Promise<DrainSessionOutcome> {\n const claims = this.claims;\n const leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;\n const claimed = await loadClaimedSession(claims, key);\n if (claimed.kind === \"notClaimed\") return claimed.notClaimed;\n const { claim, turnsAtStart } = claimed;\n options.onEvent?.({ type: \"session_started\", key, turns: turnsAtStart });\n\n // Take the lease unless this drainer already holds it.\n const busy = await adoptLease(claims, key, options.ownerId, leaseMs);\n if (busy !== null) {\n const outcome = busy;\n options.onEvent?.({ type: \"session_finished\", key, outcome });\n return outcome;\n }\n\n // The claim is the buffer for this run: every successful chunk is removed\n // from it, so a kill leaves exactly the un-extracted turns.\n const { claimBuffer, coordinator, executionDrain } = buildClaimExecution(\n this.orchestrator,\n claims,\n key,\n options.ownerId,\n claim\n );\n await claimBuffer.load();\n\n // The claim's turns (retained first, like the live buffer's order).\n const turns = [...(claim.entry.retainedTurns ?? []), ...claim.entry.turns];\n\n // A lost lease means another drainer took the claim: stop instead of\n // writing progress into a claim this run no longer owns.\n const abort = new AbortController();\n const signal = drainAbortSignal(options.signal, abort);\n const heartbeat = startLeaseHeartbeat(claims, key, options.ownerId, leaseMs, abort);\n try {\n return await runClaimDrain(\n {\n orchestrator: this.orchestrator,\n claims,\n key,\n ownerId: options.ownerId,\n claimBuffer,\n coordinator,\n executionDrain,\n turns,\n turnsAtStart,\n config: this.orchestrator.config,\n signal,\n },\n options,\n heartbeat\n );\n } finally {\n clearInterval(heartbeat);\n }\n }\n\n /** Claim a session file for the drain. Returns false when it is not claimable. */\n async claim(key: string, ownerId: string, leaseMs = DEFAULT_LEASE_MS): Promise<boolean> {\n const lease: DrainLease = {\n ownerId,\n pid: process.pid,\n host: hostname(),\n acquiredAt: new Date().toISOString(),\n heartbeatAt: new Date().toISOString(),\n leaseMs,\n };\n return (await this.claims.claimFromSessionFile(key, lease)) === \"claimed\";\n }\n\n /** Sessions with buffered turns, claimed or not (CLI scan). */\n async scan(): Promise<DrainScanEntry[]> {\n const sessions = await this.orchestrator.storage.loadBuffer();\n const { listings, seen } = await bufferSessionListings(this.claims, sessions.entries ?? {});\n return [...listings, ...(await orphanClaimListings(this.claims, seen))];\n }\n}\n"],"mappings":"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","names":["outcome"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/buffer-turn-helpers.ts"],"sourcesContent":["import type { BufferEntryState, BufferState, BufferTurn } from \"./types.js\";\n\nexport interface BufferTurnOwner {\n /**\n * Server-resolved principal that owns this session at the authenticated\n * observe/write boundary. Persisted with the buffered turn so lifecycle\n * flushes do not have to infer ownership from a client-chosen session key.\n */\n sessionOwnerPrincipal?: string;\n}\n\nexport interface AmbientCaptureProvenance {\n /**\n * True when this turn's content came from an always-on ambient capture\n * device (a wearable recorder, a room microphone) and may therefore carry\n * speech the user never authored: TV, podcasts, music, overheard\n * conversation (issue #2294). Set by the ingesting subsystem, never from\n * tool arguments — extraction reads it to warn the model about media audio\n * and to clamp high-impact personal facts to the speculative tier.\n */\n ambientCapture?: boolean;\n}\n\nexport function describeError(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (err === null) return \"null\";\n if (err === undefined) return \"undefined\";\n if (typeof err === \"string\") return err;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nexport function copyBufferTurn(turn: BufferTurn): BufferTurn {\n const copy: BufferTurn = {\n role: turn.role,\n content: turn.content,\n timestamp: turn.timestamp,\n };\n if (typeof turn.sessionKey === \"string\") copy.sessionKey = turn.sessionKey;\n if (typeof turn.logicalSessionKey === \"string\") {\n copy.logicalSessionKey = turn.logicalSessionKey;\n }\n if (turn.providerThreadId === null || typeof turn.providerThreadId === \"string\") {\n copy.providerThreadId = turn.providerThreadId;\n }\n if (typeof turn.sessionOwnerPrincipal === \"string\") {\n copy.sessionOwnerPrincipal = turn.sessionOwnerPrincipal;\n }\n if (typeof turn.turnFingerprint === \"string\") {\n copy.turnFingerprint = turn.turnFingerprint;\n }\n if (typeof turn.persistProcessedFingerprint === \"boolean\") {\n copy.persistProcessedFingerprint = turn.persistProcessedFingerprint;\n }\n if (typeof turn.sourceConnector === \"string\") {\n copy.sourceConnector = turn.sourceConnector;\n }\n if (turn.originRole === \"user\" || turn.originRole === \"assistant\" || turn.originRole === \"tool\") {\n copy.originRole = turn.originRole;\n }\n if (typeof turn.ambientCapture === \"boolean\") {\n copy.ambientCapture = turn.ambientCapture;\n }\n if (turn.sourceValidAt !== undefined) {\n copy.sourceValidAt = turn.sourceValidAt;\n }\n if (turn.parts !== undefined) {\n copy.parts = turn.parts;\n }\n if (turn.rawContent !== undefined) {\n copy.rawContent = turn.rawContent;\n }\n if (turn.sourceFormat !== undefined) {\n copy.sourceFormat = turn.sourceFormat;\n }\n // #3251 scope pins: strings copy by value; the plan object is an immutable\n // config-derived identity passed by reference.\n if (turn.writeNamespaceOverride !== undefined) {\n copy.writeNamespaceOverride = turn.writeNamespaceOverride;\n }\n if (turn.principalOverride !== undefined) {\n copy.principalOverride = turn.principalOverride;\n }\n if (turn.scopeProfileWritePlan !== undefined) {\n copy.scopeProfileWritePlan = turn.scopeProfileWritePlan;\n }\n if (turn.acceptedCapture === true) {\n copy.acceptedCapture = true;\n }\n const ctx = turn.codingContext;\n if (ctx) {\n // All four fields are identity inputs (branchScope uses branch) — copy\n // explicitly as-is; never a spread of external input.\n copy.codingContext = {\n projectId: ctx.projectId,\n branch: ctx.branch,\n rootPath: ctx.rootPath,\n defaultBranch: ctx.defaultBranch,\n };\n }\n return copy;\n}\n\n/**\n * Remove the turns that a drain already extracted (`drained`) from `turns`.\n * Identity-based (role/content/timestamp/session/...): the same comparison\n * the buffer uses to match live turns, so a stale owner can subtract exactly\n * what the drain took and keep everything else.\n */\nexport function subtractBufferTurns(turns: readonly BufferTurn[], drained: readonly BufferTurn[]): BufferTurn[] {\n if (drained.length === 0) return [...turns];\n const kept = [...turns];\n for (const gone of drained) {\n const at = kept.findIndex((live) => bufferTurnsEqual(live, gone));\n if (at >= 0) kept.splice(at, 1);\n }\n return kept;\n}\n\n/** Per-key pending counts over the IN-MEMORY buffer state (counts only — no\n * turn payloads). The claim store's answer stays authoritative for claimed\n * keys; callers must filter through `BufferDrainClaims.isClaimed` before\n * treating a count here as work. */\nexport interface BufferEntryCounts {\n turns: number;\n retainedTurns: number;\n}\n\nexport function bufferEntryCounts(state: BufferState): Map<string, BufferEntryCounts> {\n const out = new Map<string, BufferEntryCounts>();\n const entries = state.entries;\n if (!entries) return out;\n for (const [key, entry] of Object.entries(entries)) {\n out.set(key, { turns: entry.turns.length, retainedTurns: entry.retainedTurns?.length ?? 0 });\n }\n return out;\n}\n\nexport function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn): boolean {\n if (!left) return false;\n return (\n left.role === right.role &&\n left.content === right.content &&\n left.timestamp === right.timestamp &&\n left.sessionKey === right.sessionKey &&\n left.sessionOwnerPrincipal === right.sessionOwnerPrincipal &&\n left.logicalSessionKey === right.logicalSessionKey &&\n left.providerThreadId === right.providerThreadId &&\n left.turnFingerprint === right.turnFingerprint &&\n left.persistProcessedFingerprint === right.persistProcessedFingerprint &&\n left.sourceConnector === right.sourceConnector &&\n left.originRole === right.originRole &&\n left.ambientCapture === right.ambientCapture &&\n left.sourceValidAt === right.sourceValidAt &&\n left.acceptedCapture === right.acceptedCapture &&\n left.codingContext?.projectId === right.codingContext?.projectId &&\n left.codingContext?.rootPath === right.codingContext?.rootPath &&\n left.codingContext?.branch === right.codingContext?.branch &&\n left.codingContext?.defaultBranch === right.codingContext?.defaultBranch &&\n // #3251 scope pins compare by identity triple, not deep plan equality: a\n // restart-rehydrated plan must still match the pin it was accepted under.\n left.writeNamespaceOverride === right.writeNamespaceOverride &&\n left.principalOverride === right.principalOverride &&\n scopeProfilePlanIdentity(left.scopeProfileWritePlan) ===\n scopeProfilePlanIdentity(right.scopeProfileWritePlan)\n );\n}\n\n/**\n * Canonical identity key for a turn, injective over `bufferTurnsEqual`\n * equality classes. Every compared field is a primitive (or the #3251 plan\n * identity string), so set-membership replaces pairwise comparison and turn\n * merges run O(n + m) instead of O(n × m). JSON.stringify of a string is\n * injective (it escapes control characters), and the raw `\\u0001` sentinels\n * distinguish undefined from null — a distinction `===` makes and plain\n * stringification would lose.\n */\nexport function bufferTurnIdentityKey(turn: BufferTurn): string {\n const opt = (value: string | null | undefined): string =>\n value === undefined ? \"\\u0001undefined\" : value === null ? \"\\u0001null\" : JSON.stringify(value);\n const optFlag = (value: boolean | undefined): string =>\n value === undefined ? \"\\u0001undefined\" : String(value);\n const ctx = turn.codingContext;\n return JSON.stringify([\n turn.role,\n turn.content,\n turn.timestamp,\n opt(turn.sessionKey),\n opt(turn.sessionOwnerPrincipal),\n opt(turn.logicalSessionKey),\n turn.providerThreadId === undefined ? \"\\u0001undefined\" : turn.providerThreadId === null ? \"\\u0001null\" : JSON.stringify(turn.providerThreadId),\n opt(turn.turnFingerprint),\n optFlag(turn.persistProcessedFingerprint),\n opt(turn.sourceConnector),\n opt(turn.originRole),\n optFlag(turn.ambientCapture),\n opt(turn.sourceValidAt),\n optFlag(turn.acceptedCapture),\n opt(ctx?.projectId),\n opt(ctx?.rootPath),\n opt(ctx?.branch),\n opt(ctx?.defaultBranch),\n opt(turn.writeNamespaceOverride),\n opt(turn.principalOverride),\n opt(turn.scopeProfileWritePlan ? scopeProfilePlanIdentity(turn.scopeProfileWritePlan) : undefined),\n ]);\n}\n\n/**\n * Identity-dedupe `incoming` turns into `existing` (same contract as the\n * claim-merge `mergeTurns`): identical turns are dropped — the EXISTING\n * entry is kept — genuinely-new turns append in arrival order, and\n * duplicates within `incoming` collapse to their first occurrence. O(n + m)\n * via `bufferTurnIdentityKey` membership instead of pairwise equality.\n */\nexport function mergeBufferTurnsByIdentity(\n existing: readonly BufferTurn[],\n incoming: readonly BufferTurn[],\n): BufferTurn[] {\n const merged = [...existing];\n const seen = new Set(merged.map(bufferTurnIdentityKey));\n for (const turn of incoming) {\n const key = bufferTurnIdentityKey(turn);\n if (seen.has(key)) continue;\n seen.add(key);\n merged.push(turn);\n }\n return merged;\n}\n\n/** Replace only the processed snapshot; callers without one retain legacy whole-slot semantics. */\nexport function selectRetainedDeferredTurns(\n existing: readonly BufferTurn[],\n turns: BufferTurn[],\n max: number,\n originalTurns?: readonly BufferTurn[],\n): BufferTurn[] {\n const others = originalTurns === undefined ? [] : existing.filter(\n (turn) => !originalTurns.some((original) => bufferTurnsEqual(turn, original)),\n );\n if (!Array.isArray(turns) || max <= 0) return others;\n return [...others, ...turns.slice(-max).map(copyBufferTurn)];\n}\n\nexport function hasPinnedCaptureScope(turn: BufferTurn): boolean {\n return turn.acceptedCapture === true ||\n turn.writeNamespaceOverride !== undefined ||\n turn.scopeProfileWritePlan !== undefined;\n}\n\n/** Stable identity string for a pinned scope-profile plan (#3251). */\nexport function scopeProfilePlanIdentity(\n plan: BufferTurn[\"scopeProfileWritePlan\"],\n): string {\n if (!plan) return \"\";\n return `${plan.profileId}\\u0000${plan.writeLayer}\\u0000${plan.writeNamespace}`;\n}\n\nexport interface ExtractionScopeGroup {\n writeNamespaceOverride?: string;\n principalOverride?: string;\n scopeProfileWritePlan?: BufferTurn[\"scopeProfileWritePlan\"];\n codingContext?: BufferTurn[\"codingContext\"];\n turns: BufferTurn[];\n}\n\n/**\n * Partition extraction turns by their durable scope pins (#3251). Turns\n * captured under different namespaces / principals / scope profiles /\n * project bindings must never be extracted together — each group runs its\n * own extraction under its own scope. Native (unpinned) turns form their own\n * group and keep today's single-store routing.\n */\nexport function groupTurnsByExtractionScope(turns: BufferTurn[]): ExtractionScopeGroup[] {\n const groups = new Map<string, ExtractionScopeGroup>();\n for (const turn of turns) {\n const key = [\n turn.writeNamespaceOverride ?? \"\",\n turn.principalOverride ?? \"\",\n scopeProfilePlanIdentity(turn.scopeProfileWritePlan),\n // Accepted captures are deliberately accepted content: they must never\n // share a scope group with ordinary turns, or a group-level capture\n // policy (the explicit-mode gate, the planless-accepted null pin) is\n // applied to ordinary conversation turns because `.some(acceptedCapture)`\n // only looked at the merged group.\n turn.acceptedCapture === true ? \"accepted\" : \"\",\n turn.codingContext?.projectId ?? \"\",\n turn.codingContext?.rootPath ?? \"\",\n turn.codingContext?.branch ?? \"\",\n turn.codingContext?.defaultBranch ?? \"\",\n ].join(\"\\u0000\");\n const group = groups.get(key);\n if (group) {\n group.turns.push(turn);\n } else {\n const created: ExtractionScopeGroup = { turns: [turn] };\n if (turn.writeNamespaceOverride !== undefined) created.writeNamespaceOverride = turn.writeNamespaceOverride;\n if (turn.principalOverride !== undefined) created.principalOverride = turn.principalOverride;\n if (turn.scopeProfileWritePlan !== undefined) created.scopeProfileWritePlan = turn.scopeProfileWritePlan;\n if (turn.codingContext !== undefined) created.codingContext = turn.codingContext;\n groups.set(key, created);\n }\n }\n return [...groups.values()];\n}\n\nexport function bufferTurnArraysEqual(\n left: readonly BufferTurn[],\n right: readonly BufferTurn[],\n): boolean {\n return left.length === right.length && left.every((turn, index) => bufferTurnsEqual(turn, right[index]));\n}\n\nexport function bufferTurnArrayIsSuffixOfSnapshot(\n liveTurns: readonly BufferTurn[],\n snapshot: readonly BufferTurn[],\n): boolean {\n if (liveTurns.length === 0 || liveTurns.length > snapshot.length) {\n return false;\n }\n const offset = snapshot.length - liveTurns.length;\n return liveTurns.every((turn, index) => bufferTurnsEqual(turn, snapshot[offset + index]));\n}\n\nexport function liveTurnsFromExtractionSnapshot(\n entry: BufferEntryState,\n extractedTurns: readonly BufferTurn[],\n): readonly BufferTurn[] {\n const retainedTurns = entry.retainedTurns ?? [];\n if (\n retainedTurns.length > 0 &&\n extractedTurns.length >= retainedTurns.length &&\n retainedTurns.every((turn, index) => bufferTurnsEqual(extractedTurns[index], turn))\n ) {\n const withoutRetainedPrefix = extractedTurns.slice(retainedTurns.length);\n if (withoutRetainedPrefix.length > 0 && matchingPrefixLength(entry.turns, withoutRetainedPrefix) > 0) {\n return withoutRetainedPrefix;\n }\n }\n return extractedTurns;\n}\n\nfunction matchingPrefixLength(\n liveTurns: readonly BufferTurn[],\n extractedTurns: readonly BufferTurn[],\n): number {\n let index = 0;\n while (\n index < liveTurns.length &&\n index < extractedTurns.length &&\n bufferTurnsEqual(liveTurns[index], extractedTurns[index])\n ) {\n index += 1;\n }\n return index;\n}\n\nexport function matchingQueuedExtractionPrefixLength(\n liveTurns: readonly BufferTurn[],\n extractedTurns: readonly BufferTurn[],\n): number {\n let bestMatchedCount = 0;\n for (let start = 0; start < extractedTurns.length; start += 1) {\n const matchedCount = matchingPrefixLength(liveTurns, extractedTurns.slice(start));\n if (matchedCount > bestMatchedCount) {\n bestMatchedCount = matchedCount;\n if (bestMatchedCount === liveTurns.length) break;\n }\n }\n return bestMatchedCount;\n}\n\nexport class ProbeTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`probe exceeded ${timeoutMs}ms`);\n this.name = \"ProbeTimeoutError\";\n }\n}\n\nexport function probeWithTimeout<T>(inflight: Promise<T>, timeoutMs: number): Promise<T> {\n let timer: NodeJS.Timeout | null = null;\n const timeout = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(new ProbeTimeoutError(timeoutMs)), timeoutMs);\n if (typeof (timer as NodeJS.Timeout).unref === \"function\") {\n (timer as NodeJS.Timeout).unref();\n }\n });\n return Promise.race([inflight, timeout]).finally(() => {\n if (timer) clearTimeout(timer);\n });\n}\n"],"mappings":"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","names":[]}