@remnic/core 10.0.84 → 10.0.86

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 (545) hide show
  1. package/dist/access-admin-ops-surface.js +90 -88
  2. package/dist/access-authorization-probe.js +91 -89
  3. package/dist/access-boundary.d.ts.map +1 -1
  4. package/dist/access-boundary.js +90 -88
  5. package/dist/access-cli.js +198 -194
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-errors.d.ts +14 -0
  8. package/dist/access-errors.d.ts.map +1 -1
  9. package/dist/access-errors.js +5 -1
  10. package/dist/access-extraction-force-flush.js +90 -88
  11. package/dist/access-http-briefing.js +91 -89
  12. package/dist/access-http-query.js +92 -90
  13. package/dist/access-http.d.ts +12 -0
  14. package/dist/access-http.d.ts.map +1 -1
  15. package/dist/access-http.js +119 -116
  16. package/dist/access-identity-continuity-surface.js +90 -88
  17. package/dist/access-lcm-surface.js +90 -88
  18. package/dist/access-mcp-frames.d.ts +29 -0
  19. package/dist/access-mcp-frames.d.ts.map +1 -0
  20. package/dist/access-mcp-frames.js +10 -0
  21. package/dist/access-mcp.d.ts +1 -1
  22. package/dist/access-mcp.d.ts.map +1 -1
  23. package/dist/access-mcp.js +105 -102
  24. package/dist/access-memory-search-fanout.js +2 -2
  25. package/dist/access-namespace-preflight.js +90 -88
  26. package/dist/access-observe-write-category.js +15 -14
  27. package/dist/access-observe-write-surface.d.ts.map +1 -1
  28. package/dist/access-observe-write-surface.js +90 -88
  29. package/dist/access-offline-manifest.js +10 -10
  30. package/dist/access-operations-batch.js +94 -92
  31. package/dist/access-operations.js +101 -99
  32. package/dist/access-recall-concurrency.js +90 -88
  33. package/dist/access-recall-diagnostics.js +2 -2
  34. package/dist/access-recall-response.js +90 -88
  35. package/dist/access-recall-surface.js +90 -88
  36. package/dist/access-schema.d.ts.map +1 -1
  37. package/dist/access-schema.js +10 -9
  38. package/dist/access-service.d.ts +3 -3
  39. package/dist/access-service.d.ts.map +1 -1
  40. package/dist/access-service.js +90 -88
  41. package/dist/access-surface-catalog.js +92 -90
  42. package/dist/access-surface-catalog.js.map +1 -1
  43. package/dist/access-token-capabilities.js +2 -2
  44. package/dist/active-recall.js +38 -38
  45. package/dist/adapters/index.js +7 -7
  46. package/dist/adapters/registry.js +3 -3
  47. package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
  48. package/dist/briefing-deadline.d.ts +2 -2
  49. package/dist/briefing-deadline.d.ts.map +1 -1
  50. package/dist/briefing-deadline.js +1 -1
  51. package/dist/briefing.js +25 -25
  52. package/dist/buffer-admission.d.ts +22 -0
  53. package/dist/buffer-admission.d.ts.map +1 -0
  54. package/dist/buffer-admission.js +13 -0
  55. package/dist/buffer-eviction.d.ts +4 -5
  56. package/dist/buffer-eviction.d.ts.map +1 -1
  57. package/dist/buffer-eviction.js +1 -1
  58. package/dist/buffer-persistence.d.ts +8 -4
  59. package/dist/buffer-persistence.d.ts.map +1 -1
  60. package/dist/buffer-persistence.js +1 -1
  61. package/dist/buffer-turn-helpers.d.ts +12 -0
  62. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  63. package/dist/buffer-turn-helpers.js +9 -1
  64. package/dist/buffer-types.d.ts +24 -1
  65. package/dist/buffer-types.d.ts.map +1 -1
  66. package/dist/buffer.d.ts +21 -19
  67. package/dist/buffer.d.ts.map +1 -1
  68. package/dist/buffer.js +17 -13
  69. package/dist/calibration.js +3 -3
  70. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
  71. package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
  72. package/dist/capsule-gunzip-KGBDGF7P.js.map +1 -0
  73. package/dist/capsule-import-gate.d.ts +11 -0
  74. package/dist/capsule-import-gate.d.ts.map +1 -0
  75. package/dist/capsule-import-gate.js +12 -0
  76. package/dist/capsule-import-gate.js.map +1 -0
  77. package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
  78. package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
  79. package/dist/causal-behavior.js +2 -2
  80. package/dist/causal-chain.js +2 -2
  81. package/dist/causal-consolidation.js +27 -27
  82. package/dist/causal-retrieval.js +2 -2
  83. package/dist/causal-trajectory-graph.js +2 -2
  84. package/dist/causal-trajectory.js +1 -1
  85. package/dist/{chunk-MSIRVXQ3.js → chunk-2G4TV2ML.js} +12 -12
  86. package/dist/{chunk-B74NPUHT.js → chunk-2JG2NI7C.js} +54 -54
  87. package/dist/chunk-2JG2NI7C.js.map +1 -0
  88. package/dist/{chunk-IR4C3DP2.js → chunk-2K3AUDH7.js} +2 -2
  89. package/dist/{chunk-2IW5PT7U.js → chunk-2N4OUSIA.js} +2 -2
  90. package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
  91. package/dist/{chunk-QZSIDSB2.js → chunk-2NUDHVYM.js} +2 -2
  92. package/dist/{chunk-HPFVZPRH.js → chunk-2U4LMSEX.js} +3 -3
  93. package/dist/{chunk-WHQXWFH7.js → chunk-2XOFVTTT.js} +2 -2
  94. package/dist/{chunk-I6ORDT54.js → chunk-32CA7B6L.js} +2 -2
  95. package/dist/{chunk-T77AX6DG.js → chunk-32YX7EJ3.js} +4 -4
  96. package/dist/{chunk-HLKUZW42.js → chunk-3CTKEXXF.js} +144 -156
  97. package/dist/chunk-3CTKEXXF.js.map +1 -0
  98. package/dist/{chunk-HLWXCDUQ.js → chunk-3DAWHP5K.js} +5 -5
  99. package/dist/{chunk-ZJJFUEPO.js → chunk-3PJEDHLD.js} +2 -2
  100. package/dist/{chunk-PJYK7HH2.js → chunk-3UZ2VMKD.js} +2 -2
  101. package/dist/{chunk-IN4OFAHX.js → chunk-3Y5X6YFH.js} +2 -2
  102. package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
  103. package/dist/{chunk-T6T2RMWW.js → chunk-4GMI5HFH.js} +12 -12
  104. package/dist/{chunk-YOEYL6CH.js → chunk-4LCVA2LW.js} +3 -3
  105. package/dist/{chunk-66JOLMWS.js → chunk-4X7HQKAG.js} +2 -2
  106. package/dist/{chunk-65SKM4BF.js → chunk-4XYAWK5H.js} +2 -2
  107. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  108. package/dist/{chunk-TPX7XXY4.js → chunk-5SOSQTVE.js} +2 -2
  109. package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
  110. package/dist/{chunk-BK7AJUWB.js → chunk-6BCWFCDG.js} +7 -7
  111. package/dist/{chunk-V27FK6D6.js → chunk-6XTUZKCZ.js} +2 -2
  112. package/dist/{chunk-KASJJGRG.js → chunk-6ZELOK6O.js} +3 -8
  113. package/dist/chunk-6ZELOK6O.js.map +1 -0
  114. package/dist/{chunk-LECSS7EE.js → chunk-73UZZFJC.js} +3 -3
  115. package/dist/{chunk-4EACCIPO.js → chunk-77PDJ4OE.js} +3 -3
  116. package/dist/{chunk-GQZSGXR6.js → chunk-7GSL4LBG.js} +2 -2
  117. package/dist/{chunk-B6SPPYGJ.js → chunk-7IWRLYFJ.js} +2 -2
  118. package/dist/{chunk-R45L4TV6.js → chunk-7LCGFUMB.js} +6 -6
  119. package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
  120. package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
  121. package/dist/{chunk-ZXF3O7P4.js → chunk-ARRTLPRC.js} +2 -2
  122. package/dist/{chunk-S5CQI2KF.js → chunk-B3GKQ6DU.js} +2 -2
  123. package/dist/{chunk-XRBHIR7Q.js → chunk-B56LORLY.js} +2 -2
  124. package/dist/{chunk-KO3YBHOV.js → chunk-BIVKPQ6Q.js} +3 -3
  125. package/dist/{chunk-QZBTIYKD.js → chunk-BLSHFIR6.js} +7 -6
  126. package/dist/chunk-BLSHFIR6.js.map +1 -0
  127. package/dist/{chunk-54FDG7K7.js → chunk-C2HORX76.js} +3 -3
  128. package/dist/{chunk-WAD32CZG.js → chunk-CC4V7HNP.js} +43 -30
  129. package/dist/chunk-CC4V7HNP.js.map +1 -0
  130. package/dist/{chunk-LGEH473S.js → chunk-CJCTKL6Y.js} +9 -1
  131. package/dist/chunk-CJCTKL6Y.js.map +1 -0
  132. package/dist/{chunk-E33UJHY3.js → chunk-CN7Q3T2J.js} +4 -4
  133. package/dist/{chunk-YUTG35M4.js → chunk-CYCEROPR.js} +3 -3
  134. package/dist/{chunk-TFLPDNQV.js → chunk-D5EV6Y2L.js} +3 -3
  135. package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
  136. package/dist/{chunk-B4ZU34JQ.js → chunk-DT7SNJH6.js} +3 -3
  137. package/dist/{chunk-VLMBSGQF.js → chunk-DZ7DPDVW.js} +10 -8
  138. package/dist/chunk-DZ7DPDVW.js.map +1 -0
  139. package/dist/{chunk-FVRTCYZ3.js → chunk-EA2LA5RI.js} +2 -2
  140. package/dist/{chunk-WBN66HPI.js → chunk-ELHJK7LM.js} +6 -6
  141. package/dist/{chunk-77R7BQZ2.js → chunk-EQ4U73UH.js} +77 -24
  142. package/dist/chunk-EQ4U73UH.js.map +1 -0
  143. package/dist/{chunk-3JPTUCD7.js → chunk-EUBDYOLD.js} +32 -23
  144. package/dist/chunk-EUBDYOLD.js.map +1 -0
  145. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  146. package/dist/{chunk-YAUHSNPB.js → chunk-F74G4KIH.js} +2 -2
  147. package/dist/{chunk-MPGLM2VU.js → chunk-FF3EFLUT.js} +7 -5
  148. package/dist/chunk-FF3EFLUT.js.map +1 -0
  149. package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
  150. package/dist/chunk-FKBGK3NC.js +62 -0
  151. package/dist/chunk-FKBGK3NC.js.map +1 -0
  152. package/dist/{chunk-X2KO4FK5.js → chunk-FNFURZBZ.js} +5 -5
  153. package/dist/chunk-FNFURZBZ.js.map +1 -0
  154. package/dist/{chunk-KESJ5XX3.js → chunk-G54BOKZ6.js} +2 -2
  155. package/dist/{chunk-YK7GOPIE.js → chunk-G67EUQAB.js} +9 -7
  156. package/dist/chunk-G67EUQAB.js.map +1 -0
  157. package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
  158. package/dist/{chunk-OQSSQOKQ.js → chunk-GULUWOA5.js} +2 -2
  159. package/dist/{chunk-K6RTQZA4.js → chunk-HFUKXOUW.js} +2 -2
  160. package/dist/{chunk-WV254W4V.js → chunk-HLN2HOXX.js} +2 -2
  161. package/dist/{chunk-FUOAIJKQ.js → chunk-IBDWYRDR.js} +3 -3
  162. package/dist/{chunk-Z3DDQXM4.js → chunk-IBGU6AN6.js} +45 -45
  163. package/dist/chunk-IBGU6AN6.js.map +1 -0
  164. package/dist/{chunk-AJG2TAMR.js → chunk-IEEV27BC.js} +2 -2
  165. package/dist/chunk-IGLJNHOX.js +62 -0
  166. package/dist/chunk-IGLJNHOX.js.map +1 -0
  167. package/dist/{chunk-LTDLJCMQ.js → chunk-IJ3OEECW.js} +4 -4
  168. package/dist/{chunk-BK4FBHZ3.js → chunk-IPD5GC7C.js} +3 -3
  169. package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
  170. package/dist/{chunk-GCAQ3VUJ.js → chunk-JM2YZKCQ.js} +2 -2
  171. package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
  172. package/dist/{chunk-LU66I56Q.js → chunk-KVSOEVAF.js} +61 -12
  173. package/dist/chunk-KVSOEVAF.js.map +1 -0
  174. package/dist/chunk-M3BEJJNV.js +31 -0
  175. package/dist/chunk-M3BEJJNV.js.map +1 -0
  176. package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
  177. package/dist/{chunk-XHGYZRVW.js → chunk-NIVKG3DS.js} +5 -5
  178. package/dist/{chunk-VZELUMKK.js → chunk-NQP4ZYGG.js} +2 -2
  179. package/dist/{chunk-7PMOCQOL.js → chunk-NT7F7WJQ.js} +192 -63
  180. package/dist/chunk-NT7F7WJQ.js.map +1 -0
  181. package/dist/{chunk-YO2JEX2Z.js → chunk-OGBQMY46.js} +2 -2
  182. package/dist/{chunk-TLHF75OR.js → chunk-OL553AWT.js} +12 -12
  183. package/dist/{chunk-CLVILSBR.js → chunk-OZNBZJ3S.js} +22 -22
  184. package/dist/{chunk-VUROWXFL.js → chunk-P452SEKO.js} +2 -2
  185. package/dist/{chunk-JX3E4XCW.js → chunk-PDOOUXCS.js} +2 -2
  186. package/dist/{chunk-6QYPL6NI.js → chunk-PKZ274PI.js} +3 -3
  187. package/dist/{chunk-KJCL2FZO.js → chunk-QD5R6U5J.js} +2 -2
  188. package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
  189. package/dist/{chunk-VKDAJJQP.js → chunk-QEZ3DW5O.js} +11 -11
  190. package/dist/{chunk-WYPCJK6K.js → chunk-QKERFEJS.js} +2 -2
  191. package/dist/{chunk-BDN3TDGV.js → chunk-QKES7GU6.js} +2 -2
  192. package/dist/{chunk-PCQWKMOL.js → chunk-QNSZYTSG.js} +2 -2
  193. package/dist/chunk-QV2VZKD5.js +31 -0
  194. package/dist/chunk-QV2VZKD5.js.map +1 -0
  195. package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
  196. package/dist/{chunk-CIIHIAJ2.js → chunk-ROGO7FRA.js} +6 -5
  197. package/dist/{chunk-CIIHIAJ2.js.map → chunk-ROGO7FRA.js.map} +1 -1
  198. package/dist/{chunk-NUSAAN75.js → chunk-ROKDHO74.js} +2 -2
  199. package/dist/{chunk-D5SSBCYY.js → chunk-RWGRSKN5.js} +7 -5
  200. package/dist/chunk-RWGRSKN5.js.map +1 -0
  201. package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
  202. package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
  203. package/dist/{chunk-XT45WCKX.js → chunk-SYKJBS7S.js} +10 -10
  204. package/dist/{chunk-5BE3FLKL.js → chunk-TAUHAP5J.js} +4 -4
  205. package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
  206. package/dist/{chunk-XYP5WBDO.js → chunk-TQ4SCPGQ.js} +76 -60
  207. package/dist/chunk-TQ4SCPGQ.js.map +1 -0
  208. package/dist/{chunk-K3B72DKJ.js → chunk-TRMZHGIX.js} +8 -8
  209. package/dist/{chunk-WBDIZWJQ.js → chunk-TSS3D7ZY.js} +2 -2
  210. package/dist/{chunk-6LCWKS62.js → chunk-UAUV3BWS.js} +104 -121
  211. package/dist/chunk-UAUV3BWS.js.map +1 -0
  212. package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
  213. package/dist/chunk-UDKWFDV2.js.map +1 -0
  214. package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
  215. package/dist/{chunk-GGJFRVRD.js → chunk-US7KPQA5.js} +2 -2
  216. package/dist/{chunk-FJ2VJTGA.js → chunk-UWRHHRDK.js} +3 -3
  217. package/dist/{chunk-4KLQI3L2.js → chunk-UXXK523X.js} +40 -2
  218. package/dist/chunk-UXXK523X.js.map +1 -0
  219. package/dist/{chunk-SDESKFKT.js → chunk-UYHDMCY2.js} +2 -2
  220. package/dist/{chunk-3T3VHXUH.js → chunk-VKILERSL.js} +2 -2
  221. package/dist/{chunk-2TGIKMMZ.js → chunk-WXDTOE6F.js} +2 -2
  222. package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
  223. package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
  224. package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
  225. package/dist/chunk-XO23OKMK.js.map +1 -0
  226. package/dist/{chunk-WC475JOG.js → chunk-XU37EJTW.js} +24 -24
  227. package/dist/chunk-XU37EJTW.js.map +1 -0
  228. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  229. package/dist/{chunk-QA3Y2O6H.js → chunk-YAVIBVCU.js} +2 -2
  230. package/dist/{chunk-2XJZLSXE.js → chunk-YL4UKMD7.js} +124 -58
  231. package/dist/chunk-YL4UKMD7.js.map +1 -0
  232. package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
  233. package/dist/{chunk-HWHNQVRV.js → chunk-YMJRLT7S.js} +28 -8
  234. package/dist/chunk-YMJRLT7S.js.map +1 -0
  235. package/dist/{chunk-EBOHFZI4.js → chunk-YRZPBN46.js} +3 -3
  236. package/dist/{chunk-FMFCQRDM.js → chunk-ZANNIXXE.js} +3 -3
  237. package/dist/{chunk-HQGATKG2.js → chunk-ZBFLV657.js} +3 -3
  238. package/dist/{chunk-6L63IN3M.js → chunk-ZOJUWVAU.js} +1 -1
  239. package/dist/cli.js +144 -141
  240. package/dist/coding/export-okf-codegraph.js +24 -24
  241. package/dist/coding/pre-action-gate.js +2 -2
  242. package/dist/compounding/engine.js +26 -26
  243. package/dist/config.js +35 -35
  244. package/dist/connectors/codex-materialize-runner.js +24 -24
  245. package/dist/connectors/index.js +28 -28
  246. package/dist/contradiction/index.js +4 -4
  247. package/dist/convergence-refresh.js +26 -26
  248. package/dist/corpus-watermark.js +28 -28
  249. package/dist/dashboard-runtime.js +2 -2
  250. package/dist/deep-recall-config.js +2 -2
  251. package/dist/deep-recall-http-glue.js +12 -12
  252. package/dist/entity-retrieval.js +25 -25
  253. package/dist/event-order-recall.js +2 -2
  254. package/dist/explicit-capture.js +6 -6
  255. package/dist/external-wiki-access.js +91 -89
  256. package/dist/extraction-evidence-class.js +2 -2
  257. package/dist/extraction-quote-grounding.d.ts +5 -1
  258. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  259. package/dist/extraction-quote-grounding.js +1 -1
  260. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  261. package/dist/extraction-source-grounding-rules.d.ts.map +1 -1
  262. package/dist/extraction-source-grounding-rules.js +2 -2
  263. package/dist/extraction-source-grounding.d.ts +1 -1
  264. package/dist/extraction-source-grounding.d.ts.map +1 -1
  265. package/dist/extraction-source-grounding.js +5 -5
  266. package/dist/extraction-span-materialize.js +2 -2
  267. package/dist/extraction.d.ts.map +1 -1
  268. package/dist/extraction.js +43 -43
  269. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  270. package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
  271. package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
  272. package/dist/graph-jsonl.js +3 -3
  273. package/dist/graph-snapshot.js +3 -3
  274. package/dist/graph.js +2 -2
  275. package/dist/harmonic-retrieval.js +24 -24
  276. package/dist/index.js +255 -251
  277. package/dist/lcm/engine.js +2 -2
  278. package/dist/lcm/index.js +5 -5
  279. package/dist/local-llm.js +3 -3
  280. package/dist/location/index.js +28 -28
  281. package/dist/maintenance/memory-governance.js +24 -24
  282. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +24 -24
  283. package/dist/maintenance/rebuild-memory-projection-runner.js +26 -26
  284. package/dist/maintenance/rebuild-memory-projection.js +25 -25
  285. package/dist/memory-browse-http-glue.js +2 -2
  286. package/dist/memory-browse-operations.js +91 -89
  287. package/dist/memory-browse.js +5 -5
  288. package/dist/memory-subject.js +2 -2
  289. package/dist/namespaces/migrate.js +25 -25
  290. package/dist/namespaces/search.d.ts +9 -0
  291. package/dist/namespaces/search.d.ts.map +1 -1
  292. package/dist/namespaces/search.js +13 -7
  293. package/dist/namespaces/storage.js +24 -24
  294. package/dist/offline-sync-changeset-generations.js +3 -3
  295. package/dist/offline-sync-embedding-generation.js +2 -2
  296. package/dist/offline-sync-impression-drain.js +2 -2
  297. package/dist/offline-sync-snapshot-stream.js +6 -6
  298. package/dist/offline-sync.js +5 -5
  299. package/dist/operator-doctor-corpus.js +31 -31
  300. package/dist/operator-doctor-replica.js +4 -4
  301. package/dist/operator-toolkit.js +49 -49
  302. package/dist/orchestration/backlog-scheduler.d.ts +3 -2
  303. package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
  304. package/dist/orchestration/backlog-scheduler.js +209 -205
  305. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  306. package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
  307. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
  308. package/dist/orchestration/buffer-drain-runner.js +199 -195
  309. package/dist/orchestration/extraction-persist.d.ts.map +1 -1
  310. package/dist/orchestration/fact-dedup-window.d.ts +1 -4
  311. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  312. package/dist/orchestration/maintenance.js +41 -41
  313. package/dist/orchestration/turn-ingestion.d.ts +2 -9
  314. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  315. package/dist/orchestrator.js +198 -194
  316. package/dist/provenance.js +2 -2
  317. package/dist/qmd.js +4 -4
  318. package/dist/recall-navigation-http-glue.js +2 -2
  319. package/dist/recall-navigation-operations.js +91 -89
  320. package/dist/recall-navigation.js +90 -88
  321. package/dist/recall-state-view-admission.js +5 -5
  322. package/dist/recall-why-service.js +5 -5
  323. package/dist/reconcile/manifest.js +2 -2
  324. package/dist/replica-divergence.js +3 -3
  325. package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
  326. package/dist/resolution-23ZBDM6A.js.map +1 -0
  327. package/dist/resume-bundles.js +36 -36
  328. package/dist/salvage-envelope.js +4 -4
  329. package/dist/search/factory.js +6 -6
  330. package/dist/search/index.js +12 -12
  331. package/dist/semantic-consolidation.js +25 -25
  332. package/dist/semantic-rule-promotion.js +24 -24
  333. package/dist/semantic-rule-verifier.js +24 -24
  334. package/dist/source-agent-qualifier.d.ts +2 -0
  335. package/dist/source-agent-qualifier.d.ts.map +1 -1
  336. package/dist/source-agent-qualifier.js +36 -36
  337. package/dist/standup/build.js +25 -25
  338. package/dist/storage/buffer-drain-claims.d.ts +18 -6
  339. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  340. package/dist/storage/buffer-drain-claims.js +2 -2
  341. package/dist/storage/buffer-session-files.js +2 -2
  342. package/dist/storage/content-hash-index.d.ts +8 -0
  343. package/dist/storage/content-hash-index.d.ts.map +1 -1
  344. package/dist/storage.d.ts +10 -1
  345. package/dist/storage.d.ts.map +1 -1
  346. package/dist/storage.js +23 -23
  347. package/dist/subject-config.js +3 -3
  348. package/dist/summarizer.js +4 -4
  349. package/dist/support-passport/index.js +93 -91
  350. package/dist/tokens.js +3 -3
  351. package/dist/transfer/autodetect.js +1 -1
  352. package/dist/transfer/backup.js +2 -2
  353. package/dist/transfer/capsule-export.js +3 -3
  354. package/dist/transfer/capsule-gunzip.d.ts +14 -0
  355. package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
  356. package/dist/transfer/capsule-import.d.ts.map +1 -1
  357. package/dist/transfer/capsule-import.js +3 -2
  358. package/dist/transfer/capsule-merge.d.ts.map +1 -1
  359. package/dist/transfer/export-okf.js +24 -24
  360. package/dist/verified-recall.js +24 -24
  361. package/dist/who-knows-cli.js +26 -26
  362. package/dist/who-knows.js +25 -25
  363. package/dist/write-envelope.js +3 -3
  364. package/package.json +2 -2
  365. package/src/access-boundary.ts +15 -0
  366. package/src/access-errors.ts +22 -0
  367. package/src/access-fleet-wide-work-peer.test.ts +236 -0
  368. package/src/access-http-peer-fleet-wide.test.ts +192 -0
  369. package/src/access-http.test.ts +249 -4
  370. package/src/access-http.ts +114 -39
  371. package/src/access-mcp-frames.test.ts +47 -0
  372. package/src/access-mcp-frames.ts +74 -0
  373. package/src/access-mcp.test.ts +210 -0
  374. package/src/access-mcp.ts +47 -37
  375. package/src/access-observe-replay-durability.test.ts +40 -4
  376. package/src/access-observe-write-surface.ts +4 -4
  377. package/src/access-operations-batch.ts +23 -12
  378. package/src/access-proto-injection.test.ts +340 -0
  379. package/src/access-schema.ts +31 -5
  380. package/src/access-service-capsule-import.test.ts +235 -0
  381. package/src/access-service.ts +6 -45
  382. package/src/backlog-scheduler.test.ts +443 -3
  383. package/src/briefing-deadline.ts +5 -4
  384. package/src/buffer-admission.ts +45 -0
  385. package/src/buffer-eviction-backpressure.test.ts +211 -0
  386. package/src/buffer-eviction.ts +8 -14
  387. package/src/buffer-incremental-persistence.test.ts +25 -37
  388. package/src/buffer-persistence.ts +15 -6
  389. package/src/buffer-session.test.ts +32 -16
  390. package/src/buffer-turn-helpers.ts +47 -0
  391. package/src/buffer-types.ts +28 -1
  392. package/src/buffer.ts +83 -91
  393. package/src/capsule-import-gate.ts +109 -0
  394. package/src/cli.ts +4 -4
  395. package/src/extraction-quote-grounding.test.ts +125 -0
  396. package/src/extraction-quote-grounding.ts +99 -25
  397. package/src/extraction-source-grounding-rules.ts +6 -1
  398. package/src/extraction-source-grounding.ts +2 -0
  399. package/src/extraction.ts +3 -4
  400. package/src/fact-dedup-window.test.ts +103 -20
  401. package/src/namespaces/search.test.ts +480 -1
  402. package/src/namespaces/search.ts +93 -10
  403. package/src/orchestration/backlog-scheduler.ts +13 -12
  404. package/src/orchestration/buffer-drain-runner.ts +40 -16
  405. package/src/orchestration/extraction-persist.ts +2 -10
  406. package/src/orchestration/fact-dedup-window.ts +5 -12
  407. package/src/orchestration/turn-ingestion.ts +38 -39
  408. package/src/recall-navigation-operations.ts +9 -2
  409. package/src/source-agent-qualifier.ts +3 -0
  410. package/src/storage/buffer-drain-claims.test.ts +330 -6
  411. package/src/storage/buffer-drain-claims.ts +226 -80
  412. package/src/storage/buffer-session-files.ts +2 -2
  413. package/src/storage/content-hash-index.ts +9 -0
  414. package/src/storage.ts +15 -0
  415. package/src/transfer/capsule-gunzip.ts +39 -0
  416. package/src/transfer/capsule-import.test.ts +73 -0
  417. package/src/transfer/capsule-import.ts +2 -2
  418. package/src/transfer/capsule-merge.test.ts +34 -0
  419. package/src/transfer/capsule-merge.ts +2 -10
  420. package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
  421. package/dist/chunk-2XJZLSXE.js.map +0 -1
  422. package/dist/chunk-3JPTUCD7.js.map +0 -1
  423. package/dist/chunk-4KLQI3L2.js.map +0 -1
  424. package/dist/chunk-6LCWKS62.js.map +0 -1
  425. package/dist/chunk-77R7BQZ2.js.map +0 -1
  426. package/dist/chunk-7PMOCQOL.js.map +0 -1
  427. package/dist/chunk-B74NPUHT.js.map +0 -1
  428. package/dist/chunk-D5SSBCYY.js.map +0 -1
  429. package/dist/chunk-HLKUZW42.js.map +0 -1
  430. package/dist/chunk-HWHNQVRV.js.map +0 -1
  431. package/dist/chunk-KASJJGRG.js.map +0 -1
  432. package/dist/chunk-KQUSP6LB.js.map +0 -1
  433. package/dist/chunk-LGEH473S.js.map +0 -1
  434. package/dist/chunk-LU66I56Q.js.map +0 -1
  435. package/dist/chunk-MPDLLLOG.js.map +0 -1
  436. package/dist/chunk-MPGLM2VU.js.map +0 -1
  437. package/dist/chunk-QZBTIYKD.js.map +0 -1
  438. package/dist/chunk-VLMBSGQF.js.map +0 -1
  439. package/dist/chunk-WAD32CZG.js.map +0 -1
  440. package/dist/chunk-WC475JOG.js.map +0 -1
  441. package/dist/chunk-X2KO4FK5.js.map +0 -1
  442. package/dist/chunk-XYP5WBDO.js.map +0 -1
  443. package/dist/chunk-YK7GOPIE.js.map +0 -1
  444. package/dist/chunk-Z3DDQXM4.js.map +0 -1
  445. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
  446. /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
  447. /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → buffer-admission.js.map} +0 -0
  448. /package/dist/{resolution-BNWE5Y25.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  449. /package/dist/{chunk-MSIRVXQ3.js.map → chunk-2G4TV2ML.js.map} +0 -0
  450. /package/dist/{chunk-IR4C3DP2.js.map → chunk-2K3AUDH7.js.map} +0 -0
  451. /package/dist/{chunk-2IW5PT7U.js.map → chunk-2N4OUSIA.js.map} +0 -0
  452. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
  453. /package/dist/{chunk-QZSIDSB2.js.map → chunk-2NUDHVYM.js.map} +0 -0
  454. /package/dist/{chunk-HPFVZPRH.js.map → chunk-2U4LMSEX.js.map} +0 -0
  455. /package/dist/{chunk-WHQXWFH7.js.map → chunk-2XOFVTTT.js.map} +0 -0
  456. /package/dist/{chunk-I6ORDT54.js.map → chunk-32CA7B6L.js.map} +0 -0
  457. /package/dist/{chunk-T77AX6DG.js.map → chunk-32YX7EJ3.js.map} +0 -0
  458. /package/dist/{chunk-HLWXCDUQ.js.map → chunk-3DAWHP5K.js.map} +0 -0
  459. /package/dist/{chunk-ZJJFUEPO.js.map → chunk-3PJEDHLD.js.map} +0 -0
  460. /package/dist/{chunk-PJYK7HH2.js.map → chunk-3UZ2VMKD.js.map} +0 -0
  461. /package/dist/{chunk-IN4OFAHX.js.map → chunk-3Y5X6YFH.js.map} +0 -0
  462. /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
  463. /package/dist/{chunk-T6T2RMWW.js.map → chunk-4GMI5HFH.js.map} +0 -0
  464. /package/dist/{chunk-YOEYL6CH.js.map → chunk-4LCVA2LW.js.map} +0 -0
  465. /package/dist/{chunk-66JOLMWS.js.map → chunk-4X7HQKAG.js.map} +0 -0
  466. /package/dist/{chunk-65SKM4BF.js.map → chunk-4XYAWK5H.js.map} +0 -0
  467. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  468. /package/dist/{chunk-TPX7XXY4.js.map → chunk-5SOSQTVE.js.map} +0 -0
  469. /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
  470. /package/dist/{chunk-BK7AJUWB.js.map → chunk-6BCWFCDG.js.map} +0 -0
  471. /package/dist/{chunk-V27FK6D6.js.map → chunk-6XTUZKCZ.js.map} +0 -0
  472. /package/dist/{chunk-LECSS7EE.js.map → chunk-73UZZFJC.js.map} +0 -0
  473. /package/dist/{chunk-4EACCIPO.js.map → chunk-77PDJ4OE.js.map} +0 -0
  474. /package/dist/{chunk-GQZSGXR6.js.map → chunk-7GSL4LBG.js.map} +0 -0
  475. /package/dist/{chunk-B6SPPYGJ.js.map → chunk-7IWRLYFJ.js.map} +0 -0
  476. /package/dist/{chunk-R45L4TV6.js.map → chunk-7LCGFUMB.js.map} +0 -0
  477. /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
  478. /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
  479. /package/dist/{chunk-ZXF3O7P4.js.map → chunk-ARRTLPRC.js.map} +0 -0
  480. /package/dist/{chunk-S5CQI2KF.js.map → chunk-B3GKQ6DU.js.map} +0 -0
  481. /package/dist/{chunk-XRBHIR7Q.js.map → chunk-B56LORLY.js.map} +0 -0
  482. /package/dist/{chunk-KO3YBHOV.js.map → chunk-BIVKPQ6Q.js.map} +0 -0
  483. /package/dist/{chunk-54FDG7K7.js.map → chunk-C2HORX76.js.map} +0 -0
  484. /package/dist/{chunk-E33UJHY3.js.map → chunk-CN7Q3T2J.js.map} +0 -0
  485. /package/dist/{chunk-YUTG35M4.js.map → chunk-CYCEROPR.js.map} +0 -0
  486. /package/dist/{chunk-TFLPDNQV.js.map → chunk-D5EV6Y2L.js.map} +0 -0
  487. /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
  488. /package/dist/{chunk-B4ZU34JQ.js.map → chunk-DT7SNJH6.js.map} +0 -0
  489. /package/dist/{chunk-FVRTCYZ3.js.map → chunk-EA2LA5RI.js.map} +0 -0
  490. /package/dist/{chunk-WBN66HPI.js.map → chunk-ELHJK7LM.js.map} +0 -0
  491. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  492. /package/dist/{chunk-YAUHSNPB.js.map → chunk-F74G4KIH.js.map} +0 -0
  493. /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
  494. /package/dist/{chunk-KESJ5XX3.js.map → chunk-G54BOKZ6.js.map} +0 -0
  495. /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
  496. /package/dist/{chunk-OQSSQOKQ.js.map → chunk-GULUWOA5.js.map} +0 -0
  497. /package/dist/{chunk-K6RTQZA4.js.map → chunk-HFUKXOUW.js.map} +0 -0
  498. /package/dist/{chunk-WV254W4V.js.map → chunk-HLN2HOXX.js.map} +0 -0
  499. /package/dist/{chunk-FUOAIJKQ.js.map → chunk-IBDWYRDR.js.map} +0 -0
  500. /package/dist/{chunk-AJG2TAMR.js.map → chunk-IEEV27BC.js.map} +0 -0
  501. /package/dist/{chunk-LTDLJCMQ.js.map → chunk-IJ3OEECW.js.map} +0 -0
  502. /package/dist/{chunk-BK4FBHZ3.js.map → chunk-IPD5GC7C.js.map} +0 -0
  503. /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
  504. /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-JM2YZKCQ.js.map} +0 -0
  505. /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
  506. /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
  507. /package/dist/{chunk-XHGYZRVW.js.map → chunk-NIVKG3DS.js.map} +0 -0
  508. /package/dist/{chunk-VZELUMKK.js.map → chunk-NQP4ZYGG.js.map} +0 -0
  509. /package/dist/{chunk-YO2JEX2Z.js.map → chunk-OGBQMY46.js.map} +0 -0
  510. /package/dist/{chunk-TLHF75OR.js.map → chunk-OL553AWT.js.map} +0 -0
  511. /package/dist/{chunk-CLVILSBR.js.map → chunk-OZNBZJ3S.js.map} +0 -0
  512. /package/dist/{chunk-VUROWXFL.js.map → chunk-P452SEKO.js.map} +0 -0
  513. /package/dist/{chunk-JX3E4XCW.js.map → chunk-PDOOUXCS.js.map} +0 -0
  514. /package/dist/{chunk-6QYPL6NI.js.map → chunk-PKZ274PI.js.map} +0 -0
  515. /package/dist/{chunk-KJCL2FZO.js.map → chunk-QD5R6U5J.js.map} +0 -0
  516. /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
  517. /package/dist/{chunk-VKDAJJQP.js.map → chunk-QEZ3DW5O.js.map} +0 -0
  518. /package/dist/{chunk-WYPCJK6K.js.map → chunk-QKERFEJS.js.map} +0 -0
  519. /package/dist/{chunk-BDN3TDGV.js.map → chunk-QKES7GU6.js.map} +0 -0
  520. /package/dist/{chunk-PCQWKMOL.js.map → chunk-QNSZYTSG.js.map} +0 -0
  521. /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
  522. /package/dist/{chunk-NUSAAN75.js.map → chunk-ROKDHO74.js.map} +0 -0
  523. /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
  524. /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
  525. /package/dist/{chunk-XT45WCKX.js.map → chunk-SYKJBS7S.js.map} +0 -0
  526. /package/dist/{chunk-5BE3FLKL.js.map → chunk-TAUHAP5J.js.map} +0 -0
  527. /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
  528. /package/dist/{chunk-K3B72DKJ.js.map → chunk-TRMZHGIX.js.map} +0 -0
  529. /package/dist/{chunk-WBDIZWJQ.js.map → chunk-TSS3D7ZY.js.map} +0 -0
  530. /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
  531. /package/dist/{chunk-GGJFRVRD.js.map → chunk-US7KPQA5.js.map} +0 -0
  532. /package/dist/{chunk-FJ2VJTGA.js.map → chunk-UWRHHRDK.js.map} +0 -0
  533. /package/dist/{chunk-SDESKFKT.js.map → chunk-UYHDMCY2.js.map} +0 -0
  534. /package/dist/{chunk-3T3VHXUH.js.map → chunk-VKILERSL.js.map} +0 -0
  535. /package/dist/{chunk-2TGIKMMZ.js.map → chunk-WXDTOE6F.js.map} +0 -0
  536. /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
  537. /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
  538. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  539. /package/dist/{chunk-QA3Y2O6H.js.map → chunk-YAVIBVCU.js.map} +0 -0
  540. /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
  541. /package/dist/{chunk-EBOHFZI4.js.map → chunk-YRZPBN46.js.map} +0 -0
  542. /package/dist/{chunk-FMFCQRDM.js.map → chunk-ZANNIXXE.js.map} +0 -0
  543. /package/dist/{chunk-HQGATKG2.js.map → chunk-ZBFLV657.js.map} +0 -0
  544. /package/dist/{chunk-6L63IN3M.js.map → chunk-ZOJUWVAU.js.map} +0 -0
  545. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -0,0 +1,211 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
