@remnic/core 9.3.749 → 9.3.751

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 (271) hide show
  1. package/dist/access-boundary.d.ts +8 -8
  2. package/dist/access-boundary.js +15 -15
  3. package/dist/access-cli.js +39 -39
  4. package/dist/access-http.d.ts +8 -8
  5. package/dist/access-http.js +20 -20
  6. package/dist/access-mcp.d.ts +8 -8
  7. package/dist/access-mcp.js +19 -19
  8. package/dist/access-operations-batch.js +16 -16
  9. package/dist/access-operations.d.ts +8 -8
  10. package/dist/access-operations.js +18 -18
  11. package/dist/access-schema.d.ts +2 -2
  12. package/dist/access-schema.js +5 -5
  13. package/dist/{access-service-5-EVpt3v.d.ts → access-service-DmAEOJdJ.d.ts} +11 -3
  14. package/dist/access-service.d.ts +5 -5
  15. package/dist/access-service.js +14 -14
  16. package/dist/access-surface-catalog.d.ts +8 -8
  17. package/dist/action-confidence.d.ts +1 -1
  18. package/dist/active-memory-bridge.d.ts +1 -1
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/active-recall.js +8 -1
  21. package/dist/active-recall.js.map +1 -1
  22. package/dist/behavior-learner.d.ts +1 -1
  23. package/dist/behavior-signals.d.ts +1 -1
  24. package/dist/bootstrap.d.ts +7 -7
  25. package/dist/briefing.d.ts +1 -1
  26. package/dist/briefing.js +7 -7
  27. package/dist/buffer-surprise-report.d.ts +1 -1
  28. package/dist/buffer.d.ts +1 -1
  29. package/dist/calibration.d.ts +1 -1
  30. package/dist/capabilities.d.ts +1 -1
  31. package/dist/{catalog-D_ZDxu8Y.d.ts → catalog-BpOcwHdE.d.ts} +1 -1
  32. package/dist/causal-behavior.d.ts +1 -1
  33. package/dist/causal-consolidation.d.ts +1 -1
  34. package/dist/causal-consolidation.js +8 -8
  35. package/dist/causal-trajectory-graph.d.ts +1 -1
  36. package/dist/{chunk-C5WN74HH.js → chunk-2HAG7JOI.js} +8 -2
  37. package/dist/chunk-2HAG7JOI.js.map +1 -0
  38. package/dist/{chunk-GKRLPHPU.js → chunk-2KK5WNQB.js} +2 -2
  39. package/dist/{chunk-JRDPT6DA.js → chunk-4NWIGAIC.js} +2 -2
  40. package/dist/{chunk-OBJUKYDO.js → chunk-4XL3CVII.js} +2 -2
  41. package/dist/{chunk-WWVXXSLF.js → chunk-5PZCDUJ6.js} +4 -4
  42. package/dist/{chunk-URQLO7BX.js → chunk-6FNZ3NMS.js} +2 -2
  43. package/dist/{chunk-DRUDC2UX.js → chunk-6MMONPHM.js} +2 -2
  44. package/dist/{chunk-RB6A7OUN.js → chunk-6SXNQFHB.js} +2 -2
  45. package/dist/{chunk-ZGZNLRKF.js → chunk-7TWA7DKP.js} +5 -5
  46. package/dist/{chunk-KMNYXLPL.js → chunk-B35L45HZ.js} +2 -2
  47. package/dist/{chunk-SIV3JZPP.js → chunk-CGZWGVQF.js} +5 -5
  48. package/dist/{chunk-CKFLLAIS.js → chunk-DANV2HTH.js} +3 -3
  49. package/dist/{chunk-NVYF5OB6.js → chunk-DBRVU5PR.js} +3 -3
  50. package/dist/{chunk-LMFS64GY.js → chunk-E4PFDD3N.js} +4 -4
  51. package/dist/{chunk-DIHCFJVV.js → chunk-HYI2GP3F.js} +4 -4
  52. package/dist/{chunk-UIKZZ242.js → chunk-IKHKHADN.js} +2 -2
  53. package/dist/{chunk-PUR2CKX2.js → chunk-JS2HKKOK.js} +1 -1
  54. package/dist/{chunk-PUR2CKX2.js.map → chunk-JS2HKKOK.js.map} +1 -1
  55. package/dist/{chunk-R2OAN6IP.js → chunk-K2JM4DZZ.js} +2 -2
  56. package/dist/{chunk-3IJGF6K2.js → chunk-K2R3DEEH.js} +2 -2
  57. package/dist/{chunk-HIHCB2S6.js → chunk-QRRBXD24.js} +2 -2
  58. package/dist/{chunk-PNUOQYRJ.js → chunk-RWZB6MAY.js} +1953 -2172
  59. package/dist/chunk-RWZB6MAY.js.map +1 -0
  60. package/dist/{chunk-ZPZ4VVLT.js → chunk-SD34EK4Z.js} +2 -2
  61. package/dist/{chunk-WK3NK6SP.js → chunk-SNEXVX3U.js} +6 -6
  62. package/dist/{chunk-FVZ2ISIX.js → chunk-TPLE6SNY.js} +7 -7
  63. package/dist/{chunk-24YDB5QP.js → chunk-UETBEHBA.js} +44 -24
  64. package/dist/chunk-UETBEHBA.js.map +1 -0
  65. package/dist/{chunk-5IN26V6W.js → chunk-UU32EAIY.js} +5 -5
  66. package/dist/{chunk-ZGAJB74V.js → chunk-W3BFT5OJ.js} +2 -2
  67. package/dist/{chunk-L4N7SVNN.js → chunk-WA7FECSG.js} +11 -11
  68. package/dist/{chunk-Q3X7GUAQ.js → chunk-WOX2UW4K.js} +2 -2
  69. package/dist/{chunk-XTEWVF3C.js → chunk-WWSXVOGY.js} +2 -2
  70. package/dist/{chunk-PWWWLD7D.js → chunk-X64YLC24.js} +153 -27
  71. package/dist/chunk-X64YLC24.js.map +1 -0
  72. package/dist/{chunk-P4FW3ZU5.js → chunk-YGGXUNS4.js} +20 -20
  73. package/dist/{chunk-JHQUIH3W.js → chunk-ZBVQ4ZM4.js} +19 -19
  74. package/dist/{cli-DuWWjaKT.d.ts → cli-lmf5RGyQ.d.ts} +3 -3
  75. package/dist/cli.d.ts +9 -9
  76. package/dist/cli.js +33 -33
  77. package/dist/compounding/engine.d.ts +1 -1
  78. package/dist/compounding/engine.js +7 -7
  79. package/dist/compounding/preference-consolidator.d.ts +1 -1
  80. package/dist/compression-optimizer.d.ts +1 -1
  81. package/dist/config.d.ts +1 -1
  82. package/dist/config.js +8 -1
  83. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  84. package/dist/connectors/codex-materialize-runner.js +7 -7
  85. package/dist/connectors/codex-materialize.d.ts +1 -1
  86. package/dist/connectors/index.d.ts +1 -1
  87. package/dist/connectors/index.js +7 -7
  88. package/dist/consolidation-provenance-check.d.ts +1 -1
  89. package/dist/consolidation-undo.d.ts +1 -1
  90. package/dist/contradiction/index.d.ts +1 -1
  91. package/dist/conversation-index/backend.d.ts +1 -1
  92. package/dist/conversation-index/chunker.d.ts +1 -1
  93. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  94. package/dist/conversation-index/indexer.d.ts +1 -1
  95. package/dist/conversation-index/search.d.ts +1 -1
  96. package/dist/day-summary.d.ts +1 -1
  97. package/dist/delinearize.d.ts +1 -1
  98. package/dist/direct-answer-wiring.d.ts +1 -1
  99. package/dist/direct-answer.d.ts +1 -1
  100. package/dist/embedding-fallback.d.ts +1 -1
  101. package/dist/enrichment/index.d.ts +1 -1
  102. package/dist/entity-retrieval.d.ts +1 -1
  103. package/dist/entity-retrieval.js +7 -7
  104. package/dist/entity-schema.d.ts +1 -1
  105. package/dist/explicit-capture.d.ts +7 -7
  106. package/dist/extraction-faithfulness.d.ts +1 -1
  107. package/dist/extraction-judge-telemetry.d.ts +1 -1
  108. package/dist/extraction-judge-training.d.ts +1 -1
  109. package/dist/extraction-judge.d.ts +1 -1
  110. package/dist/extraction.d.ts +1 -1
  111. package/dist/fallback-llm.d.ts +1 -1
  112. package/dist/graph-dashboard-diff.d.ts +1 -1
  113. package/dist/graph-dashboard-key.d.ts +1 -1
  114. package/dist/graph-dashboard-parser.d.ts +1 -1
  115. package/dist/graph-edge-reinforcement.d.ts +1 -1
  116. package/dist/graph-snapshot.d.ts +1 -1
  117. package/dist/graph.d.ts +1 -1
  118. package/dist/identity-continuity.d.ts +1 -1
  119. package/dist/importance.d.ts +1 -1
  120. package/dist/index.d.ts +10 -10
  121. package/dist/index.js +76 -72
  122. package/dist/intent.d.ts +1 -1
  123. package/dist/lcm/engine.d.ts +1 -1
  124. package/dist/lcm/index.d.ts +1 -1
  125. package/dist/lcm/tools.d.ts +1 -1
  126. package/dist/lifecycle.d.ts +1 -1
  127. package/dist/live-connectors-runner.d.ts +1 -1
  128. package/dist/local-llm.d.ts +1 -1
  129. package/dist/local-model-endpoint.d.ts +1 -1
  130. package/dist/maintenance/memory-governance.d.ts +1 -1
  131. package/dist/maintenance/memory-governance.js +7 -7
  132. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
  133. package/dist/maintenance/rebuild-memory-projection.js +8 -8
  134. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  135. package/dist/memory-action-policy.d.ts +1 -1
  136. package/dist/memory-cache.d.ts +1 -1
  137. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  138. package/dist/memory-projection-store.d.ts +1 -1
  139. package/dist/memory-provenance.d.ts +1 -1
  140. package/dist/memory-worth-outcomes.d.ts +1 -1
  141. package/dist/models-json.d.ts +1 -1
  142. package/dist/namespaces/migrate.d.ts +2 -2
  143. package/dist/namespaces/migrate.js +8 -8
  144. package/dist/namespaces/principal.d.ts +1 -1
  145. package/dist/namespaces/search.d.ts +1 -1
  146. package/dist/namespaces/search.js +1 -1
  147. package/dist/namespaces/storage.d.ts +2 -2
  148. package/dist/namespaces/storage.js +7 -7
  149. package/dist/native-knowledge.d.ts +1 -1
  150. package/dist/offline-sync.d.ts +108 -1
  151. package/dist/offline-sync.js +7 -1
  152. package/dist/operator-toolkit.d.ts +1 -1
  153. package/dist/operator-toolkit.js +15 -12
  154. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  155. package/dist/orchestration/maintenance.d.ts +2 -2
  156. package/dist/orchestration/maintenance.js +9 -9
  157. package/dist/{orchestrator-CZI6hO_R.d.ts → orchestrator-D7bb8ZGp.d.ts} +193 -201
  158. package/dist/orchestrator.d.ts +5 -5
  159. package/dist/orchestrator.js +51 -41
  160. package/dist/patterns-cli.d.ts +1 -1
  161. package/dist/policy-runtime.d.ts +1 -1
  162. package/dist/provenance.d.ts +1 -1
  163. package/dist/qmd-recall-cache.d.ts +1 -1
  164. package/dist/qmd.d.ts +1 -1
  165. package/dist/recall-disclosure-escalation.d.ts +1 -1
  166. package/dist/recall-explain-renderer.d.ts +1 -1
  167. package/dist/recall-explain-renderer.js +3 -3
  168. package/dist/recall-planner-llm.d.ts +1 -1
  169. package/dist/recall-state.d.ts +1 -1
  170. package/dist/recall-tag-filter.d.ts +1 -1
  171. package/dist/recall-xray-cli.d.ts +1 -1
  172. package/dist/recall-xray-cli.js +4 -4
  173. package/dist/recall-xray-renderer.d.ts +1 -1
  174. package/dist/recall-xray-renderer.js +3 -3
  175. package/dist/recall-xray.d.ts +1 -1
  176. package/dist/recall-xray.js +2 -2
  177. package/dist/resolve-auth-token.d.ts +1 -1
  178. package/dist/resume-bundles.js +9 -2
  179. package/dist/retrieval-agents.d.ts +1 -1
  180. package/dist/retrieval-tiers.d.ts +1 -1
  181. package/dist/routing/engine.d.ts +1 -1
  182. package/dist/routing/store.d.ts +1 -1
  183. package/dist/schemas.d.ts +22 -22
  184. package/dist/search/embed-helper.d.ts +1 -1
  185. package/dist/search/factory.d.ts +1 -1
  186. package/dist/search/factory.js +1 -1
  187. package/dist/search/index.d.ts +1 -1
  188. package/dist/search/index.js +1 -1
  189. package/dist/search/lancedb-backend.d.ts +1 -1
  190. package/dist/search/lancedb-backend.js +1 -1
  191. package/dist/search/meilisearch-backend.d.ts +1 -1
  192. package/dist/search/meilisearch-backend.js +1 -1
  193. package/dist/search/noop-backend.d.ts +1 -1
  194. package/dist/search/orama-backend.d.ts +1 -1
  195. package/dist/search/orama-backend.js +1 -1
  196. package/dist/search/port.d.ts +1 -1
  197. package/dist/search/remote-backend.d.ts +1 -1
  198. package/dist/{semantic-consolidation-D3aSiu9t.d.ts → semantic-consolidation-CTfbVwCA.d.ts} +1 -1
  199. package/dist/semantic-consolidation.d.ts +2 -2
  200. package/dist/semantic-consolidation.js +8 -8
  201. package/dist/semantic-rule-promotion.js +7 -7
  202. package/dist/semantic-rule-verifier.d.ts +1 -1
  203. package/dist/semantic-rule-verifier.js +7 -7
  204. package/dist/session-observer-bands.d.ts +1 -1
  205. package/dist/session-observer-state.d.ts +1 -1
  206. package/dist/shared-context/manager.d.ts +1 -1
  207. package/dist/signal.d.ts +1 -1
  208. package/dist/storage.d.ts +1 -1
  209. package/dist/storage.js +6 -6
  210. package/dist/summarizer.d.ts +1 -1
  211. package/dist/summary-snapshot.d.ts +1 -1
  212. package/dist/temporal-supersession.d.ts +1 -1
  213. package/dist/temporal-validity.d.ts +1 -1
  214. package/dist/threading.d.ts +1 -1
  215. package/dist/tier-migration.d.ts +1 -1
  216. package/dist/tier-routing.d.ts +1 -1
  217. package/dist/topics.d.ts +1 -1
  218. package/dist/transcript.d.ts +1 -1
  219. package/dist/transfer/types.d.ts +12 -12
  220. package/dist/trust-score-stage.d.ts +1 -1
  221. package/dist/trust-score.d.ts +1 -1
  222. package/dist/{types-CZm6rx9-.d.ts → types-D4NYDtXI.d.ts} +7 -0
  223. package/dist/types.d.ts +1 -1
  224. package/dist/types.js +1 -1
  225. package/dist/utility-runtime.d.ts +1 -1
  226. package/dist/verified-recall.js +7 -7
  227. package/package.json +2 -2
  228. package/src/access-service.ts +22 -0
  229. package/src/config.ts +5 -0
  230. package/src/index.ts +3 -1
  231. package/src/offline-sync.test.ts +354 -52
  232. package/src/offline-sync.ts +332 -24
  233. package/src/orchestration/extraction-run.ts +4 -34
  234. package/src/orchestration/namespace-read-fanout.ts +484 -0
  235. package/src/orchestration/orchestrator-helpers.ts +1130 -0
  236. package/src/orchestration/self-deps.ts +50 -0
  237. package/src/orchestration/workspace-ops.ts +789 -0
  238. package/src/orchestrator.ts +177 -2296
  239. package/src/types.ts +9 -0
  240. package/dist/chunk-24YDB5QP.js.map +0 -1
  241. package/dist/chunk-C5WN74HH.js.map +0 -1
  242. package/dist/chunk-PNUOQYRJ.js.map +0 -1
  243. package/dist/chunk-PWWWLD7D.js.map +0 -1
  244. /package/dist/{chunk-GKRLPHPU.js.map → chunk-2KK5WNQB.js.map} +0 -0
  245. /package/dist/{chunk-JRDPT6DA.js.map → chunk-4NWIGAIC.js.map} +0 -0
  246. /package/dist/{chunk-OBJUKYDO.js.map → chunk-4XL3CVII.js.map} +0 -0
  247. /package/dist/{chunk-WWVXXSLF.js.map → chunk-5PZCDUJ6.js.map} +0 -0
  248. /package/dist/{chunk-URQLO7BX.js.map → chunk-6FNZ3NMS.js.map} +0 -0
  249. /package/dist/{chunk-DRUDC2UX.js.map → chunk-6MMONPHM.js.map} +0 -0
  250. /package/dist/{chunk-RB6A7OUN.js.map → chunk-6SXNQFHB.js.map} +0 -0
  251. /package/dist/{chunk-ZGZNLRKF.js.map → chunk-7TWA7DKP.js.map} +0 -0
  252. /package/dist/{chunk-KMNYXLPL.js.map → chunk-B35L45HZ.js.map} +0 -0
  253. /package/dist/{chunk-SIV3JZPP.js.map → chunk-CGZWGVQF.js.map} +0 -0
  254. /package/dist/{chunk-CKFLLAIS.js.map → chunk-DANV2HTH.js.map} +0 -0
  255. /package/dist/{chunk-NVYF5OB6.js.map → chunk-DBRVU5PR.js.map} +0 -0
  256. /package/dist/{chunk-LMFS64GY.js.map → chunk-E4PFDD3N.js.map} +0 -0
  257. /package/dist/{chunk-DIHCFJVV.js.map → chunk-HYI2GP3F.js.map} +0 -0
  258. /package/dist/{chunk-UIKZZ242.js.map → chunk-IKHKHADN.js.map} +0 -0
  259. /package/dist/{chunk-R2OAN6IP.js.map → chunk-K2JM4DZZ.js.map} +0 -0
  260. /package/dist/{chunk-3IJGF6K2.js.map → chunk-K2R3DEEH.js.map} +0 -0
  261. /package/dist/{chunk-HIHCB2S6.js.map → chunk-QRRBXD24.js.map} +0 -0
  262. /package/dist/{chunk-ZPZ4VVLT.js.map → chunk-SD34EK4Z.js.map} +0 -0
  263. /package/dist/{chunk-WK3NK6SP.js.map → chunk-SNEXVX3U.js.map} +0 -0
  264. /package/dist/{chunk-FVZ2ISIX.js.map → chunk-TPLE6SNY.js.map} +0 -0
  265. /package/dist/{chunk-5IN26V6W.js.map → chunk-UU32EAIY.js.map} +0 -0
  266. /package/dist/{chunk-ZGAJB74V.js.map → chunk-W3BFT5OJ.js.map} +0 -0
  267. /package/dist/{chunk-L4N7SVNN.js.map → chunk-WA7FECSG.js.map} +0 -0
  268. /package/dist/{chunk-Q3X7GUAQ.js.map → chunk-WOX2UW4K.js.map} +0 -0
  269. /package/dist/{chunk-XTEWVF3C.js.map → chunk-WWSXVOGY.js.map} +0 -0
  270. /package/dist/{chunk-P4FW3ZU5.js.map → chunk-YGGXUNS4.js.map} +0 -0
  271. /package/dist/{chunk-JHQUIH3W.js.map → chunk-ZBVQ4ZM4.js.map} +0 -0
