@openclaw/matrix 2026.9.8 → 2026.10.1-beta.2

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 (278) hide show
  1. package/dist/.setup/{account-selection-B7axQ80R.mjs → account-selection-BDeeqwwR.mjs} +1 -1
  2. package/dist/.setup/{accounts-WkgrIxMt.mjs → accounts-C_r0UlEm.mjs} +2 -2
  3. package/dist/.setup/{approval-handler.runtime-C-pw3NvY.mjs → approval-handler.runtime-QkHLH2az.mjs} +14 -19
  4. package/dist/.setup/{approval-reactions-BrS5ECU0.mjs → approval-reactions-DhB6gNPU.mjs} +11 -25
  5. package/dist/.setup/{channel-DXenXmj0.mjs → channel-CeRXEAEP.mjs} +17 -46
  6. package/dist/.setup/{channel.runtime-BXUawOno.mjs → channel.runtime-DBQA2y1F.mjs} +6 -6
  7. package/dist/.setup/{channel.setup-DWfLaXqT.mjs → channel.setup-DC2t7_PK.mjs} +12 -26
  8. package/dist/.setup/{cli-IhZO2OEj.mjs → cli-Be8tdNw8.mjs} +24 -45
  9. package/dist/.setup/{cli-metadata-nowEgdY0.mjs → cli-metadata-CtcjBFhc.mjs} +1 -1
  10. package/dist/.setup/{client-BXATebyo.mjs → client-DY3HExqI.mjs} +3 -3
  11. package/dist/.setup/{client-bootstrap-BiHX55is.mjs → client-bootstrap-CZvJjr29.mjs} +1 -1
  12. package/dist/.setup/{client-Ccv77kwh.mjs → client-kmih04ng.mjs} +21 -40
  13. package/dist/.setup/{client-support-Bn9Bk0-H.mjs → client-support-D7BlioUb.mjs} +14 -23
  14. package/dist/.setup/{config-update-5m1Un5gJ.mjs → config-update-CZndPxNd.mjs} +29 -72
  15. package/dist/.setup/{create-client-DRZ580E3.mjs → create-client-CbJVyFDy.mjs} +5 -5
  16. package/dist/.setup/{credentials-C9qDO5-2.mjs → credentials-C2dvc_kB.mjs} +1 -1
  17. package/dist/.setup/{credentials-read-KD4o5biZ.mjs → credentials-read-CqRiEAQa.mjs} +1 -1
  18. package/dist/.setup/{crypto-runtime-DPjIDF8H.mjs → crypto-runtime-B2AjWo-i.mjs} +223 -79
  19. package/dist/.setup/{deps-CLQ4EOq7.mjs → deps-CFNl7tQo.mjs} +14 -39
  20. package/dist/.setup/{device-health-U-yoVjPw.mjs → device-health-Be60VF1w.mjs} +1 -1
  21. package/dist/.setup/{direct-management-Cn2t8n1V.mjs → direct-management-DJtQhLKH.mjs} +4 -8
  22. package/dist/.setup/{directory-live-_sw-Q7KO.mjs → directory-live-DW5UykLL.mjs} +3 -3
  23. package/dist/.setup/{doctor-contract-De6geW0h.mjs → doctor-contract-CsTNmbMa.mjs} +10 -133
  24. package/dist/.setup/{draft-stream-CdVMstpR.mjs → draft-stream-B87DfA6a.mjs} +4 -8
  25. package/dist/.setup/{encryption-guidance-B6797jNA.mjs → encryption-guidance-BEjKbiMR.mjs} +1 -1
  26. package/dist/.setup/{exec-approvals-fsiMQ1ce.mjs → exec-approvals-BLDAMyHs.mjs} +6 -19
  27. package/dist/.setup/{file-sync-store-B-9bMb_O.mjs → file-sync-store-Clc9iEdn.mjs} +3 -3
  28. package/dist/.setup/{http-client-C1XvVD3K.mjs → http-client-DfJY5Ck0.mjs} +8 -29
  29. package/dist/.setup/{inbound-dedupe-BGrMrTTu.mjs → inbound-dedupe-C-EBGY0B.mjs} +1 -1
  30. package/dist/.setup/{joined-room-encryption-TZoOKXgh.mjs → joined-room-encryption-BAD70N5n.mjs} +1 -1
  31. package/dist/.setup/logger-CXQnpXCt.mjs +44 -0
  32. package/dist/.setup/{logging-mhnFQGzB.mjs → logging-BKV8TYQq.mjs} +7 -32
  33. package/dist/.setup/{messages-Buz27pi5.mjs → messages-uCbwLRKS.mjs} +5 -8
  34. package/dist/.setup/{monitor-BBg5qgov.mjs → monitor-skIntQVG.mjs} +109 -180
  35. package/dist/.setup/{onboarding-hJaieQj5.mjs → onboarding-Cm_tOJxs.mjs} +12 -19
  36. package/dist/.setup/{outbound-C9vBijaH.mjs → outbound-CqQou3ny.mjs} +16 -22
  37. package/dist/.setup/{profile-B3DD2jYa.mjs → profile-DS0xdJAi.mjs} +11 -31
  38. package/dist/.setup/{profile-update-CmxIAd4c.mjs → profile-update-C1n8SRqH.mjs} +3 -3
  39. package/dist/.setup/{reaction-events-mnzc_JvP.mjs → reaction-events-Cqq46xsG.mjs} +5 -5
  40. package/dist/.setup/{resolve-targets-BFq3q4c9.mjs → resolve-targets-DFeU1Ww6.mjs} +2 -2
  41. package/dist/.setup/retired-state-BXUQ2I-C.mjs +91 -0
  42. package/dist/.setup/{room-info-BbTGRJcR.mjs → room-info-BPMbwe8I.mjs} +33 -50
  43. package/dist/.setup/{sdk-DmRVM0VA.mjs → sdk-CgQGkCsS.mjs} +226 -182
  44. package/dist/.setup/{send-GOF64QRc.mjs → send-B1Y2L4wj.mjs} +19 -68
  45. package/dist/.setup/{setup-bootstrap-AzalcuCW.mjs → setup-bootstrap-UNIGyKnk.mjs} +2 -2
  46. package/dist/.setup/{setup-core-BeifoeL4.mjs → setup-core-6DppLENo.mjs} +5 -5
  47. package/dist/.setup/{setup-plugin-api-Bsbdw41x.mjs → setup-plugin-api-BRs9bcp4.mjs} +1 -1
  48. package/dist/.setup/startup-verification-D00eUt74.mjs +122 -0
  49. package/dist/.setup/storage-D7agewjH.mjs +464 -0
  50. package/dist/.setup/{sync-cache-state-C6sEmvJK.mjs → sync-cache-state-C2z64uyk.mjs} +10 -24
  51. package/dist/.setup/{thread-bindings-jXu_9cHE.mjs → thread-bindings-DveHVZqN.mjs} +13 -71
  52. package/dist/.setup/{tool-actions-Ce7Okg6m.mjs → tool-actions-BqlK2ilM.mjs} +51 -73
  53. package/dist/.setup/{verification-C45rOZkM.mjs → verification-kMlFYUn0.mjs} +10 -27
  54. package/dist/api.js +5 -5
  55. package/dist/channel-plugin-api.js +1 -1
  56. package/dist/cli-metadata.js +1 -1
  57. package/dist/contract-api.js +2 -2
  58. package/dist/doctor-contract-api.js +48 -201
  59. package/dist/helper-api.js +1 -1
  60. package/dist/index.js +1 -1
  61. package/dist/plugin-entry.handlers.runtime.js +1 -1
  62. package/dist/runtime-api.js +3 -3
  63. package/dist/session-binding-contract-api.js +1 -1
  64. package/dist/setup-entry.js +1 -1
  65. package/dist/setup-plugin-api.js +1 -1
  66. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
  67. package/node_modules/@openclaw/fs-safe/README.md +70 -379
  68. package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
  69. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
  70. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  71. package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
  72. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  73. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
  74. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
  75. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
  76. package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
  77. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
  78. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
  79. package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
  80. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
  81. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
  82. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
  83. package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
  84. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
  85. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
  86. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
  87. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
  88. package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
  89. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
  90. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
  91. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
  92. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
  93. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
  94. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
  95. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
  96. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
  97. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
  98. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
  99. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
  100. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
  101. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
  102. package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
  103. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
  104. package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
  105. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
  106. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
  107. package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
  108. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
  109. package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
  110. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
  111. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
  112. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
  113. package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
  114. package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
  115. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
  116. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
  117. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
  118. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
  119. package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
  120. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
  121. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
  122. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
  123. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
  124. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
  125. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
  126. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
  127. package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
  128. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
  129. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
  130. package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
  131. package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
  132. package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
  133. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
  134. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
  135. package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
  136. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
  137. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
  138. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
  139. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
  140. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
  141. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
  142. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
  143. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
  144. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
  145. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
  146. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
  147. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
  148. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
  149. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
  150. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
  151. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
  152. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
  153. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
  154. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
  155. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
  156. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
  157. package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
  158. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
  159. package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
  160. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
  161. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
  162. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
  163. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
  164. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
  165. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
  166. package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
  167. package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
  168. package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
  169. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
  170. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
  171. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
  172. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
  173. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
  174. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
  175. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
  176. package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
  177. package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
  178. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
  179. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
  180. package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
  181. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
  182. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
  183. package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
  184. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
  185. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
  186. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
  187. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
  188. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
  189. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
  190. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
  191. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
  192. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
  193. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
  194. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
  195. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
  196. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
  197. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
  198. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
  199. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
  200. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
  201. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
  202. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
  203. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
  204. package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
  205. package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
  206. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
  207. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
  208. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
  209. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
  210. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
  211. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
  212. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
  213. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
  214. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
  215. package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
  216. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
  217. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
  218. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
  219. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
  220. package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
  221. package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
  222. package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
  223. package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
  224. package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
  225. package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
  226. package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
  227. package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
  228. package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
  229. package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
  230. package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
  231. package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
  232. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
  233. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
  234. package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
  235. package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
  236. package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
  237. package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
  238. package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
  239. package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
  240. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
  241. package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
  242. package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
  243. package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
  244. package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
  245. package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
  246. package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
  247. package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
  248. package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
  249. package/node_modules/@openclaw/fs-safe/package.json +10 -10
  250. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  251. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  252. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  253. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  254. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  255. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  256. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  257. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  258. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  259. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  260. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  261. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  262. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  263. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  264. package/openclaw.plugin.json +0 -9
  265. package/package.json +5 -5
  266. package/dist/.setup/crypto-snapshot-doctor.runtime-44FrCPR7.mjs +0 -91
  267. package/dist/.setup/doctor-DMvEBx6o.mjs +0 -49
  268. package/dist/.setup/idb-persistence-DBvfre1X.mjs +0 -227
  269. package/dist/.setup/idb-persistence-lock-D_mDu4Cf.mjs +0 -27
  270. package/dist/.setup/logger-CVv56jzH.mjs +0 -74
  271. package/dist/.setup/startup-verification-Aiy_xVdI.mjs +0 -204
  272. package/dist/.setup/storage-BXS2cB2i.mjs +0 -288
  273. package/dist/.setup/storage-metadata-BDRZ7vpX.mjs +0 -371
  274. package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
  275. package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
  276. package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
  277. package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
  278. package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
