@remnic/core 9.69.102 → 9.69.104

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 (366) hide show
  1. package/dist/access-admin-ops-surface.js +53 -52
  2. package/dist/access-authorization-probe.js +54 -53
  3. package/dist/access-boundary.js +53 -52
  4. package/dist/access-cli.js +153 -150
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +53 -52
  7. package/dist/access-http-briefing.js +54 -53
  8. package/dist/access-http-query.js +55 -54
  9. package/dist/access-http.js +77 -76
  10. package/dist/access-identity-continuity-surface.js +53 -52
  11. package/dist/access-lcm-surface.js +53 -52
  12. package/dist/access-mcp.js +66 -65
  13. package/dist/access-namespace-preflight.js +53 -52
  14. package/dist/access-observe-write-category.js +9 -9
  15. package/dist/access-observe-write-surface.js +53 -52
  16. package/dist/access-offline-manifest.js +6 -6
  17. package/dist/access-operations-batch.js +57 -56
  18. package/dist/access-operations.js +64 -63
  19. package/dist/access-recall-concurrency.js +53 -52
  20. package/dist/access-recall-response.js +53 -52
  21. package/dist/access-recall-surface.js +53 -52
  22. package/dist/access-schema.js +8 -8
  23. package/dist/access-service.js +53 -52
  24. package/dist/access-surface-catalog.js +55 -54
  25. package/dist/access-surface-catalog.js.map +1 -1
  26. package/dist/active-recall.js +21 -20
  27. package/dist/active-recall.js.map +1 -1
  28. package/dist/adapters/index.js +4 -4
  29. package/dist/adapters/registry.js +2 -2
  30. package/dist/briefing.js +14 -13
  31. package/dist/buffer.js +5 -5
  32. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  33. package/dist/{capsule-merge-ZCXUPLNJ.js → capsule-merge-42GJGDUC.js} +4 -3
  34. package/dist/{capsule-merge-ZCXUPLNJ.js.map → capsule-merge-42GJGDUC.js.map} +1 -1
  35. package/dist/causal-behavior.js +2 -2
  36. package/dist/causal-chain.js +2 -2
  37. package/dist/causal-consolidation.js +17 -16
  38. package/dist/causal-consolidation.js.map +1 -1
  39. package/dist/causal-retrieval.js +2 -2
  40. package/dist/causal-trajectory-graph.js +3 -3
  41. package/dist/causal-trajectory.js +1 -1
  42. package/dist/{chunk-47LTKAGL.js → chunk-2COXDQ6C.js} +21 -6
  43. package/dist/chunk-2COXDQ6C.js.map +1 -0
  44. package/dist/{chunk-IOYY6ROU.js → chunk-2MMBJE53.js} +7 -7
  45. package/dist/{chunk-QSXEQPC4.js → chunk-2OFDC3AK.js} +2 -2
  46. package/dist/{chunk-4MP2Q2EI.js → chunk-2TCNEJ36.js} +4 -4
  47. package/dist/{chunk-5FAGCOZD.js → chunk-2U3R3DDH.js} +9 -9
  48. package/dist/{chunk-SCXURCVT.js → chunk-2UPTRDGM.js} +3 -3
  49. package/dist/{chunk-XVZA6VXC.js → chunk-3SL52JKD.js} +4 -4
  50. package/dist/{chunk-SBOCJ47I.js → chunk-5R4YPRAR.js} +19 -6
  51. package/dist/chunk-5R4YPRAR.js.map +1 -0
  52. package/dist/{chunk-ANPRNTPT.js → chunk-63LFM77Z.js} +15 -2
  53. package/dist/chunk-63LFM77Z.js.map +1 -0
  54. package/dist/{chunk-DINOVGA5.js → chunk-6WRHNA4I.js} +48 -48
  55. package/dist/chunk-6WRHNA4I.js.map +1 -0
  56. package/dist/{chunk-DSPWVQZ6.js → chunk-73R4HOOK.js} +3 -3
  57. package/dist/chunk-7CT3AI52.js +84 -0
  58. package/dist/chunk-7CT3AI52.js.map +1 -0
  59. package/dist/{chunk-PHBH74Q6.js → chunk-7NBUE5VY.js} +2 -2
  60. package/dist/{chunk-S533KGX6.js → chunk-7X7A7KVN.js} +2 -2
  61. package/dist/{chunk-XEATJBNB.js → chunk-7YQR6XHY.js} +71 -71
  62. package/dist/{chunk-F5MINS3R.js → chunk-A6WNTVA7.js} +3 -3
  63. package/dist/{chunk-F5MINS3R.js.map → chunk-A6WNTVA7.js.map} +1 -1
  64. package/dist/{chunk-533RHNHB.js → chunk-ATFFWIKL.js} +2 -2
  65. package/dist/{chunk-QCYUIZK2.js → chunk-AU43D3IT.js} +7 -7
  66. package/dist/{chunk-JZ66PN72.js → chunk-AWSP3WNM.js} +2 -2
  67. package/dist/{chunk-IY47KPML.js → chunk-B3OFQP5Y.js} +6 -6
  68. package/dist/{chunk-4DXNOVNW.js → chunk-B454SHPU.js} +6 -6
  69. package/dist/{chunk-3LMOJXZR.js → chunk-BKVCTTEN.js} +2 -2
  70. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  71. package/dist/{chunk-CZN53OT5.js → chunk-CGBE3N7T.js} +2 -2
  72. package/dist/{chunk-HWLIMQE2.js → chunk-CHDGJJV6.js} +2 -2
  73. package/dist/{chunk-UYPQOASU.js → chunk-CNBGAFDY.js} +2 -2
  74. package/dist/{chunk-JTC3PBNH.js → chunk-DB6XFMOF.js} +3 -3
  75. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  76. package/dist/{chunk-UFIZRMQZ.js → chunk-E4XLFYHV.js} +61 -29
  77. package/dist/chunk-E4XLFYHV.js.map +1 -0
  78. package/dist/{chunk-4OXXDL3X.js → chunk-EWWK65CI.js} +2 -2
  79. package/dist/{chunk-DFPG47ET.js → chunk-EZ3F4YIN.js} +11 -11
  80. package/dist/{chunk-V3I4NLZP.js → chunk-FYX2LXIX.js} +2 -2
  81. package/dist/{chunk-PNVOKGAX.js → chunk-G5I5ZT6O.js} +2 -2
  82. package/dist/{chunk-7Z2UKRNJ.js → chunk-GQ6ZF4LD.js} +2 -2
  83. package/dist/{chunk-PEZAHDDU.js → chunk-GZ2KV2RO.js} +5 -5
  84. package/dist/chunk-H6Z3RNW3.js +330 -0
  85. package/dist/chunk-H6Z3RNW3.js.map +1 -0
  86. package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
  87. package/dist/{chunk-AXHU56C3.js → chunk-HWGFTTPS.js} +2 -2
  88. package/dist/{chunk-2SGLMP2E.js → chunk-IDSZNHIP.js} +2 -2
  89. package/dist/{chunk-LO4UKPKX.js → chunk-IFCKHPY4.js} +2 -2
  90. package/dist/{chunk-ZHXSOMYH.js → chunk-IOMLGJHG.js} +4 -4
  91. package/dist/{chunk-YRULQW5J.js → chunk-ISX7GVZE.js} +4 -4
  92. package/dist/{chunk-5PHZSD75.js → chunk-J3ZJBX7I.js} +2 -2
  93. package/dist/{chunk-X5TAWDRO.js → chunk-JC7QQ2DJ.js} +2 -2
  94. package/dist/{chunk-RFQT7IZO.js → chunk-JRDL3FGM.js} +3 -3
  95. package/dist/{chunk-OGZC2WUX.js → chunk-JWFTPRYD.js} +2 -2
  96. package/dist/{chunk-7OA4JWP5.js → chunk-KFHEZJQZ.js} +2 -2
  97. package/dist/{chunk-P3WRFSYF.js → chunk-KMN2JE4D.js} +2 -2
  98. package/dist/{chunk-GNBZB43J.js → chunk-KV7LFOPF.js} +2 -2
  99. package/dist/{chunk-L6YCGS7I.js → chunk-KYUVFCT2.js} +2 -2
  100. package/dist/{chunk-5BVL2XUD.js → chunk-LHDSCWDK.js} +17 -17
  101. package/dist/{chunk-SF7CWSR6.js → chunk-LXJZUNLX.js} +2 -2
  102. package/dist/{chunk-XCB6QDL6.js → chunk-M5GVCJSJ.js} +8 -2
  103. package/dist/chunk-M5GVCJSJ.js.map +1 -0
  104. package/dist/{chunk-DGS34QSY.js → chunk-MFZACYZ6.js} +2 -2
  105. package/dist/{chunk-PYYQCHJC.js → chunk-MZOQ26LH.js} +2 -2
  106. package/dist/{chunk-R2TVWQLR.js → chunk-NT2Q6AHC.js} +2 -2
  107. package/dist/{chunk-ABGM2BN4.js → chunk-O43AAJFQ.js} +2 -2
  108. package/dist/{chunk-G3BW6SDE.js → chunk-OFGTRDWX.js} +3 -3
  109. package/dist/{chunk-6Z263Z3E.js → chunk-OWV6LKNZ.js} +2 -2
  110. package/dist/{chunk-HIHN4XHL.js → chunk-PQ7EB5AD.js} +3 -3
  111. package/dist/{chunk-VVYLVE6R.js → chunk-PQKZLBCJ.js} +1 -1
  112. package/dist/{chunk-OFFW73OZ.js → chunk-PRNR47DI.js} +2 -2
  113. package/dist/{chunk-UL7KDVLC.js → chunk-PZ2QQULY.js} +11 -11
  114. package/dist/{chunk-55NZVUNI.js → chunk-QAIVTLL2.js} +3 -3
  115. package/dist/{chunk-M7T2NKET.js → chunk-QKCGOECM.js} +2 -2
  116. package/dist/{chunk-ARY6O3JN.js → chunk-QUNCLKHM.js} +2 -2
  117. package/dist/{chunk-Z3O53WZR.js → chunk-QWUJWBZ5.js} +2 -2
  118. package/dist/{chunk-RZXFXKWY.js → chunk-QX3K3INR.js} +2 -2
  119. package/dist/{chunk-HFOY2SBG.js → chunk-RYN4AIRM.js} +7 -5
  120. package/dist/chunk-RYN4AIRM.js.map +1 -0
  121. package/dist/{chunk-4LNCLZNK.js → chunk-S2GR3T7W.js} +2 -2
  122. package/dist/{chunk-VPOAMPDD.js → chunk-SFDBLCZV.js} +59 -59
  123. package/dist/{chunk-2IWBHZ3H.js → chunk-SIGDETMB.js} +2 -2
  124. package/dist/chunk-SP4DB6SX.js +19 -0
  125. package/dist/chunk-SP4DB6SX.js.map +1 -0
  126. package/dist/{chunk-DBWNTLLI.js → chunk-STQC2FYQ.js} +6 -6
  127. package/dist/{chunk-S5R77WHV.js → chunk-SWXPR5SE.js} +2 -2
  128. package/dist/{chunk-3DNTFZAT.js → chunk-T32YYUT2.js} +2 -2
  129. package/dist/{chunk-W5JQSVPZ.js → chunk-T6RQNV7E.js} +2 -2
  130. package/dist/{chunk-O23QMYN4.js → chunk-TRN56SPQ.js} +2 -2
  131. package/dist/{chunk-NM3BT2OH.js → chunk-TXTRHF3B.js} +2 -2
  132. package/dist/{chunk-7AUIGOH4.js → chunk-VDVHIRCK.js} +2 -2
  133. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  134. package/dist/{chunk-ZGYLPCWN.js → chunk-VO2BNAA2.js} +3 -3
  135. package/dist/{chunk-WVEECBCG.js → chunk-VV2MVHCJ.js} +2 -2
  136. package/dist/{chunk-HVDEKJJR.js → chunk-VYIBRCVW.js} +70 -61
  137. package/dist/chunk-VYIBRCVW.js.map +1 -0
  138. package/dist/{chunk-B3O3IRZN.js → chunk-VZBW6CDG.js} +1 -1
  139. package/dist/{chunk-JPKK466T.js → chunk-W5XWHTDO.js} +2 -2
  140. package/dist/{chunk-CYG3K2T2.js → chunk-WR66CTVU.js} +6 -6
  141. package/dist/{chunk-JNAOTN7B.js → chunk-XLWMNDQR.js} +4 -4
  142. package/dist/chunk-XLWMNDQR.js.map +1 -0
  143. package/dist/{chunk-RP7RSENW.js → chunk-XQWKB6PY.js} +2 -2
  144. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  145. package/dist/{chunk-UCPGOUDZ.js → chunk-Y57GJDG5.js} +108 -3
  146. package/dist/chunk-Y57GJDG5.js.map +1 -0
  147. package/dist/{chunk-KYLYXGKD.js → chunk-YI6XIFQV.js} +2 -2
  148. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  149. package/dist/{chunk-Y7427SL7.js → chunk-Z6EZCN6Z.js} +10 -10
  150. package/dist/{chunk-5K2ALMSZ.js → chunk-ZRUC7FD3.js} +2 -2
  151. package/dist/cli.js +103 -102
  152. package/dist/coding/export-okf-codegraph.js +13 -12
  153. package/dist/coding/export-okf-codegraph.js.map +1 -1
  154. package/dist/coding/pre-action-gate.js +2 -2
  155. package/dist/compounding/engine.js +14 -13
  156. package/dist/config.js +21 -20
  157. package/dist/connectors/codex-materialize-runner.js +14 -13
  158. package/dist/connectors/index.js +14 -13
  159. package/dist/consolidation-provenance-check.js +4 -3
  160. package/dist/consolidation-undo.js +3 -2
  161. package/dist/consolidation-undo.js.map +1 -1
  162. package/dist/contradiction/index.js +3 -2
  163. package/dist/convergence-refresh.js +16 -15
  164. package/dist/corpus-watermark.js +16 -15
  165. package/dist/dashboard-runtime.js +2 -2
  166. package/dist/deep-recall-http-glue.js +9 -9
  167. package/dist/deep-recall.js +2 -2
  168. package/dist/embedding-fallback.d.ts +2 -1
  169. package/dist/embedding-fallback.d.ts.map +1 -1
  170. package/dist/embedding-fallback.js +2 -2
  171. package/dist/embedding-index-storage.d.ts +28 -1
  172. package/dist/embedding-index-storage.d.ts.map +1 -1
  173. package/dist/embedding-index-storage.js +1 -1
  174. package/dist/entity-mention-carry.d.ts +16 -0
  175. package/dist/entity-mention-carry.d.ts.map +1 -0
  176. package/dist/entity-mention-carry.js +8 -0
  177. package/dist/entity-mention-epoch.d.ts +22 -0
  178. package/dist/entity-mention-epoch.d.ts.map +1 -0
  179. package/dist/entity-mention-epoch.js +8 -0
  180. package/dist/entity-mention-index-cache.d.ts +28 -0
  181. package/dist/entity-mention-index-cache.d.ts.map +1 -0
  182. package/dist/entity-mention-index-cache.js +29 -0
  183. package/dist/entity-mention-index-cache.js.map +1 -0
  184. package/dist/entity-retrieval.d.ts.map +1 -1
  185. package/dist/entity-retrieval.js +18 -15
  186. package/dist/event-order-recall.js +2 -2
  187. package/dist/external-wiki-access.js +54 -53
  188. package/dist/extraction-span-materialize.js +2 -2
  189. package/dist/extraction.js +21 -20
  190. package/dist/{graph-edge-decay-MKIRZ762.js → graph-edge-decay-PVKDKLQK.js} +6 -6
  191. package/dist/graph-edge-decay-PVKDKLQK.js.map +1 -0
  192. package/dist/graph-jsonl.js +4 -4
  193. package/dist/graph-snapshot.js +4 -4
  194. package/dist/graph.js +5 -5
  195. package/dist/harmonic-retrieval.js +1 -1
  196. package/dist/index.js +187 -184
  197. package/dist/lcm/engine.js +4 -4
  198. package/dist/lcm/index.js +13 -13
  199. package/dist/location/index.js +16 -15
  200. package/dist/maintenance/memory-governance.js +14 -13
  201. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -13
  202. package/dist/maintenance/rebuild-memory-projection.js +15 -14
  203. package/dist/memory-browse-operations.js +54 -53
  204. package/dist/memory-browse.js +5 -5
  205. package/dist/memory-corpus-version.d.ts +8 -3
  206. package/dist/memory-corpus-version.d.ts.map +1 -1
  207. package/dist/memory-corpus-version.js +2 -1
  208. package/dist/namespaces/migrate.js +15 -14
  209. package/dist/namespaces/search.js +7 -7
  210. package/dist/namespaces/storage.js +14 -13
  211. package/dist/offline-sync-changeset-generations.js +4 -4
  212. package/dist/offline-sync-embedding-generation.js +3 -3
  213. package/dist/offline-sync-exclude-globs.d.ts +8 -0
  214. package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
  215. package/dist/offline-sync-exclude-globs.js +3 -1
  216. package/dist/offline-sync-snapshot-stream.js +6 -6
  217. package/dist/offline-sync.d.ts.map +1 -1
  218. package/dist/offline-sync.js +9 -9
  219. package/dist/operator-doctor-corpus.js +17 -16
  220. package/dist/operator-toolkit.js +34 -33
  221. package/dist/orchestration/maintenance.js +22 -21
  222. package/dist/orchestrator.js +153 -150
  223. package/dist/page-versioning.js +3 -2
  224. package/dist/preferences/preference-drift.d.ts.map +1 -1
  225. package/dist/procedural/library-maintenance.d.ts.map +1 -1
  226. package/dist/qmd.js +4 -4
  227. package/dist/recall-navigation-operations.js +54 -53
  228. package/dist/recall-navigation.js +53 -52
  229. package/dist/recall-state-view-admission.js +5 -5
  230. package/dist/recall-why-service.js +5 -5
  231. package/dist/{resolution-BROE3TRO.js → resolution-RSG4HZTL.js} +4 -3
  232. package/dist/resolution-RSG4HZTL.js.map +1 -0
  233. package/dist/resume-bundles.js +22 -21
  234. package/dist/search/factory.js +6 -6
  235. package/dist/search/index.js +12 -12
  236. package/dist/semantic-consolidation.js +15 -14
  237. package/dist/semantic-rule-promotion.js +14 -13
  238. package/dist/semantic-rule-verifier.js +14 -13
  239. package/dist/source-agent-qualifier.js +17 -16
  240. package/dist/standup/build.js +14 -13
  241. package/dist/storage/entity-mention-frontmatter-hold.d.ts +14 -0
  242. package/dist/storage/entity-mention-frontmatter-hold.d.ts.map +1 -0
  243. package/dist/storage.d.ts +13 -9
  244. package/dist/storage.d.ts.map +1 -1
  245. package/dist/storage.js +13 -12
  246. package/dist/support-passport/index.js +56 -55
  247. package/dist/transfer/backup.js +2 -2
  248. package/dist/transfer/capsule-export.js +2 -2
  249. package/dist/transfer/capsule-import.js +5 -4
  250. package/dist/transfer/export-okf.js +14 -13
  251. package/dist/verified-recall.js +14 -13
  252. package/dist/who-knows-cli.js +15 -14
  253. package/dist/who-knows.js +14 -13
  254. package/package.json +2 -2
  255. package/src/embedding-fallback-cross-process.test.ts +533 -0
  256. package/src/embedding-fallback.ts +19 -7
  257. package/src/embedding-index-storage.test.ts +482 -2
  258. package/src/embedding-index-storage.ts +131 -2
  259. package/src/entity-mention-carry.ts +31 -0
  260. package/src/entity-mention-epoch.ts +120 -0
  261. package/src/entity-mention-index-cache.ts +443 -0
  262. package/src/entity-retrieval.ts +72 -71
  263. package/src/maintenance/rebuild-memory-projection.ts +1 -1
  264. package/src/memory-corpus-version.ts +13 -3
  265. package/src/offline-sync-exclude-globs.ts +23 -0
  266. package/src/offline-sync.test.ts +325 -0
  267. package/src/offline-sync.ts +17 -1
  268. package/src/preferences/preference-drift.ts +5 -3
  269. package/src/procedural/library-maintenance.ts +4 -3
  270. package/src/semantic-rule-promotion.ts +1 -1
  271. package/src/storage/entity-mention-frontmatter-hold.ts +44 -0
  272. package/src/storage.ts +40 -38
  273. package/dist/chunk-47LTKAGL.js.map +0 -1
  274. package/dist/chunk-ANPRNTPT.js.map +0 -1
  275. package/dist/chunk-DINOVGA5.js.map +0 -1
  276. package/dist/chunk-HFOY2SBG.js.map +0 -1
  277. package/dist/chunk-HVDEKJJR.js.map +0 -1
  278. package/dist/chunk-JNAOTN7B.js.map +0 -1
  279. package/dist/chunk-SBOCJ47I.js.map +0 -1
  280. package/dist/chunk-UCPGOUDZ.js.map +0 -1
  281. package/dist/chunk-UFIZRMQZ.js.map +0 -1
  282. package/dist/chunk-XCB6QDL6.js.map +0 -1
  283. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  284. /package/dist/{chunk-IOYY6ROU.js.map → chunk-2MMBJE53.js.map} +0 -0
  285. /package/dist/{chunk-QSXEQPC4.js.map → chunk-2OFDC3AK.js.map} +0 -0
  286. /package/dist/{chunk-4MP2Q2EI.js.map → chunk-2TCNEJ36.js.map} +0 -0
  287. /package/dist/{chunk-5FAGCOZD.js.map → chunk-2U3R3DDH.js.map} +0 -0
  288. /package/dist/{chunk-SCXURCVT.js.map → chunk-2UPTRDGM.js.map} +0 -0
  289. /package/dist/{chunk-XVZA6VXC.js.map → chunk-3SL52JKD.js.map} +0 -0
  290. /package/dist/{chunk-DSPWVQZ6.js.map → chunk-73R4HOOK.js.map} +0 -0
  291. /package/dist/{chunk-PHBH74Q6.js.map → chunk-7NBUE5VY.js.map} +0 -0
  292. /package/dist/{chunk-S533KGX6.js.map → chunk-7X7A7KVN.js.map} +0 -0
  293. /package/dist/{chunk-XEATJBNB.js.map → chunk-7YQR6XHY.js.map} +0 -0
  294. /package/dist/{chunk-533RHNHB.js.map → chunk-ATFFWIKL.js.map} +0 -0
  295. /package/dist/{chunk-QCYUIZK2.js.map → chunk-AU43D3IT.js.map} +0 -0
  296. /package/dist/{chunk-JZ66PN72.js.map → chunk-AWSP3WNM.js.map} +0 -0
  297. /package/dist/{chunk-IY47KPML.js.map → chunk-B3OFQP5Y.js.map} +0 -0
  298. /package/dist/{chunk-4DXNOVNW.js.map → chunk-B454SHPU.js.map} +0 -0
  299. /package/dist/{chunk-3LMOJXZR.js.map → chunk-BKVCTTEN.js.map} +0 -0
  300. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  301. /package/dist/{chunk-CZN53OT5.js.map → chunk-CGBE3N7T.js.map} +0 -0
  302. /package/dist/{chunk-HWLIMQE2.js.map → chunk-CHDGJJV6.js.map} +0 -0
  303. /package/dist/{chunk-UYPQOASU.js.map → chunk-CNBGAFDY.js.map} +0 -0
  304. /package/dist/{chunk-JTC3PBNH.js.map → chunk-DB6XFMOF.js.map} +0 -0
  305. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  306. /package/dist/{chunk-4OXXDL3X.js.map → chunk-EWWK65CI.js.map} +0 -0
  307. /package/dist/{chunk-DFPG47ET.js.map → chunk-EZ3F4YIN.js.map} +0 -0
  308. /package/dist/{chunk-V3I4NLZP.js.map → chunk-FYX2LXIX.js.map} +0 -0
  309. /package/dist/{chunk-PNVOKGAX.js.map → chunk-G5I5ZT6O.js.map} +0 -0
  310. /package/dist/{chunk-7Z2UKRNJ.js.map → chunk-GQ6ZF4LD.js.map} +0 -0
  311. /package/dist/{chunk-PEZAHDDU.js.map → chunk-GZ2KV2RO.js.map} +0 -0
  312. /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
  313. /package/dist/{chunk-AXHU56C3.js.map → chunk-HWGFTTPS.js.map} +0 -0
  314. /package/dist/{chunk-2SGLMP2E.js.map → chunk-IDSZNHIP.js.map} +0 -0
  315. /package/dist/{chunk-LO4UKPKX.js.map → chunk-IFCKHPY4.js.map} +0 -0
  316. /package/dist/{chunk-ZHXSOMYH.js.map → chunk-IOMLGJHG.js.map} +0 -0
  317. /package/dist/{chunk-YRULQW5J.js.map → chunk-ISX7GVZE.js.map} +0 -0
  318. /package/dist/{chunk-5PHZSD75.js.map → chunk-J3ZJBX7I.js.map} +0 -0
  319. /package/dist/{chunk-X5TAWDRO.js.map → chunk-JC7QQ2DJ.js.map} +0 -0
  320. /package/dist/{chunk-RFQT7IZO.js.map → chunk-JRDL3FGM.js.map} +0 -0
  321. /package/dist/{chunk-OGZC2WUX.js.map → chunk-JWFTPRYD.js.map} +0 -0
  322. /package/dist/{chunk-7OA4JWP5.js.map → chunk-KFHEZJQZ.js.map} +0 -0
  323. /package/dist/{chunk-P3WRFSYF.js.map → chunk-KMN2JE4D.js.map} +0 -0
  324. /package/dist/{chunk-GNBZB43J.js.map → chunk-KV7LFOPF.js.map} +0 -0
  325. /package/dist/{chunk-L6YCGS7I.js.map → chunk-KYUVFCT2.js.map} +0 -0
  326. /package/dist/{chunk-5BVL2XUD.js.map → chunk-LHDSCWDK.js.map} +0 -0
  327. /package/dist/{chunk-SF7CWSR6.js.map → chunk-LXJZUNLX.js.map} +0 -0
  328. /package/dist/{chunk-DGS34QSY.js.map → chunk-MFZACYZ6.js.map} +0 -0
  329. /package/dist/{chunk-PYYQCHJC.js.map → chunk-MZOQ26LH.js.map} +0 -0
  330. /package/dist/{chunk-R2TVWQLR.js.map → chunk-NT2Q6AHC.js.map} +0 -0
  331. /package/dist/{chunk-ABGM2BN4.js.map → chunk-O43AAJFQ.js.map} +0 -0
  332. /package/dist/{chunk-G3BW6SDE.js.map → chunk-OFGTRDWX.js.map} +0 -0
  333. /package/dist/{chunk-6Z263Z3E.js.map → chunk-OWV6LKNZ.js.map} +0 -0
  334. /package/dist/{chunk-HIHN4XHL.js.map → chunk-PQ7EB5AD.js.map} +0 -0
  335. /package/dist/{chunk-VVYLVE6R.js.map → chunk-PQKZLBCJ.js.map} +0 -0
  336. /package/dist/{chunk-OFFW73OZ.js.map → chunk-PRNR47DI.js.map} +0 -0
  337. /package/dist/{chunk-UL7KDVLC.js.map → chunk-PZ2QQULY.js.map} +0 -0
  338. /package/dist/{chunk-55NZVUNI.js.map → chunk-QAIVTLL2.js.map} +0 -0
  339. /package/dist/{chunk-M7T2NKET.js.map → chunk-QKCGOECM.js.map} +0 -0
  340. /package/dist/{chunk-ARY6O3JN.js.map → chunk-QUNCLKHM.js.map} +0 -0
  341. /package/dist/{chunk-Z3O53WZR.js.map → chunk-QWUJWBZ5.js.map} +0 -0
  342. /package/dist/{chunk-RZXFXKWY.js.map → chunk-QX3K3INR.js.map} +0 -0
  343. /package/dist/{chunk-4LNCLZNK.js.map → chunk-S2GR3T7W.js.map} +0 -0
  344. /package/dist/{chunk-VPOAMPDD.js.map → chunk-SFDBLCZV.js.map} +0 -0
  345. /package/dist/{chunk-2IWBHZ3H.js.map → chunk-SIGDETMB.js.map} +0 -0
  346. /package/dist/{chunk-DBWNTLLI.js.map → chunk-STQC2FYQ.js.map} +0 -0
  347. /package/dist/{chunk-S5R77WHV.js.map → chunk-SWXPR5SE.js.map} +0 -0
  348. /package/dist/{chunk-3DNTFZAT.js.map → chunk-T32YYUT2.js.map} +0 -0
  349. /package/dist/{chunk-W5JQSVPZ.js.map → chunk-T6RQNV7E.js.map} +0 -0
  350. /package/dist/{chunk-O23QMYN4.js.map → chunk-TRN56SPQ.js.map} +0 -0
  351. /package/dist/{chunk-NM3BT2OH.js.map → chunk-TXTRHF3B.js.map} +0 -0
  352. /package/dist/{chunk-7AUIGOH4.js.map → chunk-VDVHIRCK.js.map} +0 -0
  353. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  354. /package/dist/{chunk-ZGYLPCWN.js.map → chunk-VO2BNAA2.js.map} +0 -0
  355. /package/dist/{chunk-WVEECBCG.js.map → chunk-VV2MVHCJ.js.map} +0 -0
  356. /package/dist/{chunk-B3O3IRZN.js.map → chunk-VZBW6CDG.js.map} +0 -0
  357. /package/dist/{chunk-JPKK466T.js.map → chunk-W5XWHTDO.js.map} +0 -0
  358. /package/dist/{chunk-CYG3K2T2.js.map → chunk-WR66CTVU.js.map} +0 -0
  359. /package/dist/{chunk-RP7RSENW.js.map → chunk-XQWKB6PY.js.map} +0 -0
  360. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  361. /package/dist/{chunk-KYLYXGKD.js.map → chunk-YI6XIFQV.js.map} +0 -0
  362. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  363. /package/dist/{chunk-Y7427SL7.js.map → chunk-Z6EZCN6Z.js.map} +0 -0
  364. /package/dist/{chunk-5K2ALMSZ.js.map → chunk-ZRUC7FD3.js.map} +0 -0
  365. /package/dist/{graph-edge-decay-MKIRZ762.js.map → entity-mention-carry.js.map} +0 -0
  366. /package/dist/{resolution-BROE3TRO.js.map → entity-mention-epoch.js.map} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { appendFileSync, mkdirSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { entityMentionEpoch } from "./entity-mention-epoch.js";
3
4
  import { deleteInFlightReadsForDir } from "./in-flight-reads.js";
4
5
  import { invalidateDerivedAndGlobalForDir } from "./memory-cache.js";
5
6
 
@@ -17,10 +18,16 @@ export const MEMORY_CORPUS_VERSION_SENTINEL = ".memory-corpus-version.log";
17
18
  * — review approval, governance restore, migration. Appends one byte to the
18
19
  * on-disk sentinel so any process's version-keyed hot-memories cache rescans
19
20
  * on its next read (StorageManager.readSharedVersion reads the file size, so
20
- * the growth is observed immediately). Fail-open: a bump failure must never
21
- * crash the caller — a stale-cache window self-heals on the next real write.
21
+ * the growth is observed immediately). The same call moves the entity-mention
22
+ * epoch, unless the caller passes `{ indexedText: false }` for a metadata
23
+ * stamp that cannot change snippet text, entityRef, or origin. Fail-open: a
24
+ * bump failure must never crash the caller — a stale-cache window self-heals
25
+ * on the next real write.
22
26
  */