@@ -14,6 +14,8 @@ import {
14
14
  buildOfflineSyncSnapshot,
15
15
  buildOfflineSyncSnapshotFromBase,
16
16
  buildOfflineSyncSnapshotForPaths,
17
+ compileOfflineSyncExcludeGlobs,
18
+ globToRegExp,
17
19
  OFFLINE_SYNC_MAX_MTIME_MS,
18
20
  readOfflineSyncFileContentChunk,
19
21
  shouldPreferIncomingOfflineRuntimeFile,
@@ -74,9 +76,6 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
74
76
  "state/last_intent.json",
75
77
  "state/last_qmd_recall.json",
76
78
  "state/last_recall.json",
77
- "state/lcm.sqlite",
78
- "state/lcm.sqlite-shm",
79
- "state/lcm.sqlite-wal",
80
79
  "transcripts/session.jsonl",
81
80
  ],
82
81
  );
@@ -101,9 +100,6 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
101
100
  "state/last_intent.json",
102
101
  "state/last_qmd_recall.json",
103
102
  "state/last_recall.json",
104
- "state/lcm.sqlite",
105
- "state/lcm.sqlite-shm",
106
- "state/lcm.sqlite-wal",
107
103
  ],
108
104
  );
109
105
  } finally {
@@ -191,7 +187,7 @@ test("offline sync includes retrieval debug snapshots for full-fidelity offline
191
187
  }
192
188
  });
