@remnic/core 10.0.30 → 10.0.31

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 (276) hide show
  1. package/dist/access-admin-ops-surface.js +47 -46
  2. package/dist/access-authorization-probe.js +48 -47
  3. package/dist/access-boundary.js +47 -46
  4. package/dist/access-cli.js +107 -107
  5. package/dist/access-extraction-force-flush.d.ts.map +1 -1
  6. package/dist/access-extraction-force-flush.js +47 -46
  7. package/dist/access-http-briefing.js +48 -47
  8. package/dist/access-http-query.js +49 -48
  9. package/dist/access-http.js +65 -64
  10. package/dist/access-identity-continuity-surface.js +47 -46
  11. package/dist/access-lcm-surface.js +47 -46
  12. package/dist/access-mcp.js +57 -56
  13. package/dist/access-namespace-preflight.js +47 -46
  14. package/dist/access-observe-write-surface.d.ts.map +1 -1
  15. package/dist/access-observe-write-surface.js +47 -46
  16. package/dist/access-offline-manifest.js +4 -4
  17. package/dist/access-operations-batch.js +48 -47
  18. package/dist/access-operations.js +55 -54
  19. package/dist/access-recall-concurrency.js +47 -46
  20. package/dist/access-recall-diagnostics.js +2 -2
  21. package/dist/access-recall-response.js +47 -46
  22. package/dist/access-recall-surface.js +47 -46
  23. package/dist/access-schema.d.ts +47 -47
  24. package/dist/access-service.js +47 -46
  25. package/dist/access-surface-catalog.js +49 -48
  26. package/dist/access-surface-catalog.js.map +1 -1
  27. package/dist/active-recall.js +20 -20
  28. package/dist/briefing.js +21 -21
  29. package/dist/buffer-turn-helpers.d.ts +20 -0
  30. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  31. package/dist/buffer-turn-helpers.js +11 -3
  32. package/dist/buffer-turn.d.ts +50 -0
  33. package/dist/buffer-turn.d.ts.map +1 -0
  34. package/dist/buffer-turn.js +1 -0
  35. package/dist/buffer-turn.js.map +1 -0
  36. package/dist/buffer.d.ts +13 -1
  37. package/dist/buffer.d.ts.map +1 -1
  38. package/dist/buffer.js +4 -4
  39. package/dist/causal-consolidation.js +26 -26
  40. package/dist/{chunk-BIE3ZH3N.js → chunk-2IXEKMBJ.js} +143 -112
  41. package/dist/chunk-2IXEKMBJ.js.map +1 -0
  42. package/dist/{chunk-YGVZ4QOZ.js → chunk-3CRE3UZI.js} +2 -2
  43. package/dist/{chunk-YZTFQBYY.js → chunk-3GOPAWA7.js} +3 -3
  44. package/dist/{chunk-NAPHQ2MG.js → chunk-3IDNKOCE.js} +2 -2
  45. package/dist/{chunk-PYCTZ2HX.js → chunk-3VYNGM5A.js} +2 -2
  46. package/dist/{chunk-2XVLY424.js → chunk-42MM5DKK.js} +2 -2
  47. package/dist/{chunk-CKR7LXEK.js → chunk-464QR7WO.js} +255 -63
  48. package/dist/chunk-464QR7WO.js.map +1 -0
  49. package/dist/{chunk-3IXNL6SK.js → chunk-4ZTVOECF.js} +4 -4
  50. package/dist/{chunk-ETZUIRCI.js → chunk-53GYS3GG.js} +2 -2
  51. package/dist/{chunk-ZXTAHSQU.js → chunk-5GPHJ333.js} +2 -2
  52. package/dist/{chunk-AUYFUBBE.js → chunk-5HJCZ7SB.js} +4 -4
  53. package/dist/{chunk-52II7PB6.js → chunk-645WOU5I.js} +4 -4
  54. package/dist/{chunk-2WDMUQOH.js → chunk-64IO6ZEE.js} +2 -2
  55. package/dist/{chunk-OIIAIY7J.js → chunk-7SXUGYPE.js} +3 -3
  56. package/dist/{chunk-74IN6PV5.js → chunk-7WWITD3H.js} +4 -4
  57. package/dist/{chunk-7FZ4YVOH.js → chunk-A5ZY537U.js} +2 -2
  58. package/dist/{chunk-LDXNKEYB.js → chunk-A6PKEU7T.js} +1 -1
  59. package/dist/chunk-A6PKEU7T.js.map +1 -0
  60. package/dist/{chunk-VLBFSD7B.js → chunk-APRGEZQN.js} +2 -2
  61. package/dist/{chunk-XNROYP5E.js → chunk-AQ5XLA36.js} +2 -2
  62. package/dist/{chunk-K2REHR42.js → chunk-BDHYRMJC.js} +2 -2
  63. package/dist/{chunk-GQ7VWY6N.js → chunk-BQ6DGL6A.js} +2 -2
  64. package/dist/{chunk-4OE4UE2Y.js → chunk-D3EXT5KG.js} +2 -2
  65. package/dist/{chunk-MROA6OFU.js → chunk-DGBWPSM6.js} +2 -2
  66. package/dist/{chunk-NOMHXZD7.js → chunk-DNZ2CWPG.js} +2 -2
  67. package/dist/{chunk-HG2VVIKI.js → chunk-DZBMASSI.js} +12 -12
  68. package/dist/{chunk-NB2YX6X2.js → chunk-EXYDKAR6.js} +5 -5
  69. package/dist/{chunk-V4FI2TG6.js → chunk-F5FZ7DB4.js} +2 -2
  70. package/dist/{chunk-IY7DGT2Z.js → chunk-GDPTMK5V.js} +2 -2
  71. package/dist/{chunk-27OJ5G3D.js → chunk-GJBSEMC5.js} +7 -7
  72. package/dist/{chunk-MMBLOJJB.js → chunk-HMON55NJ.js} +85 -44
  73. package/dist/chunk-HMON55NJ.js.map +1 -0
  74. package/dist/{chunk-C2QVDLQV.js → chunk-HNTB7NIE.js} +2 -2
  75. package/dist/{chunk-4HNINU7Q.js → chunk-HSPEVXT5.js} +42 -42
  76. package/dist/{chunk-4QPW6JVI.js → chunk-ID2PLENW.js} +2 -2
  77. package/dist/{chunk-FN6OFDBZ.js → chunk-ID62FNER.js} +2 -2
  78. package/dist/{chunk-GSKW64NB.js → chunk-J6SZXAQ2.js} +2 -2
  79. package/dist/{chunk-3CHHQXAR.js → chunk-JTACUJP3.js} +2 -2
  80. package/dist/{chunk-3SN2D43X.js → chunk-K6RQ2KOX.js} +2 -2
  81. package/dist/{chunk-GWB7ZD2A.js → chunk-K6YJGWJP.js} +2 -2
  82. package/dist/{chunk-OANSZGAG.js → chunk-KFR343VH.js} +2 -2
  83. package/dist/{chunk-WC3V7HHP.js → chunk-KJZKO2M6.js} +2 -1
  84. package/dist/chunk-KJZKO2M6.js.map +1 -0
  85. package/dist/{chunk-T5IF2ZCE.js → chunk-KZMRMKC7.js} +81 -2
  86. package/dist/chunk-KZMRMKC7.js.map +1 -0
  87. package/dist/{chunk-NPO4ZQFY.js → chunk-LDXS746V.js} +3 -3
  88. package/dist/{chunk-UTXYOWDR.js → chunk-LGR54NYT.js} +3 -3
  89. package/dist/{chunk-UTXYOWDR.js.map → chunk-LGR54NYT.js.map} +1 -1
  90. package/dist/{chunk-DDLRKPR2.js → chunk-NKW27NU3.js} +2 -2
  91. package/dist/{chunk-4HYP4OKH.js → chunk-NU247WVX.js} +2 -2
  92. package/dist/{chunk-RRJ462HP.js → chunk-O6YJI5VX.js} +2 -2
  93. package/dist/{chunk-WYLBEJQI.js → chunk-OCWNT322.js} +4 -4
  94. package/dist/{chunk-UUYAW3JH.js → chunk-OIKLZCJD.js} +2 -2
  95. package/dist/{chunk-IO2UQVR2.js → chunk-OTWZXOJI.js} +2 -2
  96. package/dist/{chunk-OXYXMGRH.js → chunk-PGHYQMKZ.js} +11 -11
  97. package/dist/{chunk-LDEKVBVO.js → chunk-PZWW6MDE.js} +2 -2
  98. package/dist/{chunk-TFA6HPOA.js → chunk-QCP75JGO.js} +5 -5
  99. package/dist/{chunk-M236YHUH.js → chunk-RILIWZA6.js} +3 -3
  100. package/dist/{chunk-7VB3Y5OL.js → chunk-RRYN5FFC.js} +4 -4
  101. package/dist/{chunk-PZEADMX4.js → chunk-RU6WB55E.js} +9 -9
  102. package/dist/{chunk-VARNSLUW.js → chunk-SGII36L6.js} +2 -2
  103. package/dist/{chunk-KFKFK6G6.js → chunk-TB2J4PBY.js} +3 -3
  104. package/dist/{chunk-WQIYGYY6.js → chunk-TEUPXKC5.js} +2 -2
  105. package/dist/{chunk-VEB7NEON.js → chunk-U4BNX4R2.js} +3 -3
  106. package/dist/{chunk-7HFTCBCZ.js → chunk-UBBZFODO.js} +2 -2
  107. package/dist/{chunk-654GT5UP.js → chunk-UZDOPJ2P.js} +2 -2
  108. package/dist/{chunk-D4RV54HH.js → chunk-VAPVRDC4.js} +2 -2
  109. package/dist/{chunk-OXX4ORRL.js → chunk-W7X7XT7Z.js} +15 -15
  110. package/dist/{chunk-5NPZFXQH.js → chunk-WK7GFAQ2.js} +2 -2
  111. package/dist/{chunk-G6DAE2LT.js → chunk-WPCK6EJ2.js} +2 -2
  112. package/dist/{chunk-VKQI47DJ.js → chunk-Y7427SL7.js} +4 -4
  113. package/dist/{chunk-ZCRDYQL6.js → chunk-YGBWSKBD.js} +2 -2
  114. package/dist/{chunk-NCPR2FOJ.js → chunk-YXWLQHAW.js} +2 -2
  115. package/dist/{chunk-UM73Q46J.js → chunk-ZEMBRSKK.js} +2 -2
  116. package/dist/{chunk-TTIEXXL3.js → chunk-ZGP2XB4S.js} +16 -16
  117. package/dist/{chunk-UVUBLLM3.js → chunk-ZY4S5WMQ.js} +2 -2
  118. package/dist/cli.js +83 -82
  119. package/dist/coding/export-okf-codegraph.js +20 -20
  120. package/dist/compounding/engine.js +22 -22
  121. package/dist/config.js +20 -20
  122. package/dist/connectors/codex-materialize-runner.js +21 -21
  123. package/dist/connectors/index.js +21 -21
  124. package/dist/convergence-refresh.js +23 -23
  125. package/dist/conversation-index/backend.js +2 -2
  126. package/dist/corpus-watermark.js +23 -23
  127. package/dist/entity-retrieval.js +22 -22
  128. package/dist/event-order-recall.js +3 -3
  129. package/dist/external-wiki-access.js +48 -47
  130. package/dist/extraction.js +31 -31
  131. package/dist/harmonic-retrieval.js +21 -21
  132. package/dist/index.js +126 -126
  133. package/dist/lcm/engine.js +2 -2
  134. package/dist/lcm/index.js +2 -2
  135. package/dist/location/index.js +23 -23
  136. package/dist/maintenance/memory-governance.js +21 -21
  137. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +21 -21
  138. package/dist/maintenance/rebuild-memory-projection-runner.js +23 -23
  139. package/dist/maintenance/rebuild-memory-projection.js +22 -22
  140. package/dist/memory-browse-operations.js +48 -47
  141. package/dist/namespaces/migrate.js +22 -22
  142. package/dist/namespaces/scope-profiles.d.ts +1 -0
  143. package/dist/namespaces/scope-profiles.d.ts.map +1 -1
  144. package/dist/namespaces/search.js +9 -9
  145. package/dist/namespaces/storage.js +21 -21
  146. package/dist/offline-sync-impression-drain.js +3 -3
  147. package/dist/operator-doctor-corpus.js +24 -24
  148. package/dist/operator-toolkit.js +32 -32
  149. package/dist/orchestration/maintenance.js +33 -33
  150. package/dist/orchestration/session-context.d.ts +6 -0
  151. package/dist/orchestration/session-context.d.ts.map +1 -1
  152. package/dist/orchestration/turn-ingestion.d.ts +35 -1
  153. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  154. package/dist/orchestrator.d.ts +2 -32
  155. package/dist/orchestrator.d.ts.map +1 -1
  156. package/dist/orchestrator.js +107 -107
  157. package/dist/recall-explain-renderer.js +3 -3
  158. package/dist/recall-navigate-access.js +2 -2
  159. package/dist/recall-navigate-disclosure.js +2 -2
  160. package/dist/recall-navigate.js +2 -2
  161. package/dist/recall-navigation-operations.js +48 -47
  162. package/dist/recall-navigation.js +47 -46
  163. package/dist/recall-xray-cli.js +4 -4
  164. package/dist/recall-xray-renderer.js +3 -3
  165. package/dist/recall-xray.js +2 -2
  166. package/dist/relay/mission.d.ts +2 -2
  167. package/dist/resume-bundles.js +20 -20
  168. package/dist/schemas.d.ts +4 -4
  169. package/dist/search/factory.js +8 -8
  170. package/dist/search/index.js +13 -13
  171. package/dist/search/lancedb-backend.js +2 -2
  172. package/dist/search/meilisearch-backend.js +2 -2
  173. package/dist/search/orama-backend.js +2 -2
  174. package/dist/semantic-consolidation.js +22 -22
  175. package/dist/semantic-rule-promotion.js +21 -21
  176. package/dist/semantic-rule-verifier.js +21 -21
  177. package/dist/source-agent-qualifier.js +24 -24
  178. package/dist/standup/build.js +21 -21
  179. package/dist/storage.js +20 -20
  180. package/dist/support-passport/contracts.d.ts +6 -6
  181. package/dist/support-passport/grant-contracts.d.ts +4 -4
  182. package/dist/support-passport/index.js +50 -49
  183. package/dist/transfer/export-okf.js +21 -21
  184. package/dist/types.d.ts +3 -17
  185. package/dist/types.d.ts.map +1 -1
  186. package/dist/types.js +1 -1
  187. package/dist/verified-recall.js +21 -21
  188. package/dist/who-knows-cli.js +22 -22
  189. package/dist/who-knows.js +21 -21
  190. package/package.json +2 -2
  191. package/src/access-extraction-force-flush.ts +37 -25
  192. package/src/access-observe-acceptance-boundary.test.ts +172 -0
  193. package/src/access-observe-replay-durability.test.ts +2212 -0
  194. package/src/access-observe-write-surface.ts +36 -41
  195. package/src/access-service-observe-lcm-parity.test.ts +63 -39
  196. package/src/access-service-observe-scope.test.ts +3 -2
  197. package/src/buffer-turn-helpers.ts +120 -1
  198. package/src/buffer-turn.ts +50 -0
  199. package/src/buffer.ts +115 -49
  200. package/src/namespaces/scope-profiles.ts +1 -1
  201. package/src/orchestration/extraction-run.ts +1 -1
  202. package/src/orchestration/session-context.ts +117 -49
  203. package/src/orchestration/turn-ingestion.ts +313 -9
  204. package/src/orchestrator-flush.test.ts +16 -3
  205. package/src/orchestrator.ts +2 -32
  206. package/src/types.ts +3 -17
  207. package/dist/chunk-BIE3ZH3N.js.map +0 -1
  208. package/dist/chunk-CKR7LXEK.js.map +0 -1
  209. package/dist/chunk-LDXNKEYB.js.map +0 -1
  210. package/dist/chunk-MMBLOJJB.js.map +0 -1
  211. package/dist/chunk-T5IF2ZCE.js.map +0 -1
  212. package/dist/chunk-WC3V7HHP.js.map +0 -1
  213. /package/dist/{chunk-YGVZ4QOZ.js.map → chunk-3CRE3UZI.js.map} +0 -0
  214. /package/dist/{chunk-YZTFQBYY.js.map → chunk-3GOPAWA7.js.map} +0 -0
  215. /package/dist/{chunk-NAPHQ2MG.js.map → chunk-3IDNKOCE.js.map} +0 -0
  216. /package/dist/{chunk-PYCTZ2HX.js.map → chunk-3VYNGM5A.js.map} +0 -0
  217. /package/dist/{chunk-2XVLY424.js.map → chunk-42MM5DKK.js.map} +0 -0
  218. /package/dist/{chunk-3IXNL6SK.js.map → chunk-4ZTVOECF.js.map} +0 -0
  219. /package/dist/{chunk-ETZUIRCI.js.map → chunk-53GYS3GG.js.map} +0 -0
  220. /package/dist/{chunk-ZXTAHSQU.js.map → chunk-5GPHJ333.js.map} +0 -0
  221. /package/dist/{chunk-AUYFUBBE.js.map → chunk-5HJCZ7SB.js.map} +0 -0
  222. /package/dist/{chunk-52II7PB6.js.map → chunk-645WOU5I.js.map} +0 -0
  223. /package/dist/{chunk-2WDMUQOH.js.map → chunk-64IO6ZEE.js.map} +0 -0
  224. /package/dist/{chunk-OIIAIY7J.js.map → chunk-7SXUGYPE.js.map} +0 -0
  225. /package/dist/{chunk-74IN6PV5.js.map → chunk-7WWITD3H.js.map} +0 -0
  226. /package/dist/{chunk-7FZ4YVOH.js.map → chunk-A5ZY537U.js.map} +0 -0
  227. /package/dist/{chunk-VLBFSD7B.js.map → chunk-APRGEZQN.js.map} +0 -0
  228. /package/dist/{chunk-XNROYP5E.js.map → chunk-AQ5XLA36.js.map} +0 -0
  229. /package/dist/{chunk-K2REHR42.js.map → chunk-BDHYRMJC.js.map} +0 -0
  230. /package/dist/{chunk-GQ7VWY6N.js.map → chunk-BQ6DGL6A.js.map} +0 -0
  231. /package/dist/{chunk-4OE4UE2Y.js.map → chunk-D3EXT5KG.js.map} +0 -0
  232. /package/dist/{chunk-MROA6OFU.js.map → chunk-DGBWPSM6.js.map} +0 -0
  233. /package/dist/{chunk-NOMHXZD7.js.map → chunk-DNZ2CWPG.js.map} +0 -0
  234. /package/dist/{chunk-HG2VVIKI.js.map → chunk-DZBMASSI.js.map} +0 -0
  235. /package/dist/{chunk-NB2YX6X2.js.map → chunk-EXYDKAR6.js.map} +0 -0
  236. /package/dist/{chunk-V4FI2TG6.js.map → chunk-F5FZ7DB4.js.map} +0 -0
  237. /package/dist/{chunk-IY7DGT2Z.js.map → chunk-GDPTMK5V.js.map} +0 -0
  238. /package/dist/{chunk-27OJ5G3D.js.map → chunk-GJBSEMC5.js.map} +0 -0
  239. /package/dist/{chunk-C2QVDLQV.js.map → chunk-HNTB7NIE.js.map} +0 -0
  240. /package/dist/{chunk-4HNINU7Q.js.map → chunk-HSPEVXT5.js.map} +0 -0
  241. /package/dist/{chunk-4QPW6JVI.js.map → chunk-ID2PLENW.js.map} +0 -0
  242. /package/dist/{chunk-FN6OFDBZ.js.map → chunk-ID62FNER.js.map} +0 -0
  243. /package/dist/{chunk-GSKW64NB.js.map → chunk-J6SZXAQ2.js.map} +0 -0
  244. /package/dist/{chunk-3CHHQXAR.js.map → chunk-JTACUJP3.js.map} +0 -0
  245. /package/dist/{chunk-3SN2D43X.js.map → chunk-K6RQ2KOX.js.map} +0 -0
  246. /package/dist/{chunk-GWB7ZD2A.js.map → chunk-K6YJGWJP.js.map} +0 -0
  247. /package/dist/{chunk-OANSZGAG.js.map → chunk-KFR343VH.js.map} +0 -0
  248. /package/dist/{chunk-NPO4ZQFY.js.map → chunk-LDXS746V.js.map} +0 -0
  249. /package/dist/{chunk-DDLRKPR2.js.map → chunk-NKW27NU3.js.map} +0 -0
  250. /package/dist/{chunk-4HYP4OKH.js.map → chunk-NU247WVX.js.map} +0 -0
  251. /package/dist/{chunk-RRJ462HP.js.map → chunk-O6YJI5VX.js.map} +0 -0
  252. /package/dist/{chunk-WYLBEJQI.js.map → chunk-OCWNT322.js.map} +0 -0
  253. /package/dist/{chunk-UUYAW3JH.js.map → chunk-OIKLZCJD.js.map} +0 -0
  254. /package/dist/{chunk-IO2UQVR2.js.map → chunk-OTWZXOJI.js.map} +0 -0
  255. /package/dist/{chunk-OXYXMGRH.js.map → chunk-PGHYQMKZ.js.map} +0 -0
  256. /package/dist/{chunk-LDEKVBVO.js.map → chunk-PZWW6MDE.js.map} +0 -0
  257. /package/dist/{chunk-TFA6HPOA.js.map → chunk-QCP75JGO.js.map} +0 -0
  258. /package/dist/{chunk-M236YHUH.js.map → chunk-RILIWZA6.js.map} +0 -0
  259. /package/dist/{chunk-7VB3Y5OL.js.map → chunk-RRYN5FFC.js.map} +0 -0
  260. /package/dist/{chunk-PZEADMX4.js.map → chunk-RU6WB55E.js.map} +0 -0
  261. /package/dist/{chunk-VARNSLUW.js.map → chunk-SGII36L6.js.map} +0 -0
  262. /package/dist/{chunk-KFKFK6G6.js.map → chunk-TB2J4PBY.js.map} +0 -0
  263. /package/dist/{chunk-WQIYGYY6.js.map → chunk-TEUPXKC5.js.map} +0 -0
  264. /package/dist/{chunk-VEB7NEON.js.map → chunk-U4BNX4R2.js.map} +0 -0
  265. /package/dist/{chunk-7HFTCBCZ.js.map → chunk-UBBZFODO.js.map} +0 -0
  266. /package/dist/{chunk-654GT5UP.js.map → chunk-UZDOPJ2P.js.map} +0 -0
  267. /package/dist/{chunk-D4RV54HH.js.map → chunk-VAPVRDC4.js.map} +0 -0
  268. /package/dist/{chunk-OXX4ORRL.js.map → chunk-W7X7XT7Z.js.map} +0 -0
  269. /package/dist/{chunk-5NPZFXQH.js.map → chunk-WK7GFAQ2.js.map} +0 -0
  270. /package/dist/{chunk-G6DAE2LT.js.map → chunk-WPCK6EJ2.js.map} +0 -0
  271. /package/dist/{chunk-VKQI47DJ.js.map → chunk-Y7427SL7.js.map} +0 -0
  272. /package/dist/{chunk-ZCRDYQL6.js.map → chunk-YGBWSKBD.js.map} +0 -0
  273. /package/dist/{chunk-NCPR2FOJ.js.map → chunk-YXWLQHAW.js.map} +0 -0
  274. /package/dist/{chunk-UM73Q46J.js.map → chunk-ZEMBRSKK.js.map} +0 -0
  275. /package/dist/{chunk-TTIEXXL3.js.map → chunk-ZGP2XB4S.js.map} +0 -0
  276. /package/dist/{chunk-UVUBLLM3.js.map → chunk-ZY4S5WMQ.js.map} +0 -0
