@openclaw/feishu 2026.9.8 → 2026.10.1-beta.1

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 (225) hide show
  1. package/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
  2. package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
  3. package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
  4. package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
  5. package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
  6. package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
  7. package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
  8. package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
  9. package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
  10. package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
  11. package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
  12. package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
  13. package/dist/api.js +234 -297
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/doctor-contract-api.js +2 -2
  16. package/dist/session-binding-contract-api.js +1 -1
  17. package/dist/setup-api.js +1 -1
  18. package/dist/setup-entry.js +1 -1
  19. package/dist/subagent-hooks-api.js +1 -1
  20. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
  21. package/node_modules/@openclaw/fs-safe/README.md +70 -379
  22. package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
  23. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
  24. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  25. package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
  26. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  27. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
  28. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
  29. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
  30. package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
  31. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
  32. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
  33. package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
  34. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
  35. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
  36. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
  37. package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
  38. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
  39. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
  40. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
  41. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
  42. package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
  43. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
  44. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
  45. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
  46. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
  47. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
  48. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
  49. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
  50. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
  51. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
  52. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
  53. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
  54. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
  55. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
  56. package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
  57. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
  58. package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
  59. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
  60. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
  61. package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
  62. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
  63. package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
  64. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
  65. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
  66. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
  67. package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
  68. package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
  69. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
  70. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
  71. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
  72. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
  73. package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
  74. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
  75. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
  76. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
  77. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
  78. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
  79. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
  80. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
  81. package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
  82. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
  83. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
  84. package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
  85. package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
  86. package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
  87. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
  88. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
  89. package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
  90. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
  91. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
  92. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
  93. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
  94. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
  95. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
  96. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
  97. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
  98. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
  99. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
  100. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
  101. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
  102. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
  103. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
  104. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
  105. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
  106. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
  107. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
  108. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
  109. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
  110. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
  111. package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
  112. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
  113. package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
  114. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
  115. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
  116. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
  117. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
  118. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
  119. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
  120. package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
  121. package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
  122. package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
  123. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
  124. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
  125. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
  126. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
  127. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
  128. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
  129. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
  130. package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
  131. package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
  132. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
  133. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
  134. package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
  135. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
  136. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
  137. package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
  139. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
  140. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
  141. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
  142. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
  143. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
  144. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
  145. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
  146. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
  147. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
  148. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
  149. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
  150. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
  151. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
  152. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
  153. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
  154. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
  155. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
  156. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
  157. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
  158. package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
  159. package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
  160. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
  161. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
  162. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
  163. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
  164. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
  165. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
  166. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
  167. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
  168. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
  169. package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
  170. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
  171. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
  172. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
  173. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
  174. package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
  175. package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
  176. package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
  177. package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
  178. package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
  179. package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
  180. package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
  181. package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
  182. package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
  183. package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
  184. package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
  185. package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
  186. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
  187. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
  188. package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
  189. package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
  190. package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
  191. package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
  192. package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
  193. package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
  194. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
  195. package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
  196. package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
  197. package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
  198. package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
  199. package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
  200. package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
  201. package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
  202. package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
  203. package/node_modules/@openclaw/fs-safe/package.json +10 -10
  204. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  205. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  206. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  207. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  208. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  209. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  210. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  211. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  212. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  213. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  214. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  215. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  216. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  217. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  218. package/package.json +5 -5
  219. package/skills/feishu-wiki/SKILL.md +1 -1
  220. package/dist/.setup/accounts-wRqItHug.mjs +0 -206
  221. package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
  222. package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
  223. package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
  224. package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
  225. package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
@@ -57,66 +57,36 @@ This is a **library-level guardrail**, not OS-level isolation. It does not repla
57
57
  pnpm add @openclaw/fs-safe
58
58
  ```
59
59
 
60
- Node 22 or newer. Core root/path/json/temp helpers avoid framework dependencies. With all optional dependencies omitted, public subpaths remain safe to import and fallback-capable operations work in `auto` or `off`. Native-only features, including no-clobber `Root.move()` and [`private: true` creation on macOS](docs/creation.md#permission-options), remain unavailable and fail with `helper-unavailable`. TAR, gzip, zstd, and bzip2 extraction and bounded entry reads use the same Rust TAR parser through bundled WASM when native support is disabled or absent. Zstd/bzip2 codecs are bundled too; gzip uses Node's built-in decoder. ZIP fallback still needs optional `jszip`. See the [0.6 migration guide](docs/migrating-to-0.6.md).
60
+ Requires Node.js 22 or newer. Bun 1.4.2 is also supported with the
61
+ [Bun runtime requirements](docs/install.md#bun-runtime), including the matching
62
+ Rust addon on macOS and Linux. See
63
+ [Installation](docs/install.md) for supported platforms and optional dependencies.
61
64
 
62
- Bun 1.4.2 is also supported with the [Bun runtime requirements](docs/install.md#bun-runtime), including the matching Rust addon on macOS and Linux. JIT-disabled Bun works too.
63
-
64
- The package installs one prebuilt native binding for the current supported target. It
65
- supplies fd-relative and atomic no-replace primitives that Node does not expose
66
- directly. Configure the lazy loader before first use when you need a strict
67
- environment policy:
65
+ Configure native policy before first use:
68
66
 
69
67
  ```ts
