@remnic/core 10.0.60 → 10.0.61

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 (252) hide show
  1. package/dist/access-admin-ops-surface.js +21 -20
  2. package/dist/access-authorization-probe.js +22 -21
  3. package/dist/access-boundary.js +21 -20
  4. package/dist/access-cli.js +64 -63
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +21 -20
  7. package/dist/access-http-briefing.js +22 -21
  8. package/dist/access-http-query.js +23 -22
  9. package/dist/access-http.js +39 -38
  10. package/dist/access-identity-continuity-surface.js +21 -20
  11. package/dist/access-lcm-surface.js +21 -20
  12. package/dist/access-mcp.js +31 -30
  13. package/dist/access-namespace-preflight.js +21 -20
  14. package/dist/access-observe-write-category.js +4 -4
  15. package/dist/access-observe-write-surface.js +21 -20
  16. package/dist/access-operations-batch.js +23 -22
  17. package/dist/access-operations.js +30 -29
  18. package/dist/access-recall-concurrency.js +21 -20
  19. package/dist/access-recall-response.js +21 -20
  20. package/dist/access-recall-surface.js +21 -20
  21. package/dist/access-schema.js +3 -3
  22. package/dist/access-service.js +21 -20
  23. package/dist/access-surface-catalog.js +23 -22
  24. package/dist/access-surface-catalog.js.map +1 -1
  25. package/dist/active-recall.js +6 -4
  26. package/dist/active-recall.js.map +1 -1
  27. package/dist/briefing.js +7 -5
  28. package/dist/buffer-persistence.d.ts +14 -3
  29. package/dist/buffer-persistence.d.ts.map +1 -1
  30. package/dist/buffer-persistence.js +1 -1
  31. package/dist/buffer-turn-helpers.d.ts +7 -0
  32. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  33. package/dist/buffer-turn-helpers.js +5 -3
  34. package/dist/buffer.d.ts +2 -0
  35. package/dist/buffer.d.ts.map +1 -1
  36. package/dist/buffer.js +3 -3
  37. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
  38. package/dist/causal-consolidation.js +8 -6
  39. package/dist/causal-consolidation.js.map +1 -1
  40. package/dist/{chunk-PRSF7DWT.js → chunk-23OEPU3K.js} +2 -2
  41. package/dist/{chunk-2EJAPJ5G.js → chunk-46U6L5DM.js} +2 -2
  42. package/dist/{chunk-NF5EHQTJ.js → chunk-6HTEPYOI.js} +4 -4
  43. package/dist/{chunk-TIS6T6UP.js → chunk-6T4CMKNP.js} +2 -2
  44. package/dist/{chunk-W37MS6SX.js → chunk-73A2DDDR.js} +2 -2
  45. package/dist/{chunk-X2TZWVCC.js → chunk-7AKPLEJV.js} +2 -2
  46. package/dist/{chunk-T4SMMTGY.js → chunk-7OKYO2UC.js} +2 -2
  47. package/dist/{chunk-WJC5KGYC.js → chunk-7ULSP7AU.js} +6 -6
  48. package/dist/{chunk-J53IBVMF.js → chunk-A4FZLM6Q.js} +2 -2
  49. package/dist/{chunk-KQAFEZQX.js → chunk-BUKK5SWA.js} +2 -2
  50. package/dist/{chunk-JRY55I6Z.js → chunk-C2AYUXEF.js} +2 -2
  51. package/dist/{chunk-PTJWUN4H.js → chunk-D3BYJSHX.js} +25 -25
  52. package/dist/{chunk-ADK5TMQL.js → chunk-D4WR56FR.js} +2 -2
  53. package/dist/{chunk-3DG2YAAX.js → chunk-DMYNM3WG.js} +59 -32
  54. package/dist/chunk-DMYNM3WG.js.map +1 -0
  55. package/dist/{chunk-66CC6L6R.js → chunk-DP22K7IC.js} +2 -2
  56. package/dist/{chunk-AWKADEMO.js → chunk-DTQJ7BRN.js} +3 -3
  57. package/dist/{chunk-IA3XZG4V.js → chunk-DZL5PBEJ.js} +2 -2
  58. package/dist/{chunk-7DHNBABU.js → chunk-EGCC5OJE.js} +3 -3
  59. package/dist/{chunk-N5H22V75.js → chunk-EJ4RS7I7.js} +2 -2
  60. package/dist/{chunk-MOB4JHG6.js → chunk-ENAPZQA6.js} +2 -2
  61. package/dist/{chunk-DF4SYU4Z.js → chunk-F2UAAI6B.js} +2 -2
  62. package/dist/{chunk-KXDUSGPM.js → chunk-FKNNWH7P.js} +2 -2
  63. package/dist/{chunk-74Z2BYEK.js → chunk-GNP4BV4Z.js} +2 -2
  64. package/dist/{chunk-LGLLCWA2.js → chunk-HWMGQDZI.js} +2 -2
  65. package/dist/{chunk-PIVSUPF2.js → chunk-IIUEHIQ2.js} +2 -2
  66. package/dist/{chunk-JFRO5HJY.js → chunk-IPN67KVR.js} +2 -2
  67. package/dist/chunk-JCL7RO2G.js +674 -0
  68. package/dist/chunk-JCL7RO2G.js.map +1 -0
  69. package/dist/{chunk-ZXEPLZUQ.js → chunk-JGSWWD33.js} +7 -7
  70. package/dist/{chunk-A3UFV4K3.js → chunk-JIYJIH5N.js} +2 -2
  71. package/dist/{chunk-M7KAJBO2.js → chunk-JKUVPVKR.js} +3 -3
  72. package/dist/{chunk-L5RMURVI.js → chunk-JL5FHZ7O.js} +29 -5
  73. package/dist/chunk-JL5FHZ7O.js.map +1 -0
  74. package/dist/{chunk-QWURVT5Z.js → chunk-L2HSSVVD.js} +2 -2
  75. package/dist/{chunk-VQ6H4DL6.js → chunk-L334PSLW.js} +4 -4
  76. package/dist/{chunk-UY67YP6Q.js → chunk-LIUI46BX.js} +2 -2
  77. package/dist/{chunk-4OFXLQJD.js → chunk-MPDLLLOG.js} +8 -3
  78. package/dist/chunk-MPDLLLOG.js.map +1 -0
  79. package/dist/{chunk-2HVCTFKG.js → chunk-MPJ7VHHA.js} +2 -2
  80. package/dist/{chunk-I6UTOUBK.js → chunk-N7RAABFM.js} +149 -344
  81. package/dist/chunk-N7RAABFM.js.map +1 -0
  82. package/dist/{chunk-RSYFSCKL.js → chunk-NSUGAIIB.js} +12 -12
  83. package/dist/{chunk-Z4EH5TSO.js → chunk-NXHBQC2O.js} +2 -2
  84. package/dist/{chunk-JEDS2QYV.js → chunk-OH55F76C.js} +2 -2
  85. package/dist/{chunk-GGHX4YHM.js → chunk-OUJ4DVR5.js} +2 -2
  86. package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
  87. package/dist/{chunk-4O4VAEWH.js → chunk-P5F7ISLV.js} +2 -2
  88. package/dist/{chunk-5XY46Z2W.js → chunk-PNVQNAJM.js} +3 -3
  89. package/dist/{chunk-HL643H2F.js → chunk-PRYDNBCO.js} +7 -7
  90. package/dist/{chunk-F3GX76LV.js → chunk-PUTH5REB.js} +2 -2
  91. package/dist/{chunk-IJFX4JRI.js → chunk-RBUR6USI.js} +7 -7
  92. package/dist/{chunk-JM2WJBP7.js → chunk-RZSHIJYY.js} +2 -2
  93. package/dist/{chunk-6BKSUA6P.js → chunk-SVTIWBSA.js} +2 -2
  94. package/dist/{chunk-M3GOWG2P.js → chunk-TBFAQHQZ.js} +2 -2
  95. package/dist/{chunk-ZRDEOC4X.js → chunk-TKCJI7WA.js} +2 -2
  96. package/dist/{chunk-NJZY5LOC.js → chunk-TLOAAOT3.js} +2 -2
  97. package/dist/{chunk-KZMRMKC7.js → chunk-U7MIMS2Y.js} +11 -1
  98. package/dist/chunk-U7MIMS2Y.js.map +1 -0
  99. package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
  100. package/dist/{chunk-GJF337YM.js → chunk-VCYH2XUY.js} +2 -2
  101. package/dist/{chunk-UP2Q7U4G.js → chunk-WD2QH3JM.js} +2 -2
  102. package/dist/{chunk-MHSUEA32.js → chunk-WWSJQ7MP.js} +3 -3
  103. package/dist/{chunk-UIX4JLVV.js → chunk-XAFSZ4DP.js} +2 -2
  104. package/dist/{chunk-PJLJN23N.js → chunk-XICJL56Z.js} +2 -2
  105. package/dist/{chunk-C5GTIRIO.js → chunk-XJDSZFBZ.js} +18 -18
  106. package/dist/cli.js +52 -51
  107. package/dist/coding/export-okf-codegraph.js +6 -4
  108. package/dist/coding/export-okf-codegraph.js.map +1 -1
  109. package/dist/compounding/engine.js +8 -6
  110. package/dist/config.js +6 -4
  111. package/dist/connectors/codex-materialize-runner.js +7 -5
  112. package/dist/connectors/index.js +7 -5
  113. package/dist/convergence-refresh.js +9 -7
  114. package/dist/corpus-watermark.js +9 -7
  115. package/dist/deep-recall-http-glue.js +4 -4
  116. package/dist/entity-retrieval.js +7 -5
  117. package/dist/external-wiki-access.js +22 -21
  118. package/dist/extraction.js +7 -5
  119. package/dist/harmonic-retrieval.js +7 -5
  120. package/dist/index.d.ts +1 -1
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +66 -63
  123. package/dist/location/index.js +9 -7
  124. package/dist/maintenance/memory-governance.js +7 -5
  125. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -5
  126. package/dist/maintenance/rebuild-memory-projection-runner.js +9 -7
  127. package/dist/maintenance/rebuild-memory-projection.js +8 -6
  128. package/dist/memory-browse-operations.js +22 -21
  129. package/dist/namespaces/migrate.js +8 -6
  130. package/dist/namespaces/search.js +3 -3
  131. package/dist/namespaces/storage.js +7 -5
  132. package/dist/operator-doctor-corpus.js +10 -8
  133. package/dist/operator-toolkit.js +15 -13
  134. package/dist/orchestration/buffer-drain-gate.d.ts +21 -0
  135. package/dist/orchestration/buffer-drain-gate.d.ts.map +1 -0
  136. package/dist/orchestration/buffer-drain-runner.d.ts +79 -0
  137. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -0
  138. package/dist/orchestration/buffer-drain-runner.js +780 -0
  139. package/dist/orchestration/buffer-drain-runner.js.map +1 -0
  140. package/dist/orchestration/buffer-swap.d.ts +12 -0
  141. package/dist/orchestration/buffer-swap.d.ts.map +1 -0
  142. package/dist/orchestration/maintenance.js +17 -15
  143. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  144. package/dist/orchestrator.d.ts +4 -1
  145. package/dist/orchestrator.d.ts.map +1 -1
  146. package/dist/orchestrator.js +64 -63
  147. package/dist/recall-navigation-operations.js +22 -21
  148. package/dist/recall-navigation.js +21 -20
  149. package/dist/resume-bundles.js +6 -4
  150. package/dist/search/factory.js +2 -2
  151. package/dist/search/index.js +4 -4
  152. package/dist/semantic-consolidation.js +8 -6
  153. package/dist/semantic-rule-promotion.js +7 -5
  154. package/dist/semantic-rule-verifier.js +7 -5
  155. package/dist/source-agent-qualifier.js +7 -5
  156. package/dist/standup/build.js +7 -5
  157. package/dist/storage/buffer-drain-claims.d.ts +159 -0
  158. package/dist/storage/buffer-drain-claims.d.ts.map +1 -0
  159. package/dist/storage/buffer-drain-claims.js +18 -0
  160. package/dist/storage/buffer-drain-claims.js.map +1 -0
  161. package/dist/storage/buffer-session-files.d.ts +14 -2
  162. package/dist/storage/buffer-session-files.d.ts.map +1 -1
  163. package/dist/storage/buffer-session-files.js +22 -0
  164. package/dist/storage/buffer-session-files.js.map +1 -0
  165. package/dist/storage.d.ts +6 -1
  166. package/dist/storage.d.ts.map +1 -1
  167. package/dist/storage.js +6 -4
  168. package/dist/support-passport/index.js +24 -23
  169. package/dist/transfer/backup.js +2 -2
  170. package/dist/transfer/capsule-export.js +2 -2
  171. package/dist/transfer/capsule-import.js +2 -2
  172. package/dist/transfer/export-okf.js +7 -5
  173. package/dist/verified-recall.js +7 -5
  174. package/dist/who-knows-cli.js +8 -6
  175. package/dist/who-knows.js +7 -5
  176. package/package.json +32 -2
  177. package/src/buffer-drain-handover.test.ts +307 -0
  178. package/src/buffer-persistence.ts +27 -5
  179. package/src/buffer-turn-helpers.ts +16 -0
  180. package/src/buffer.ts +32 -2
  181. package/src/index.ts +1 -1
  182. package/src/orchestration/buffer-drain-gate.test.ts +40 -0
  183. package/src/orchestration/buffer-drain-gate.ts +38 -0
  184. package/src/orchestration/buffer-drain-runner.ts +312 -0
  185. package/src/orchestration/buffer-swap.ts +27 -0
  186. package/src/orchestration/turn-ingestion.ts +2 -0
  187. package/src/orchestrator.ts +9 -6
  188. package/src/storage/buffer-drain-claims.test.ts +448 -0
  189. package/src/storage/buffer-drain-claims.ts +593 -0
  190. package/src/storage/buffer-session-files.ts +50 -3
  191. package/src/storage.ts +8 -11
  192. package/src/testing/subjects/extraction-lifecycle.test.ts +34 -0
  193. package/dist/chunk-3DG2YAAX.js.map +0 -1
  194. package/dist/chunk-4OFXLQJD.js.map +0 -1
  195. package/dist/chunk-I6UTOUBK.js.map +0 -1
  196. package/dist/chunk-KZMRMKC7.js.map +0 -1
  197. package/dist/chunk-L5RMURVI.js.map +0 -1
  198. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  199. /package/dist/{chunk-PRSF7DWT.js.map → chunk-23OEPU3K.js.map} +0 -0
  200. /package/dist/{chunk-2EJAPJ5G.js.map → chunk-46U6L5DM.js.map} +0 -0
  201. /package/dist/{chunk-NF5EHQTJ.js.map → chunk-6HTEPYOI.js.map} +0 -0
  202. /package/dist/{chunk-TIS6T6UP.js.map → chunk-6T4CMKNP.js.map} +0 -0
  203. /package/dist/{chunk-W37MS6SX.js.map → chunk-73A2DDDR.js.map} +0 -0
  204. /package/dist/{chunk-X2TZWVCC.js.map → chunk-7AKPLEJV.js.map} +0 -0
  205. /package/dist/{chunk-T4SMMTGY.js.map → chunk-7OKYO2UC.js.map} +0 -0
  206. /package/dist/{chunk-WJC5KGYC.js.map → chunk-7ULSP7AU.js.map} +0 -0
  207. /package/dist/{chunk-J53IBVMF.js.map → chunk-A4FZLM6Q.js.map} +0 -0
  208. /package/dist/{chunk-KQAFEZQX.js.map → chunk-BUKK5SWA.js.map} +0 -0
  209. /package/dist/{chunk-JRY55I6Z.js.map → chunk-C2AYUXEF.js.map} +0 -0
  210. /package/dist/{chunk-PTJWUN4H.js.map → chunk-D3BYJSHX.js.map} +0 -0
  211. /package/dist/{chunk-ADK5TMQL.js.map → chunk-D4WR56FR.js.map} +0 -0
  212. /package/dist/{chunk-66CC6L6R.js.map → chunk-DP22K7IC.js.map} +0 -0
  213. /package/dist/{chunk-AWKADEMO.js.map → chunk-DTQJ7BRN.js.map} +0 -0
  214. /package/dist/{chunk-IA3XZG4V.js.map → chunk-DZL5PBEJ.js.map} +0 -0
  215. /package/dist/{chunk-7DHNBABU.js.map → chunk-EGCC5OJE.js.map} +0 -0
  216. /package/dist/{chunk-N5H22V75.js.map → chunk-EJ4RS7I7.js.map} +0 -0
  217. /package/dist/{chunk-MOB4JHG6.js.map → chunk-ENAPZQA6.js.map} +0 -0
  218. /package/dist/{chunk-DF4SYU4Z.js.map → chunk-F2UAAI6B.js.map} +0 -0
  219. /package/dist/{chunk-KXDUSGPM.js.map → chunk-FKNNWH7P.js.map} +0 -0
  220. /package/dist/{chunk-74Z2BYEK.js.map → chunk-GNP4BV4Z.js.map} +0 -0
  221. /package/dist/{chunk-LGLLCWA2.js.map → chunk-HWMGQDZI.js.map} +0 -0
  222. /package/dist/{chunk-PIVSUPF2.js.map → chunk-IIUEHIQ2.js.map} +0 -0
  223. /package/dist/{chunk-JFRO5HJY.js.map → chunk-IPN67KVR.js.map} +0 -0
  224. /package/dist/{chunk-ZXEPLZUQ.js.map → chunk-JGSWWD33.js.map} +0 -0
  225. /package/dist/{chunk-A3UFV4K3.js.map → chunk-JIYJIH5N.js.map} +0 -0
  226. /package/dist/{chunk-M7KAJBO2.js.map → chunk-JKUVPVKR.js.map} +0 -0
  227. /package/dist/{chunk-QWURVT5Z.js.map → chunk-L2HSSVVD.js.map} +0 -0
  228. /package/dist/{chunk-VQ6H4DL6.js.map → chunk-L334PSLW.js.map} +0 -0
  229. /package/dist/{chunk-UY67YP6Q.js.map → chunk-LIUI46BX.js.map} +0 -0
  230. /package/dist/{chunk-2HVCTFKG.js.map → chunk-MPJ7VHHA.js.map} +0 -0
  231. /package/dist/{chunk-RSYFSCKL.js.map → chunk-NSUGAIIB.js.map} +0 -0
  232. /package/dist/{chunk-Z4EH5TSO.js.map → chunk-NXHBQC2O.js.map} +0 -0
  233. /package/dist/{chunk-JEDS2QYV.js.map → chunk-OH55F76C.js.map} +0 -0
  234. /package/dist/{chunk-GGHX4YHM.js.map → chunk-OUJ4DVR5.js.map} +0 -0
  235. /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
  236. /package/dist/{chunk-4O4VAEWH.js.map → chunk-P5F7ISLV.js.map} +0 -0
  237. /package/dist/{chunk-5XY46Z2W.js.map → chunk-PNVQNAJM.js.map} +0 -0
  238. /package/dist/{chunk-HL643H2F.js.map → chunk-PRYDNBCO.js.map} +0 -0
  239. /package/dist/{chunk-F3GX76LV.js.map → chunk-PUTH5REB.js.map} +0 -0
  240. /package/dist/{chunk-IJFX4JRI.js.map → chunk-RBUR6USI.js.map} +0 -0
  241. /package/dist/{chunk-JM2WJBP7.js.map → chunk-RZSHIJYY.js.map} +0 -0
  242. /package/dist/{chunk-6BKSUA6P.js.map → chunk-SVTIWBSA.js.map} +0 -0
  243. /package/dist/{chunk-M3GOWG2P.js.map → chunk-TBFAQHQZ.js.map} +0 -0
  244. /package/dist/{chunk-ZRDEOC4X.js.map → chunk-TKCJI7WA.js.map} +0 -0
  245. /package/dist/{chunk-NJZY5LOC.js.map → chunk-TLOAAOT3.js.map} +0 -0
  246. /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
  247. /package/dist/{chunk-GJF337YM.js.map → chunk-VCYH2XUY.js.map} +0 -0
  248. /package/dist/{chunk-UP2Q7U4G.js.map → chunk-WD2QH3JM.js.map} +0 -0
  249. /package/dist/{chunk-MHSUEA32.js.map → chunk-WWSJQ7MP.js.map} +0 -0
  250. /package/dist/{chunk-UIX4JLVV.js.map → chunk-XAFSZ4DP.js.map} +0 -0
  251. /package/dist/{chunk-PJLJN23N.js.map → chunk-XICJL56Z.js.map} +0 -0
  252. /package/dist/{chunk-C5GTIRIO.js.map → chunk-XJDSZFBZ.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  importCapsule
3
- } from "../chunk-A3UFV4K3.js";
3
+ } from "../chunk-JIYJIH5N.js";
4
4
  import "../chunk-564YP3DW.js";
