@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,26 +1,22 @@
1
- import { o as __exportAll } from "./doctor-contract-api-BIdVW0PF.mjs";
2
- import { i as normalizeSignalTransportHost } from "./transport-url-CAC0JcuI.mjs";
3
- import { i as resolveSignalAccount, o as resolveSignalReplyToMode } from "./accounts-B0Pba1-q.mjs";
4
- import { n as normalizeSignalMessagingTarget } from "./normalize-D7kSomXt.mjs";
5
- 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 "./identity-B3JyPqGO.mjs";
6
- import { _ as materializeSignalPresentationFallback, a as formatSignalDaemonDroppedEnvNotice, b as resolveSignalReactionLevel, g as registerSignalReplyContext, i as buildSignalDaemonEnv, o as listSignalDaemonDroppedEnvHints, w as isSignalNativeApprovalHandlerConfigured, x as formatSignalMediaText } from "./channel-CM-rLLPN.mjs";
7
- import { a as prepareSignalSocketPath, t as SignalSseRejectionError } from "./client-Duwqbgqi.mjs";
8
- import { n as signalRpcRequest, r as streamSignalEvents, t as signalCheck } from "./client-adapter-Chnbch_z.mjs";
9
- import { n as sendReactionSignal } from "./reaction-runtime-api-mdO4k0yv.mjs";
1
+ import { o as __exportAll } from "./doctor-contract-api-CcLwIhsD.mjs";
2
+ import { o as normalizeSignalTransportHost } from "./legacy-transport-D_cZC3bS.mjs";
3
+ import { i as resolveSignalAccount, o as resolveSignalReplyToMode } from "./accounts-enJKL3ci.mjs";
4
+ 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 } from "./identity-vKtR3DrM.mjs";
5
+ import { A as isSignalNativeApprovalHandlerConfigured, C as signalCheck, D as formatSignalMediaText, E as resolveSignalReactionLevel, T as streamSignalEvents, _ as materializeSignalPresentationFallback, a as formatSignalDaemonDroppedEnvNotice, g as registerSignalReplyContext, i as buildSignalDaemonEnv, o as listSignalDaemonDroppedEnvHints, w as signalRpcRequest, x as sendReactionSignal } from "./channel-ByJj8fSh.mjs";
6
+ import { a as prepareSignalSocketPath, t as SignalSseRejectionError } from "./client-CesmEjRc.mjs";
10
7
  import { n as getSignalRuntime, t as getOptionalSignalRuntime } from "./runtime-8sxUF217.mjs";
11
- import { i as maybeResolveSignalApprovalReaction, s as resolveSignalApprovalConversationKey, t as addSignalApprovalReactionHintToStructuredPayload } from "./approval-reactions-JsqCEOHK.mjs";
12
- import { n as registerSignalReactionTargetsForDeliveredPayload, r as maybeResolveSignalQuestionReaction } from "./reaction-targets-BgPLDHBf.mjs";
13
- import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-CM7-016e.mjs";
8
+ import { i as maybeResolveSignalApprovalReaction, o as registerSignalApprovalReactionTargetForDeliveredPayload, s as resolveSignalApprovalConversationKey, t as addSignalApprovalReactionHintToStructuredPayload } from "./approval-reactions-CvqVSSx8.mjs";
9
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-BJuOWLky.mjs";
14
10
  import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
