@remnic/core 10.0.76 → 10.0.78

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 (249) hide show
  1. package/dist/access-admin-ops-surface.js +27 -27
  2. package/dist/access-authorization-probe.js +28 -28
  3. package/dist/access-boundary.js +27 -27
  4. package/dist/access-cli.js +78 -77
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +27 -27
  7. package/dist/access-http-briefing.js +28 -28
  8. package/dist/access-http-query.js +29 -29
  9. package/dist/access-http.js +45 -45
  10. package/dist/access-identity-continuity-surface.js +27 -27
  11. package/dist/access-lcm-surface.js +27 -27
  12. package/dist/access-mcp.js +37 -37
  13. package/dist/access-namespace-preflight.js +27 -27
  14. package/dist/access-observe-helpers.d.ts.map +1 -1
  15. package/dist/access-observe-helpers.js +1 -1
  16. package/dist/access-observe-write-category.js +5 -5
  17. package/dist/access-observe-write-surface.js +27 -27
  18. package/dist/access-operations-batch.js +29 -29
  19. package/dist/access-operations.js +36 -36
  20. package/dist/access-recall-concurrency.js +27 -27
  21. package/dist/access-recall-response.js +27 -27
  22. package/dist/access-recall-surface.js +27 -27
  23. package/dist/access-schema.js +4 -4
  24. package/dist/access-service.js +27 -27
  25. package/dist/access-surface-catalog.js +29 -29
  26. package/dist/active-recall.js +7 -7
  27. package/dist/briefing.js +8 -8
  28. package/dist/calibration.js +1 -1
  29. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  30. package/dist/causal-consolidation.js +9 -9
  31. package/dist/{chunk-Z3CKK5RR.js → chunk-2XG7QECM.js} +3 -3
  32. package/dist/{chunk-BZTA7DR4.js → chunk-35RSL5B4.js} +3 -3
  33. package/dist/{chunk-3GGCMRO7.js → chunk-3RYP4OTZ.js} +2 -2
  34. package/dist/{chunk-VEQVXNWR.js → chunk-446H5UUO.js} +4 -4
  35. package/dist/{chunk-XC7OYOQC.js → chunk-4BCX6YLW.js} +2 -2
  36. package/dist/{chunk-RXGFITWS.js → chunk-4HLPFYF2.js} +7 -7
  37. package/dist/{chunk-WIAXVBQH.js → chunk-65SKM4BF.js} +2 -2
  38. package/dist/{chunk-VEWTHVX2.js → chunk-6HERQLF4.js} +2 -2
  39. package/dist/{chunk-4DLZ2ITE.js → chunk-6OGFQ5GH.js} +2 -2
  40. package/dist/{chunk-C44DRDZM.js → chunk-6WULHFNJ.js} +2 -2
  41. package/dist/{chunk-IVXOYE43.js → chunk-7IQ5HYBJ.js} +2 -2
  42. package/dist/{chunk-SZBZOUHR.js → chunk-A45ZOJV4.js} +2 -2
  43. package/dist/chunk-ADJXJKU6.js +74 -0
  44. package/dist/chunk-ADJXJKU6.js.map +1 -0
  45. package/dist/{chunk-DANFOVQG.js → chunk-B7PLF3HW.js} +2 -2
  46. package/dist/{chunk-PBE7NFB5.js → chunk-BNQJKWWZ.js} +4 -4
  47. package/dist/{chunk-AY2UK5XK.js → chunk-BSBGI4BD.js} +2 -2
  48. package/dist/{chunk-AY2UK5XK.js.map → chunk-BSBGI4BD.js.map} +1 -1
  49. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  50. package/dist/{chunk-IPHIIRDN.js → chunk-CYTTY4M2.js} +5 -7
  51. package/dist/chunk-CYTTY4M2.js.map +1 -0
  52. package/dist/{chunk-R24RYSQX.js → chunk-CZD654DQ.js} +22 -22
  53. package/dist/{chunk-MBENRPMY.js → chunk-D3WNJF7D.js} +12 -12
  54. package/dist/{chunk-5IQL4N37.js → chunk-D4WBMLPD.js} +2 -2
  55. package/dist/{chunk-FYQUUIPI.js → chunk-DLZGSZVC.js} +4 -4
  56. package/dist/{chunk-YK6SFDP2.js → chunk-DOJBV6WR.js} +4 -4
  57. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  58. package/dist/{chunk-FETPTCQZ.js → chunk-E33UJHY3.js} +2 -2
  59. package/dist/{chunk-LJUQPT5G.js → chunk-EFRMEOZQ.js} +3 -3
  60. package/dist/{chunk-HTDXNIO6.js → chunk-HK3CUTAG.js} +2 -2
  61. package/dist/{chunk-FGUWCE5I.js → chunk-HUF4S4MY.js} +2 -2
  62. package/dist/{chunk-6Q6AQ36B.js → chunk-HWHNQVRV.js} +2 -2
  63. package/dist/{chunk-SYDPJUFR.js → chunk-IDGCL5OP.js} +2 -2
  64. package/dist/{chunk-F3FNXE7C.js → chunk-JHJP2IAU.js} +3 -3
  65. package/dist/{chunk-IBQ7LGUJ.js → chunk-JIGHVQF6.js} +6 -6
  66. package/dist/{chunk-P7HNMNN2.js → chunk-K4SVTZWP.js} +7 -7
  67. package/dist/{chunk-KAED7IBB.js → chunk-KDWO22ZK.js} +2 -2
  68. package/dist/{chunk-N6GEGRCT.js → chunk-KYVBGDGA.js} +2 -2
  69. package/dist/{chunk-HB5TNQMM.js → chunk-L7BOS7KV.js} +2 -2
  70. package/dist/{chunk-3MBSD53O.js → chunk-LE4MQ2WF.js} +2 -2
  71. package/dist/{chunk-I4LJJ44E.js → chunk-LNHBYT3F.js} +2 -2
  72. package/dist/{chunk-SHVLNI4L.js → chunk-LU66I56Q.js} +2 -2
  73. package/dist/{chunk-TD5VTVPT.js → chunk-N2H6VC65.js} +3 -3
  74. package/dist/{chunk-L4QYW2HO.js → chunk-NBFHOQIO.js} +2 -2
  75. package/dist/{chunk-KNZDGZSY.js → chunk-NVYY2TJG.js} +2 -2
  76. package/dist/{chunk-7FFGWIBN.js → chunk-PCSQRQPJ.js} +2 -2
  77. package/dist/{chunk-P3AQ2YSX.js → chunk-PFUXPTBT.js} +4 -4
  78. package/dist/{chunk-KQAFEZQX.js → chunk-R45L4TV6.js} +6 -6
  79. package/dist/{chunk-E22CQZ3H.js → chunk-R63X5BQA.js} +12 -12
  80. package/dist/{chunk-PLGBPFEO.js → chunk-RF7QJ736.js} +2 -2
  81. package/dist/{chunk-ZZSFRWKD.js → chunk-RIBCP4DJ.js} +2 -2
  82. package/dist/{chunk-HDN7IVIO.js → chunk-RIZ5NUNN.js} +16 -62
  83. package/dist/chunk-RIZ5NUNN.js.map +1 -0
  84. package/dist/{chunk-HR6NTVYZ.js → chunk-S34XZUTY.js} +172 -39
  85. package/dist/chunk-S34XZUTY.js.map +1 -0
  86. package/dist/{chunk-DEGE32C3.js → chunk-SLORPQZT.js} +2 -2
  87. package/dist/{chunk-6RPPZL7W.js → chunk-SMSJOWVE.js} +2 -2
  88. package/dist/{chunk-X53HJENJ.js → chunk-SPYFXSFZ.js} +2 -2
  89. package/dist/{chunk-IRFQKR4Y.js → chunk-SW6OAWK7.js} +2 -2
  90. package/dist/{chunk-VPEMGBTI.js → chunk-SZEPOUW2.js} +2 -2
  91. package/dist/{chunk-COIQ5N4J.js → chunk-TSITK6QK.js} +2 -2
  92. package/dist/{chunk-HCFNZWFB.js → chunk-VBNXDC7P.js} +2 -2
  93. package/dist/{chunk-FP2AT4WL.js → chunk-VJ3HFCG2.js} +2 -2
  94. package/dist/{chunk-OBQ2NZAR.js → chunk-VKDAJJQP.js} +4 -4
  95. package/dist/{chunk-HZNURRDS.js → chunk-VUROWXFL.js} +2 -2
  96. package/dist/{chunk-G5NH3SSX.js → chunk-X3KFSJLQ.js} +2 -2
  97. package/dist/{chunk-GCJA5KKT.js → chunk-XBNO56HC.js} +2 -2
  98. package/dist/{chunk-GWOFCHPN.js → chunk-YGECOOTF.js} +2 -2
  99. package/dist/{chunk-2R7OCBRR.js → chunk-YK7GOPIE.js} +2 -2
  100. package/dist/{chunk-ELUG6NKQ.js → chunk-YSA4CYTK.js} +2 -2
  101. package/dist/{chunk-233S3MXC.js → chunk-YXUNSOHD.js} +3 -3
  102. package/dist/{chunk-SY4AFWUZ.js → chunk-Z3O7ZOCC.js} +30 -30
  103. package/dist/{chunk-ZG57BEUS.js → chunk-ZAU2OPDW.js} +2 -2
  104. package/dist/cli.js +63 -62
  105. package/dist/coding/export-okf-codegraph.js +7 -7
  106. package/dist/compounding/engine.js +9 -9
  107. package/dist/config.js +7 -7
  108. package/dist/connectors/codex-materialize-runner.js +8 -8
  109. package/dist/connectors/index.js +8 -8
  110. package/dist/convergence-refresh.js +10 -10
  111. package/dist/corpus-watermark.js +10 -10
  112. package/dist/deep-recall-http-glue.js +5 -5
  113. package/dist/entity-retrieval.js +8 -8
  114. package/dist/external-wiki-access.js +28 -28
  115. package/dist/extraction.js +10 -10
  116. package/dist/harmonic-retrieval.js +8 -8
  117. package/dist/index.js +86 -85
  118. package/dist/lcm/engine.js +2 -2
  119. package/dist/lcm/index.js +5 -5
  120. package/dist/local-llm.js +1 -1
  121. package/dist/location/index.js +10 -10
  122. package/dist/maintenance/memory-governance.js +8 -8
  123. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +8 -8
  124. package/dist/maintenance/rebuild-memory-projection-runner.js +10 -10
  125. package/dist/maintenance/rebuild-memory-projection.js +9 -9
  126. package/dist/memory-browse-operations.js +28 -28
  127. package/dist/namespaces/migrate.js +9 -9
  128. package/dist/namespaces/search.js +6 -6
  129. package/dist/namespaces/storage.js +8 -8
  130. package/dist/operator-doctor-corpus.js +11 -11
  131. package/dist/operator-toolkit.js +16 -16
  132. package/dist/orchestration/backlog-scheduler.js +79 -78
  133. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  134. package/dist/orchestration/buffer-drain-runner.js +79 -78
  135. package/dist/orchestration/extraction-queue-snapshot-sharing.d.ts +113 -0
  136. package/dist/orchestration/extraction-queue-snapshot-sharing.d.ts.map +1 -0
  137. package/dist/orchestration/maintenance.js +19 -19
  138. package/dist/orchestration/turn-ingestion.d.ts +3 -0
  139. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  140. package/dist/orchestrator.js +78 -77
  141. package/dist/recall-navigation-operations.js +28 -28
  142. package/dist/recall-navigation.js +27 -27
  143. package/dist/resume-bundles.js +10 -9
  144. package/dist/search/factory.js +5 -5
  145. package/dist/search/index.js +5 -5
  146. package/dist/search/lancedb-backend.js +2 -2
  147. package/dist/search/meilisearch-backend.js +2 -2
  148. package/dist/search/orama-backend.js +2 -2
  149. package/dist/semantic-consolidation.js +9 -9
  150. package/dist/semantic-rule-promotion.js +8 -8
  151. package/dist/semantic-rule-verifier.js +8 -8
  152. package/dist/source-agent-qualifier.js +8 -8
  153. package/dist/standup/build.js +8 -8
  154. package/dist/storage.js +7 -7
  155. package/dist/summarizer.js +2 -2
  156. package/dist/support-passport/index.js +30 -30
  157. package/dist/transcript-ownership-digest.d.ts +31 -0
  158. package/dist/transcript-ownership-digest.d.ts.map +1 -0
  159. package/dist/transcript-ownership-digest.js +10 -0
  160. package/dist/transcript-ownership-digest.js.map +1 -0
  161. package/dist/transcript.d.ts +2 -5
  162. package/dist/transcript.d.ts.map +1 -1
  163. package/dist/transcript.js +2 -1
  164. package/dist/transfer/autodetect.js +1 -1
  165. package/dist/transfer/backup.js +2 -2
  166. package/dist/transfer/capsule-export.js +3 -3
  167. package/dist/transfer/capsule-import.js +2 -2
  168. package/dist/transfer/export-okf.js +8 -8
  169. package/dist/verified-recall.js +8 -8
  170. package/dist/who-knows-cli.js +9 -9
  171. package/dist/who-knows.js +8 -8
  172. package/package.json +2 -2
  173. package/src/access-observe-helpers.test.ts +156 -0
  174. package/src/access-observe-helpers.ts +6 -6
  175. package/src/local-llm-qos.test.ts +59 -0
  176. package/src/local-llm.ts +1 -1
  177. package/src/orchestration/extraction-queue-bounded-retention.test.ts +548 -0
  178. package/src/orchestration/extraction-queue-snapshot-sharing.ts +257 -0
  179. package/src/orchestration/turn-ingestion.ts +19 -6
  180. package/src/transcript-ownership-digest.test.ts +403 -0
  181. package/src/transcript-ownership-digest.ts +99 -0
  182. package/src/transcript.ts +14 -76
  183. package/dist/chunk-HDN7IVIO.js.map +0 -1
  184. package/dist/chunk-HR6NTVYZ.js.map +0 -1
  185. package/dist/chunk-IPHIIRDN.js.map +0 -1
  186. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  187. /package/dist/{chunk-Z3CKK5RR.js.map → chunk-2XG7QECM.js.map} +0 -0
  188. /package/dist/{chunk-BZTA7DR4.js.map → chunk-35RSL5B4.js.map} +0 -0
  189. /package/dist/{chunk-3GGCMRO7.js.map → chunk-3RYP4OTZ.js.map} +0 -0
  190. /package/dist/{chunk-VEQVXNWR.js.map → chunk-446H5UUO.js.map} +0 -0
  191. /package/dist/{chunk-XC7OYOQC.js.map → chunk-4BCX6YLW.js.map} +0 -0
  192. /package/dist/{chunk-RXGFITWS.js.map → chunk-4HLPFYF2.js.map} +0 -0
  193. /package/dist/{chunk-WIAXVBQH.js.map → chunk-65SKM4BF.js.map} +0 -0
  194. /package/dist/{chunk-VEWTHVX2.js.map → chunk-6HERQLF4.js.map} +0 -0
  195. /package/dist/{chunk-4DLZ2ITE.js.map → chunk-6OGFQ5GH.js.map} +0 -0
  196. /package/dist/{chunk-C44DRDZM.js.map → chunk-6WULHFNJ.js.map} +0 -0
  197. /package/dist/{chunk-IVXOYE43.js.map → chunk-7IQ5HYBJ.js.map} +0 -0
  198. /package/dist/{chunk-SZBZOUHR.js.map → chunk-A45ZOJV4.js.map} +0 -0
  199. /package/dist/{chunk-DANFOVQG.js.map → chunk-B7PLF3HW.js.map} +0 -0
  200. /package/dist/{chunk-PBE7NFB5.js.map → chunk-BNQJKWWZ.js.map} +0 -0
  201. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  202. /package/dist/{chunk-R24RYSQX.js.map → chunk-CZD654DQ.js.map} +0 -0
  203. /package/dist/{chunk-MBENRPMY.js.map → chunk-D3WNJF7D.js.map} +0 -0
  204. /package/dist/{chunk-5IQL4N37.js.map → chunk-D4WBMLPD.js.map} +0 -0
  205. /package/dist/{chunk-FYQUUIPI.js.map → chunk-DLZGSZVC.js.map} +0 -0
  206. /package/dist/{chunk-YK6SFDP2.js.map → chunk-DOJBV6WR.js.map} +0 -0
  207. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  208. /package/dist/{chunk-FETPTCQZ.js.map → chunk-E33UJHY3.js.map} +0 -0
  209. /package/dist/{chunk-LJUQPT5G.js.map → chunk-EFRMEOZQ.js.map} +0 -0
  210. /package/dist/{chunk-HTDXNIO6.js.map → chunk-HK3CUTAG.js.map} +0 -0
  211. /package/dist/{chunk-FGUWCE5I.js.map → chunk-HUF4S4MY.js.map} +0 -0
  212. /package/dist/{chunk-6Q6AQ36B.js.map → chunk-HWHNQVRV.js.map} +0 -0
  213. /package/dist/{chunk-SYDPJUFR.js.map → chunk-IDGCL5OP.js.map} +0 -0
  214. /package/dist/{chunk-F3FNXE7C.js.map → chunk-JHJP2IAU.js.map} +0 -0
  215. /package/dist/{chunk-IBQ7LGUJ.js.map → chunk-JIGHVQF6.js.map} +0 -0
  216. /package/dist/{chunk-P7HNMNN2.js.map → chunk-K4SVTZWP.js.map} +0 -0
  217. /package/dist/{chunk-KAED7IBB.js.map → chunk-KDWO22ZK.js.map} +0 -0
  218. /package/dist/{chunk-N6GEGRCT.js.map → chunk-KYVBGDGA.js.map} +0 -0
  219. /package/dist/{chunk-HB5TNQMM.js.map → chunk-L7BOS7KV.js.map} +0 -0
  220. /package/dist/{chunk-3MBSD53O.js.map → chunk-LE4MQ2WF.js.map} +0 -0
  221. /package/dist/{chunk-I4LJJ44E.js.map → chunk-LNHBYT3F.js.map} +0 -0
  222. /package/dist/{chunk-SHVLNI4L.js.map → chunk-LU66I56Q.js.map} +0 -0
  223. /package/dist/{chunk-TD5VTVPT.js.map → chunk-N2H6VC65.js.map} +0 -0
  224. /package/dist/{chunk-L4QYW2HO.js.map → chunk-NBFHOQIO.js.map} +0 -0
  225. /package/dist/{chunk-KNZDGZSY.js.map → chunk-NVYY2TJG.js.map} +0 -0
  226. /package/dist/{chunk-7FFGWIBN.js.map → chunk-PCSQRQPJ.js.map} +0 -0
  227. /package/dist/{chunk-P3AQ2YSX.js.map → chunk-PFUXPTBT.js.map} +0 -0
  228. /package/dist/{chunk-KQAFEZQX.js.map → chunk-R45L4TV6.js.map} +0 -0
  229. /package/dist/{chunk-E22CQZ3H.js.map → chunk-R63X5BQA.js.map} +0 -0
  230. /package/dist/{chunk-PLGBPFEO.js.map → chunk-RF7QJ736.js.map} +0 -0
  231. /package/dist/{chunk-ZZSFRWKD.js.map → chunk-RIBCP4DJ.js.map} +0 -0
  232. /package/dist/{chunk-DEGE32C3.js.map → chunk-SLORPQZT.js.map} +0 -0
  233. /package/dist/{chunk-6RPPZL7W.js.map → chunk-SMSJOWVE.js.map} +0 -0
  234. /package/dist/{chunk-X53HJENJ.js.map → chunk-SPYFXSFZ.js.map} +0 -0
  235. /package/dist/{chunk-IRFQKR4Y.js.map → chunk-SW6OAWK7.js.map} +0 -0
  236. /package/dist/{chunk-VPEMGBTI.js.map → chunk-SZEPOUW2.js.map} +0 -0
  237. /package/dist/{chunk-COIQ5N4J.js.map → chunk-TSITK6QK.js.map} +0 -0
  238. /package/dist/{chunk-HCFNZWFB.js.map → chunk-VBNXDC7P.js.map} +0 -0
  239. /package/dist/{chunk-FP2AT4WL.js.map → chunk-VJ3HFCG2.js.map} +0 -0
  240. /package/dist/{chunk-OBQ2NZAR.js.map → chunk-VKDAJJQP.js.map} +0 -0
  241. /package/dist/{chunk-HZNURRDS.js.map → chunk-VUROWXFL.js.map} +0 -0
  242. /package/dist/{chunk-G5NH3SSX.js.map → chunk-X3KFSJLQ.js.map} +0 -0
  243. /package/dist/{chunk-GCJA5KKT.js.map → chunk-XBNO56HC.js.map} +0 -0
  244. /package/dist/{chunk-GWOFCHPN.js.map → chunk-YGECOOTF.js.map} +0 -0
  245. /package/dist/{chunk-2R7OCBRR.js.map → chunk-YK7GOPIE.js.map} +0 -0
  246. /package/dist/{chunk-ELUG6NKQ.js.map → chunk-YSA4CYTK.js.map} +0 -0
  247. /package/dist/{chunk-233S3MXC.js.map → chunk-YXUNSOHD.js.map} +0 -0
  248. /package/dist/{chunk-SY4AFWUZ.js.map → chunk-Z3O7ZOCC.js.map} +0 -0
  249. /package/dist/{chunk-ZG57BEUS.js.map → chunk-ZAU2OPDW.js.map} +0 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Bounded-queue retention for automatic extraction snapshots (#3324
3
+ * follow-up).
4
+ *
5
+ * The coordinator's queue keeps every accepted task's own snapshot (that is
6
+ * the #3324 contract: per-entry captured scope and settlement), but nothing
7
+ * requires each queued snapshot to RETAIN its own turn objects. Automatic
8
+ * triggers hand out prefix-extended snapshots of the same buffer (the buffer
9
+ * clears only when extraction runs), so consecutive queued snapshots overlap
10
+ * almost entirely while their drain is slow. Retaining one deep copy per
11
+ * snapshot grows quadratic overhead (demonstrated: scaling exponent 1.89 on
12
+ * main, 36× the unique content at 1000 turns for one session).
13
+ *
14
+ * This registry makes the OVERLAP shared without changing ANY task's
15
+ * semantics: a queued snapshot is stored as a chain node
16
+ * `{ base, tail }` whose turns are verified element-wise equivalent to the
17
+ * base chain at admit time. Each task materializes its own fresh array at
18
+ * run time — identical values, same windows, same chunking, same clear
19
+ * matching, same settlements. Sharing is data-structure only.
20
+ *
21
+ * Safety properties:
22
+ * - Equivalence is CONSUMER-equivalence, not `bufferTurnsEqual` alone: the
23
+ * five fields `bufferTurnsEqual` ignores (extractionContextOnly, parts,
24
+ * rawContent, sourceFormat, importProvenance) are compared strictly
25
+ * (objects by reference identity), so an aliased segment is
26
+ * indistinguishable from a private copy to every downstream reader.
27
+ * - Turn objects are immutable by audit: no code path writes a BufferTurn
28
+ * field or a parts array in place (all writes go to freshly constructed
29
+ * objects: `copyBufferTurn`, the replay `pinned` spread). Audited across
30
+ * the extraction call graph.
31
+ * - Distinct scope pins can never share: pins are turn fields covered by
32
+ * `bufferTurnsEqual`, and the scope-class bucket is a lookup heuristic
33
+ * only — a wrong pairing fails the element-wise check and simply does not
34
+ * share.
35
+ * - Replay, bulk import, force flush, fail-closed callers, and claim-backed
36
+ * execution contexts are excluded (they are not automatic snapshots of the
37
+ * live buffer).
38
+ */
39
+
40
+ import type { BufferTurn } from "../types.js";
41
+ import { bufferTurnsEqual, scopeProfilePlanIdentity } from "../buffer-turn-helpers.js";
42
+
43
+ /** Option surface of `queueBufferedExtraction` relevant to eligibility. */
44
+ export interface AutomaticSharingOptions {
45
+ /** Claim-backed backlog tasks own a different buffer view: never share. */
46
+ execution?: unknown;
47
+ /** Automatic trigger paths set this; forced replay/import do not. */
48
+ preemptable?: boolean;
49
+ failOnExtractionFailure?: boolean;
50
+ forceExtractionAttempt?: boolean;
51
+ /** Fail-closed front flush: never share. */
52
+ preemptRunning?: boolean;
53
+ }
54
+
55
+ export function automaticSnapshotSharingEligible(options: AutomaticSharingOptions): boolean {
56
+ return (
57
+ options.execution === undefined &&
58
+ options.preemptable === true &&
59
+ options.failOnExtractionFailure !== true &&
60
+ options.forceExtractionAttempt !== true &&
61
+ options.preemptRunning !== true
62
+ );
63
+ }
64
+
65
+ /** Consumer-visible equivalence: `bufferTurnsEqual` PLUS the fields it ignores. */
66
+ export function turnRepresentationEquivalent(left: BufferTurn, right: BufferTurn): boolean {
67
+ return (
68
+ bufferTurnsEqual(left, right) &&
69
+ left.extractionContextOnly === right.extractionContextOnly &&
70
+ left.importProvenance === right.importProvenance &&
71
+ left.parts === right.parts &&
72
+ left.rawContent === right.rawContent &&
73
+ left.sourceFormat === right.sourceFormat
74
+ );
75
+ }
76
+
77
+ /** Scope-class bucket key, the same composite `groupTurnsByExtractionScope` uses. */
78
+ function scopeClassKey(turn: BufferTurn): string {
79
+ return [
80
+ turn.writeNamespaceOverride ?? "",
81
+ turn.principalOverride ?? "",
82
+ scopeProfilePlanIdentity(turn.scopeProfileWritePlan),
83
+ turn.acceptedCapture === true ? "accepted" : "",
84
+ turn.codingContext?.projectId ?? "",
85
+ turn.codingContext?.rootPath ?? "",
86
+ turn.codingContext?.branch ?? "",
87
+ turn.codingContext?.defaultBranch ?? "",
88
+ ].join("\u0000");
89
+ }
90
+
91
+ /** One queued automatic snapshot: `base` chain plus this trigger's own new turns. */
92
+ export interface SnapshotChain {
93
+ readonly key: string;
94
+ readonly scopeClass: string;
95
+ readonly base: SnapshotChain | undefined;
96
+ /** Total turns retained by the base chain. */
97
+ readonly baseLength: number;
98
+ /** This trigger's non-shared suffix (fresh reference slots, disjoint across the chain). */
99
+ readonly tail: readonly BufferTurn[];
100
+ /** Total turns this snapshot represents. */
101
+ readonly total: number;
102
+ /** Outstanding tasks holding this node; the node leaves the registry when it reaches 0. */
103
+ holders: number;
104
+ }
105
+
106
+ export interface SnapshotSharingStats {
107
+ /** Distinct content chains registered (not task count — see queuedHolders). */
108
+ queuedNodes: number;
109
+ /** Σ per-node window lengths over registered nodes. Sibling nodes sharing
110
+ * a base chain count that base once per node (per-task window basis). */
111
+ queuedTotalRefs: number;
112
+ /** Distinct turn references REACHABLE from queued nodes, counting
113
+ * released ancestors reached through `base` exactly once (visited-set). */
114
+ queuedTailRefs: number;
115
+ /** Longest base chain reachable from a queued node (must stay bounded). */
116
+ queuedReachableDepth: number;
117
+ /** Outstanding tasks holding nodes (per-task, not per-node). */
118
+ queuedHolders: number;
119
+ /** What retention would be WITHOUT sharing: Σ per-node window length ×
120
+ * holders (each outstanding task would carry its own full window). */
121
+ unsharedEquivalentRefs: number;
122
+ }
123
+
124
+ /** Iterative root-first walk: recursive `yield*` delegation costs O(depth)
125
+ * per item and hangs production-scale chains (3,000+ turn sessions,
126
+ * hours-long backlogs). Same order, bounded stack, no casts. */
127
+ function chainNodes(chain: SnapshotChain): SnapshotChain[] {
128
+ const nodes: SnapshotChain[] = [];
129
+ for (let node: SnapshotChain | undefined = chain; node; node = node.base) nodes.push(node);
130
+ return [...nodes].reverse();
131
+ }
132
+
133
+ /**
134
+ * Element-wise consumer-equivalence of the chain's turns against a snapshot
135
+ * prefix. Cost is O(chain.total) compares per admit — required to prove the
136
+ * shared prefix is consumer-equivalent to the incoming snapshot — so a chain
137
+ * build is O(depth^2) compares in total (measured, acknowledged in the PR).
138
+ */
139
+ export function chainPrefixEquivalent(chain: SnapshotChain, turns: readonly BufferTurn[]): boolean {
140
+ let offset = 0;
141
+ for (const node of chainNodes(chain)) {
142
+ for (const chained of node.tail) {
143
+ const incoming = turns[offset];
144
+ if (incoming === undefined) return false;
145
+ if (!turnRepresentationEquivalent(chained, incoming)) return false;
146
+ offset += 1;
147
+ }
148
+ }
149
+ return offset === chain.total;
150
+ }
151
+
152
+ /** Fresh array with exactly this snapshot's turns, in order. Run-time only. */
153
+ export function materializeChain(chain: SnapshotChain): BufferTurn[] {
154
+ const out: BufferTurn[] = [];
155
+ for (const node of chainNodes(chain)) {
156
+ for (const turn of node.tail) out.push(turn);
157
+ }
158
+ return out;
159
+ }
160
+
161
+ export class ExtractionSnapshotSharingRegistry {
162
+ private readonly buckets = new Map<string, SnapshotChain[]>();
163
+
164
+ /**
165
+ * Admit one automatic snapshot for `bufferKey`: chain it onto the newest
166
+ * queued same-scope snapshot when that is a verified prefix-extension,
167
+ * otherwise start a fresh chain. The caller's array is never mutated.
168
+ */
169
+ admit(bufferKey: string, turns: readonly BufferTurn[]): SnapshotChain {
170
+ const scopeClass = turns.length > 0 ? scopeClassKey(turns[0]) : "";
171
+ const bucketKey = `${bufferKey}\u0000${scopeClass}`;
172
+ const list = this.buckets.get(bucketKey);
173
+ const base = list !== undefined && list.length > 0 ? list[list.length - 1] : undefined;
174
+ if (base && base.total === turns.length && chainPrefixEquivalent(base, turns)) {
175
+ // Identical window: reuse the existing node. Creating a tail=[] node
176
+ // here would chain onto the base and grow reachable depth (and admit
177
+ // cost) by one per admission under continuous unchanged ingress, while
178
+ // stats showed only the newest node. Sharing is exact: the element-wise
179
+ // equivalence above proved this window equals the base's. The holder
180
+ // count keeps the node registered until the LAST sharer releases.
181
+ base.holders += 1;
182
+ return base;
183
+ }
184
+ const node: SnapshotChain =
185
+ base && base.total <= turns.length && chainPrefixEquivalent(base, turns)
186
+ ? {
187
+ key: bufferKey,
188
+ scopeClass,
189
+ base,
190
+ baseLength: base.total,
191
+ tail: turns.slice(base.total),
192
+ total: turns.length,
193
+ holders: 1,
194
+ }
195
+ : {
196
+ key: bufferKey,
197
+ scopeClass,
198
+ base: undefined,
199
+ baseLength: 0,
200
+ tail: [...turns],
201
+ total: turns.length,
202
+ holders: 1,
203
+ };
204
+ if (list) list.push(node);
205
+ else this.buckets.set(bucketKey, [node]);
206
+ return node;
207
+ }
208
+
209
+ /** Release one holder of a node when its task exits (or on enqueue
210
+ * refusal). CONTRACT: called EXACTLY ONCE per admit, by the task that
211
+ * admitted it — the registry has no task identity, so a second call by the
212
+ * same task would steal another holder's count. The only production caller
213
+ * (queueBufferedExtraction) enforces this structurally: the closure-finally
214
+ * and the enqueue-refusal branch are mutually exclusive. Defensively, a
215
+ * release past zero is a no-op. */
216
+ release(node: SnapshotChain): void {
217
+ if (node.holders <= 0) return;
218
+ node.holders -= 1;
219
+ if (node.holders > 0) return;
220
+ const list = this.buckets.get(`${node.key}\u0000${node.scopeClass}`);
221
+ if (!list) return;
222
+ const index = list.indexOf(node);
223
+ if (index >= 0) list.splice(index, 1);
224
+ if (list.length === 0) this.buckets.delete(`${node.key}\u0000${node.scopeClass}`);
225
+ }
226
+
227
+ /** Proof seam: what the queue retains right now. */
228
+ stats(): SnapshotSharingStats {
229
+ // Reachability-accurate accounting: a released ancestor can remain
230
+ // reachable through a queued descendant's `base`, so bucket-node sums
231
+ // under-report. Walk each chain once with a visited set of nodes.
232
+ const visited = new Set<SnapshotChain>();
233
+ let queuedNodes = 0;
234
+ let queuedTailRefs = 0;
235
+ let queuedReachableDepth = 0;
236
+ let queuedHolders = 0;
237
+ let unsharedEquivalentRefs = 0;
238
+ let queuedTotalRefs = 0;
239
+ for (const list of this.buckets.values()) {
240
+ for (const node of list) {
241
+ queuedNodes += 1;
242
+ unsharedEquivalentRefs += node.total * node.holders;
243
+ queuedTotalRefs += node.total;
244
+ let depth = 0;
245
+ for (let walk: SnapshotChain | undefined = node; walk; walk = walk.base) {
246
+ depth += 1;
247
+ if (visited.has(walk)) continue;
248
+ visited.add(walk);
249
+ queuedTailRefs += walk.tail.length;
250
+ queuedHolders += walk.holders;
251
+ }
252
+ if (depth > queuedReachableDepth) queuedReachableDepth = depth;
253
+ }
254
+ }
255
+ return { queuedNodes, queuedTotalRefs, queuedTailRefs, queuedReachableDepth, queuedHolders, unsharedEquivalentRefs };
256
+ }
257
+ }
@@ -44,11 +44,8 @@ import type { BufferTurn, PluginConfig, SourceConnectorProvenance } from "../typ
44
44
  import { groupTurnsByExtractionScope, bufferTurnsEqual } from "../buffer-turn-helpers.js";
45
45
  import { ExtractionDrain } from "./extraction-drain.js";
46
46
  import { skipClaimedSession } from "./buffer-drain-gate.js";
47
- import {
48
- queueBufferedCaptureScopeGroups,
49
- type CaptureScopeGroupQueueOptions,
50
- type ExtractionTaskExecutionContext,
51
- } from "./extraction-task-queueing.js";
47
+ import { queueBufferedCaptureScopeGroups, type CaptureScopeGroupQueueOptions, type ExtractionTaskExecutionContext } from "./extraction-task-queueing.js";
48
+ import { automaticSnapshotSharingEligible, ExtractionSnapshotSharingRegistry, materializeChain, type SnapshotChain } from "./extraction-queue-snapshot-sharing.js";
52
49
  import { resolveScopeProfilePlan, type ResolvedScopeProfilePlan, hasExplicitNamespacePolicy } from "../namespaces/scope-profiles.js";
53
50
  import { resolveCodingNamespaceOverlay, combineNamespaces } from "../coding/coding-namespace.js";
54
51
  import { canWriteNamespace, defaultNamespaceForPrincipal } from "../namespaces/principal.js";
@@ -166,6 +163,8 @@ function isFlushPlanRecoveryTurn(
166
163
 
167
164
  export class TurnIngestionCoordinator {
168
165
  private readonly drain: ExtractionDrain; // chunked trigger-drain (extraction-drain.ts)
166
+ /** Queued automatic-snapshot chains: bounded-queue retention via structural sharing. */
167
+ readonly snapshotSharing = new ExtractionSnapshotSharingRegistry();
169
168
  constructor(private readonly deps: TurnIngestionDeps) {
170
169
  this.drain = new ExtractionDrain(deps);
171
170
  }
@@ -902,6 +901,15 @@ export class TurnIngestionCoordinator {
902
901
  }
903
902
  if (settled) return;
904
903
  const preemptable = options.preemptable === true && options.preemptRunning !== true;
904
+ const sharingNode =
905
+ automaticSnapshotSharingEligible(options) && turnsToExtract.length > 0
906
+ ? this.snapshotSharing.admit(bufferKey, turnsToExtract)
907
+ : undefined;
908
+ // Discriminated source so the task closure never captures the caller's
909
+ // snapshot array when sharing: a textual reference would retain every
910
+ // overlapping copy and defeat the bound.
911
+ const queuedTurnsSource: { chain: SnapshotChain } | { direct: BufferTurn[] } =
912
+ sharingNode ? { chain: sharingNode } : { direct: turnsToExtract };
905
913
  const accepted = this.deps.extractionQueueCoordinator.enqueue(async (preemptHandle) => {
906
914
  try {
907
915
  await this.deps.extractionQueueCoordinator.runPreemptable(preemptHandle, preemptable, options.abortSignal, async (abortSignal) => {
@@ -931,7 +939,10 @@ export class TurnIngestionCoordinator {
931
939
  // concurrent drain that already took these turns skips the LLM work,
932
940
  // and re-resolve a pinned plan against CURRENT config — revoke skips
933
941
  // WITHOUT clearing, and the plan is never retargeted.
934
- let effectiveTurns = turnsToExtract;
942
+ let effectiveTurns =
943
+ "chain" in queuedTurnsSource
944
+ ? materializeChain(queuedTurnsSource.chain)
945
+ : queuedTurnsSource.direct;
935
946
  let effectiveScopePlan = options.scopeProfileWritePlan;
936
947
  const executionBuffer = options.execution?.buffer;
937
948
  if (effectiveTurns.some((turn) => turn.acceptedCapture === true)) {
@@ -1083,10 +1094,12 @@ export class TurnIngestionCoordinator {
1083
1094
  }
1084
1095
  });
1085
1096
  } finally {
1097
+ if (sharingNode) this.snapshotSharing.release(sharingNode);
1086
1098
  options.onTaskExit?.();
1087
1099
  }
1088
1100
  }, { front: options.preemptRunning === true, key: bufferKey });
1089
1101
  if (!accepted) {
1102
+ if (sharingNode) this.snapshotSharing.release(sharingNode);
1090
1103
  settleTask(new Error("extraction queue is stopped"));
1091
1104
  options.onTaskExit?.();
1092
1105
  return;