4
+ import { parseConfig } from "./config.js";
5
+ import { log } from "./logger.js";
6
+ import { TurnIngestionCoordinator } from "./orchestration/turn-ingestion.js";
7
+ import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
8
+
9
+ class FakeStorage {
10
+ public saved: BufferState | null = null;
11
+ public saveCount = 0;
12
+
13
+ constructor(private readonly initial: BufferState) {}
14
+
15
+ async loadBuffer(): Promise<BufferState> {
16
+ return structuredClone(this.initial);
17
+ }
18
+
19
+ async saveBuffer(state: BufferState): Promise<void> {
20
+ this.saveCount += 1;
21
+ this.saved = structuredClone(state);
22
+ }
23
+ }
24
+
25
+ function makeTurn(key: string, content: string, accepted = false): BufferTurn {
26
+ const turn: BufferTurn = {
27
+ role: "user",
28
+ content,
29
+ timestamp: new Date().toISOString(),
30
+ sessionKey: key,
31
+ };
32
+ if (accepted) turn.acceptedCapture = true;
33
+ return turn;
34
+ }
35
+
36
+ function entry(turns: BufferTurn[]): BufferEntryState {
37
+ return { turns, lastExtractionAt: null, extractionCount: 0 };
38
+ }
39
+
40
+ function emptyEntry(): BufferEntryState {
41
+ return { turns: [], lastExtractionAt: null, extractionCount: 0 };
42
+ }
43
+
44
+ /** default + the given entries; default stays empty unless given. */
45
+ function seededState(entries: Record<string, BufferEntryState>): BufferState {
46
+ const defaultEntry = entries.default ?? emptyEntry();
47
+ return {
48
+ turns: defaultEntry.turns,
49
+ lastExtractionAt: defaultEntry.lastExtractionAt,
50
+ extractionCount: defaultEntry.extractionCount,
51
+ entries: { default: defaultEntry, ...entries },
52
+ };
53
+ }
54
+
55
+ function makeBuffer(state: BufferState, maxSessions: number) {
56
+ const config = parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: maxSessions });
57
+ const storage = new FakeStorage(state);
58
+ const buffer = new SmartBuffer(config, storage);
59
+ return { buffer, storage };
60
+ }
61
+
62
+ const CAP = 4;
63
+
64
+ function savedKeys(storage: FakeStorage): string[] {
65
+ return Object.keys(storage.saved?.entries ?? {});
66
+ }
67
+
68
+ test("live capture at a cap full of non-empty backlog is refused, never evicts buffered turns", async () => {
69
+ const { buffer } = makeBuffer(
70
+ seededState({
71
+ a: entry([makeTurn("a", "backlog a")]),
72
+ b: entry([makeTurn("b", "backlog b")]),
73
+ c: entry([makeTurn("c", "backlog c")]),
74
+ }),
75
+ CAP,
76
+ );
77
+
78
+ await assert.rejects(
79
+ buffer.addTurn("new-session", makeTurn("new-session", "live turn")),
80
+ BufferAdmissionRefusedError,
81
+ );
82
+
83
+ assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"], "oldest backlog lost to live admission");
84
+ assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b"]);
85
+ assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
86
+ assert.equal(buffer.getTurns("new-session").length, 0, "refused turn was not buffered");
87
+
88
+ // Accepted captures are subject to the same no-backlog-loss limit: refusal
89
+ // happens before acceptance, preserving accepted-capture non-eviction.
90
+ assert.equal(
91
+ await buffer.addTurnBounded("accepted-1", makeTurn("accepted-1", "accepted", true)),
92
+ null,
93
+ "accepted capture is refused rather than evicting native backlog",
94
+ );
95
+ assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"]);
96
+ assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b"]);
97
+ assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
98
+ });
99
+
100
+ test("an already-admitted session keeps accepting turns at a full cap", async () => {
101
+ const { buffer, storage } = makeBuffer(
102
+ seededState({
103
+ a: entry([makeTurn("a", "backlog a")]),
104
+ b: entry([makeTurn("b", "backlog b")]),
105
+ c: entry([makeTurn("c", "backlog c")]),
106
+ }),
107
+ CAP,
108
+ );
109
+
110
+ const decision = await buffer.addTurn("b", makeTurn("b", "another b turn"));
111
+ assert.equal(decision, "keep_buffering");
112
+ assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b", "another b turn"]);
113
+ assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"], "existing-session admission must not evict peers");
114
+ assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
115
+ assert.equal(savedKeys(storage).length, CAP, "entry count stays at the cap");
116
+ });
117
+
118
+ test("empty entries are still evicted first to admit a new live session", async () => {
119
+ const { buffer, storage } = makeBuffer(
120
+ seededState({
121
+ "empty-oldest": emptyEntry(),
122
+ n1: entry([makeTurn("n1", "native 1")]),
123
+ n2: entry([makeTurn("n2", "native 2")]),
124
+ }),
125
+ CAP,
126
+ );
127
+
128
+ const decision = await buffer.addTurn("new-session", makeTurn("new-session", "live turn"));
129
+ assert.equal(decision, "keep_buffering");
130
+ const keys = savedKeys(storage);
131
+ assert.ok(!keys.includes("empty-oldest"), "oldest empty entry reclaimed");
132
+ assert.ok(keys.includes("new-session"), "new session admitted");
133
+ assert.deepEqual(buffer.getTurns("n1").map((t) => t.content), ["native 1"], "empty eviction must not touch backlog");
134
+ assert.deepEqual(buffer.getTurns("n2").map((t) => t.content), ["native 2"]);
135
+ });
136
+
137
+ test("a retained-only entry is non-evictable backlog: live admission is refused instead", async () => {
138
+ const retainedOnly: BufferEntryState = {
139
+ turns: [],
140
+ retainedTurns: [makeTurn("r", "deferred retry turn")],
141
+ lastExtractionAt: null,
142
+ extractionCount: 0,
143
+ };
144
+ const { buffer } = makeBuffer(
145
+ seededState({
146
+ r: retainedOnly,
147
+ n1: entry([makeTurn("n1", "native 1")]),
148
+ n2: entry([makeTurn("n2", "native 2")]),
149
+ }),
150
+ CAP,
151
+ );
152
+
153
+ await assert.rejects(
154
+ buffer.addTurn("new-session", makeTurn("new-session", "live turn")),
155
+ BufferAdmissionRefusedError,
156
+ );
157
+ assert.equal(
158
+ buffer.getRetainedDeferredTurns("r").length,
159
+ 1,
160
+ "retained retry turns must not be evicted as 'empty'",
161
+ );
162
+ });
163
+ test("concurrent multi-session admissions reserve capacity without partial batches", async () => {
164
+ const { buffer } = makeBuffer(seededState({}), 2);
165
+ const results = await Promise.all([
166
+ buffer.addTurnsBounded([{ bufferKey: "batch-a", turns: [makeTurn("batch-a", "a")] }]),
167
+ buffer.addTurnsBounded([{ bufferKey: "batch-b", turns: [makeTurn("batch-b", "b")] }]),
168
+ ]);
169
+
170
+ assert.equal(results.filter((result) => result !== null).length, 1);
171
+ assert.equal(results.filter((result) => result === null).length, 1);
172
+ assert.equal(buffer.getTurns("batch-a").length + buffer.getTurns("batch-b").length, 1);
173
+ });
174
+
175
+ test("processTurn propagates retryable admission refusal without queueing extraction", async () => {
176
+ const { buffer } = makeBuffer(
177
+ seededState({
178
+ a: entry([makeTurn("a", "backlog a")]),
179
+ b: entry([makeTurn("b", "backlog b")]),
180
+ c: entry([makeTurn("c", "backlog c")]),
181
+ }),
182
+ CAP,
183
+ );
184
+ const config = parseConfig({
185
+ memoryDir: "/tmp/unused-buffer-eviction-backpressure",
186
+ bufferSaveDebounceMs: 0,
187
+ bufferMaxSessions: CAP,
188
+ });
189
+ const queued: BufferTurn[][] = [];
190
+ type CoordinatorDeps = ConstructorParameters<typeof TurnIngestionCoordinator>[0];
191
+ // SAFETY: this fixture exercises only admission + refusal; the remaining
192
+ // coordinator deps are never reached on this path.
193
+ const deps = Object.assign({} as CoordinatorDeps, {
194
+ buffer,
195
+ config,
196
+ queueBufferedExtraction: async (turns: BufferTurn[]) => {
197
+ queued.push(turns);
198
+ },
199
+ });
200
+ const coordinator = new TurnIngestionCoordinator(deps);
201
+
202
+ await assert.rejects(
203
+ coordinator.processTurn("user", "live turn", "new-session"),
204
+ BufferAdmissionRefusedError,
205
+ "caller must receive retryable refusal rather than successful acknowledgment",
206
+ );
207
+
208
+ assert.equal(queued.length, 0, "a refused turn must not queue extraction");
209
+ assert.equal(buffer.getTurns("new-session").length, 0, "a refused turn must not be buffered");
210
+ assert.deepEqual(buffer.getTurns("a").map((x) => x.content), ["backlog a"], "backlog dropped during refused admission");
211
+ });
@@ -10,11 +10,10 @@ export interface BufferEvictionHooks {
10
10
  }