@@ -1,8 +1,8 @@
1
- import { n as normalizeCompatibilityConfig, r as runtime_doctor_migrations_exports, t as legacyConfigRules } from "./.setup/doctor-contract-De6geW0h.mjs";
1
+ import { n as normalizeCompatibilityConfig, r as runtime_doctor_migrations_exports, t as legacyConfigRules } from "./.setup/doctor-contract-CsTNmbMa.mjs";
2
2
  import { i as normalizeMatrixStoredCredentials, n as isMatrixCredentialRevocation, r as matrixCredentialsStoreKey, t as MATRIX_CREDENTIALS_NAMESPACE } from "./.setup/credentials-state-B3cuUFwZ.mjs";
3
3
  import { c as resolveMatrixStateLayoutChildDepth, i as resolveMatrixCredentialsDir } from "./.setup/storage-paths-B51JM-pC.mjs";
4
- import { D as writeMatrixRecoveryKeyStateToStore, O as resolveMatrixSqliteStateEnv, T as writeMatrixLegacyCryptoMigrationStateToStore, a as writeMatrixStorageMetaStateToStore, c as MATRIX_RECOVERY_KEY_FILENAME, g as readLegacyMatrixLegacyCryptoMigrationState, h as openMatrixRecoveryKeyStoreOptions, i as openMatrixStorageMetaStoreOptions, l as hasMatrixLegacyCryptoMigrationStateInStore, m as openMatrixLegacyCryptoMigrationStoreOptions, n as hasMatrixStorageMetaStateInStore, o as MATRIX_IDB_SNAPSHOT_FILENAME, r as normalizeMatrixStorageMetadata, s as MATRIX_LEGACY_CRYPTO_MIGRATION_FILENAME, u as hasMatrixRecoveryKeyStateInStore, v as readLegacyMatrixRecoveryKeyState } from "./.setup/storage-metadata-BDRZ7vpX.mjs";
5
- import { a as readLegacyMatrixSyncCacheState, c as writeMatrixSyncCacheStateToStore, i as openMatrixSyncCacheStoreOptions, r as hasMatrixSyncCacheStateInStore } from "./.setup/sync-cache-state-C6sEmvJK.mjs";
4
+ import { a as describeRetiredMatrixState, o as resolveMatrixSqliteStateEnv, t as RETIRED_MATRIX_STATE_FILENAMES } from "./.setup/retired-state-BXUQ2I-C.mjs";
5
+ import { i as readLegacyMatrixSyncCacheState, n as hasMatrixSyncCacheStateInStore, r as openMatrixSyncCacheStoreOptions, s as writeMatrixSyncCacheStateToStore } from "./.setup/sync-cache-state-C2z64uyk.mjs";
6
6
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
7
7
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
8
8
  import { createHash } from "node:crypto";
@@ -35,8 +35,10 @@ async function walkMatrixStateFiles(stateDir, includeFile, stateRootDepths = [])
35
35
  //#region extensions/matrix/src/matrix/account-state-schema-doctor.ts
36
36
  const STATE_DATABASE_FILENAME = "openclaw.sqlite";
