@remnic/core 10.0.48 → 10.0.50

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 (449) hide show
  1. package/dist/access-admin-ops-surface.js +73 -70
  2. package/dist/access-audit.js +2 -2
  3. package/dist/access-authorization-probe.js +74 -71
  4. package/dist/access-boundary.js +73 -70
  5. package/dist/access-cli.js +170 -167
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +73 -70
  8. package/dist/access-http-briefing.d.ts +2 -0
  9. package/dist/access-http-briefing.d.ts.map +1 -1
  10. package/dist/access-http-briefing.js +74 -71
  11. package/dist/access-http-query.js +75 -72
  12. package/dist/access-http.d.ts.map +1 -1
  13. package/dist/access-http.js +96 -93
  14. package/dist/access-identity-continuity-surface.js +73 -70
  15. package/dist/access-lcm-surface.js +73 -70
  16. package/dist/access-mcp.js +85 -82
  17. package/dist/access-namespace-preflight.js +73 -70
  18. package/dist/access-observe-write-category.d.ts +1 -1
  19. package/dist/access-observe-write-category.d.ts.map +1 -1
  20. package/dist/access-observe-write-category.js +15 -14
  21. package/dist/access-observe-write-surface.d.ts.map +1 -1
  22. package/dist/access-observe-write-surface.js +73 -70
  23. package/dist/access-offline-manifest.js +9 -8
  24. package/dist/access-operations-batch.js +76 -73
  25. package/dist/access-operations.js +83 -80
  26. package/dist/access-recall-concurrency.js +73 -70
  27. package/dist/access-recall-response.js +73 -70
  28. package/dist/access-recall-surface.js +73 -70
  29. package/dist/access-schema.js +11 -10
  30. package/dist/access-service.d.ts +5 -0
  31. package/dist/access-service.d.ts.map +1 -1
  32. package/dist/access-service.js +73 -70
  33. package/dist/access-surface-catalog.js +75 -72
  34. package/dist/access-surface-catalog.js.map +1 -1
  35. package/dist/active-recall.js +32 -30
  36. package/dist/active-recall.js.map +1 -1
  37. package/dist/adapters/index.js +4 -4
  38. package/dist/adapters/registry.js +2 -2
  39. package/dist/{auto-sync-BNCBMU6H.js → auto-sync-UQQ6YJSB.js} +3 -3
  40. package/dist/briefing-deadline.d.ts +7 -0
  41. package/dist/briefing-deadline.d.ts.map +1 -0
  42. package/dist/briefing-deadline.js +13 -0
  43. package/dist/briefing-window.d.ts +12 -4
  44. package/dist/briefing-window.d.ts.map +1 -1
  45. package/dist/briefing-window.js +3 -1
  46. package/dist/briefing.d.ts +2 -0
  47. package/dist/briefing.d.ts.map +1 -1
  48. package/dist/briefing.js +22 -20
  49. package/dist/buffer.js +6 -6
  50. package/dist/calibration.js +2 -2
  51. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  52. package/dist/{capsule-merge-RNQUP2VL.js → capsule-merge-D4BTIJGK.js} +3 -3
  53. package/dist/capture-write-lock-scope.d.ts +7 -0
  54. package/dist/capture-write-lock-scope.d.ts.map +1 -0
  55. package/dist/capture-write-lock-scope.js +10 -0
  56. package/dist/causal-behavior.js +2 -2
  57. package/dist/causal-chain.js +2 -2
  58. package/dist/causal-consolidation.js +23 -22
  59. package/dist/causal-consolidation.js.map +1 -1
  60. package/dist/causal-retrieval.js +2 -2
  61. package/dist/causal-trajectory-graph.js +3 -3
  62. package/dist/causal-trajectory.js +1 -1
  63. package/dist/{chunk-FAKRTS3Y.js → chunk-2AKIVSHP.js} +2 -2
  64. package/dist/{chunk-EIYJK7OD.js → chunk-2M36PR7R.js} +2 -2
  65. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  66. package/dist/{chunk-6P6MZDBL.js → chunk-3NL4ALUP.js} +2 -2
  67. package/dist/{chunk-6CC2YCKG.js → chunk-43TUWQNR.js} +3 -3
  68. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  69. package/dist/{chunk-2NLCDNGA.js → chunk-564YP3DW.js} +2 -2
  70. package/dist/{chunk-5KGYR5VN.js → chunk-5DVOBBTV.js} +2 -2
  71. package/dist/{chunk-RT2GN6KQ.js → chunk-5FUDSK6Z.js} +2 -2
  72. package/dist/{chunk-B4XSXFKI.js → chunk-5G53JSRO.js} +2 -2
  73. package/dist/{chunk-BGK3SFCC.js → chunk-5X2H3X4J.js} +2 -2
  74. package/dist/{chunk-OXSTKCYK.js → chunk-6AEKSOWV.js} +33 -5
  75. package/dist/chunk-6AEKSOWV.js.map +1 -0
  76. package/dist/{chunk-4J7BNL7B.js → chunk-6ZQX3R66.js} +2 -2
  77. package/dist/{chunk-JNLYHJG7.js → chunk-74AY3S7U.js} +4 -4
  78. package/dist/{chunk-RUAIO5UA.js → chunk-7J6A5MV2.js} +2 -2
  79. package/dist/{chunk-A45QXZMU.js → chunk-7SAE6T36.js} +2 -2
  80. package/dist/chunk-7VLBZKNB.js +26 -0
  81. package/dist/chunk-7VLBZKNB.js.map +1 -0
  82. package/dist/{chunk-QE2XQM3S.js → chunk-7YVER7ZO.js} +2 -2
  83. package/dist/{chunk-IJH6METJ.js → chunk-A3UFV4K3.js} +4 -4
  84. package/dist/{chunk-LWRIGKFM.js → chunk-A5L5KGDI.js} +19 -1
  85. package/dist/chunk-A5L5KGDI.js.map +1 -0
  86. package/dist/{chunk-LXN737FX.js → chunk-AAANTXCQ.js} +2 -2
  87. package/dist/{chunk-I3IPXGFQ.js → chunk-AJJPXUE6.js} +4 -4
  88. package/dist/{chunk-2DPJFJFH.js → chunk-BNJAVRNJ.js} +11 -11
  89. package/dist/{chunk-YL564QS2.js → chunk-CCL2OXFO.js} +3 -3
  90. package/dist/{chunk-CHSRA76M.js → chunk-CRN3EPE2.js} +4 -3
  91. package/dist/chunk-CRN3EPE2.js.map +1 -0
  92. package/dist/{chunk-ZX5N5D6M.js → chunk-DAOMWGO2.js} +4 -4
  93. package/dist/{chunk-YUE7J2HZ.js → chunk-DMKTOCC7.js} +2 -2
  94. package/dist/chunk-DMKTOCC7.js.map +1 -0
  95. package/dist/{chunk-FRXS6SC7.js → chunk-EKUI7I2X.js} +20 -4
  96. package/dist/chunk-EKUI7I2X.js.map +1 -0
  97. package/dist/{chunk-PKPOO6X5.js → chunk-EKYWZYZJ.js} +25 -17
  98. package/dist/chunk-EKYWZYZJ.js.map +1 -0
  99. package/dist/{chunk-23ZMQONL.js → chunk-ERMT5WQM.js} +2 -2
  100. package/dist/{chunk-VTCNVGM6.js → chunk-EWBXMGXR.js} +2 -2
  101. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  102. package/dist/{chunk-XTN54NML.js → chunk-G3T6YMBO.js} +4 -4
  103. package/dist/{chunk-2JNBPOZ2.js → chunk-GDYLZ6FR.js} +3 -3
  104. package/dist/{chunk-DZPTR2XQ.js → chunk-GFAEKSFR.js} +2 -2
  105. package/dist/{chunk-L46MG2GE.js → chunk-GQIYL7OD.js} +400 -301
  106. package/dist/chunk-GQIYL7OD.js.map +1 -0
  107. package/dist/{chunk-M7LA4QBY.js → chunk-GXVXBYRA.js} +3 -3
  108. package/dist/{chunk-UXAF4RQM.js → chunk-HKOMHP6D.js} +27 -15
  109. package/dist/chunk-HKOMHP6D.js.map +1 -0
  110. package/dist/{chunk-JGMJZBFV.js → chunk-HPNXS6W6.js} +2 -2
  111. package/dist/{chunk-BFPF5N3H.js → chunk-HXFIILGF.js} +4 -4
  112. package/dist/{chunk-YBKQAVPZ.js → chunk-IMKOJVD4.js} +101 -81
  113. package/dist/chunk-IMKOJVD4.js.map +1 -0
  114. package/dist/{chunk-CMNL3QSL.js → chunk-IN6U2BS5.js} +2 -2
  115. package/dist/{chunk-BWRX322G.js → chunk-IUYXCTA7.js} +2 -2
  116. package/dist/{chunk-LU57EEJW.js → chunk-JBWD3J5U.js} +3 -3
  117. package/dist/chunk-JNA7LFMQ.js +41 -0
  118. package/dist/chunk-JNA7LFMQ.js.map +1 -0
  119. package/dist/{chunk-2V664H6O.js → chunk-JS44PBCM.js} +5 -5
  120. package/dist/{chunk-XKTHJHSQ.js → chunk-JZCBMKR5.js} +3 -3
  121. package/dist/{chunk-MXPVNDGO.js → chunk-K5QQXVG6.js} +12 -12
  122. package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
  123. package/dist/{chunk-WUBGMV3G.js → chunk-KRAMCBIW.js} +9 -9
  124. package/dist/{chunk-NDMPRT3Z.js → chunk-KW5YZELC.js} +2 -2
  125. package/dist/{chunk-2RQR4TXV.js → chunk-LIBVANOF.js} +2 -2
  126. package/dist/{chunk-M7L5A2AT.js → chunk-LMMBV7QC.js} +4 -4
  127. package/dist/{chunk-PJBYVJKO.js → chunk-LXXIV7Y2.js} +2 -2
  128. package/dist/{chunk-MZCTS3ZG.js → chunk-M2DOJBH7.js} +2 -2
  129. package/dist/{chunk-WOPZ2P5Z.js → chunk-M4HRYLOA.js} +2 -2
  130. package/dist/{chunk-JCYEDMOY.js → chunk-M6TP6UYD.js} +3 -3
  131. package/dist/{chunk-KAUCVTAW.js → chunk-MLNXATFG.js} +2 -2
  132. package/dist/{chunk-2NX7JTST.js → chunk-MPWOAIIH.js} +2 -2
  133. package/dist/{chunk-RQLTCHUL.js → chunk-MWURUSRE.js} +13 -13
  134. package/dist/{chunk-3EGL7XZO.js → chunk-N74WJ7B2.js} +2 -2
  135. package/dist/{chunk-DRXTMDAG.js → chunk-N7FNCE7F.js} +2 -2
  136. package/dist/{chunk-6CWAY4R7.js → chunk-NPLM7AFU.js} +2 -2
  137. package/dist/{chunk-VECRSYQG.js → chunk-NRGQ5HJO.js} +1 -1
  138. package/dist/{chunk-PPDNG7WC.js → chunk-NSP5PIQH.js} +2 -2
  139. package/dist/{chunk-6HVLHIAB.js → chunk-P7OVFGMU.js} +2 -2
  140. package/dist/{chunk-EX27VKU6.js → chunk-PDTNQLPC.js} +2 -2
  141. package/dist/{chunk-ME5LVISI.js → chunk-PJCVI44H.js} +2 -2
  142. package/dist/{chunk-LJCHDFLP.js → chunk-Q3PNQYAC.js} +2 -2
  143. package/dist/{chunk-YFJHFEET.js → chunk-QS765SBL.js} +2 -2
  144. package/dist/{chunk-FJ264QBP.js → chunk-QTDZ2NYC.js} +11 -11
  145. package/dist/{chunk-Q6UZREJK.js → chunk-RKRX4C4H.js} +2 -2
  146. package/dist/{chunk-YFXRLRKP.js → chunk-S4OZKPBS.js} +7 -11
  147. package/dist/{chunk-YFXRLRKP.js.map → chunk-S4OZKPBS.js.map} +1 -1
  148. package/dist/{chunk-GLZWOIU3.js → chunk-SJTSWG2Z.js} +3 -3
  149. package/dist/{chunk-Y57GJDG5.js → chunk-SWRRZNHM.js} +14 -17
  150. package/dist/chunk-SWRRZNHM.js.map +1 -0
  151. package/dist/{chunk-CCTLRRV6.js → chunk-TEH5MDX5.js} +55 -34
  152. package/dist/chunk-TEH5MDX5.js.map +1 -0
  153. package/dist/{chunk-XM747YJF.js → chunk-TFWJXWL6.js} +2 -2
  154. package/dist/{chunk-QFQ4UG3I.js → chunk-TXKQBQYW.js} +4 -4
  155. package/dist/{chunk-BMOJFGVX.js → chunk-UALXWWGN.js} +2 -2
  156. package/dist/{chunk-B44NFHWI.js → chunk-UNOAVHV4.js} +2 -2
  157. package/dist/{chunk-TF5NZGPS.js → chunk-UQBOPCDK.js} +2 -2
  158. package/dist/{chunk-SUKPM6DV.js → chunk-UWMZT2TY.js} +47 -4
  159. package/dist/chunk-UWMZT2TY.js.map +1 -0
  160. package/dist/{chunk-5R2IF2KI.js → chunk-UXGXJUZ5.js} +7 -7
  161. package/dist/{chunk-FWV2SXSL.js → chunk-UZVQFGAY.js} +101 -98
  162. package/dist/chunk-UZVQFGAY.js.map +1 -0
  163. package/dist/{chunk-J6B6FK2X.js → chunk-VCOMACR6.js} +53 -53
  164. package/dist/{chunk-WCGJK4NQ.js → chunk-VKQI47DJ.js} +7 -7
  165. package/dist/{chunk-MPJDQC4G.js → chunk-WJCXUE2J.js} +2 -2
  166. package/dist/{chunk-BCDECPVO.js → chunk-WV4UYDP6.js} +2 -2
  167. package/dist/{chunk-VWJ56T63.js → chunk-WXJC2L66.js} +2 -2
  168. package/dist/chunk-X2KO4FK5.js +40 -0
  169. package/dist/chunk-X2KO4FK5.js.map +1 -0
  170. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  171. package/dist/{chunk-FPANDKMQ.js → chunk-XC3H7KON.js} +2 -2
  172. package/dist/{chunk-OOOX5C4X.js → chunk-XL2EE5W3.js} +4 -4
  173. package/dist/{chunk-3DKKZVXS.js → chunk-XTXWR6KQ.js} +26 -11
  174. package/dist/chunk-XTXWR6KQ.js.map +1 -0
  175. package/dist/{chunk-3SL52JKD.js → chunk-XVZA6VXC.js} +4 -4
  176. package/dist/{chunk-CZVNOTLR.js → chunk-XXR4A33N.js} +2 -2
  177. package/dist/{chunk-2AV2U2SZ.js → chunk-YDIOMDBW.js} +2 -2
  178. package/dist/{chunk-XTVH2NAW.js → chunk-YECEEJUU.js} +3 -3
  179. package/dist/{chunk-QR6W3FSW.js → chunk-Z5277JWF.js} +3 -3
  180. package/dist/{chunk-CTHYR4QK.js → chunk-ZFNXJEUG.js} +2 -2
  181. package/dist/{chunk-6Q56WDHG.js → chunk-ZLCBLDJO.js} +2 -2
  182. package/dist/{chunk-VY6BD5KB.js → chunk-ZMO2S5RT.js} +12 -10
  183. package/dist/chunk-ZMO2S5RT.js.map +1 -0
  184. package/dist/{chunk-KRACAE73.js → chunk-ZNRTHYYW.js} +6 -6
  185. package/dist/{chunk-7AIPWNM7.js → chunk-ZSMA67FV.js} +2 -2
  186. package/dist/cli.js +123 -120
  187. package/dist/coding/export-okf-codegraph.js +19 -18
  188. package/dist/coding/export-okf-codegraph.js.map +1 -1
  189. package/dist/coding/pre-action-gate.js +2 -2
  190. package/dist/compounding/engine.js +21 -20
  191. package/dist/config.js +32 -30
  192. package/dist/connectors/codex-materialize-runner.js +20 -19
  193. package/dist/connectors/index.js +20 -19
  194. package/dist/consolidation-provenance-check.js +4 -4
  195. package/dist/consolidation-undo.js +2 -2
  196. package/dist/convergence-refresh.js +22 -21
  197. package/dist/corpus-watermark.js +22 -21
  198. package/dist/correction/passive-correction-notifications.d.ts +1 -1
  199. package/dist/correction/passive-correction-notifications.d.ts.map +1 -1
  200. package/dist/dashboard-runtime.js +2 -2
  201. package/dist/deep-recall-http-glue.js +12 -11
  202. package/dist/deep-recall.js +2 -2
  203. package/dist/embedding-fallback.js +3 -2
  204. package/dist/embedding-generation-marker.d.ts +16 -0
  205. package/dist/embedding-generation-marker.d.ts.map +1 -0
  206. package/dist/embedding-generation-marker.js +19 -0
  207. package/dist/embedding-generation-marker.js.map +1 -0
  208. package/dist/embedding-index-storage.d.ts +2 -4
  209. package/dist/embedding-index-storage.d.ts.map +1 -1
  210. package/dist/embedding-index-storage.js +2 -1
  211. package/dist/{entity-canonical-id-migration-2YOLMOFC.js → entity-canonical-id-migration-ZBNRGUNV.js} +3 -3
  212. package/dist/entity-canonical-id-migration-ZBNRGUNV.js.map +1 -0
  213. package/dist/entity-retrieval.js +22 -21
  214. package/dist/event-order-recall.js +2 -2
  215. package/dist/explicit-capture.js +3 -3
  216. package/dist/external-wiki-access.js +74 -71
  217. package/dist/extraction-span-materialize.js +2 -2
  218. package/dist/extraction.js +30 -29
  219. package/dist/{graph-edge-decay-3HVLMT6U.js → graph-edge-decay-IU75NV42.js} +6 -6
  220. package/dist/graph-edge-decay-IU75NV42.js.map +1 -0
  221. package/dist/graph-jsonl.js +4 -4
  222. package/dist/graph-snapshot.js +4 -4
  223. package/dist/graph.js +5 -5
  224. package/dist/harmonic-retrieval.js +20 -19
  225. package/dist/index.d.ts +2 -1
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +230 -218
  228. package/dist/lcm/engine.js +3 -3
  229. package/dist/lcm/index.js +5 -5
  230. package/dist/local-llm.js +2 -2
  231. package/dist/location/index.js +25 -24
  232. package/dist/location/tagging.d.ts +7 -5
  233. package/dist/location/tagging.d.ts.map +1 -1
  234. package/dist/maintenance/memory-governance.js +20 -19
  235. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -19
  236. package/dist/maintenance/rebuild-memory-projection-runner.js +22 -21
  237. package/dist/maintenance/rebuild-memory-projection.js +21 -20
  238. package/dist/memory-browse-operations.js +74 -71
  239. package/dist/memory-browse.js +5 -5
  240. package/dist/namespaces/migrate.js +21 -20
  241. package/dist/namespaces/search.js +6 -6
  242. package/dist/namespaces/storage.js +20 -19
  243. package/dist/offline-sync-changeset-generations.js +5 -4
  244. package/dist/offline-sync-embedding-generation.d.ts +12 -2
  245. package/dist/offline-sync-embedding-generation.d.ts.map +1 -1
  246. package/dist/offline-sync-embedding-generation.js +6 -3
  247. package/dist/offline-sync-file-io.d.ts +15 -0
  248. package/dist/offline-sync-file-io.d.ts.map +1 -1
  249. package/dist/offline-sync-file-io.js +9 -1
  250. package/dist/offline-sync-snapshot-stream.js +9 -8
  251. package/dist/offline-sync-upload-staging.js +2 -2
  252. package/dist/offline-sync.d.ts.map +1 -1
  253. package/dist/offline-sync.js +10 -9
  254. package/dist/operator-doctor-corpus.js +23 -22
  255. package/dist/operator-toolkit.js +45 -43
  256. package/dist/orchestration/maintenance.js +33 -32
  257. package/dist/orchestrator.d.ts +1 -0
  258. package/dist/orchestrator.d.ts.map +1 -1
  259. package/dist/orchestrator.js +170 -167
  260. package/dist/page-versioning.js +2 -2
  261. package/dist/qmd.js +2 -2
  262. package/dist/recall-navigation-operations.js +74 -71
  263. package/dist/recall-navigation.js +73 -70
  264. package/dist/recall-state-view-admission.js +5 -5
  265. package/dist/recall-why-service.js +5 -5
  266. package/dist/resume-bundles.js +33 -31
  267. package/dist/search/factory.js +5 -5
  268. package/dist/search/index.js +9 -9
  269. package/dist/semantic-consolidation.js +25 -24
  270. package/dist/semantic-rule-promotion.js +20 -19
  271. package/dist/semantic-rule-verifier.js +20 -19
  272. package/dist/source-agent-qualifier.js +24 -23
  273. package/dist/standup/build.js +20 -19
  274. package/dist/storage/entity-canonical-id-migration-adapter.d.ts +1 -0
  275. package/dist/storage/entity-canonical-id-migration-adapter.d.ts.map +1 -1
  276. package/dist/storage/entity-canonical-id-migration.d.ts +7 -0
  277. package/dist/storage/entity-canonical-id-migration.d.ts.map +1 -1
  278. package/dist/storage/entity-canonical-id-references.d.ts +2 -0
  279. package/dist/storage/entity-canonical-id-references.d.ts.map +1 -1
  280. package/dist/storage/entity-migration-completion-cache.d.ts +46 -0
  281. package/dist/storage/entity-migration-completion-cache.d.ts.map +1 -0
  282. package/dist/storage/entity-store.d.ts +1 -8
  283. package/dist/storage/entity-store.d.ts.map +1 -1
  284. package/dist/storage/memory-read-store.d.ts +3 -2
  285. package/dist/storage/memory-read-store.d.ts.map +1 -1
  286. package/dist/storage/windowed-memory-read.d.ts +6 -0
  287. package/dist/storage/windowed-memory-read.d.ts.map +1 -1
  288. package/dist/storage.d.ts +2 -0
  289. package/dist/storage.d.ts.map +1 -1
  290. package/dist/storage.js +19 -18
  291. package/dist/summarizer.js +3 -3
  292. package/dist/support-passport/index.js +76 -73
  293. package/dist/transfer/backup.js +2 -2
  294. package/dist/transfer/capsule-export.js +2 -2
  295. package/dist/transfer/capsule-import.js +4 -4
  296. package/dist/transfer/export-okf.js +20 -19
  297. package/dist/verified-recall.js +20 -19
  298. package/dist/who-knows-cli.js +21 -20
  299. package/dist/who-knows.js +20 -19
  300. package/package.json +2 -2
  301. package/src/access-http-briefing.ts +4 -0
  302. package/src/access-http.test.ts +40 -11
  303. package/src/access-http.ts +6 -0
  304. package/src/access-observe-write-category.ts +1 -1
  305. package/src/access-observe-write-surface.ts +24 -19
  306. package/src/access-operations-batch.ts +18 -2
  307. package/src/access-service-peer-capture-backoff.test.ts +236 -0
  308. package/src/access-service.ts +11 -2
  309. package/src/briefing-abort.test.ts +173 -0
  310. package/src/briefing-deadline.test.ts +84 -0
  311. package/src/briefing-deadline.ts +33 -0
  312. package/src/briefing-window-abort.test.ts +47 -0
  313. package/src/briefing-window.ts +33 -4
  314. package/src/briefing.ts +23 -16
  315. package/src/capture-write-lock-scope.test.ts +32 -0
  316. package/src/capture-write-lock-scope.ts +30 -0
  317. package/src/correction/passive-correction-notifications-abort.test.ts +42 -0
  318. package/src/correction/passive-correction-notifications.ts +4 -0
  319. package/src/embedding-generation-marker.ts +51 -0
  320. package/src/embedding-index-storage.ts +13 -29
  321. package/src/entity-lazy-compatibility.test.ts +229 -0
  322. package/src/entity-migration-completion-notification.test.ts +148 -0
  323. package/src/index.ts +2 -2
  324. package/src/location/tagging.ts +14 -12
  325. package/src/offline-sync-embedding-delete-digest.test.ts +58 -0
  326. package/src/offline-sync-embedding-generation-marker.test.ts +245 -0
  327. package/src/offline-sync-embedding-generation.ts +47 -2
  328. package/src/offline-sync-embedding-lock-loss.test.ts +42 -0
  329. package/src/offline-sync-file-io.ts +61 -2
  330. package/src/offline-sync.test.ts +313 -20
  331. package/src/offline-sync.ts +47 -22
  332. package/src/orchestrator-init.test.ts +49 -1
  333. package/src/orchestrator.ts +9 -8
  334. package/src/storage/entity-canonical-id-migration-adapter.ts +16 -1
  335. package/src/storage/entity-canonical-id-migration.ts +17 -8
  336. package/src/storage/entity-canonical-id-references.ts +24 -0
  337. package/src/storage/entity-migration-completion-cache.nofollow.test.ts +223 -0
  338. package/src/storage/entity-migration-completion-cache.ts +153 -0
  339. package/src/storage/entity-store.ts +58 -86
  340. package/src/storage/memory-read-store.ts +9 -3
  341. package/src/storage/windowed-memory-read.ts +8 -0
  342. package/src/storage-startup-nonblocking-migration.test.ts +183 -0
  343. package/src/storage.ts +9 -3
  344. package/src/testing/subjects/entity-migration-completion-cache.test.ts +90 -0
  345. package/dist/chunk-3DKKZVXS.js.map +0 -1
  346. package/dist/chunk-CCTLRRV6.js.map +0 -1
  347. package/dist/chunk-CHSRA76M.js.map +0 -1
  348. package/dist/chunk-FRXS6SC7.js.map +0 -1
  349. package/dist/chunk-FWV2SXSL.js.map +0 -1
  350. package/dist/chunk-L46MG2GE.js.map +0 -1
  351. package/dist/chunk-LWRIGKFM.js.map +0 -1
  352. package/dist/chunk-OXSTKCYK.js.map +0 -1
  353. package/dist/chunk-PKPOO6X5.js.map +0 -1
  354. package/dist/chunk-SUKPM6DV.js.map +0 -1
  355. package/dist/chunk-UXAF4RQM.js.map +0 -1
  356. package/dist/chunk-VY6BD5KB.js.map +0 -1
  357. package/dist/chunk-Y57GJDG5.js.map +0 -1
  358. package/dist/chunk-YBKQAVPZ.js.map +0 -1
  359. package/dist/chunk-YUE7J2HZ.js.map +0 -1
  360. /package/dist/{auto-sync-BNCBMU6H.js.map → auto-sync-UQQ6YJSB.js.map} +0 -0
  361. /package/dist/{capsule-crypto-GWVG7LGC.js.map → briefing-deadline.js.map} +0 -0
  362. /package/dist/{entity-canonical-id-migration-2YOLMOFC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  363. /package/dist/{capsule-merge-RNQUP2VL.js.map → capsule-merge-D4BTIJGK.js.map} +0 -0
  364. /package/dist/{graph-edge-decay-3HVLMT6U.js.map → capture-write-lock-scope.js.map} +0 -0
  365. /package/dist/{chunk-FAKRTS3Y.js.map → chunk-2AKIVSHP.js.map} +0 -0
  366. /package/dist/{chunk-EIYJK7OD.js.map → chunk-2M36PR7R.js.map} +0 -0
  367. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  368. /package/dist/{chunk-6P6MZDBL.js.map → chunk-3NL4ALUP.js.map} +0 -0
  369. /package/dist/{chunk-6CC2YCKG.js.map → chunk-43TUWQNR.js.map} +0 -0
  370. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  371. /package/dist/{chunk-2NLCDNGA.js.map → chunk-564YP3DW.js.map} +0 -0
  372. /package/dist/{chunk-5KGYR5VN.js.map → chunk-5DVOBBTV.js.map} +0 -0
  373. /package/dist/{chunk-RT2GN6KQ.js.map → chunk-5FUDSK6Z.js.map} +0 -0
  374. /package/dist/{chunk-B4XSXFKI.js.map → chunk-5G53JSRO.js.map} +0 -0
  375. /package/dist/{chunk-BGK3SFCC.js.map → chunk-5X2H3X4J.js.map} +0 -0
  376. /package/dist/{chunk-4J7BNL7B.js.map → chunk-6ZQX3R66.js.map} +0 -0
  377. /package/dist/{chunk-JNLYHJG7.js.map → chunk-74AY3S7U.js.map} +0 -0
  378. /package/dist/{chunk-RUAIO5UA.js.map → chunk-7J6A5MV2.js.map} +0 -0
  379. /package/dist/{chunk-A45QXZMU.js.map → chunk-7SAE6T36.js.map} +0 -0
  380. /package/dist/{chunk-QE2XQM3S.js.map → chunk-7YVER7ZO.js.map} +0 -0
  381. /package/dist/{chunk-IJH6METJ.js.map → chunk-A3UFV4K3.js.map} +0 -0
  382. /package/dist/{chunk-LXN737FX.js.map → chunk-AAANTXCQ.js.map} +0 -0
  383. /package/dist/{chunk-I3IPXGFQ.js.map → chunk-AJJPXUE6.js.map} +0 -0
  384. /package/dist/{chunk-2DPJFJFH.js.map → chunk-BNJAVRNJ.js.map} +0 -0
  385. /package/dist/{chunk-YL564QS2.js.map → chunk-CCL2OXFO.js.map} +0 -0
  386. /package/dist/{chunk-ZX5N5D6M.js.map → chunk-DAOMWGO2.js.map} +0 -0
  387. /package/dist/{chunk-23ZMQONL.js.map → chunk-ERMT5WQM.js.map} +0 -0
  388. /package/dist/{chunk-VTCNVGM6.js.map → chunk-EWBXMGXR.js.map} +0 -0
  389. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  390. /package/dist/{chunk-XTN54NML.js.map → chunk-G3T6YMBO.js.map} +0 -0
  391. /package/dist/{chunk-2JNBPOZ2.js.map → chunk-GDYLZ6FR.js.map} +0 -0
  392. /package/dist/{chunk-DZPTR2XQ.js.map → chunk-GFAEKSFR.js.map} +0 -0
  393. /package/dist/{chunk-M7LA4QBY.js.map → chunk-GXVXBYRA.js.map} +0 -0
  394. /package/dist/{chunk-JGMJZBFV.js.map → chunk-HPNXS6W6.js.map} +0 -0
  395. /package/dist/{chunk-BFPF5N3H.js.map → chunk-HXFIILGF.js.map} +0 -0
  396. /package/dist/{chunk-CMNL3QSL.js.map → chunk-IN6U2BS5.js.map} +0 -0
  397. /package/dist/{chunk-BWRX322G.js.map → chunk-IUYXCTA7.js.map} +0 -0
  398. /package/dist/{chunk-LU57EEJW.js.map → chunk-JBWD3J5U.js.map} +0 -0
  399. /package/dist/{chunk-2V664H6O.js.map → chunk-JS44PBCM.js.map} +0 -0
  400. /package/dist/{chunk-XKTHJHSQ.js.map → chunk-JZCBMKR5.js.map} +0 -0
  401. /package/dist/{chunk-MXPVNDGO.js.map → chunk-K5QQXVG6.js.map} +0 -0
  402. /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
  403. /package/dist/{chunk-WUBGMV3G.js.map → chunk-KRAMCBIW.js.map} +0 -0
  404. /package/dist/{chunk-NDMPRT3Z.js.map → chunk-KW5YZELC.js.map} +0 -0
  405. /package/dist/{chunk-2RQR4TXV.js.map → chunk-LIBVANOF.js.map} +0 -0
  406. /package/dist/{chunk-M7L5A2AT.js.map → chunk-LMMBV7QC.js.map} +0 -0
  407. /package/dist/{chunk-PJBYVJKO.js.map → chunk-LXXIV7Y2.js.map} +0 -0
  408. /package/dist/{chunk-MZCTS3ZG.js.map → chunk-M2DOJBH7.js.map} +0 -0
  409. /package/dist/{chunk-WOPZ2P5Z.js.map → chunk-M4HRYLOA.js.map} +0 -0
  410. /package/dist/{chunk-JCYEDMOY.js.map → chunk-M6TP6UYD.js.map} +0 -0
  411. /package/dist/{chunk-KAUCVTAW.js.map → chunk-MLNXATFG.js.map} +0 -0
  412. /package/dist/{chunk-2NX7JTST.js.map → chunk-MPWOAIIH.js.map} +0 -0
  413. /package/dist/{chunk-RQLTCHUL.js.map → chunk-MWURUSRE.js.map} +0 -0
  414. /package/dist/{chunk-3EGL7XZO.js.map → chunk-N74WJ7B2.js.map} +0 -0
  415. /package/dist/{chunk-DRXTMDAG.js.map → chunk-N7FNCE7F.js.map} +0 -0
  416. /package/dist/{chunk-6CWAY4R7.js.map → chunk-NPLM7AFU.js.map} +0 -0
  417. /package/dist/{chunk-VECRSYQG.js.map → chunk-NRGQ5HJO.js.map} +0 -0
  418. /package/dist/{chunk-PPDNG7WC.js.map → chunk-NSP5PIQH.js.map} +0 -0
  419. /package/dist/{chunk-6HVLHIAB.js.map → chunk-P7OVFGMU.js.map} +0 -0
  420. /package/dist/{chunk-EX27VKU6.js.map → chunk-PDTNQLPC.js.map} +0 -0
  421. /package/dist/{chunk-ME5LVISI.js.map → chunk-PJCVI44H.js.map} +0 -0
  422. /package/dist/{chunk-LJCHDFLP.js.map → chunk-Q3PNQYAC.js.map} +0 -0
  423. /package/dist/{chunk-YFJHFEET.js.map → chunk-QS765SBL.js.map} +0 -0
  424. /package/dist/{chunk-FJ264QBP.js.map → chunk-QTDZ2NYC.js.map} +0 -0
  425. /package/dist/{chunk-Q6UZREJK.js.map → chunk-RKRX4C4H.js.map} +0 -0
  426. /package/dist/{chunk-GLZWOIU3.js.map → chunk-SJTSWG2Z.js.map} +0 -0
  427. /package/dist/{chunk-XM747YJF.js.map → chunk-TFWJXWL6.js.map} +0 -0
  428. /package/dist/{chunk-QFQ4UG3I.js.map → chunk-TXKQBQYW.js.map} +0 -0
  429. /package/dist/{chunk-BMOJFGVX.js.map → chunk-UALXWWGN.js.map} +0 -0
  430. /package/dist/{chunk-B44NFHWI.js.map → chunk-UNOAVHV4.js.map} +0 -0
  431. /package/dist/{chunk-TF5NZGPS.js.map → chunk-UQBOPCDK.js.map} +0 -0
  432. /package/dist/{chunk-5R2IF2KI.js.map → chunk-UXGXJUZ5.js.map} +0 -0
  433. /package/dist/{chunk-J6B6FK2X.js.map → chunk-VCOMACR6.js.map} +0 -0
  434. /package/dist/{chunk-WCGJK4NQ.js.map → chunk-VKQI47DJ.js.map} +0 -0
  435. /package/dist/{chunk-MPJDQC4G.js.map → chunk-WJCXUE2J.js.map} +0 -0
  436. /package/dist/{chunk-BCDECPVO.js.map → chunk-WV4UYDP6.js.map} +0 -0
  437. /package/dist/{chunk-VWJ56T63.js.map → chunk-WXJC2L66.js.map} +0 -0
  438. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  439. /package/dist/{chunk-FPANDKMQ.js.map → chunk-XC3H7KON.js.map} +0 -0
  440. /package/dist/{chunk-OOOX5C4X.js.map → chunk-XL2EE5W3.js.map} +0 -0
  441. /package/dist/{chunk-3SL52JKD.js.map → chunk-XVZA6VXC.js.map} +0 -0
  442. /package/dist/{chunk-CZVNOTLR.js.map → chunk-XXR4A33N.js.map} +0 -0
  443. /package/dist/{chunk-2AV2U2SZ.js.map → chunk-YDIOMDBW.js.map} +0 -0
  444. /package/dist/{chunk-XTVH2NAW.js.map → chunk-YECEEJUU.js.map} +0 -0
  445. /package/dist/{chunk-QR6W3FSW.js.map → chunk-Z5277JWF.js.map} +0 -0
  446. /package/dist/{chunk-CTHYR4QK.js.map → chunk-ZFNXJEUG.js.map} +0 -0
  447. /package/dist/{chunk-6Q56WDHG.js.map → chunk-ZLCBLDJO.js.map} +0 -0
  448. /package/dist/{chunk-KRACAE73.js.map → chunk-ZNRTHYYW.js.map} +0 -0
  449. /package/dist/{chunk-7AIPWNM7.js.map → chunk-ZSMA67FV.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { createHash } from "node:crypto";
3
- import { chmod, mkdir, mkdtemp, readdir, readFile, rm, stat, utimes, writeFile } from "node:fs/promises";
3
+ import { chmod, mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, utimes, writeFile } from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import test from "node:test";
@@ -3714,33 +3714,37 @@ test("applyOfflineSyncSnapshot consumes readIncomingFile for records without inl
3714
3714
  }
3715
3715
  });