70
68
  import { configureFsSafeNative } from "@openclaw/fs-safe";
71
69
 
72
70
  configureFsSafeNative({ mode: "auto" }); // default: native when available
73
71
  configureFsSafeNative({ mode: "off" }); // disable the addon; use supported fallbacks
74
- configureFsSafeNative({ mode: "require" }); // fail closed if the binding is unavailable
72
+ configureFsSafeNative({ mode: "require" }); // fail closed if the operation's native capability is unavailable
75
73
  ```
76
74
 
77
- Native mode performs `write()`, `create()`, and `copyIn()` parent creation and
78
- publication relative to pinned directory descriptors, including atomic
79
- replacement. The JavaScript path selected by `off`, or by `auto` when no
80
- binding can load, is explicitly best-effort: a same-privilege peer that can
81
- replace a writable parent between its identity check and Node's pathname
82
- mutation can redirect that mutation outside the root before the post-check
83
- reports the escape. Use `require` when hostile concurrent mutation is in scope.
84
-
85
- Equivalent env var: `FS_SAFE_NATIVE_MODE=auto|off|require`. The seven bindings
86
- ship as exact-version optional packages filtered by OS, CPU, and Linux libc, so
87
- a normal install receives only its matching binary. Linux GNU x64/arm64 bindings
88
- support [glibc 2.28 or newer](docs/install.md#supported-native-platforms), including
89
- RHEL 8-family systems. There are no postinstall
90
- steps, runtime downloads, or consumer Rust builds. On a platform without a
91
- published binding, or when optional dependencies are omitted, `auto` silently retains lexical and canonical root
92
- checks, no-follow opens, guarded temp+rename writes, and post-write identity
93
- verification. See the [native
94
- helper policy](docs/native-helper.md) for the exact boundary and deployment
95
- tradeoff, and [native architecture](docs/native.md) for the platform mechanisms
96
- and policy ownership model.
97
-
98
- Open results report the mechanism's containment class as `"kernel-atomic"` or
99
- `"best-effort"`. Linux native `openBeneath()` is kernel-atomic when `openat2`
100
- is available. Older kernels and syscall-filtered containers use a guarded
101
- descriptor-relative walk reporting best-effort; nested no-clobber moves keep
102
- atomic `renameat2(RENAME_NOREPLACE)`. macOS, Windows, and guarded JavaScript
103
- results are best-effort. See [Linux compatibility](docs/native.md#linux-without-openat2). See the [security model](docs/security-model.md#containment-guarantees-by-platform) before using that fact in higher-level policy.
75
+ `FS_SAFE_NATIVE_MODE=auto|off|require` selects the same policy. Native-only
76
+ operations fail with `helper-unavailable` when their capability is unavailable.
77
+ Guarded JavaScript mutations are best-effort: a hostile peer can redirect a
78
+ pathname mutation before its post-check detects the escape. `require` selects
79
+ hardened native paths where documented, but does not make every operation
80
+ kernel-atomic. Read the [native helper policy](docs/native-helper.md) and
81
+ [operation/platform matrix](docs/security-model.md#native-root-mutation-capabilities)
82
+ when concurrent mutation is in scope.
104
83
 
105
84
  ## Migrating from the Python helper
106
85
 
107
- Version 0.5 replaces the persistent Python worker with prebuilt native
108
- bindings. The modes map directly: `configureFsSafePython({ mode: "auto" })`
109
- becomes `configureFsSafeNative({ mode: "auto" })`, and likewise for `off` and
110
- `require`. Replace `FS_SAFE_PYTHON_MODE` with `FS_SAFE_NATIVE_MODE`; remove
111
- `pythonPath`, `FS_SAFE_PYTHON`, and interpreter provisioning because the native
112
- loader does not spawn Python.
113
-
114
- Version 0.5 retains the old function and documented `FS_SAFE_PYTHON*`
115
- and OpenClaw Python environment names emit one `FS_SAFE_PYTHON_DEPRECATED`
116
- warning and map the old mode to its native equivalent. They are migration
117
- bridges for shipped 0.4 consumers, not an alternate helper contract. Update
118
- startup configuration as part of the 0.5 upgrade rather than relying on the
119
- warning path. Follow the [0.5 migration checklist](docs/migrating-to-0.5.md).
86
+ Version 0.5 replaced the Python worker with prebuilt native bindings. Follow the
87
+ [0.5 migration checklist](docs/migrating-to-0.5.md) to replace the removed Python
88
+ configuration with native mode selection; current archive changes are covered
89
+ in the [0.6 migration guide](docs/migrating-to-0.6.md).
120
90
 
121
91
  ## Quick start
122
92
 
@@ -138,68 +108,18 @@ await fs.move("notes/today.txt", "notes/archive/today.txt", { overwrite: true })
138
108
  await fs.remove("notes/archive/today.txt");
139
109
  ```
140
110
 