23
- export function bumpMemoryCorpusVersionForDir(memoryDir: string): void {
27
+ export function bumpMemoryCorpusVersionForDir(
28
+ memoryDir: string,
29
+ options?: { indexedText?: boolean },
30
+ ): void {
24
31
  try {
25
32
  const stateDir = path.join(memoryDir, "state");
26
33
  mkdirSync(stateDir, { recursive: true });
@@ -28,6 +35,9 @@ export function bumpMemoryCorpusVersionForDir(memoryDir: string): void {
28
35
  } catch {
29
36
  /* fail-open: never let a cache-coherence bump crash the primary operation */
30
37
  }
38
+ if (options?.indexedText !== false && !entityMentionEpoch.suppressed()) {
39
+ entityMentionEpoch.bump(memoryDir);
40
+ }
31
41
  // Drop any in-flight readAllMemories scan for this dir (Cursor Medium, #1902):
32
42
  // after the sentinel advances, a concurrent read that missed the version-keyed
33
43
  // hot entry must not attach to a scan that began BEFORE this out-of-band write
@@ -67,12 +67,35 @@ export const DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS: readonly string[] = [
67
67
  "**/namespaces/*/state/embeddings.pre-replace.tmp/**",
68
68
  "**/namespaces/*/state/embeddings.staging.tmp-*/**",
69
69
  "**/namespaces/*/state/embeddings.json.pre-migration.tmp-*",
70
+ // The warm-cache generation marker (PR #3176) is node-local coherence
71
+ // state: a synced copy — worse, a captured `in-flight` value — would make
72
+ // the receiving node's stamp probes unique forever and force a full index
73
+ // reload on every search. Exclude on both sides: never snapshotted, and
74
+ // an incoming (old or hostile) marker never overwrites or deletes the
75
+ // local one.
76
+ "**/state/embeddings.generation",
77
+ "**/namespaces/*/state/embeddings.generation",
70
78
  ];
71
79
 
72
80
 
73
81
  const DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS: readonly RegExp[] =
74
82
  DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS.map((glob) => globToRegExp(glob));
75
83
 
84
+ /**
85
+ * The warm-cache generation marker is the one default-excluded path that
86
+ * must ALSO be refused on apply (push-side default excludes like the live
87
+ * LCM sqlite are deliberately apply-accepted for first-sync bootstrapping).
88
+ * Node-local on both sides: never snapshotted, never overwritten by an
89
+ * incoming old or hostile copy.
90
+ */
91
+ export function isEmbeddingGenerationMarkerPath(relPosix: string): boolean {
92
+ const normalized = relPosix.includes("\\") ? relPosix.replaceAll("\\", "/") : relPosix;
93
+ // Case-insensitive: on APFS/NTFS a differently cased path resolves to the
94
+ // same file, so `state/Embeddings.Generation` must be refused like the
95
+ // canonical spelling.
96
+ return /(?:^|\/)state\/embeddings\.generation$/i.test(normalized);
97
+ }
98
+
76
99
  /**
77
100
  * Precompiled-once default-exclude check for the snapshot enumeration hot
78
101
  * path (Kilo review, PR #1793). Moved here from offline-sync.ts with the
@@ -26,7 +26,15 @@ import {
26
26
  summarizeOfflineSyncPendingFiles,
27
27
  } from "./offline-sync.js";
28
28
  import { isEncryptedFile } from "./secure-store/secure-fs.js";
29
+ import { isEmbeddingGenerationMarkerPath } from "./offline-sync-exclude-globs.js";
29
30
  import { StorageManager } from "./storage.js";
31
+ import { parseConfig } from "./config.js";
32
+ import { EmbeddingFallback } from "./embedding-fallback.js";
33
+ import {
34
+ clearHostEmbeddingProvidersForTest,
35
+ registerHostEmbeddingProvider,
36
+ } from "./host-embedding-provider.js";
37
+ import type { EmbeddingIndexStoreIo } from "./embedding-index-storage.js";
30
38
 
31
39
  async function tempDir(name: string): Promise<string> {
32
40
  return mkdtemp(path.join(os.tmpdir(), `${name}-`));
@@ -551,6 +559,21 @@ test("offline sync push-side default excludes live LCM sqlite but apply-side sti
551
559
  assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "live shm");
552
560
  assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "live wal");
553
561
 
562
+ // The apply-side acceptance is real, not vacuous: a node with NO local
563
+ // sqlite must bootstrap it from the incoming snapshot.
564
+ const freshRoot = await tempDir("remnic-offline-lcm-sqlite-bootstrap");
565
+ try {
566
+ const bootstrap = await applyOfflineSyncSnapshot({
567
+ root: freshRoot,
568
+ snapshot: remoteSnapshot,
569
+ baseFiles: [],
570
+ });
571
+ assert.equal(await readUtf8(freshRoot, "state/lcm.sqlite"), "live db", "first sync bootstraps the LCM DB from the incoming snapshot");
572
+ assert.equal(bootstrap.upserted, 1);
573
+ } finally {
574
+ await rm(freshRoot, { recursive: true, force: true });
575
+ }
576
+
554
577
  // The push-side changeset excludes lcm.sqlite and friends so the
555
578
  // local node never pushes the live sqlite state up.
556
579
  const changeset = await buildOfflineSyncChangeset({
@@ -569,6 +592,308 @@ test("offline sync push-side default excludes live LCM sqlite but apply-side sti
569
592
  }
570
593
  });
571
594
 
595
+ test("offline sync excludes the node-local embedding generation marker", async () => {
596
+ const root = await tempDir("remnic-offline-emb3176-marker");
597
+ try {
598
+ await write(root, "facts/a.md", "alpha");
599
+ await write(root, "state/embeddings.generation", "in-flight");
600
+ await write(root, "namespaces/team/state/embeddings.generation", "gen-team-uuid");
601
+
602
+ const snapshot = await buildOfflineSyncSnapshot({
603
+ root,
604
+ sourceId: "remote",
605
+ includeContent: true,
606
+ });
607
+ assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
608
+
609
+ const changeset = await buildOfflineSyncChangeset({
610
+ root,
611
+ sourceId: "laptop",
612
+ baseFiles: [{
613
+ path: "facts/a.md",
614
+ sha256: createHash("sha256").update("alpha").digest("hex"),
615
+ bytes: Buffer.byteLength("alpha"),
616
+ mtimeMs: 0,
617
+ }],
618
+ });
619
+ assert.deepEqual(changeset.changes.map((change) => change.path), []);
620
+ } finally {
621
+ await rm(root, { recursive: true, force: true });
622
+ }
623
+ });
624
+
625
+ test("offline sync apply never lets an incoming marker overwrite the local one", async () => {
626
+ const root = await tempDir("remnic-offline-emb3176-marker-apply");
627
+ try {
628
+ const localMarker = "0d9d2c11-5f6e-4a0e-8f1a-2b3c4d5e6f70";
629
+ await write(root, "facts/a.md", "alpha");
630
+ await write(root, "state/embeddings.generation", localMarker);
631
+
632
+ // A hostile or pre-exclusion remote snapshot carries the marker as an
633
+ // ordinary file; the pinned-`in-flight` payload would make every local
634
+ // stamp probe unique and force a full reload on every search.
635
+ const hostile = "in-flight";
636
+ const snapshot = {
637
+ format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
638
+ schemaVersion: 1 as const,
639
+ createdAt: new Date().toISOString(),
640
+ sourceId: "remote",
641
+ includeTranscripts: true,
642
+ files: [
643
+ {
644
+ path: "facts/b.md",
645
+ sha256: createHash("sha256").update("beta").digest("hex"),
646
+ bytes: Buffer.byteLength("beta"),
647
+ mtimeMs: 0,
648
+ contentBase64: Buffer.from("beta").toString("base64"),
649
+ },
650
+ {
651
+ path: "state/embeddings.generation",
652
+ sha256: createHash("sha256").update(hostile).digest("hex"),
653
+ bytes: Buffer.byteLength(hostile),
654
+ mtimeMs: 0,
655
+ contentBase64: Buffer.from(hostile).toString("base64"),
656
+ },
657
+ ],
658
+ };
659
+ const result = await applyOfflineSyncSnapshot({ root, snapshot, baseFiles: [] });
660
+
661
+ assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "the local marker is node-local: an incoming marker must never overwrite it");
662
+ assert.equal(await readUtf8(root, "facts/b.md"), "beta", "unrelated incoming files still apply");
663
+ assert.equal(result.deleted, 0);
664
+ assert.deepEqual(result.conflicts, [], "an incoming marker must be skipped node-local, not turned into a conflict");
665
+
666
+ // A stale base entry for the marker plus its absence from the incoming
667
+ // snapshot is NOT a delete instruction either.
668
+ const staleBase = [{
669
+ path: "state/embeddings.generation",
670
+ sha256: createHash("sha256").update("stale").digest("hex"),
671
+ bytes: Buffer.byteLength("stale"),
672
+ mtimeMs: 0,
673
+ }];
674
+ const deletionProbe = await applyOfflineSyncSnapshot({
675
+ root,
676
+ snapshot: {
677
+ ...snapshot,
678
+ files: snapshot.files.filter((file) => file.path !== "state/embeddings.generation"),
679
+ },
680
+ baseFiles: staleBase,
681
+ });
682
+ assert.equal(await exists(root, "state/embeddings.generation"), true);
683
+ assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker);
684
+ assert.equal(deletionProbe.deleted, 0);
685
+ assert.deepEqual(deletionProbe.conflicts, [], "a marker absence is node-local, never a delete instruction or conflict");
686
+ } finally {
687
+ await rm(root, { recursive: true, force: true });
688
+ }
689
+ });
690
+
691
+ test("offline sync apply refuses differently cased marker paths on case-insensitive filesystems", async () => {
692
+ const root = await tempDir("remnic-offline-emb3176-marker-case");
693
+ try {
694
+ const localMarker = "0d9d2c11-5f6e-4a0e-8f1a-2b3c4d5e6f70";
695
+ await write(root, "facts/a.md", "alpha");
696
+ await write(root, "state/embeddings.generation", localMarker);
697
+
698
+ assert.equal(isEmbeddingGenerationMarkerPath("state/embeddings.generation"), true);
699
+ assert.equal(isEmbeddingGenerationMarkerPath("state/Embeddings.Generation"), true, "APFS/NTFS resolve this to the same file");
700
+ assert.equal(isEmbeddingGenerationMarkerPath("State/Embeddings.Generation"), true);
701
+ assert.equal(isEmbeddingGenerationMarkerPath("state/embeddings.json"), false);
702
+
703
+ const hostile = "in-flight";
704
+ const markerEntry = (relPath: string) => ({
705
+ path: relPath,
706
+ sha256: createHash("sha256").update(hostile).digest("hex"),
707
+ bytes: Buffer.byteLength(hostile),
708
+ mtimeMs: 0,
709
+ contentBase64: Buffer.from(hostile).toString("base64"),
710
+ });
711
+ const result = await applyOfflineSyncSnapshot({
712
+ root,
713
+ snapshot: {
714
+ format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
715
+ schemaVersion: 1 as const,
716
+ createdAt: new Date().toISOString(),
717
+ sourceId: "remote",
718
+ includeTranscripts: true,
719
+ files: [
720
+ {
721
+ path: "facts/b.md",
722
+ sha256: createHash("sha256").update("beta").digest("hex"),
723
+ bytes: Buffer.byteLength("beta"),
724
+ mtimeMs: 0,
725
+ contentBase64: Buffer.from("beta").toString("base64"),
726
+ },
727
+ markerEntry("state/Embeddings.Generation"),
728
+ ],
729
+ },
730
+ baseFiles: [],
731
+ });
732
+ assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "the canonical local marker survives a cased incoming copy");
733
+ assert.equal(await exists(root, "state/Embeddings.Generation"), false, "a cased marker path is refused node-local, never written");
734
+ assert.equal(await exists(root, "facts/b.md"), true, "unrelated incoming files still apply");
735
+ assert.deepEqual(result.conflicts, []);
736
+ } finally {
737
+ await rm(root, { recursive: true, force: true });
738
+ }
739
+ });
740
+
741
+ test("a hostile incoming marker leaves the local warm embedding cache untouched", async () => {
742
+ const root = await tempDir("remnic-offline-emb3176-marker-warm");
743
+ try {
744
+ const vectorFor = (text: string): number[] => {
745
+ const vec = [0, 0, 0, 0];
746
+ for (let i = 0; i < text.length; i++) vec[i % 4] += text.charCodeAt(i) % 13;
747
+ return vec;
748
+ };
749
+ const unregister = registerHostEmbeddingProvider(root, {
750
+ id: "host-model-a",
751
+ model: "host-model-a",
752
+ embed: async (text: string) => vectorFor(text),
753
+ });
754
+ let reads = 0;
755
+ const countingIo: EmbeddingIndexStoreIo = {
756
+ readUtf8: async (filePath) => {
757
+ reads += 1;
758
+ return readFile(filePath, "utf-8");
759
+ },
760
+ writeUtf8: async (filePath, contents) => {
761
+ await mkdir(path.dirname(filePath), { recursive: true });
762
+ await writeFile(filePath, contents, "utf-8");
763
+ },
764
+ };
765
+ const fallback = new EmbeddingFallback(
766
+ parseConfig({
767
+ memoryDir: root,
768
+ embeddingFallbackEnabled: true,
769
+ embeddingFallbackProvider: "openai",
770
+ openaiApiKey: false,
771
+ }),
772
+ countingIo,
773
+ );
774
+ try {
775
+ await fallback.indexFile("mem-a", "parent fact about database indexes", "facts/a.md");
776
+ assert.equal((await fallback.search("parent fact about database indexes", 5))[0]?.id, "mem-a");
777
+ const readsBeforeApply = reads;
778
+
779
+ const hostile = "in-flight";
780
+ await applyOfflineSyncSnapshot({
781
+ root,
782
+ snapshot: {
783
+ format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
784
+ schemaVersion: 1 as const,
785
+ createdAt: new Date().toISOString(),
786
+ sourceId: "remote",
787
+ includeTranscripts: true,
788
+ files: [{
789
+ path: "state/embeddings.generation",
790
+ sha256: createHash("sha256").update(hostile).digest("hex"),
791
+ bytes: Buffer.byteLength(hostile),
792
+ mtimeMs: 0,
793
+ contentBase64: Buffer.from(hostile).toString("base64"),
794
+ }],
795
+ },
796
+ baseFiles: [],
797
+ });
798
+
799
+ const warm = await fallback.search("parent fact about database indexes", 5);
800
+ assert.equal(warm[0]?.id, "mem-a", "the local generation is still served");
801
+ assert.equal(
802
+ reads,
803
+ readsBeforeApply,
804
+ "the local marker survived the apply: the warm cache keeps its stable stamp and performs no index reads",
805
+ );
806
+ } finally {
807
+ unregister();
808
+ }
809
+ } finally {
810
+ await rm(root, { recursive: true, force: true });
811
+ }
812
+ });
813
+
814
+ test("offline sync changeset apply refuses marker upserts and deletes, continues ordinary changes", async () => {
815
+ const root = await tempDir("remnic-offline-emb3176-marker-changeset");
816
+ try {
817
+ const localMarker = "7f6c1e64-9b2a-4c7d-8e0f-1a2b3c4d5e6f";
818
+ const hostile = "in-flight";
819
+ const sha = (value: string) => createHash("sha256").update(value).digest("hex");
820
+ await write(root, "facts/a.md", "alpha");
821
+ await write(root, "state/embeddings.generation", localMarker);
822
+
823
+ const result = await applyOfflineSyncChangeset({
824
+ root,
825
+ currentFiles: [
826
+ { path: "facts/a.md", sha256: sha("alpha"), bytes: 5, mtimeMs: 0 },
827
+ { path: "state/embeddings.generation", sha256: sha(localMarker), bytes: localMarker.length, mtimeMs: 0 },
828
+ ],
829
+ changeset: {
830
+ format: "remnic.offline-sync.changeset.v1",
831
+ schemaVersion: 1,
832
+ createdAt: new Date().toISOString(),
833
+ sourceId: "peer",
834
+ includeTranscripts: false,
835
+ changes: [
836
+ { type: "upsert", path: "facts/b.md", file: { path: "facts/b.md", sha256: sha("beta"), bytes: 4, mtimeMs: 0, contentBase64: Buffer.from("beta").toString("base64") } },
837
+ { type: "upsert", path: "state/embeddings.generation", file: { path: "state/embeddings.generation", sha256: sha(hostile), bytes: hostile.length, mtimeMs: 0, contentBase64: Buffer.from(hostile).toString("base64") } },
838
+ ],
839
+ } as never,
840
+ });
841
+ const deletionResult = await applyOfflineSyncChangeset({
842
+ root,
843
+ currentFiles: [
844
+ { path: "facts/a.md", sha256: sha("alpha"), bytes: 5, mtimeMs: 0 },
845
+ { path: "facts/b.md", sha256: sha("beta"), bytes: 4, mtimeMs: 0 },
846
+ { path: "state/embeddings.generation", sha256: sha(localMarker), bytes: localMarker.length, mtimeMs: 0 },
847
+ ],
848
+ changeset: {
849
+ format: "remnic.offline-sync.changeset.v1",
850
+ schemaVersion: 1,
851
+ createdAt: new Date().toISOString(),
852
+ sourceId: "peer",
853
+ includeTranscripts: false,
854
+ changes: [
855
+ { type: "delete", path: "facts/a.md", baseSha256: sha("alpha"), mtimeMs: 0 },
856
+ { type: "delete", path: "state/embeddings.generation", baseSha256: sha(localMarker), mtimeMs: 0 },
857
+ ],
858
+ } as never,
859
+ });
860
+
861
+ assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "node-local marker: neither an incoming upsert nor a delete may touch it");
862
+ assert.equal(await exists(root, "state/embeddings.generation"), true);
863
+ assert.equal(await readUtf8(root, "facts/b.md"), "beta", "ordinary upserts continue");
864
+ assert.equal(await exists(root, "facts/a.md"), false, "ordinary deletes continue");
865
+ assert.equal(deletionResult.appliedDeletes, 1);
866
+ assert.deepEqual(result.conflicts, [], "a marker upsert must be skipped node-local, not turned into a conflict");
867
+ assert.deepEqual(deletionResult.conflicts, [], "a marker delete must be skipped node-local, not turned into a conflict");
868
+ } finally {
869
+ await rm(root, { recursive: true, force: true });
870
+ }
871
+ });
872
+
873
+ test("offline sync file content chunks refuse the generation marker", async () => {
874
+ const root = await tempDir("remnic-offline-emb3176-marker-chunk");
875
+ try {
876
+ await write(root, "state/embeddings.generation", "local-uuid");
877
+ const content = Buffer.from("in-flight");
878
+ await assert.rejects(
879
+ () =>
880
+ applyOfflineSyncFileContentChunk({
881
+ root,
882
+ sourceId: "peer",
883
+ path: "state/embeddings.generation",
884
+ sha256: createHash("sha256").update(content).digest("hex"),
885
+ bytes: content.length,
886
+ mtimeMs: 0,
887
+ content,
888
+ }),
889
+ /node-local/,
890
+ );
891
+ assert.equal(await readUtf8(root, "state/embeddings.generation"), "local-uuid", "the chunked hostile payload never lands");
892
+ } finally {
893
+ await rm(root, { recursive: true, force: true });
894
+ }
895
+ });
896
+
572
897
  test("offline sync includes durable runtime state and excludes only transient sync temp files", async () => {
573
898
  const root = await tempDir("remnic-offline-runtime-state");
574
899
  try {
@@ -25,7 +25,7 @@ import {
25
25
  } from "./transfer/fs-utils.js";
26
26
  import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
27
27
  import { EmbeddingIndexStorageError } from "./embedding-index-storage.js";
28
- import { matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
28
+ import { isEmbeddingGenerationMarkerPath, matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
29
29
  import {
30
30
  applyIncomingEmbeddingGenerations,
31
31
  assertEmbeddingGenerationStillIncluded,
@@ -1696,6 +1696,15 @@ export async function applyOfflineSyncSnapshot(options: {
1696
1696
  }
1697
1697
 
1698
1698
  if (incoming) {
1699
+ // The generation marker (PR #3176) is node-local on BOTH sides: an
1700
+ // incoming copy — old or hostile — never overwrites the local file
1701
+ // and never enters the base set. Other default excludes stay
1702
+ // apply-accepted (#1786: first-sync LCM sqlite bootstrapping).
1703
+ if (isEmbeddingGenerationMarkerPath(relPath)) {
1704
+ nextBase.delete(relPath);
1705
+ skipped += 1;
1706
+ continue;
1707
+ }
1699
1708
  if (currentEntry?.sha256 === incoming.sha256) {
1700
1709
  if (await setSafeFileMtime(root, relPath, incoming.mtimeMs)) {
1701
1710
  nextBase.set(relPath, toFileState(incoming));
@@ -1933,6 +1942,10 @@ export async function applyOfflineSyncChangeset(options: {
1933
1942
  for (const change of changeset.changes) {
1934
1943
  if (generationRouting.conflictedPaths.has(change.path)) continue;
1935
1944
  if (generationRouting.transactionHandled.has(change.path)) continue;
1945
+ if (isEmbeddingGenerationMarkerPath(change.path)) {
1946
+ skipped += 1;
1947
+ continue;
1948
+ }
1936
1949
  const currentEntry = currentMap.get(change.path);
1937
1950
  if (change.type === "upsert") {
1938
1951
  if (currentEntry?.sha256 === change.file.sha256) {
@@ -2225,6 +2238,9 @@ export async function applyOfflineSyncFileContentChunk(options: {
2225
2238
  if (shouldExcludeRelPath(relPath, includeTranscripts)) {
2226
2239
  throw new Error(`offline sync file content path is excluded: ${relPath}`);
2227
2240
  }
2241
+ if (isEmbeddingGenerationMarkerPath(relPath)) {
2242
+ throw new Error(`offline sync file content path is node-local: ${relPath}`);
2243
+ }
2228
2244
  const sha256 = assertSha256(options.sha256, "sha256");
2229
2245
  const bytes = assertNonNegativeInteger(options.bytes, "bytes");
2230
2246
  const mtimeMs = assertOfflineSyncMtimeMs(options.mtimeMs, "mtimeMs");
@@ -558,8 +558,10 @@ export async function runPreferenceDriftScan(
558
558
  // shared corpus version out of band — otherwise hot recall caches keep
559
559
  // serving pre-stamp frontmatter and the damping stage never sees the new
560
560
  // `driftState` (§25/§31).
561
- bumpMemoryCorpusVersionForDir(storage.dir);
562
- if (storage.dir !== deps.memoryDir) bumpMemoryCorpusVersionForDir(deps.memoryDir);
561
+ bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
562
+ if (storage.dir !== deps.memoryDir) {
563
+ bumpMemoryCorpusVersionForDir(deps.memoryDir, { indexedText: false });
564
+ }
563
565
  }
564
566
  try {
565
567
  await writeJsonFileAtomic(driftMarkerPath(deps.memoryDir), {
@@ -640,7 +642,7 @@ export async function resolvePreferenceDrift(
640
642
  { lastCorroborated: nowIso, driftState: undefined, updated: nowIso },
641
643
  { at: now, actor: DRIFT_ACTOR, reasonCode: "drift_kept", ruleVersion: DRIFT_RULE_VERSION },
642
644
  );
643
- bumpMemoryCorpusVersionForDir(storage.dir);
645
+ bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
644
646
  touched();
645
647
  return { affectedIds: [preferenceId], message: `Preference ${preferenceId} confirmed still current` };
646
648
  }
@@ -526,9 +526,10 @@ export async function runProcedureLibraryMaintenance(options: {
526
526
  report.appliedCount = report.applied.length;
527
527
 
528
528
  if (writes > 0) {
529
- // Out-of-band corpus bump + marker so hot caches and the stats surface
530
- // observe the pass (§25/§31 — direct writes must not leave stale views).
531
- bumpMemoryCorpusVersionForDir(options.memoryDir);
529
+ // Corpus bump so hot caches observe the pass. Archive already moved the
530
+ // mention epoch when it removed the live file. Merge-supersede and repair
531
+ // stamps are metadata-only, so this bump must not move the epoch.
532
+ bumpMemoryCorpusVersionForDir(options.memoryDir, { indexedText: false });
532
533
  }
533
534
  try {
534
535
  await writeJsonFileAtomic(maintenanceMarkerPath(options.memoryDir), {
@@ -610,7 +610,7 @@ export async function promoteSemanticRuleFromMemory(options: {
610
610
  };
611
611
 
612
612
  return withPromotionLock(options.memoryDir, ruleKey, async (lock) => {
613
- storage.invalidateAllMemoriesCacheForDir();
613
+ storage.invalidateAllMemoriesCacheForDir({ indexedText: false });
614
614
  const existingRule = (await storage.readAllMemories()).find(
615
615
  (memory) =>
616
616
  memory.frontmatter.category === "rule" &&
@@ -0,0 +1,44 @@
1
+ import { entityMentionEpoch } from "../entity-mention-epoch.js";
2
+ import type { MemoryFile, MemoryFrontmatter } from "../types.js";
3
+ import {
4
+ buildCapturePathLockIdentity,
5
+ buildExplicitCaptureDedupKey,
6
+ } from "./tombstone-blocked-capture-mutation.js";
7
+
8
+ type MentionHoldStorage = {
9
+ withTombstoneBlockedCaptureWriteLock<T>(
10
+ task: () => Promise<T>,
11
+ identity?: readonly string[],
12
+ ): Promise<T>;
13
+ readMemoryByPath(pathname: string): Promise<MemoryFile | null>;
14
+ };
15
+
16
+ /**
17
+ * Metadata frontmatter writes may suppress the mention epoch only when the
18
+ * file still has the same body, entityRef, and origin while the path lock is
19
+ * held. The write runs in that same lock so a concurrent body or entityRef
20
+ * change cannot be overwritten under suppression.
21
+ */
22
+ export async function holdMentionNeutralFrontmatter<T>(
23
+ storage: MentionHoldStorage,
24
+ memory: MemoryFile,
25
+ updated: MemoryFrontmatter,
26
+ write: () => Promise<T>,
27
+ ): Promise<T | undefined> {
28
+ const identities = [
29
+ buildCapturePathLockIdentity(memory.path),
30
+ buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector),
31
+ buildExplicitCaptureDedupKey(memory.content, updated.category, updated.sourceConnector),
32
+ ];
33
+ return storage.withTombstoneBlockedCaptureWriteLock(async () => {
34
+ const persisted = await storage.readMemoryByPath(memory.path);
35
+ if (
36
+ !persisted ||
37
+ persisted.content !== memory.content ||
38
+ !entityMentionEpoch.neutral(persisted.frontmatter, updated)
39
+ ) {
40
+ return undefined;
41
+ }
42
+ return write();
43
+ }, identities);
44
+ }