5
5
  import "../chunk-M5GVCJSJ.js";
6
6
  import "../chunk-RBOJCG4T.js";
@@ -11,7 +11,7 @@ import "../chunk-7CT3AI52.js";
11
11
  import "../chunk-C64XATK7.js";
12
12
  import "../chunk-42JKGUFJ.js";
13
13
  import "../chunk-WEHSQBFR.js";
14
- import "../chunk-X7Y7WX73.js";
14
+ import "../chunk-UVYI6VIX.js";
15
15
  import "../chunk-BJMBJZ2Y.js";
16
16
  import "../chunk-UKJAGEXH.js";
17
17
  import "../chunk-FP2373TW.js";
@@ -4,19 +4,21 @@ import {
4
4
  exportOkfBundle,
5
5
  firstHeading,
6
6
  parseIncludeStatus
7
- } from "../chunk-6BKSUA6P.js";
7
+ } from "../chunk-SVTIWBSA.js";
8
8
  import {
9
9
  OKF_EXPORT_VERSION
10
10
  } from "../chunk-I7TR2OCU.js";
11
11
  import "../chunk-D24OXEPB.js";
12
12
  import "../chunk-3UXOZBHV.js";
13
- import "../chunk-I6UTOUBK.js";
13
+ import "../chunk-N7RAABFM.js";
14
14
  import "../chunk-UHVXE24Y.js";
