@openclaw/signal 2026.11.3 → 2026.11.5-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 (179) hide show
  1. package/dist/.setup/{accounts-B0Pba1-q.mjs → accounts-enJKL3ci.mjs} +4 -8
  2. package/dist/.setup/api-CrasQEKI.mjs +14 -0
  3. package/dist/.setup/{api-CbN3mye4.mjs → api-DljZsfUn.mjs} +9 -11
  4. package/dist/.setup/{approval-auth-DhUKkSkA.mjs → approval-auth-B_5Ytx8K.mjs} +7 -11
  5. package/dist/.setup/{approval-handler.runtime-CsEcBZoc.mjs → approval-handler.runtime-0RbVmTxx.mjs} +7 -8
  6. package/dist/.setup/{approval-reactions-JsqCEOHK.mjs → approval-reactions-CvqVSSx8.mjs} +16 -24
  7. package/dist/.setup/{channel-CM-rLLPN.mjs → channel-ByJj8fSh.mjs} +417 -372
  8. package/dist/.setup/{client-Duwqbgqi.mjs → client-CesmEjRc.mjs} +170 -186
  9. package/dist/.setup/{config-schema-DvxZ18SD.mjs → config-schema-BCmW0BLB.mjs} +2 -5
  10. package/dist/.setup/{doctor-contract-api-BIdVW0PF.mjs → doctor-contract-api-CcLwIhsD.mjs} +88 -95
  11. package/dist/.setup/{identity-B3JyPqGO.mjs → identity-vKtR3DrM.mjs} +39 -2
  12. package/dist/.setup/{install-signal-cli-hYVLYGXe.mjs → install-signal-cli-DvLDZfpP.mjs} +95 -109
  13. package/dist/.setup/{transport-url-CAC0JcuI.mjs → legacy-transport-D_cZC3bS.mjs} +18 -1
  14. package/dist/.setup/{monitor-C6u1JV2C.mjs → monitor-Cf5sihwl.mjs} +166 -239
  15. package/dist/.setup/{probe-D_93QCe0.mjs → probe-Caq5Y4kA.mjs} +3 -3
  16. package/dist/.setup/{send-CM7-016e.mjs → send-BJuOWLky.mjs} +34 -66
  17. package/dist/.setup/{setup-surface-DvKLwYrl.mjs → setup-surface-DQ1yFZmY.mjs} +3 -3
  18. package/dist/.setup/{transport-detection-CmlgOybe.mjs → transport-detection-DvR6X_UQ.mjs} +3 -3
  19. package/dist/.setup/{transport-policy-B6C_LogW.mjs → transport-policy-zIR3lRUj.mjs} +1 -1
  20. package/dist/.setup/transport-probes.runtime-CoH6UFtc.mjs +1 -0
  21. package/dist/.source-module-urls/b02c526df8b90083105b63e5b51d5c8bbfe02676df72f6be8dd8f825817b32b9.json +1 -0
  22. package/dist/api.js +9 -11
  23. package/dist/channel-config-api.js +1 -2
  24. package/dist/channel-plugin-api.js +1 -2
  25. package/dist/contract-api.js +3 -4
  26. package/dist/doctor-contract-api.js +1 -2
  27. package/dist/runtime-api.js +1 -2
  28. package/dist/setup-entry.js +1 -1
  29. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +94 -0
  30. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  31. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
  32. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +3 -1
  33. package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +2 -2
  34. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +8 -1
  35. package/node_modules/@openclaw/fs-safe/dist/file-open-error.d.ts +2 -0
  36. package/node_modules/@openclaw/fs-safe/dist/file-open-error.js +14 -0
  37. package/node_modules/@openclaw/fs-safe/dist/file-sync.d.ts +2 -1
  38. package/node_modules/@openclaw/fs-safe/dist/file-sync.js +6 -1
  39. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
  40. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +1 -0
  41. package/node_modules/@openclaw/fs-safe/dist/native-config.js +14 -3
  42. package/node_modules/@openclaw/fs-safe/dist/native-operations.js +2 -0
  43. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +9 -2
  44. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +40 -32
  45. package/node_modules/@openclaw/fs-safe/dist/output.js +6 -0
  46. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.d.ts +2 -0
  47. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -0
  48. package/node_modules/@openclaw/fs-safe/dist/path.js +5 -1
  49. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +25 -2
  50. package/node_modules/@openclaw/fs-safe/dist/publication-handle.d.ts +4 -0
  51. package/node_modules/@openclaw/fs-safe/dist/publication-handle.js +32 -0
  52. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.d.ts +2 -1
  53. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +19 -9
  54. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +23 -24
  55. package/node_modules/@openclaw/fs-safe/dist/publish-rename-fallback.d.ts +21 -0
  56. package/node_modules/@openclaw/fs-safe/dist/publish-rename-fallback.js +102 -0
  57. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +2 -0
  58. package/node_modules/@openclaw/fs-safe/dist/root-directory-entry.d.ts +3 -1
  59. package/node_modules/@openclaw/fs-safe/dist/root-directory-entry.js +8 -5
  60. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +3 -0
  61. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -1
  62. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +19 -9
  63. package/node_modules/@openclaw/fs-safe/dist/root-file-final-admission.js +3 -3
  64. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.d.ts +2 -2
  65. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +27 -13
  66. package/node_modules/@openclaw/fs-safe/dist/root-path.js +2 -2
  67. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.d.ts +3 -0
  68. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +18 -3
  69. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +1 -1
  70. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +68 -21
  71. package/node_modules/@openclaw/fs-safe/dist/watch-io-error.d.ts +2 -0
  72. package/node_modules/@openclaw/fs-safe/dist/watch-io-error.js +27 -0
  73. package/node_modules/@openclaw/fs-safe/dist/watch-io-worker.d.ts +1 -0
  74. package/node_modules/@openclaw/fs-safe/dist/watch-io-worker.js +93 -0
  75. package/node_modules/@openclaw/fs-safe/dist/watch-io.d.ts +47 -0
  76. package/node_modules/@openclaw/fs-safe/dist/watch-io.js +236 -0
  77. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +1 -1
  78. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +3 -3
  79. package/node_modules/@openclaw/fs-safe/dist/watch-probe.d.ts +16 -0
  80. package/node_modules/@openclaw/fs-safe/dist/watch-probe.js +135 -0
  81. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +1 -1
  82. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +86 -24
  83. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +4 -0
  84. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +78 -16
  85. package/node_modules/@openclaw/fs-safe/dist/watch-stream.d.ts +2 -0
  86. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +23 -0
  87. package/node_modules/@openclaw/fs-safe/dist/watch-types.d.ts +3 -1
  88. package/node_modules/@openclaw/fs-safe/dist/watch.js +60 -13
  89. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +1 -1
  90. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +1 -0
  91. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
  92. package/node_modules/@openclaw/fs-safe/docs/advanced.md +5 -2
  93. package/node_modules/@openclaw/fs-safe/docs/compiler-qualification.md +392 -0
  94. package/node_modules/@openclaw/fs-safe/docs/config.md +7 -1
  95. package/node_modules/@openclaw/fs-safe/docs/contributing.md +25 -5
  96. package/node_modules/@openclaw/fs-safe/docs/copy.md +10 -0
  97. package/node_modules/@openclaw/fs-safe/docs/durability.md +9 -3
  98. package/node_modules/@openclaw/fs-safe/docs/errors.md +13 -0
  99. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -4
  100. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +6 -4
  101. package/node_modules/@openclaw/fs-safe/docs/output.md +4 -2
  102. package/node_modules/@openclaw/fs-safe/docs/path.md +5 -2
  103. package/node_modules/@openclaw/fs-safe/docs/permissions.md +15 -0
  104. package/node_modules/@openclaw/fs-safe/docs/security-model.md +9 -0
  105. package/node_modules/@openclaw/fs-safe/docs/staged-file.md +2 -0
  106. package/node_modules/@openclaw/fs-safe/docs/testing.md +1 -1
  107. package/node_modules/@openclaw/fs-safe/docs/watch.md +62 -5
  108. package/node_modules/@openclaw/fs-safe/docs/writing.md +6 -1
  109. package/node_modules/@openclaw/fs-safe/package.json +18 -16
  110. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  111. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  112. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  113. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  114. package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
  115. package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +1 -1
  116. package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
  117. package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +1 -1
  118. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  119. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  120. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  121. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  122. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  123. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  124. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  125. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  126. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
  127. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +1 -1
  128. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  129. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  130. package/node_modules/zod/package.json +1 -1
  131. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  132. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  133. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  134. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  135. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  136. package/node_modules/zod/src/v4/core/util.ts +13 -0
  137. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  138. package/node_modules/zod/v4/core/compile.cjs +8 -0
  139. package/node_modules/zod/v4/core/compile.js +8 -0
  140. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  141. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  142. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  143. package/node_modules/zod/v4/core/schemas.js +30 -0
  144. package/node_modules/zod/v4/core/util.cjs +15 -0
  145. package/node_modules/zod/v4/core/util.d.cts +2 -0
  146. package/node_modules/zod/v4/core/util.d.ts +2 -0
  147. package/node_modules/zod/v4/core/util.js +14 -0
  148. package/node_modules/zod/v4/core/versions.cjs +1 -1
  149. package/node_modules/zod/v4/core/versions.js +1 -1
  150. package/openclaw.plugin.json +0 -30
  151. package/package.json +6 -8
  152. package/dist/.setup/api-CJBtD3lT.mjs +0 -16
  153. package/dist/.setup/client-adapter-Chnbch_z.mjs +0 -30
  154. package/dist/.setup/legacy-transport-QIpaMNST.mjs +0 -18
  155. package/dist/.setup/normalize-D7kSomXt.mjs +0 -39
  156. package/dist/.setup/reaction-runtime-api-mdO4k0yv.mjs +0 -65
  157. package/dist/.setup/reaction-targets-BgPLDHBf.mjs +0 -72
  158. package/dist/.setup/rpc-context-BAXcX7dI.mjs +0 -13
  159. package/dist/.setup/transport-probes.runtime-D-xIMRcn.mjs +0 -2
  160. package/dist/reaction-runtime-api.js +0 -2
  161. package/node_modules/ws/LICENSE +0 -20
  162. package/node_modules/ws/README.md +0 -548
  163. package/node_modules/ws/browser.js +0 -8
  164. package/node_modules/ws/index.js +0 -22
  165. package/node_modules/ws/lib/buffer-util.js +0 -131
  166. package/node_modules/ws/lib/constants.js +0 -19
  167. package/node_modules/ws/lib/event-target.js +0 -292
  168. package/node_modules/ws/lib/extension.js +0 -203
  169. package/node_modules/ws/lib/limiter.js +0 -55
  170. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  171. package/node_modules/ws/lib/receiver.js +0 -743
  172. package/node_modules/ws/lib/sender.js +0 -607
  173. package/node_modules/ws/lib/stream.js +0 -161
  174. package/node_modules/ws/lib/subprotocol.js +0 -62
  175. package/node_modules/ws/lib/validation.js +0 -152
  176. package/node_modules/ws/lib/websocket-server.js +0 -562
  177. package/node_modules/ws/lib/websocket.js +0 -1421
  178. package/node_modules/ws/package.json +0 -74
  179. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,6 +1,6 @@
