@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
@@ -7,6 +7,7 @@ import type {
7
7
  SearchExecutionOptions,
8
8
  SearchQueryOptions,
9
9
  } from "../search/port.js";
10
+ import { reportSearchDegradation } from "../search/port.js";
10
11
  import { createSearchBackend } from "../search/factory.js";
11
12
  import { namespaceIdentityToken, normalizeNamespaceIdentity } from "./identity.js";
12
13
  import { ALL_CATEGORY_DIRS } from "../utils/category-dir.js";
@@ -14,6 +15,13 @@ import { ALL_CATEGORY_DIRS } from "../utils/category-dir.js";
14
15
  const NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR = 4;
15
16
  const NESTED_NAMESPACE_FILTER_OVERFETCH_MIN = 50;
16
17
 
18
+ // Short TTL so repeated health polls skip a full QMD re-probe (~1.5s).
19
+ const HEALTH_RESULT_CACHE_TTL_MS = 10_000;
20
+
21
+ // Each cross-namespace search leg gets this budget; a stalled backend leg
22
+ // degrades its own namespace instead of holding the whole search (#3139).
23
+ export const NAMESPACE_SEARCH_LEG_TIMEOUT_MS = 25_000;
24
+
17
25
  export function namespaceCollectionName(
18
26
  baseCollection: string,
19
27
  namespace: string,
@@ -72,6 +80,27 @@ export interface NamespaceSearchHealth {
72
80
  embeddingBacklogThreshold: number;
73
81
  }
74
82
 
83
+ /** True when the pending-embedding count is above a positive backlog threshold. */
84
+ export function embeddingBacklogExceeds(pending: number | null, threshold: number): boolean {
85
+ return threshold > 0 && pending !== null && pending > threshold;
86
+ }
87
+
88
+ /**
89
+ * The one definition of a degraded search answer. The readiness consumer
90
+ * reports it, and `healthForNamespace` refuses to cache it.
91
+ */
92
+ export function isSearchHealthDegraded(
93
+ health: Pick<NamespaceSearchHealth, "available" | "collectionState" | "pendingEmbeddings">,
94
+ backlogThreshold: number,
95
+ ): boolean {
96
+ const active = health.available && health.collectionState !== "missing";
97
+ return (
98
+ !active ||
99
+ health.collectionState === "unknown" ||
100
+ embeddingBacklogExceeds(health.pendingEmbeddings, backlogThreshold)
101
+ );
102
+ }
103
+
75
104
  type NamespaceScopedSearchConfig = PluginConfig & {
76
105
  hostEmbeddingProviderScope?: string;
77
106
  };
@@ -84,6 +113,11 @@ type BackendRecordOptions = {
84
113
 
85
114
  export class NamespaceSearchRouter {
86
115
  private readonly cache = new Map<string, Promise<NamespaceBackendRecord>>();
116
+ private readonly healthResultCache = new Map<
117
+ string,
118
+ { expiresAt: number; value: NamespaceSearchHealth }
119
+ >();
120
+
87
121
  private readonly pendingDisposals = new Set<Promise<void>>();
88
122
 
89
123
  constructor(
@@ -148,42 +182,67 @@ export class NamespaceSearchRouter {
148
182
  return { namespace, results: [] as QmdSearchResult[] };
149
183
  }
150
184
  const backendLimit = backendSearchLimit(record, maxResults);
151
- let results: QmdSearchResult[];
185
+ // One stalled backend leg must not hold the whole cross-namespace
186
+ // search (#3139). The backend gets the deadline signal so it cancels
187
+ // its own work, and the await is raced against the same budget. A leg
188
+ // that outlives its budget degrades only its own namespace; real
189
+ // backend errors and caller aborts propagate as before.
190
+ const legAbort = new AbortController();
191
+ const legTimer = setTimeout(() => legAbort.abort(), NAMESPACE_SEARCH_LEG_TIMEOUT_MS);
192
+ const legSignal = options.execution?.signal
193
+ ? AbortSignal.any([options.execution.signal, legAbort.signal])
194
+ : legAbort.signal;
195
+ const legExecution: SearchExecutionOptions = { ...options.execution, signal: legSignal };
196
+ let leg: Promise<QmdSearchResult[]>;
152
197
  switch (method) {
153
198
  case "hybrid":
154
- results = await record.backend.hybridSearch(
199
+ leg = record.backend.hybridSearch(
155
200
  query,
156
201
  record.collection,
157
202
  backendLimit,
158
- options.execution,
203
+ legExecution,
159
204
  );
160
205
  break;
161
206
  case "bm25":
162
- results = await record.backend.bm25Search(
207
+ leg = record.backend.bm25Search(
163
208
  query,
164
209
  record.collection,
165
210
  backendLimit,
166
- options.execution,
211
+ legExecution,
167
212
  );
168
213
  break;
169
214
  case "vector":
170
- results = await record.backend.vectorSearch(
215
+ leg = record.backend.vectorSearch(
171
216
  query,
172
217
  record.collection,
173
218
  backendLimit,
174
- options.execution,
219
+ legExecution,
175
220
  );
176
221
  break;
177
222
  default:
178
- results = await record.backend.search(
223
+ leg = record.backend.search(
179
224
  query,
180
225
  record.collection,
181
226
  backendLimit,
182
227
  options.searchOptions,
183
- options.execution,
228
+ legExecution,
184
229
  );
185
230
  break;
186
231
  }
232
+ let results: QmdSearchResult[];
233
+ try {
234
+ results = await awaitWithAbort(leg, legSignal);
235
+ } catch (error) {
236
+ if (options.execution?.signal?.aborted || !legAbort.signal.aborted) throw error;
237
+ reportSearchDegradation(options.execution, {
238
+ backend: "qmd",
239
+ code: "deadline_exceeded",
240
+ detail: `namespace search deadline exceeded: ${namespace}`,
241
+ });
242
+ return { namespace, results: [] };
243
+ } finally {
244
+ clearTimeout(legTimer);
245
+ }
187
246
  results = filterNamespaceSubtreeResults(record, results)
188
247
  .map((result) => ({
189
248
  ...result,
@@ -279,6 +338,11 @@ export class NamespaceSearchRouter {
279
338
  execution?: SearchExecutionOptions,
280
339
  ): Promise<NamespaceSearchHealth> {
281
340
  const key = namespace.trim() || this.config.defaultNamespace;
341
+ const cached = this.healthResultCache.get(key);
342
+ if (cached && cached.expiresAt > Date.now()) {
343
+ return { ...cached.value };
344
+ }
345
+
282
346
  const record = await this.createBackendRecordFor(
283
347
  key,
284
348
  execution,
@@ -310,7 +374,7 @@ export class NamespaceSearchRouter {
310
374
  ? "missing"
311
375
  : record.collectionState;
312
376
 
313
- return {
377
+ const health: NamespaceSearchHealth = {
314
378
  collection: record.collection,
315
379
  memoryDir: record.memoryDir,
316
380
  available: liveRecord?.available ?? record.available,
@@ -326,6 +390,22 @@ export class NamespaceSearchRouter {
326
390
  oldestPendingAgeMs: backendStatus?.oldestPendingAgeMs ?? null,
327
391
  embeddingBacklogThreshold: this.config.qmdEmbeddingBacklogThreshold,
328
392
  };
393
+
394
+ // Cache only a healthy answer that carries backlog data when the backend
395
+ // reports status. A degraded or incomplete answer must re-probe so a
396
+ // readiness poll sees recovery on the next call, not one TTL late.
397
+ const statusMissing = backendStatus === null && "status" in diagnosticBackend;
398
+ if (
399
+ execution?.signal?.aborted !== true &&
400
+ !statusMissing &&
401
+ !isSearchHealthDegraded(health, health.embeddingBacklogThreshold)
402
+ ) {
403
+ this.healthResultCache.set(key, {
404
+ expiresAt: Date.now() + HEALTH_RESULT_CACHE_TTL_MS,
405
+ value: { ...health },
406
+ });
407
+ }
408
+ return { ...health };
329
409
  } finally {
330
410
  const dispose = (record.backend as { dispose?: () => void | Promise<void> }).dispose;
331
411
  await dispose?.call(record.backend);
@@ -358,6 +438,7 @@ export class NamespaceSearchRouter {
358
438
 
359
439
  /** Clear cached backend records so the next access re-probes availability. */
360
440
  clearCache(): void {
441
+ this.healthResultCache.clear();
361
442
  const pendingRecords = Array.from(this.cache.values());
362
443
  this.cache.clear();
363
444
  const disposal = Promise.allSettled(pendingRecords).then(async (settled) => {
@@ -379,6 +460,7 @@ export class NamespaceSearchRouter {
379
460
  /** Release any per-namespace backend handles held by cached records. */
380
461
  async dispose(): Promise<void> {
381
462
  await this.drainPendingDisposals();
463
+ this.healthResultCache.clear();
382
464
  const pendingRecords = Array.from(this.cache.values());
383
465
  this.cache.clear();
384
466
  const settled = await Promise.allSettled(pendingRecords);
@@ -395,6 +477,7 @@ export class NamespaceSearchRouter {
395
477
  * has been switched to NoopSearchBackend for a missing collection.
396
478
  */
397
479
  async disableSearchBackends(): Promise<void> {
480
+ this.healthResultCache.clear();
398
481
  const settled = await Promise.allSettled(this.cache.values());
399
482
  await Promise.allSettled(
400
483
  settled.flatMap((entry) => {
@@ -25,6 +25,7 @@
25
25
  * every claim retryable; a restarted daemon's scheduler adopts them.
26
26
  */
27
27
 
28
+ import { randomUUID } from "node:crypto";
28
29
  import { hostname } from "node:os";
29
30
  import { log } from "../logger.js";
30
31
  import type { Orchestrator } from "../orchestrator.js";
@@ -44,8 +45,8 @@ export interface BacklogSchedulerDeps {
44
45
  liveBufferCounts():
45
46
  | Map<string, { turns: number; retainedTurns: number }>
46
47
  | Promise<Map<string, { turns: number; retainedTurns: number }>>;
47
- /** A fresh lease for claims this scheduler takes. */
48
- lease(): DrainLease;
48
+ /** Create a fresh lease for a unique owner token. */
49
+ lease(ownerId: string): DrainLease;
49
50
  /** Drain one key to quiescence (BufferDrainRunner.drainSession). */
50
51
  drainKey(
51
52
  key: string,
@@ -63,6 +64,7 @@ export interface BacklogSchedulerDeps {
63
64
  }
64
65
 
65
66
  export interface BacklogSchedulerOptions {
67
+ /** Optional label prefix; each scheduler instance adds a unique token. */
66
68
  ownerId?: string;
67
69
  /** Scan cadence in ms (startup + new-work + retry checks). */
68
70
  pollMs?: number;
@@ -135,7 +137,7 @@ export class BacklogScheduler {
135
137
  constructor(deps: BacklogSchedulerDeps, options: BacklogSchedulerOptions = {}) {
136
138
  this.deps = deps;
137
139
  this.options = {
138
- ownerId: options.ownerId ?? `daemon-drain-${process.pid}`,
140
+ ownerId: `${options.ownerId ?? "daemon-drain"}-${randomUUID()}`,
139
141
  pollMs: options.pollMs ?? DEFAULT_POLL_MS,
140
142
  retryBaseMs: options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS,
141
143
  retryMaxMs: options.retryMaxMs ?? DEFAULT_RETRY_MAX_MS,
@@ -333,11 +335,11 @@ export class BacklogScheduler {
333
335
  const { promise: settled, resolve: settle } = Promise.withResolvers<void>();
334
336
  this.inFlight.set(key, { controller, settled });
335
337
  try {
336
- if (!(await this.claimForSubmit(key, needsClaim))) return;
338
+ if (!(await this.claimForSubmit(key, needsClaim, this.options.ownerId))) return;
337
339
  // Stopped between the claim decision and the task: leave the claim
338
340
  // with its lease (it expires; the next start adopts) — never drain.
339
341
  if (this.stopped) return;
340
- await this.drainClaimedKey(key, controller.signal);
342
+ await this.drainClaimedKey(key, controller.signal, this.options.ownerId);
341
343
  } catch (err) {
342
344
  // Claim/drain failures are scheduler-level: the claim content is
343
345
  // untouched — count failed and back off (never an unhandled rejection
@@ -357,10 +359,10 @@ export class BacklogScheduler {
357
359
  }
358
360
 
359
361
  /** after all — the key goes quiet without a task. "claimed": ours. */
360
- private async claimForSubmit(key: string, needsClaim: boolean): Promise<boolean> {
362
+ private async claimForSubmit(key: string, needsClaim: boolean, ownerId: string): Promise<boolean> {
361
363
  if (!needsClaim) return true;
362
364
  try {
363
- const outcome = await this.deps.claims.claimFromSessionFile(key, this.deps.lease());
365
+ const outcome = await this.deps.claims.claimFromSessionFile(key, this.deps.lease(ownerId));
364
366
  if (outcome === "empty") {
365
367
  this.keys.delete(key);
366
368
  return false;
@@ -375,11 +377,11 @@ export class BacklogScheduler {
375
377
  /** Drain one claimed key. drainKey reports failures as outcomes; a thrown
376
378
  * error still means the claim content is intact — count it failed and
377
379
  * back off. */
378
- private async drainClaimedKey(key: string, signal: AbortSignal): Promise<void> {
380
+ private async drainClaimedKey(key: string, signal: AbortSignal, ownerId: string): Promise<void> {
379
381
  this.counters.claimed += 1;
380
382
  try {
381
383
  const outcome = await this.deps.drainKey(key, {
382
- ownerId: this.options.ownerId,
384
+ ownerId,
383
385
  signal,
384
386
  });
385
387
  this.applyOutcome(key, outcome);
@@ -480,7 +482,6 @@ export function wireBacklogScheduler(
480
482
  orchestrator: Orchestrator,
481
483
  options: BacklogSchedulerOptions = {}
482
484
  ): BacklogScheduler {
483
- const ownerId = options.ownerId ?? `daemon-drain-${process.pid}`;
484
485
  const runner = new BufferDrainRunner(orchestrator);
485
486
  return new BacklogScheduler(
486
487
  {
@@ -492,7 +493,7 @@ export function wireBacklogScheduler(
492
493
  await orchestrator.buffer.load();
493
494
  return orchestrator.buffer.entryCounts();
494
495
  },
495
- lease: () => {
496
+ lease: (ownerId) => {
496
497
  const now = new Date().toISOString();
497
498
  return {
498
499
  ownerId,
@@ -508,6 +509,6 @@ export function wireBacklogScheduler(
508
509
  // claim-backed drain accumulate toward the same threshold).
509
510
  breakerSnapshot: () => orchestrator.getExtractionResilienceStatus().breaker,
510
511
  },
511
- { ...options, ownerId }
512
+ options
512
513
  );
513
514
  }
@@ -74,27 +74,35 @@ const DEFAULT_LEASE_MS = 120_000;
74
74
  * A `BufferStorage` view of one drain claim: the claim file IS the buffer.
75
75
  * Writes are the drain's progress (what is left to extract), so every
76
76
  * successfully extracted chunk is durably removed before the next one runs.
77
+ *
78
+ * `seen` is the drainer's own view of the claim: the entry it loaded, then the
79
+ * entry it last wrote. Each progress write is diffed against it, so a write
80
+ * records only the turns extracted since the previous one.
77
81
  */
78
82
  export class ClaimBufferStorage implements BufferStorage {
83
+ private seen: BufferEntryState;
84
+
79
85
  constructor(
80
86
  private readonly claims: BufferDrainClaims,
81
87
  private readonly key: string,
82
88
  private readonly ownerId: string,
83
- private readonly entry: BufferEntryState
84
- ) {}
89
+ entry: BufferEntryState
90
+ ) {
91
+ this.seen = entry;
92
+ }
85
93
 
86
94
  async loadBuffer(): Promise<BufferState> {
87
95
  const entries: NonNullable<BufferState["entries"]> = Object.create(null);
88
96
  entries[this.key] = {
89
- turns: this.entry.turns,
90
- retainedTurns: this.entry.retainedTurns,
91
- lastExtractionAt: this.entry.lastExtractionAt,
92
- extractionCount: this.entry.extractionCount,
97
+ turns: this.seen.turns,
98
+ retainedTurns: this.seen.retainedTurns,
99
+ lastExtractionAt: this.seen.lastExtractionAt,
100
+ extractionCount: this.seen.extractionCount,
93
101
  };
94
102
  return {
95
- turns: this.entry.turns,
96
- lastExtractionAt: this.entry.lastExtractionAt,
97
- extractionCount: this.entry.extractionCount,
103
+ turns: this.seen.turns,
104
+ lastExtractionAt: this.seen.lastExtractionAt,
105
+ extractionCount: this.seen.extractionCount,
98
106
  entries,
99
107
  };
100
108
  }
@@ -102,16 +110,21 @@ export class ClaimBufferStorage implements BufferStorage {
102
110
  async saveBuffer(state: BufferState): Promise<void> {
103
111
  // Whole-state fallback (the claim covers one session): persist the entry.
104
112
  const entry = state.entries?.[this.key];
105
- await this.claims.writeEntry(
106
- this.key,
107
- this.ownerId,
108
- entry ?? { turns: [], lastExtractionAt: null, extractionCount: 0 },
109
- this.entry
110
- );
113
+ await this.writeProgress(entry ?? { turns: [], lastExtractionAt: null, extractionCount: 0 });
111
114
  }
112
115
 
113
116
  async saveBufferSession(key: string, entry: BufferEntryState): Promise<void> {
114
- await this.claims.writeEntry(key, this.ownerId, entry, key === this.key ? this.entry : undefined);
117
+ if (key !== this.key) {
118
+ await this.claims.writeEntry(key, this.ownerId, entry, undefined);
119
+ return;
120
+ }
121
+ await this.writeProgress(entry);
122
+ }
123
+
124
+ private async writeProgress(entry: BufferEntryState): Promise<void> {
125
+ await this.claims.writeEntry(this.key, this.ownerId, entry, this.seen);
126
+ // Copy the lists: the buffer keeps mutating its own arrays after a save.
127
+ this.seen = { ...entry, turns: [...entry.turns], retainedTurns: entry.retainedTurns ? [...entry.retainedTurns] : undefined };
115
128
  }
116
129
 
117
130
  async deleteBufferSession(): Promise<void> {
@@ -554,14 +567,25 @@ async function runClaimDrain(
554
567
  options: BufferDrainRunnerOptions,
555
568
  heartbeat: NodeJS.Timeout
556
569
  ): Promise<DrainSessionOutcome> {
570
+ // Reconcile only identities recorded as durably extracted under this
571
+ // owner's claim lease. The claim lock is released before entering the live
572
+ // buffer mutation queue, preserving the buffer→claim lock order used by
573
+ // concurrent ingress saves.
574
+ const reconcileDrainedTurns = async (): Promise<void> => {
575
+ const drained = await ctx.claims.drainedTurnsForOwner(ctx.key, ctx.ownerId);
576
+ if (drained !== null) await ctx.orchestrator.buffer.save({ key: ctx.key, drained });
577
+ };
578
+
557
579
  // An emptied claim acks here: the ack verdict is the truth (drained), or
558
580
  // live ingress merged between the read and the ack — deferred, source kept.
559
581
  if (ctx.turns.length === 0) {
582
+ await reconcileDrainedTurns();
560
583
  const outcome = await ackEmptyClaim(ctx.claims, ctx.key, ctx.ownerId, ctx.turnsAtStart);
561
584
  options.onEvent?.({ type: "session_finished", key: ctx.key, outcome });
562
585
  return outcome;
563
586
  }
564
587
  const { groupOutcomes, groupError } = await drainScopeGroups(ctx);
588
+ await reconcileDrainedTurns();
565
589
  // Heartbeat cleared on EVERY exit path: a rejected drain must not keep
566
590
  // renewing the lease forever (cleared in the caller's finally).
567
591
  const remaining = await ctx.claims.load(ctx.key);
@@ -1694,11 +1694,7 @@ export class ExtractionPersistCoordinator {
1694
1694
  // confirmation, so a disabled deployment never suppresses a write via
1695
1695
  // dedup (PR #2016 thread SD-nH).
1696
1696
  if (factDedupEnabled) {
1697
- exactDuplicate = await confirmActiveFactDuplicate(
1698
- { hasContentHashDedup: (storage, key) => this.deps.hasContentHashDedup(storage, key) },
1699
- targetStorage,
1700
- raceDedupOpts(contentHashDedupKey),
1701
- );
1697
+ exactDuplicate = await confirmActiveFactDuplicate(targetStorage, raceDedupOpts(contentHashDedupKey));
1702
1698
  }
1703
1699
  // PR #2016/#3231: finalize committed writes before a fail-closed
1704
1700
  // window throw propagates (PRRT_kwDORJXyws6zxG6H).
@@ -1711,11 +1707,7 @@ export class ExtractionPersistCoordinator {
1711
1707
  const raceRecheck = (storage: StorageManager, key: string) => () => {
1712
1708
  // Gates mirror the persist-time suppression gates (PR #2016 SD-nH; #1645 quarantine).
1713
1709
  if (!factDedupEnabled || injectionScreenStatus === "pending_review") return Promise.resolve(false);
1714
- return confirmActiveFactDuplicate(
1715
- { hasContentHashDedup: (s, k) => this.deps.hasContentHashDedup(s, k) },
1716
- storage,
1717
- raceDedupOpts(key),
1718
- );
1710
+ return confirmActiveFactDuplicate(storage, raceDedupOpts(key));
1719
1711
  };
1720
1712
  // #1671/#1707 — backfill bi-temporal bounds onto the surviving copy
1721
1713
  // when a duplicate short-circuits: persist-time hit OR raced duplicate
@@ -19,9 +19,6 @@ import { log } from "../logger.js";
19
19
  * OUTSIDE the window exactly like the consolidation LLM call stays outside
20
20
  * commitConsolidatedProfile.
21
21
  */
22
- export interface FactDedupProbe {
23
- hasContentHashDedup(storage: StorageManager, key: string): Promise<boolean>;
24
- }
25
22
 
26
23
  interface FactDedupMatchOptions {
27
24
  readonly category: string;
@@ -55,22 +52,18 @@ function matchesActiveConnectorDuplicate(memory: MemoryFile, opts: FactDedupMatc
55
52
  * silently drop content.
56
53
  */
57
54
  export async function confirmActiveFactDuplicate(
58
- probe: FactDedupProbe,
59
55
  storage: StorageManager,
60
56
  opts: FactDedupMatchOptions,
61
57
  ): Promise<boolean> {
62
- let needsCorpusConfirm: boolean;
63
58
  try {
64
- if (!(await probe.hasContentHashDedup(storage, opts.contentHashDedupKey))) {
65
- if (await storage.isFactContentHashAuthoritative()) return false;
66
- needsCorpusConfirm = true;
67
- } else {
68
- needsCorpusConfirm = true;
69
- }
59
+ // One lookup returns the MISS together with the authority behind it, so a
60
+ // stale MISS is never paired with authority from another moment: any
61
+ // second read of authority can rebuild the index and bless it (#3312).
62
+ const lookup = await storage.lookupContentHash(opts.contentHashDedupKey);
63
+ if (!lookup.present && lookup.authoritative) return false;
70
64
  } catch {
71
65
  // Dedup lookup failed (e.g. the authoritative rebuild could not run). Do
72
66
  // NOT fail-open into a possible duplicate — confirm against the corpus.
73
- needsCorpusConfirm = true;
74
67
  }
75
68
  try {
76
69
  // #2016 cold-tier finding: the authoritative content-hash rebuild unions
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { createHash, randomBytes } from "node:crypto";
20
20
  import { abortError, isPreemptedExtraction } from "../abort-error.js";
21
- import { SmartBuffer } from "../buffer.js";
21
+ import { SmartBuffer, type AddTurnOutcome } from "../buffer.js";
22
22
  import type { ImportTurn } from "../bulk-import/types.js";
23
23
  import { resolveNamespaceCapabilities, resolvePipelineProcessingCapabilities, resolveRecallAuxiliaryCapabilities } from "../capabilities.js";
24
24
  import type { CorrectionService } from "../correction/correction-service.js";
@@ -57,18 +57,10 @@ import {
57
57
  type BulkImportBatchIngestResult,
58
58
  } from "../orchestrator.js";
59
59
 
60
- /**
61
- * #3251: raised by the buffered observe capture path when bounded admission
62
- * refuses a NEW session entry because the extraction buffer is at its entry
63
- * cap. The request was never accepted — no durable capture exists and no
64
- * previously accepted capture was evicted to make room.
65
- */
66
- export class ReplayAdmissionRefusedError extends Error {
67
- constructor(message: string) {
68
- super(message);
69
- this.name = "ReplayAdmissionRefusedError";
70
- }
71
- }
60
+ // The class lives in ../buffer-admission.js next to BufferAdmissionRefusedError;
61
+ // re-exported here so existing importers keep their import path.
62
+ import { ReplayAdmissionRefusedError } from "../buffer-admission.js";
63
+ export { ReplayAdmissionRefusedError };
72
64
 
73
65
  export interface TurnIngestionDeps {
74
66
  readonly buffer: SmartBuffer;
@@ -440,10 +432,29 @@ export class TurnIngestionCoordinator {
440
432
  "accepted capture requires an explicit storage target (writeNamespaceOverride or scopeProfileWritePlan.writeNamespace)",
441
433
  );
442
434
  }
443
- for (const [bufferKey, sessionTurns] of bySession) {
444
- // No top-level abort check: cancellation applies to the background
445
- // extraction, never to the durable capture or save barrier (#3251).
446
- if (options.archiveLcm !== false && this.deps.lcmEngine?.enabled) {
435
+ const pin = (turn: BufferTurn): BufferTurn => {
436
+ const pinned: BufferTurn = { ...turn, acceptedCapture: true, writeNamespaceOverride: resolvedWriteTarget };
437
+ if (options.codingContext) pinned.codingContext = options.codingContext;
438
+ if (options.principalOverride) pinned.principalOverride = options.principalOverride;
439
+ if (options.scopeProfileWritePlan) pinned.scopeProfileWritePlan = options.scopeProfileWritePlan;
440
+ return pinned;
441
+ };
442
+ const batches = [...bySession].map(([bufferKey, sessionTurns]) => ({ bufferKey, turns: sessionTurns.map(pin) }));
443
+ const outcomes = await this.deps.buffer.addTurnsBounded(batches);
444
+ if (outcomes === null) {
445
+ const refusedKey = batches.find((batch) => batch.turns.length > 0)?.bufferKey ?? "unknown";
446
+ throw new ReplayAdmissionRefusedError(
447
+ `durable observe capture refused: extraction buffer at entry capacity for batch containing ${refusedKey}`,
448
+ );
449
+ }
450
+
451
+ // All requested session keys were capacity-admitted together before any
452
+ // turn was saved. There is no retry identity: a repeat after a crash that
453
+ // persisted only a prefix duplicates that prefix, as on main.
454
+ await this.deps.buffer.flushPendingSave({ throwOnFailure: true });
455
+
456
+ if (options.archiveLcm !== false && this.deps.lcmEngine?.enabled) {
457
+ for (const [bufferKey, sessionTurns] of bySession) {
447
458
  await this.deps.lcmEngine.observeMessages(
448
459
  bufferKey,
449
460
  sessionTurns.map((turn) => ({
@@ -455,29 +466,17 @@ export class TurnIngestionCoordinator {
455
466
  })),
456
467
  );
457
468
  }
458
- for (const turn of sessionTurns) {
459
- const pinned: BufferTurn = { ...turn, acceptedCapture: true, writeNamespaceOverride: resolvedWriteTarget };
460
- if (options.codingContext) pinned.codingContext = options.codingContext;
461
- if (options.principalOverride) pinned.principalOverride = options.principalOverride;
462
- if (options.scopeProfileWritePlan) pinned.scopeProfileWritePlan = options.scopeProfileWritePlan;
463
- const outcome = await this.deps.buffer.addTurnBounded(bufferKey, pinned);
464
- if (outcome === null) {
465
- throw new ReplayAdmissionRefusedError(
466
- `durable observe capture refused: extraction buffer at entry capacity for ${bufferKey}`,
467
- );
468
- }
469
- if (outcome.decision !== "keep_buffering" && outcome.extractionTurns) {
470
- queueBufferedCaptureScopeGroups(
471
- this.deps.queueBufferedExtraction,
472
- outcome.extractionTurns,
473
- bufferKey,
474
- options,
475
- );
476
- }
469
+ }
470
+
471
+ for (const { bufferKey, outcome } of outcomes) {
472
+ if (outcome.decision !== "keep_buffering" && outcome.extractionTurns) {
473
+ queueBufferedCaptureScopeGroups(
474
+ this.deps.queueBufferedExtraction,
475
+ outcome.extractionTurns,
476
+ bufferKey,
477
+ options,
478
+ );
477
479
  }
478
- // Acceptance durability barrier (#1909 seam): surfaces storage failures
479
- // BEFORE the caller can report acceptance. Not abort-gated.
480
- await this.deps.buffer.flushPendingSave({ throwOnFailure: true });
481
480
  }
482
481
  }
483
482
 
@@ -11,13 +11,20 @@
11
11
  import { z } from "zod";
12
12
 
13
13
  import { defineOperation } from "./access-boundary.js";
14
- import { EngramAccessInputError } from "./access-errors.js";
14
+ import { EngramAccessInputError, rejectReservedArgumentKey } from "./access-errors.js";
15
15
 
16
+ /**
17
+ * Null-prototype accumulator for cleaned request arguments, matching the
18
+ * batch preprocessors (issue #3225 B1): a copied own `__proto__` member on a
19
+ * plain `{}` switches the accumulator's prototype and an inherited field then
20
+ * reaches the zod parse.
21
+ */
16
22
  function stripNulls(data: unknown): unknown {
17
23
  if (data !== null && typeof data === "object" && !Array.isArray(data)) {
18
24
  const obj = data as Record<string, unknown>;
19
- const cleaned: Record<string, unknown> = {};
25
+ const cleaned = Object.create(null);
20
26
  for (const [key, value] of Object.entries(obj)) {
27
+ rejectReservedArgumentKey(key);
21
28
  if (value !== null) cleaned[key] = value;
22
29
  }
23
30
  return cleaned;
@@ -52,6 +52,8 @@ export interface ExtractionGroundingContext {
52
52
  * second pass reads it so its prompts carry the same media warning. */
53
53
  ambientCapture?: boolean;
54
54
  scopeClassificationEnabled?: boolean;
55
+ /** Contents of the turns assertionSource was joined from, so quote anchors cut at real turn boundaries. */
56
+ turns?: readonly string[];
55
57
  }
56
58
 
57
59
  export function applyGroundingWithConnector(
@@ -64,6 +66,7 @@ export function applyGroundingWithConnector(
64
66
  sourceGrounding: caps.sourceGrounding,
65
67
  anchorTemporalExpressions: caps.delinearize,
66
68
  spanMode: config.extraction?.spanMode ?? "off",
69
+ turns: ctx.turns,
67
70
  };
68
71
  const known = buildKnownConnectors(ctx.sourceConnector);
69
72
  // Scope-forcing is decoupled from strip/restore (runs without a trusted