193
189
 
194
- test("offline sync includes live LCM sqlite artifacts for full-fidelity offline mode", async () => {
190
+ test("offline sync push-side default excludes live LCM sqlite but apply-side still accepts it (#1786)", async () => {
195
191
  const root = await tempDir("remnic-offline-lcm-sqlite");
196
192
  try {
197
193
  await write(root, "facts/a.md", "alpha");
@@ -199,33 +195,53 @@ test("offline sync includes live LCM sqlite artifacts for full-fidelity offline
199
195
  await write(root, "state/lcm.sqlite-shm", "live shm");
200
196
  await write(root, "state/lcm.sqlite-wal", "live wal");
201
197
 
198
+ // Push-side enumeration: node-local sqlite state is excluded by default
199
+ // so the local node does not broadcast its live sqlite WAL/SHM to peers.
202
200
  const snapshot = await buildOfflineSyncSnapshot({
203
201
  root,
204
202
  sourceId: "remote",
205
203
  includeContent: true,
206
204
  });
207
205
 
208
- assert.deepEqual(snapshot.files.map((file) => file.path), [
209
- "facts/a.md",
210
- "state/lcm.sqlite",
211
- "state/lcm.sqlite-shm",
212
- "state/lcm.sqlite-wal",
213
- ]);
206
+ assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
214
207
  assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/lcm.sqlite-shm"), true);
215
208
  assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/lcm.sqlite-wal"), true);
216
209
  assert.equal(shouldPreferIncomingOfflineRuntimeFile("state/last_qmd_recall.json"), true);
217
- const focused = await buildOfflineSyncSnapshotForPaths({
218
- root,
219
- sourceId: "remote",
220
- paths: ["state/lcm.sqlite"],
221
- includeContent: true,
222
- });
223
- assert.deepEqual(focused.files.map((file) => file.path), ["state/lcm.sqlite"]);
224
210
 
211
+ // buildOfflineSyncSnapshotForPaths still rejects explicitly excluded
212
+ // paths so callers cannot bypass the default exclude via path lists.
213
+ await assert.rejects(
214
+ () =>
215
+ buildOfflineSyncSnapshotForPaths({
216
+ root,
217
+ sourceId: "remote",
218
+ paths: ["state/lcm.sqlite"],
219
+ includeContent: true,
220
+ }),
221
+ /offline sync snapshot path is excluded/,
222
+ );
223
+
224
+ // Apply-side: the remote is authoritative for lcm.sqlite and we must
225
+ // still accept the file content when an upstream snapshot includes it
226
+ // (e.g. first sync bootstrapping the LCM DB).
227
+ const remoteSnapshot = {
228
+ format: "remnic.offline-sync.snapshot.v1" as const,
229
+ schemaVersion: 1 as const,
230
+ createdAt: new Date().toISOString(),
231
+ sourceId: "remote",
232
+ includeTranscripts: true,
233
+ files: [{
234
+ path: "state/lcm.sqlite",
235
+ sha256: createHash("sha256").update("live db").digest("hex"),
236
+ bytes: Buffer.byteLength("live db"),
237
+ mtimeMs: 0,
238
+ contentBase64: Buffer.from("live db").toString("base64"),
239
+ }],
240
+ };
225
241
  const oldDb = Buffer.from("old live db");
226
242
  const pull = await applyOfflineSyncSnapshot({
227
243
  root,
228
- snapshot,
244
+ snapshot: remoteSnapshot,
229
245
  baseFiles: [{
230
246
  path: "state/lcm.sqlite",
231
247
  sha256: createHash("sha256").update(oldDb).digest("hex"),
@@ -234,14 +250,27 @@ test("offline sync includes live LCM sqlite artifacts for full-fidelity offline
234
250
  }],
235
251
  });
236
252
 
237
- assert.equal(pull.skipped, 4);
253
+ // Corrected apply contract (#1793 review): the apply-side view SEES the
254
+ // local sqlite (identical content -> no write, no conflict), the local
255
+ // sidecars survive (node-local guard: absence from a push-filtered
256
+ // snapshot is not a delete instruction), and nothing is deleted.
257
+ assert.equal(pull.deleted, 0);
258
+ assert.deepEqual(pull.conflicts, []);
238
259
  assert.equal(await readUtf8(root, "state/lcm.sqlite"), "live db");
260
+ assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "live shm");
261
+ assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "live wal");
239
262
 
240
- await write(root, "state/lcm.sqlite-wal", "local wal churn");
263
+ // The push-side changeset excludes lcm.sqlite and friends so the
264
+ // local node never pushes the live sqlite state up.
241
265
  const changeset = await buildOfflineSyncChangeset({
242
266
  root,
243
267
  sourceId: "laptop",
244
- baseFiles: snapshot.files,
268
+ baseFiles: [{
269
+ path: "facts/a.md",
270
+ sha256: createHash("sha256").update("alpha").digest("hex"),
271
+ bytes: Buffer.byteLength("alpha"),
272
+ mtimeMs: 0,
273
+ }],
245
274
  });
246
275
  assert.deepEqual(changeset.changes.map((change) => change.path), []);
247
276
  } finally {
@@ -278,24 +307,23 @@ test("offline sync includes durable runtime state and excludes only transient sy
278
307
  includeContent: true,
279
308
  });
280
309
 
310
+ // Issue #1786: default excludes drop state/*.sqlite*, state/index_tags.json,
311
+ // state/entity-mention-index.json, and state/memory-governance/runs/** from
312
+ // push-side enumeration. Durableremote-authoritative runtime files
313
+ // (last_intent.json, .artifact-write-version.log, etc.) and namespace
314
+ // state still ship so the remote can merge.
281
315
  assert.deepEqual(snapshot.files.map((file) => file.path), [
282
316
  "assets/state/fact-hashes.txt",
283
317
  "facts/a.md",
284
318
  "namespaces/generalist-project-origin-6ebeaa54/state/.memory-status-version.log",
285
- "namespaces/generalist-project-origin-6ebeaa54/state/entity-mention-index.json",
286
319
  "namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json",
287
320
  "state/.artifact-write-version.log",
288
321
  "state/.memory-status-version.log",
289
322
  "state/buffer-surprise-ledger.jsonl",
290
323
  "state/buffer.json",
291
324
  "state/embeddings.json",
292
- "state/entity-mention-index.json",
293
- "state/index_tags.json",
294
325
  "state/index_time.json",
295
326
  "state/memory-lifecycle-ledger.jsonl",
296
- "state/memory-projection.sqlite",
297
- "state/memory-projection.sqlite-shm",
298
- "state/memory-projection.sqlite-wal",
299
327
  "state/recall_impressions.jsonl",
300
328
  ]);
301
329
  const focused = await buildOfflineSyncSnapshotForPaths({
@@ -335,6 +363,10 @@ test("offline sync includes durable runtime state and excludes only transient sy
335
363
  }],
336
364
  });
337
365
 
366
+ // 18 = the 13 durable local-only files plus the 5 node-local excluded
367
+ // artifacts, which the corrected apply-side view (#1793 review) now
368
+ // SEES and deliberately leaves untouched (counted as skipped) instead
369
+ // of hiding from enumeration entirely.
338
370
  assert.equal(pull.skipped, 18);
339
371
  assert.equal(await readUtf8(root, "state/memory-lifecycle-ledger.jsonl"), "ledger");
340
372
  } finally {
@@ -885,7 +917,7 @@ test("offline sync reads bounded file content chunks with metadata", async () =>
885
917
  const root = await tempDir("remnic-offline-file-content");
886
918
  try {
887
919
  await write(root, "artifacts/large.txt", "alpha\nbeta\ngamma\n");
888
- await write(root, "state/lcm.sqlite", "live db");
920
+ await write(root, "facts/small.md", "live db");
889
921
 
890
922
  const chunk = await readOfflineSyncFileContentChunk({
891
923
  root,
@@ -900,11 +932,22 @@ test("offline sync reads bounded file content chunks with metadata", async () =>
900
932
  assert.equal(chunk.content.toString("utf-8"), "beta\n");
901
933
  assert.equal(chunk.bytes, Buffer.byteLength("alpha\nbeta\ngamma\n"));
902
934
 
903
- const lcm = await readOfflineSyncFileContentChunk({
935
+ const factsFile = await readOfflineSyncFileContentChunk({
904
936
  root,
905
- path: "state/lcm.sqlite",
937
+ path: "facts/small.md",
906
938
  });
907
- assert.equal(lcm.content.toString("utf-8"), "live db");
939
+ assert.equal(factsFile.content.toString("utf-8"), "live db");
940
+
941
+ // Issue #1786: push-side default excludes state/lcm.sqlite; reading the
942
+ // chunk for an excluded path is rejected loudly.
943
+ await assert.rejects(
944
+ () =>
945
+ readOfflineSyncFileContentChunk({
946
+ root,
947
+ path: "state/lcm.sqlite",
948
+ }),
949
+ /offline sync file content path is excluded: state\/lcm\.sqlite/,
950
+ );
908
951
  } finally {
909
952
  await rm(root, { recursive: true, force: true });
910
953
  }
@@ -1280,7 +1323,13 @@ test("offline snapshot apply preserves new local runtime files without a base",
1280
1323
  }
1281
1324
  });
1282
1325
 
1283
- test("offline snapshot apply removes current-only sqlite sidecars when absent remotely", async () => {
1326
+ test("offline snapshot apply leaves stale sqlite sidecars in place under push-side default exclude (#1786)", async () => {
1327
+ // Issue #1786: lcm.sqlite-wal/shm are excluded from push-side enumeration
1328
+ // because each node rebuilds its own live sqlite state. The remote never
1329
+ // ships those sidecars, so apply cannot enumerate them as "current-only"
1330
+ // and never issues a delete for them. The local WAL/SHM is naturally
1331
+ // overwritten when the next lcm.sqlite reconciliation lands a fresh
1332
+ // page and the WAL is checkpointed.
1284
1333
  const root = await tempDir("remnic-offline-runtime-sidecar-local-create");
1285
1334
  try {
1286
1335
  await write(root, "state/lcm.sqlite-wal", "stale local wal");
@@ -1299,19 +1348,18 @@ test("offline snapshot apply removes current-only sqlite sidecars when absent re
1299
1348
  baseFiles: [],
1300
1349
  });
1301
1350
 
1302
- assert.equal(pull.deleted, 2);
1351
+ assert.equal(pull.deleted, 0);
1303
1352
  assert.equal(pull.pendingLocal, 0);
1304
- assert.equal(pull.skipped, 0);
1353
+ // The corrected apply-side view (#1793 review) sees both sidecars and
1354
+ // skips them via the node-local guard — visible-but-untouched, rather
1355
+ // than invisible. deleted stays 0 either way.
1356
+ assert.equal(pull.skipped, 2);
1305
1357
  assert.equal(pull.conflicts.length, 0);
1306
1358
  assert.deepEqual(pull.nextBaseFiles, []);
1307
- await assert.rejects(
1308
- () => readFile(path.join(root, "state/lcm.sqlite-wal")),
1309
- /ENOENT/,
1310
- );
1311
- await assert.rejects(
1312
- () => readFile(path.join(root, "state/lcm.sqlite-shm")),
1313
- /ENOENT/,
1314
- );
1359
+ // Local sidecars are still on disk because the remote never asserted
1360
+ // them and the push-side default keeps them out of enumeration.
1361
+ assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "stale local wal");
1362
+ assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "stale local shm");
1315
1363
  } finally {
1316
1364
  await rm(root, { recursive: true, force: true });
1317
1365
  }
@@ -1788,19 +1836,22 @@ test("offline pull can record conflicts without hydrating oversized incoming con
1788
1836
  const remote = await tempDir("remnic-offline-metadata-conflict-remote");
1789
1837
  const local = await tempDir("remnic-offline-metadata-conflict-local");
1790
1838
  try {
1791
- await write(remote, "state/lcm.sqlite", "base");
1839
+ // Issue #1786: live sqlite is excluded by push-side defaults, so use a
1840
+ // regular facts path to exercise the metadata-only conflict path.
1841
+ await write(remote, "facts/big.bin", "base");
1792
1842
  const initial = await buildOfflineSyncSnapshot({
1793
1843
  root: remote,
1794
1844
  sourceId: "remote",
1795
1845
  includeContent: true,
1796
1846
  });
1797
- const firstPull = await applyOfflineSyncSnapshot({
1847
+ await applyOfflineSyncSnapshot({
1798
1848
  root: local,
1799
1849
  snapshot: initial,
1850
+ baseFiles: initial.files,
1800
1851
  });
1801
1852
 
1802
- await write(local, "state/lcm.sqlite", "local edit");
1803
- await write(remote, "state/lcm.sqlite", "remote edit");
1853
+ await write(local, "facts/big.bin", "local edit");
1854
+ await write(remote, "facts/big.bin", "remote edit");
1804
1855
  const metadataOnly = await buildOfflineSyncSnapshot({
1805
1856
  root: remote,
1806
1857
  sourceId: "remote",
@@ -1810,20 +1861,23 @@ test("offline pull can record conflicts without hydrating oversized incoming con
1810
1861
  const secondPull = await applyOfflineSyncSnapshot({
1811
1862
  root: local,
1812
1863
  snapshot: metadataOnly,
1813
- baseFiles: firstPull.nextBaseFiles,
1864
+ // Use the initial base so the apply path can detect both_modified
1865
+ // (local edited from "base" -> "local edit", remote edited from
1866
+ // "base" -> "remote edit").
1867
+ baseFiles: initial.files,
1814
1868
  allowMissingConflictContent: true,
1869
+ writeConflictCopies: false,
1815
1870
  });
1816
1871
 
1817
1872
  assert.equal(secondPull.conflicts.length, 1);
1818
1873
  assert.equal(secondPull.conflicts[0]?.reason, "both_modified");
1819
1874
  assert.equal(secondPull.conflicts[0]?.conflictPath, undefined);
1820
- assert.equal(await readUtf8(local, "state/lcm.sqlite"), "local edit");
1875
+ assert.equal(await readUtf8(local, "facts/big.bin"), "local edit");
1821
1876
  } finally {
1822
1877
  await rm(remote, { recursive: true, force: true });
1823
1878
  await rm(local, { recursive: true, force: true });
1824
1879
  }
1825
1880
  });
1826
-
1827
1881
  test("offline push preserves remote edits when both sides changed since the base", async () => {
1828
1882
  const remote = await tempDir("remnic-offline-push-conflict-remote");
1829
1883
  const local = await tempDir("remnic-offline-push-conflict-local");
@@ -2647,3 +2701,251 @@ test("offline changeset validation reports client input errors with an offline s
2647
2701
  await rm(root, { recursive: true, force: true });
2648
2702
  }
2649
2703
  });
2704
+
2705
+ test("globToRegExp matches single-segment globs and rejects cross-segment or suffix matches (#1786)", () => {
2706
+ const re = globToRegExp("state/*.sqlite");
2707
+ assert.equal(re.test("state/lcm.sqlite"), true);
2708
+ // Single `*` must stay in-segment: a nested file is NOT matched.
2709
+ assert.equal(re.test("state/nested/lcm.sqlite"), false);
2710
+ // Anchored to full path: trailing variants like -wal are NOT matched.
2711
+ assert.equal(re.test("state/lcm.sqlite-wal"), false);
2712
+ });
2713
+
2714
+ test("globToRegExp matches sqlite sidecars via the state/*.sqlite-* pattern (#1786)", () => {
2715
+ const re = globToRegExp("state/*.sqlite-*");
2716
+ assert.equal(re.test("state/lcm.sqlite-wal"), true);
2717
+ assert.equal(re.test("state/lcm.sqlite-shm"), true);
2718
+ // The bare database file is NOT matched by the sidecar pattern.
2719
+ assert.equal(re.test("state/lcm.sqlite"), false);
2720
+ });
2721
+
2722
+ test("globToRegExp leading **/ matches zero or more directory segments (#1786)", () => {
2723
+ const re = globToRegExp("**/foo.md");
2724
+ // Leading ** matches zero segments (file at the root).
2725
+ assert.equal(re.test("foo.md"), true);
2726
+ // And any number of intermediate segments.
2727
+ assert.equal(re.test("a/b/foo.md"), true);
2728
+ assert.equal(re.test("a/b/c/d/foo.md"), true);
2729
+ // No match for an unrelated path.
2730
+ assert.equal(re.test("bar.md"), false);
2731
+ assert.equal(re.test("a/b/bar.md"), false);
2732
+ });
2733
+
2734
+ test("globToRegExp escapes regex metacharacters in literal glob segments (#1786)", () => {
2735
+ // The literal `+` in the glob must NOT act as a regex quantifier.
2736
+ const re = globToRegExp("a+b.md");
2737
+ assert.equal(re.test("aab.md"), false);
2738
+ assert.equal(re.test("a+b.md"), true);
2739
+ });
2740
+
2741
+ test("globToRegExp rejects empty input and strings containing NUL (#1786)", () => {
2742
+ assert.throws(() => globToRegExp(""), /non-empty string/);
2743
+ assert.throws(() => globToRegExp("state/with\0nul"), /NUL bytes/);
2744
+ });
2745
+
2746
+ test("globToRegExp treats ** as cross-segment in trailing and embedded positions (#1786 review)", () => {
2747
+ // Trailing `dir/**` must exclude the whole subtree, matching the
2748
+ // offline-mode guide's `scratch/**` example (Cursor review, PR #1793).
2749
+ const trailing = globToRegExp("scratch/**");
2750
+ assert.equal(trailing.test("scratch/a.md"), true);
2751
+ assert.equal(trailing.test("scratch/a/b.md"), true);
2752
+ assert.equal(trailing.test("scratch"), false);
2753
+ assert.equal(trailing.test("other/scratch/a.md"), false);
2754
+ // Embedded `a/**/b` spans any depth (including zero extra segments).
2755
+ const embedded = globToRegExp("state/**/runs.json");
2756
+ assert.equal(embedded.test("state/runs.json"), true);
2757
+ assert.equal(embedded.test("state/a/runs.json"), true);
2758
+ assert.equal(embedded.test("state/a/b/runs.json"), true);
2759
+ assert.equal(embedded.test("state/a/b/other.json"), false);
2760
+ });
2761
+
2762
+ test("default excludes cover per-namespace state dirs (#1793 review)", () => {
2763
+ const sqlite = globToRegExp("**/state/*.sqlite");
2764
+ assert.equal(sqlite.test("state/lcm.sqlite"), true);
2765
+ assert.equal(sqlite.test("namespaces/team-a/state/lcm.sqlite"), true);
2766
+ assert.equal(sqlite.test("facts/lcm.sqlite"), false);
2767
+ });
2768
+
2769
+ test("apply-side local enumeration sees node-local state via excludeNodeLocalState=false (#1793 review)", async () => {
2770
+ const root = await tempDir("remnic-offline-apply-view");
2771
+ try {
2772
+ await write(root, "facts/a.md", "alpha");
2773
+ await write(root, "state/lcm.sqlite", "live db");
2774
+ const pushView = await buildOfflineSyncSnapshot({ root, sourceId: "local", includeContent: false });
2775
+ assert.deepEqual(pushView.files.map((f) => f.path), ["facts/a.md"]);
2776
+ const applyView = await buildOfflineSyncSnapshot({
2777
+ root,
2778
+ sourceId: "local",
2779
+ includeContent: false,
2780
+ excludeNodeLocalState: false,
2781
+ });
2782
+ assert.deepEqual(applyView.files.map((f) => f.path), ["facts/a.md", "state/lcm.sqlite"]);
2783
+ } finally {
2784
+ await rm(root, { recursive: true, force: true });
2785
+ }
2786
+ });
2787
+
2788
+ test("applyOfflineSyncChangeset accepts an incoming node-local state upsert without currentFiles (#1793 review)", async () => {
2789
+ // Forces the internal buildOfflineSyncSnapshotForPaths local enumeration:
2790
+ // with push-side excludes it would THROW "path is excluded" and fail the
2791
+ // whole apply. Apply-side must stay permissive.
2792
+ const root = await tempDir("remnic-offline-apply-state-upsert");
2793
+ try {
2794
+ const content = Buffer.from("incoming durable sqlite", "utf-8");
2795
+ const result = await applyOfflineSyncChangeset({
2796
+ root,
2797
+ changeset: {
2798
+ format: "remnic.offline-sync.changeset.v1",
2799
+ schemaVersion: 1,
2800
+ createdAt: new Date().toISOString(),
2801
+ sourceId: "laptop",
2802
+ includeTranscripts: true,
2803
+ changes: [
2804
+ {
2805
+ type: "upsert",
2806
+ path: "state/lcm.sqlite",
2807
+ file: {
2808
+ path: "state/lcm.sqlite",
2809
+ sha256: createHash("sha256").update(content).digest("hex"),
2810
+ bytes: content.length,
2811
+ mtimeMs: Date.now(),
2812
+ contentBase64: content.toString("base64"),
2813
+ },
2814
+ },
2815
+ ],
2816
+ },
2817
+ });
2818
+ assert.equal(result.appliedUpserts, 1);
2819
+ assert.equal(await readUtf8(root, "state/lcm.sqlite"), "incoming durable sqlite");
2820
+ } finally {
2821
+ await rm(root, { recursive: true, force: true });
2822
+ }
2823
+ });
2824
+
2825
+ test("buildOfflineSyncChangesetFromSnapshot honors excludePaths for 3-strikes skipped files (#1793 review)", async () => {
2826
+ const root = await tempDir("remnic-offline-changeset-skip");
2827
+ try {
2828
+ await write(root, "facts/a.md", "alpha");
2829
+ await write(root, "facts/huge.md", "huge changed content");
2830
+ const current = await buildOfflineSyncSnapshot({ root, sourceId: "local", includeContent: false });
2831
+ const changeset = await buildOfflineSyncChangesetFromSnapshot({
2832
+ root,
2833
+ sourceId: "local",
2834
+ currentFiles: current.files,
2835
+ baseFiles: [],
2836
+ excludePaths: ["facts/huge.md"],
2837
+ });
2838
+ // The skipped file must not ride the inline changeset path.
2839
+ assert.deepEqual(changeset.changes.map((c) => c.path), ["facts/a.md"]);
2840
+ } finally {
2841
+ await rm(root, { recursive: true, force: true });
2842
+ }
2843
+ });
2844
+
2845
+ test("buildOfflineSyncSnapshotForPaths deduplicates repeated request paths (#1786 review)", async () => {
2846
+ const root = await tempDir("remnic-offline-forpaths-dedup");
2847
+ try {
2848
+ await write(root, "facts/a.md", "alpha");
2849
+ const snapshot = await buildOfflineSyncSnapshotForPaths({
2850
+ root,
2851
+ sourceId: "remote",
2852
+ paths: ["facts/a.md", "facts/a.md", "facts/a.md"],
2853
+ includeContent: false,
2854
+ });
2855
+ // Duplicate entries in paths[] must yield exactly one record —
2856
+ // the seen-set add was dropped in an earlier refactor (Cursor
2857
+ // review, PR #1793) and produced duplicate file records.
2858
+ assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
2859
+ } finally {
2860
+ await rm(root, { recursive: true, force: true });
2861
+ }
2862
+ });
2863
+
2864
+ test("compileOfflineSyncExcludeGlobs throws on non-string / empty entries and accepts empty input (#1786)", () => {
2865
+ assert.throws(() => compileOfflineSyncExcludeGlobs([42]), /non-empty strings/);
2866
+ assert.throws(() => compileOfflineSyncExcludeGlobs([""]), /non-empty strings/);
2867
+ assert.deepEqual(compileOfflineSyncExcludeGlobs([]), []);
2868
+ });
2869
+
2870
+ test("buildOfflineSyncSnapshot excludes all default #1786 push-side runtime state artifacts", async () => {
2871
+ const root = await tempDir("remnic-offline-default-exclude-1786");
2872
+ try {
2873
+ await write(root, "facts/a.md", "alpha");
2874
+ await write(root, "state/lcm.sqlite", "live db");
2875
+ await write(root, "state/lcm.sqlite-wal", "live wal");
2876
+ await write(root, "state/index_tags.json", "tags");
2877
+ await write(root, "state/entity-mention-index.json", "entities");
2878
+ await mkdir(path.join(root, "state/memory-governance/runs"), { recursive: true });
2879
+ await writeFile(path.join(root, "state/memory-governance/runs/r1.json"), "run");
2880
+
2881
+ const snapshot = await buildOfflineSyncSnapshot({
2882
+ root,
2883
+ sourceId: "remote",
2884
+ includeContent: true,
2885
+ });
2886
+
2887
+ const paths = snapshot.files.map((file) => file.path);
2888
+ // Only the source-of-truth fact ships; every default-excluded state
2889
+ // artifact is gone from push-side enumeration.
2890
+ assert.deepEqual(paths, ["facts/a.md"]);
2891
+ // And the excluded paths are individually absent — guard against
2892
+ // accidentally re-introducing one of them via a future code path.
2893
+ for (const excluded of [
2894
+ "state/lcm.sqlite",
2895
+ "state/lcm.sqlite-wal",
2896
+ "state/index_tags.json",
2897
+ "state/entity-mention-index.json",
2898
+ "state/memory-governance/runs/r1.json",
2899
+ ]) {
2900
+ assert.equal(paths.includes(excluded), false, `unexpectedly included: ${excluded}`);
2901
+ }
2902
+ } finally {
2903
+ await rm(root, { recursive: true, force: true });
2904
+ }
2905
+ });
2906
+
2907
+ test("buildOfflineSyncSnapshot honors userExcludeRegexps additively over default excludes (#1786)", async () => {
2908
+ const root = await tempDir("remnic-offline-user-exclude-1786");
2909
+ try {
2910
+ await write(root, "facts/a.md", "alpha");
2911
+ await write(root, "notes/secret.md", "do not sync");
2912
+
2913
+ const snapshot = await buildOfflineSyncSnapshot({
2914
+ root,
2915
+ sourceId: "remote",
2916
+ includeContent: true,
2917
+ userExcludeRegexps: compileOfflineSyncExcludeGlobs(["notes/**"]),
2918
+ });
2919
+
2920
+ assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
2921
+ } finally {
2922
+ await rm(root, { recursive: true, force: true });
2923
+ }
2924
+ });
2925
+
2926
+ test("buildOfflineSyncSnapshotFromBase honors userExcludeRegexps on the fast-base path (#1786)", async () => {
2927
+ const root = await tempDir("remnic-offline-user-exclude-fast-base-1786");
2928
+ try {
2929
+ await write(root, "facts/a.md", "alpha");
2930
+ await write(root, "notes/secret.md", "do not sync");
2931
+
2932
+ const baseSnapshot = await buildOfflineSyncSnapshot({
2933
+ root,
2934
+ sourceId: "remote",
2935
+ includeContent: false,
2936
+ });
2937
+
2938
+ const next = await buildOfflineSyncSnapshotFromBase({
2939
+ root,
2940
+ sourceId: "remote",
2941
+ baseFiles: baseSnapshot.files,
2942
+ baseCapturedAt: new Date(),
2943
+ includeContent: false,
2944
+ userExcludeRegexps: compileOfflineSyncExcludeGlobs(["notes/**"]),
2945
+ });
2946
+
2947
+ assert.deepEqual(next.files.map((file) => file.path), ["facts/a.md"]);
2948
+ } finally {
2949
+ await rm(root, { recursive: true, force: true });
2950
+ }
2951
+ });