3716
3716
 
3717
- test("buildOfflineSyncSnapshot skips a file that vanishes during the walk", async () => {
3717
+ test("buildOfflineSyncSnapshot fails closed when a file vanishes during digest", async () => {
3718
3718
  const root = await tempDir("remnic-offline-vanish");
3719
3719
  try {
3720
3720
  await write(root, "facts/keep.md", "keep");
3721
3721
  await write(root, "facts/gone.md", "gone");
3722
- const snapshot = await buildOfflineSyncSnapshot({
3723
- root,
3724
- sourceId: "local",
3725
- includeContent: false,
3726
- readFileDigest: async ({ path: relPath, filePath }) => {
3727
- if (relPath.endsWith("gone.md")) {
3728
- await rm(filePath);
3729
- throw Object.assign(new Error("ENOENT: vanished"), { code: "ENOENT" });
3730
- }
3731
- const content = await readFile(filePath);
3732
- return {
3733
- sha256: createHash("sha256").update(content).digest("hex"),
3734
- bytes: content.length,
3735
- };
3736
- },
3737
- });
3738
- assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/keep.md"]);
3722
+ await assert.rejects(
3723
+ () => buildOfflineSyncSnapshot({
3724
+ root,
3725
+ sourceId: "local",
3726
+ includeContent: false,
3727
+ readFileDigest: async ({ path: relPath, filePath }) => {
3728
+ if (relPath.endsWith("gone.md")) {
3729
+ await rm(filePath);
3730
+ throw Object.assign(new Error("ENOENT: vanished"), { code: "ENOENT" });
3731
+ }
3732
+ const content = await readFile(filePath);
3733
+ return {
3734
+ sha256: createHash("sha256").update(content).digest("hex"),
3735
+ bytes: content.length,
3736
+ };
3737
+ },
3738
+ }),
3739
+ /census race|vanished during digest|retry the snapshot/i,
3740
+ );
3741
+ assert.equal(await readUtf8(root, "facts/keep.md"), "keep");
3739
3742
  } finally {
3740
3743
  await rm(root, { recursive: true, force: true });
3741
3744
  }
3742
3745
  });
3743
3746
 
3747
+
3744
3748
  test("base snapshot fails when a base-known file vanishes before the fast-base stat", async () => {
3745
3749
  const root = await tempDir("remnic-offline-base-vanish");
3746
3750
  try {
@@ -3770,7 +3774,7 @@ test("base snapshot fails when a base-known file vanishes before the fast-base s
3770
3774
  }
3771
3775
  });
3772
3776
 
3773
- test("base snapshot omits a brand-new file that vanishes during the walk", async () => {
3777
+ test("base snapshot omits a brand-new file that vanishes before the fast-base stat", async () => {
3774
3778
  const root = await tempDir("remnic-offline-new-vanish");
3775
3779
  try {
3776
3780
  await write(root, "facts/keep.md", "keep");
@@ -3798,6 +3802,7 @@ test("base snapshot omits a brand-new file that vanishes during the walk", async
3798
3802
  }
3799
3803
  });
3800
3804
 
3805
+
3801
3806
  test("snapshot fails when an embedding generation member vanishes during the walk", async () => {
3802
3807
  const root = await tempDir("remnic-offline-shard-vanish");
3803
3808
  try {
@@ -3822,3 +3827,291 @@ test("snapshot fails when an embedding generation member vanishes during the wal
3822
3827
  await rm(root, { recursive: true, force: true });
3823
3828
  }
3824
3829
  });
3830
+
3831
+ test("a transient snapshot omission must not emit a delete for a shared-base file", async () => {
3832
+ const root = await tempDir("remnic-offline-uncertain-delete");
3833
+ try {
3834
+ await write(root, "facts/keep.md", "keep");
3835
+ await write(root, "facts/race.md", "race-v1");
3836
+ const base = await buildOfflineSyncSnapshot({
3837
+ root,
3838
+ sourceId: "local",
3839
+ includeContent: false,
3840
+ });
3841
+ // Live census fail-closes on digest ENOENT. A stale/incomplete currentFiles
3842
+ // list is still possible from an older client cache — that path must not
3843
+ // emit a confirmed delete when the file is back on disk.
3844
+ const incompleteFiles = base.files.filter((file) => file.path !== "facts/race.md");
3845
+ assert.deepEqual(
3846
+ incompleteFiles.map((file) => file.path),
3847
+ ["facts/keep.md"],
3848
+ "fixture: the race path is absent from the incomplete currentFiles view",
3849
+ );
3850
+ await write(root, "facts/race.md", "race-v1");
3851
+ const changeset = await buildOfflineSyncChangesetFromSnapshot({
3852
+ root,
3853
+ sourceId: "local",
3854
+ currentFiles: incompleteFiles,
3855
+ baseFiles: base.files,
3856
+ });
3857
+ assert.equal(
3858
+ changeset.changes.filter((change) => change.path === "facts/race.md" && change.type === "delete").length,
3859
+ 0,
3860
+ "an unreadable/recreated path must not become a confirmed delete against the shared base",
3861
+ );
3862
+ } finally {
3863
+ await rm(root, { recursive: true, force: true });
3864
+ }
3865
+ });
3866
+
3867
+ test("apply must not overwrite a recreated local file omitted from the local census", async () => {
3868
+ const root = await tempDir("remnic-offline-uncertain-overwrite");
3869
+ try {
3870
+ const recreated = "recreated local bytes";
3871
+ const incoming = "incoming remote bytes";
3872
+ await write(root, "facts/race.md", recreated);
3873
+ const incomingSha = createHash("sha256").update(incoming).digest("hex");
3874
+ // Local census omitted the recreated path (stale/racy currentFiles).
3875
+ // Incoming is base-less, so the apply path would treat it as a pure create.
3876
+ const result = await applyOfflineSyncSnapshot({
3877
+ root,
3878
+ currentFiles: [],
3879
+ baseFiles: [],
3880
+ snapshot: {
3881
+ format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
3882
+ schemaVersion: 1,
3883
+ createdAt: new Date().toISOString(),
3884
+ sourceId: "remote",
3885
+ includeTranscripts: false,
3886
+ files: [{
3887
+ path: "facts/race.md",
3888
+ sha256: incomingSha,
3889
+ bytes: Buffer.byteLength(incoming),
3890
+ mtimeMs: 2,
3891
+ contentBase64: Buffer.from(incoming).toString("base64"),
3892
+ }],
3893
+ },
3894
+ });
3895
+ assert.equal(
3896
+ await readUtf8(root, "facts/race.md"),
3897
+ recreated,
3898
+ "recreated local content must survive when the local census omitted it",
3899
+ );
3900
+ assert.equal(result.upserted, 0, "must not publish over an uncertain local path");
3901
+ } finally {
3902
+ await rm(root, { recursive: true, force: true });
3903
+ }
3904
+ });
3905
+
3906
+ test("changeset apply must not overwrite a recreated local file omitted by controlled digest census", async () => {
3907
+ // Public applyOfflineSyncChangeset builds local currentFiles via
3908
+ // buildOfflineSyncSnapshotForPaths when currentFiles is omitted. A controlled
3909
+ // digest that recreates local bytes then throws ENOENT must fail closed at
3910
+ // the shared census boundary — never export confirmed absence and overwrite.
3911
+ const root = await tempDir("remnic-offline-changeset-uncertain-overwrite");
3912
+ try {
3913
+ const recreated = "recreated local bytes";
3914
+ const incoming = "incoming remote bytes";
3915
+ const incomingSha = createHash("sha256").update(incoming).digest("hex");
3916
+ await write(root, "facts/race.md", recreated);
3917
+
3918
+ await assert.rejects(
3919
+ () => applyOfflineSyncChangeset({
3920
+ root,
3921
+ // No currentFiles: exercise the public snapshotForPaths census path.
3922
+ returnCurrentFiles: false,
3923
+ writeConflictCopies: false,
3924
+ readFileDigest: async ({ path: relPath, filePath }) => {
3925
+ if (relPath === "facts/race.md") {
3926
+ await writeFile(filePath, recreated);
3927
+ throw Object.assign(new Error("ENOENT: vanished during census"), { code: "ENOENT" });
3928
+ }
3929
+ const content = await readFile(filePath);
3930
+ return {
3931
+ sha256: createHash("sha256").update(content).digest("hex"),
3932
+ bytes: content.length,
3933
+ };
3934
+ },
3935
+ changeset: {
3936
+ format: "remnic.offline-sync.changeset.v1",
3937
+ schemaVersion: 1,
3938
+ createdAt: new Date().toISOString(),
3939
+ sourceId: "remote",
3940
+ includeTranscripts: false,
3941
+ changes: [{
3942
+ type: "upsert",
3943
+ path: "facts/race.md",
3944
+ file: {
3945
+ path: "facts/race.md",
3946
+ sha256: incomingSha,
3947
+ bytes: Buffer.byteLength(incoming),
3948
+ mtimeMs: 2,
3949
+ contentBase64: Buffer.from(incoming).toString("base64"),
3950
+ },
3951
+ }],
3952
+ },
3953
+ }),
3954
+ /census race|vanished during digest|retry the snapshot/i,
3955
+ );
3956
+
3957
+ assert.equal(
3958
+ await readUtf8(root, "facts/race.md"),
3959
+ recreated,
3960
+ "recreated local content must survive a fail-closed public changeset census race",
3961
+ );
3962
+ } finally {
3963
+ await rm(root, { recursive: true, force: true });
3964
+ }
3965
+ });
3966
+
3967
+ test("sender snapshot omission from digest race must not delete receiver shared-base file", async () => {
3968
+ // Root premise: sender digest race must not export an apparently-complete
3969
+ // snapshot that omits the path. Fail closed at the shared census boundary so
3970
+ // no consumer can assign confirmed absence. Sender and receiver bytes stay.
3971
+ const senderRoot = await tempDir("remnic-offline-sender-uncertain-omit");
3972
+ const receiverRoot = await tempDir("remnic-offline-receiver-shared-base");
3973
+ try {
3974
+ const good = "unchanged good fact bytes";
3975
+ const keep = "keep peer";
3976
+ await write(senderRoot, "facts/race.md", good);
3977
+ await write(senderRoot, "facts/keep.md", keep);
3978
+ await write(receiverRoot, "facts/race.md", good);
3979
+ await write(receiverRoot, "facts/keep.md", keep);
3980
+
3981
+ const sharedBase = await buildOfflineSyncSnapshot({
3982
+ root: receiverRoot,
3983
+ sourceId: "shared",
3984
+ includeContent: false,
3985
+ });
3986
+ assert.ok(
3987
+ sharedBase.files.some((file) => file.path === "facts/race.md"),
3988
+ "fixture: shared base includes the good race path",
3989
+ );
3990
+
3991
+ await assert.rejects(
3992
+ () => buildOfflineSyncSnapshot({
3993
+ root: senderRoot,
3994
+ sourceId: "sender",
3995
+ includeContent: false,
3996
+ readFileDigest: async ({ path: relPath, filePath }) => {
3997
+ if (relPath === "facts/race.md") {
3998
+ await writeFile(filePath, good);
3999
+ throw Object.assign(new Error("ENOENT: vanished during sender census"), { code: "ENOENT" });
4000
+ }
4001
+ const content = await readFile(filePath);
4002
+ return {
4003
+ sha256: createHash("sha256").update(content).digest("hex"),
4004
+ bytes: content.length,
4005
+ };
4006
+ },
4007
+ }),
4008
+ /census race|vanished during digest|retry the snapshot/i,
4009
+ );
4010
+
4011
+ assert.equal(await readUtf8(senderRoot, "facts/race.md"), good, "sender bytes preserved");
4012
+ assert.equal(await readUtf8(receiverRoot, "facts/race.md"), good, "receiver bytes preserved without a false snapshot");
4013
+ assert.equal(await readUtf8(receiverRoot, "facts/keep.md"), keep);
4014
+ } finally {
4015
+ await rm(senderRoot, { recursive: true, force: true });
4016
+ await rm(receiverRoot, { recursive: true, force: true });
4017
+ }
4018
+ });
4019
+
4020
+
4021
+ test("a file vanishing during the changeset upsert read keeps the retryable changed-while-building message", async () => {
4022
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-offline-changeset-vanish-"));
4023
+ try {
4024
+ const rel = "facts/volatile.md";
4025
+ const bytes = Buffer.from("volatile bytes\n");
4026
+ await mkdir(path.join(root, "facts"), { recursive: true });
4027
+ await writeFile(path.join(root, rel), bytes);
4028
+ const currentFile = {
4029
+ path: rel,
4030
+ sha256: createHash("sha256").update(bytes).digest("hex"),
4031
+ bytes: bytes.byteLength,
4032
+ mtimeMs: 1,
4033
+ };
4034
+ await assert.rejects(
4035
+ buildOfflineSyncChangesetFromSnapshot({
4036
+ root,
4037
+ sourceId: "laptop",
4038
+ baseFiles: [],
4039
+ currentFiles: [currentFile],
4040
+ readFile: async () => {
4041
+ await rm(path.join(root, rel), { force: true });
4042
+ throw Object.assign(new Error("ENOENT: file vanished"), { code: "ENOENT" });
4043
+ },
4044
+ }),
4045
+ (error: Error) => {
4046
+ // The CLI retry loop matches this exact prefix to defer the path and rebuild the census.
4047
+ assert.equal(error.message, `offline sync file changed while building changeset: ${rel}`);
4048
+ return true;
4049
+ },
4050
+ );
4051
+ } finally {
4052
+ await rm(root, { recursive: true, force: true });
4053
+ }
4054
+ });
4055
+
4056
+ test("a tracked file replaced by a symlink is a local delete, not a push-blocking error", async () => {
4057
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-offline-symlink-replaced-"));
4058
+ const outside = await mkdtemp(path.join(os.tmpdir(), "remnic-offline-symlink-target-"));
4059
+ try {
4060
+ const rel = "facts/tracked.md";
4061
+ const bytes = Buffer.from("tracked bytes\n");
4062
+ await mkdir(path.join(root, "facts"), { recursive: true });
4063
+ await writeFile(path.join(outside, "target.md"), "outside\n");
4064
+ await symlink(path.join(outside, "target.md"), path.join(root, rel));
4065
+ const baseFile = {
4066
+ path: rel,
4067
+ sha256: createHash("sha256").update(bytes).digest("hex"),
4068
+ bytes: bytes.byteLength,
4069
+ mtimeMs: 1,
4070
+ };
4071
+ const changeset = await buildOfflineSyncChangesetFromSnapshot({
4072
+ root,
4073
+ sourceId: "laptop",
4074
+ baseFiles: [baseFile],
4075
+ currentFiles: [],
4076
+ });
4077
+ assert.deepEqual(
4078
+ changeset.changes.map((change) => [change.type, change.path]),
4079
+ [["delete", rel]],
4080
+ "a symlink is not a regular local file: it reads as a delete, as before the census change",
4081
+ );
4082
+ } finally {
4083
+ await rm(root, { recursive: true, force: true });
4084
+ await rm(outside, { recursive: true, force: true });
4085
+ }
4086
+ });
4087
+
4088
+ test("a tracked file under a directory replaced by an in-root symlink is a local delete", async () => {
4089
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-offline-symlink-parent-"));
4090
+ try {
4091
+ const rel = "facts/tracked.md";
4092
+ const bytes = Buffer.from("tracked bytes\n");
4093
+ await mkdir(path.join(root, "elsewhere"), { recursive: true });
4094
+ await writeFile(path.join(root, "elsewhere", "tracked.md"), bytes);
4095
+ // The census skips a symlinked directory, so facts/tracked.md is not in it.
4096
+ await symlink(path.join(root, "elsewhere"), path.join(root, "facts"));
4097
+ const baseFile = {
4098
+ path: rel,
4099
+ sha256: createHash("sha256").update(bytes).digest("hex"),
4100
+ bytes: bytes.byteLength,
4101
+ mtimeMs: 1,
4102
+ };
4103
+ const changeset = await buildOfflineSyncChangesetFromSnapshot({
4104
+ root,
4105
+ sourceId: "laptop",
4106
+ baseFiles: [baseFile],
4107
+ currentFiles: [],
4108
+ });
4109
+ assert.deepEqual(
4110
+ changeset.changes.map((change) => [change.type, change.path]),
4111
+ [["delete", rel]],
4112
+ "a path reached only through a symlinked parent is absent, as the census sees it",
4113
+ );
4114
+ } finally {
4115
+ await rm(root, { recursive: true, force: true });
4116
+ }
4117
+ });
@@ -32,6 +32,7 @@ import { isMacOsReplicationJunkBasename } from "./utils/macos-replication-junk.j
32
32
  import {
33
33
  applyIncomingEmbeddingGenerations,
34
34
  assertEmbeddingGenerationStillIncluded,
35
+ advanceEmbeddingGenerationMarker,
35
36
  computeOmittedEmbeddingGenerationPaths,
36
37
  divergedEmbeddingGenerationDeferrals,
37
38
  embeddingGenerationMembership,
@@ -69,6 +70,10 @@ import {
69
70
  setSafeFileMtime,
70
71
  assertNonNegativeFinite,
71
72
  assertOfflineSyncMtimeMs,
73
+ offlineSyncRegularFilePresent,
74
+ OfflineSyncCensusRaceError,
75
+ censusRaceAsChangedWhileBuilding,
76
+ changedWhileBuildingChangesetError,
72
77
  OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS,
73
78
  } from "./offline-sync-file-io.js";
74
79
  import { CENSUS_MAX_MTIME_MS, isSha256Hex } from "./census-validation.js";
@@ -792,19 +797,29 @@ async function canReuseFastBaseFileStateFromDisk(
792
797
 
793
798
  async function readOfflineSyncFileRecord(
794
799
  options: OfflineSyncFileRecordOptions,
795
- ): Promise<OfflineSyncFileRecord | null> {
800
+ ): Promise<OfflineSyncFileRecord> {
796
801
  try {
797
802
  return await readPresentOfflineSyncFileRecord(options);
798
803
  } catch (error) {
799
- // A file removed between readdir and stat/hash is a race, not a corrupt corpus.
800
- if (isNotFoundError(error)) return null;
804
+ // Caller already observed the path (readdir/lstat). Digest ENOENT is an
805
+ // ambiguous race: exporting a silent omission assigns confirmed absence to
806
+ // every consumer (full snapshot apply + changeset apply). Fail closed.
807
+ if (isNotFoundError(error)) {
808
+ if (embeddingGenerationMembership(options.relPath) !== null) {
809
+ throw new Error(
810
+ `offline sync embedding generation member vanished during snapshot walk: ${options.relPath}`,
811
+ );
812
+ }
813
+ throw new OfflineSyncCensusRaceError(options.relPath, error);
814
+ }
801
815
  throw error;
802
816
  }
803
817
  }
804
818
 
805
- // A vanished path may only be omitted when nothing can misread the gap:
806
- // embedding generations travel and replace as one unit (issue #3148), and a
807
- // base-known file omitted here would fabricate a delete in
819
+
820
+ // Pre-stat vanish (readdir then lstat/stat ENOENT) may omit only when nothing
821
+ // can misread the gap: embedding generations travel as one unit (issue #3148),
822
+ // and a base-known file omitted here would fabricate a delete in
808
823
  // buildOfflineSyncChangesetFromSnapshot.
809
824
  function assertSnapshotVanishIsSafe(relPath: string, base: ReadonlyMap<string, unknown> | undefined): void {
810
825
  if (embeddingGenerationMembership(relPath) !== null) {
@@ -815,6 +830,7 @@ function assertSnapshotVanishIsSafe(relPath: string, base: ReadonlyMap<string, u
815
830
  }
816
831
  }
817
832
 
833
+
818
834
  async function readPresentOfflineSyncFileRecord(
819
835
  options: OfflineSyncFileRecordOptions,
820
836
  ): Promise<OfflineSyncFileRecord> {
@@ -922,7 +938,7 @@ export async function* iterateOfflineSyncSnapshotFileRecords(options: {
922
938
  path: relPosix,
923
939
  filePath: abs,
924
940
  })) continue;
925
- const record = await readOfflineSyncFileRecord({
941
+ yield await readOfflineSyncFileRecord({
926
942
  root,
927
943
  relPath: relPosix,
928
944
  filePath: abs,
@@ -931,8 +947,7 @@ export async function* iterateOfflineSyncSnapshotFileRecords(options: {
931
947
  readFileDigest: options.readFileDigest,
932
948
  signal: options.signal,
933
949
  });
934
- if (record) yield record;
935
- else assertSnapshotVanishIsSafe(relPosix, undefined);
950
+
936
951
  }
937
952
  }
938
953
 
@@ -1093,8 +1108,7 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
1093
1108
  try {
1094
1109
  st = await stat(abs);
1095
1110
  } catch (error) {
1096
- // Same race readOfflineSyncFileRecord tolerates, unless omitting the
1097
- // path could fabricate a delete or downgrade a generation.
1111
+ // Pre-stat vanish after readdir: omit only when assertSnapshotVanishIsSafe allows.
1098
1112
  if (isNotFoundError(error)) {
1099
1113
  assertSnapshotVanishIsSafe(relPosix, base);
1100
1114
  continue;
@@ -1111,7 +1125,7 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
1111
1125
  files.push(baseEntry);
1112
1126
  continue;
1113
1127
  }
1114
- const record = await readOfflineSyncFileRecord({
1128
+ files.push(await readOfflineSyncFileRecord({
1115
1129
  root,
1116
1130
  relPath: relPosix,
1117
1131
  filePath: abs,
@@ -1119,9 +1133,7 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
1119
1133
  readFile: options.readFile,
1120
1134
  readFileDigest: options.readFileDigest,
1121
1135
  signal: options.signal,
1122
- });
1123
- if (record) files.push(record);
1124
- else assertSnapshotVanishIsSafe(relPosix, base);
1136
+ }));
1125
1137
  }
1126
1138
  }
1127
1139
 
@@ -1207,7 +1219,7 @@ export async function buildOfflineSyncSnapshotForPaths(options: {
1207
1219
  })) {
1208
1220
  throw new Error(`offline sync snapshot path is excluded: ${relPath}`);
1209
1221
  }
1210
- const record = await readOfflineSyncFileRecord({
1222
+ files.push(await readOfflineSyncFileRecord({
1211
1223
  root,
1212
1224
  relPath,
1213
1225
  filePath,
@@ -1215,9 +1227,7 @@ export async function buildOfflineSyncSnapshotForPaths(options: {
1215
1227
  readFile: options.readFile,
1216
1228
  readFileDigest: options.readFileDigest,
1217
1229
  signal: options.signal,
1218
- });
1219
- if (record) files.push(record);
1220
- else assertSnapshotVanishIsSafe(relPath, undefined);
1230
+ }));
1221
1231
  }
1222
1232
  throwIfOfflineSyncAborted(options.signal);
1223
1233
 
@@ -1413,6 +1423,11 @@ export async function buildOfflineSyncChangesetFromSnapshot(options: {
1413
1423
  excludeNodeLocalState?: boolean;
1414
1424
  readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
1415
1425
  }): Promise<OfflineSyncChangeset> {
1426
+ const root = await prepareSafeArchiveRoot(
1427
+ path.resolve(options.root),
1428
+ "buildOfflineSyncChangesetFromSnapshot",
1429
+ "root",
1430
+ );
1416
1431
  const includeTranscripts = options.includeTranscripts !== false;
1417
1432
  const excludedPaths = new Set(
1418
1433
  (options.excludePaths ?? []).map((relPath) => normalizeRelativePath(relPath, "excludePaths[]")),
@@ -1452,10 +1467,10 @@ export async function buildOfflineSyncChangesetFromSnapshot(options: {
1452
1467
  now: options.now,
1453
1468
  userExcludeRegexps: options.userExcludeRegexps,
1454
1469
  readFile: options.readFile,
1455
- });
1470
+ }).catch(censusRaceAsChangedWhileBuilding(relPath));
1456
1471
  const record = file.files[0];
1457
1472
  if (!record || typeof record.contentBase64 !== "string" || record.sha256 !== currentEntry.sha256) {
1458
- throw new Error(`offline sync file changed while building changeset: ${relPath}`);
1473
+ throw changedWhileBuildingChangesetError(relPath);
1459
1474
  }
1460
1475
  changes.push({
1461
1476
  type: "upsert",
@@ -1466,6 +1481,8 @@ export async function buildOfflineSyncChangesetFromSnapshot(options: {
1466
1481
  continue;
1467
1482
  }
1468
1483
  if (!currentEntry && baseEntry) {
1484
+ // Census miss is not confirmed delete — recheck disk (transient ENOENT / recreate).
1485
+ if (await offlineSyncRegularFilePresent(root, relPath)) continue;
1469
1486
  const mtimeMs = deletionMtimeByPath?.get(relPath);
1470
1487
  changes.push({
1471
1488
  type: "delete",
@@ -1807,6 +1824,12 @@ export async function applyOfflineSyncSnapshot(options: {
1807
1824
  continue;
1808
1825
  }
1809
1826
  if (!currentEntry && !base) {
1827
+ // Census miss with on-disk file: uncertain local presence, do not overwrite.
1828
+ if (await offlineSyncRegularFilePresent(root, relPath)) {
1829
+ pendingLocal += 1;
1830
+ skipped += 1;
1831
+ continue;
1832
+ }
1810
1833
  await writeSafeFile(root, relPath, requiredBuffer(incomingBuffers, relPath), options.writeFile, incoming.mtimeMs);
1811
1834
  nextBase.set(relPath, toFileState(incoming));
1812
1835
  upserted += 1;
@@ -2466,7 +2489,9 @@ export async function applyOfflineSyncFileContentChunk(options: {
2466
2489
  };
2467
2490
  }
2468
2491
 
2469
- await writeSafeFileFromUpload(root, relPath, upload, options.readFile, options.writeFileChunks, mtimeMs);
2492
+ await advanceEmbeddingGenerationMarker(root.abs, embeddingGenerationMembership(relPath), () =>
2493
+ writeSafeFileFromUpload(root, relPath, upload, options.readFile, options.writeFileChunks, mtimeMs),
2494
+ );
2470
2495
  return {
2471
2496
  ...baseResult,
2472
2497
  applied: true,
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdir, mkdtemp, rm } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
4
  import os from "node:os";
4
5
  import path from "node:path";
5
6
  import test from "node:test";
@@ -1547,3 +1548,50 @@ test("startupSearchSync composes its caller abort into the QMD probe", async ()
1547
1548
  await rm(memoryDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
1548
1549
  }
1549
1550
  });
1551
+
1552
+ // Both startup call sites must open read readiness without migration replay.
1553
+ for (const namespace of ["default", "shared"]) {
1554
+ test(`startup defers entity migration for ${namespace} storage`, async () => {
1555
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-init-migration-defer-"));
1556
+ const orchestrator = new Orchestrator(
1557
+ parseConfig({
1558
+ memoryDir,
1559
+ workspaceDir: path.join(memoryDir, "workspace"),
1560
+ qmdEnabled: false,
1561
+ namespacesEnabled: namespace === "shared",
1562
+ namespaceCatalogEnabled: false,
1563
+ defaultNamespace: "default",
1564
+ sharedNamespace: "shared",
1565
+ namespacePolicies: [],
1566
+ embeddingFallbackEnabled: false,
1567
+ identityContinuityEnabled: false,
1568
+ transcriptEnabled: false,
1569
+ hourlySummariesEnabled: false,
1570
+ compoundingEnabled: false,
1571
+ knowledgeIndexEnabled: false,
1572
+ }),
1573
+ );
1574
+ const storage = await orchestrator.getStorage(namespace);
1575
+ const lockPath = path.join(storage.dir, "state", "entity-canonical-id-migration.lock");
1576
+ const journalPath = path.join(storage.dir, "state", "entity-canonical-id-migration-v1.json");
1577
+ try {
1578
+ await mkdir(path.dirname(lockPath), { recursive: true });
1579
+ await writeFile(lockPath, `${process.pid} init-migration-test ${new Date().toISOString()}\n`);
1580
+ const outcome = await withTestDeadline(
1581
+ orchestrator.initialize().then(() => "settled" as const, () => "rejected" as const),
1582
+ );
1583
+ assert.equal(outcome, "settled", "startup must succeed while the migration lock is held");
1584
+ assert.equal(existsSync(lockPath), true);
1585
+ assert.equal(existsSync(journalPath), false, "startup must not run the migration");
1586
+ await rm(lockPath);
1587
+ await storage.ensureDirectories();
1588
+ // SAFETY: the journal is written by the migration under test and carries an optional boolean `complete`.
1589
+ const state = JSON.parse(await readFile(journalPath, "utf8")) as { complete?: boolean };
1590
+ assert.equal(state.complete, true, "default setup must still run migration");
1591
+ } finally {
1592
+ await rm(lockPath, { force: true });
1593
+ await orchestrator.destroy();
1594
+ await rm(memoryDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
1595
+ }
1596
+ });
1597
+ }
@@ -746,6 +746,7 @@ export class Orchestrator {
746
746
  private _dependencyPropagationDelivery: DependencyPropagationDelivery | undefined;
747
747
  private _dependencyPropagationRecovery: Promise<void> | null = null;
748
748
  private destroyed = false;
749
+ private acceptingEntityMigrationMaintenance = true;
749
750
 
750
751
  private get dependencyPropagationDelivery(): DependencyPropagationDelivery {
751
752
  if (this.destroyed) throw new Error("orchestrator has been destroyed");
@@ -967,6 +968,7 @@ export class Orchestrator {
967
968
  * commands should call it before returning to let Node exit naturally.
968
969
  */
969
970
  async destroy(): Promise<void> {
971
+ this.acceptingEntityMigrationMaintenance = false;
970
972
  this._orchestratorInitCoordinator?.retireStartupEpoch();
971
973
  this.abortDeferredInit();
972
974
  this.extractionQueueCoordinator.stopAccepting();
@@ -1499,14 +1501,6 @@ export class Orchestrator {
1499
1501
  (namespace) => this.namespaceSearchRouter.backendForNamespace(namespace),
1500
1502
  (msg) => log.debug(msg),
1501
1503
  );
1502
- const defaultNs = this.config.defaultNamespace;
1503
- this.storage.onMemoryWrite = (filePath) => this.prioritizedEmbedding?.enqueue(filePath, defaultNs);
1504
- this.storageRouter.onStorageCreated = (storage, namespace) => {
1505
- storage.onMemoryWrite = (filePath) => this.prioritizedEmbedding?.enqueue(filePath, namespace);
1506
- };
1507
- this.storageRouter.forEachCachedStorage((storage, namespace) => {
1508
- storage.onMemoryWrite = (filePath) => this.prioritizedEmbedding?.enqueue(filePath, namespace);
1509
- });
1510
1504
  }
1511
1505
  this.maintenanceScheduler = new MaintenanceScheduler({
1512
1506
  config,
@@ -1516,6 +1510,13 @@ export class Orchestrator {
1516
1510
  getStorage: () => this.storage, storageForNamespace: (namespace) => this.storageRouter.storageFor(namespace),
1517
1511
  onActivitySynced: (summary) => { if (!this.config.meetings.enabled) return; void this.getMeetingsService().then((s) => s.requestBuildForActivitySync(summary.ranAt, this.config.activity.timezone, summary.touchedDates)).catch((err) => log.warn(`meetings: activity tail-step build failed to schedule: ${err instanceof Error ? err.message : String(err)}`)); },
1518
1512
  });
1513
+ const bindStorage = (storage: StorageManager, namespace: string) => {
1514
+ storage.onEntityMigrationComplete = () => { if (this.acceptingEntityMigrationMaintenance) this.requestQmdMaintenance(); };
1515
+ if (this.prioritizedEmbedding) storage.onMemoryWrite = (filePath) => this.prioritizedEmbedding?.enqueue(filePath, namespace);
1516
+ };
1517
+ bindStorage(this.storage, this.config.defaultNamespace);
1518
+ this.storageRouter.onStorageCreated = bindStorage;
1519
+ this.storageRouter.forEachCachedStorage(bindStorage);
1519
1520
  // Issue #1526: background extraction queue lives on its own coordinator.
1520
1521
  this.extractionQueueCoordinator = new ExtractionQueueCoordinator();
1521
1522