15
15
  import "../chunk-DM5QZ3YU.js";
16
16
  import "../chunk-WVEEVYMH.js";
17
+ import "../chunk-JCL7RO2G.js";
17
18
  import "../chunk-G6OFEGUR.js";
18
- import "../chunk-JEDS2QYV.js";
19
- import "../chunk-VQ6H4DL6.js";
19
+ import "../chunk-OH55F76C.js";
20
+ import "../chunk-L334PSLW.js";
21
+ import "../chunk-CYEPCZN5.js";
20
22
  import "../chunk-DLZGSZVC.js";
21
23
  import "../chunk-OVZIYLBZ.js";
22
24
  import "../chunk-EXYT52NR.js";
@@ -24,7 +26,6 @@ import "../chunk-DOJBV6WR.js";
24
26
  import "../chunk-PFUXPTBT.js";
25
27
  import "../chunk-WNEVEMJ3.js";
26
28
  import "../chunk-Y5NLBXPV.js";
27
- import "../chunk-CYEPCZN5.js";
28
29
  import "../chunk-TFA6HPOA.js";
29
30
  import "../chunk-F4TWWUMP.js";
30
31
  import "../chunk-72A4L5JJ.js";
@@ -68,6 +69,7 @@ import "../chunk-OR42ZI54.js";
68
69
  import "../chunk-23G5P7TA.js";