11
11
 
12
12
  /**
13
- * Pick the keys to evict so `entries` returns to `maxCount`: the oldest empty
14
- * sessions first; retained failed extractions (#1908) keep their turns, so if
15
- * empty entries alone cannot reach the cap the oldest NON-empty sessions go
16
- * too rather than growing unboundedly. "default", the retained keys and
17
- * entries holding an accepted capture are never chosen.
13
+ * Pick empty entries to evict so `entries` returns to `maxCount`. Backlog
14
+ * (active or retained turns) is never evicted; admission must refuse when
15
+ * empty entries cannot free a slot. "default", retained keys and entries
16
+ * holding an accepted capture are never chosen.
18
17
  */
19
18
  export function selectEvictedBufferKeys(
20
19
  entries: Readonly<Record<string, BufferEntryState>>,
@@ -35,17 +34,12 @@ export function selectEvictedBufferKeys(
35
34
  const candidates = keys.filter(
36
35
  (key) => key !== "default" && !retainKeys.includes(key) && !hooks.holdsAcceptedCapture(entries[key]),
37
36
  );
37
+ // Backlog = active turns OR retained retry turns (#1908); only an entry
38
+ // holding neither counts as empty.
38
39
  const emptyRemovable = candidates
39
- .filter((key) => (entries[key]?.turns.length ?? 0) === 0)
40
+ .filter((key) => (entries[key]?.turns.length ?? 0) === 0 && (entries[key]?.retainedTurns?.length ?? 0) === 0)
40
41
  .sort(byOldestActivity);
41
42
 
42
43
  const removableCount = Math.max(0, keys.length - maxCount);
43
- const evicted = emptyRemovable.slice(0, removableCount);
44
- if (evicted.length < removableCount) {
45
- const nonEmptyRemovable = candidates
46
- .filter((key) => (entries[key]?.turns.length ?? 0) > 0)
47
- .sort(byOldestActivity);
48
- return evicted.concat(nonEmptyRemovable.slice(0, removableCount - evicted.length));
49
- }
50
- return evicted;
44
+ return emptyRemovable.slice(0, removableCount);
51
45
  }
@@ -22,7 +22,7 @@ import { mkdtemp, rm } from "node:fs/promises";
22
22
  import os from "node:os";
23
23
  import path from "node:path";
24
24
 
25
- import { SmartBuffer } from "./buffer.js";
25
+ import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
26
26
  import { parseConfig } from "./config.js";
27
27
  import { StorageManager } from "./storage.js";
28
28
  import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
@@ -161,34 +161,28 @@ test("clearAfterExtraction persists the cleared session state", async () => {
161
161
  assert.equal(record?.entry.extractionCount, 1);
162
162
  });
163
163
 
164
- test("evicted sessions get their files deleted", async () => {
164
+ test("empty evicted sessions get their files deleted", async () => {
165
165
  const storage = new IncrementalStorage(emptyState());
166
- const buffer = new SmartBuffer(parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions: 200 }), storage);
167
-
168
- // normalizeState always materializes a "default" entry, so 198 seeded
169
- // sessions + default = 199; new-session reaches exactly 200 (the
170
- // addTurnBounded cap). One more UNBOUNDED addTurn pushes to 201 and
171
- // pruneEntries evicts the oldest non-default session (s0).
172
- for (let i = 0; i < 198; i++) {
173
- const key = `s${i}`;
174
- await buffer.addTurnBounded(key, makeTurn(key, `content ${i}`));
175
- }
166
+ const buffer = new SmartBuffer(parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions: 4 }), storage);
167
+
168
+ // Clear one persisted entry to make it genuinely empty. Accepted-capture
169
+ // admission may reclaim this slot, but never a session with buffered turns.
170
+ await buffer.addTurn("empty-old", makeTurn("empty-old", "will be extracted"));
171
+ await buffer.flushPendingSave();
172
+ await buffer.clearAfterExtraction("empty-old");
173
+ await buffer.addTurnBounded("native-1", makeTurn("native-1", "native 1"));
174
+ await buffer.addTurnBounded("native-2", makeTurn("native-2", "native 2"));
176
175
  await buffer.flushPendingSave();
177
176
 
178
- const outcome = await buffer.addTurnBounded("new-session", makeTurn("new-session", "fresh"));
179
- assert.notEqual(outcome, null, "addTurnBounded refused below capacity");
180
- await buffer.addTurn("overflow", makeTurn("overflow", "one too many"));
181
- // The evicted key's file deletion lands on the next persist, like any
182
- // other buffered state change.
177
+ const accepted = await buffer.addTurnBounded("accepted", {
178
+ ...makeTurn("accepted", "durable accepted"),
179
+ acceptedCapture: true,
180
+ });
181
+ assert.notEqual(accepted, null, "empty entry should be reclaimed to admit accepted capture");
183
182
  await buffer.flushPendingSave();
184
- assert.ok(storage.deleted.includes("s0"), "oldest session was not evicted under capacity pressure");
185
- assert.ok(
186
- storage.deleted.length > 0,
187
- "capacity pressure evicted entries without deleting their session files",
188
- );
189
- for (const key of storage.deleted) {
190
- assert.equal(storage.sessions.has(key), false, `evicted session ${key} still has a file record`);
191
- }
183
+ assert.ok(storage.deleted.includes("empty-old"), "reclaimed empty entry's file was not deleted");
184
+ assert.equal(buffer.getTurns("native-1").length, 1);
185
+ assert.equal(buffer.getTurns("native-2").length, 1);
192
186
  });