37
37
  async function collectMatrixAccountStateRoots(stateDir) {
38
- const { entries, failedDirs } = await walkMatrixStateFiles(stateDir, (name, depth) => depth === 5 && name === STATE_DATABASE_FILENAME, [2, 4]);
38
+ const { entries, failedDirs } = await walkMatrixStateFiles(stateDir, (name, depth) => depth === 5 && name === STATE_DATABASE_FILENAME || (depth === 0 || depth === 2 || depth === 4) && RETIRED_MATRIX_STATE_FILENAMES.has(name), [2, 4]);
39
39
  if (failedDirs.length > 0) throw failedDirs[0].error;
40
+ const retired = entries.filter((entry) => path.basename(entry.path) !== STATE_DATABASE_FILENAME);
41
+ if (retired.length > 0) throw new Error(retired.map((entry) => describeRetiredMatrixState(entry.path)).join("\n"));
40
42
  return entries.map((entry) => path.dirname(path.dirname(entry.path))).toSorted();
41
43
  }
42
44
  function describeMatrixAccountStateMigration(storageRootDir, migration) {
@@ -81,12 +83,10 @@ const matrixAccountStateSchemaMigration = {
81
83
  //#region extensions/matrix/src/matrix/monitor/inbound-dedupe-migration.ts
82
84
  const LEGACY_SQLITE_NAMESPACE = "inbound-dedupe";
83
85
  const LEGACY_MARKERS_NAMESPACE = "inbound-dedupe-migrations";
84
- const LEGACY_JSON_VERSION = 1;
85
86
  const MATRIX_PLUGIN_ID = "matrix";
86
87
  const MIGRATION_COMPLETION_NAMESPACE = "inbound-dedupe-migration-state";
87
88
  const MIGRATION_COMPLETION_KEY = "sqlite-json-to-claimable-v1";
88
89
  const STATE_DATABASE_RELATIVE_PATH = path.join("state", "openclaw.sqlite");
89
- const STORAGE_META_FILENAME = "storage-meta.json";
90
90
  const MATRIX_LEGACY_INBOUND_DEDUPE_FILENAME = "inbound-dedupe.json";
91
91
  function openMatrixInboundDedupeMigrationCompletionStore(context, env) {
92
92
  return context.openPluginStateKeyedStore({
@@ -120,20 +120,16 @@ async function reserveMatrixInboundDedupeMigrationCompletion(context, env) {
120
120
  async function collectMatrixInboundDedupeSources(stateDir) {
121
121
  const matrixRoot = path.resolve(stateDir, "matrix");
122
122
  const sqliteRoots = /* @__PURE__ */ new Set();
123
- const jsonRoots = /* @__PURE__ */ new Set();
124
- const { entries, failedDirs } = await walkMatrixStateFiles(stateDir, (name, depth) => (depth === 0 || depth === 2 || depth === 4) && name === "inbound-dedupe.json" || depth === 5 && name === "openclaw.sqlite", [
123
+ const { entries, failedDirs } = await walkMatrixStateFiles(stateDir, (name, depth) => (depth === 0 || depth === 2 || depth === 4) && name === MATRIX_LEGACY_INBOUND_DEDUPE_FILENAME || depth === 5 && name === "openclaw.sqlite", [
125
124
  0,
126
125
  2,
127
126
  4
128
127
  ]);
129
- for (const entry of entries) if (entry.name === "inbound-dedupe.json") jsonRoots.add(path.dirname(entry.path));
128
+ for (const entry of entries) if (entry.name === MATRIX_LEGACY_INBOUND_DEDUPE_FILENAME) throw new Error(describeRetiredMatrixState(entry.path));
130
129
  else sqliteRoots.add(path.dirname(path.dirname(entry.path)));
131
130
  const warnings = failedDirs.map(({ path: directory, error }) => `Failed scanning Matrix inbound dedupe sources under ${directory}: ${String(error)}`);
132
131
  const isAccountRoot = (root) => root !== matrixRoot;
133
- const roots = {
134
- sqliteRoots: [...sqliteRoots].filter(isAccountRoot).toSorted(),
135
- jsonRoots: [...jsonRoots].filter(isAccountRoot).toSorted()
136
- };
132
+ const roots = { sqliteRoots: [...sqliteRoots].filter(isAccountRoot).toSorted() };
137
133
  return warnings.length === 0 ? {
138
134
  status: "complete",
139
135
  ...roots
@@ -232,50 +228,8 @@ async function verifyMatrixInboundDedupeSourcesRetired(stateDir) {
232
228
  } catch (err) {
233
229
  warnings.push(`Failed verifying retired Matrix inbound dedupe rows for ${storageRootDir}: ${String(err)}`);
234
230
  }
235
- for (const storageRootDir of remaining.jsonRoots) warnings.push(`Matrix inbound dedupe JSON remains after retirement for ${storageRootDir}`);
236
231
  return warnings;
237
232
  }
238
- async function resolveJsonRootAccountId(storageRootDir) {
239
- for (const filename of [STORAGE_META_FILENAME, `${STORAGE_META_FILENAME}.migrated`]) try {
240
- const metadata = normalizeMatrixStorageMetadata(JSON.parse(await fs.readFile(path.join(storageRootDir, filename), "utf8")));
241
- if (metadata?.accountId) return metadata.accountId;
242
- } catch {}
243
- return "default";
244
- }
245
- /**
246
- * Reads one storage root's legacy inbound-dedupe.json markers. Throws on file
247
- * read errors so a transiently unreadable file is never retired unread, and
248
- * returns null for malformed content so the caller can archive it explicitly.
249
- */
250
- async function readLegacyInboundDedupeJsonSource(storageRootDir) {
251
- const jsonPath = path.join(storageRootDir, MATRIX_LEGACY_INBOUND_DEDUPE_FILENAME);
252
- const raw = await fs.readFile(jsonPath, "utf8");
253
- let parsed;
254
- try {
255
- parsed = JSON.parse(raw);
256
- } catch {
257
- return null;
258
- }
259
- if (!isRecord(parsed) || parsed.version !== LEGACY_JSON_VERSION || !Array.isArray(parsed.entries)) return null;
260
- const accountId = await resolveJsonRootAccountId(storageRootDir);
261
- const markers = [];
262
- for (const entry of parsed.entries) {
263
- if (!isRecord(entry) || typeof entry.key !== "string") continue;
264
- const separator = entry.key.indexOf("|");
265
- if (separator <= 0) continue;
266
- const roomId = entry.key.slice(0, separator).trim();
267
- const eventId = entry.key.slice(separator + 1).trim();
268
- const ts = normalizeLegacyTimestamp(entry.ts);
269
- if (!roomId || !eventId || ts === null) continue;
270
- markers.push({
271
- accountId,
272
- roomId,
273
- eventId,
274
- ts
275
- });
276
- }
277
- return markers;
278
- }
279
233
  /**
280
234
  * Imports the globally newest legacy markers into the claimable-dedupe store.
281
235
  * Never exceeds capacity, never replaces a post-upgrade destination row, and
@@ -285,7 +239,7 @@ async function readLegacyInboundDedupeJsonSource(storageRootDir) {
285
239
  */
286
240
  async function importNewestInboundDedupeMarkers(params) {
287
241
  const { createPersistentDedupeImportEntry } = await import("openclaw/plugin-sdk/persistent-dedupe");
288
- const { buildMatrixInboundDedupeEventKey, MATRIX_INBOUND_DEDUPE_STATE_MAX_ENTRIES, MATRIX_INBOUND_DEDUPE_TTL_MS, resolveMatrixInboundDedupeStateNamespace } = await import("./.setup/inbound-dedupe-BGrMrTTu.mjs");
242
+ const { buildMatrixInboundDedupeEventKey, MATRIX_INBOUND_DEDUPE_STATE_MAX_ENTRIES, MATRIX_INBOUND_DEDUPE_TTL_MS, resolveMatrixInboundDedupeStateNamespace } = await import("./.setup/inbound-dedupe-C-EBGY0B.mjs");
289
243
  const now = params.now ?? Date.now();
290
244
  const stateMaxEntries = params.stateMaxEntries ?? MATRIX_INBOUND_DEDUPE_STATE_MAX_ENTRIES;
291
245
  const newestByKey = /* @__PURE__ */ new Map();
@@ -352,7 +306,6 @@ async function importNewestInboundDedupeMarkers(params) {
352
306
  //#endregion
353
307
  //#region extensions/matrix/doctor-contract-api.ts
354
308
  const MATRIX_SYNC_CACHE_FILENAME = "bot-storage.json";
355
- const MATRIX_STORAGE_META_FILENAME = "storage-meta.json";
356
309
  async function collectLegacyMatrixCredentialSources(params) {
357
310
  const credentialsDir = resolveMatrixCredentialsDir(params.stateDir);
358
311
  let entries;
@@ -367,7 +320,7 @@ async function collectLegacyMatrixCredentialSources(params) {
367
320
  return left.name.localeCompare(right.name);
368
321
  });
369
322
  if (files.length === 0) return [];
370
- const { requiresExplicitMatrixDefaultAccount, resolveMatrixDefaultOrOnlyAccountId } = await import("./.setup/account-selection-B7axQ80R.mjs").then((n) => n.t);
323
+ const { requiresExplicitMatrixDefaultAccount, resolveMatrixDefaultOrOnlyAccountId } = await import("./.setup/account-selection-BDeeqwwR.mjs").then((n) => n.t);
371
324
  return files.map((entry) => {
372
325
  const namedAccount = /^credentials(?:-([a-z0-9._-]+))?\.json$/iu.exec(entry.name)?.[1];
373
326
  return {
@@ -389,94 +342,16 @@ async function readLegacyMatrixCredentials(source) {
389
342
  return null;
390
343
  }
391
344
  }
392
- async function collectLegacyMatrixStateRoots(stateDir, filename, options) {
393
- const { entries } = await walkMatrixStateFiles(stateDir, (name, depth) => name === filename && (depth === 2 || depth === 4 || depth === 0 && options?.includeMatrixRoot === true));
394
- return entries.map((entry) => path.dirname(entry.path)).toSorted();
395
- }
396
- async function readLegacyMatrixStorageMetadata(storageRootDir) {
397
- try {
398
- return normalizeMatrixStorageMetadata(JSON.parse(await fs.readFile(path.join(storageRootDir, MATRIX_STORAGE_META_FILENAME), "utf8")));
399
- } catch {
400
- return null;
345
+ async function* readLegacyMatrixSyncCacheSources(stateDir) {
346
+ const { entries } = await walkMatrixStateFiles(stateDir, (name, depth) => name === MATRIX_SYNC_CACHE_FILENAME && (depth === 2 || depth === 4));
347
+ for (const storageRootDir of entries.map((entry) => path.dirname(entry.path)).toSorted()) {
348
+ const payload = await readLegacyMatrixSyncCacheState(storageRootDir);
349
+ if (payload) yield {
350
+ storageRootDir,
351
+ payload
352
+ };
401
353
  }
402
354
  }
403
- async function archiveLegacyMatrixStateFile(params) {
404
- const warningCount = params.warnings.length;
405
- await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
406
- filePath: path.join(params.storageRootDir, params.filename),
407
- label: params.label,
408
- changes: params.changes,
409
- warnings: params.warnings
410
- });
411
- if (params.notice && params.warnings.length === warningCount) params.notices?.push(params.notice);
412
- }
413
- function defineMatrixLegacyFileMigration(spec) {
414
- const jsonLabel = spec.jsonLabel ?? spec.label;
415
- const readSources = async function* (stateDir) {
416
- for (const storageRootDir of await collectLegacyMatrixStateRoots(stateDir, spec.filename, spec)) {
417
- const payload = await spec.read(storageRootDir);
418
- if (payload) yield {
419
- storageRootDir,
420
- payload
421
- };
422
- }
423
- };
424
- return {
425
- id: spec.id,
426
- label: spec.label,
427
- async detectLegacyState(params) {
428
- const previews = [];
429
- for await (const { storageRootDir } of readSources(params.stateDir)) previews.push(`${jsonLabel} JSON can migrate to SQLite: ${storageRootDir}`);
430
- return previews.length > 0 ? { preview: previews } : null;
431
- },
432
- async migrateLegacyState(params) {
433
- const changes = [];
434
- const warnings = [];
435
- const notices = [];
436
- for await (const source of readSources(params.stateDir)) {
437
- const store = params.context.openPluginStateKeyedStore(spec.storeOptions(source.storageRootDir));
438
- const existing = await spec.hasState({
439
- ...source,
440
- store
441
- });
442
- if (!existing) {
443
- await spec.write({
444
- ...source,
445
- store
446
- });
447
- changes.push(`Migrated ${jsonLabel} JSON to SQLite for ${source.storageRootDir}`);
448
- }
449
- await archiveLegacyMatrixStateFile({
450
- storageRootDir: source.storageRootDir,
451
- filename: spec.filename,
452
- label: spec.label,
453
- changes,
454
- warnings,
455
- notices,
456
- notice: existing ? `Kept existing ${spec.label} in SQLite and archived the legacy source for ${source.storageRootDir}` : void 0
457
- });
458
- }
459
- return {
460
- changes,
461
- warnings,
462
- ...notices.length > 0 ? { notices } : {}
463
- };
464
- }
465
- };
466
- }
467
- const legacyCryptoMigration = defineMatrixLegacyFileMigration({
468
- id: "matrix-legacy-crypto-migration-json-to-plugin-state",
469
- label: "Matrix legacy crypto migration",
470
- filename: MATRIX_LEGACY_CRYPTO_MIGRATION_FILENAME,
471
- includeMatrixRoot: true,
472
- read: readLegacyMatrixLegacyCryptoMigrationState,
473
- storeOptions: openMatrixLegacyCryptoMigrationStoreOptions,
474
- hasState: hasMatrixLegacyCryptoMigrationStateInStore,
475
- write: ({ payload, store }) => writeMatrixLegacyCryptoMigrationStateToStore({
476
- state: payload,
477
- store
478
- })
479
- });
480
355
  const stateMigrations = [
481
356
  matrixAccountStateSchemaMigration,
482
357
  {
@@ -557,9 +432,12 @@ const stateMigrations = [
557
432
  id: "matrix-inbound-dedupe-to-claimable-dedupe",
558
433
  label: "Matrix inbound dedupe markers",
559
434
  async detectLegacyState(params) {
435
+ const sources = await collectMatrixInboundDedupeSources(params.stateDir);
436
+ if (params.config.channels?.matrix === void 0 && sources.status === "complete" && sources.sqliteRoots.length === 0) return null;
560
437
  return await hasCompletedMatrixInboundDedupeMigration(params.context, params.env) ? null : { preview: ["Matrix inbound dedupe legacy sources need a one-time migration scan"] };
561
438
  },
562
439
  async migrateLegacyState(params) {
440
+ const sources = await collectMatrixInboundDedupeSources(params.stateDir);
563
441
  const io = {
564
442
  context: params.context,
565
443
  env: params.env
@@ -579,7 +457,6 @@ const stateMigrations = [
579
457
  warnings
580
458
  };
581
459
  }
582
- const sources = await collectMatrixInboundDedupeSources(params.stateDir);
583
460
  if (sources.status === "incomplete") warnings.push(...sources.warnings);
584
461
  const recordCompletionIfClean = async (verifyRetirement = false) => {
585
462
  if (warnings.length > 0) return;
@@ -589,7 +466,7 @@ const stateMigrations = [
589
466
  }
590
467
  try {
591
468
  await recordMatrixInboundDedupeMigrationCompletion(params.context, params.env);
592
- if (sources.sqliteRoots.length + sources.jsonRoots.length > 0) changes.push(`Recorded Matrix inbound dedupe migration completion (${sources.sqliteRoots.length} SQLite roots, ${sources.jsonRoots.length} JSON roots scanned)`);
469
+ if (sources.sqliteRoots.length > 0) changes.push(`Recorded Matrix inbound dedupe migration completion (${sources.sqliteRoots.length} SQLite roots scanned)`);
593
470
  } catch (err) {
594
471
  warnings.push(`Failed recording Matrix inbound dedupe migration completion: ${String(err)}`);
595
472
  }
@@ -604,16 +481,7 @@ const stateMigrations = [
604
481
  } catch (err) {
605
482
  warnings.push(`Failed reading Matrix inbound dedupe rows for ${storageRootDir}: ${String(err)}; left legacy rows in place`);
606
483
  }
607
- const jsonRootsToRetire = [];
608
- for (const storageRootDir of sources.jsonRoots) try {
609
- const markers = await readLegacyInboundDedupeJsonSource(storageRootDir);
610
- if (markers === null) warnings.push(`Matrix inbound dedupe JSON for ${storageRootDir} is malformed; archived without import`);
611
- else gathered.push(...markers);
612
- jsonRootsToRetire.push(storageRootDir);
613
- } catch (err) {
614
- warnings.push(`Failed reading Matrix inbound dedupe JSON for ${storageRootDir}: ${String(err)}; left legacy file in place`);
615
- }
616
- if (sqliteRootsToRetire.length + jsonRootsToRetire.length === 0) {
484
+ if (sqliteRootsToRetire.length === 0) {
617
485
  await recordCompletionIfClean();
618
486
  return {
619
487
  changes,
@@ -639,13 +507,6 @@ const stateMigrations = [
639
507
  } catch (err) {
640
508
  warnings.push(`Failed retiring Matrix inbound dedupe rows for ${storageRootDir}: ${String(err)}`);
641
509
  }
642
- for (const storageRootDir of jsonRootsToRetire) await archiveLegacyMatrixStateFile({
643
- storageRootDir,
644
- filename: MATRIX_LEGACY_INBOUND_DEDUPE_FILENAME,
645
- label: "Matrix inbound dedupe",
646
- changes,
647
- warnings
648
- });
649
510
  await recordCompletionIfClean(true);
650
511
  return {
651
512
  changes,
@@ -653,53 +514,39 @@ const stateMigrations = [
653
514
  };
654
515
  }
655
516
  },
656
- defineMatrixLegacyFileMigration({
657
- id: "matrix-storage-meta-json-to-plugin-state",
658
- label: "Matrix storage metadata",
659
- filename: MATRIX_STORAGE_META_FILENAME,
660
- read: readLegacyMatrixStorageMetadata,
661
- storeOptions: openMatrixStorageMetaStoreOptions,
662
- hasState: hasMatrixStorageMetaStateInStore,
663
- write: writeMatrixStorageMetaStateToStore
664
- }),
665
- defineMatrixLegacyFileMigration({
517
+ {
666
518
  id: "matrix-sync-cache-json-to-plugin-state",
667
519
  label: "Matrix sync cache",
668
- filename: MATRIX_SYNC_CACHE_FILENAME,
669
- read: readLegacyMatrixSyncCacheState,
670
- storeOptions: openMatrixSyncCacheStoreOptions,
671
- hasState: hasMatrixSyncCacheStateInStore,
672
- write: writeMatrixSyncCacheStateToStore
673
- }),
674
- defineMatrixLegacyFileMigration({
675
- id: "matrix-recovery-key-json-to-plugin-state",
676
- label: "Matrix recovery key",
677
- jsonLabel: "Matrix recovery-key",
678
- filename: MATRIX_RECOVERY_KEY_FILENAME,
679
- read: readLegacyMatrixRecoveryKeyState,
680
- storeOptions: openMatrixRecoveryKeyStoreOptions,
681
- hasState: hasMatrixRecoveryKeyStateInStore,
682
- write: writeMatrixRecoveryKeyStateToStore
683
- }),
684
- {
685
- id: "matrix-legacy-crypto-migration-json-to-plugin-state",
686
- label: "Matrix legacy crypto state",
687
520
  async detectLegacyState(params) {
688
- const previews = (await legacyCryptoMigration.detectLegacyState(params))?.preview ?? [];
689
- for (const storageRootDir of await collectLegacyMatrixStateRoots(params.stateDir, MATRIX_IDB_SNAPSHOT_FILENAME, { includeMatrixRoot: true })) previews.push(`Matrix IndexedDB snapshot JSON can migrate to SQLite: ${storageRootDir}`);
521
+ const previews = [];
522
+ for await (const { storageRootDir } of readLegacyMatrixSyncCacheSources(params.stateDir)) previews.push(`Matrix sync cache JSON can migrate to SQLite: ${storageRootDir}`);
690
523
  return previews.length > 0 ? { preview: previews } : null;
691
524
  },
692
525
  async migrateLegacyState(params) {
693
- const { changes, warnings, notices = [] } = await legacyCryptoMigration.migrateLegacyState(params);
694
- for (const storageRootDir of await collectLegacyMatrixStateRoots(params.stateDir, MATRIX_IDB_SNAPSHOT_FILENAME, { includeMatrixRoot: true })) {
695
- const { migrateLegacyMatrixIdbSnapshot } = await import("./.setup/crypto-snapshot-doctor.runtime-44FrCPR7.mjs");
696
- await migrateLegacyMatrixIdbSnapshot({
697
- storageRootDir,
698
- context: params.context,
526
+ const changes = [];
527
+ const warnings = [];
528
+ const notices = [];
529
+ for await (const source of readLegacyMatrixSyncCacheSources(params.stateDir)) {
530
+ const store = params.context.openPluginStateKeyedStore(openMatrixSyncCacheStoreOptions(source.storageRootDir));
531
+ const existing = await hasMatrixSyncCacheStateInStore({
532
+ ...source,
533
+ store
534
+ });
535
+ if (!existing) {
536
+ await writeMatrixSyncCacheStateToStore({
537
+ ...source,
538
+ store
539
+ });
540
+ changes.push(`Migrated Matrix sync cache JSON to SQLite for ${source.storageRootDir}`);
541
+ }
542
+ const warningCount = warnings.length;
543
+ await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
544
+ filePath: path.join(source.storageRootDir, MATRIX_SYNC_CACHE_FILENAME),
545
+ label: "Matrix sync cache",
699
546
  changes,
700
- notices,
701
547
  warnings
702
548
  });
549
+ if (existing && warnings.length === warningCount) notices.push(`Kept existing Matrix sync cache in SQLite and archived the legacy source for ${source.storageRootDir}`);
703
550
  }
704
551
  return {
705
552
  changes,
@@ -1,4 +1,4 @@
1
- import { a as resolveConfiguredMatrixAccountIds, i as requiresExplicitMatrixDefaultAccount, n as findMatrixAccountEntry, o as resolveMatrixChannelConfig, r as hasImplicitMatrixAccountConfig, s as resolveMatrixDefaultOrOnlyAccountId } from "./.setup/account-selection-B7axQ80R.mjs";
1
+ import { a as resolveConfiguredMatrixAccountIds, i as requiresExplicitMatrixDefaultAccount, n as findMatrixAccountEntry, o as resolveMatrixChannelConfig, r as hasImplicitMatrixAccountConfig, s as resolveMatrixDefaultOrOnlyAccountId } from "./.setup/account-selection-BDeeqwwR.mjs";
2
2
  import { n as listMatrixEnvAccountIds, r as resolveMatrixEnvAccountToken, t as getMatrixScopedEnvVarNames } from "./.setup/env-vars-dGqak9dN.mjs";
3
3
  import { a as resolveMatrixCredentialsFilename, c as resolveMatrixStateLayoutChildDepth, i as resolveMatrixCredentialsDir, l as sanitizeMatrixPathSegment, n as isMatrixActiveTokenRootDirectory, o as resolveMatrixCredentialsPath, r as resolveMatrixAccountStorageRoot, s as resolveMatrixHomeserverKey, t as hashMatrixAccessToken } from "./.setup/storage-paths-B51JM-pC.mjs";
4
4
  export { findMatrixAccountEntry, getMatrixScopedEnvVarNames, hasImplicitMatrixAccountConfig, hashMatrixAccessToken, isMatrixActiveTokenRootDirectory, listMatrixEnvAccountIds, requiresExplicitMatrixDefaultAccount, resolveConfiguredMatrixAccountIds, resolveMatrixAccountStorageRoot, resolveMatrixChannelConfig, resolveMatrixCredentialsDir, resolveMatrixCredentialsFilename, resolveMatrixCredentialsPath, resolveMatrixDefaultOrOnlyAccountId, resolveMatrixEnvAccountToken, resolveMatrixHomeserverKey, resolveMatrixStateLayoutChildDepth, sanitizeMatrixPathSegment };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as registerMatrixCliMetadata } from "./.setup/cli-metadata-nowEgdY0.mjs";
1
+ import { n as registerMatrixCliMetadata } from "./.setup/cli-metadata-CtcjBFhc.mjs";
2
2
  import { a as setMatrixRuntimeLifecycle } from "./.setup/runtime-1kn1P6io.mjs";
3
3
  import "./runtime-setter-api.js";
4
4
  import { registerMatrixSubagentHooks } from "./subagent-hooks-api.js";
@@ -2,7 +2,7 @@ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
2
2
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
4
  //#region extensions/matrix/src/plugin-entry.runtime.ts
5
- const loadMatrixVerificationRuntime = createLazyRuntimeModule(() => import("./.setup/verification-C45rOZkM.mjs").then((n) => n.y));
5
+ const loadMatrixVerificationRuntime = createLazyRuntimeModule(() => import("./.setup/verification-kMlFYUn0.mjs").then((n) => n.y));
6
6
  function sendError(respond, err) {
7
7
  respond(false, { error: formatErrorMessage(err) });
8
8
  }
@@ -1,16 +1,16 @@
1
1
  import { i as setMatrixRuntime } from "./.setup/runtime-1kn1P6io.mjs";
2
- import { a as resolveConfiguredMatrixAccountIds, f as resolveMatrixAccountStringValues, i as requiresExplicitMatrixDefaultAccount, n as findMatrixAccountEntry, o as resolveMatrixChannelConfig, s as resolveMatrixDefaultOrOnlyAccountId } from "./.setup/account-selection-B7axQ80R.mjs";
2
+ import { a as resolveConfiguredMatrixAccountIds, f as resolveMatrixAccountStringValues, i as requiresExplicitMatrixDefaultAccount, n as findMatrixAccountEntry, o as resolveMatrixChannelConfig, s as resolveMatrixDefaultOrOnlyAccountId } from "./.setup/account-selection-BDeeqwwR.mjs";
3
3
  import { n as listMatrixEnvAccountIds, r as resolveMatrixEnvAccountToken, t as getMatrixScopedEnvVarNames } from "./.setup/env-vars-dGqak9dN.mjs";
4
4
  import { a as resolveMatrixCredentialsFilename, i as resolveMatrixCredentialsDir, l as sanitizeMatrixPathSegment, o as resolveMatrixCredentialsPath, r as resolveMatrixAccountStorageRoot, s as resolveMatrixHomeserverKey, t as hashMatrixAccessToken } from "./.setup/storage-paths-B51JM-pC.mjs";
5
5
  import { d as setMatrixThreadBindingMaxAgeBySessionKey, l as setMatrixThreadBindingIdleTimeoutBySessionKey } from "./.setup/thread-bindings-shared-hxKkYC1W.mjs";
6
- import { n as ensureMatrixSdkInstalled, r as isMatrixSdkAvailable } from "./.setup/deps-CLQ4EOq7.mjs";
6
+ import { n as ensureMatrixSdkInstalled, r as isMatrixSdkAvailable } from "./.setup/deps-CFNl7tQo.mjs";
7
7
  import { chunkTextForOutbound as chunkTextForOutbound$1 } from "openclaw/plugin-sdk/text-chunking";
8
8
  import { assertHttpUrlTargetsPrivateNetwork, closeDispatcher, createPinnedDispatcher, resolvePinnedHostnameWithPolicy, ssrfPolicyFromDangerouslyAllowPrivateNetwork } from "openclaw/plugin-sdk/ssrf-runtime";
9
9
  import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
10
10
  import { formatZonedTimestamp } from "openclaw/plugin-sdk/time-runtime";
11
11
  //#region extensions/matrix/runtime-api.ts
12
12
  function chunkTextForOutbound(text, limit) {
13
- if (text.length === 0) return [""];
13
+ if (text.length === 0 || limit <= 0) return [text];
14
14
  if (Number.isFinite(limit) && limit > 0 && !Number.isInteger(limit)) return chunkTextForOutbound$1(text, limit);
15
15
  const chunks = [];
16
16
  let remaining = text;
@@ -1,3 +1,3 @@
1
1
  import { i as setMatrixRuntime } from "./.setup/runtime-1kn1P6io.mjs";
2
- import { t as createMatrixThreadBindingManager } from "./.setup/thread-bindings-jXu_9cHE.mjs";
2
+ import { t as createMatrixThreadBindingManager } from "./.setup/thread-bindings-DveHVZqN.mjs";
3
3
  export { createMatrixThreadBindingManager, setMatrixRuntime };
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  importMetaUrl: import.meta.url,
6
6
  plugin: {
7
- specifier: fileURLToPath(new URL(".setup/setup-plugin-api-Bsbdw41x.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/setup-plugin-api-BRs9bcp4.mjs", import.meta.url).href),
8
8
  exportName: "matrixSetupPlugin"
9
9
  },
10
10
  secrets: {
@@ -1,2 +1,2 @@
1
- import { n as matrixSetupPlugin } from "./.setup/channel.setup-DWfLaXqT.mjs";
1
+ import { n as matrixSetupPlugin } from "./.setup/channel.setup-DC2t7_PK.mjs";
2
2
  export { matrixSetupPlugin };
@@ -2,6 +2,101 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.23.0 - 2026-10-02
6
+
7
+ ### Highlights
8
+
9
+ - **BREAKING: Python bridge removed:** remove the Python bridge and its compatibility options; migrate to `configureFsSafeNative` / `FS_SAFE_NATIVE_MODE`. ([#815](https://github.com/openclaw/fs-safe/pull/815))
10
+ - **Watch overflow:** fold busy hints so unselected sibling churn beside a missing tree no longer overflows the watch. ([#813](https://github.com/openclaw/fs-safe/pull/813))
11
+ - **Windows exclusive creation:** no longer follows unchanged dangling file symlinks, including standalone creators, unrooted locks and cross-volume copy publication. ([#823](https://github.com/openclaw/fs-safe/pull/823), [#824](https://github.com/openclaw/fs-safe/pull/824))
12
+ - **Faster metadata listings:** reduce path normalization and per-entry allocation when listing Root directory metadata. ([#818](https://github.com/openclaw/fs-safe/pull/818), [#819](https://github.com/openclaw/fs-safe/pull/819))
13
+
14
+ ### Fixes
15
+
16
+ - **Windows directory preparation:** retain exact file identities in `ensureAbsoluteDirectory()` so a replaced directory cannot compare equal after large Windows file IDs are rounded to JavaScript numbers. ([#825](https://github.com/openclaw/fs-safe/pull/825))
17
+ - **Windows writable creation:** in native `auto`/`off` mode, `openWritable()`, `append()`, and create-only writes could create a dangling file symlink's missing in-Root target before rejecting the link. Check final leaves before fallback creation; outside-Root targets were already rejected. ([#823](https://github.com/openclaw/fs-safe/pull/823))
18
+ - **Windows symlink creation escape:** unchanged dangling file symlinks could redirect standalone exclusive creation outside the caller-selected directory, including source contents during cross-volume copy publication and records from unrooted fallback locks. Check final leaves before creation, including internal staging files; concurrent replacement remains best-effort. ([#824](https://github.com/openclaw/fs-safe/pull/824))
19
+ - **Atomic writes:** preserve both parent-directory preparation and descriptor-close failures, in operation-first order, for synchronous and asynchronous replacements. ([#820](https://github.com/openclaw/fs-safe/pull/820))
20
+ - **Watch overflow:** fold busy pending hints into guarded subtree reconciliation so unselected sibling churn beside a missing tree no longer exhausts the hint budget. ([#813](https://github.com/openclaw/fs-safe/pull/813))
21
+ - **Native initialization:** lazily load the addon in fs-safe's import context so its housekeeping does not retain the first operation's `AsyncLocalStorage` stores when fs-safe is imported outside caller scopes. ([#797](https://github.com/openclaw/fs-safe/pull/797))
22
+
23
+ ### Performance
24
+
25
+ - **Root metadata path assembly:** avoid renormalizing each literal child beneath its canonical directory, cutting 1,000-entry listing time by a further 19–23% and sampled allocations by 33–34% on Linux x64. ([#819](https://github.com/openclaw/fs-safe/pull/819))
26
+ - **Root metadata listings:** construct named metadata once per entry, reducing 1,000-entry listing time by 16% and sampled allocations by 27–28% on Linux x64, with all filesystem and identity checks retained. ([#818](https://github.com/openclaw/fs-safe/pull/818))
27
+
28
+ ### Compatibility
29
+
30
+ - **Removed compatibility options:** remove `configureFsSafePython`, `FsSafePythonConfig`, `pythonPath`, and the six Python environment variables; use `configureFsSafeNative`, `FsSafeNativeConfig`, and `FS_SAFE_NATIVE_MODE` (or `OPENCLAW_FS_SAFE_NATIVE_MODE`). The Python variables are now ignored, so a deployment that still sets `FS_SAFE_PYTHON_MODE=require` or `off` runs in `auto` until it sets `FS_SAFE_NATIVE_MODE`. Remove secure-temp `chmodSync`, atomic adapter `chmod`/`chmodSync`, Root `nonBlockingRead`, and ACL `principalSids`; use descriptor-bound permission adapters (`descriptor` for secure-temp, `FileHandle.chmod` / `fchmodSync` for atomic writes), automatic nonblocking read admission, and direct SID classification. Callers passing removed options get TypeScript errors. ([#815](https://github.com/openclaw/fs-safe/pull/815))
31
+
32
+ ## 0.22.0 - 2026-10-01
33
+
34
+ ### Highlights
35
+
36
+ - **One-way publication:** `retainEntryForPublication` on `advanced` provides no-replace export for POSIX directories, regular files and direct symlinks, plus Windows NTFS files, directories, symbolic links and junctions. ([#776](https://github.com/openclaw/fs-safe/pull/776), [#778](https://github.com/openclaw/fs-safe/pull/778), [#779](https://github.com/openclaw/fs-safe/pull/779))
37
+ - **Windows path hardening:** reject foreign UNC shares and device namespaces before filesystem probes, blocking attacker-chosen SMB lookups, NTLM exposure and stalls. ([#781](https://github.com/openclaw/fs-safe/pull/781), [#784](https://github.com/openclaw/fs-safe/pull/784))
38
+ - **Watch overflow fixes:** Linux watch-retirement echoes no longer overflow unrelated subscriptions; Linux and macOS classify undecodable names, Linux preserves nameless lifecycle events, and macOS cleans up failed startup correctly. ([#782](https://github.com/openclaw/fs-safe/pull/782), [#783](https://github.com/openclaw/fs-safe/pull/783), [#769](https://github.com/openclaw/fs-safe/pull/769))
39
+
40
+ ### Features
41
+
42
+ - **One-way publication:** `retainEntryForPublication` retains source and parent identities for native no-replace export on supported local POSIX filesystems and Windows NTFS. It supports directories, regular files and direct symbolic links, plus NTFS junctions; link targets stay opaque, and POSIX relative and dangling targets retain their exact bytes. Commit, indeterminate and close failures remain distinct. Requires caller-exclusive source namespaces, with no source-CAS or automatic rollback guarantee; disposal only closes retained resources and never deletes either location. ([#776](https://github.com/openclaw/fs-safe/pull/776), [#778](https://github.com/openclaw/fs-safe/pull/778), [#779](https://github.com/openclaw/fs-safe/pull/779))
43
+
44
+ ### Fixes
45
+
46
+ - **Watch name handling:** ignore undecodable unselected siblings, fail closed for selected tree names on Linux and macOS, and preserve Linux directory lifecycle detail without spurious overflow. ([#783](https://github.com/openclaw/fs-safe/pull/783))
47
+ - **Windows UNC admission:** Root, root-file readers, `pathScope`, `isPathInside`, secret-file writers, sibling-temp output, trash admission, and archive output preparation reject foreign UNC shares and device namespaces before probing them, comparing hosts without Unicode case folding, preventing attacker-chosen SMB lookups, NTLM exposure, and stalls. Existing containment errors are preserved; absolute paths on another share no longer resolve through aliases back into the boundary. ([#781](https://github.com/openclaw/fs-safe/pull/781), [#784](https://github.com/openclaw/fs-safe/pull/784))
48
+ - **Linux watch isolation:** closing or replacing a watch, including after a scan failure, no longer overflows unrelated subscriptions. ([#782](https://github.com/openclaw/fs-safe/pull/782))
49
+ - **macOS watch startup:** clean up rejected FSEvents configuration and failed stream startup with the correct lifecycle calls, keeping the registration available for retry. ([#769](https://github.com/openclaw/fs-safe/pull/769))
50
+
51
+ ### Performance
52
+
53
+ - **Bounded temporary-directory cleanup:** avoid a redundant descriptor metadata read for each descendant directory on Linux and macOS, while retaining fresh name identity checks before removal. ([#760](https://github.com/openclaw/fs-safe/pull/760))
54
+ - **Atomic temp cleanup:** use the file identity captured after creation, avoiding an unnecessary metadata lookup and preserving preexisting temporary paths. ([#772](https://github.com/openclaw/fs-safe/pull/772))
55
+
56
+ ### Compatibility
57
+
58
+ - Windows now rejects foreign UNC shares and device namespaces before probing them. Absolute paths on another share no longer resolve through aliases back into the boundary; callers that relied on that behavior must pass in-boundary paths. ([#781](https://github.com/openclaw/fs-safe/pull/781), [#784](https://github.com/openclaw/fs-safe/pull/784))
59
+
60
+ ## 0.21.3 - 2026-09-30
61
+
62
+ ### Highlights
63
+
64
+ - **Watch transient files:** excluded files and atomic-save temp files no longer cause spurious overflow when they disappear between guarded passes. ([#773](https://github.com/openclaw/fs-safe/pull/773))
65
+ - **Faster watch rescans:** unrelated native hints skip rescans, and relevant hints reconcile affected directories, reducing scan work and allocation. ([#774](https://github.com/openclaw/fs-safe/pull/774))
66
+
67
+ ### Fixes
68
+
69
+ - **Watch:** no longer reports overflow when an excluded or atomic-save temp file appears and disappears between guarded passes under the same observed parent directory. ([#773](https://github.com/openclaw/fs-safe/pull/773))
70
+ - **Windows removal:** nonrecursive `Root.remove()` reports `not-empty` for a nonempty directory in required native mode, including with `force: true`, instead of `not-removable`. ([#771](https://github.com/openclaw/fs-safe/pull/771))
71
+ - **Copy and staging failures:** preserve publication callback errors with unreadable metadata, retain uncertain native rename outcomes, and prevent repeated or reentrant cleanup from reusing consumed descriptors. ([#763](https://github.com/openclaw/fs-safe/pull/763))
72
+
73
+ ### Performance
74
+
75
+ - **Watch:** skip guarded unrelated native hints and reconcile affected directories, reducing a 502-file memory-watch fixture from 513 to 55 visits and 8.16 MB to 1.33 MB sampled allocation per relevant event pass; unrelated detailed hints at two edits/second no longer trigger 120 full scans/minute, while periodic and uncertain-event reconciliation remain full. ([#774](https://github.com/openclaw/fs-safe/pull/774))
76
+ - **Linux fallback resolution:** reuse the file type from each component's checked descriptor observation, avoiding a duplicate metadata read while retaining fresh identity, symlink-target, and mount-policy checks. ([#757](https://github.com/openclaw/fs-safe/pull/757))
77
+ - **Native parent validation:** reuse the retained descriptor's captured identity during ordinary POSIX policy checks, avoiding a duplicate metadata read while keeping the final pathname inspection fresh. ([#735](https://github.com/openclaw/fs-safe/pull/735))
78
+
79
+ ## 0.21.2 - 2026-09-28
80
+
81
+ ### Highlights
82
+
83
+ - **`require` mode confines mutations under hostile concurrency:** with `FS_SAFE_NATIVE_MODE=require` (or `configureFsSafeNative({ mode: "require" })`), remove, recursive remove, `mkdir`, writable-open creation and overwrite `move` now run through retained, identity-checked native parents: descriptor-relative `unlinkat`, `mkdirat` and `renameat2` on POSIX, and handle-relative operations on Windows. An adversarial race fuzzer observed no outside or denied-path effects across about 2.8 million racing calls on Linux (with and without `openat2`), macOS and Windows. `auto` keeps its documented best-effort paths and performance. See the [operation/platform matrix](docs/security-model.md#native-root-mutation-capabilities). ([#744](https://github.com/openclaw/fs-safe/pull/744), [#751](https://github.com/openclaw/fs-safe/pull/751), [#750](https://github.com/openclaw/fs-safe/pull/750), [#752](https://github.com/openclaw/fs-safe/pull/752))
84
+ - **Watch on macOS:** entry scopes use identity-checked kqueue descriptors instead of recursive FSEvents streams, ending overflow storms for shallow ancestor scopes above busy trees. ([#753](https://github.com/openclaw/fs-safe/pull/753))
85
+ - **Faster watch scans:** watch readiness and reconciliation are 27–34% faster on large trees. ([#747](https://github.com/openclaw/fs-safe/pull/747))
86
+
87
+ ### Fixes
88
+
89
+ - **Watch cancellation:** a subscription's `signal` closes it even when another abort listener stops event propagation. ([#738](https://github.com/openclaw/fs-safe/pull/738))
90
+ - **Walk cancellation:** walks stop without yielding the current entry when a synchronous filter aborts the signal. ([#739](https://github.com/openclaw/fs-safe/pull/739))
91
+ - **Writes with `mkdir: false`:** the JavaScript fallback writes through existing in-root parent aliases using the same guarded walk, instead of rejecting them. ([#740](https://github.com/openclaw/fs-safe/pull/740))
92
+ - **Descriptor exhaustion:** native writes that fail with `EMFILE`/`ENFILE` report `helper-failed` with the exhaustion code, the indeterminate publication outcome and the preserved stage, instead of a misleading path error. ([#742](https://github.com/openclaw/fs-safe/pull/742))
93
+ - **Walk memory:** `walkDirectory()` and `walkDirectorySync()` stop reading a directory once `maxEntries` is reached instead of enumerating it completely first. `Root.walk()` sorted mode stays deterministic. ([#743](https://github.com/openclaw/fs-safe/pull/743))
94
+
95
+ ### Compatibility
96
+
97
+ - In `require` mode, the operations above now reject with `helper-unavailable` where no confining native primitive exists, instead of silently falling back to JavaScript. Examples are recursive removal on Linux without `openat2` and recursive directory removal on Windows. Use `auto` for the best-effort fallback. These `require`-mode mutations also do more identity-checked work and are slower than before, while `auto` is unchanged. ([#744](https://github.com/openclaw/fs-safe/pull/744), [#751](https://github.com/openclaw/fs-safe/pull/751), [#750](https://github.com/openclaw/fs-safe/pull/750))
98
+ - Create-only writes to an existing directory report `already-exists` in every implementation (previously `not-file` on some Windows paths). With `mutationSymlinks` omitted, Windows native and legacy JavaScript writes still differ for final links and parent junctions; set `mutationSymlinks: "reject"` for uniform behavior. ([#745](https://github.com/openclaw/fs-safe/pull/745))
99
+
5
100
  ## 0.21.1 - 2026-09-27
6
101
 
7
102
  ### Fixes