69
70
  import "../chunk-C64XATK7.js";
70
71
  import "../chunk-42JKGUFJ.js";
72
+ import "../chunk-U7MIMS2Y.js";
71
73
  import "../chunk-AVVCPA7U.js";
72
74
  import "../chunk-I55KAVET.js";
73
75
  import "../chunk-L2EXJQJP.js";
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  compareVerifiedEpisodeResults,
3
3
  searchVerifiedEpisodes
4
- } from "./chunk-66CC6L6R.js";
4
+ } from "./chunk-DP22K7IC.js";
5
5
  import "./chunk-B7RWBZXW.js";
6
- import "./chunk-I6UTOUBK.js";
6
+ import "./chunk-N7RAABFM.js";
7
7
  import "./chunk-UHVXE24Y.js";
8
8
  import "./chunk-DM5QZ3YU.js";
9
9
  import "./chunk-WVEEVYMH.js";
10
+ import "./chunk-JCL7RO2G.js";
10
11
  import "./chunk-G6OFEGUR.js";
11
- import "./chunk-JEDS2QYV.js";
12
- import "./chunk-VQ6H4DL6.js";
12
+ import "./chunk-OH55F76C.js";
13
+ import "./chunk-L334PSLW.js";
14
+ import "./chunk-CYEPCZN5.js";
13
15
  import "./chunk-DLZGSZVC.js";
14
16
  import "./chunk-OVZIYLBZ.js";
15
17
  import "./chunk-EXYT52NR.js";
@@ -17,7 +19,6 @@ import "./chunk-DOJBV6WR.js";
17
19
  import "./chunk-PFUXPTBT.js";
18
20
  import "./chunk-WNEVEMJ3.js";
19
21
  import "./chunk-Y5NLBXPV.js";
20
- import "./chunk-CYEPCZN5.js";
21
22
  import "./chunk-TFA6HPOA.js";
22
23
  import "./chunk-F4TWWUMP.js";
23
24
  import "./chunk-72A4L5JJ.js";
@@ -61,6 +62,7 @@ import "./chunk-OR42ZI54.js";
61
62
  import "./chunk-23G5P7TA.js";
62
63
  import "./chunk-C64XATK7.js";
63
64
  import "./chunk-42JKGUFJ.js";
65
+ import "./chunk-U7MIMS2Y.js";
64
66
  import "./chunk-AVVCPA7U.js";
65
67
  import "./chunk-I55KAVET.js";
66
68
  import "./chunk-L2EXJQJP.js";
@@ -4,15 +4,17 @@ import {
4
4
  renderWhoKnows,
5
5
  renderWhoKnowsJson,
6
6
  renderWhoKnowsText
7
- } from "./chunk-PIVSUPF2.js";
8
- import "./chunk-J53IBVMF.js";
9
- import "./chunk-I6UTOUBK.js";
7
+ } from "./chunk-IIUEHIQ2.js";
8
+ import "./chunk-A4FZLM6Q.js";
9
+ import "./chunk-N7RAABFM.js";
10
10
  import "./chunk-UHVXE24Y.js";