15
11
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
16
12
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
17
13
  import { asPositiveSafeInteger, isRecord, normalizeNullableString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
18
14
  import path from "node:path";
19
15
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
20
- import { normalizeE164, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
16
+ import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
21
17
  import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, logInboundDrop, matchesMentionPatterns, readAgentRunTerminalOutcome, resolveInboundDebounceMs, resolveInboundMentionDecision, resolveInboundSessionEnvelopeContextAsync, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
22
18
  import { canonicalizeBase64, detectMime, estimateBase64DecodedBytes, kindFromMime, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
23
- import { resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
19
+ import { asPositiveFiniteNumber, resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
24
20
  import { ensurePortAvailable, extractErrorCode, formatErrorMessage, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
25
21
  import os from "node:os";
26
22
  import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention, resolveChannelGroups, resolveChannelGroupsConfigPath } from "openclaw/plugin-sdk/channel-policy";
@@ -28,7 +24,7 @@ import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runti
28
24
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
29
25
  import { chunkTextWithMode, createReplyReferencePlanner, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
30
26
  import { captureRuntimeConfigAsyncReader, createRuntimeConfigReader } from "openclaw/plugin-sdk/runtime-config-snapshot";
31
- import { computeBackoff, createNonExitingRuntime, createSubsystemLogger, danger, logVerbose, shouldLogVerbose, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
27
+ import { computeBackoff, createNonExitingRuntime, createSubsystemLogger, danger, logVerbose, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
32
28
  import { resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
33
29
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
34
30
  import { createInterface } from "node:readline";
@@ -106,14 +102,11 @@ async function waitForSignalDaemonReady(params) {
106
102
  }
107
103
  });
108
104
  }
109
- function isRecoverableSignalCliReceiveException(line) {
110
- return /\breceive exception:\s+.*\binvalid PreKey message:\s+decryption failed\b/i.test(line);
111
- }
112
105
  function classifySignalCliLogLine(line) {
113
106
  const trimmed = line.trim();
114
107
  if (!trimmed) return null;
115
108
  if (/\bERROR\b/.test(trimmed)) return "error";
116
- if (isRecoverableSignalCliReceiveException(trimmed)) return "log";
109
+ if (/\breceive exception:\s+.*\binvalid PreKey message:\s+decryption failed\b/i.test(trimmed)) return "log";
117
110
  if (/\b(FAILED|SEVERE|EXCEPTION)\b/i.test(trimmed)) return "error";
118
111
  return "log";
119
112
  }
@@ -131,7 +124,7 @@ function resolveSignalCliConfigPath(raw) {
131
124
  if (value.startsWith("~/") || value.startsWith("~\\")) return path.join(os.homedir(), value.slice(2));
132
125
  return value;
133
126
  }
134
- function buildDaemonArgs(opts) {
127
+ function spawnSignalDaemon(opts) {
135
128
  const args = [];
136
129
  if (opts.configPath?.trim()) args.push("--config", resolveSignalCliConfigPath(opts.configPath));
137
130
  if (opts.account) args.push("-a", opts.account);
@@ -144,10 +137,6 @@ function buildDaemonArgs(opts) {
144
137
  if (opts.ignoreAttachments) args.push("--ignore-attachments");
145
138
  if (opts.ignoreStories) args.push("--ignore-stories");
146
139
  if (opts.sendReadReceipts) args.push("--send-read-receipts");
147
- return args;
148
- }
149
- function spawnSignalDaemon(opts) {
150
- const args = buildDaemonArgs(opts);
151
140
  warnSignalDaemonDroppedEnvOnce();
152
141
  const child = spawnOomScoreAdjusted(opts.cliPath, args, {
153
142
  env: buildSignalDaemonEnv(),
@@ -235,35 +224,6 @@ function spawnSignalDaemon(opts) {
235
224
  };
236
225
  }
237
226
  //#endregion
238
- //#region extensions/signal/src/daemon-lifecycle.ts
239
- function createSignalDaemonLifecycle(params) {
240
- let daemonHandle = null;
241
- let daemonStopPromise;
242
- let daemonExitError;
243
- const daemonAbortController = new AbortController();
244
- const abortSignal = params.abortSignal ? AbortSignal.any([params.abortSignal, daemonAbortController.signal]) : daemonAbortController.signal;
245
- const stop = () => {
246
- if (daemonStopPromise) return daemonStopPromise;
247
- daemonAbortController.abort(params.abortSignal?.reason ?? /* @__PURE__ */ new Error("Signal monitor stopped"));
248
- daemonStopPromise = daemonHandle?.stop() ?? Promise.resolve();
249
- return daemonStopPromise;
250
- };
251
- const attach = (handle) => {
252
- daemonHandle = handle;
253
- handle.exited.then((exit) => {
254
- if (abortSignal.aborted) return;
255
- daemonExitError = new Error(formatSignalDaemonExit(exit));
256
- daemonAbortController.abort(daemonExitError);
257
- });
258
- };
259
- return {
260
- attach,
261
- stop,
262
- getExitError: () => daemonExitError,
263
- abortSignal
264
- };
265
- }
266
- //#endregion
267
227
  //#region extensions/signal/src/native-reply.ts
268
228
  function resolveSignalNativeReplyId(params) {
269
229
  if (params.payload.replyToCurrent === false) return;
@@ -471,41 +431,26 @@ function resolveSignalControlLaneKey(accountId, entry) {
471
431
  return conversationId ? `signal:${accountId}:${conversationId}:control` : null;
472
432
  }
473
433
  function createSignalPendingInboundRegistry(accountId) {
474
- const trackedEntries = /* @__PURE__ */ new WeakMap();
475
- const countsByConversation = /* @__PURE__ */ new Map();
434
+ const trackedEntries = /* @__PURE__ */ new Map();
476
435
  const track = (entry) => {
477
- if (trackedEntries.has(entry)) return;
478
436
  const conversationKey = resolveSignalInboundConversationKey(accountId, entry);
479
437
  const inboundKey = resolveSignalInboundDebounceKey(accountId, entry);
480
438
  if (!conversationKey || !inboundKey) return;
481
- const counts = countsByConversation.get(conversationKey) ?? /* @__PURE__ */ new Map();
482
- counts.set(inboundKey, (counts.get(inboundKey) ?? 0) + 1);
483
- countsByConversation.set(conversationKey, counts);
484
439
  trackedEntries.set(entry, {
485
440
  conversationKey,
486
441
  inboundKey
487
442
  });
488
443
  };
489
444
  const complete = (entries) => {
490
- for (const entry of entries) {
491
- const tracked = trackedEntries.get(entry);
492
- if (!tracked) continue;
493
- trackedEntries.delete(entry);
494
- const counts = countsByConversation.get(tracked.conversationKey);
495
- const nextCount = (counts?.get(tracked.inboundKey) ?? 0) - 1;
496
- if (nextCount > 0) {
497
- counts?.set(tracked.inboundKey, nextCount);
498
- continue;
499
- }
500
- counts?.delete(tracked.inboundKey);
501
- if (counts?.size === 0) countsByConversation.delete(tracked.conversationKey);
502
- }
445
+ for (const entry of entries) trackedEntries.delete(entry);
503
446
  };
504
447
  const cancelPendingOnAbort = (entry, cancelKey) => {
505
448
  if (!entry.commandAuthorized || !isAbortRequestText(entry.commandBody)) return;
506
449
  const conversationKey = resolveSignalInboundConversationKey(accountId, entry);
507
450
  if (!conversationKey) return;
508
- for (const inboundKey of countsByConversation.get(conversationKey)?.keys() ?? []) cancelKey(inboundKey);
451
+ const inboundKeys = /* @__PURE__ */ new Set();
452
+ for (const tracked of trackedEntries.values()) if (tracked.conversationKey === conversationKey) inboundKeys.add(tracked.inboundKey);
453
+ for (const inboundKey of inboundKeys) cancelKey(inboundKey);
509
454
  };
510
455
  return {
511
456
  track,
@@ -606,9 +551,8 @@ function renderSignalMentions(message, mentions) {
606
551
  //#endregion
607
552
  //#region extensions/signal/src/monitor/reactions.ts
608
553
  function resolveSignalStatusReactionTimestamp(params) {
609
- if (typeof params.timestamp === "number") return Number.isFinite(params.timestamp) && params.timestamp > 0 ? params.timestamp : null;
610
- const parsed = Number(params.messageId);
611
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
554
+ const value = typeof params.timestamp === "number" ? params.timestamp : Number(params.messageId);
555
+ return asPositiveFiniteNumber(value) ?? null;
612
556
  }
613
557
  function resolveSignalReactionTargets(reaction) {
614
558
  const targets = [];
@@ -710,7 +654,7 @@ function createSignalEventHandler(deps) {
710
654
  agentId: route.agentId,
711
655
  sessionKey: route.sessionKey
712
656
  });
713
- const body = formatInboundEnvelope({
657
+ let combinedBody = formatInboundEnvelope({
714
658
  channel: "Signal",
715
659
  from: fromLabel,
716
660
  timestamp: entry.timestamp ?? void 0,
@@ -723,7 +667,6 @@ function createSignalEventHandler(deps) {
723
667
  previousTimestamp,
724
668
  envelope: envelopeOptions
725
669
  });
726
- let combinedBody = body;
727
670
  const historyKey = entry.isGroup ? entry.groupId ?? "unknown" : void 0;
728
671
  if (entry.isGroup && historyKey) combinedBody = createChannelHistoryWindow({ historyMap: deps.groupHistories }).buildPendingContext({
729
672
  historyKey,
@@ -803,10 +746,6 @@ function createSignalEventHandler(deps) {
803
746
  ReplyThreading: replyThreading
804
747
  }
805
748
  });
806
- if (shouldLogVerbose()) {
807
- const preview = truncateUtf16Safe(body, 200).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
808
- logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
809
- }
810
749
  const statusReactionTimestamp = resolveSignalStatusReactionTimestamp(entry);
811
750
  const statusReactionsConfig = cfg.messages?.statusReactions;
812
751
  const signalReactionLevel = resolveSignalReactionLevel({
@@ -854,21 +793,26 @@ function createSignalEventHandler(deps) {
854
793
  }
855
794
  }) : null;
856
795
  if (statusReactionController) statusReactionController.setQueued();
796
+ const sendTyping = async (stop) => {
797
+ if (ctxPayload.To) {
798
+ const { baseUrl, account, accountId } = deps;
799
+ await sendTypingSignal(ctxPayload.To, {
800
+ cfg,
801
+ baseUrl,
802
+ account,
803
+ accountId,
804
+ stop
805
+ });
806
+ }
807
+ };
857
808
  const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
858
809
  cfg,
859
810
  agentId: route.agentId,
860
811
  channel: "signal",
861
812
  accountId: route.accountId,
862
813
  typing: {
863
- start: async () => {
864
- if (!ctxPayload.To) return;
865
- await sendTypingSignal(ctxPayload.To, {
866
- cfg,
867
- baseUrl: deps.baseUrl,
868
- account: deps.account,
869
- accountId: deps.accountId
870
- });
871
- },
814
+ start: () => sendTyping(),
815
+ stop: () => sendTyping(true),
872
816
  onStartError: (err) => {
873
817
  logTypingFailure({
874
818
  log: logVerbose,
@@ -1089,7 +1033,7 @@ function createSignalEventHandler(deps) {
1089
1033
  ...last.messageId ? { messageId: last.messageId } : {},
1090
1034
  inboundEventKind: "user_request"
1091
1035
  };
1092
- return await Promise.all(entries.flatMap((entry) => entry.resolveChannelIngress ? [entry.resolveChannelIngress(contextBinding)] : (entry.channelIngress ?? []).map(async (ingress) => ingress)));
1036
+ return await Promise.all(entries.map((entry) => entry.resolveChannelIngress(contextBinding)));
1093
1037
  }
1094
1038
  const flushDebouncedSignalInboundEntries = (entries, createFlush) => {
1095
1039
  const { lifecycle, settle } = fanInChannelIngressLifecycles(entries.map((entry) => entry.turnAdoptionLifecycle));
@@ -1148,95 +1092,17 @@ function createSignalEventHandler(deps) {
1148
1092
  onFlush: (entries, createFlush) => trackSignalInboundFlush(flushDebouncedSignalInboundEntries(entries, createFlush)),
1149
1093
  onError: reportSignalInboundFlushError
1150
1094
  });
1151
- async function handleReactionOnlyInbound(params) {
1152
- if (params.hasBodyContent) return false;
1153
- if (params.reaction.isRemove) return true;
1154
- const emojiLabel = normalizeOptionalString(params.reaction.emoji) ?? "emoji";
1155
- const senderName = params.envelope.sourceName ?? params.senderDisplay;
1156
- logVerbose(`signal reaction: ${emojiLabel} from ${senderName}`);
1157
- const groupId = params.reaction.groupInfo?.groupId ?? void 0;
1158
- const groupName = params.reaction.groupInfo?.groupName ?? void 0;
1159
- const isGroup = Boolean(groupId);
1160
- const messageId = params.reaction.targetSentTimestamp ? String(params.reaction.targetSentTimestamp) : "unknown";
1161
- const conversationKey = resolveSignalApprovalConversationKey(groupId ? `group:${groupId}` : `signal:${resolveSignalRecipient(params.sender)}`);
1162
- if (conversationKey && await maybeResolveSignalApprovalReaction({
1163
- cfg: params.cfg,
1164
- accountId: deps.accountId,
1165
- conversationKey,
1166
- messageId,
1167
- reactionKey: emojiLabel,
1168
- actorId: formatSignalSenderId(params.sender),
1169
- targetAuthor: params.reaction.targetAuthor,
1170
- targetAuthorUuid: params.reaction.targetAuthorUuid,
1171
- logVerboseMessage: logVerbose
1172
- })) return true;
1173
- if (params.accessDecision.decision !== "allow") {
1174
- logVerbose(`Blocked signal reaction sender ${params.senderDisplay} (${params.accessDecision.reasonCode})`);
1175
- return true;
1176
- }
1177
- if (conversationKey && await maybeResolveSignalQuestionReaction({
1178
- cfg: params.cfg,
1179
- accountId: deps.accountId,
1180
- conversationKey,
1181
- messageId,
1182
- reactionKey: emojiLabel,
1183
- isRemove: Boolean(params.reaction.isRemove),
1184
- actorId: formatSignalSenderId(params.sender),
1185
- targetAuthor: params.reaction.targetAuthor,
1186
- targetAuthorUuid: params.reaction.targetAuthorUuid,
1187
- logDebug: logVerbose
1188
- })) return true;
1189
- const targets = resolveSignalReactionTargets(params.reaction);
1190
- if (!shouldEmitSignalReactionNotification({
1191
- mode: deps.reactionMode,
1192
- account: deps.account,
1193
- accountUuid: deps.accountUuid,
1194
- targets,
1195
- sender: params.sender,
1196
- allowlist: deps.reactionAllowlist
1197
- })) return true;
1198
- const senderPeerId = resolveSignalPeerId(params.sender);
1199
- const route = resolveSignalInboundRoute({
1200
- cfg: params.cfg,
1201
- accountId: deps.accountId,
1202
- isGroup,
1203
- groupId,
1204
- senderPeerId
1205
- });
1206
- const groupLabel = isGroup ? `${groupName ?? "Signal Group"} id:${groupId}` : void 0;
1207
- const text = buildSignalReactionSystemEventText({
1208
- emojiLabel,
1209
- actorLabel: senderName,
1210
- messageId,
1211
- targetLabel: targets[0]?.display,
1212
- groupLabel
1213
- });
1214
- const contextKey = [
1215
- "signal",
1216
- "reaction",
1217
- "added",
1218
- messageId,
1219
- formatSignalSenderId(params.sender),
1220
- emojiLabel,
1221
- groupId ?? ""
1222
- ].filter(Boolean).join(":");
1223
- enqueueSystemEvent(text, {
1224
- sessionKey: route.sessionKey,
1225
- contextKey
1226
- });
1227
- return true;
1228
- }
1229
1095
  return async (event, turnAdoptionLifecycle, preparedPayload) => {
1230
1096
  if (event.event !== "receive" || !event.data) return;
1231
1097
  const cfg = readConfig();
1232
1098
  let payload = preparedPayload ?? null;
1233
1099
  if (!preparedPayload) try {
1234
1100
  payload = JSON.parse(event.data);
1235
- } catch (err) {
1236
- deps.runtime.error?.(`failed to parse event: ${String(err)}`);
1101
+ } catch {
1102
+ deps.runtime.error?.("failed to parse event: invalid JSON");
1237
1103
  return;
1238
1104
  }
1239
- if (payload?.exception?.message) deps.runtime.error?.(`receive exception: ${payload.exception.message}`);
1105
+ if (payload?.exception?.message) deps.runtime.error?.("receive exception");
1240
1106
  const envelope = payload?.envelope;
1241
1107
  if (!envelope) return;
1242
1108
  const sender = resolveSignalSender(envelope);
@@ -1276,15 +1142,70 @@ function createSignalEventHandler(deps) {
1276
1142
  });
1277
1143
  if (quoteText && !visibleQuoteText && isGroup) logVerbose(`signal: drop quote context (mode=${contextVisibilityMode}, sender_allowed=${quoteSenderAllowed ? "yes" : "no"})`);
1278
1144
  const hasBodyContent = Boolean(messageText || visibleQuoteText) || Boolean(!reaction && dataMessage?.attachments?.length);
1279
- if (reaction && await handleReactionOnlyInbound({
1280
- cfg,
1281
- envelope,
1282
- sender,
1283
- senderDisplay,
1284
- reaction,
1285
- hasBodyContent,
1286
- accessDecision: senderAccess
1287
- })) return;
1145
+ if (reaction && !hasBodyContent) {
1146
+ if (reaction.isRemove) return;
1147
+ const emojiLabel = normalizeOptionalString(reaction.emoji) ?? "emoji";
1148
+ const reactionSenderName = envelope.sourceName ?? senderDisplay;
1149
+ const reactionGroupId = reaction.groupInfo?.groupId ?? void 0;
1150
+ const reactionGroupName = reaction.groupInfo?.groupName ?? void 0;
1151
+ const isReactionGroup = Boolean(reactionGroupId);
1152
+ const reactionMessageId = reaction.targetSentTimestamp ? String(reaction.targetSentTimestamp) : "unknown";
1153
+ const conversationKey = resolveSignalApprovalConversationKey(reactionGroupId ? `group:${reactionGroupId}` : `signal:${resolveSignalRecipient(sender)}`);
1154
+ if (conversationKey && await maybeResolveSignalApprovalReaction({
1155
+ cfg,
1156
+ accountId: deps.accountId,
1157
+ conversationKey,
1158
+ messageId: reactionMessageId,
1159
+ reactionKey: emojiLabel,
1160
+ actorId: formatSignalSenderId(sender),
1161
+ targetAuthor: reaction.targetAuthor,
1162
+ targetAuthorUuid: reaction.targetAuthorUuid,
1163
+ logVerboseMessage: logVerbose
1164
+ })) return;
1165
+ if (senderAccess.decision !== "allow") {
1166
+ logVerbose(`Blocked signal reaction sender ${senderDisplay} (${senderAccess.reasonCode})`);
1167
+ return;
1168
+ }
1169
+ const targets = resolveSignalReactionTargets(reaction);
1170
+ if (!shouldEmitSignalReactionNotification({
1171
+ mode: deps.reactionMode,
1172
+ account: deps.account,
1173
+ accountUuid: deps.accountUuid,
1174
+ targets,
1175
+ sender,
1176
+ allowlist: deps.reactionAllowlist
1177
+ })) return;
1178
+ const reactionSenderPeerId = resolveSignalPeerId(sender);
1179
+ const reactionRoute = resolveSignalInboundRoute({
1180
+ cfg,
1181
+ accountId: deps.accountId,
1182
+ isGroup: isReactionGroup,
1183
+ groupId: reactionGroupId,
1184
+ senderPeerId: reactionSenderPeerId
1185
+ });
1186
+ const groupLabel = isReactionGroup ? `${reactionGroupName ?? "Signal Group"} id:${reactionGroupId}` : void 0;
1187
+ const text = buildSignalReactionSystemEventText({
1188
+ emojiLabel,
1189
+ actorLabel: reactionSenderName,
1190
+ messageId: reactionMessageId,
1191
+ targetLabel: targets[0]?.display,
1192
+ groupLabel
1193
+ });
1194
+ const contextKey = [
1195
+ "signal",
1196
+ "reaction",
1197
+ "added",
1198
+ reactionMessageId,
1199
+ formatSignalSenderId(sender),
1200
+ emojiLabel,
1201
+ reactionGroupId ?? ""
1202
+ ].filter(Boolean).join(":");
1203
+ enqueueSystemEvent(text, {
1204
+ sessionKey: reactionRoute.sessionKey,
1205
+ contextKey
1206
+ });
1207
+ return;
1208
+ }
1288
1209
  if (!dataMessage) return;
1289
1210
  const senderRecipient = resolveSignalRecipient(sender);
1290
1211
  const senderPeerId = resolveSignalPeerId(sender);
@@ -1462,9 +1383,9 @@ function createSignalEventHandler(deps) {
1462
1383
  kind: kindFromMime(contentType) ?? "unknown"
1463
1384
  };
1464
1385
  } else unavailableAttachmentCount += 1;
1465
- } catch (err) {
1386
+ } catch {
1466
1387
  unavailableAttachmentCount += 1;
1467
- deps.runtime.error?.(danger(`attachment fetch failed: ${String(err)}`));
1388
+ deps.runtime.error?.(danger("attachment fetch failed"));
1468
1389
  }
1469
1390
  }
1470
1391
  let bodyText = messageText;
@@ -1517,7 +1438,6 @@ function createSignalEventHandler(deps) {
1517
1438
  media: mediaFacts,
1518
1439
  commandAuthorized,
1519
1440
  canDetectMention,
1520
- requireMention,
1521
1441
  wasMentioned: effectiveWasMentioned,
1522
1442
  replyToBody: visibleQuoteText || void 0,
1523
1443
  replyToSender: visibleQuoteSender,
@@ -1643,14 +1563,6 @@ const DEFAULT_RECONNECT_POLICY = {
1643
1563
  factor: 2,
1644
1564
  jitter: .2
1645
1565
  };
1646
- function isPermanentSignalSseRejection(error) {
1647
- if (!(error instanceof SignalSseRejectionError)) return false;
1648
- return error.status >= 400 && error.status < 500 && ![
1649
- 408,
1650
- 425,
1651
- 429
1652
- ].includes(error.status);
1653
- }
1654
1566
  function publishSignalRecovering(statusSink, lastError) {
1655
1567
  statusSink?.({
1656
1568
  connected: false,
@@ -1664,6 +1576,7 @@ async function runSignalSseLoop({ baseUrl, account, abortSignal, runtime, onEven
1664
1576
  ...policy
1665
1577
  };
1666
1578
  let reconnectAttempts = 0;
1579
+ let lastEventId;
1667
1580
  for (;;) {
1668
1581
  if (abortSignal?.aborted) break;
1669
1582
  let lastError;
@@ -1674,12 +1587,14 @@ async function runSignalSseLoop({ baseUrl, account, abortSignal, runtime, onEven
1674
1587
  abortSignal,
1675
1588
  timeoutMs,
1676
1589
  transportKind,
1590
+ ...lastEventId ? { lastEventId } : {},
1677
1591
  onStreamOpen: () => {
1678
1592
  statusSink?.(channelReadyPatch());
1679
1593
  },
1680
1594
  onEvent: async (event) => {
1681
1595
  reconnectAttempts = 0;
1682
1596
  await onEvent(event);
1597
+ if (event.id !== void 0) lastEventId = event.id || void 0;
1683
1598
  },
1684
1599
  logger: {
1685
1600
  log: runtime.log,
@@ -1691,7 +1606,11 @@ async function runSignalSseLoop({ baseUrl, account, abortSignal, runtime, onEven
1691
1606
  } catch (err) {
1692
1607
  if (abortSignal?.aborted) return;
1693
1608
  runtime.error?.(`Signal stream error: ${String(err)}`);
1694
- if (isPermanentSignalSseRejection(err)) {
1609
+ if (err instanceof SignalSseRejectionError && err.status >= 400 && err.status < 500 && ![
1610
+ 408,
1611
+ 425,
1612
+ 429
1613
+ ].includes(err.status)) {
1695
1614
  lastError = `Signal daemon rejected the event stream: ${err.message}. Check the configured account and daemon URL, fix the daemon or proxy response, then restart the channel.`;
1696
1615
  runtime.log?.(`Signal reconnect stopped: ${lastError}`);
1697
1616
  statusSink?.(channelBlockedPatch(lastError, { connected: false }));
@@ -1739,36 +1658,6 @@ function deriveSignalAttachmentRpcMaxResponseBytes(maxBytes) {
1739
1658
  if (!Number.isFinite(maxBytes) || maxBytes <= 0) return;
1740
1659
  return Math.ceil(maxBytes * SIGNAL_BASE64_OVERHEAD_NUMERATOR / SIGNAL_BASE64_OVERHEAD_DENOMINATOR) + SIGNAL_ATTACHMENT_RPC_RESPONSE_HEADROOM_BYTES;
1741
1660
  }
1742
- async function fetchAttachment(params) {
1743
- const { attachment } = params;
1744
- if (!attachment?.id) return null;
1745
- if (typeof attachment.size === "number" && attachment.size > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1746
- const rpcParams = { id: attachment.id };
1747
- if (params.account) rpcParams.account = params.account;
1748
- if (params.groupId) rpcParams.groupId = params.groupId;
1749
- else if (params.sender) rpcParams.recipient = params.sender;
1750
- else return null;
1751
- const result = await signalRpcRequest("getAttachment", rpcParams, {
1752
- baseUrl: params.baseUrl,
1753
- maxResponseBytes: deriveSignalAttachmentRpcMaxResponseBytes(params.maxBytes),
1754
- transportKind: params.transportKind
1755
- });
1756
- if (!result?.data) return null;
1757
- if (estimateBase64DecodedBytes(result.data) > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1758
- const canonicalData = canonicalizeBase64(result.data);
1759
- if (!canonicalData) throw new Error(`Signal attachment ${attachment.id} returned malformed base64 data`);
1760
- const buffer = Buffer.from(canonicalData, "base64");
1761
- const originalFilename = normalizeOptionalString(attachment.filename ?? void 0);
1762
- const contentType = normalizeOptionalString(attachment.contentType ?? void 0) ?? await detectMime({
1763
- buffer,
1764
- filePath: originalFilename
1765
- });
1766
- const saved = await saveMediaBuffer(buffer, contentType, "inbound", params.maxBytes, originalFilename);
1767
- return {
1768
- path: saved.path,
1769
- contentType: saved.contentType
1770
- };
1771
- }
1772
1661
  async function deliverReplies(params) {
1773
1662
  const { replies, target, baseUrl, account, accountUuid, accountId, runtime, maxBytes, textLimit, chunkMode } = params;
1774
1663
  const replyToMode = resolveSignalReplyToMode({
@@ -1826,7 +1715,7 @@ async function deliverReplies(params) {
1826
1715
  sendText: send,
1827
1716
  sendMedia: ({ mediaUrl, caption }) => send(caption ?? "", mediaUrl)
1828
1717
  }) !== "empty") {
1829
- await registerSignalReactionTargetsForDeliveredPayload({
1718
+ await registerSignalApprovalReactionTargetForDeliveredPayload({
1830
1719
  cfg: params.cfg,
1831
1720
  target: {
1832
1721
  channel: "signal",
@@ -1883,9 +1772,16 @@ async function monitorSignalProvider(opts = {}) {
1883
1772
  const autoStart = Boolean(managedTransport) && (opts.autoStart ?? true);
1884
1773
  const startupTimeoutMs = Math.min(12e4, Math.max(1e3, opts.startupTimeoutMs ?? managedTransport?.startupTimeoutMs ?? 3e4));
1885
1774
  const readReceiptsViaDaemon = autoStart && sendReadReceipts;
1886
- const daemonLifecycle = createSignalDaemonLifecycle({ abortSignal: opts.abortSignal });
1775
+ const callerAbortSignal = opts.abortSignal;
1776
+ let daemonExitError;
1777
+ const daemonAbortController = new AbortController();
1778
+ const abortSignal = callerAbortSignal ? AbortSignal.any([callerAbortSignal, daemonAbortController.signal]) : daemonAbortController.signal;
1887
1779
  const monitorTaskRunner = createSignalMonitorTaskRunner(runtime);
1888
1780
  let daemonHandle = null;
1781
+ const stopDaemon = () => {
1782
+ daemonAbortController.abort(callerAbortSignal?.reason ?? /* @__PURE__ */ new Error("Signal monitor stopped"));
1783
+ return daemonHandle?.stop() ?? Promise.resolve();
1784
+ };
1889
1785
  let ingressMonitor;
1890
1786
  const startupDeadline = Date.now() + startupTimeoutMs;
1891
1787
  if (autoStart) {
@@ -1922,20 +1818,23 @@ async function monitorSignalProvider(opts = {}) {
1922
1818
  sendReadReceipts,
1923
1819
  runtime
1924
1820
  });
1925
- daemonLifecycle.attach(daemonHandle);
1821
+ daemonHandle.exited.then((exit) => {
1822
+ if (abortSignal.aborted) return;
1823
+ daemonExitError = new Error(formatSignalDaemonExit(exit));
1824
+ daemonAbortController.abort(daemonExitError);
1825
+ });
1926
1826
  }
1927
- const onAbort = () => void daemonLifecycle.stop();
1827
+ const onAbort = () => void stopDaemon();
1928
1828
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
1929
1829
  try {
1930
1830
  if (daemonHandle) {
1931
1831
  await waitForSignalDaemonReady({
1932
1832
  baseUrl,
1933
- abortSignal: daemonLifecycle.abortSignal,
1833
+ abortSignal,
1934
1834
  startupDeadlineMs: startupDeadline,
1935
1835
  runtime,
1936
1836
  waitForTransportReadyFn
1937
1837
  });
1938
- const daemonExitError = daemonLifecycle.getExitError();
1939
1838
  if (daemonExitError) throw daemonExitError;
1940
1839
  }
1941
1840
  registerChannelRuntimeContext({
@@ -1957,7 +1856,7 @@ async function monitorSignalProvider(opts = {}) {
1957
1856
  const handleEvent = createSignalEventHandler({
1958
1857
  runtime,
1959
1858
  channelRuntime: opts.channelRuntime,
1960
- abortSignal: daemonLifecycle.abortSignal,
1859
+ abortSignal,
1961
1860
  runTrackedTask: (task) => {
1962
1861
  monitorTaskRunner.runTask(task);
1963
1862
  },
@@ -1980,10 +1879,40 @@ async function monitorSignalProvider(opts = {}) {
1980
1879
  ignoreAttachments,
1981
1880
  sendReadReceipts,
1982
1881
  readReceiptsViaDaemon,
1983
- fetchAttachment: (params) => fetchAttachment({
1984
- ...params,
1985
- transportKind
1986
- }),
1882
+ fetchAttachment: async (attachmentInput) => {
1883
+ const params = {
1884
+ ...attachmentInput,
1885
+ transportKind
1886
+ };
1887
+ const { attachment } = params;
1888
+ if (!attachment?.id) return null;
1889
+ if (typeof attachment.size === "number" && attachment.size > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1890
+ const rpcParams = { id: attachment.id };
1891
+ if (params.account) rpcParams.account = params.account;
1892
+ if (params.groupId) rpcParams.groupId = params.groupId;
1893
+ else if (params.sender) rpcParams.recipient = params.sender;
1894
+ else return null;
1895
+ const result = await signalRpcRequest("getAttachment", rpcParams, {
1896
+ baseUrl: params.baseUrl,
1897
+ maxResponseBytes: deriveSignalAttachmentRpcMaxResponseBytes(params.maxBytes),
1898
+ transportKind: params.transportKind
1899
+ });
1900
+ if (!result?.data) return null;
1901
+ if (estimateBase64DecodedBytes(result.data) > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1902
+ const canonicalData = canonicalizeBase64(result.data);
1903
+ if (!canonicalData) throw new Error(`Signal attachment ${attachment.id} returned malformed base64 data`);
1904
+ const buffer = Buffer.from(canonicalData, "base64");
1905
+ const originalFilename = normalizeOptionalString(attachment.filename ?? void 0);
1906
+ const contentType = normalizeOptionalString(attachment.contentType ?? void 0) ?? await detectMime({
1907
+ buffer,
1908
+ filePath: originalFilename
1909
+ });
1910
+ const saved = await saveMediaBuffer(buffer, contentType, "inbound", params.maxBytes, originalFilename);
1911
+ return {
1912
+ path: saved.path,
1913
+ contentType: saved.contentType
1914
+ };
1915
+ },
1987
1916
  deliverReplies: (params) => deliverReplies({
1988
1917
  ...params,
1989
1918
  cfg,
@@ -1998,7 +1927,7 @@ async function monitorSignalProvider(opts = {}) {
1998
1927
  await runSignalSseLoop({
1999
1928
  baseUrl,
2000
1929
  account,
2001
- abortSignal: daemonLifecycle.abortSignal,
1930
+ abortSignal,
2002
1931
  runtime,
2003
1932
  timeoutMs: 0,
2004
1933
  transportKind,
@@ -2006,16 +1935,14 @@ async function monitorSignalProvider(opts = {}) {
2006
1935
  statusSink: opts.statusSink,
2007
1936
  onEvent: (event) => monitorTaskRunner.runTask(async () => await ingressMonitor?.receive(event))
2008
1937
  });
2009
- const daemonExitError = daemonLifecycle.getExitError();
2010
1938
  if (daemonExitError) throw daemonExitError;
2011
1939
  } catch (err) {
2012
- const daemonExitError = daemonLifecycle.getExitError();
2013
1940
  if (opts.abortSignal?.aborted && !daemonExitError) return;
2014
1941
  if (daemonExitError) publishSignalRecovering(opts.statusSink, daemonExitError.message);
2015
1942
  throw err;
2016
1943
  } finally {
2017
1944
  await ingressMonitor?.stop();
2018
- await Promise.all([daemonLifecycle.stop(), monitorTaskRunner.waitForIdle()]);
1945
+ await Promise.all([stopDaemon(), monitorTaskRunner.waitForIdle()]);
2019
1946
  opts.abortSignal?.removeEventListener("abort", onAbort);
2020
1947
  }
2021
1948
  }