@remnic/core 9.69.88 → 9.69.90

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 (431) hide show
  1. package/dist/access-admin-ops-surface.js +74 -72
  2. package/dist/access-authorization-probe.js +75 -73
  3. package/dist/access-boundary.js +74 -72
  4. package/dist/access-cli.js +168 -165
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +74 -72
  7. package/dist/access-http-briefing.js +75 -73
  8. package/dist/access-http-query.js +76 -74
  9. package/dist/access-http.js +98 -96
  10. package/dist/access-identity-continuity-surface.js +74 -72
  11. package/dist/access-lcm-surface.js +74 -72
  12. package/dist/access-mcp-output-schemas.d.ts.map +1 -1
  13. package/dist/access-mcp-output-schemas.js +1 -1
  14. package/dist/access-mcp.js +86 -84
  15. package/dist/access-memory-search-fanout.js +3 -3
  16. package/dist/access-namespace-preflight.js +74 -72
  17. package/dist/access-observe-write-category.js +11 -10
  18. package/dist/access-observe-write-surface.js +74 -72
  19. package/dist/access-offline-manifest.js +10 -9
  20. package/dist/access-operations-batch.js +76 -74
  21. package/dist/access-operations.js +83 -81
  22. package/dist/access-recall-concurrency.js +74 -72
  23. package/dist/access-recall-corpus-versions.d.ts +12 -0
  24. package/dist/access-recall-corpus-versions.d.ts.map +1 -0
  25. package/dist/access-recall-corpus-versions.js +10 -0
  26. package/dist/access-recall-diagnostics.js +5 -5
  27. package/dist/access-recall-response.d.ts +4 -0
  28. package/dist/access-recall-response.d.ts.map +1 -1
  29. package/dist/access-recall-response.js +74 -72
  30. package/dist/access-recall-surface.d.ts +8 -0
  31. package/dist/access-recall-surface.d.ts.map +1 -1
  32. package/dist/access-recall-surface.js +74 -72
  33. package/dist/access-schema.js +10 -9
  34. package/dist/access-service.d.ts +5 -0
  35. package/dist/access-service.d.ts.map +1 -1
  36. package/dist/access-service.js +74 -72
  37. package/dist/access-surface-catalog.js +76 -74
  38. package/dist/access-surface-catalog.js.map +1 -1
  39. package/dist/active-memory-bridge.js +3 -3
  40. package/dist/active-recall.js +27 -24
  41. package/dist/active-recall.js.map +1 -1
  42. package/dist/adapters/index.js +7 -7
  43. package/dist/adapters/registry.js +3 -3
  44. package/dist/{auto-sync-U5N3CB5Y.js → auto-sync-PAWBFQWL.js} +5 -5
  45. package/dist/briefing.js +19 -19
  46. package/dist/buffer.js +8 -8
  47. package/dist/calibration.js +2 -2
  48. package/dist/capabilities.d.ts +2 -1
  49. package/dist/capabilities.d.ts.map +1 -1
  50. package/dist/capabilities.js +1 -1
  51. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
  52. package/dist/causal-consolidation.js +19 -19
  53. package/dist/{chunk-BMXEBWBT.js → chunk-2EMROIXS.js} +2 -1
  54. package/dist/{chunk-BMXEBWBT.js.map → chunk-2EMROIXS.js.map} +1 -1
  55. package/dist/{chunk-MCTM4YPL.js → chunk-2FLFJTCI.js} +4 -2
  56. package/dist/chunk-2FLFJTCI.js.map +1 -0
  57. package/dist/{chunk-RDJ6C7NZ.js → chunk-2IWBHZ3H.js} +5 -5
  58. package/dist/{chunk-UP75KL3L.js → chunk-2JRB3Z5N.js} +2 -2
  59. package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
  60. package/dist/{chunk-KN6CIN7I.js → chunk-32HO62I4.js} +2 -2
  61. package/dist/{chunk-JLPNONPH.js → chunk-35A7GCHG.js} +2 -2
  62. package/dist/{chunk-MXCAJKMA.js → chunk-37ZQYFX4.js} +6 -6
  63. package/dist/{chunk-LW3HKRL7.js → chunk-3BTHXB7Z.js} +3 -3
  64. package/dist/{chunk-C7V3KPKJ.js → chunk-47LTKAGL.js} +2 -2
  65. package/dist/{chunk-3O5BB45T.js → chunk-4DXNOVNW.js} +10 -10
  66. package/dist/{chunk-65I2LZEK.js → chunk-4G2YCKLT.js} +2 -2
  67. package/dist/{chunk-OK52IY3W.js → chunk-4GEBLO7S.js} +10 -10
  68. package/dist/{chunk-M6C7SOR6.js → chunk-4I6PEEFA.js} +3 -3
  69. package/dist/{chunk-Z4TXB4K7.js → chunk-5BUCKFRE.js} +2 -2
  70. package/dist/{chunk-BGI4NUAO.js → chunk-5S7SNRSJ.js} +2 -2
  71. package/dist/{chunk-JVPWRNHA.js → chunk-5YR4UGF2.js} +5 -5
  72. package/dist/{chunk-YJ7MW627.js → chunk-6BG364RN.js} +2 -2
  73. package/dist/{chunk-D5BSQR63.js → chunk-76LNUSS5.js} +2 -2
  74. package/dist/{chunk-7CRUDH7G.js → chunk-77HNHT6S.js} +2 -2
  75. package/dist/{chunk-OWYHHW67.js → chunk-7XI2UJHL.js} +4 -4
  76. package/dist/{chunk-BJVO4H5C.js → chunk-7Z2UKRNJ.js} +4 -4
  77. package/dist/{chunk-2WLMWP2T.js → chunk-A3K2KKSG.js} +2 -2
  78. package/dist/chunk-A5ZYP4K5.js +130 -0
  79. package/dist/chunk-A5ZYP4K5.js.map +1 -0
  80. package/dist/{chunk-AWG3TIAY.js → chunk-ACCOC6UM.js} +2 -2
  81. package/dist/{chunk-3JDA3XF5.js → chunk-AEGNDL24.js} +2 -2
  82. package/dist/{chunk-2NAD4YCI.js → chunk-AEUMONCD.js} +2 -2
  83. package/dist/{chunk-C6U57KG4.js → chunk-AF5PW35O.js} +3 -3
  84. package/dist/{chunk-IOSZHHJC.js → chunk-ANGZWYL2.js} +6 -6
  85. package/dist/{chunk-K6T33R4Q.js → chunk-AXGJMDBS.js} +156 -7
  86. package/dist/chunk-AXGJMDBS.js.map +1 -0
  87. package/dist/{chunk-6LYAEM54.js → chunk-AXHU56C3.js} +2 -2
  88. package/dist/{chunk-GWM6T5EQ.js → chunk-BCGMG2RD.js} +2 -2
  89. package/dist/{chunk-HXCJF4XU.js → chunk-BFIZXFUG.js} +2 -2
  90. package/dist/{chunk-DBYAY6UW.js → chunk-BTOHGPHC.js} +3 -3
  91. package/dist/{chunk-5X4MXNEU.js → chunk-C6HPNPNK.js} +3 -3
  92. package/dist/{chunk-4MP7XR5J.js → chunk-CAKHZAXH.js} +2 -2
  93. package/dist/{chunk-YSBL7VPO.js → chunk-CDWFWOSZ.js} +2 -2
  94. package/dist/{chunk-QAW55ZHJ.js → chunk-CZ44FH45.js} +4 -4
  95. package/dist/{chunk-T3FMRLWQ.js → chunk-D5HQ3VEX.js} +4 -4
  96. package/dist/{chunk-S7LWX2PQ.js → chunk-DPG5HQLE.js} +4 -4
  97. package/dist/{chunk-C7ZDKWPS.js → chunk-DVSAMW5T.js} +3 -3
  98. package/dist/{chunk-WBYQA3VN.js → chunk-E5H4TM4Y.js} +3 -3
  99. package/dist/{chunk-4RL3JOB3.js → chunk-EEDI3OXB.js} +2 -2
  100. package/dist/{chunk-3TB2XQ3C.js → chunk-EUL3S3LE.js} +4 -4
  101. package/dist/{chunk-Q4AUIFUA.js → chunk-F23U5YE7.js} +7 -7
  102. package/dist/{chunk-ZZZ4I455.js → chunk-GDVORIFM.js} +2 -2
  103. package/dist/{chunk-QB57WIS6.js → chunk-GXETU7AE.js} +98 -73
  104. package/dist/chunk-GXETU7AE.js.map +1 -0
  105. package/dist/{chunk-SWBC3UER.js → chunk-HCPUVB7S.js} +3 -3
  106. package/dist/{chunk-R3X2T2LP.js → chunk-HFOY2SBG.js} +2 -2
  107. package/dist/{chunk-LU7E2MXE.js → chunk-HQCUUVG2.js} +20 -20
  108. package/dist/{chunk-FJLQXMTU.js → chunk-I6B3S62A.js} +2 -2
  109. package/dist/{chunk-RBITPCMK.js → chunk-IDSSDL4W.js} +9 -9
  110. package/dist/{chunk-OCFXVDBZ.js → chunk-IVEHY2VT.js} +54 -123
  111. package/dist/chunk-IVEHY2VT.js.map +1 -0
  112. package/dist/{chunk-B3OFQP5Y.js → chunk-IY47KPML.js} +6 -6
  113. package/dist/{chunk-AXC4WSF3.js → chunk-JDOBIFBK.js} +2 -2
  114. package/dist/{chunk-P2ODTYOF.js → chunk-JGXHQV5N.js} +3 -3
  115. package/dist/{chunk-7LNHRQKR.js → chunk-JLIGUFPF.js} +2 -2
  116. package/dist/{chunk-EDIH6X2P.js → chunk-JZ66PN72.js} +2 -2
  117. package/dist/{chunk-BLVR4WYU.js → chunk-KICEAGKM.js} +2 -2
  118. package/dist/{chunk-S2GE2K36.js → chunk-KNI4SGQE.js} +109 -96
  119. package/dist/chunk-KNI4SGQE.js.map +1 -0
  120. package/dist/{chunk-VDX2J7OX.js → chunk-KQAFEZQX.js} +2 -2
  121. package/dist/{chunk-PEBHPCX2.js → chunk-KTG23JDX.js} +2 -2
  122. package/dist/{chunk-BOZJD5PK.js → chunk-KYOZ5E6V.js} +2 -2
  123. package/dist/{chunk-2DLWLNO4.js → chunk-L3TZXELN.js} +4 -4
  124. package/dist/{chunk-VALKBYBN.js → chunk-M3N2TUZ7.js} +2 -2
  125. package/dist/{chunk-76M5OK7V.js → chunk-M7T2NKET.js} +5 -5
  126. package/dist/chunk-MSAI3Q7D.js +372 -0
  127. package/dist/chunk-MSAI3Q7D.js.map +1 -0
  128. package/dist/{chunk-Z5LP6ILP.js → chunk-MZAH3CBT.js} +2 -2
  129. package/dist/{chunk-QI7K7BZ5.js → chunk-NJW2ZG64.js} +2 -2
  130. package/dist/{chunk-CZJA2EQE.js → chunk-NSKXRTVZ.js} +2 -2
  131. package/dist/{chunk-4O4XCBOS.js → chunk-NUSQ3YEL.js} +2 -2
  132. package/dist/{chunk-4OQET73E.js → chunk-O57FCN42.js} +15 -15
  133. package/dist/{chunk-WKY7N3L7.js → chunk-OEBUAC3O.js} +10 -10
  134. package/dist/{chunk-Y23L4YJV.js → chunk-OEIA4IHU.js} +2 -2
  135. package/dist/{chunk-66DKTOBY.js → chunk-OGZC2WUX.js} +2 -2
  136. package/dist/{chunk-3SGTV7RD.js → chunk-P62DSJUK.js} +6 -6
  137. package/dist/{chunk-FKD656D2.js → chunk-PH2V7EXZ.js} +2 -2
  138. package/dist/{chunk-KKNZ7PUZ.js → chunk-QAIKLICT.js} +2 -2
  139. package/dist/{chunk-SSUDD72S.js → chunk-QPWUG2S3.js} +2 -2
  140. package/dist/{chunk-QI2E6ISQ.js → chunk-R2TVWQLR.js} +2 -2
  141. package/dist/chunk-R2TVWQLR.js.map +1 -0
  142. package/dist/{chunk-GZDTVYK2.js → chunk-R36IBI2M.js} +3 -3
  143. package/dist/{chunk-W3XXFPRI.js → chunk-R5B3OWI4.js} +64 -64
  144. package/dist/{chunk-VL4X7ZHC.js → chunk-RFIJRZ4K.js} +2 -2
  145. package/dist/{chunk-PWF4JXKD.js → chunk-RKDSELA3.js} +1 -1
  146. package/dist/chunk-RKDSELA3.js.map +1 -0
  147. package/dist/{chunk-AEHVZY3X.js → chunk-RPVHRZ5C.js} +2 -2
  148. package/dist/{chunk-X5SHE2O3.js → chunk-RVBLCU5C.js} +4 -4
  149. package/dist/{chunk-BUQKSQXL.js → chunk-S533KGX6.js} +5 -5
  150. package/dist/{chunk-STEOT4DI.js → chunk-SCPHU3RW.js} +7 -7
  151. package/dist/{chunk-LV5H6FTN.js → chunk-SFSD5Q6O.js} +3 -3
  152. package/dist/{chunk-AE5U5NMS.js → chunk-SMLKHXPF.js} +2 -2
  153. package/dist/{chunk-AEU4EQGV.js → chunk-T7R5HI6A.js} +1 -1
  154. package/dist/{chunk-H5BJWPUI.js → chunk-TVQZAQPE.js} +2 -2
  155. package/dist/{chunk-H56VY4BY.js → chunk-U36GYZ2P.js} +10 -5
  156. package/dist/chunk-U36GYZ2P.js.map +1 -0
  157. package/dist/{chunk-XOV7NRY5.js → chunk-U46P4UAR.js} +3 -3
  158. package/dist/{chunk-DZ42MDZE.js → chunk-UCJINCZT.js} +2 -2
  159. package/dist/{chunk-5E6RYRYW.js → chunk-UG5V3Z3R.js} +7 -7
  160. package/dist/{chunk-IW3RMBPB.js → chunk-VAOC6GHN.js} +2 -2
  161. package/dist/{chunk-6UQFJULG.js → chunk-VBNA2JEN.js} +2 -2
  162. package/dist/{chunk-HLB7GKGG.js → chunk-VOZNBQNL.js} +2 -2
  163. package/dist/{chunk-NYKIUQL6.js → chunk-WHIGRW7T.js} +2 -2
  164. package/dist/{chunk-AZSGSRIM.js → chunk-WHTMVTU4.js} +3 -3
  165. package/dist/{chunk-AERXJCOG.js → chunk-WL7N5VGG.js} +5 -5
  166. package/dist/{chunk-TFA6HPOA.js → chunk-WQUGCOKF.js} +4 -4
  167. package/dist/{chunk-A4VSOUCB.js → chunk-WTZQM74L.js} +5 -5
  168. package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
  169. package/dist/{chunk-T2YX56QI.js → chunk-XWSUK5GL.js} +4 -4
  170. package/dist/{chunk-ACX55QDO.js → chunk-XYOT5GQU.js} +3 -3
  171. package/dist/{chunk-Z6EZCN6Z.js → chunk-Y7427SL7.js} +10 -10
  172. package/dist/{chunk-4KN2JLQV.js → chunk-YHZWVDP7.js} +2 -2
  173. package/dist/chunk-YVRQ5JRA.js +49 -0
  174. package/dist/chunk-YVRQ5JRA.js.map +1 -0
  175. package/dist/{chunk-5MNUBT7T.js → chunk-ZFTODDTX.js} +2 -2
  176. package/dist/{chunk-VVX5PZFS.js → chunk-ZHXSOMYH.js} +2 -2
  177. package/dist/{chunk-CRXRZA3G.js → chunk-ZQTYWSO3.js} +2 -2
  178. package/dist/cli.js +126 -122
  179. package/dist/coding/export-okf-codegraph.js +17 -17
  180. package/dist/compounding/engine.js +19 -19
  181. package/dist/config.d.ts.map +1 -1
  182. package/dist/config.js +27 -24
  183. package/dist/connectors/codex-materialize-runner.js +18 -18
  184. package/dist/connectors/index.js +20 -20
  185. package/dist/contradiction/index.js +3 -3
  186. package/dist/{contradiction-scan-TAGMOUQ2.js → contradiction-scan-GGRTVB7C.js} +3 -3
  187. package/dist/convergence-refresh.js +20 -20
  188. package/dist/conversation-index/backend.js +2 -2
  189. package/dist/corpus-watermark.js +20 -20
  190. package/dist/day-summary.js +2 -2
  191. package/dist/deep-recall-http-glue.js +11 -10
  192. package/dist/embedding-fallback.js +2 -2
  193. package/dist/entity-retrieval.js +19 -19
  194. package/dist/explicit-capture.js +2 -2
  195. package/dist/external-wiki-access.js +75 -73
  196. package/dist/extraction-judge.js +3 -3
  197. package/dist/extraction-normalization.js +3 -3
  198. package/dist/extraction-prompt.js +2 -2
  199. package/dist/extraction.js +32 -32
  200. package/dist/{first-start-migration-7OATHJKB.js → first-start-migration-OM3PHMZO.js} +2 -2
  201. package/dist/harmonic-retrieval.js +1 -1
  202. package/dist/index.d.ts +1 -1
  203. package/dist/index.d.ts.map +1 -1
  204. package/dist/index.js +198 -195
  205. package/dist/lcm/engine.js +4 -4
  206. package/dist/lcm/index.js +10 -10
  207. package/dist/local-llm-config.js +1 -1
  208. package/dist/local-llm.js +3 -3
  209. package/dist/location/index.js +20 -20
  210. package/dist/maintenance/memory-governance.js +18 -18
  211. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +18 -18
  212. package/dist/maintenance/rebuild-memory-projection.js +19 -19
  213. package/dist/memory-browse-operations.js +75 -73
  214. package/dist/memory-browse.js +5 -5
  215. package/dist/namespaces/migrate.js +19 -19
  216. package/dist/namespaces/principal.js +2 -2
  217. package/dist/namespaces/search.js +13 -13
  218. package/dist/namespaces/storage.js +18 -18
  219. package/dist/offline-sync-changeset-generations.d.ts +82 -0
  220. package/dist/offline-sync-changeset-generations.d.ts.map +1 -0
  221. package/dist/offline-sync-changeset-generations.js +21 -0
  222. package/dist/offline-sync-changeset-generations.js.map +1 -0
  223. package/dist/offline-sync-embedding-generation.d.ts +50 -0
  224. package/dist/offline-sync-embedding-generation.d.ts.map +1 -1
  225. package/dist/offline-sync-embedding-generation.js +4 -2
  226. package/dist/offline-sync-impression-drain.js +5 -5
  227. package/dist/offline-sync-snapshot-stream.js +5 -4
  228. package/dist/offline-sync.d.ts +12 -1
  229. package/dist/offline-sync.d.ts.map +1 -1
  230. package/dist/offline-sync.js +6 -5
  231. package/dist/operator-doctor-corpus.js +21 -21
  232. package/dist/operator-toolkit.js +39 -36
  233. package/dist/orchestration/compression-guideline-coordinator.js +2 -2
  234. package/dist/orchestration/maintenance.js +33 -33
  235. package/dist/orchestration/orchestrator-helpers.d.ts +1 -0
  236. package/dist/orchestration/orchestrator-helpers.d.ts.map +1 -1
  237. package/dist/orchestration/recall-entry.d.ts.map +1 -1
  238. package/dist/orchestration/recall-search-pipeline.d.ts.map +1 -1
  239. package/dist/orchestrator.js +168 -165
  240. package/dist/outcome-boost-config.d.ts +17 -0
  241. package/dist/outcome-boost-config.d.ts.map +1 -0
  242. package/dist/outcome-boost-config.js +1 -0
  243. package/dist/outcome-boost-config.js.map +1 -0
  244. package/dist/qmd.js +4 -4
  245. package/dist/recall-explain-renderer.js +3 -3
  246. package/dist/recall-navigate-access.js +2 -2
  247. package/dist/recall-navigate-disclosure.js +2 -2
  248. package/dist/recall-navigate.js +2 -2
  249. package/dist/recall-navigation-operations.js +75 -73
  250. package/dist/recall-navigation.js +74 -72
  251. package/dist/recall-state-view-admission.js +5 -5
  252. package/dist/recall-why-service.js +9 -9
  253. package/dist/recall-why.js +3 -3
  254. package/dist/recall-xray-cli.js +4 -4
  255. package/dist/recall-xray-renderer.js +3 -3
  256. package/dist/recall-xray.js +2 -2
  257. package/dist/rerank-outcome.d.ts +73 -0
  258. package/dist/rerank-outcome.d.ts.map +1 -0
  259. package/dist/rerank-outcome.js +28 -0
  260. package/dist/rerank-outcome.js.map +1 -0
  261. package/dist/{resolution-K4A3VPKZ.js → resolution-BROE3TRO.js} +4 -4
  262. package/dist/resolution-BROE3TRO.js.map +1 -0
  263. package/dist/resume-bundles.js +28 -25
  264. package/dist/search/embed-helper.js +2 -2
  265. package/dist/search/factory.js +12 -12
  266. package/dist/search/index.js +18 -18
  267. package/dist/search/lancedb-backend.js +2 -2
  268. package/dist/search/meilisearch-backend.js +2 -2
  269. package/dist/search/orama-backend.js +2 -2
  270. package/dist/semantic-consolidation.js +19 -19
  271. package/dist/semantic-rule-promotion.js +18 -18
  272. package/dist/semantic-rule-verifier.js +18 -18
  273. package/dist/shared-context/manager.js +2 -2
  274. package/dist/source-agent-qualifier.js +26 -26
  275. package/dist/standup/build.js +18 -18
  276. package/dist/storage.js +17 -17
  277. package/dist/summarizer.js +4 -4
  278. package/dist/support-passport/index.js +77 -75
  279. package/dist/support-passport/offline-sync-guard.d.ts +6 -2
  280. package/dist/support-passport/offline-sync-guard.d.ts.map +1 -1
  281. package/dist/{tier-stats-VF6DS52E.js → tier-stats-M2FMMD2N.js} +2 -2
  282. package/dist/transfer/backup.js +2 -2
  283. package/dist/transfer/capsule-export.js +2 -2
  284. package/dist/transfer/capsule-import.js +2 -2
  285. package/dist/transfer/export-okf.js +18 -18
  286. package/dist/types.d.ts +4 -2
  287. package/dist/types.d.ts.map +1 -1
  288. package/dist/types.js +1 -1
  289. package/dist/verified-recall.js +18 -18
  290. package/dist/who-knows-cli.js +19 -19
  291. package/dist/who-knows.js +18 -18
  292. package/package.json +13 -3
  293. package/src/access-mcp-output-schemas.ts +2 -0
  294. package/src/access-recall-corpus-versions.ts +59 -0
  295. package/src/access-recall-response.ts +3 -0
  296. package/src/access-recall-surface.ts +22 -23
  297. package/src/access-service-raw-excerpt-read-gate.test.ts +1 -1
  298. package/src/access-service.ts +3 -0
  299. package/src/capabilities.test.ts +1 -0
  300. package/src/capabilities.ts +6 -8
  301. package/src/config.ts +3 -0
  302. package/src/index.ts +1 -1
  303. package/src/offline-sync-changeset-generations.test.ts +962 -0
  304. package/src/offline-sync-changeset-generations.ts +549 -0
  305. package/src/offline-sync-embedding-generation.ts +272 -4
  306. package/src/offline-sync.ts +75 -162
  307. package/src/orchestration/orchestrator-helpers.ts +1 -1
  308. package/src/orchestration/recall-entry.ts +32 -16
  309. package/src/orchestration/recall-internal.ts +2 -2
  310. package/src/orchestration/recall-search-pipeline.ts +3 -0
  311. package/src/outcome-boost-config.ts +16 -0
  312. package/src/recall-retrieval-failure.test.ts +456 -4
  313. package/src/rerank-outcome.test.ts +218 -0
  314. package/src/rerank-outcome.ts +219 -0
  315. package/src/support-passport/offline-sync-guard.ts +16 -2
  316. package/src/types.ts +3 -3
  317. package/dist/chunk-H56VY4BY.js.map +0 -1
  318. package/dist/chunk-K6T33R4Q.js.map +0 -1
  319. package/dist/chunk-MCTM4YPL.js.map +0 -1
  320. package/dist/chunk-OCFXVDBZ.js.map +0 -1
  321. package/dist/chunk-PWF4JXKD.js.map +0 -1
  322. package/dist/chunk-QB57WIS6.js.map +0 -1
  323. package/dist/chunk-QI2E6ISQ.js.map +0 -1
  324. package/dist/chunk-S2GE2K36.js.map +0 -1
  325. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-recall-corpus-versions.js.map} +0 -0
  326. /package/dist/{auto-sync-U5N3CB5Y.js.map → auto-sync-PAWBFQWL.js.map} +0 -0
  327. /package/dist/{contradiction-scan-TAGMOUQ2.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  328. /package/dist/{chunk-RDJ6C7NZ.js.map → chunk-2IWBHZ3H.js.map} +0 -0
  329. /package/dist/{chunk-UP75KL3L.js.map → chunk-2JRB3Z5N.js.map} +0 -0
  330. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
  331. /package/dist/{chunk-KN6CIN7I.js.map → chunk-32HO62I4.js.map} +0 -0
  332. /package/dist/{chunk-JLPNONPH.js.map → chunk-35A7GCHG.js.map} +0 -0
  333. /package/dist/{chunk-MXCAJKMA.js.map → chunk-37ZQYFX4.js.map} +0 -0
  334. /package/dist/{chunk-LW3HKRL7.js.map → chunk-3BTHXB7Z.js.map} +0 -0
  335. /package/dist/{chunk-C7V3KPKJ.js.map → chunk-47LTKAGL.js.map} +0 -0
  336. /package/dist/{chunk-3O5BB45T.js.map → chunk-4DXNOVNW.js.map} +0 -0
  337. /package/dist/{chunk-65I2LZEK.js.map → chunk-4G2YCKLT.js.map} +0 -0
  338. /package/dist/{chunk-OK52IY3W.js.map → chunk-4GEBLO7S.js.map} +0 -0
  339. /package/dist/{chunk-M6C7SOR6.js.map → chunk-4I6PEEFA.js.map} +0 -0
  340. /package/dist/{chunk-Z4TXB4K7.js.map → chunk-5BUCKFRE.js.map} +0 -0
  341. /package/dist/{chunk-BGI4NUAO.js.map → chunk-5S7SNRSJ.js.map} +0 -0
  342. /package/dist/{chunk-JVPWRNHA.js.map → chunk-5YR4UGF2.js.map} +0 -0
  343. /package/dist/{chunk-YJ7MW627.js.map → chunk-6BG364RN.js.map} +0 -0
  344. /package/dist/{chunk-D5BSQR63.js.map → chunk-76LNUSS5.js.map} +0 -0
  345. /package/dist/{chunk-7CRUDH7G.js.map → chunk-77HNHT6S.js.map} +0 -0
  346. /package/dist/{chunk-OWYHHW67.js.map → chunk-7XI2UJHL.js.map} +0 -0
  347. /package/dist/{chunk-BJVO4H5C.js.map → chunk-7Z2UKRNJ.js.map} +0 -0
  348. /package/dist/{chunk-2WLMWP2T.js.map → chunk-A3K2KKSG.js.map} +0 -0
  349. /package/dist/{chunk-AWG3TIAY.js.map → chunk-ACCOC6UM.js.map} +0 -0
  350. /package/dist/{chunk-3JDA3XF5.js.map → chunk-AEGNDL24.js.map} +0 -0
  351. /package/dist/{chunk-2NAD4YCI.js.map → chunk-AEUMONCD.js.map} +0 -0
  352. /package/dist/{chunk-C6U57KG4.js.map → chunk-AF5PW35O.js.map} +0 -0
  353. /package/dist/{chunk-IOSZHHJC.js.map → chunk-ANGZWYL2.js.map} +0 -0
  354. /package/dist/{chunk-6LYAEM54.js.map → chunk-AXHU56C3.js.map} +0 -0
  355. /package/dist/{chunk-GWM6T5EQ.js.map → chunk-BCGMG2RD.js.map} +0 -0
  356. /package/dist/{chunk-HXCJF4XU.js.map → chunk-BFIZXFUG.js.map} +0 -0
  357. /package/dist/{chunk-DBYAY6UW.js.map → chunk-BTOHGPHC.js.map} +0 -0
  358. /package/dist/{chunk-5X4MXNEU.js.map → chunk-C6HPNPNK.js.map} +0 -0
  359. /package/dist/{chunk-4MP7XR5J.js.map → chunk-CAKHZAXH.js.map} +0 -0
  360. /package/dist/{chunk-YSBL7VPO.js.map → chunk-CDWFWOSZ.js.map} +0 -0
  361. /package/dist/{chunk-QAW55ZHJ.js.map → chunk-CZ44FH45.js.map} +0 -0
  362. /package/dist/{chunk-T3FMRLWQ.js.map → chunk-D5HQ3VEX.js.map} +0 -0
  363. /package/dist/{chunk-S7LWX2PQ.js.map → chunk-DPG5HQLE.js.map} +0 -0
  364. /package/dist/{chunk-C7ZDKWPS.js.map → chunk-DVSAMW5T.js.map} +0 -0
  365. /package/dist/{chunk-WBYQA3VN.js.map → chunk-E5H4TM4Y.js.map} +0 -0
  366. /package/dist/{chunk-4RL3JOB3.js.map → chunk-EEDI3OXB.js.map} +0 -0
  367. /package/dist/{chunk-3TB2XQ3C.js.map → chunk-EUL3S3LE.js.map} +0 -0
  368. /package/dist/{chunk-Q4AUIFUA.js.map → chunk-F23U5YE7.js.map} +0 -0
  369. /package/dist/{chunk-ZZZ4I455.js.map → chunk-GDVORIFM.js.map} +0 -0
  370. /package/dist/{chunk-SWBC3UER.js.map → chunk-HCPUVB7S.js.map} +0 -0
  371. /package/dist/{chunk-R3X2T2LP.js.map → chunk-HFOY2SBG.js.map} +0 -0
  372. /package/dist/{chunk-LU7E2MXE.js.map → chunk-HQCUUVG2.js.map} +0 -0
  373. /package/dist/{chunk-FJLQXMTU.js.map → chunk-I6B3S62A.js.map} +0 -0
  374. /package/dist/{chunk-RBITPCMK.js.map → chunk-IDSSDL4W.js.map} +0 -0
  375. /package/dist/{chunk-B3OFQP5Y.js.map → chunk-IY47KPML.js.map} +0 -0
  376. /package/dist/{chunk-AXC4WSF3.js.map → chunk-JDOBIFBK.js.map} +0 -0
  377. /package/dist/{chunk-P2ODTYOF.js.map → chunk-JGXHQV5N.js.map} +0 -0
  378. /package/dist/{chunk-7LNHRQKR.js.map → chunk-JLIGUFPF.js.map} +0 -0
  379. /package/dist/{chunk-EDIH6X2P.js.map → chunk-JZ66PN72.js.map} +0 -0
  380. /package/dist/{chunk-BLVR4WYU.js.map → chunk-KICEAGKM.js.map} +0 -0
  381. /package/dist/{chunk-VDX2J7OX.js.map → chunk-KQAFEZQX.js.map} +0 -0
  382. /package/dist/{chunk-PEBHPCX2.js.map → chunk-KTG23JDX.js.map} +0 -0
  383. /package/dist/{chunk-BOZJD5PK.js.map → chunk-KYOZ5E6V.js.map} +0 -0
  384. /package/dist/{chunk-2DLWLNO4.js.map → chunk-L3TZXELN.js.map} +0 -0
  385. /package/dist/{chunk-VALKBYBN.js.map → chunk-M3N2TUZ7.js.map} +0 -0
  386. /package/dist/{chunk-76M5OK7V.js.map → chunk-M7T2NKET.js.map} +0 -0
  387. /package/dist/{chunk-Z5LP6ILP.js.map → chunk-MZAH3CBT.js.map} +0 -0
  388. /package/dist/{chunk-QI7K7BZ5.js.map → chunk-NJW2ZG64.js.map} +0 -0
  389. /package/dist/{chunk-CZJA2EQE.js.map → chunk-NSKXRTVZ.js.map} +0 -0
  390. /package/dist/{chunk-4O4XCBOS.js.map → chunk-NUSQ3YEL.js.map} +0 -0
  391. /package/dist/{chunk-4OQET73E.js.map → chunk-O57FCN42.js.map} +0 -0
  392. /package/dist/{chunk-WKY7N3L7.js.map → chunk-OEBUAC3O.js.map} +0 -0
  393. /package/dist/{chunk-Y23L4YJV.js.map → chunk-OEIA4IHU.js.map} +0 -0
  394. /package/dist/{chunk-66DKTOBY.js.map → chunk-OGZC2WUX.js.map} +0 -0
  395. /package/dist/{chunk-3SGTV7RD.js.map → chunk-P62DSJUK.js.map} +0 -0
  396. /package/dist/{chunk-FKD656D2.js.map → chunk-PH2V7EXZ.js.map} +0 -0
  397. /package/dist/{chunk-KKNZ7PUZ.js.map → chunk-QAIKLICT.js.map} +0 -0
  398. /package/dist/{chunk-SSUDD72S.js.map → chunk-QPWUG2S3.js.map} +0 -0
  399. /package/dist/{chunk-GZDTVYK2.js.map → chunk-R36IBI2M.js.map} +0 -0
  400. /package/dist/{chunk-W3XXFPRI.js.map → chunk-R5B3OWI4.js.map} +0 -0
  401. /package/dist/{chunk-VL4X7ZHC.js.map → chunk-RFIJRZ4K.js.map} +0 -0
  402. /package/dist/{chunk-AEHVZY3X.js.map → chunk-RPVHRZ5C.js.map} +0 -0
  403. /package/dist/{chunk-X5SHE2O3.js.map → chunk-RVBLCU5C.js.map} +0 -0
  404. /package/dist/{chunk-BUQKSQXL.js.map → chunk-S533KGX6.js.map} +0 -0
  405. /package/dist/{chunk-STEOT4DI.js.map → chunk-SCPHU3RW.js.map} +0 -0
  406. /package/dist/{chunk-LV5H6FTN.js.map → chunk-SFSD5Q6O.js.map} +0 -0
  407. /package/dist/{chunk-AE5U5NMS.js.map → chunk-SMLKHXPF.js.map} +0 -0
  408. /package/dist/{chunk-AEU4EQGV.js.map → chunk-T7R5HI6A.js.map} +0 -0
  409. /package/dist/{chunk-H5BJWPUI.js.map → chunk-TVQZAQPE.js.map} +0 -0
  410. /package/dist/{chunk-XOV7NRY5.js.map → chunk-U46P4UAR.js.map} +0 -0
  411. /package/dist/{chunk-DZ42MDZE.js.map → chunk-UCJINCZT.js.map} +0 -0
  412. /package/dist/{chunk-5E6RYRYW.js.map → chunk-UG5V3Z3R.js.map} +0 -0
  413. /package/dist/{chunk-IW3RMBPB.js.map → chunk-VAOC6GHN.js.map} +0 -0
  414. /package/dist/{chunk-6UQFJULG.js.map → chunk-VBNA2JEN.js.map} +0 -0
  415. /package/dist/{chunk-HLB7GKGG.js.map → chunk-VOZNBQNL.js.map} +0 -0
  416. /package/dist/{chunk-NYKIUQL6.js.map → chunk-WHIGRW7T.js.map} +0 -0
  417. /package/dist/{chunk-AZSGSRIM.js.map → chunk-WHTMVTU4.js.map} +0 -0
  418. /package/dist/{chunk-AERXJCOG.js.map → chunk-WL7N5VGG.js.map} +0 -0
  419. /package/dist/{chunk-TFA6HPOA.js.map → chunk-WQUGCOKF.js.map} +0 -0
  420. /package/dist/{chunk-A4VSOUCB.js.map → chunk-WTZQM74L.js.map} +0 -0
  421. /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
  422. /package/dist/{chunk-T2YX56QI.js.map → chunk-XWSUK5GL.js.map} +0 -0
  423. /package/dist/{chunk-ACX55QDO.js.map → chunk-XYOT5GQU.js.map} +0 -0
  424. /package/dist/{chunk-Z6EZCN6Z.js.map → chunk-Y7427SL7.js.map} +0 -0
  425. /package/dist/{chunk-4KN2JLQV.js.map → chunk-YHZWVDP7.js.map} +0 -0
  426. /package/dist/{chunk-5MNUBT7T.js.map → chunk-ZFTODDTX.js.map} +0 -0
  427. /package/dist/{chunk-VVX5PZFS.js.map → chunk-ZHXSOMYH.js.map} +0 -0
  428. /package/dist/{chunk-CRXRZA3G.js.map → chunk-ZQTYWSO3.js.map} +0 -0
  429. /package/dist/{resolution-K4A3VPKZ.js.map → contradiction-scan-GGRTVB7C.js.map} +0 -0
  430. /package/dist/{first-start-migration-7OATHJKB.js.map → first-start-migration-OM3PHMZO.js.map} +0 -0
  431. /package/dist/{tier-stats-VF6DS52E.js.map → tier-stats-M2FMMD2N.js.map} +0 -0
@@ -186,6 +186,30 @@ export interface EmbeddingGenerationTransactionInput {
186
186
  * (codex P1: a daemon mutation between census and lock was discarded).
187
187
  */
188
188
  baseStates?: ReadonlyMap<string, { sha256: string }>;
189
+ /**
190
+ * Paths this apply is pre-authorized to remove (changeset deletes / remote
191
+ * tombstones). A base member absent from the under-lock census is
192
+ * divergence UNLESS it is pre-authorized — the retry after a response loss
193
+ * must re-apply idempotently instead of failing as diverged (#3150
194
+ * review). A pre-authorized member still present with a changed digest
195
+ * defers as before, so a daemon rewrite is never silently swept.
196
+ */
197
+ justifiedRemovals?: ReadonlySet<string>;
198
+ /**
199
+ * Deletion revisions (mtimeMs by path) to record through the delete hook
200
+ * AFTER the swap publishes. Swept members bypass the per-file delete path,
201
+ * so without this their tombstones are lost and a later peer without
202
+ * shared-base evidence can reintroduce the removed shards (codex P1).
203
+ */
204
+ deletionMtimeByPath?: ReadonlyMap<string, number>;
205
+ /**
206
+ * True when the incoming set claims to be a FRESH creation but carries no
207
+ * base evidence (manifested changeset members are all base-less upserts):
208
+ * a non-empty local generation must match the incoming set exactly or the
209
+ * apply defers as diverged, because the empty-base divergence skip would
210
+ * otherwise silently replace it (#3150 review).
211
+ */
212
+ freshCreateClaim?: boolean;
189
213
  io: EmbeddingGenerationTransactionIo;
190
214
  now: number;
191
215
  }
@@ -323,7 +347,11 @@ export async function applyEmbeddingGenerationTransaction(
323
347
  // secure IO and defer the WHOLE generation when the disk no longer
324
348
  // matches the shared base — a concurrent daemon mutation must never be
325
349
  // silently discarded by the swap.
326
- // No shared base (fresh catch-up) = no divergence semantics to protect.
350
+ // No shared base (fresh catch-up) = no divergence semantics to protect,
351
+ // UNLESS the apply claims a fresh creation (manifested base-less
352
+ // upserts): an existing local generation must then match the incoming
353
+ // set exactly or defer, instead of being silently replaced.
354
+ const justifiedRemovals = input.justifiedRemovals ?? new Set<string>();
327
355
  const baseStates = baseStatesForGeneration(input.baseStates, shardDirRel);
328
356
  if (baseStates.size > 0) {
329
357
  const localCensus = await censusGenerationState(io, root, markerRel, markerAbs, shardDirAbs, shardDirRel);
@@ -331,12 +359,23 @@ export async function applyEmbeddingGenerationTransaction(
331
359
  const censusArr = [...localCensus].map(([relPath, sha256]) => ({ path: relPath, sha256 }));
332
360
  const deferrals = divergedEmbeddingGenerationDeferrals({
333
361
  incomingFiles: incomingGenerationStates(input),
334
- baseFiles: baseArr,
362
+ // A pre-authorized removal keeps its base entry only while still on
363
+ // disk (digest auth): present members are compared normally — a
364
+ // daemon rewrite still defers — while an absent member is already
365
+ // applied and must not diverge the retry (#3150 review).
366
+ baseFiles: baseArr.filter((member) => !justifiedRemovals.has(member.path) || localCensus.has(member.path)),
335
367
  currentFiles: censusArr,
336
368
  });
369
+ const incomingShardPaths = new Set(input.incomingShardPaths);
337
370
  const addedOrRemovedSinceBase =
338
- censusArr.some((member) => !baseStates.has(member.path)) ||
339
- baseArr.some((member) => !localCensus.has(member.path));
371
+ censusArr.some((member) =>
372
+ !baseStates.has(member.path) &&
373
+ !justifiedRemovals.has(member.path) &&
374
+ // An incoming member found in the census is the apply's own
375
+ // already-published work (response-loss retry), not a local
376
+ // addition; its bytes were verified above.
377
+ !incomingShardPaths.has(member.path)) ||
378
+ baseArr.some((member) => !localCensus.has(member.path) && !justifiedRemovals.has(member.path));
340
379
  if (deferrals.length > 0 || addedOrRemovedSinceBase) {
341
380
  const handled = new Set<string>(input.incomingShardPaths);
342
381
  if (input.incomingMarker) handled.add(input.incomingMarker.path);
@@ -355,6 +394,35 @@ export async function applyEmbeddingGenerationTransaction(
355
394
  deferredLocalDivergence: true,
356
395
  };
357
396
  }
397
+ } else if (input.freshCreateClaim) {
398
+ const localCensus = await censusGenerationState(io, root, markerRel, markerAbs, shardDirAbs, shardDirRel);
399
+ if (localCensus.size > 0) {
400
+ const incomingArr = incomingGenerationStates(input).sort((left, right) => left.path.localeCompare(right.path));
401
+ const censusArr = [...localCensus]
402
+ .map(([relPath, sha256]) => ({ path: relPath, sha256 }))
403
+ .sort((left, right) => left.path.localeCompare(right.path));
404
+ const identical =
405
+ censusArr.length === incomingArr.length &&
406
+ censusArr.every((member, index) => member.path === incomingArr[index]?.path && member.sha256 === incomingArr[index]?.sha256);
407
+ if (!identical) {
408
+ const handled = new Set<string>(input.incomingShardPaths);
409
+ if (input.incomingMarker) handled.add(input.incomingMarker.path);
410
+ if (input.incomingMarkerPresent) handled.add(markerRel);
411
+ for (const relPath of localCensus.keys()) handled.add(relPath);
412
+ log.warn(
413
+ `embedding generation ${shardDirRel} deferred: base-less creation does not match the existing local generation under the mutation lock; nothing published, nothing removed`,
414
+ );
415
+ return {
416
+ shardDirRel,
417
+ upserted: 0,
418
+ deleted: 0,
419
+ writtenStates: new Map<string, OfflineSyncFileState>(),
420
+ removedPaths: new Set<string>(),
421
+ handledPaths: handled,
422
+ deferredLocalDivergence: true,
423
+ };
424
+ }
425
+ }
358
426
  }
359
427
 
360
428
  const published = new Map<string, Buffer>();
@@ -507,6 +575,32 @@ export async function applyEmbeddingGenerationTransaction(
507
575
  log.warn(`embedding generation ${shardDirRel}: marker cleanup deferred after publish: ${err}`);
508
576
  }
509
577
  }
578
+ // Swept members bypass the per-file delete path, so record their
579
+ // deletion revisions here — mirroring the whole-generation removal
580
+ // path — or a later peer without shared-base evidence can reintroduce
581
+ // the removed shards (codex P1). An empty map still records (with
582
+ // `now`) when the caller declared a deletion context, so tombstones
583
+ // whose changeset deletes omit mtimeMs are not lost. Every member
584
+ // gets an attempt before the failure propagates: the push side then
585
+ // retries a fully-recorded apply or nothing, and the
586
+ // justified-removals exemption keeps that retry idempotent.
587
+ if (input.deletionMtimeByPath !== undefined && io.deleteFile) {
588
+ let firstTombstoneError: unknown = null;
589
+ for (const relPath of removedPaths) {
590
+ try {
591
+ await io.deleteFile({
592
+ root: root.abs,
593
+ path: relPath,
594
+ filePath: await resolveSafeArchiveTarget(root, relPath),
595
+ mtimeMs: input.deletionMtimeByPath.get(relPath) ?? input.now,
596
+ });
597
+ } catch (err) {
598
+ firstTombstoneError ??= err;
599
+ log.warn(`embedding generation ${shardDirRel}: tombstone recording failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`);
600
+ }
601
+ }
602
+ if (firstTombstoneError) throw firstTombstoneError;
603
+ }
510
604
  return {
511
605
  shardDirRel,
512
606
  upserted: published.size,
@@ -883,3 +977,177 @@ export function divergedEmbeddingGenerationDeferrals(options: {
883
977
  }
884
978
  return deferred.sort((left, right) => left.localeCompare(right));
885
979
  }
980
+
981
+ // ---------------------------------------------------------------------------
982
+ // Snapshot-side generation routing, extracted from applyOfflineSyncSnapshot
983
+ // for issue #3150 so the changeset path could share the machinery without
984
+ // growing the offline-sync.ts ratchet ceiling. Behavior-preserving.
985
+ // ---------------------------------------------------------------------------
986
+
987
+ export interface IncomingEmbeddingGenerationApply {
988
+ transactionResults: EmbeddingGenerationTransactionResult[];
989
+ deferredGenerationDirs: Set<string>;
990
+ }
991
+
992
+ export interface IncomingEmbeddingGenerationApplyOptions {
993
+ root: SafeArchiveRoot;
994
+ incomingMap: ReadonlyMap<string, OfflineSyncFileState>;
995
+ incomingBuffers: ReadonlyMap<string, Buffer>;
996
+ baseMap: ReadonlyMap<string, OfflineSyncFileState>;
997
+ currentMap: ReadonlyMap<string, OfflineSyncFileState>;
998
+ deferredPaths: ReadonlySet<string>;
999
+ omittedGenerationDirs: ReadonlySet<string>;
1000
+ deletionMtimeByPath?: ReadonlyMap<string, number>;
1001
+ io: EmbeddingGenerationTransactionIo;
1002
+ /** True when the caller supplied ANY custom storage IO. */
1003
+ customIoPresent: boolean;
1004
+ now: number;
1005
+ }
1006
+
1007
+ /**
1008
+ * Snapshot-side generation routing, extracted from applyOfflineSyncSnapshot
1009
+ * for issue #3150 (the changeset path needed the same machinery and the
1010
+ * offline-sync.ts line ratchet had no headroom). Incoming generations replace
1011
+ * as ONE unit through the shared locked transaction. Behavior-preserving —
1012
+ * the #3148 regression suites own this contract.
1013
+ */
1014
+ export async function applyIncomingEmbeddingGenerations(
1015
+ options: IncomingEmbeddingGenerationApplyOptions,
1016
+ ): Promise<IncomingEmbeddingGenerationApply> {
1017
+ const incomingGenerations = detectIncomingEmbeddingGenerations(options.incomingMap.keys());
1018
+ const generationDirs = new Set<string>([
1019
+ ...incomingGenerations.shardDirs,
1020
+ ...incomingGenerations.legacyMarkerDirs,
1021
+ ]);
1022
+ const deferredGenerationDirs = new Set<string>();
1023
+ for (const deferredPath of options.deferredPaths) {
1024
+ const membership = embeddingGenerationMembership(deferredPath);
1025
+ if (membership) deferredGenerationDirs.add(membership.shardDir);
1026
+ }
1027
+ const transactionResults: EmbeddingGenerationTransactionResult[] = [];
1028
+ // Included and omitted cannot describe the same generation.
1029
+ for (const shardDir of generationDirs) {
1030
+ if (options.omittedGenerationDirs.has(shardDir)) {
1031
+ throw new EmbeddingIndexStorageError(
1032
+ `offline sync snapshot both includes and omits embedding generation ${shardDir}; refusing to apply`,
1033
+ );
1034
+ }
1035
+ }
1036
+ // Fail closed BEFORE any publication: custom storage IO (encrypted or
1037
+ // otherwise) without the staging pair would make the generation transaction
1038
+ // fall back to unencrypted raw writes — a plaintext downgrade of the staged
1039
+ // generation. Callers must wire both staging hooks (the CLI does).
1040
+ if (
1041
+ generationDirs.size > 0 &&
1042
+ options.customIoPresent &&
1043
+ !(options.io.writeStagingFile && options.io.readStagingFile)
1044
+ ) {
1045
+ throw new EmbeddingIndexStorageError(
1046
+ "refusing to replace embedding generation: custom storage IO requires both writeStagingFile and readStagingFile hooks; unencrypted raw staging would plaintext-downgrade the index",
1047
+ );
1048
+ }
1049
+ for (const shardDir of generationDirs) {
1050
+ if (deferredGenerationDirs.has(shardDir)) continue;
1051
+ if (
1052
+ incomingGenerations.legacyMarkerDirs.has(shardDir) &&
1053
+ !incomingGenerations.shardDirs.has(shardDir)
1054
+ ) {
1055
+ // Cross-layout: an incoming legacy marker replaces a local sharded
1056
+ // generation as a whole (codex round 4). When shards are ALSO
1057
+ // incoming, the sharded set is the generation and the marker is
1058
+ // written as an inert artifact by the per-file loop below.
1059
+ const markerRel = [...options.incomingMap.keys()].find(
1060
+ (relPath) => embeddingMarkerDirOf(relPath) === shardDir.slice(0, -`/${EMBEDDING_SHARD_DIR_BASENAME}`.length),
1061
+ );
1062
+ if (!markerRel) continue;
1063
+ transactionResults.push(await applyEmbeddingGenerationTransaction({
1064
+ root: options.root,
1065
+ shardDirRel: shardDir,
1066
+ incomingShardPaths: [],
1067
+ incomingMarker: {
1068
+ path: markerRel,
1069
+ sha256: options.incomingMap.get(markerRel)!.sha256,
1070
+ buffer: options.incomingBuffers.get(markerRel) ?? null,
1071
+ },
1072
+ incomingMarkerPresent: false,
1073
+ incomingShardStates: new Map(),
1074
+ incomingBuffers: options.incomingBuffers,
1075
+ baseStates: options.baseMap,
1076
+ io: options.io,
1077
+ now: options.now,
1078
+ }));
1079
+ continue;
1080
+ }
1081
+ transactionResults.push(await applyEmbeddingGenerationTransaction({
1082
+ root: options.root,
1083
+ shardDirRel: shardDir,
1084
+ incomingShardPaths: [...options.incomingMap.keys()]
1085
+ .filter((relPath) => embeddingShardDirOf(relPath) === shardDir)
1086
+ .sort(),
1087
+ incomingMarker: null,
1088
+ incomingMarkerPresent: Boolean(
1089
+ incomingGenerations.legacyMarkerDirs.has(shardDir),
1090
+ ),
1091
+ incomingShardStates: new Map(
1092
+ [...options.incomingMap.values()]
1093
+ .filter((state) => embeddingShardDirOf(state.path) === shardDir)
1094
+ .map((state) => [state.path, state]),
1095
+ ),
1096
+ incomingBuffers: options.incomingBuffers,
1097
+ baseStates: options.baseMap,
1098
+ io: options.io,
1099
+ now: options.now,
1100
+ }));
1101
+ }
1102
+ // Whole-generation removal (codex round 6 P1): when the remote deleted a
1103
+ // previously shared sharded index, the deletion revisions cover EVERY
1104
+ // local member. Remove that generation as ONE atomic swap instead of
1105
+ // deleting shards one-by-one, so a crash or delete failure can never
1106
+ // expose a partial same-identity generation serving missing memories.
1107
+ const localGenerationDirs = new Set<string>();
1108
+ for (const relPath of options.currentMap.keys()) {
1109
+ const membership = embeddingGenerationMembership(relPath);
1110
+ if (membership?.kind === "shard") localGenerationDirs.add(membership.shardDir);
1111
+ }
1112
+ for (const shardDir of localGenerationDirs) {
1113
+ if (
1114
+ generationDirs.has(shardDir) ||
1115
+ deferredGenerationDirs.has(shardDir) ||
1116
+ options.omittedGenerationDirs.has(shardDir)
1117
+ ) {
1118
+ continue;
1119
+ }
1120
+ const localMembers = [...options.currentMap.keys()].filter(
1121
+ (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,
1122
+ );
1123
+ if (localMembers.length === 0) continue;
1124
+ // Tombstone or shared-base absence authorizes whole-generation removal.
1125
+ const baseMembers = localMembers.filter((relPath) => options.baseMap.has(relPath));
1126
+ // Tombstone evidence is satisfied by the members the REMOTE knew: the
1127
+ // remote cannot tombstone local-only extras or an inert legacy marker,
1128
+ // and the removal sweeps those once the remote deletion is established.
1129
+ // Non-empty same-generation tombstones establish remote absence on
1130
+ // their own: they may name shards already absent locally (removed in an
1131
+ // earlier round), so the set is NOT required to intersect localMembers
1132
+ // (codex round 6, XiOb).
1133
+ const tombstonedMembers =
1134
+ options.deletionMtimeByPath === undefined
1135
+ ? []
1136
+ : [...options.deletionMtimeByPath.keys()].filter(
1137
+ (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,
1138
+ );
1139
+ const tombstoneCovered = tombstonedMembers.length > 0;
1140
+ const baseEvidenced =
1141
+ baseMembers.length > 0 && baseMembers.every((relPath) => !options.incomingMap.has(relPath));
1142
+ if (!tombstoneCovered && !baseEvidenced) continue;
1143
+ transactionResults.push(await applyEmbeddingGenerationRemoval({
1144
+ root: options.root,
1145
+ shardDirRel: shardDir,
1146
+ io: { deleteFile: options.io.deleteFile, readFile: options.io.readFile },
1147
+ deletionMtimeByPath: options.deletionMtimeByPath,
1148
+ baseStates: options.baseMap,
1149
+ now: options.now,
1150
+ }));
1151
+ }
1152
+ return { transactionResults, deferredGenerationDirs };
1153
+ }
@@ -27,21 +27,19 @@ import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
27
27
  import { EmbeddingIndexStorageError } from "./embedding-index-storage.js";
28
28
  import { matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
29
29
  import {
30
- EMBEDDING_SHARD_DIR_BASENAME,
31
- applyEmbeddingGenerationRemoval,
32
- applyEmbeddingGenerationTransaction,
30
+ applyIncomingEmbeddingGenerations,
33
31
  assertEmbeddingGenerationStillIncluded,
34
32
  computeOmittedEmbeddingGenerationPaths,
35
- detectIncomingEmbeddingGenerations,
36
33
  divergedEmbeddingGenerationDeferrals,
37
34
  embeddingGenerationMembership,
38
- embeddingMarkerDirOf,
39
- embeddingShardDirOf,
40
35
  isEmbeddingGenerationDirPath,
41
36
  isPathInOmittedEmbeddingGeneration,
42
- type EmbeddingGenerationTransactionIo,
43
- type EmbeddingGenerationTransactionResult,
44
37
  } from "./offline-sync-embedding-generation.js";
38
+ import {
39
+ applyChangesetEmbeddingGenerations,
40
+ normalizeChangesetGenerations,
41
+ type OfflineSyncChangesetGeneration,
42
+ } from "./offline-sync-changeset-generations.js";
45
43
  import { withEmbeddingGenerationLock, withEmbeddingGenerationLockIter } from "./embedding-generation-lock.js";
46
44
  import {
47
45
  isCanonicalRuntimeStatePath,
@@ -50,6 +48,7 @@ import {
50
48
  } from "./offline-sync-runtime-state.js";
51
49
  export { shouldPreferIncomingOfflineRuntimeFile } from "./offline-sync-runtime-state.js";
52
50
  export { computeOmittedEmbeddingGenerationPaths } from "./offline-sync-embedding-generation.js";
51
+ export type { OfflineSyncChangesetGeneration } from "./offline-sync-changeset-generations.js";
53
52
  export {
54
53
  compileOfflineSyncExcludeGlobs,
55
54
  globToRegExp,
@@ -150,6 +149,13 @@ export interface OfflineSyncChangeset {
150
149
  sourceId: string;
151
150
  includeTranscripts: boolean;
152
151
  changes: OfflineSyncChange[];
152
+ /**
153
+ * Complete post-change membership of every embedding generation the
154
+ * changeset touches (issue #3150). Absent on legacy senders: receivers
155
+ * defer manifest-less shard upserts as conflicts instead of publishing a
156
+ * mixed generation.
157
+ */
158
+ embeddingGenerations?: OfflineSyncChangesetGeneration[];
153
159
  }
154
160
 
155
161
  export interface OfflineSyncState {
@@ -170,7 +176,12 @@ export interface OfflineSyncConflict {
170
176
  | "remote_exists_for_local_create"
171
177
  | "remote_changed_for_local_update"
172
178
  | "remote_deleted_for_local_update"
173
- | "remote_changed_for_local_delete";
179
+ | "remote_changed_for_local_delete"
180
+ // Changeset generation deferrals (#3150): the receiver refused a whole
181
+ // embedding generation; surfaced as conflicts so the push side never
182
+ // checkpoints it as applied.
183
+ | "embedding_generation_diverged"
184
+ | "embedding_generation_manifest_required";
174
185
  baseSha256?: string;
175
186
  localSha256?: string;
176
187
  incomingSha256?: string;
@@ -637,6 +648,7 @@ export function normalizeOfflineSyncChangeset(
637
648
  if (excludedPath) {
638
649
  throw new Error(`offline sync changeset contains excluded path: ${excludedPath}`);
639
650
  }
651
+ const embeddingGenerations = normalizeChangesetGenerations(obj.embeddingGenerations, changes);
640
652
  return {
641
653
  format: OFFLINE_SYNC_CHANGESET_FORMAT,
642
654
  schemaVersion: 1,
@@ -644,6 +656,7 @@ export function normalizeOfflineSyncChangeset(
644
656
  sourceId,
645
657
  includeTranscripts,
646
658
  changes: changes.sort(compareByPath),
659
+ ...(embeddingGenerations === undefined ? {} : { embeddingGenerations }),
647
660
  };
648
661
  }
649
662
 
@@ -1614,159 +1627,28 @@ export async function applyOfflineSyncSnapshot(options: {
1614
1627
  return requiredBuffer(incomingBuffers, relPath);
1615
1628
  };
1616
1629
 
1617
- // Embedding generations travel and replace as ONE unit (issue #3148).
1618
- // Incoming shards form the generation; an incoming legacy marker is a
1619
- // complete generation too (converted to the sharded layout, preserving the
1620
- // one-way migration). A deferral anywhere in a generation defers the WHOLE
1621
- // generation; a generation the push omitted by filter is announced by the
1622
- // snapshot and its absence is never a delete instruction.
1623
- const incomingGenerations = detectIncomingEmbeddingGenerations(incomingMap.keys());
1624
- const generationDirs = new Set([
1625
- ...incomingGenerations.shardDirs,
1626
- ...incomingGenerations.legacyMarkerDirs,
1627
- ]);
1628
- const deferredGenerationDirs = new Set<string>();
1629
- for (const deferredPath of deferredPaths) {
1630
- const membership = embeddingGenerationMembership(deferredPath);
1631
- if (membership) {
1632
- deferredGenerationDirs.add(membership.shardDir);
1633
- }
1634
- }
1635
- const transactionResults: EmbeddingGenerationTransactionResult[] = [];
1636
- // Included and omitted cannot describe the same generation.
1637
- for (const shardDir of generationDirs) {
1638
- if (omittedGenerationDirs.has(shardDir)) {
1639
- throw new EmbeddingIndexStorageError(
1640
- `offline sync snapshot both includes and omits embedding generation ${shardDir}; refusing to apply`,
1641
- );
1642
- }
1643
- }
1644
- // Fail closed BEFORE any publication: custom storage IO (encrypted or
1645
- // otherwise) without the staging pair would make the generation transaction
1646
- // fall back to unencrypted raw writes — a plaintext downgrade of the staged
1647
- // generation. Callers must wire both staging hooks (the CLI does).
1648
- if (
1649
- generationDirs.size > 0 &&
1650
- (options.writeFile || options.readFile || options.readFileDigest || options.deleteFile) &&
1651
- !(options.writeStagingFile && options.readStagingFile)
1652
- ) {
1653
- throw new EmbeddingIndexStorageError(
1654
- "refusing to replace embedding generation: custom storage IO requires both writeStagingFile and readStagingFile hooks; unencrypted raw staging would plaintext-downgrade the index",
1655
- );
1656
- }
1657
- for (const shardDir of generationDirs) {
1658
- if (deferredGenerationDirs.has(shardDir)) continue;
1659
- if (
1660
- incomingGenerations.legacyMarkerDirs.has(shardDir) &&
1661
- !incomingGenerations.shardDirs.has(shardDir)
1662
- ) {
1663
- // Cross-layout: an incoming legacy marker replaces a local sharded
1664
- // generation as a whole (codex round 4). When shards are ALSO
1665
- // incoming, the sharded set is the generation and the marker is
1666
- // written as an inert artifact by the per-file loop below.
1667
- const markerRel = [...incomingMap.keys()].find(
1668
- (relPath) => embeddingMarkerDirOf(relPath) === shardDir.slice(0, -`/${EMBEDDING_SHARD_DIR_BASENAME}`.length),
1669
- );
1670
- if (!markerRel) continue;
1671
- transactionResults.push(await applyEmbeddingGenerationTransaction({
1672
- root,
1673
- shardDirRel: shardDir,
1674
- incomingShardPaths: [],
1675
- incomingMarker: {
1676
- path: markerRel,
1677
- sha256: incomingMap.get(markerRel)!.sha256,
1678
- buffer: incomingBuffers.get(markerRel) ?? null,
1679
- },
1680
- incomingMarkerPresent: false,
1681
- incomingShardStates: new Map(),
1682
- incomingBuffers,
1683
- baseStates: baseMap,
1684
- io: {
1685
- readFile: options.readFile,
1686
- writeStagingFile: options.writeStagingFile,
1687
- readStagingFile: options.readStagingFile,
1688
- deleteFile: options.deleteFile,
1689
- },
1690
- now: Date.now(),
1691
- }));
1692
- continue;
1693
- }
1694
- transactionResults.push(await applyEmbeddingGenerationTransaction({
1695
- root,
1696
- shardDirRel: shardDir,
1697
- incomingShardPaths: [...incomingMap.keys()]
1698
- .filter((relPath) => embeddingShardDirOf(relPath) === shardDir)
1699
- .sort(),
1700
- incomingMarker: null,
1701
- incomingMarkerPresent: Boolean(
1702
- incomingGenerations.legacyMarkerDirs.has(shardDir),
1703
- ),
1704
- incomingShardStates: new Map(
1705
- [...incomingMap.values()]
1706
- .filter((relPath) => embeddingShardDirOf(relPath.path) === shardDir)
1707
- .map((state) => [state.path, state]),
1708
- ),
1709
- incomingBuffers,
1710
- baseStates: baseMap,
1711
- io: {
1712
- readFile: options.readFile,
1713
- writeStagingFile: options.writeStagingFile,
1714
- readStagingFile: options.readStagingFile,
1715
- deleteFile: options.deleteFile,
1716
- },
1717
- now: Date.now(),
1718
- }));
1719
- }
1720
- // Whole-generation removal (codex round 6 P1): when the remote deleted a
1721
- // previously shared sharded index, the deletion revisions cover EVERY
1722
- // local member. Remove that generation as ONE atomic swap instead of
1723
- // deleting shards one-by-one, so a crash or delete failure can never
1724
- // expose a partial same-identity generation serving missing memories.
1725
- const localGenerationDirs = new Set<string>();
1726
- for (const relPath of currentMap.keys()) {
1727
- const membership = embeddingGenerationMembership(relPath);
1728
- if (membership?.kind === "shard") localGenerationDirs.add(membership.shardDir);
1729
- }
1730
- for (const shardDir of localGenerationDirs) {
1731
- if (
1732
- generationDirs.has(shardDir) ||
1733
- deferredGenerationDirs.has(shardDir) ||
1734
- omittedGenerationDirs.has(shardDir)
1735
- ) {
1736
- continue;
1737
- }
1738
- const localMembers = [...currentMap.keys()].filter(
1739
- (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,
1740
- );
1741
- if (localMembers.length === 0) continue;
1742
- // Tombstone or shared-base absence authorizes whole-generation removal.
1743
- const baseMembers = localMembers.filter((relPath) => baseMap.has(relPath));
1744
- // Tombstone evidence is satisfied by the members the REMOTE knew: the
1745
- // remote cannot tombstone local-only extras or an inert legacy marker,
1746
- // and the removal sweeps those once the remote deletion is established.
1747
- // Non-empty same-generation tombstones establish remote absence on
1748
- // their own: they may name shards already absent locally (removed in an
1749
- // earlier round), so the set is NOT required to intersect localMembers
1750
- // (codex round 6, XiOb).
1751
- const tombstonedMembers =
1752
- deletionMtimeByPath === undefined
1753
- ? []
1754
- : [...deletionMtimeByPath.keys()].filter(
1755
- (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,
1756
- );
1757
- const tombstoneCovered = tombstonedMembers.length > 0;
1758
- const baseEvidenced =
1759
- baseMembers.length > 0 && baseMembers.every((relPath) => !incomingMap.has(relPath));
1760
- if (!tombstoneCovered && !baseEvidenced) continue;
1761
- transactionResults.push(await applyEmbeddingGenerationRemoval({
1762
- root,
1763
- shardDirRel: shardDir,
1764
- io: { deleteFile: options.deleteFile, readFile: options.readFile },
1765
- deletionMtimeByPath,
1766
- baseStates: baseMap,
1767
- now: Date.now(),
1768
- }));
1769
- }
1630
+ // Embedding generations travel and replace as ONE unit (issue #3148):
1631
+ // atomic replacement, cross-layout marker conversion, whole-generation
1632
+ // removal on tombstones — all extracted to the generation module (issue
1633
+ // #3150) so the changeset path can share it without growing this file.
1634
+ const { transactionResults, deferredGenerationDirs } = await applyIncomingEmbeddingGenerations({
1635
+ root,
1636
+ incomingMap,
1637
+ incomingBuffers,
1638
+ baseMap,
1639
+ currentMap,
1640
+ deferredPaths,
1641
+ omittedGenerationDirs,
1642
+ deletionMtimeByPath,
1643
+ io: {
1644
+ readFile: options.readFile,
1645
+ writeStagingFile: options.writeStagingFile,
1646
+ readStagingFile: options.readStagingFile,
1647
+ deleteFile: options.deleteFile,
1648
+ },
1649
+ customIoPresent: Boolean(options.writeFile || options.readFile || options.readFileDigest || options.deleteFile),
1650
+ now: Date.now(),
1651
+ });
1770
1652
 
1771
1653
  const transactionHandled = new Set<string>();
1772
1654
  let generationUpserted = 0;
@@ -1993,6 +1875,8 @@ export async function applyOfflineSyncChangeset(options: {
1993
1875
  readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
1994
1876
  readFileDigest?: (target: OfflineSyncFileTarget) => Promise<OfflineSyncFileDigest>;
1995
1877
  writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;
1878
+ writeStagingFile?: (target: OfflineSyncFileStagingWriteTarget) => Promise<void>;
1879
+ readStagingFile?: (target: OfflineSyncFileStagingReadTarget) => Promise<Buffer>;
1996
1880
  deleteFile?: (target: OfflineSyncFileDeleteTarget) => Promise<void>;
1997
1881
  recordDeletionRevision?: OfflineSyncRecordDeletionRevision;
1998
1882
  }): Promise<OfflineSyncApplyChangesetResult> {
@@ -2030,7 +1914,30 @@ export async function applyOfflineSyncChangeset(options: {
2030
1914
  let appliedDeletes = 0;
2031
1915
  let skipped = 0;
2032
1916
 
1917
+ // Embedding generations apply as ONE unit through the shared locked
1918
+ // transaction (issue #3150). Deferred generations surface as conflicts so
1919
+ // the push side never checkpoints them as applied.
1920
+ const generationRouting = await applyChangesetEmbeddingGenerations({
1921
+ root,
1922
+ changeset,
1923
+ incomingBuffers,
1924
+ currentMap,
1925
+ io: {
1926
+ readFile: options.readFile,
1927
+ writeStagingFile: options.writeStagingFile,
1928
+ readStagingFile: options.readStagingFile,
1929
+ deleteFile: options.deleteFile,
1930
+ },
1931
+ customIoPresent: Boolean(options.writeFile || options.readFile || options.readFileDigest || options.deleteFile),
1932
+ now: Date.now(),
1933
+ });
1934
+ appliedUpserts += generationRouting.appliedUpserts;
1935
+ appliedDeletes += generationRouting.appliedDeletes;
1936
+ conflicts.push(...generationRouting.conflicts);
1937
+
2033
1938
  for (const change of changeset.changes) {
1939
+ if (generationRouting.conflictedPaths.has(change.path)) continue;
1940
+ if (generationRouting.transactionHandled.has(change.path)) continue;
2034
1941
  const currentEntry = currentMap.get(change.path);
2035
1942
  if (change.type === "upsert") {
2036
1943
  if (currentEntry?.sha256 === change.file.sha256) {
@@ -2110,6 +2017,12 @@ export async function applyOfflineSyncChangeset(options: {
2110
2017
  });
2111
2018
  }
2112
2019
 
2020
+ // Generation transactions bypassed the per-file loop; fold their published
2021
+ // and removed members into the partial result map (returnCurrentFiles ===
2022
+ // false consumers) so it reflects the post-apply generation exactly.
2023
+ for (const [relPath, state] of generationRouting.writtenStates) currentMap.set(relPath, state);
2024
+ for (const relPath of generationRouting.removedPaths) currentMap.delete(relPath);
2025
+
2113
2026
  return {
2114
2027
  appliedUpserts,
2115
2028
  appliedDeletes,
@@ -199,6 +199,7 @@ export interface RecallInvocationOptions {
199
199
  namespace?: string;
200
200
  topK?: number;
201
201
  mode?: RecallPlanMode;
202
+ onRecallPlanResolved?: (mode: RecallPlanMode, readNamespaces: readonly string[]) => void | Promise<void>;
202
203
  abortSignal?: AbortSignal;
203
204
  /** Server-resolved identity of the connector performing this recall. */
204
205
  sourceConnector?: string;
@@ -689,7 +690,6 @@ export function buildCompressionGuidelinesMarkdown(
689
690
  return buildCompressionGuidelinesMarkdownV2(events, generatedAtIso);
690
691
  }
691
692
 
692
-
693
693
  export function applyQueryAwareCandidateFilter(
694
694
  candidates: QmdSearchResult[],
695
695
  candidatePaths: Set<string> | null,