11
11
  import "./chunk-DM5QZ3YU.js";
12
12
  import "./chunk-WVEEVYMH.js";
13
+ import "./chunk-JCL7RO2G.js";
13
14
  import "./chunk-G6OFEGUR.js";
14
- import "./chunk-JEDS2QYV.js";
15
- import "./chunk-VQ6H4DL6.js";
15
+ import "./chunk-OH55F76C.js";
16
+ import "./chunk-L334PSLW.js";
17
+ import "./chunk-CYEPCZN5.js";
16
18
  import "./chunk-DLZGSZVC.js";
17
19
  import "./chunk-OVZIYLBZ.js";
18
20
  import "./chunk-EXYT52NR.js";
@@ -20,7 +22,6 @@ import "./chunk-DOJBV6WR.js";
20
22
  import "./chunk-PFUXPTBT.js";
21
23
  import "./chunk-WNEVEMJ3.js";
22
24
  import "./chunk-Y5NLBXPV.js";
23
- import "./chunk-CYEPCZN5.js";
24
25
  import "./chunk-TFA6HPOA.js";
25
26
  import "./chunk-F4TWWUMP.js";
26
27
  import "./chunk-72A4L5JJ.js";
@@ -64,6 +65,7 @@ import "./chunk-OR42ZI54.js";
64
65
  import "./chunk-23G5P7TA.js";
65
66
  import "./chunk-C64XATK7.js";
66
67
  import "./chunk-42JKGUFJ.js";
68
+ import "./chunk-U7MIMS2Y.js";
67
69
  import "./chunk-AVVCPA7U.js";
68
70
  import "./chunk-I55KAVET.js";
69
71
  import "./chunk-L2EXJQJP.js";
package/dist/who-knows.js CHANGED
@@ -9,14 +9,16 @@ import {
9
9
  loadWhoKnowsEntities,
10
10
  tokenizeTopic,
11
11
  validateWhoKnowsInput
12
- } from "./chunk-J53IBVMF.js";
13
- import "./chunk-I6UTOUBK.js";
12
+ } from "./chunk-A4FZLM6Q.js";
13
+ import "./chunk-N7RAABFM.js";
14
14
  import "./chunk-UHVXE24Y.js";
15
15
  import "./chunk-DM5QZ3YU.js";
16
16
  import "./chunk-WVEEVYMH.js";
17
+ import "./chunk-JCL7RO2G.js";
17
18
  import "./chunk-G6OFEGUR.js";
18
- import "./chunk-JEDS2QYV.js";
19
- import "./chunk-VQ6H4DL6.js";
19
+ import "./chunk-OH55F76C.js";
20
+ import "./chunk-L334PSLW.js";
21
+ import "./chunk-CYEPCZN5.js";
20
22
  import "./chunk-DLZGSZVC.js";
21
23
  import "./chunk-OVZIYLBZ.js";
22
24
  import "./chunk-EXYT52NR.js";
@@ -24,7 +26,6 @@ import "./chunk-DOJBV6WR.js";
24
26
  import "./chunk-PFUXPTBT.js";
25
27
  import "./chunk-WNEVEMJ3.js";
26
28
  import "./chunk-Y5NLBXPV.js";
27
- import "./chunk-CYEPCZN5.js";
28
29
  import "./chunk-TFA6HPOA.js";
29
30
  import "./chunk-F4TWWUMP.js";
30
31
  import "./chunk-72A4L5JJ.js";
@@ -68,6 +69,7 @@ import "./chunk-OR42ZI54.js";
68
69
  import "./chunk-23G5P7TA.js";
69
70
  import "./chunk-C64XATK7.js";
70
71
  import "./chunk-42JKGUFJ.js";
72
+ import "./chunk-U7MIMS2Y.js";
71
73
  import "./chunk-AVVCPA7U.js";
72
74
  import "./chunk-I55KAVET.js";
73
75
  import "./chunk-L2EXJQJP.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "10.0.60",
3
+ "version": "10.0.61",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1900,6 +1900,16 @@
1900
1900
  "remnic-source": "./src/orchestration/maintenance.ts",
1901
1901
  "import": "./dist/orchestration/maintenance.js"
1902
1902
  },
1903
+ "./orchestration/buffer-drain-runner": {
1904
+ "types": "./dist/orchestration/buffer-drain-runner.d.ts",
1905
+ "remnic-source": "./src/orchestration/buffer-drain-runner.ts",
1906
+ "import": "./dist/orchestration/buffer-drain-runner.js"
1907
+ },
1908
+ "./orchestration/buffer-drain-runner.js": {
1909
+ "types": "./dist/orchestration/buffer-drain-runner.d.ts",
1910
+ "remnic-source": "./src/orchestration/buffer-drain-runner.ts",
1911
+ "import": "./dist/orchestration/buffer-drain-runner.js"
1912
+ },
1903
1913
  "./orchestration/compression-guideline-coordinator": {
1904
1914
  "types": "./dist/orchestration/compression-guideline-coordinator.d.ts",
1905
1915
  "remnic-source": "./src/orchestration/compression-guideline-coordinator.ts",
@@ -2780,6 +2790,26 @@
2780
2790
  "remnic-source": "./src/storage.ts",
2781
2791
  "import": "./dist/storage.js"
2782
2792
  },
2793
+ "./storage/buffer-drain-claims": {
2794
+ "types": "./dist/storage/buffer-drain-claims.d.ts",
2795
+ "remnic-source": "./src/storage/buffer-drain-claims.ts",
2796
+ "import": "./dist/storage/buffer-drain-claims.js"
2797
+ },
2798
+ "./storage/buffer-drain-claims.js": {
2799
+ "types": "./dist/storage/buffer-drain-claims.d.ts",
2800
+ "remnic-source": "./src/storage/buffer-drain-claims.ts",
2801
+ "import": "./dist/storage/buffer-drain-claims.js"
2802
+ },
2803
+ "./storage/buffer-session-files": {
2804
+ "types": "./dist/storage/buffer-session-files.d.ts",
2805
+ "remnic-source": "./src/storage/buffer-session-files.ts",
2806
+ "import": "./dist/storage/buffer-session-files.js"
2807
+ },
2808
+ "./storage/buffer-session-files.js": {
2809
+ "types": "./dist/storage/buffer-session-files.d.ts",
2810
+ "remnic-source": "./src/storage/buffer-session-files.ts",
2811
+ "import": "./dist/storage/buffer-session-files.js"
2812
+ },
2783
2813
  "./store-contract": {
2784
2814
  "types": "./dist/store-contract.d.ts",
2785
2815
  "remnic-source": "./src/store-contract.ts",
@@ -3346,7 +3376,7 @@
3346
3376
  "core"
3347
3377
  ],