193
187
 
194
188
  test("the default entry cap leaves headroom for live sessions during backlog drains (#3303)", async () => {
@@ -273,9 +267,7 @@ test("legacy-only storage keeps the whole-state saveBuffer behavior", async () =
273
267
  assert.equal(storage.lastSaved?.entries?.["thread-b"]?.turns[0]?.content, "beta");
274
268
  });
275
269
 
276
- test("durable capture at the entry cap evicts native entries instead of refusing (COS-244c)", async () => {
277
- // Stalled extraction left the cap full of native (non-accepted)
278
- // sessions; addTurnBounded refused every new accepted session.
270
+ test("durable capture at the entry cap is refused without evicting native backlog (COS-244c)", async () => {
279
271
  const storage = new IncrementalStorage(emptyState());
280
272
  const buffer = new SmartBuffer(
281
273
  parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions: 4 }),
@@ -286,17 +278,13 @@ test("durable capture at the entry cap evicts native entries instead of refusing
286
278
  await buffer.flushPendingSave();
287
279
 
288
280
  const accepted = (key: string): BufferTurn => ({ ...makeTurn(key, "accepted"), acceptedCapture: true });
281
+ assert.equal(await buffer.addTurnBounded("accepted-0", accepted("accepted-0")), null);
282
+ await buffer.flushPendingSave();
283
+ assert.equal(storage.deleted.length, 0, "accepted refusal must not evict native session files");
289
284
  for (let i = 0; i < 3; i++) {
290
- const outcome = await buffer.addTurnBounded(`accepted-${i}`, accepted(`accepted-${i}`));
291
- assert.notEqual(outcome, null, `accepted-${i} refused while native entries were evictable`);
285
+ assert.ok(buffer.getTurns(`native-${i}`).some((turn) => turn.content === `native ${i}`));
292
286
  }
293
- await buffer.flushPendingSave();
294
- assert.ok(storage.deleted.includes("native-0"), "oldest native entry was not evicted");
295
-
296
- // Accepted work is still never evicted: with only accepted entries left the
297
- // cap refuses rather than dropping accepted captures.
298
- assert.equal(await buffer.addTurnBounded("accepted-3", accepted("accepted-3")), null);
299
- for (let i = 0; i < 3; i++) assert.ok(!storage.deleted.includes(`accepted-${i}`));
287
+ assert.equal(buffer.getTurns("accepted-0").length, 0, "refused capture was never accepted");
300
288
  });
301
289
 
302
290
  test("at-cap admission evicts nothing when eviction cannot free a slot (COS-244c)", async () => {
@@ -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,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { SmartBuffer } from "./buffer.js";
3
+ import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
4
4
  import { parseConfig } from "./config.js";
5
5
  import type { BufferSurpriseProbe } from "./buffer.js";
6
6
  import type { BufferState, BufferTurn } from "./types.js";
@@ -642,13 +642,14 @@ test("getTurns prepends retained deferred turns before live turns", async () =>
642
642
  assert.equal(all[1]?.content, "new live turn");
643
643
  });
644
644
 
645
- test("SmartBuffer prefers pruning empty entries and bounds pending-turn buffers at the cap (#1908 policy)", async () => {
645
+ test("SmartBuffer refuses new sessions instead of dropping backlog at the cap (#1908 policy)", async () => {
646
646
  // Pre-#1908 this contract was "never prune non-empty entries". With retry
647
647
  // retention (failed extractions keep their turns), unbounded non-empty
648
648
  // growth during a provider outage would eventually OOM the daemon — the
649
- // documented policy (#1908 step 7) is: retain up to MAX_BUFFER_ENTRY_COUNT
650
- // session entries, then prune the OLDEST sessions with a loud warning.
651
- // Empty entries are still evicted first.
649
+ // documented policy (#1908 step 7) bounds the ENTRY COUNT, not by dropping
650
+ // buffered turns: a NEW session is refused (retryable backpressure) when
651
+ // only non-empty backlog fills the cap. Empty entries are still evicted
652
+ // first, and already-admitted sessions keep buffering.
652
653
  const entries = Object.fromEntries(
653
654
  Array.from({ length: 205 }, (_, index) => [
654
655
  `thread-${index}`,
@@ -674,21 +675,32 @@ test("SmartBuffer prefers pruning empty entries and bounds pending-turn buffers
674
675
  });
675
676
  const buffer = new SmartBuffer(parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: 200 }), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
676
677
 
677
- await buffer.addTurn("active-thread", makeTurn("active-thread", "pending memory"));
678
+ await assert.rejects(
679
+ buffer.addTurn("active-thread", makeTurn("active-thread", "pending memory")),
680
+ BufferAdmissionRefusedError,
681
+ "a new session must be refused while only non-empty backlog fills the cap",
682
+ );
683
+
684
+ // Already-admitted sessions keep buffering; the save reflects the
685
+ // post-refusal state: nothing was evicted, nothing refused was buffered.
686
+ await buffer.addTurn("thread-204", makeTurn("thread-204", "another turn"));
687
+ assert.equal(buffer.getTurns("thread-204").length, 2);
678
688
 
679
689
  const persistedKeys = Object.keys(storage.saved?.entries ?? {});
680
- assert.ok(persistedKeys.length <= 201, `bounded at the cap plus default (got ${persistedKeys.length})`);
690
+ assert.ok(persistedKeys.length <= 206, `entry count stays at the seeded cap state (got ${persistedKeys.length})`);
681
691
  assert.ok(persistedKeys.includes("default"), "default entry never pruned");
682
- assert.ok(persistedKeys.includes("active-thread"), "the just-written key is retained");
683
- assert.ok(!persistedKeys.includes("thread-0"), "oldest pending entry pruned once over the cap");
692
+ assert.ok(persistedKeys.includes("thread-0"), "oldest backlog keeps its turns for extraction");
684
693
  assert.ok(persistedKeys.includes("thread-204"), "newest pending entry survives");
694
+ assert.ok(!persistedKeys.includes("active-thread"), "the refused turn was never buffered");
685
695
  });
686
696
 
687
- test("pruneEntries evicts oldest NON-empty session entries past the cap (#1908 retry-retention bound)", async () => {
697
+ test("pruneEntries never evicts non-empty backlog; live admission is refused past the cap (#1908 retry-retention bound)", async () => {
688
698
  // Retained failed extractions keep their turns in the buffer, so during a
689
699
  // provider outage non-empty entries can exceed MAX_BUFFER_ENTRY_COUNT (200).
690
- // The cap is the documented bound: the oldest non-empty sessions must be
691
- // evicted (loudly) rather than growing without bound (codex review).
700
+ // The cap is still the documented bound: buffered turn data is never the
701
+ // release valve — a NEW session is refused (retryable backpressure) until
702
+ // extraction drains an entry, so the daemon cannot OOM on unbounded growth
703
+ // and no buffered turn is dropped (codex review).
692
704
  const entries: NonNullable<BufferState["entries"]> = {};
693
705
  for (let i = 0; i < 210; i += 1) {
694
706
  const at = new Date(1_700_000_000_000 + i * 60_000).toISOString();
@@ -701,13 +713,17 @@ test("pruneEntries evicts oldest NON-empty session entries past the cap (#1908 r
701
713
  const storage = new FakeStorage({ turns: [], lastExtractionAt: null, extractionCount: 0, entries });
702
714
  const buffer = new SmartBuffer(parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: 200 }), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
703
715
  await buffer.load();
704
- await buffer.addTurn("session-newest", makeTurn("session-newest", "new turn"));
716
+ await assert.rejects(
717
+ buffer.addTurn("session-newest", makeTurn("session-newest", "new turn")),
718
+ BufferAdmissionRefusedError,
719
+ "live admission refuses rather than evicting oldest non-empty backlog",
720
+ );
705
721
 
706
722
  const savedKeys = Object.keys(storage.saved?.entries ?? {});
707
- assert.ok(savedKeys.length <= 200, `entries bounded at MAX_BUFFER_ENTRY_COUNT (got ${savedKeys.length})`);
708
- assert.ok(savedKeys.includes("session-newest"), "the just-written key is retained");
709
- assert.equal(buffer.getTurns("session-000").length, 0, "oldest non-empty entry was evicted");
723
+ assert.ok(savedKeys.length <= 211, `entries bounded at MAX_BUFFER_ENTRY_COUNT plus default (got ${savedKeys.length})`);
724
+ assert.equal(buffer.getTurns("session-000").length, 1, "oldest non-empty entry keeps its turn for extraction");
710
725
  assert.ok(buffer.getTurns("session-209").length > 0, "newest pre-existing entry survives");
726
+ assert.ok(!savedKeys.includes("session-newest"), "the refused turn was never buffered");
711
727
  });
712
728
 
713
729
  // ---------------------------------------------------------------------------
@@ -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
@@ -392,3 +398,44 @@ export function probeWithTimeout<T>(inflight: Promise<T>, timeoutMs: number): Pr
392
398
  if (timer) clearTimeout(timer);
393
399
  });
394
400
  }
401
+
402
+ export const isAcceptedTurn = (t: { acceptedCapture?: boolean; writeNamespaceOverride?: string }): boolean =>
403
+ t.acceptedCapture === true || t.writeNamespaceOverride !== undefined;
404
+
405
+ export function entryActivityAt(entry: BufferEntryState): number {
406
+ const lastTurnAt = entry.turns.reduce((latest, turn) => {
407
+ const parsed = Date.parse(turn.timestamp);
408
+ return Number.isFinite(parsed) ? Math.max(latest, parsed) : latest;
409
+ }, -1);
410
+ const lastExtractionAt =
411
+ entry.lastExtractionAt === null ? Number.NaN : Date.parse(entry.lastExtractionAt);
412
+ return Math.max(
413
+ lastTurnAt,
414
+ Number.isFinite(lastExtractionAt) ? lastExtractionAt : -1,
415
+ );
416
+ }
417
+
418
+ export function entryHoldsAcceptedCapture(entry: BufferEntryState | undefined): boolean {
419
+ return (entry?.turns ?? []).some(isAcceptedTurn) || (entry?.retainedTurns ?? []).some(isAcceptedTurn);
420
+ }
421
+
422
+ export function normalizeBufferState(state: BufferState): BufferState {
423
+ // SAFETY: persisted-state contract — entries, when present, is a record of BufferEntryState.
424
+ const entries = Object.assign(
425
+ Object.create(null),
426
+ state.entries ?? {},
427
+ ) as NonNullable<BufferState["entries"]>;
428
+ if (!entries.default) {
429
+ entries.default = {
430
+ turns: Array.isArray(state.turns) ? [...state.turns] : [],
431
+ lastExtractionAt: state.lastExtractionAt ?? null,
432
+ extractionCount: state.extractionCount ?? 0,
433
+ };
434
+ }
435
+ return {
436
+ turns: entries.default.turns,
437
+ lastExtractionAt: entries.default.lastExtractionAt,
438
+ extractionCount: entries.default.extractionCount,
439
+ entries,
440
+ };
441
+ }
@@ -1,6 +1,33 @@
1
1
  /** Types split out of buffer.ts (structural ratchet, issue #1995). */
2
2
 
3
- import type { BufferTurn } from "./types.js";
3
+ import type { BufferTurn, CodingContext, SignalLevel } from "./types.js";
4
+ import type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
5
+
6
+ export type TriggerDecision = "extract_now" | "extract_batch" | "keep_buffering";
7
+
8
+ export interface AddTurnOutcome {
9
+ decision: TriggerDecision;
10
+ extractionTurns?: BufferTurn[];
11
+ }
12
+
13
+ export interface AddTurnMutationResult {
14
+ decision: TriggerDecision;
15
+ signalLevel: SignalLevel;
16
+ priorTurns: BufferTurn[];
17
+ activeTurnsSnapshot: BufferTurn[];
18
+ retainedTurnsSnapshot: BufferTurn[];
19
+ turnCountInWindow: number;
20
+ }
21
+
22
+ export interface RetainedTurnCleanupOptions {
23
+ abortSignal?: AbortSignal;
24
+ deadlineMs?: number;
25
+ /** Keep cleanup within the accepted namespace. */
26
+ writeNamespaceOverride?: string;
27
+ /** Match the original profile and project binding, not just its write namespace. */
28
+ scopeProfileWritePlan?: ResolvedScopeProfilePlan | null;
29
+ codingContext?: CodingContext;
30
+ }
4
31
 
5
32
  /**
6
33
  * Optional surprise probe injected into `SmartBuffer`.