package/src/buffer.ts CHANGED
@@ -10,8 +10,9 @@ import type {
10
10
  PluginConfig,
11
11
  SignalLevel,
12
12
  } from "./types.js";
13
- import { resolvePresentationCapabilities } from "./capabilities.js";
13
+ import { resolvePresentationCapabilities, resolveNamespaceCapabilities } from "./capabilities.js";
14
14
  import { resolvePrincipal } from "./namespaces/principal.js";
15
+ import { scopeProfilePlanIdentity } from "./buffer-turn-helpers.js";
15
16
  import { ExtractionDeadlineError } from "./orchestration/extraction-run.js";
16
17
  import type { ExtractionBufferSnapshot } from "./extraction-liveness.js";
17
18
  import {
@@ -20,9 +21,11 @@ import {
20
21
  bufferTurnsEqual,
21
22
  copyBufferTurn,
22
23
  describeError,
24
+ hasPinnedCaptureScope,
23
25
  liveTurnsFromExtractionSnapshot,
24
26
  matchingQueuedExtractionPrefixLength,
25
27
  probeWithTimeout,
28
+ selectRetainedDeferredTurns,
26
29
  } from "./buffer-turn-helpers.js";
27
30
 
28
31
  export type TriggerDecision = "extract_now" | "extract_batch" | "keep_buffering";
@@ -35,6 +38,11 @@ export interface AddTurnOutcome {
35
38
  export interface RetainedTurnCleanupOptions {
36
39
  abortSignal?: AbortSignal;
37
40
  deadlineMs?: number;
41
+ /** Keep cleanup within the accepted namespace. */
42
+ writeNamespaceOverride?: string;
43
+ /** Match the original profile and project binding, not just its write namespace. */
44
+ scopeProfileWritePlan?: import("./namespaces/scope-profiles.js").ResolvedScopeProfilePlan | null;
45
+ codingContext?: import("./types.js").CodingContext;
38
46
  }
39
47
 
40
48
  /**
@@ -65,6 +73,8 @@ export interface BufferSurpriseProbe {
65
73
  }
66
74
 
67
75
  const MAX_BUFFER_ENTRY_COUNT = 200;
76
+ const isAcceptedTurn = (t: { acceptedCapture?: boolean; writeNamespaceOverride?: string }): boolean =>
77
+ t.acceptedCapture === true || t.writeNamespaceOverride !== undefined;
68
78
 
69
79
  /**
70
80
  * Upper bound on how long a debounced buffer save may be deferred under
@@ -272,7 +282,13 @@ export class SmartBuffer {
272
282
  if (leftAt !== rightAt) return leftAt - rightAt;
273
283
  return (insertionOrder.get(left) ?? 0) - (insertionOrder.get(right) ?? 0);
274
284
  };
275
- const candidates = keys.filter((key) => key !== "default" && !retainKeys.includes(key));
285
+ const candidates = keys.filter(
286
+ (key) =>
287
+ key !== "default" &&
288
+ !retainKeys.includes(key) &&
289
+ // #3251: accepted captures exit via refusal/backpressure, never eviction.
290
+ !this.entryHoldsAcceptedCapture(entries[key]),
291
+ );
276
292
  const emptyRemovable = candidates
277
293
  .filter((key) => (entries[key]?.turns.length ?? 0) === 0)
278
294
  .sort(byOldestActivity);
@@ -524,11 +540,66 @@ export class SmartBuffer {
524
540
  return (await this.addTurnWithOutcome(bufferKey, turn)).decision;
525
541
  }
526
542
 
543
+ /** Admit atomically; refuse at capacity rather than evict accepted work. */
544
+ async addTurnBounded(bufferKey: string, turn: BufferTurn): Promise<AddTurnOutcome | null> {
545
+ const mutation = await this.enqueueMutation(async () => {
546
+ await this.loadUnlocked();
547
+ if (this.peekEntry(bufferKey) === null) {
548
+ this.makeRoomForEntryUnlocked();
549
+ if (Object.keys(this.state.entries ?? {}).length >= MAX_BUFFER_ENTRY_COUNT) {
550
+ log.warn(
551
+ `buffer[${bufferKey}]: durable capture refused at MAX_BUFFER_ENTRY_COUNT=${MAX_BUFFER_ENTRY_COUNT}`,
552
+ );
553
+ return null;
554
+ }
555
+ }
556
+ return this.recordTurnUnlocked(bufferKey, turn);
557
+ });
558
+ if (mutation === null) return null;
559
+ return this.completeAddTurnOutcome(bufferKey, turn, mutation);
560
+ }
561
+
562
+ /** Prune empty entries only; accepted work must survive capacity pressure. */
563
+ private makeRoomForEntryUnlocked(): void {
564
+ const entries = this.state.entries;
565
+ if (!entries) return;
566
+ const keys = Object.keys(entries);
567
+ if (keys.length < MAX_BUFFER_ENTRY_COUNT) return;
568
+ const byOldestActivity = (left: string, right: string): number =>
569
+ this.entryActivityAt(entries[left] ?? { turns: [], lastExtractionAt: null, extractionCount: 0 }) -
570
+ this.entryActivityAt(entries[right] ?? { turns: [], lastExtractionAt: null, extractionCount: 0 });
571
+ const emptyRemovable = keys
572
+ .filter(
573
+ (key) =>
574
+ key !== "default" &&
575
+ (entries[key]?.turns.length ?? 0) === 0 &&
576
+ (entries[key]?.retainedTurns?.length ?? 0) === 0,
577
+ )
578
+ .sort(byOldestActivity);
579
+ for (const key of emptyRemovable) {
580
+ if (Object.keys(entries).length < MAX_BUFFER_ENTRY_COUNT) break;
581
+ delete entries[key];
582
+ }
583
+ }
584
+
585
+ private entryHoldsAcceptedCapture(entry: BufferEntryState | undefined): boolean {
586
+ return (entry?.turns ?? []).some(isAcceptedTurn) || (entry?.retainedTurns ?? []).some(isAcceptedTurn);
587
+ }
588
+
527
589
  async addTurnWithOutcome(
528
590
  bufferKey: string,
529
591
  turn: BufferTurn,
530
592
  ): Promise<AddTurnOutcome> {
531
593
  const mutation = await this.enqueueMutation(() => this.recordTurnUnlocked(bufferKey, turn));
594
+ return this.completeAddTurnOutcome(bufferKey, turn, mutation);
595
+ }
596
+
597
+ /** Run probes outside the mutation lock to avoid reentrancy deadlocks. */
598
+ private async completeAddTurnOutcome(
599
+ bufferKey: string,
600
+ turn: BufferTurn,
601
+ mutation: AddTurnMutationResult,
602
+ ): Promise<AddTurnOutcome> {
532
603
  let decision = mutation.decision;
533
604
  let extractionTurns: BufferTurn[] | undefined;
534
605
 
@@ -874,61 +945,20 @@ export class SmartBuffer {
874
945
  bufferKey: string,
875
946
  turns: BufferTurn[],
876
947
  max = 10,
948
+ originalTurns?: readonly BufferTurn[],
877
949
  ): Promise<void> {
878
950
  await this.enqueueMutation(async () => {
879
951
  await this.loadUnlocked();
880
952
  const entry = this.entryFor(bufferKey);
881
- if (!Array.isArray(turns) || turns.length === 0 || max <= 0) {
882
- delete entry.retainedTurns;
883
- } else {
884
- // Guard `slice(-max)` against `max === 0` (CLAUDE.md gotcha 27):
885
- // `slice(-0)` equals `slice(0)` and would return ALL entries. We
886
- // already early-return above when max <= 0.
887
- const tail = turns.slice(-max);
888
- // Copy explicit fields only — never spread an external object into a
889
- // plain object because spread preserves any own `__proto__` /
890
- // `constructor` keys that may have arrived via JSON deserialization
891
- // of untrusted input (CodeQL js/prototype-polluting-assignment).
892
- entry.retainedTurns = tail.map<BufferTurn>((t) => {
893
- const copy: BufferTurn = {
894
- role: t.role,
895
- content: typeof t.content === "string" ? t.content : "",
896
- timestamp:
897
- typeof t.timestamp === "string"
898
- ? t.timestamp
899
- : new Date().toISOString(),
900
- };
901
- if (typeof t.sessionKey === "string") copy.sessionKey = t.sessionKey;
902
- if (typeof t.sessionOwnerPrincipal === "string") {
903
- copy.sessionOwnerPrincipal = t.sessionOwnerPrincipal;
904
- }
905
- if (typeof t.logicalSessionKey === "string") {
906
- copy.logicalSessionKey = t.logicalSessionKey;
907
- }
908
- if (
909
- t.providerThreadId === null ||
910
- typeof t.providerThreadId === "string"
911
- ) {
912
- copy.providerThreadId = t.providerThreadId;
913
- }
914
- if (typeof t.turnFingerprint === "string") {
915
- copy.turnFingerprint = t.turnFingerprint;
916
- }
917
- if (typeof t.persistProcessedFingerprint === "boolean") {
918
- copy.persistProcessedFingerprint = t.persistProcessedFingerprint;
919
- }
920
- if (typeof t.sourceConnector === "string") copy.sourceConnector = t.sourceConnector;
921
- if (t.originRole === "user" || t.originRole === "assistant" || t.originRole === "tool") {
922
- copy.originRole = t.originRole;
923
- }
924
- return copy;
925
- });
926
- }
953
+ const retained = selectRetainedDeferredTurns(
954
+ entry.retainedTurns ?? [], turns, max, originalTurns,
955
+ );
956
+ if (retained.length > 0) entry.retainedTurns = retained;
957
+ else delete entry.retainedTurns;
927
958
  await this.saveUnlocked();
928
959
  });
929
960
  }
930
961
 
931
-
932
962
  /**
933
963
  * Return the current retention window (issue #562, PR 2). Primarily for
934
964
  * tests and diagnostics.
@@ -953,6 +983,11 @@ export class SmartBuffer {
953
983
  typeof ownerPrincipal === "string" && ownerPrincipal.trim().length > 0
954
984
  ? ownerPrincipal.trim()
955
985
  : undefined;
986
+ const namespacesEnabled = resolveNamespaceCapabilities(this.config).namespaces === true;
987
+ const normalizedCleanupNamespace =
988
+ namespacesEnabled && options.writeNamespaceOverride !== undefined && options.writeNamespaceOverride.length > 0
989
+ ? options.writeNamespaceOverride
990
+ : undefined;
956
991
  const assertLifecycle = (): void => {
957
992
  throwIfAborted(options.abortSignal, "extraction force-flush aborted");
958
993
  if (typeof options.deadlineMs === "number" && Date.now() >= options.deadlineMs) {
@@ -978,8 +1013,39 @@ export class SmartBuffer {
978
1013
  return;
979
1014
  }
980
1015
  let changed = false;
1016
+ const matchesCleanupScope = (turn: BufferTurn): boolean => {
1017
+ // Legacy unpinned turns keep their existing cleanup semantics.
1018
+ if (
1019
+ turn.writeNamespaceOverride !== undefined &&
1020
+ normalizedCleanupNamespace !== undefined &&
1021
+ turn.writeNamespaceOverride !== normalizedCleanupNamespace
1022
+ ) {
1023
+ return false;
1024
+ }
1025
+ // Equal write namespaces can still belong to different project bindings.
1026
+ if (options.scopeProfileWritePlan && hasPinnedCaptureScope(turn)) {
1027
+ if (
1028
+ scopeProfilePlanIdentity(turn.scopeProfileWritePlan) !== scopeProfilePlanIdentity(options.scopeProfileWritePlan) &&
1029
+ !(turn.acceptedCapture === true && turn.scopeProfileWritePlan === undefined)
1030
+ ) {
1031
+ return false;
1032
+ }
1033
+ const ctx = options.codingContext;
1034
+ if (
1035
+ ctx && turn.codingContext &&
1036
+ (turn.codingContext.projectId !== ctx.projectId ||
1037
+ turn.codingContext.rootPath !== ctx.rootPath ||
1038
+ turn.codingContext.branch !== ctx.branch ||
1039
+ turn.codingContext.defaultBranch !== ctx.defaultBranch)
1040
+ ) {
1041
+ return false;
1042
+ }
1043
+ }
1044
+ return true;
1045
+ };
981
1046
  const belongsToOwner = (turn: BufferTurn): boolean => {
982
1047
  if (turn.sessionKey !== sessionKey) return false;
1048
+ if (!matchesCleanupScope(turn)) return false;
983
1049
  if (normalizedOwnerPrincipal === undefined) return true;
984
1050
  if (turn.sessionOwnerPrincipal === normalizedOwnerPrincipal) return true;
985
1051
  return (
@@ -87,7 +87,7 @@ function scopeProfileSelfNamespace(principal: string | undefined, config: Plugin
87
87
  return derivedScopeProfileSelfNamespace(principal, config) ?? existing;
88
88
  }
89
89
 
90
- function hasExplicitNamespacePolicy(namespace: string, config: PluginConfig): boolean {
90
+ export function hasExplicitNamespacePolicy(namespace: string, config: PluginConfig): boolean {
91
91
  const identity = normalizeNamespaceIdentity(namespace);
92
92
  return (config.namespacePolicies ?? []).some(
93
93
  (policy) => normalizeNamespaceIdentity(policy.name) === identity,
@@ -1108,7 +1108,7 @@ export class ExtractionRunCoordinator {
1108
1108
  normalizedTurns: normalizedTurns as BufferTurn[],
1109
1109
  bufferKey,
1110
1110
  retainDeferredTurns: (key, retainedTurns, max) =>
1111
- this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max),
1111
+ this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max, turns),
1112
1112
  clearBuffer,
1113
1113
  failOnExtractionFailure: options.failOnExtractionFailure === true,
1114
1114
  });
@@ -28,6 +28,7 @@ import type { ExtractionRunResult } from "./extraction-run.js";
28
28
  import type { EntitySynthesisCoordinator } from "./entity-synthesis-coordinator.js";
29
29
  import type { BufferTurn, CodingContext, DaySummaryResult, MemoryFile, PluginConfig } from "../types.js";
30
30
  import type { ResolvedScopeProfilePlan } from "../namespaces/scope-profiles.js";
31
+ import { groupTurnsByExtractionScope, hasPinnedCaptureScope, scopeProfilePlanIdentity } from "../buffer-turn-helpers.js";
31
32
  import {
32
33
  Orchestrator,
33
34
  } from "../orchestrator.js";
@@ -103,6 +104,12 @@ export interface SessionFlushOptions {
103
104
  /** Resolved profile plan from access scope resolution; avoids recomputing from shared session context. */
104
105
  scopeProfileWritePlan?: ResolvedScopeProfilePlan | null;
105
106
  principalOverride?: string;
107
+ /**
108
+ * #3251: the ALREADY-resolved native coding context this flush's plan was
109
+ * resolved with — plan-pinned flushes drain only captures bound to the
110
+ * SAME project identity.
111
+ */
112
+ codingContext?: CodingContext;
106
113
  }
107
114
 
108
115
  /** Scoped flushes must never claim an opaque buffer without trusted ingestion ownership. */
@@ -475,8 +482,49 @@ export class SessionContextCoordinator {
475
482
  ownershipEnforced &&
476
483
  ownerPrincipal !== undefined &&
477
484
  (resolvedSessionPrincipal === undefined || resolvedSessionPrincipal === "default");
485
+ // #3251: the flush's effective write namespace. An explicit override pins
486
+ // it; otherwise it is the session principal's default store. Only PINNED
487
+ // captures are namespace-compared — legacy unpinned turns keep their
488
+ // prior semantics (any flush of their session drains them), while a
489
+ // pinned capture is never drained under a different namespace.
490
+ const flushNamespace =
491
+ options.writeNamespaceOverride !== undefined
492
+ ? options.writeNamespaceOverride
493
+ : namespacesEnabled && this.deps.config
494
+ ? defaultNamespaceForPrincipal(resolvePrincipal(sessionKey, this.deps.config), this.deps.config)
495
+ : undefined;
478
496
  const belongsToSession = (turn: BufferTurn): boolean => {
479
497
  if (turn.sessionKey !== sessionKey) return false;
498
+ if (
499
+ namespacesEnabled &&
500
+ turn.writeNamespaceOverride !== undefined &&
501
+ turn.writeNamespaceOverride !== flushNamespace
502
+ ) {
503
+ return false;
504
+ }
505
+ // Existing profile pins require the same profile and project binding.
506
+ // Accepted no-plan captures stay unprofiled within the matching namespace.
507
+ const flushPlanIdentity = scopeProfilePlanIdentity(options.scopeProfileWritePlan);
508
+ if (flushPlanIdentity !== "" && hasPinnedCaptureScope(turn)) {
509
+ if (
510
+ scopeProfilePlanIdentity(turn.scopeProfileWritePlan) !== flushPlanIdentity &&
511
+ !(turn.acceptedCapture === true && turn.scopeProfileWritePlan === undefined)
512
+ ) {
513
+ return false;
514
+ }
515
+ // Compare bindings only when the ORIGINAL capture is context-bound —
516
+ // an unbound capture must not be orphaned by a project attached later.
517
+ if (
518
+ options.codingContext &&
519
+ turn.codingContext &&
520
+ (turn.codingContext.projectId !== options.codingContext.projectId ||
521
+ turn.codingContext.rootPath !== options.codingContext.rootPath ||
522
+ turn.codingContext.branch !== options.codingContext.branch ||
523
+ turn.codingContext.defaultBranch !== options.codingContext.defaultBranch)
524
+ ) {
525
+ return false;
526
+ }
527
+ }
480
528
  if (!ownershipEnforced || ownerPrincipal === undefined) return true;
481
529
  if (turn.sessionOwnerPrincipal === ownerPrincipal) return true;
482
530
  return (
@@ -499,60 +547,80 @@ export class SessionContextCoordinator {
499
547
  }
500
548
  for (const bufferKey of bufferKeys) {
501
549
  const turns = this.deps.buffer.getTurns(bufferKey);
502
- const turnsForSession = scopedRequest ? turns.filter(belongsToSession) : turns;
550
+ // #3251: EVERY flush filters — unqualified lifecycle drains must not
551
+ // clear another namespace's pinned capture; single-store mode no-ops
552
+ // the filter (namespacesEnabled gate inside belongsToSession).
553
+ const turnsForSession = turns.filter(belongsToSession);
503
554
  if (turnsForSession.length === 0) continue;
504
- const retainedTurnsForOtherSessions =
505
- scopedRequest && typeof this.deps.buffer.getRetainedDeferredTurns === "function"
506
- ? this.deps.buffer
507
- .getRetainedDeferredTurns(bufferKey)
508
- .filter((turn) => !belongsToSession(turn))
509
- : [];
510
- try {
511
- await new Promise<void>((resolve, reject) => {
512
- void this.deps.queueBufferedExtraction(turnsForSession, "trigger_mode", {
513
- bufferKey,
514
- clearBufferAfterExtraction: true,
515
- skipDedupeCheck: true,
516
- skipCharThreshold: true,
517
- skipUserTurnThreshold: true,
518
- failOnExtractionFailure: options.failOnExtractionFailure === true,
519
- forceExtractionAttempt: true,
520
- abortSignal: options.abortSignal,
521
- extractionDeadlineMs: options.extractionDeadlineMs,
522
- writeNamespaceOverride: options.writeNamespaceOverride,
523
- principalOverride: options.principalOverride,
524
- scopeProfileWritePlan: options.scopeProfileWritePlan,
525
- clearMatchingTurns: options.clearMatchingTurns ?? scopedRequest,
526
- onDurableCommit: options.onCommitted,
527
- onTaskSettled: (error) => (error ? reject(error) : resolve()),
528
- })
529
- .catch(reject);
530
- });
531
- if (options.reason === "session_end" && this.deps.config.sessionExperience?.enabled === true) {
532
- await this.extractSessionEndExperience(turnsForSession, sessionKey, options);
555
+ const scopeGroups = groupTurnsByExtractionScope(turnsForSession);
556
+ // Filtered or grouped slices need identity clearing rather than prefix clearing.
557
+ const needsIdentityClear =
558
+ turnsForSession.length < turns.length || scopeGroups.length > 1;
559
+ const namespacesOn = this.deps.config
560
+ ? resolveNamespaceCapabilities(this.deps.config).namespaces === true
561
+ : true;
562
+ for (const group of scopeGroups) {
563
+ const groupPinned =
564
+ group.writeNamespaceOverride !== undefined ||
565
+ group.principalOverride !== undefined ||
566
+ group.scopeProfileWritePlan !== undefined;
567
+ // Preserve groups captured under another profile or project binding;
568
+ // the binding compares only when the ORIGINAL group is context-bound.
569
+ if (
570
+ group.scopeProfileWritePlan &&
571
+ options.scopeProfileWritePlan &&
572
+ (scopeProfilePlanIdentity(group.scopeProfileWritePlan) !==
573
+ scopeProfilePlanIdentity(options.scopeProfileWritePlan) ||
574
+ (options.codingContext &&
575
+ group.codingContext &&
576
+ (group.codingContext.projectId !== options.codingContext.projectId ||
577
+ group.codingContext.rootPath !== options.codingContext.rootPath ||
578
+ group.codingContext.branch !== options.codingContext.branch ||
579
+ group.codingContext.defaultBranch !== options.codingContext.defaultBranch)))
580
+ ) {
581
+ continue;
533
582
  }
534
- } finally {
583
+ // Force-flush must reject preserved work before retained-turn cleanup can run.
535
584
  if (
536
- retainedTurnsForOtherSessions.length > 0 &&
537
- typeof this.deps.buffer.retainDeferredTurns === "function"
585
+ groupPinned &&
586
+ !namespacesOn &&
587
+ group.writeNamespaceOverride !== undefined &&
588
+ group.writeNamespaceOverride !== this.deps.config?.defaultNamespace
538
589
  ) {
539
- const retainedAfterExtraction = this.deps.buffer.getRetainedDeferredTurns(bufferKey);
540
- await this.deps.buffer
541
- .retainDeferredTurns(
542
- bufferKey,
543
- [
544
- ...retainedAfterExtraction.filter((turn) => belongsToSession(turn)),
545
- ...retainedTurnsForOtherSessions,
546
- ],
547
- 10,
548
- )
549
- .catch((error: unknown) => {
550
- log.warn(
551
- `session flush could not restore retained turns for other sessions in ${bufferKey}`,
552
- error,
553
- );
554
- });
590
+ if (options.failOnExtractionFailure === true) {
591
+ throw new Error(
592
+ `accepted capture preserved: pinned_namespace_unavailable (${group.writeNamespaceOverride})`,
593
+ );
594
+ }
595
+ log.warn(
596
+ `flushSession: pinned namespace ${group.writeNamespaceOverride} unavailable while namespaces are disabled; capture stays pending`,
597
+ );
598
+ continue;
555
599
  }
600
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
601
+ void this.deps.queueBufferedExtraction(group.turns, "trigger_mode", {
602
+ bufferKey,
603
+ clearBufferAfterExtraction: true,
604
+ skipDedupeCheck: true,
605
+ skipCharThreshold: true,
606
+ skipUserTurnThreshold: true,
607
+ failOnExtractionFailure: options.failOnExtractionFailure === true,
608
+ forceExtractionAttempt: true,
609
+ abortSignal: options.abortSignal,
610
+ extractionDeadlineMs: options.extractionDeadlineMs,
611
+ writeNamespaceOverride: group.writeNamespaceOverride ?? options.writeNamespaceOverride,
612
+ principalOverride: group.principalOverride ?? options.principalOverride,
613
+ scopeProfileWritePlan: group.turns.some((turn) => turn.acceptedCapture === true)
614
+ ? group.scopeProfileWritePlan ?? null
615
+ : group.scopeProfileWritePlan ?? options.scopeProfileWritePlan,
616
+ clearMatchingTurns: options.clearMatchingTurns === true || scopedRequest || groupPinned || needsIdentityClear,
617
+ onDurableCommit: options.onCommitted,
618
+ onTaskSettled: (error) => (error ? reject(error) : resolve()),
619
+ }).catch(reject);
620
+ await promise;
621
+ }
622
+ if (options.reason === "session_end" && this.deps.config.sessionExperience?.enabled === true) {
623
+ await this.extractSessionEndExperience(turnsForSession, sessionKey, options);
556
624
  }
557
625
  }
558
626
  }