3348
3378
  "peerDependencies": {
3349
- "@remnic/coding-graph": "^10.0.60"
3379
+ "@remnic/coding-graph": "^10.0.61"
3350
3380
  },
3351
3381
  "peerDependenciesMeta": {
3352
3382
  "@remnic/coding-graph": {
@@ -0,0 +1,307 @@
1
+ // Buffer-drain handover protocol: a live buffer owner (the daemon) must hand
2
+ // a claimed session's turns INTO the drain claim instead of writing a session
3
+ // file, so the out-of-process `remnic buffer drain` can extract them without
4
+ // the daemon resurrecting or losing content. Covers:
5
+ // 1. persist path: claimed key saves hand over + evict the in-memory entry,
6
+ // merging the owner's newest turns into the claim (identity-deduped).
7
+ // 2. no resurrection: after handover the session file stays gone.
8
+ // 3. extraction gate: a claimed session's queued extraction settles skipped
9
+ // (claimed_by_buffer_drain) without touching the LLM; turns stay in the
10
+ // claim for the drain worker.
11
+ // 4. default-key mirror: handing over "default" clears the legacy mirror.
12
+ import assert from "node:assert/strict";
13
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "node:fs";
14
+ import os from "node:os";
15
+ import { hostname } from "node:os";
16
+ import path from "node:path";
17
+ import test from "node:test";
18
+
19
+ import { SmartBuffer } from "./buffer.js";
20
+ import { BufferSessionFiles } from "./storage/buffer-session-files.js";
21
+ import { BufferDrainClaims, type DrainLease } from "./storage/buffer-drain-claims.js";
22
+ import type { BufferStorage } from "./buffer-persistence.js";
23
+ import type { BufferState } from "./types.js";
24
+ import { TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
25
+ import type { BufferEntryState, BufferTurn, PluginConfig } from "./types.js";
26
+ import { parseConfig } from "./config.js";
27
+ import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
28
+
29
+ function plainIo() {
30
+ return {
31
+ read: async (filePath: string) => readFileSync(filePath, "utf8"),
32
+ write: async (filePath: string, content: string) => {
33
+ mkdirSync(path.dirname(filePath), { recursive: true });
34
+ writeFileSync(filePath, content);
35
+ },
36
+ };
37
+ }
38
+
39
+ function turn(content: string, sessionKey = "sess-1", timestamp = "2026-11-01T00:00:00.000Z"): BufferTurn {
40
+ return { role: "user", content, timestamp, sessionKey };
41
+ }
42
+
43
+ function entry(turns: ReturnType<typeof turn>[]): BufferEntryState {
44
+ return { turns, lastExtractionAt: null, extractionCount: 0 };
45
+ }
46
+
47
+ function makeConfig(): PluginConfig {
48
+ return parseConfig({
49
+ bufferSaveDebounceMs: 0,
50
+ triggerMode: "smart",
51
+ bufferMaxTurns: 1000,
52
+ bufferMaxMinutes: 100000,
53
+ highSignalPatterns: [],
54
+ extractionMinUserTurns: 1,
55
+ extractionMinChars: 0,
56
+ });
57
+ }
58
+
59
+ interface Harness {
60
+ stateDir: string;
61
+ buffer: SmartBuffer;
62
+ claims: BufferDrainClaims;
63
+ storage: BufferStorage;
64
+ lease: DrainLease;
65
+ cleanup(): void;
66
+ }
67
+
68
+ /** A SmartBuffer wired exactly like the daemon wires it: storage methods backed by BufferSessionFiles. */
69
+ function makeHarness(): Harness {
70
+ const stateDir = mkdtempSync(path.join(os.tmpdir(), "remnic-handover-"));
71
+ const files = new BufferSessionFiles(stateDir, plainIo());
72
+ const claims = new BufferDrainClaims(stateDir, plainIo());
73
+ const storage: BufferStorage = {
74
+ loadBuffer: () => files.load(),
75
+ saveBuffer: (state: BufferState) => files.reconcile(state),
76
+ saveBufferSession: (key: string, entryState: BufferEntryState) => files.save(key, entryState),
77
+ deleteBufferSession: (key: string) => files.remove(key),
78
+ isBufferSessionClaimed: (key: string) => files.isClaimed(key),
79
+ };
80
+ const buffer = new SmartBuffer(makeConfig(), storage);
81
+ const now = new Date().toISOString();
82
+ return {
83
+ stateDir,
84
+ buffer,
85
+ claims,
86
+ storage,
87
+ lease: { ownerId: "worker-1", pid: process.pid, host: hostname(), acquiredAt: now, heartbeatAt: now, leaseMs: 60_000 },
88
+ cleanup: () => rmSync(stateDir, { recursive: true, force: true }),
89
+ };
90
+ }
91
+
92
+ test("a claimed session's save hands over and evicts instead of writing a session file", async () => {
93
+ const h = makeHarness();
94
+ try {
95
+ await h.buffer.load();
96
+ await h.buffer.addTurn("sess-1", turn("old-1"));
97
+ await h.buffer.addTurn("sess-1", turn("old-2"));
98
+ assert.ok(readdirSync(path.join(h.stateDir, "buffer")).some((name) => name.endsWith(".json")), "seeded turns persisted as a session file");
99
+
100
+ const outcome = await h.claims.claimFromSessionFile("sess-1", h.lease);
101
+ assert.equal(outcome, "claimed");
102
+
103
+ // The daemon owner buffers one more turn for the claimed session and its
104
+ // debounced-off save fires: this is the resurrection path, and it must
105
+ // hand over instead.
106
+ await h.buffer.addTurn("sess-1", turn("new-3"));
107
+ await h.buffer.save();
108
+
109
+ const claim = await h.claims.load("sess-1");
110
+ assert.ok(claim, "claim must exist after handover");
111
+ assert.deepEqual(
112
+ claim.entry.turns.map((t) => t.content),
113
+ ["old-1", "old-2", "new-3"],
114
+ "the owner's newest turn merges into the claim; nothing duplicates",
115
+ );
116
+ const topLevel = readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json"));
117
+ assert.deepEqual(topLevel, [], "a claimed session must never be written back as a session file");
118
+
119
+ // The in-memory entry was evicted with the handover: the next observe
120
+ // starts fresh (its turns flow into the claim at the next handover).
121
+ await h.buffer.addTurn("sess-1", turn("post-eviction", "sess-1", "2026-11-01T00:05:00.000Z"));
122
+ await h.buffer.save();
123
+ const claimAfter = await h.claims.load("sess-1");
124
+ assert.ok(claimAfter);
125
+ assert.deepEqual(
126
+ claimAfter.entry.turns.map((t) => t.content),
127
+ ["old-1", "old-2", "new-3", "post-eviction"],
128
+ "post-handover observes keep flowing into the claim",
129
+ );
130
+ assert.deepEqual(
131
+ readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json")),
132
+ [],
133
+ );
134
+ } finally {
135
+ h.cleanup();
136
+ }
137
+ });
138
+
139
+ test("handover of the default key clears the legacy top-level mirror", async () => {
140
+ const h = makeHarness();
141
+ try {
142
+ await h.buffer.load();
143
+ await h.buffer.addTurn("default", turn("legacy", "legacy-session"));
144
+ const outcome = await h.claims.claimFromSessionFile("default", h.lease);
145
+ assert.equal(outcome, "claimed");
146
+ await h.buffer.addTurn("default", turn("legacy-2", "legacy-session"));
147
+ await h.buffer.save();
148
+ const claim = await h.claims.load("default");
149
+ assert.ok(claim);
150
+ assert.deepEqual(claim.entry.turns.map((t) => t.content), ["legacy", "legacy-2"]);
151
+ // No duplicate writes: the snapshot must settle empty after handover.
152
+ assert.deepEqual(
153
+ readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json")),
154
+ [],
155
+ );
156
+ } finally {
157
+ h.cleanup();
158
+ }
159
+ });
160
+
161
+ /**
162
+ * Narrow a test double to the dependency contract. The fixture provides the
163
+ * members the drain path reads; the rest are unused by that path.
164
+ */
165
+ /** The dependency members the drain path reads. */
166
+ interface DrainDepsFixture {
167
+ buffer: unknown;
168
+ config: unknown;
169
+ extractionQueueCoordinator: unknown;
170
+ extractionWindowBudgetChars: unknown;
171
+ getStorage: unknown;
172
+ shouldQueueExtraction: unknown;
173
+ runExtraction: unknown;
174
+ }
175
+
176
+ function asDeps<T extends object>(value: DrainDepsFixture): T {
177
+ // SAFETY: the fixture provides the members the drain path reads; the
178
+ // remaining contract members are unused by that path.
179
+ return value as T;
180
+ }
181
+
182
+ function gateFixture(isClaimed: boolean) {
183
+ const runs: { turns: BufferTurn[] }[] = [];
184
+ const config = {
185
+ extractionChunkDropAfterFailures: 3,
186
+ extractionMinUserTurns: 1,
187
+ extractionMinChars: 0,
188
+ extractionMaxTurnChars: 100_000,
189
+ };
190
+ const deps = {
191
+ buffer: {
192
+ isSessionClaimed: () => isClaimed,
193
+ getTurns: () => [],
194
+ dropExtractionChunk: async () => 0,
195
+ },
196
+ config,
197
+ extractionQueueCoordinator: {
198
+ enqueue: (fn: () => Promise<void>) => {
199
+ void fn().catch(() => {});
200
+ return true;
201
+ },
202
+ runPreemptable: (_preemptable: boolean, parent: AbortSignal | undefined, fn: (signal: AbortSignal) => Promise<void>) => fn(parent ?? new AbortController().signal),
203
+ beginPreemptable: () => {},
204
+ },
205
+ extractionWindowBudgetChars: () => 500,
206
+ getStorage: async () => ({ saveDroppedExtractionChunk: async () => "/dropped" }),
207
+ shouldQueueExtraction: () => true,
208
+ runExtraction: (async (turns: BufferTurn[]) => {
209
+ runs.push({ turns });
210
+ return { status: "completed", persistedCount: 0, durableOutputCount: 0 };
211
+ }),
212
+ // SAFETY: the fixture provides exactly the deps the coordinator reads
213
+ // for the drain path (buffer, queue coordinator, budget, storage, dedupe
214
+ // gate and runExtraction); the cast only fills the unused members.
215
+ };
216
+ const coordinator = new TurnIngestionCoordinator(asDeps<TurnIngestionDeps>(deps));
217
+ return { coordinator, runs };
218
+ }
219
+
220
+ test("a claimed session's queued extraction settles skipped without an LLM call", async () => {
221
+ const claimed = gateFixture(true);
222
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
223
+ void claimed.coordinator.queueBufferedExtraction([turn("blocked")], "trigger_mode", {
224
+ bufferKey: "sess-1",
225
+ clearBufferAfterExtraction: true,
226
+ onTaskSettled: (error, result) => {
227
+ try {
228
+ assert.equal(error, undefined);
229
+ assert.equal(result?.status, "skipped");
230
+ assert.equal(result?.reason, "claimed_by_buffer_drain");
231
+ assert.equal(claimed.runs.length, 0, "the LLM must not run for a claimed session");
232
+ resolve();
233
+ } catch (err) {
234
+ reject(err instanceof Error ? err : new Error(String(err)));
235
+ }
236
+ },
237
+ });
238
+ await promise;
239
+ });
240
+
241
+ test("an unclaimed session still extracts normally (gate is claim-only)", async () => {
242
+ const unclaimed = gateFixture(false);
243
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
244
+ void unclaimed.coordinator.queueBufferedExtraction([turn("flows")], "trigger_mode", {
245
+ bufferKey: "sess-2",
246
+ clearBufferAfterExtraction: true,
247
+ onTaskSettled: (error, result) => {
248
+ try {
249
+ assert.equal(error, undefined);
250
+ assert.notEqual(result?.reason, "claimed_by_buffer_drain");
251
+ assert.equal(unclaimed.runs.length, 1);
252
+ resolve();
253
+ } catch (err) {
254
+ reject(err instanceof Error ? err : new Error(String(err)));
255
+ }
256
+ },
257
+ });
258
+ await promise;
259
+ });
260
+
261
+ test("the owner keeps the claim while a drain is still running on it", async () => {
262
+ const h = makeHarness();
263
+ try {
264
+ await h.buffer.load();
265
+ await h.buffer.addTurn("sess-1", turn("old-1"));
266
+ await h.buffer.addTurn("sess-1", turn("old-2"));
267
+ const claimOutcome = await h.claims.claimFromSessionFile("sess-1", h.lease);
268
+ assert.equal(claimOutcome, "claimed");
269
+
270
+ // The drainer extracts old-1 (progress write moves it to `drained`) but
271
+ // old-2 is still pending, and the drainer holds the lease.
272
+ const loaded = await h.claims.load("sess-1");
273
+ assert.ok(loaded);
274
+ await h.claims.writeEntry("sess-1", h.lease.ownerId, entry([turn("old-2")]), loaded.entry);
275
+
276
+ // The owner saves while the drain is mid-flight: this must hand over into
277
+ // the claim (keeping the claim AND the lease), not consume the tombstone.
278
+ await h.buffer.addTurn("sess-1", turn("new-3"));
279
+ await h.buffer.save();
280
+ const saveSession = h.storage.saveBufferSession;
281
+ assert.ok(saveSession, "the harness wires saveBufferSession");
282
+ const saveOutcome = await saveSession("sess-1", {
283
+ turns: [turn("old-1"), turn("old-2"), turn("new-3")],
284
+ lastExtractionAt: null,
285
+ extractionCount: 0,
286
+ });
287
+ assert.equal(saveOutcome?.status, "handed_over", "a running drain keeps its claim");
288
+ assert.equal(await h.claims.load("sess-1") !== null, true, "the claim survives the owner's mid-drain save");
289
+ assert.equal(existsSync(h.claims.leasePathForTest("sess-1")), true, "the lease survives the owner's mid-drain save");
290
+
291
+ // The drain finishes: everything extracted, the drainer acks (drops its
292
+ // lease, leaving the tombstone). Only now does the owner's save consume it.
293
+ const current = await h.claims.load("sess-1");
294
+ assert.ok(current);
295
+ await h.claims.writeEntry("sess-1", h.lease.ownerId, entry([]), current.entry);
296
+ await h.claims.releaseLease("sess-1", h.lease.ownerId);
297
+ const finalOutcome = await saveSession("sess-1", {
298
+ turns: [turn("old-1"), turn("old-2"), turn("new-3")],
299
+ lastExtractionAt: null,
300
+ extractionCount: 0,
301
+ });
302
+ assert.equal(finalOutcome?.status, "saved", "the acked tombstone is consumed");
303
+ assert.equal(await h.claims.load("sess-1"), null, "the tombstone is gone after the consume");
304
+ } finally {
305
+ h.cleanup();
306
+ }
307
+ });
@@ -1,4 +1,5 @@
1
- import type { BufferEntryState, BufferState, BufferSurpriseEvent } from "./types.js";
1
+ import type { BufferEntryState, BufferState, BufferSurpriseEvent, BufferTurn } from "./types.js";
2
+ import type { BufferSessionSaveOutcome } from "./storage/buffer-drain-claims.js";
2
3
 
3
4
  /**
4
5
  * The buffer persistence surface SmartBuffer depends on.
@@ -12,12 +13,23 @@ import type { BufferEntryState, BufferState, BufferSurpriseEvent } from "./types
12
13
  export interface BufferStorage {
13
14
  loadBuffer(): Promise<BufferState>;
14
15
  saveBuffer(state: BufferState): Promise<void>;
15
- saveBufferSession?(key: string, entry: BufferEntryState): Promise<void>;
16
+ /** Reports "handed_over" when the session is claimed by `remnic buffer drain` and the entry moved into the claim instead of a session file. Legacy doubles may return void (treated as "saved"). */
17
+ saveBufferSession?(key: string, entry: BufferEntryState): Promise<BufferSessionSaveOutcome | void>;
16
18
  deleteBufferSession?(key: string): Promise<void>;
19
+ /** Sync claim check for the extraction gate; omitted when the backend has no drain-claim store. */
20
+ isBufferSessionClaimed?(key: string): boolean;
17
21
  /** Optional surprise-telemetry sink; older storage and test doubles omit it. */
18
22
  appendBufferSurpriseEvents?(events: BufferSurpriseEvent[]): Promise<number>;
19
23
  }
20
24
 
25
+ /** What one persist pass changed beyond plain writes. */
26
+ export interface BufferPersistOutcome {
27
+ /** Sessions whose in-memory entry moved into a drain claim: the caller must evict them (a claimed session no longer lives in this process). */
28
+ handedOver: string[];
29
+ /** Per session, the turns a drain already extracted: the caller subtracts them from its in-memory copy. */
30
+ drainedTurns: Map<string, BufferTurn[]>;
31
+ }
32
+
21
33
  /**
22
34
  * Tracks which sessions' persisted files are stale and writes only those.
23
35
  *
@@ -61,15 +73,17 @@ export class BufferSessionPersistence {
61
73
  storage: BufferStorage,
62
74
  state: BufferState,
63
75
  peekEntry: (key: string) => BufferEntryState | undefined,
64
- ): Promise<void> {
76
+ ): Promise<BufferPersistOutcome> {
65
77
  const saveSession = storage.saveBufferSession?.bind(storage);
66
78
  const deleteSession = storage.deleteBufferSession?.bind(storage);
67
79
  if (!saveSession || !deleteSession) {
68
80
  await storage.saveBuffer(state);
69
81
  this.dirty.clear();
70
82
  this.deleted.clear();
71
- return;
83
+ return { handedOver: [], drainedTurns: new Map() };
72
84
  }
85
+ const handedOver: string[] = [];
86
+ const drainedTurns = new Map<string, BufferTurn[]>();
73
87
  for (const key of [...this.deleted]) {
74
88
  await deleteSession(key);
75
89
  this.deleted.delete(key);
@@ -82,8 +96,16 @@ export class BufferSessionPersistence {
82
96
  this.markDeleted(key);
83
97
  continue;
84
98
  }
85
- await saveSession(key, entry);
99
+ const outcome = await saveSession(key, entry);
86
100
  this.dirty.delete(key);
101
+ // "handed_over": the entry moved into a drain claim. Content is durable
102
+ // in the claim, so this key's pending state is settled — the caller
103
+ // evicts the in-memory entry on its side.
104
+ if (outcome?.status === "handed_over") handedOver.push(key);
105
+ // A drain tombstone: the turns it extracted must leave this process's
106
+ // copy too, or the owner would extract them a second time.
107
+ if (outcome?.drainedTurns && outcome.drainedTurns.length > 0) drainedTurns.set(key, outcome.drainedTurns);
87
108
  }
109
+ return { handedOver, drainedTurns };
88
110
  }
89
111
  }
@@ -104,6 +104,22 @@ export function copyBufferTurn(turn: BufferTurn): BufferTurn {
104
104
  return copy;
105
105
  }
106
106
 
107
+ /**
108
+ * Remove the turns that a drain already extracted (`drained`) from `turns`.
109
+ * Identity-based (role/content/timestamp/session/...): the same comparison
110
+ * the buffer uses to match live turns, so a stale owner can subtract exactly
111
+ * what the drain took and keep everything else.
112
+ */
113
+ export function subtractBufferTurns(turns: readonly BufferTurn[], drained: readonly BufferTurn[]): BufferTurn[] {
114
+ if (drained.length === 0) return [...turns];
115
+ const kept = [...turns];
116
+ for (const gone of drained) {
117
+ const at = kept.findIndex((live) => bufferTurnsEqual(live, gone));
118
+ if (at >= 0) kept.splice(at, 1);
119
+ }
120
+ return kept;
121
+ }
122
+
107
123
  export function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn): boolean {
108
124
  if (!left) return false;
109
125
  return (