141
- Use `remove(path, { recursive: true, maxEntries: 20_000, maxDepth: 32 })` for
142
- bounded tree cleanup. It streams directory entries, checks mutation authority
143
- before every removal, and supports cancellation. See [removal options and partial
144
- failure semantics](docs/writing.md).
145
-
146
- `root()` takes the trusted directory; relative paths in subsequent calls are resolved against it. Defaults you pass to `root()` apply to every call below; per-call options override them.
147
-
148
- `copyIn()` also accepts `{ root: sourceRoot, relativePath }`, `overwrite: false`,
149
- and `clone: "auto"` for guarded, exclusive file copies with optional native
150
- copy-on-write acceleration. Byte limits, cancellation, and publication receipts
151
- are described in the [Root copy contract](docs/root.md#writes).
152
-
153
- When you need metadata or a `FileHandle`:
154
-
155
- ```ts
156
- const { buffer, realPath, stat } = await fs.read("notes/today.txt");
157
- const opened = await fs.open("notes/today.txt");
158
- ```
159
-
160
- `create()` is the don't-clobber variant of `write()` and throws `already-exists` when the target already exists:
111
+ `root()` requires an existing trusted directory. Its defaults apply to each
112
+ operation; per-call options handle exceptions. See the [Root reference](docs/root.md).
161
113
 
162
- ```ts
163
- await fs.create("notes/README.md", "seed\n"); // throws if it already exists
164
- ```
114
+ `write()` replaces contents by default. Use `create()` or `overwrite: false`
115
+ when an existing destination should be an error. `move()` defaults to no clobber
116
+ and requires native support for the atomic collision decision; it fails with
117
+ `helper-unavailable` when unavailable. Pass `overwrite: true` when replacement
118
+ is intended.
165
119
 
166
- Use `private: true` on `mkdir()`, `ensureRoot()`, `create()`, or `createJson()`
167
- for private creation. On macOS, this requires native ACL inspection before
168
- creating parents or stages and verifies owner-only permissions with no ACL
169
- before writing payload bytes. Native `off` or a missing ACL capability rejects
170
- with `helper-unavailable`; nonprivate creation is unchanged. See the
171
- [creation contract](docs/creation.md#permission-options) for parent ACL handling
172
- and platform support.
173
-
174
- Pass `{ atomic: true }` to buffered `create()` or `createJson()` to keep the
175
- destination absent until complete content is ready, including with native support
176
- disabled. The JavaScript fallback requires hardlinks and never downgrades to a
177
- partial visible file. Omitted or `false` retains the existing buffered behavior.
178
- Atomic visibility is separate from the existing `durable` synchronization policy.
179
- Use `durable: "file"` on `create()` or `createJson()` when file-flush errors,
180
- including `EPERM`, must propagate. It combines with `atomic: true` without
181
- requiring strict parent-directory synchronization.
182
-
183
- `create()` also accepts an `AsyncIterable<Uint8Array>` for large or incrementally
184
- produced files. Streamed creates keep the destination absent until all chunks
185
- are written, support `maxBytes` and `signal`, and recheck mutation authority
186
- before writes and publication. See [streamed creation](docs/writing.md#streamed-creation)
187
- for producer ownership and cancellation semantics.
188
-
189
- `write()` replaces file contents by default; pass `{ overwrite: false }` or use `create()` when an existing file should be an error. `move()` defaults to no clobber because it can otherwise delete an unrelated target while also consuming the source. No-clobber moves require the native helper so the collision decision and rename are one descriptor-relative operation; they fail with `helper-unavailable` rather than falling back to a replacing rename. Pass `{ overwrite: true }` when replacing the target is intended.
190
-
191
- Mutating methods accept `assertBeforeMutation: () => void` for live lease or
192
- cancellation checks immediately before filesystem dispatch. Root defaults and
193
- per-call checks compose; cleanup and already-dispatched work still settle.
194
- See [live mutation authority](docs/root.md#live-mutation-authority) for the exact
195
- scope, including raw writable handles and lock bookkeeping.
196
- For workspaces with directory aliases, use `symlinks: "follow-parents-within-root"`
197
- on reads and `mutationSymlinks: "follow-parents-within-root"` on mutations or root
198
- defaults. Contained parent symlinks are resolved by the library, while a final
199
- symlink is rejected. Read policy and mutation policy are separate; omitting
200
- `mutationSymlinks` preserves the existing mutation behavior. See [root policies](docs/root.md#defaults-vs-per-call-options).
201
-
202
- Use `ensureRoot()` when a computed relative directory target resolves to the root itself (`""` or `"."`) and you want the operation to be accepted. `root()` still requires the trusted root directory to already exist.
120
+ See [Writing](docs/writing.md) for copy sources, mutation authority, symlink
121
+ policy, writable handles, and bounded removal, and [Creation](docs/creation.md)
122
+ for private permissions, atomic creation, and durability options.
203
123
 
204
124
  ## Reading
205
125
 
@@ -227,35 +147,10 @@ Root reads default to `DEFAULT_ROOT_MAX_BYTES` (16 MiB). Pass a larger `maxBytes
227
147
  for expected large reads, or `Number.POSITIVE_INFINITY` when the caller has a
228
148
  separate size budget.
229
149
 
230
- `reader()` returns a callback that reads absolute or relative paths through the same root boundary. It is useful for APIs that accept a `(path) => Promise<Buffer>` loader. For roots configured through a directory symlink or Windows junction, absolute inputs may use either the configured spelling or the canonical real path. Absolute paths outside the root are rejected with `outside-workspace`. `readAbsolute()` has the same absolute-path behavior directly.
231
-
232
- When you need a writable `FileHandle`, use `openWritable()` and prefer `await using` for cleanup:
233
-
234
- ```ts
235
- await using opened = await fs.openWritable("logs/current.log", { writeMode: "append" });
236
- {
237
- await opened.handle.appendFile("line\n");
238
- }
239
- ```
240
-
241
- `nonBlockingRead` remains as a compatibility hint in `RootDefaults`. Safe read/open operations already use nonblocking descriptor opens where the platform supports them so a raced FIFO cannot pin a worker; filesystem safety policy remains explicit through `hardlinks`, `symlinks`, and `denyMutations`.
242
-
243
- On POSIX, `openWritable()` also uses nonblocking admission for existing targets
244
- so a no-reader FIFO cannot stall validation. Ordinary regular-file write
245
- semantics are unchanged; see [writing](docs/writing.md#openwritable-for-streaming).
246
-
247
- ```ts
248
- const locked = await root("/srv/workspace", {
249
- denyMutations: {
250
- paths: ["/srv/workspace/.env"],
251
- prefixes: ["/srv/workspace/.ssh"],
252
- },
253
- });
254
-
255
- await locked.write(".env", "token"); // FsSafeError code "denied-path"
256
- ```
257
-
258
- `stat()`, `exists()`, `list()`, and `entries()` check the exact selected objects while collecting their advisory results, but they cannot pin a later operation to the same filesystem object. Use `read()`, `open()`, `write()`, `create()`, `copyIn()`, `move()`, or `remove()` for operation-local identity checks, and inspect `containment` when the platform distinction matters.
150
+ See [Reading](docs/reading.md) for absolute-path loaders, aliases, and read
151
+ budgets, and [Writing](docs/writing.md#openwritable-for-streaming) for writable
152
+ handles. Inspection results from `stat()`, `exists()`, `list()`, and `entries()`
153
+ are advisory; use the operation methods for identity checks at the time of I/O.
259
154
 
260
155
  ## Subpaths
261
156
 
@@ -264,30 +159,7 @@ and error exports. Prefer focused subpaths when a consumer needs a narrower
264
159
  contract. Low-level helpers that OpenClaw needs to compose higher-level APIs are grouped under
265
160
  `@openclaw/fs-safe/advanced` instead of being separate public leaf contracts.
266
161
 
267
- | Subpath | Contents |
268
- |---|---|
269
- | `@openclaw/fs-safe/root` | `root()`, `Root`, `RootDefaults`, and root-bounded walking with pruning/error markers |
270
- | `@openclaw/fs-safe/config` | process-global native helper and lock defaults |
271
- | `@openclaw/fs-safe/path` | canonical path checks: `isPathInside`, `safeRealpathSync`, `isNotFoundPathError`, `isSymlinkOpenError` |
272
- | `@openclaw/fs-safe/json` | `tryReadJson`, `readJson`, `readJsonIfExists`, `writeJson`, sync variants |
273
- | `@openclaw/fs-safe/output` | `writeExternalFileWithinRoot` for external libraries that need a temp output path |
274
- | `@openclaw/fs-safe/store` | `fileStore`, `fileStoreSync`, and `jsonStore` |
275
- | `@openclaw/fs-safe/secret` | sync/async strict and try-style secret reads, atomic replace, and create-only secret writes |
276
- | `@openclaw/fs-safe/atomic` | `replaceFileAtomic`, `replaceFileAtomicSync`, `replaceDirectoryAtomic`, `movePathWithCopyFallback` |
277
- | `@openclaw/fs-safe/durability` | pinned directory identities, strict directory sync, durable nested-directory creation, exclusive publication, streaming and synchronous SHA-256, provenance receipts, and sync-failure policy |
278
- | `@openclaw/fs-safe/temp` | `tempWorkspace`, `tempWorkspaceSync`, `withTempWorkspace`, `resolveSecureTempRoot` |
279
- | `@openclaw/fs-safe/secure-file` | fd-pinned absolute file reads with owner, mode, ACL, trusted-dir, size, and timeout checks |
280
- | `@openclaw/fs-safe/file-lock` | async/sync sidecar locks, root-bounded sidecars, ownership verification, and stale policy |
281
- | `@openclaw/fs-safe/permissions` | POSIX mode and Windows ACL inspection, raw owner/ACE facts, private-directory creation, and remediation helpers |
282
- | [`@openclaw/fs-safe/watch`](docs/watch.md) | Guarded observation with native event hints, bounded scans, and joined close |
283
- | `@openclaw/fs-safe/walk` | budget-bounded directory walking with symlink policy, filters, and truncation accounting; not root-bounded |
284
- | `@openclaw/fs-safe/copy` | directory copying with `clone: "auto"`, `"always"`, or `"never"`; native APFS, Btrfs, ReFS, XFS, and ZFS cloning, portable byte copying, and clone metadata; see [directory copying](docs/copy.md) |
285
- | `@openclaw/fs-safe/archive` | policy-driven ZIP/TAR extraction, clamp/filter policy, metadata/path-depth limits, gzip/zstd/bzip2 support, and bounded entry reads |
286
- | `@openclaw/fs-safe/advanced` | lower-level composition helpers such as path scopes, root-file open, bounded descriptor reads, [borrowed-handle and descriptor copying](docs/copy.md#borrowed-filehandle-transfers), [complete byte-window writes](docs/advanced.md#borrowed-handle-writes), [exact directory identity](docs/directory-identity.md), [case probing](docs/path-case.md), [suffix-alias probing](docs/path-suffix-aliases.md), [in-place writes](docs/in-place-write.md), [versioned install-ID encoding](docs/install-path.md#safepathsegmenthashedv2), filename sanitizing, temp-file targets, sibling-temp writes, local-root readers, regular-file helpers, `pathExists`, and `withTimeout`; less stable than focused public subpaths |
287
- | `@openclaw/fs-safe/errors` | `FsSafeError`, closed codes/categories, causes, and operation-specific details receipts |
288
- | `@openclaw/fs-safe/types` | shared types: `DirEntry`, `PathStat`, … |
289
- | `@openclaw/fs-safe/test-hooks` | hooks the test suite uses to inject races; registration requires `NODE_ENV=test` or `VITEST=true` |
290
- | `@openclaw/fs-safe/guest` | Python source and exit constants for caller-launched filesystem operations in Linux/macOS guests without Node; see the [guest protocol and trust boundary](docs/guest.md) |
162
+ See the [complete subpath catalogue](docs/install.md#subpath-exports) for every entry point and its contents.
291
163
 
292
164
  ## Failure semantics in the name
293
165
 
@@ -307,53 +179,14 @@ JSON5-backed plugin manifests.
307
179
 
308
180
  ## Directory durability
309
181
 
310
- ```ts
311
- import { ensureDurableDirectory, pinDirectory } from "@openclaw/fs-safe/durability";
312
-
313
- const receipt = await ensureDurableDirectory({
314
- directoryPath: "/srv/backups/sqlite",
315
- mode: 0o700,
316
- });
317
- const pinned = await pinDirectory(receipt);
318
- try {
319
- await publishSnapshot();
320
- const outcome = await pinned.sync();
321
- // `unsupported` is explicit on platforms without directory flushing.
322
- console.log(outcome.status);
323
- } finally {
324
- await pinned.close();
325
- }
326
- ```
327
-
328
- The durability subpath pins a directory descriptor to its pathname identity,
329
- detects symlink/FIFO/replacement races, and synchronizes every new parent edge
330
- when creating a nested directory. Strict sync propagates real I/O failures and
331
- reports known Windows directory-flush limitations explicitly. Separate
332
- best-effort helpers preserve operations that do not promise crash durability.
333
-
334
- `publishFileExclusive()` adds no-clobber hardlink/copy/rename strategies and a
335
- typed post-creation receipt. Its `onSyncFailure` policy defaults to
336
- `"rollback"`; backup writers can choose `"preserve"` to keep a complete target
337
- when parent-directory sync fails, then inspect `details.directorySync` and
338
- retry or record the weaker durability state.
339
-
340
- See [Directory durability](docs/durability.md) for the receipt, pin lifecycle,
341
- publication policy, creation callback, and platform contract.
182
+ Use the [Directory durability](docs/durability.md) reference for directory
183
+ receipts, pinned synchronization, and exclusive publication policies, including
184
+ whether a completed target is preserved after a parent-directory sync failure.
342
185
 
343
186
  ## Atomic writes
344
187
 
345
- For preparation that must survive a parent rename until abort cleanup, use
346
- [`stageFileInDirectory()`](docs/staged-file.md) from `advanced`. It retains the
347
- original directory on Linux/macOS and requires native support for this operation.
348
- It offers atomic replace/no-replace publication, not expected-inode replacement
349
- or a crash-durability promise; application checks and coordination remain yours.
350
-
351
- For an already staged POSIX symlink, [`retainSymlinkInDirectory()`](docs/staged-symlink.md)
352
- admits caller-captured identity and retains that exact inode through no-replace
353
- publication or explicit recovery. Same-target foreign replacements are not adopted.
354
- Staging, cooperative locking and crash recovery remain application responsibilities.
355
-
356
- `replaceFileAtomic()` writes a sibling temp file, applies its exact mode through the still-open descriptor, optionally fsyncs it, and renames it over the destination. It never follows the published destination path to set file permissions. Mode preservation inherits only rwx bits from an existing non-symlink regular file; special bits, ownership, ACLs, and extended attributes are not copied. Pinned-destination hardlink rejection, rename retry / copy fallback on `EPERM`, bounded original-content restoration after a torn fallback, parent-directory fsync, and a `beforeRename` hook for backup or observer flows are all opt-in. `movePathWithCopyFallback()` stages cross-device moves before commit and removes only the copied source entries, so concurrent source additions or replacements are preserved. Its optional synchronous `assertBeforeMutation` hook rechecks caller authority before renames and each source removal; `onDestinationPublished` reports an exact bigint destination identity before later checks or cleanup can fail. See [mutation authority and publication receipts](docs/atomic.md#mutation-authority-and-publication-receipts).
188
+ `replaceFileAtomic()` writes a sibling temp and renames it over the destination.
189
+ File and parent-directory synchronization are opt-in:
357
190
 
358
191
  ```ts
359
192
  import { replaceFileAtomic } from "@openclaw/fs-safe/atomic";
@@ -367,13 +200,10 @@ await replaceFileAtomic({
367
200
  });
368
201
  ```
369
202
 
370
- `replaceFileAtomicSync()` covers the synchronous case with the same options shape. Both accept an injectable `fileSystem` for tests. Async adapters use `chmod()` on the `FileHandle` returned by their required `open()` operation; custom sync adapters using `mode` or `preserveExistingMode` provide the optional descriptor-bound `fchmodSync` operation.
371
-
372
- Both variants accept `assertBeforeMutation` for revocable caller authority and
373
- `onDestinationState` for observed removal, partial-write, and publication facts.
374
- The observer receives exact bigint identities from retained descriptors, including
375
- when later completion fails. These facts do not authorize rollback; the caller
376
- still owns current authority and content checks. See [atomic write authority](docs/atomic.md#atomic-write-authority-and-destination-state).
203
+ See [Atomic writes](docs/atomic.md) for synchronous adapters, fallback recovery,
204
+ mutation authority, and publication receipts. Retained lifecycles have separate
205
+ contracts: [staged files](docs/staged-file.md), [entry publication](docs/entry-publication.md),
206
+ and [staged symlinks](docs/staged-symlink.md).
377
207
 
378
208
  ## External outputs
379
209
 
@@ -393,22 +223,8 @@ await writeExternalFileWithinRoot({
393
223
  });
394
224
  ```
395
225
 
396
- The callback receives a staged path, not the final destination. The default
397
- `"workspace"` mode uses private temp storage plus `Root.copyIn()` for
398
- cross-device-tolerant finalization. `"sibling"` stages in the target directory,
399
- fsyncs the completed file, and atomically renames it over the target. Choose it
400
- when the destination directory is itself the writable boundary and atomic
401
- replacement matters.
402
-
403
- For sibling producers that can leave partial output before throwing, opt in to
404
- `producerIsolation: "private-directory"`. The callback writes inside an owned
405
- private workspace on the target filesystem, allowing cleanup after producer
406
- failure while preserving sibling publication behavior. See [external outputs](docs/output.md)
407
- for the identity checks and cleanup limits.
408
-
409
- Use it when the final filename is known before the external writer runs. If the
410
- filename depends on sniffing the produced bytes, write to a private temp
411
- workspace first, then finalize through the normal root APIs after validation.
226
+ The callback receives a staged path. Choose workspace or sibling staging and
227
+ producer isolation using the [staging-mode guide](docs/output.md#choosing-a-staging-mode).
412
228
 
413
229
  ## Stores
414
230
 
@@ -425,73 +241,11 @@ const store = files.json("settings.json", { lock: true });
425
241
  await store.updateOr({ enabled: false }, (current) => ({ ...current, enabled: true }));
426
242
  ```
427
243
 
428
- `jsonStore({ filePath })` is the single-path convenience wrapper for the same
429
- primitive and exposes its resolved absolute path.
430
-
431
- Use `update()` when missing state is part of your model; use `updateOr()` for
432
- the common merge-into-defaults case. Standalone helpers use options bags
433
- because they do not carry a bound root and often need multiple authority, path,
434
- and policy knobs.
435
-
436
- Sidecar locks fail closed on stale holders by default. Opt-in `remove-if-unchanged`
437
- recovery requires caller approval and serializes snapshot verification and unlink
438
- with an exclusive reclaim guard so a replacement lock cannot be deleted; see the
439
- [file lock docs](docs/sidecar-lock.md).
440
-
441
- Use `fileStore()` for cache/blob/media-style directories where callers
442
- need safe relative paths, size limits, atomic replacement, stream writes, and
443
- TTL cleanup behind one root. Pass `private: true` for credentials, auth
444
- profiles, tokens, and per-agent private state; private mode keeps the same
445
- store shape while routing writes through the secret-file atomic path.
446
-
447
- ```ts
448
- import { fileStore } from "@openclaw/fs-safe/store";
449
-
450
- const media = fileStore({
451
- rootDir: "/safe/workspace/media",
452
- maxBytes: 5 * 1024 * 1024,
453
- mode: 0o600,
454
- });
455
-
456
- await media.write("inbound/photo.jpg", bytes);
457
- await media.writeJson("state/photo.json", { id: "photo" });
458
- const cached = await media.readJsonIfExists("state/photo.json");
459
- const opened = await media.open("inbound/photo.jpg");
460
- await media.pruneExpired({ ttlMs: 10 * 60 * 1000, recursive: true });
461
- ```
462
-
463
- The `store` subpath also includes durable JSON queue helpers for the common
464
- "one JSON file per work item" pattern: atomic entry writes, pending-entry loads,
465
- acknowledgement via `.delivered` markers, failed-entry moves, and stale temp
466
- cleanup. On Windows, every independently supplied queue path rejects NTFS
467
- alternate-stream and directory-index namespace spellings before reads, locks,
468
- or mutations. Retry, dedupe, and transport semantics stay with the caller.
469
-
470
- `tempWorkspace()` exposes `write()`, `writeText()`, `writeJson()`, `copyIn()`, and `read()` for
471
- single-file scratch workflows without hand-rolled path joins, plus a `store: FileStore` view of
472
- the workspace dir for the richer cases (`writeStream`, `readJsonIfExists`, `store.json<T>(rel)`).
473
- Compatible creation and cleanup remain available without native support.
474
- Set `cleanupSafety: "require-bounded"` to require collision-safe quarantine and
475
- descriptor-bounded recursive cleanup before creating a child. See the
476
- [temp workspace contract](docs/temp.md).
477
- On POSIX, bounded cleanup requires owner read and search in the final `dirMode`
478
- (`0o500`); restrictive modes select compatible fallback or reject `require-bounded`
479
- before child creation.
480
- Linux bounded cleanup requires the exact `openat2`/`RESOLVE_NO_XDEV` capability
481
- at runtime; compatible mode falls back when unavailable, while `require-bounded`
482
- rejects before child creation.
483
-
484
- `tempFile()` is the smaller one-file temp helper. It is intentionally an
485
- advanced primitive: use `tempWorkspace()` for the stable temp surface and reach
486
- for `tempFile()` only when you need a raw file target.
487
-
488
- ```ts
489
- import { tempFile } from "@openclaw/fs-safe/advanced";
490
-
491
- await using target = await tempFile({ prefix: "download", fileName: "payload.bin" });
492
- await fs.promises.writeFile(target.path, bytes);
493
- const checksumPath = target.file("payload.sha256");
494
- ```
244
+ See [JSON stores](docs/json-store.md) for single-path stores and update semantics,
245
+ [File stores](docs/file-store.md) for blobs, streams, and private state, and
246
+ [File locks](docs/sidecar-lock.md) for coordination and stale-lock recovery.
247
+ The store subpath also provides [durable JSON queues](docs/store.md#durable-json-queues).
248
+ Use [temp workspaces](docs/temp.md) for scoped scratch files and cleanup policies.
495
249
 
496
250
  ## Exact file comparison
497
251
 
@@ -502,18 +256,10 @@ descriptors' positions and ownership.
502
256
 
503
257
  ## Secure absolute file reads
504
258
 
505
- Use `readSecureFile()` when the caller gives you an absolute credential path
506
- instead of a root-relative workspace path. It opens the file first, validates the
507
- same handle it will read from, checks trusted directories, owner, POSIX mode or
508
- Windows ACLs, hardlink count, size, and optional timeout, then reads through the
509
- pinned handle. On Windows, both the bytes and the owner/DACL facts come from that
510
- handle. In native `auto` or `off` mode, a packaged, readable PowerShell script can
511
- inspect that borrowed handle when the native capability is unavailable, subject
512
- to the [Windows security fallback prerequisites](docs/install.md#windows-security-fallback).
513
- It emits one fallback warning per process for secure reads and adds PowerShell
514
- startup and compilation overhead per call. Native `require` remains strict, and
515
- native operation failures are terminal. Neither route reopens the pathname to
516
- inspect its ACL.
259
+ Use [`readSecureFile()`](docs/secure-file.md) for an absolute credential path.
260
+ It validates permissions, ownership, identity, and size through the opened handle.
261
+ See [Windows fallback prerequisites](docs/install.md#windows-security-fallback)
262
+ when native support is unavailable.
517
263
 
518
264
  ```ts
519
265
  import { readSecureFile } from "@openclaw/fs-safe/secure-file";
@@ -531,10 +277,8 @@ flows where a warning is preferable to refusing the file.
531
277
 
532
278
  ## Directory walking
533
279
 
534
- [`Root.entries()`](docs/entries.md) observes one directory without descending or
535
- following child symlinks. It streams in filesystem order by default and supports
536
- cancellation, entry limits that throw on overflow, and bounded sorted-name
537
- collection. Use it when the caller owns traversal or symlink validation:
280
+ [`Root.entries()`](docs/entries.md) lists immediate children without following
281
+ child symlinks:
538
282
 
539
283
  ```ts
540
284
  for await (const entry of fs.entries("plugins", { maxEntries: 1_000 })) {
@@ -542,40 +286,11 @@ for await (const entry of fs.entries("plugins", { maxEntries: 1_000 })) {
542
286
  }
543
287
  ```
544
288
 
545
- `walkDirectory()` and `walkDirectorySync()` replace ad-hoc recursive
546
- `readdir()` loops with entry and depth budgets, a symlink policy, and stable
547
- relative paths.
548
-
549
- ```ts
550
- import { walkDirectory } from "@openclaw/fs-safe/walk";
551
-
552
- const scan = await walkDirectory("/safe/workspace", {
553
- maxDepth: 4,
554
- maxEntries: 10_000,
555
- symlinks: "skip",
556
- include: (entry) => entry.kind === "file",
557
- });
558
-
559
- for (const file of scan.entries) {
560
- console.log(file.relativePath);
561
- }
562
- ```
563
-
564
- Check `scan.truncated` before treating the result as complete, and `scan.failedDirs` to tell an incomplete scan (a directory that could not be read) from an empty one before pruning state from the listing.
565
-
566
- `walkDirectory()` accepts asynchronous `include` and `descend` callbacks through `AsyncWalkDirectoryOptions`, so a marker lookup can prune a directory before its children are read. Decisions remain serial and retain the options object as their `this` receiver; `walkDirectorySync()` and its options remain synchronous. See [Directory walking](docs/walk.md) for callback timing, JavaScript result compatibility, and error handling.
567
-
568
- For caller-controlled paths, `Root.walk()` is the root-bounded async iterator.
569
- It supports entry/depth budgets, including links without following their targets,
570
- in-root symlink following, cancellation, and a
571
- truncation marker (or typed error) when a budget is reached. Its `entryFilter`
572
- accepts `"include"`, `"skip"`, or `"skip-subtree"`, directly or through a Promise.
573
- After an awaited decision resolves, the walk rechecks cancellation and the
574
- current listing directory and Root identities before using it. Pending callbacks
575
- settle before cancellation or iterator disposal completes. Callback failures
576
- reject the walk. `onDirectoryError: "skip-and-report"` yields typed `"directory-error"` markers
577
- for directory read or identity-check failures while preserving entries from
578
- readable subtrees.
289
+ Use `Root.walk()` for root-bounded recursive traversal of caller-controlled
290
+ paths. Standalone `walkDirectory()` and `walkDirectorySync()` provide best-effort
291
+ inventories; inspect `truncated` and `failedDirs` before treating a scan as complete.
292
+ See [Directory walking](docs/walk.md) for budgets, ordering, filtering, and
293
+ cancellation contracts.
579
294
 
580
295
  ## Archive extraction
581
296
 
@@ -606,20 +321,13 @@ Extraction stages into a private directory and merges through the same safe-open
606
321
 
607
322
  ## Advanced path scopes
608
323
 
609
- For code that already has a trusted absolute path and wants lower-level boundary
610
- validation without going through `root()`:
611
-
612
- ```ts
613
- import { pathScope } from "@openclaw/fs-safe/advanced";
614
-
615
- const uploads = pathScope("/safe/uploads", { label: "uploads directory" });
616
- const files = await uploads.files(["photo.jpg"]);
617
- const target = await uploads.writable("report.pdf");
618
- ```
324
+ Use [`pathScope()`](docs/path-scope.md) for lower-level boundary validation over
325
+ a trusted absolute path.
619
326
 
620
327
  ## Errors
621
328
 
622
- Every failure surfaces as an `FsSafeError` with a closed `code` union you can branch on:
329
+ Boundary and policy failures use `FsSafeError` with a closed `code` union.
330
+ Parsing, callbacks, and underlying I/O can also throw other error types:
623
331
 
624
332
  ```ts
625
333
  import { FsSafeError } from "@openclaw/fs-safe/errors";
@@ -634,32 +342,15 @@ try {
634
342
  }
635
343
  ```
636
344
 
637
- Codes are grouped by category:
638
-
639
- ```ts
640
- if (err instanceof FsSafeError) {
641
- if (err.category === "policy") {
642
- // Unsafe caller input or filesystem state rejected by a safety policy.
643
- } else {
644
- // Routine filesystem outcome or runtime/environment problem.
645
- }
646
- }
647
- ```
648
-
649
- Routine filesystem outcomes such as `not-found`, `not-empty`, and
650
- `not-removable`, plus runtime failures such as `read-failed`, are operational;
651
- they do not indicate that a filesystem
652
- boundary policy was violated.
653
-
654
- Current `FsSafeErrorCode` values are `already-exists`, `denied-path`, `device-path`, `hardlink`, `helper-failed`, `helper-unavailable`, `invalid-path`, `insecure-permissions`, `not-empty`, `not-file`, `not-found`, `not-owned`, `not-removable`, `outside-workspace`, `path-alias`, `path-mismatch`, `permission-unverified`, `read-failed`, `secret-exists`, `store-reentrant-update`, `symlink`, `timeout`, `too-large`, and `unsupported-platform`.
345
+ For `FsSafeError`, `category` distinguishes policy rejections from operational
346
+ filesystem or runtime failures. See [Errors](docs/errors.md) for codes, receipts,
347
+ and other error families; check the error type before branching on its code.
655
348
 
656
349
  ## Safety model
657
350
 
658
- - root-bounded APIs resolve paths against a configured root and reject canonical escapes
659
- - reads reject known unsafe device paths, open with `O_NOFOLLOW` where available, then verify fd identity matches the path identity before returning the buffer or handle
660
- - create-only writes, sidecar acquisition, and exclusive publication prefer fd-relative native primitives, with verified guarded JavaScript fallbacks
661
- - `remove`, `mkdir`, `move`, `stat`, and `list` retain guarded JavaScript implementations with pre/post identity checks
662
- - archive extraction stages into a private directory and merges through the same boundary checks used by direct writes
351
+ Root operations combine confinement, no-follow opens, and identity checks.
352
+ The [security model](docs/security-model.md) describes guarantees and race limits
353
+ for native and JavaScript mechanisms on each platform.
663
354
 
664
355
  ## Limitations
665
356