1
- import { o as __exportAll } from "./doctor-contract-api-BIdVW0PF.mjs";
2
- import { n as signalRpcRequest, t as signalCheck } from "./client-adapter-Chnbch_z.mjs";
3
- import { t as detectSignalTransport } from "./transport-detection-CmlgOybe.mjs";
1
+ import { o as __exportAll } from "./doctor-contract-api-CcLwIhsD.mjs";
2
+ import { C as signalCheck, w as signalRpcRequest } from "./channel-ByJj8fSh.mjs";
3
+ import { t as detectSignalTransport } from "./transport-detection-DvR6X_UQ.mjs";
4
4
  import { normalizeOptionalString, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
6
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
@@ -1,10 +1,8 @@
1
- import { o as __exportAll } from "./doctor-contract-api-BIdVW0PF.mjs";
2
- import { i as resolveSignalAccount } from "./accounts-B0Pba1-q.mjs";
3
- import { n as normalizeSignalMessagingTarget } from "./normalize-D7kSomXt.mjs";
4
- import { S as markdownToSignalText, g as registerSignalReplyContext } from "./channel-CM-rLLPN.mjs";
5
- import { o as isSignalQuoteMetadataRejection } from "./client-Duwqbgqi.mjs";
6
- import { n as signalRpcRequest } from "./client-adapter-Chnbch_z.mjs";
7
- import { t as resolveSignalRpcContext } from "./rpc-context-BAXcX7dI.mjs";
1
+ import { o as __exportAll } from "./doctor-contract-api-CcLwIhsD.mjs";
2
+ import { i as resolveSignalAccount } from "./accounts-enJKL3ci.mjs";
3
+ import { d as normalizeSignalMessagingTarget } from "./identity-vKtR3DrM.mjs";
4
+ import { O as markdownToSignalText, S as resolveSignalRpcContext, g as registerSignalReplyContext, w as signalRpcRequest } from "./channel-ByJj8fSh.mjs";
5
+ import { o as isSignalQuoteMetadataRejection } from "./client-CesmEjRc.mjs";
8
6
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
9
7
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
10
8
  import { asPositiveSafeInteger, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -61,49 +59,6 @@ function buildTargetParams(target) {
61
59
  if (target.type === "group") return { groupId: target.groupId };
62
60
  return { username: [target.username] };
63
61
  }
64
- function createSignalSendReceipt(params) {
65
- const messageId = params.messageId.trim();
66
- const results = messageId && messageId !== "unknown" ? [{
67
- channel: "signal",
68
- messageId,
69
- meta: {
70
- targetType: params.target.type,
71
- ...params.replyToId ? {
72
- replyToId: params.replyToId,
73
- nativeReplyStatus: params.nativeReplyStatus ?? "sent"
74
- } : {}
75
- }
76
- }] : [];
77
- if (results[0]) {
78
- if (params.timestamp != null) results[0].timestamp = params.timestamp;
79
- if (params.target.type === "group") results[0].chatId = params.target.groupId;
80
- else if (params.target.type === "recipient") results[0].toJid = params.target.recipient;
81
- else results[0].toJid = params.target.username;
82
- }
83
- return createMessageReceiptFromOutboundResults({
84
- results,
85
- kind: params.kind,
86
- ...params.replyToId ? { replyToId: params.replyToId } : {}
87
- });
88
- }
89
- function parseSignalReplyTimestamp(raw) {
90
- const value = normalizeOptionalString(raw);
91
- if (!value || !/^\d+$/.test(value)) return;
92
- return asPositiveSafeInteger(Number(value));
93
- }
94
- function resolveSignalQuoteParams(opts) {
95
- const timestamp = parseSignalReplyTimestamp(opts.replyToId);
96
- const author = normalizeOptionalString(opts.replyToAuthor);
97
- if (timestamp === void 0 || !author) return;
98
- return {
99
- replyToId: String(timestamp),
100
- params: {
101
- quoteTimestamp: timestamp,
102
- quoteAuthor: author,
103
- quoteMessage: opts.replyToBody ?? ""
104
- }
105
- };
106
- }
107
62
  async function sendMessageSignal(to, text, opts) {
108
63
  const cfg = requireRuntimeConfig(opts.cfg, "Signal send");
109
64
  const accountInfo = resolveSignalAccount({
@@ -114,16 +69,11 @@ async function sendMessageSignal(to, text, opts) {
114
69
  const target = parseTarget(to);
115
70
  const targetAuthor = normalizeOptionalString(account);
116
71
  const targetAuthorUuid = normalizeOptionalString(accountInfo.config.accountUuid);
117
- let message = text ?? "";
72
+ let message = text;
118
73
  let outboundMedia;
119
74
  let textStyles = [];
120
75
  const textMode = opts.textMode ?? "markdown";
121
- const maxBytes = (() => {
122
- if (typeof opts.maxBytes === "number") return opts.maxBytes;
123
- if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
124
- if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
125
- return 8388608;
126
- })();
76
+ const maxBytes = opts.maxBytes ?? (accountInfo.config.mediaMaxMb ?? cfg.agents?.defaults?.mediaMaxMb ?? 8) * 1024 * 1024;
127
77
  let attachments;
128
78
  if (opts.mediaUrl?.trim()) {
129
79
  const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, {
@@ -156,7 +106,17 @@ async function sendMessageSignal(to, text, opts) {
156
106
  if (account) params.account = account;
157
107
  if (attachments && attachments.length > 0) params.attachments = attachments;
158
108
  Object.assign(params, buildTargetParams(target));
159
- const quote = resolveSignalQuoteParams(opts);
109
+ const rawReplyToId = normalizeOptionalString(opts.replyToId);
110
+ const quoteTimestamp = rawReplyToId && /^\d+$/.test(rawReplyToId) ? asPositiveSafeInteger(Number(rawReplyToId)) : void 0;
111
+ const quoteAuthor = normalizeOptionalString(opts.replyToAuthor);
112
+ const quote = quoteTimestamp !== void 0 && quoteAuthor ? {
113
+ replyToId: String(quoteTimestamp),
114
+ params: {
115
+ quoteTimestamp,
116
+ quoteAuthor,
117
+ quoteMessage: opts.replyToBody ?? ""
118
+ }
119
+ } : void 0;
160
120
  const sendOpts = {
161
121
  baseUrl,
162
122
  timeoutMs: opts.timeoutMs,
@@ -190,18 +150,26 @@ async function sendMessageSignal(to, text, opts) {
190
150
  media: outboundMedia ? [outboundMedia] : void 0,
191
151
  sourceTimestamp: timestamp
192
152
  });
153
+ const receiptMessageId = messageId.trim();
193
154
  return {
194
155
  messageId,
195
156
  timestamp,
196
- receipt: createSignalSendReceipt({
197
- messageId,
198
- target,
157
+ receipt: createMessageReceiptFromOutboundResults({
158
+ results: receiptMessageId && receiptMessageId !== "unknown" ? [{
159
+ channel: "signal",
160
+ messageId: receiptMessageId,
161
+ ...timestamp != null ? { timestamp } : {},
162
+ ...target.type === "group" ? { chatId: target.groupId } : { toJid: target.type === "recipient" ? target.recipient : target.username },
163
+ meta: {
164
+ targetType: target.type,
165
+ ...quote ? {
166
+ replyToId: quote.replyToId,
167
+ nativeReplyStatus: nativeReplyStatus ?? "sent"
168
+ } : {}
169
+ }
170
+ }] : [],
199
171
  kind: attachments && attachments.length > 0 ? "media" : "text",
200
- ...quote ? {
201
- replyToId: quote.replyToId,
202
- nativeReplyStatus
203
- } : {},
204
- ...timestamp != null ? { timestamp } : {}
172
+ ...quote ? { replyToId: quote.replyToId } : {}
205
173
  })
206
174
  };
207
175
  }
@@ -1,6 +1,6 @@
1
- import { i as resolveSignalAccount, n as listSignalAccountIds } from "./accounts-B0Pba1-q.mjs";
2
- import { d as signalNumberTextInput, l as signalCompletionNote, s as createSignalCliPathTextInput, u as signalDmPolicy } from "./channel-CM-rLLPN.mjs";
3
- import { r as installSignalCli } from "./install-signal-cli-hYVLYGXe.mjs";
1
+ import { i as resolveSignalAccount, n as listSignalAccountIds } from "./accounts-enJKL3ci.mjs";
2
+ import { d as signalNumberTextInput, l as signalCompletionNote, s as createSignalCliPathTextInput, u as signalDmPolicy } from "./channel-ByJj8fSh.mjs";
3
+ import { n as installSignalCli } from "./install-signal-cli-DvLDZfpP.mjs";
4
4
  import { createDetectedBinaryStatus, createSetupTranslator, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
5
5
  import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
6
6
  //#region extensions/signal/src/setup-surface.ts
@@ -1,12 +1,12 @@
1
- import { o as __exportAll } from "./doctor-contract-api-BIdVW0PF.mjs";
2
- import { a as normalizeSignalTransportUrl } from "./transport-url-CAC0JcuI.mjs";
1
+ import { o as __exportAll } from "./doctor-contract-api-CcLwIhsD.mjs";
2
+ import { s as normalizeSignalTransportUrl } from "./legacy-transport-D_cZC3bS.mjs";
3
3
  //#region extensions/signal/src/transport-detection.ts
4
4
  var transport_detection_exports = /* @__PURE__ */ __exportAll({ detectSignalTransport: () => detectSignalTransport });
5
5
  const DEFAULT_PROBE_TIMEOUT_MS = 1e4;
6
6
  async function detectSignalTransport(params) {
7
7
  const url = normalizeSignalTransportUrl(params.url);
8
8
  const timeoutMs = params.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
9
- const probes = params.probeNative && params.probeContainer ? void 0 : await import("./transport-probes.runtime-D-xIMRcn.mjs");
9
+ const probes = params.probeNative && params.probeContainer ? void 0 : await import("./transport-probes.runtime-CoH6UFtc.mjs");
10
10
  const probeNative = params.probeNative ?? probes?.nativeCheck;
11
11
  const probeContainer = params.probeContainer ?? probes?.containerCheck;
12
12
  if (!probeNative || !probeContainer) throw new Error("Signal transport checks are unavailable");
@@ -1,4 +1,4 @@
1
- import { a as normalizeSignalTransportUrl } from "./transport-url-CAC0JcuI.mjs";
1
+ import { s as normalizeSignalTransportUrl } from "./legacy-transport-D_cZC3bS.mjs";
2
2
  //#region extensions/signal/src/transport-policy.ts
3
3
  const DEFAULT_SIGNAL_MANAGED_NATIVE_PORT = 8080;
4
4
  const DEFAULT_SIGNAL_MANAGED_NATIVE_HOST = "127.0.0.1";
@@ -0,0 +1 @@
1
+ export { s as containerCheck, n as nativeCheck } from "./client-CesmEjRc.mjs";
@@ -0,0 +1 @@
1
+ {"version":3,"entries":[{"output":"api.js","source":"extensions/signal/api.ts"},{"output":"channel-config-api.js","source":"extensions/signal/channel-config-api.ts"},{"output":"channel-entry.js","source":"extensions/signal/channel-entry.ts"},{"output":"channel-plugin-api.js","source":"extensions/signal/channel-plugin-api.ts"},{"output":"contract-api.js","source":"extensions/signal/contract-api.ts"},{"output":"doctor-contract-api.js","source":"extensions/signal/doctor-contract-api.ts"},{"output":"index.js","source":"extensions/signal/index.ts"},{"output":"runtime-api.js","source":"extensions/signal/runtime-api.ts"},{"output":"secret-contract-api.js","source":"extensions/signal/secret-contract-api.ts"},{"output":"setup-entry.js","source":"extensions/signal/setup-entry.ts"}],"chunks":[{"output":".setup/accounts-enJKL3ci.mjs","sha256":"88c2d065d3093e5d29cbe6bfa1fc9b7df33b5454123a7b985f8f738d1234329a","facadeSource":null,"sources":["extensions/signal/src/accounts.ts"]},{"output":".setup/api-CrasQEKI.mjs","sha256":"fd4326f73ef2e98af1369fbb05f4ab82b649b49fdd2263d9575bf731e8ac3837","facadeSource":null,"sources":["extensions/signal/src/channel.setup.ts"]},{"output":".setup/api-DljZsfUn.mjs","sha256":"81a1a748efe99832d51a600fcb1deccaa9e745adb940cd01cd94f385bf535e2e","facadeSource":"extensions/signal/api.ts","sources":[]},{"output":".setup/approval-auth-B_5Ytx8K.mjs","sha256":"d828624f211bd0a249d8ce51167e467c0f31c5c40558a96cec41f2f1b8b65e4b","facadeSource":null,"sources":["extensions/signal/src/aliases.ts","extensions/signal/src/approval-auth.ts"]},{"output":".setup/approval-handler.runtime-0RbVmTxx.mjs","sha256":"ac14e5813201b439744fe7bd77a5b74678deb7107723d9b496d35e6b406f1e8a","facadeSource":"extensions/signal/src/approval-handler.runtime.ts","sources":["extensions/signal/src/approval-handler.runtime.ts"]},{"output":".setup/approval-reactions-CvqVSSx8.mjs","sha256":"cfd63850a1634e11eebabd17d8f753852c149b44adb6b7207c85aee6a9e2160e","facadeSource":null,"sources":["extensions/signal/src/approval-reactions.ts"]},{"output":".setup/channel-ByJj8fSh.mjs","sha256":"e8155da380d4aaf94a35a5c4de00a01da17f1cdf133406b18fbe9eb85df2c462","facadeSource":null,"sources":["extensions/signal/src/approval-native.ts","extensions/signal/src/channel.ts","extensions/signal/src/client-adapter.ts","extensions/signal/src/daemon-env.ts","extensions/signal/src/doctor.ts","extensions/signal/src/format.ts","extensions/signal/src/media-text.ts","extensions/signal/src/message-actions.ts","extensions/signal/src/outbound-session.ts","extensions/signal/src/presentation-fallback.ts","extensions/signal/src/reaction-level.ts","extensions/signal/src/reply-authors-state.ts","extensions/signal/src/reply-authors.ts","extensions/signal/src/rpc-context.ts","extensions/signal/src/send-reactions.ts","extensions/signal/src/setup-core.ts","extensions/signal/src/setup-transport.ts","extensions/signal/src/shared.ts"]},{"output":".setup/client-CesmEjRc.mjs","sha256":"96373cf58fadb4210e2a0d691926e83a769baee44dc25cb384085088c6e40bd9","facadeSource":null,"sources":["extensions/signal/src/client-container.ts","extensions/signal/src/client.ts","extensions/signal/src/quote-rejection.ts","extensions/signal/src/socket-path.ts"]},{"output":".setup/config-schema-BCmW0BLB.mjs","sha256":"9d1d54aff8ea37ea11104f8ad217009d1e08c067e37b2cb86de172eb71b9d560","facadeSource":null,"sources":["extensions/signal/src/config-schema.ts"]},{"output":".setup/doctor-contract-api-CcLwIhsD.mjs","sha256":"da87e080f6148ea3f1898779834d1ebe83a6941becf1a91c0f31cf1039c7359d","facadeSource":null,"sources":["extensions/signal/config-doctor-api.ts","extensions/signal/src/account-key-repair.ts","extensions/signal/src/config-compat.ts","packages/normalization-core/src/record-coerce.ts","src/config/channel-config-normalization.ts","src/plugin-sdk/legacy-webhook-listener-migration.ts"]},{"output":".setup/identity-vKtR3DrM.mjs","sha256":"b74cb347fb1f871e99a6817ff22f5aff9aae0bbab34425fda93a0f1c902bb72d","facadeSource":null,"sources":["extensions/signal/src/identity.ts","extensions/signal/src/normalize.ts","extensions/signal/src/uuid.ts"]},{"output":".setup/install-signal-cli-DvLDZfpP.mjs","sha256":"5d313e25788a11f02405510870be3a62a91cd673844a4352aff969a806ebf79e","facadeSource":null,"sources":["extensions/signal/src/install-signal-cli.ts"]},{"output":".setup/legacy-transport-D_cZC3bS.mjs","sha256":"e76b5f3a0602975ef5d7cd3d07ba1e823289078ca5ad1949331c2a4afe72f078","facadeSource":null,"sources":["extensions/signal/src/account-selection.ts","extensions/signal/src/legacy-transport.ts","extensions/signal/src/transport-url.ts"]},{"output":".setup/monitor-Cf5sihwl.mjs","sha256":"22f42112c9852edf4c2fe81ee5d2f1ff81d672976871faa5e592f217942511f1","facadeSource":null,"sources":["extensions/signal/src/daemon.ts","extensions/signal/src/monitor.ts","extensions/signal/src/monitor/access-policy.ts","extensions/signal/src/monitor/event-handler.control-lane.ts","extensions/signal/src/monitor/event-handler.ts","extensions/signal/src/monitor/inbound-context.ts","extensions/signal/src/monitor/mentions.ts","extensions/signal/src/monitor/reactions.ts","extensions/signal/src/native-reply.ts","extensions/signal/src/signal-ingress.ts","extensions/signal/src/sse-reconnect.ts"]},{"output":".setup/probe-Caq5Y4kA.mjs","sha256":"90af76a48bc8cf40b740910fd59b6754db5877f6f55efb3d198b5d95f5ea7988","facadeSource":null,"sources":["extensions/signal/src/probe.ts"]},{"output":".setup/runtime-8sxUF217.mjs","sha256":"b9d6d598e4dad4e9bc044e65bd855e67a07a283d543e9169a82e4d6ab2c03794","facadeSource":null,"sources":["extensions/signal/src/runtime.ts"]},{"output":".setup/send-BJuOWLky.mjs","sha256":"e6bc0863f63bacb446d5a49c55fb60e61972c2966f6ccbf3429d359c33e4e62c","facadeSource":null,"sources":["extensions/signal/src/send.ts"]},{"output":".setup/setup-surface-DQ1yFZmY.mjs","sha256":"3fc0930fc3c45d64a630cbba99c1ec4623c4799d42a9b872b04654a2117e1f04","facadeSource":"extensions/signal/src/setup-surface.ts","sources":["extensions/signal/src/setup-surface.ts"]},{"output":".setup/transport-detection-DvR6X_UQ.mjs","sha256":"284b97e5e7a78eaf14c258602dd47de205bc8138ca684e0df3a7da1d351c901f","facadeSource":null,"sources":["extensions/signal/src/transport-detection.ts"]},{"output":".setup/transport-policy-zIR3lRUj.mjs","sha256":"47e1ee186bb8a91dab5488df97c885fff2579b0ea74b0effcdfbf916869b9276","facadeSource":null,"sources":["extensions/signal/src/transport-policy.ts"]},{"output":".setup/transport-probes.runtime-CoH6UFtc.mjs","sha256":"aaa1a60900f23c50d3d5e6bb21c335fb0457e595fcde2f413949c1fa407af05a","facadeSource":"extensions/signal/src/transport-probes.runtime.ts","sources":[]},{"output":"api.js","sha256":"f3ca8fcfb69b75622a13bf326be478b12e0130195b0bb742a143bd7fa562328a","facadeSource":"extensions/signal/api.ts","sources":[]},{"output":"channel-config-api.js","sha256":"f981b812c6c39af1bfc2ca678f6486fa057ba4cb9a34c0e037e01ba96e37a122","facadeSource":"extensions/signal/channel-config-api.ts","sources":[]},{"output":"channel-entry.js","sha256":"0168747ca73aed666ba88aeaf867be2fa571aa581f39c8727e4e2a0daf6fcc55","facadeSource":"extensions/signal/channel-entry.ts","sources":["extensions/signal/channel-entry.ts"]},{"output":"channel-plugin-api.js","sha256":"e9e84b93622e7d320557395ac8a2dc28bb30c699f84e6353f5a53938a18a98ea","facadeSource":"extensions/signal/channel-plugin-api.ts","sources":[]},{"output":"contract-api.js","sha256":"a53a963b4828486edcf4256526740135245cd09bebd327fe2ed25f5044b2db61","facadeSource":"extensions/signal/contract-api.ts","sources":[]},{"output":"doctor-contract-api.js","sha256":"17a19c6b72174724617b0c5eb0a7204ac20cf68d644d3839bac835cdb70c2987","facadeSource":"extensions/signal/doctor-contract-api.ts","sources":[]},{"output":"index.js","sha256":"6bec25c4f06a6812637b9e63581877359f06595e39c683fe8447b8c2aa4cab42","facadeSource":"extensions/signal/index.ts","sources":["extensions/signal/index.ts"]},{"output":"runtime-api.js","sha256":"4165bbc9560754c2fb7980b535e9e4c5efa4ab169715242f27d492993074a82d","facadeSource":"extensions/signal/runtime-api.ts","sources":[]},{"output":"secret-contract-api.js","sha256":"4dbe6f638c54b4b19bc7e251990d9fed2e7c4c98e054402defe157405159569b","facadeSource":"extensions/signal/secret-contract-api.ts","sources":["extensions/signal/secret-contract-api.ts"]},{"output":"setup-entry.js","sha256":"f7ee4a2d55bed198fec605c72753a94f0e11ad5caf0d7e13b8cd67fe422b518b","facadeSource":"extensions/signal/setup-entry.ts","sources":["extensions/signal/setup-entry.ts"]}],"unrenderedSources":["extensions/signal/api.ts","extensions/signal/channel-config-api.ts","extensions/signal/channel-plugin-api.ts","extensions/signal/contract-api.ts","extensions/signal/doctor-contract-api.ts","extensions/signal/runtime-api.ts","extensions/signal/src/transport-probes.runtime.ts"]}
package/dist/api.js CHANGED
@@ -1,12 +1,10 @@
1
- import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./.setup/accounts-B0Pba1-q.mjs";
2
- import { n as normalizeSignalMessagingTarget, t as looksLikeSignalTargetId } from "./.setup/normalize-D7kSomXt.mjs";
3
- import { a as normalizeSignalAllowRecipient, c as resolveSignalSender, i as isSignalSenderAllowed, l as looksLikeUuid, n as formatSignalSenderDisplay, o as resolveSignalPeerId, r as formatSignalSenderId, s as resolveSignalRecipient, t as formatSignalPairingIdLine } from "./.setup/identity-B3JyPqGO.mjs";
4
- import { C as markdownToSignalTextChunks, S as markdownToSignalText, b as resolveSignalReactionLevel, c as normalizeSignalAccountInput, h as writeSignalAccountTransport, m as probeSignalTransport, p as prepareSignalManagedNativeTransport, t as signalPlugin, v as resolveSignalOutboundTarget, y as signalMessageActions } from "./.setup/channel-CM-rLLPN.mjs";
5
- import { n as sendReactionSignal, t as removeReactionSignal } from "./.setup/reaction-runtime-api-mdO4k0yv.mjs";
6
- import { t as detectSignalTransport } from "./.setup/transport-detection-CmlgOybe.mjs";
7
- import { t as signalSetupPlugin } from "./.setup/api-CJBtD3lT.mjs";
8
- import { r as installSignalCli } from "./.setup/install-signal-cli-hYVLYGXe.mjs";
9
- import { t as monitorSignalProvider } from "./.setup/monitor-C6u1JV2C.mjs";
10
- import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./.setup/send-CM7-016e.mjs";
11
- import { t as probeSignal } from "./.setup/probe-D_93QCe0.mjs";
1
+ import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./.setup/accounts-enJKL3ci.mjs";
2
+ import { a as normalizeSignalAllowRecipient, c as resolveSignalSender, d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, l as looksLikeUuid, n as formatSignalSenderDisplay, o as resolveSignalPeerId, r as formatSignalSenderId, s as resolveSignalRecipient, t as formatSignalPairingIdLine, u as looksLikeSignalTargetId } from "./.setup/identity-vKtR3DrM.mjs";
3
+ import { E as resolveSignalReactionLevel, O as markdownToSignalText, b as removeReactionSignal, c as normalizeSignalAccountInput, h as writeSignalAccountTransport, k as markdownToSignalTextChunks, m as probeSignalTransport, p as prepareSignalManagedNativeTransport, t as signalPlugin, v as resolveSignalOutboundTarget, x as sendReactionSignal, y as signalMessageActions } from "./.setup/channel-ByJj8fSh.mjs";
4
+ import { t as detectSignalTransport } from "./.setup/transport-detection-DvR6X_UQ.mjs";
5
+ import { t as signalSetupPlugin } from "./.setup/api-CrasQEKI.mjs";
6
+ import { n as installSignalCli } from "./.setup/install-signal-cli-DvLDZfpP.mjs";
7
+ import { t as monitorSignalProvider } from "./.setup/monitor-Cf5sihwl.mjs";
8
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./.setup/send-BJuOWLky.mjs";
9
+ import { t as probeSignal } from "./.setup/probe-Caq5Y4kA.mjs";
12
10
  export { detectSignalTransport, formatSignalPairingIdLine, formatSignalSenderDisplay, formatSignalSenderId, installSignalCli, isSignalSenderAllowed, listEnabledSignalAccounts, listSignalAccountIds, looksLikeSignalTargetId, looksLikeUuid, markdownToSignalText, markdownToSignalTextChunks, monitorSignalProvider, normalizeSignalAccountInput, normalizeSignalAllowRecipient, normalizeSignalMessagingTarget, prepareSignalManagedNativeTransport, probeSignal, probeSignalTransport, removeReactionSignal, resolveDefaultSignalAccountId, resolveSignalAccount, resolveSignalOutboundTarget, resolveSignalPeerId, resolveSignalReactionLevel, resolveSignalRecipient, resolveSignalSender, sendMessageSignal, sendReactionSignal, sendReadReceiptSignal, sendTypingSignal, signalMessageActions, signalPlugin, signalSetupPlugin, writeSignalAccountTransport };
@@ -1,2 +1 @@
1
- import { t as SignalChannelConfigSchema } from "./.setup/config-schema-DvxZ18SD.mjs";
2
- export { SignalChannelConfigSchema };
1
+ export { t as SignalChannelConfigSchema } from "./.setup/config-schema-BCmW0BLB.mjs";
@@ -1,2 +1 @@
1
- import { t as signalPlugin } from "./.setup/channel-CM-rLLPN.mjs";
2
- export { signalPlugin };
1
+ export { t as signalPlugin } from "./.setup/channel-ByJj8fSh.mjs";
@@ -1,4 +1,3 @@
1
- import { n as normalizeSignalMessagingTarget, r as normalizeSignalReactionRecipient, t as looksLikeSignalTargetId } from "./.setup/normalize-D7kSomXt.mjs";
2
- import { i as isSignalSenderAllowed } from "./.setup/identity-B3JyPqGO.mjs";
3
- import { i as installSignalCliFromRelease, n as extractSignalCliArchive, r as installSignalCli, t as downloadToFile } from "./.setup/install-signal-cli-hYVLYGXe.mjs";
4
- export { downloadToFile, extractSignalCliArchive, installSignalCli, installSignalCliFromRelease, isSignalSenderAllowed, looksLikeSignalTargetId, normalizeSignalMessagingTarget, normalizeSignalReactionRecipient };
1
+ import { d as normalizeSignalMessagingTarget, f as normalizeSignalReactionRecipient, i as isSignalSenderAllowed, u as looksLikeSignalTargetId } from "./.setup/identity-vKtR3DrM.mjs";
2
+ import { n as installSignalCli, t as downloadToFile } from "./.setup/install-signal-cli-DvLDZfpP.mjs";
3
+ export { downloadToFile, installSignalCli, isSignalSenderAllowed, looksLikeSignalTargetId, normalizeSignalMessagingTarget, normalizeSignalReactionRecipient };
@@ -1,2 +1 @@
1
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./.setup/doctor-contract-api-BIdVW0PF.mjs";
2
- export { legacyConfigRules, normalizeCompatibilityConfig };
1
+ export { t as legacyConfigRules, n as normalizeCompatibilityConfig } from "./.setup/doctor-contract-api-CcLwIhsD.mjs";
@@ -1,2 +1 @@
1
- import { r as setSignalRuntime } from "./.setup/runtime-8sxUF217.mjs";
2
- export { setSignalRuntime };
1
+ export { r as setSignalRuntime } from "./.setup/runtime-8sxUF217.mjs";
@@ -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/api-CbN3mye4.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/api-DljZsfUn.mjs", import.meta.url).href),
8
8
  exportName: "signalSetupPlugin"
9
9
  }
10
10
  });
@@ -2,6 +2,100 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.26.13 - 2026-10-24
6
+
7
+ **Highlights:** Reliable file publication on NFS mounts.
8
+
9
+ ### Fixes
10
+
11
+ - **Guest creation on NFS:** close the synced payload before publication so deferred unlink cannot report `ESTALE` after creating the destination. Thanks @bsteiner98 for [openclaw/fs-safe#1024](https://github.com/openclaw/fs-safe/issues/1024). ([#1033](https://github.com/openclaw/fs-safe/pull/1033))
12
+ - **NFS publication:** retain the destination descriptor while settling staged creates, copies, and no-replace file publication, preventing open temporary names from causing false post-publication failures while retaining exact identity and link-count checks. Thanks @bsteiner98 for [openclaw/fs-safe#1024](https://github.com/openclaw/fs-safe/issues/1024). ([#1034](https://github.com/openclaw/fs-safe/pull/1034))
13
+
14
+ ## 0.26.12 - 2026-10-23
15
+
16
+ **Highlights:** Keep durable native writes responsive by synchronizing staged files and publication directories asynchronously.
17
+
18
+ ### Fixes
19
+
20
+ - **Async native writes:** synchronize staged files and publication directories asynchronously, retaining durability and descriptor ownership without blocking the JavaScript thread.
21
+
22
+ ## 0.26.11 - 2026-10-23
23
+
24
+ ### Highlights
25
+
26
+ - **macOS root paths:** accept differently cased root paths on case-insensitive filesystems and return consistent on-disk spelling. ([#1022](https://github.com/openclaw/fs-safe/pull/1022), [#1025](https://github.com/openclaw/fs-safe/pull/1025))
27
+ - **Windows volume-root ACLs:** expose verified local volume-root facts so consumers can distinguish ineffective DELETE-self grants from replaceable directory permissions. ([#1023](https://github.com/openclaw/fs-safe/pull/1023))
28
+
29
+ ### Fixes
30
+
31
+ - **macOS guarded reads:** accept differently cased root paths on case-insensitive filesystems by binding native root spelling to the same directory identity. Thanks @0xtc23 for [openclaw#181107](https://github.com/openclaw/openclaw/issues/181107). ([#1022](https://github.com/openclaw/fs-safe/pull/1022))
32
+ - **macOS realpath spelling:** `safeRealpathSync` returns the on-disk spelling on POSIX, so differently cased paths resolve to the same identity that guarded opens use instead of looking like root escapes. Thanks @obviyus. ([#1025](https://github.com/openclaw/fs-safe/pull/1025))
33
+ - **Windows ACL facts:** identify OS-verified local volume roots so consumers can distinguish ineffective DELETE-self grants from replaceable directory permissions, while retaining all ACL rights and fail-closed unknown/remote results. Thanks @WG-Mojo for [openclaw#180892](https://github.com/openclaw/openclaw/issues/180892). ([#1023](https://github.com/openclaw/fs-safe/pull/1023))
34
+
35
+ ## 0.26.10 - 2026-10-21
36
+
37
+ ### Fixes
38
+
39
+ - **Read-only filesystems:** include the failed path in native file, stage, and sidecar creation `EROFS` diagnostics, with operation and flags for beneath opens, while preserving existing error codes. Thanks @eliasbogordos for [openclaw#178322](https://github.com/openclaw/openclaw/issues/178322).
40
+
41
+ ### Packaging
42
+
43
+ - **macOS addons:** Developer ID sign both architectures with the OpenClaw Foundation identity and hardened runtime, and require Apple notarization before packaging.
44
+
45
+ ## 0.26.9 - 2026-10-20
46
+
47
+ **Highlights:** Recover macOS watchers when FSEvents streams are exhausted, improve watcher error handling, and verify published package completeness.
48
+
49
+ ### Fixes
50
+
51
+ - **macOS watch:** fall back to guarded polling when FSEvents cannot create or start a stream during initial `auto` admission, including system-wide stream exhaustion. Explicit event and required-native modes still fail. Thanks @LGx0. ([#1005](https://github.com/openclaw/fs-safe/issues/1005))
52
+
53
+ - **Watch errors:** consistently report `path-mismatch` if a Root disappears during identity revalidation, without adopting a replacement directory.
54
+
55
+ - **Windows watch:** avoid spurious overflow notifications when an unrelated directory disappears during path validation, while retaining failures for persistent access errors and selected-path changes.
56
+
57
+ ### Packaging
58
+
59
+ - **macOS addons:** ad-hoc sign both native architectures and verify their signatures before packaging, so x64 and ARM64 addons consistently pass strict code-signature checks.
60
+
61
+ - **Package completeness:** verify literal relative runtime imports, module-relative worker URLs, and assets against the actual npm tarball during package checks and release collection, catching files omitted by the package allowlist. Thanks @vincentkoc. ([#1008](https://github.com/openclaw/fs-safe/pull/1008))
62
+
63
+ ## 0.26.8 - 2026-10-18
64
+
65
+ ### Highlights
66
+
67
+ - **Watcher reconciliation:** measured Linux polling on a 20,313-directory tree uses about 10× less idle CPU and 29× fewer filesystem syscalls, with worst event-loop stall reduced from 151 ms to about 6 ms. ([#1002](https://github.com/openclaw/fs-safe/pull/1002))
68
+
69
+ ### Performance
70
+
71
+ - **Watcher reconciliation:** reuse unchanged guarded watcher listings between full safety-net reconciliations, with guarded filesystem work in a shared Node worker thread, cheap per-entry metadata checks and full rescans on uncertainty. Ticks reconcile incrementally; full guarded reconciliation runs every five minutes by default, configurable with `fullReconcileIntervalMs`. Thanks @martinderm for [openclaw#174341](https://github.com/openclaw/openclaw/issues/174341). ([#1002](https://github.com/openclaw/fs-safe/pull/1002))
72
+
73
+ ## 0.26.7 - 2026-10-17
74
+
75
+ ### Highlights
76
+
77
+ - **Windows idle watch CPU:** share native identity and physical-path queries while retaining directory checks. ([#1000](https://github.com/openclaw/fs-safe/pull/1000))
78
+ - **Root containment:** preserve kernel parent semantics through nested and dangling symlink chains, and reject redirected sibling-output parents. ([#993](https://github.com/openclaw/fs-safe/pull/993))
79
+ - **Legacy native policy migration:** removed Python-mode settings now fail with an actionable migration error when no explicit native policy is selected. ([#995](https://github.com/openclaw/fs-safe/pull/995))
80
+
81
+ ### Fixes
82
+
83
+ - **Windows watch scan CPU:** share native identity and physical-path queries while retaining every listed entry's directory checks. Thanks @martinderm for reporting the idle Gateway CPU regression in [openclaw#174341](https://github.com/openclaw/openclaw/issues/174341). ([#1000](https://github.com/openclaw/fs-safe/pull/1000))
84
+ - **ZIP integrity:** update the native ZIP decoder while preserving format-error and byte-limit classifications for entries that exceed their declared size. ([#997](https://github.com/openclaw/fs-safe/pull/997))
85
+ - **Release dry runs:** keep generated unsigned package artifacts outside source-provenance checks so the final package smoke test can complete without signing or publishing. ([#996](https://github.com/openclaw/fs-safe/pull/996))
86
+ - **Root containment:** resolve nested and dangling symlink chains with kernel parent semantics, and reject redirected sibling-output parents before invoking external writers. Thanks @yetval for [#987](https://github.com/openclaw/fs-safe/issues/987) and [#986](https://github.com/openclaw/fs-safe/issues/986). ([#993](https://github.com/openclaw/fs-safe/pull/993))
87
+ - **Watch invalidations:** refresh the admitted parent after transient create/delete activity so consumer caches cannot retain a file absent from both watcher scans. ([#993](https://github.com/openclaw/fs-safe/pull/993))
88
+
89
+ ### Compatibility
90
+
91
+ - **Legacy native policy migration:** reject removed `FS_SAFE_PYTHON_MODE` and `OPENCLAW_FS_SAFE_PYTHON_MODE` settings with an actionable migration error when no explicit native policy is selected, instead of silently changing `require` or `off` to `auto`. Set the corresponding native-mode variable to the same value before upgrading. ([#995](https://github.com/openclaw/fs-safe/pull/995))
92
+
93
+ ## 0.26.6 - 2026-10-15
94
+
95
+ ### Fixes
96
+
97
+ - **Btrfs file cloning:** explicit `clone: "auto"` and `"always"` copies match NOCOW on their new empty destination so different source and destination-directory policies do not force a full byte copy. Sources and unrelated destination flags are preserved; automatic fallback recreates the destination with its ordinary inherited policy.
98
+
5
99
  ## 0.26.5 - 2026-10-14
6
100
 
7
101
  ### Highlights
@@ -19,5 +19,5 @@ export declare function assertCopySourceCurrent(source: {
19
19
  realPath: string;
20
20
  }, identity: BigIntStats): void;
21
21
  export declare function writeCopyFileToFd(fd: number, input: CopyFileInput, maxBytes?: number, assertBeforeMutation?: () => void): Promise<void>;
22
- export declare function createNativeCopyFile(native: NativeBinding, input: CopyFileInput, parentFd: number, basename: string, maxBytes: number | undefined): Promise<NativeFileCopyResult | undefined>;
22
+ export declare function createNativeCopyFile(native: NativeBinding, input: CopyFileInput, parentFd: number, basename: string, maxBytes: number | undefined, targetPath: string): Promise<NativeFileCopyResult | undefined>;
23
23
  export declare function assertNativeCopyCompleted(input: CopyFileInput, copied?: NativeFileCopyResult): void;
@@ -5,6 +5,7 @@ import { getNativeBinding, selectNativeBinding } from "./native.js";
5
5
  import { captureNativeFdClose } from "./native-binding.js";
6
6
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
7
7
  import { transferFileHandle } from "./file-handle-transfer.js";
8
+ import { annotateReadOnlyOpenError } from "./file-open-error.js";
8
9
  export function resolveFileCopyCloneMode(mode) {
9
10
  const clone = resolveCopyCloneMode(mode, "never");
10
11
  if (clone === "always" && !getNativeBinding()?.copyFileExclusive) {
@@ -27,7 +28,7 @@ export async function writeCopyFileToFd(fd, input, maxBytes, assertBeforeMutatio
27
28
  maxBytes, sizeHint: input.size, signal: input.signal, assertBeforeMutation,
28
29
  });
29
30
  }
30
- export async function createNativeCopyFile(native, input, parentFd, basename, maxBytes) {
31
+ export async function createNativeCopyFile(native, input, parentFd, basename, maxBytes, targetPath) {
31
32
  input.signal?.throwIfAborted();
32
33
  const copier = selectNativeBinding(native, "copyFileExclusive");
33
34
  if (!copier) {
@@ -43,6 +44,7 @@ export async function createNativeCopyFile(native, input, parentFd, basename, ma
43
44
  return await copier.copyFileExclusive(input.handle.fd, parentFd, basename, input.clone, maxBytes !== undefined && Number.isFinite(maxBytes) ? maxBytes : undefined, nativeSignal);
44
45
  }
45
46
  catch (error) {
47
+ annotateReadOnlyOpenError(error, targetPath);
46
48
  const code = error?.code;
47
49
  if (code === "Cancelled" || code === "ABORT_ERR")
48
50
  input.signal?.throwIfAborted();
@@ -10,7 +10,7 @@ function admitUncachedCopyPath(input, create = false, admittedRoot) {
10
10
  if (!path.isAbsolute(input.rootPath) || !path.isAbsolute(input.absolutePath)) {
11
11
  throw new FsSafeError("invalid-path", "copy roots and paths must be absolute");
12
12
  }
13
- const root = admittedRoot ?? captureDirectoryGuard(realpathSync(input.rootPath), "native", { bigint: true });
13
+ const root = admittedRoot ?? captureDirectoryGuard(realpathSync.native(input.rootPath), "native", { bigint: true });
14
14
  // Canonical POSIX callers already own a freshly checked root. Keep the
15
15
  // resolver's lexical/symlink walk, but do not rediscover that root each time.
16
16
  const rootCanonicalPath = admittedRoot?.realPath;
@@ -75,7 +75,7 @@ export function createCopyPathAdmissionCache() {
75
75
  }
76
76
  let root = roots.get(input.rootPath);
77
77
  if (!root) {
78
- const canonical = realpathSync(input.rootPath);
78
+ const canonical = realpathSync.native(input.rootPath);
79
79
  if (canonical !== input.rootPath)
80
80
  return admitUncachedCopyPath(input, create);
81
81
  root = captureDirectoryGuard(canonical, "native", { bigint: true });
@@ -10,6 +10,7 @@ import { creationAdmissionFromParent, removeRecordedCreationFileSync } from "./c
10
10
  import { FsSafeError } from "./errors.js";
11
11
  import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
12
12
  import { copyFileDescriptorSync } from "./file-handle-transfer.js";
13
+ import { annotateReadOnlyOpenError } from "./file-open-error.js";
13
14
  import { captureNativeFdClose } from "./native-binding.js";
14
15
  import { getNativeBinding } from "./native.js";
15
16
  import { openRootFileSync } from "./root-file.js";
@@ -114,7 +115,13 @@ function copyRootFileWithAdmissionSync(options, cache) {
114
115
  parentOpen = true;
115
116
  inspectFileIdentitySync(() => fs.fstatSync(parent.fd, { bigint: true }), { dev: BigInt(target.parent.stat.dev), ino: BigInt(target.parent.stat.ino) });
116
117
  target.assertCurrent();
117
- copied = nativeCopy(opened.fd, parent.fd, path.basename(target.path), clone, maxBytes !== undefined && Number.isFinite(maxBytes) ? maxBytes : undefined);
118
+ try {
119
+ copied = nativeCopy(opened.fd, parent.fd, path.basename(target.path), clone, maxBytes !== undefined && Number.isFinite(maxBytes) ? maxBytes : undefined);
120
+ }
121
+ catch (error) {
122
+ annotateReadOnlyOpenError(error, target.path);
123
+ throw error;
124
+ }
118
125
  targetOwner = ownFileDescriptorSync(copied.fd, closeNative);
119
126
  }
120
127
  else {
@@ -0,0 +1,2 @@
1
+ /** Enrich only an attempted open whose absolute pathname is known by its owner. */
2
+ export declare function annotateReadOnlyOpenError(error: unknown, filePath: string): void;
@@ -0,0 +1,14 @@
1
+ import { formatErrorDetail } from "./error-detail.js";
2
+ /** Enrich only an attempted open whose absolute pathname is known by its owner. */
3
+ export function annotateReadOnlyOpenError(error, filePath) {
4
+ if (!(error instanceof Error) || error.code !== "EROFS")
5
+ return;
6
+ // Keep the original error, code and errno, even if a caller froze its diagnostics.
7
+ try {
8
+ const failure = error;
9
+ failure.path = filePath;
10
+ failure.syscall = "open";
11
+ failure.message = `EROFS: read-only filesystem, open '${formatErrorDetail(filePath)}'`;
12
+ }
13
+ catch { /* Diagnostics must not replace the I/O failure. */ }
14
+ }
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
2
  import type { FileHandle } from "node:fs/promises";
3
- export declare function syncFileBestEffort(handle: Pick<FileHandle, "sync">): Promise<void>;
3
+ export declare function syncFile(handle: Pick<FileHandle, "sync"> | number): Promise<void>;
4
+ export declare function syncFileBestEffort(handle: Pick<FileHandle, "sync"> | number): Promise<void>;
4
5
  export declare function syncFileBestEffortSync(fd: number, fsModule?: Pick<typeof fs, "fsyncSync">): void;
@@ -1,7 +1,12 @@
1
1
  import fs from "node:fs";
2
+ export function syncFile(handle) {
3
+ return typeof handle === "number"
4
+ ? new Promise((resolve, reject) => fs.fsync(handle, error => error ? reject(error) : resolve()))
5
+ : handle.sync();
6
+ }
2
7
  export async function syncFileBestEffort(handle) {
3
8
  try {
4
- await handle.sync();
9
+ await syncFile(handle);
5
10
  }
6
11
  catch (error) {
7
12
  if (error?.code !== "EPERM")
@@ -138,6 +138,9 @@ export const GUEST_FILESYSTEM_CREATE_EXCLUSIVE_PYTHON = [
138
138
  " write_all(temp_fd, chunk)",
139
139
  " # exclusive create payload is durable before publication",
140
140
  " os.fsync(temp_fd)",
141
+ " # NFS defers unlink of an open payload, retaining a second link.",
142
+ " closing_fd, temp_fd = temp_fd, None",
143
+ " os.close(closing_fd)",
141
144
  " # Publish with no-replace rename or the checked file fallback.",
142
145
  " rename_no_replace(staging_fd, temp_name, parent_fd, basename)",
143
146
  " target_stat = os.lstat(basename, dir_fd=parent_fd)",
@@ -113,6 +113,7 @@ export interface NativeWindowsSecurityFacts {
113
113
  fallbackRequired: boolean;
114
114
  daclPresent: boolean;
115
115
  isLocal: boolean;
116
+ isVolumeRoot?: boolean;
116
117
  aceListComplete: boolean;
117
118
  unsupportedAceTypes: number[];
118
119
  aces: NativeWindowsAccessControlEntry[];
@@ -18,12 +18,23 @@ function parseMode(value) {
18
18
  export function configureFsSafeNative(config) {
19
19
  overrideConfig = { ...overrideConfig, ...config };
20
20
  }
21
+ function rejectLegacyMode() {
22
+ for (const source of ["FS_SAFE_PYTHON_MODE", "OPENCLAW_FS_SAFE_PYTHON_MODE"]) {
23
+ if (process.env[source] === undefined)
24
+ continue;
25
+ const mode = parseMode(process.env[source]) ?? "auto";
26
+ throw new Error(`${source} has been removed. Set FS_SAFE_NATIVE_MODE=${mode} or ` +
27
+ `configureFsSafeNative({ mode: "${mode}" }) before using fs-safe, then remove ${source}. ` +
28
+ "Legacy mode settings no longer select native policy.");
29
+ }
30
+ return "auto";
31
+ }
21
32
  export function getFsSafeNativeConfig() {
22
33
  return {
23
34
  mode: overrideConfig.mode ??
24
35
  parseMode(process.env.FS_SAFE_NATIVE_MODE) ??
25
36
  parseMode(process.env.OPENCLAW_FS_SAFE_NATIVE_MODE) ??
26
- "auto",
37
+ rejectLegacyMode(),
27
38
  };
28
39
  }
29
40
  /** Internal provenance; keep the public configuration shape unchanged. */
@@ -35,13 +46,13 @@ export function getNativeModeSetting() {
35
46
  if (mode !== undefined)
36
47
  return { mode, source };
37
48
  }
38
- return { mode: "auto", source: "default" };
49
+ return { mode: rejectLegacyMode(), source: "default" };
39
50
  }
40
51
  export function isFsSafeNativeRequired() {
41
52
  return (overrideConfig.mode ??
42
53
  parseMode(process.env.FS_SAFE_NATIVE_MODE) ??
43
54
  parseMode(process.env.OPENCLAW_FS_SAFE_NATIVE_MODE) ??
44
- "auto") === "require";
55
+ rejectLegacyMode()) === "require";
45
56
  }
46
57
  export function __resetFsSafeNativeConfigForTest() {
47
58
  overrideConfig = {};
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { sameFileIdentityForCleanup } from "./file-identity.js";
4
4
  import { getNativeBinding } from "./native.js";
5
5
  import { captureNativeFdClose } from "./native-binding.js";
6
+ import { annotateReadOnlyOpenError } from "./file-open-error.js";
6
7
  export function nativeOpenFlags(flags) {
7
8
  const closeOnExec = fsSync.constants.O_CLOEXEC;
8
9
  return (flags |
@@ -55,6 +56,7 @@ export async function createNativeExclusiveFile(targetPath, mode) {
55
56
  if (process.platform === "win32" && openError.code === "EPERM") {
56
57
  openError.path = targetPath;
57
58
  }
59
+ annotateReadOnlyOpenError(error, targetPath);
58
60
  throw error;
59
61
  }
60
62
  fd = opened.fd;