@openclaw/signal 2026.9.1-beta.1 → 2026.9.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 (29) hide show
  1. package/dist/api.js +7 -7
  2. package/dist/{approval-auth-BsYHLTHK.js → approval-auth-pNCKKotV.js} +14 -2
  3. package/dist/{approval-handler.runtime-COrjDqm1.js → approval-handler.runtime-Dsg_GE8N.js} +8 -4
  4. package/dist/{approval-reactions-D8x4WFYZ.js → approval-reactions-D-nkbsvm.js} +7 -17
  5. package/dist/{channel-DBwSJm7U.js → channel-B-A_5_Vz.js} +35 -30
  6. package/dist/channel-config-api.js +1 -1
  7. package/dist/channel-plugin-api.js +1 -1
  8. package/dist/{channel.runtime-DvhLiRgJ.js → channel.runtime-LfE0qPMj.js} +4 -3
  9. package/dist/{client-CvnR8_XA.js → client-BmpTN7R3.js} +21 -14
  10. package/dist/{client-adapter-D9SNPaNx.js → client-adapter-CDyW9LrQ.js} +1 -1
  11. package/dist/{config-schema-CkMIK7zp.js → config-schema-CHihQTOp.js} +10 -8
  12. package/dist/contract-api.js +1 -1
  13. package/dist/{doctor-contract-api-Dnp5eQQJ.js → doctor-contract-api-C18u1hW0.js} +1 -1
  14. package/dist/doctor-contract-api.js +1 -1
  15. package/dist/{install-signal-cli-Cpiyk0k1.js → install-signal-cli-7tEL37_j.js} +11 -7
  16. package/dist/{monitor-BIMAiZNm.js → monitor-D7Slr0-A.js} +99 -69
  17. package/dist/{probe-C2Z15YNS.js → probe-CtO2EIfg.js} +2 -2
  18. package/dist/{question-reactions-CkO8Q5i2.js → question-reactions-CsVOpHg_.js} +3 -14
  19. package/dist/{reaction-runtime-api-Ch0dk2sh.js → reaction-runtime-api-7FQFT_qb.js} +3 -2
  20. package/dist/reaction-runtime-api.js +1 -1
  21. package/dist/runtime-api.js +8 -8
  22. package/dist/{send-u8wH_fQM.js → send-DuH2ntJt.js} +20 -16
  23. package/dist/{send.runtime-DolSm_sS.js → send.runtime-BLiqhyHW.js} +1 -1
  24. package/dist/{transport-detection-B9TcrBCr.js → transport-detection-BcDiypLn.js} +21 -14
  25. package/dist/{transport-detection.runtime-kDheC5_M.js → transport-detection.runtime-DYuvIa7s.js} +1 -1
  26. package/dist/transport-probes.runtime-B7kr_6Zu.js +2 -0
  27. package/openclaw.plugin.json +2 -7
  28. package/package.json +4 -4
  29. package/dist/transport-probes.runtime-BsJdp0oI.js +0 -2
@@ -3,29 +3,30 @@ import { l as normalizeSignalTransportHost } from "./transport-policy-DxvSMHp9.j
3
3
  import { i as resolveSignalAccount, o as resolveSignalReplyToMode } from "./accounts-DO4HMqaK.js";
4
4
  import { n as normalizeSignalMessagingTarget } from "./normalize-l_b99hap.js";
5
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-YXPmgFMu.js";
6
- import { c as resolveSignalReactionLevel, f as isSignalNativeApprovalHandlerConfigured, i as getOptionalSignalRuntime, l as formatSignalMediaText, n as registerSignalReplyContext, o as materializeSignalPresentationFallback } from "./transport-detection-B9TcrBCr.js";
7
- import { n as signalRpcRequest, r as streamSignalEvents, t as signalCheck } from "./client-adapter-D9SNPaNx.js";
8
- import { n as sendReactionSignal } from "./reaction-runtime-api-Ch0dk2sh.js";
9
- import { i as maybeResolveSignalApprovalReaction, o as registerSignalApprovalReactionTargetForDeliveredPayload, s as resolveSignalApprovalConversationKey, t as addSignalApprovalReactionHintToStructuredPayload } from "./approval-reactions-D8x4WFYZ.js";
10
- import { r as registerSignalQuestionReactionTargetForDeliveredPayload, t as maybeResolveSignalQuestionReaction } from "./question-reactions-CkO8Q5i2.js";
11
- import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-u8wH_fQM.js";
6
+ import { c as resolveSignalReactionLevel, f as isSignalNativeApprovalHandlerConfigured, i as getOptionalSignalRuntime, l as formatSignalMediaText, n as registerSignalReplyContext, o as materializeSignalPresentationFallback } from "./transport-detection-BcDiypLn.js";
7
+ import { t as SignalSseRejectionError } from "./client-BmpTN7R3.js";
8
+ import { n as signalRpcRequest, r as streamSignalEvents, t as signalCheck } from "./client-adapter-CDyW9LrQ.js";
9
+ import { n as sendReactionSignal } from "./reaction-runtime-api-7FQFT_qb.js";
10
+ import { i as maybeResolveSignalApprovalReaction, o as registerSignalApprovalReactionTargetForDeliveredPayload, s as resolveSignalApprovalConversationKey, t as addSignalApprovalReactionHintToStructuredPayload } from "./approval-reactions-D-nkbsvm.js";
11
+ import { r as registerSignalQuestionReactionTargetForDeliveredPayload, t as maybeResolveSignalQuestionReaction } from "./question-reactions-CsVOpHg_.js";
12
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-DuH2ntJt.js";
12
13
  import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
13
14
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
14
- import { canonicalizeBase64, detectMime, estimateBase64DecodedBytes, kindFromMime, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
15
15
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
16
16
  import { asPositiveSafeInteger, isRecord, normalizeNullableString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
17
17
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
18
18
  import { normalizeE164, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
19
- import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, logInboundDrop, matchesMentionPatterns, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
19
+ import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, logInboundDrop, matchesMentionPatterns, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, resolveInboundDebounceMs, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
20
+ import { canonicalizeBase64, detectMime, estimateBase64DecodedBytes, kindFromMime, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
20
21
  import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
21
22
  import { ensurePortAvailable, extractErrorCode, formatErrorMessage as formatErrorMessage$1, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
22
- import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
23
+ import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention, resolveChannelGroups, resolveChannelGroupsConfigPath } from "openclaw/plugin-sdk/channel-policy";
23
24
  import path from "node:path";
24
25
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
25
26
  import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
26
27
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
27
28
  import { chunkTextWithMode, createReplyReferencePlanner, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
28
- import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
29
+ import { createRuntimeConfigReader, getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
29
30
  import { computeBackoff, createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
30
31
  import { resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
31
32
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
@@ -45,7 +46,7 @@ import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-ru
45
46
  import { listChatCommands, maybeResolveTextAlias, normalizeCommandBody } from "openclaw/plugin-sdk/command-auth-native";
46
47
  import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
47
48
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
48
- import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
49
+ import { channelBlockedPatch, channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
49
50
  //#region extensions/signal/src/daemon.ts
50
51
  const SIGNAL_DAEMON_STOP_KILL_TIMEOUT_MS = 1500;
51
52
  function formatSignalDaemonExit(exit) {
@@ -675,9 +676,17 @@ async function finalizeSignalStatusReaction(params) {
675
676
  await params.controller.restoreInitial();
676
677
  }
677
678
  function createSignalEventHandler(deps) {
679
+ const readConfig = createRuntimeConfigReader(deps.cfg);
680
+ const groupsConfigPath = resolveChannelGroupsConfigPath({
681
+ cfg: deps.cfg,
682
+ channel: "signal",
683
+ accountId: deps.accountId,
684
+ groups: resolveChannelGroups(deps.cfg, "signal", deps.accountId)
685
+ });
678
686
  const statusReactionTiming = deps.statusReactionTiming ?? DEFAULT_TIMING;
679
687
  const activeEnqueueEntries = /* @__PURE__ */ new WeakSet();
680
688
  async function handleSignalInboundMessage(entry) {
689
+ const { cfg } = entry;
681
690
  const fromLabel = formatInboundFromLabel({
682
691
  isGroup: entry.isGroup,
683
692
  groupLabel: entry.groupName ?? void 0,
@@ -687,14 +696,14 @@ function createSignalEventHandler(deps) {
687
696
  directId: entry.senderDisplay
688
697
  });
689
698
  const route = resolveSignalInboundRoute({
690
- cfg: deps.cfg,
699
+ cfg,
691
700
  accountId: deps.accountId,
692
701
  isGroup: entry.isGroup,
693
702
  groupId: entry.groupId,
694
703
  senderPeerId: entry.senderPeerId
695
704
  });
696
- const storePath = resolveStorePath(deps.cfg.session?.store, { agentId: route.agentId });
697
- const envelopeOptions = resolveEnvelopeFormatOptions(deps.cfg);
705
+ const storePath = resolveStorePath(cfg.session?.store, { agentId: route.agentId });
706
+ const envelopeOptions = resolveEnvelopeFormatOptions(cfg);
698
707
  const previousTimestamp = readSessionUpdatedAt({
699
708
  storePath,
700
709
  sessionKey: route.sessionKey
@@ -735,7 +744,7 @@ function createSignalEventHandler(deps) {
735
744
  limit: deps.historyLimit
736
745
  }) : void 0;
737
746
  const replyToMode = resolveSignalReplyToMode({
738
- cfg: deps.cfg,
747
+ cfg,
739
748
  accountId: deps.accountId,
740
749
  chatType: entry.isGroup ? "group" : "direct"
741
750
  });
@@ -797,17 +806,17 @@ function createSignalEventHandler(deps) {
797
806
  logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
798
807
  }
799
808
  const statusReactionTimestamp = resolveSignalStatusReactionTimestamp(entry);
800
- const statusReactionsConfig = deps.cfg.messages?.statusReactions;
809
+ const statusReactionsConfig = cfg.messages?.statusReactions;
801
810
  const signalReactionLevel = resolveSignalReactionLevel({
802
- cfg: deps.cfg,
811
+ cfg,
803
812
  accountId: route.accountId
804
813
  });
805
- const ackReaction = resolveAckReaction(deps.cfg, route.agentId, {
814
+ const ackReaction = resolveAckReaction(cfg, route.agentId, {
806
815
  channel: "signal",
807
816
  accountId: route.accountId
808
817
  });
809
818
  const shouldSendStatusReaction = Boolean(ackReaction && shouldAckReaction({
810
- scope: deps.cfg.messages?.ackReactionScope,
819
+ scope: cfg.messages?.ackReactionScope,
811
820
  isDirect: !entry.isGroup,
812
821
  isGroup: entry.isGroup,
813
822
  isMentionableGroup: entry.isGroup,
@@ -816,7 +825,7 @@ function createSignalEventHandler(deps) {
816
825
  }));
817
826
  const statusReactionTarget = `${entry.groupId ?? entry.senderRecipient}/${statusReactionTimestamp ?? "unknown"}`;
818
827
  const signalReactionOpts = {
819
- cfg: deps.cfg,
828
+ cfg,
820
829
  ...deps.baseUrl ? { baseUrl: deps.baseUrl } : {},
821
830
  ...deps.account ? { account: deps.account } : {},
822
831
  ...deps.accountId ? { accountId: deps.accountId } : {},
@@ -845,7 +854,7 @@ function createSignalEventHandler(deps) {
845
854
  }) : null;
846
855
  if (statusReactionController) statusReactionController.setQueued();
847
856
  const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
848
- cfg: deps.cfg,
857
+ cfg,
849
858
  agentId: route.agentId,
850
859
  channel: "signal",
851
860
  accountId: route.accountId,
@@ -853,7 +862,7 @@ function createSignalEventHandler(deps) {
853
862
  start: async () => {
854
863
  if (!ctxPayload.To) return;
855
864
  await sendTypingSignal(ctxPayload.To, {
856
- cfg: deps.cfg,
865
+ cfg,
857
866
  baseUrl: deps.baseUrl,
858
867
  account: deps.account,
859
868
  accountId: deps.accountId
@@ -879,13 +888,13 @@ function createSignalEventHandler(deps) {
879
888
  const dispatcherOptions = {
880
889
  ...replyPipeline,
881
890
  propagateRetryableNoSendFailure: true,
882
- humanDelay: resolveHumanDelayConfig(deps.cfg, route.agentId),
891
+ humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
883
892
  typingCallbacks
884
893
  };
885
894
  const delivery = {
886
895
  deliver: async (payload, _info) => {
887
896
  await deps.deliverReplies({
888
- cfg: deps.cfg,
897
+ cfg,
889
898
  replies: [payload],
890
899
  target: ctxPayload.To,
891
900
  baseUrl: deps.baseUrl,
@@ -945,7 +954,7 @@ function createSignalEventHandler(deps) {
945
954
  raw: entry
946
955
  }),
947
956
  resolveTurn: () => ({
948
- cfg: deps.cfg,
957
+ cfg,
949
958
  channel: "signal",
950
959
  accountId: route.accountId,
951
960
  route: {
@@ -963,7 +972,7 @@ function createSignalEventHandler(deps) {
963
972
  mainDmOwnerPin: (() => {
964
973
  if (inboundLastRouteSessionKey !== route.mainSessionKey) return;
965
974
  const pinnedOwner = resolvePinnedMainDmOwnerFromAllowlist({
966
- dmScope: deps.cfg.session?.dmScope,
975
+ dmScope: cfg.session?.dmScope,
967
976
  allowFrom: deps.allowFrom,
968
977
  normalizeEntry: normalizeSignalAllowRecipient
969
978
  });
@@ -1071,7 +1080,7 @@ function createSignalEventHandler(deps) {
1071
1080
  async function resolveSignalBatchChannelIngress(entries, last) {
1072
1081
  if (last.boundChannelIngress) return last.boundChannelIngress;
1073
1082
  const route = resolveSignalInboundRoute({
1074
- cfg: deps.cfg,
1083
+ cfg: last.cfg,
1075
1084
  accountId: deps.accountId,
1076
1085
  isGroup: last.isGroup,
1077
1086
  groupId: last.groupId,
@@ -1151,6 +1160,10 @@ function createSignalEventHandler(deps) {
1151
1160
  const { debouncer } = createChannelInboundDebouncer({
1152
1161
  cfg: deps.cfg,
1153
1162
  channel: "signal",
1163
+ resolveDebounceMs: (entry) => resolveInboundDebounceMs({
1164
+ cfg: entry.cfg,
1165
+ channel: "signal"
1166
+ }),
1154
1167
  buildKey: (entry) => resolveSignalInboundDebounceKey(deps.accountId, entry),
1155
1168
  shouldDebounce: (entry) => shouldDebounceTextInbound({
1156
1169
  text: entry.commandBody,
@@ -1182,7 +1195,7 @@ function createSignalEventHandler(deps) {
1182
1195
  const messageId = params.reaction.targetSentTimestamp ? String(params.reaction.targetSentTimestamp) : "unknown";
1183
1196
  const conversationKey = resolveSignalApprovalConversationKey(groupId ? `group:${groupId}` : `signal:${resolveSignalRecipient(params.sender)}`);
1184
1197
  if (conversationKey && await maybeResolveSignalApprovalReaction({
1185
- cfg: deps.cfg,
1198
+ cfg: params.cfg,
1186
1199
  accountId: deps.accountId,
1187
1200
  conversationKey,
1188
1201
  messageId,
@@ -1197,7 +1210,7 @@ function createSignalEventHandler(deps) {
1197
1210
  return true;
1198
1211
  }
1199
1212
  if (conversationKey && await maybeResolveSignalQuestionReaction({
1200
- cfg: deps.cfg,
1213
+ cfg: params.cfg,
1201
1214
  accountId: deps.accountId,
1202
1215
  conversationKey,
1203
1216
  messageId,
@@ -1219,7 +1232,7 @@ function createSignalEventHandler(deps) {
1219
1232
  })) return true;
1220
1233
  const senderPeerId = resolveSignalPeerId(params.sender);
1221
1234
  const route = resolveSignalInboundRoute({
1222
- cfg: deps.cfg,
1235
+ cfg: params.cfg,
1223
1236
  accountId: deps.accountId,
1224
1237
  isGroup,
1225
1238
  groupId,
@@ -1250,6 +1263,7 @@ function createSignalEventHandler(deps) {
1250
1263
  }
1251
1264
  return async (event, turnAdoptionLifecycle, preparedPayload) => {
1252
1265
  if (event.event !== "receive" || !event.data) return;
1266
+ const cfg = readConfig();
1253
1267
  let payload = preparedPayload ?? null;
1254
1268
  if (!preparedPayload) try {
1255
1269
  payload = JSON.parse(event.data);
@@ -1271,7 +1285,7 @@ function createSignalEventHandler(deps) {
1271
1285
  const messageText = renderSignalMentions(rawMessage, dataMessage?.mentions).trim();
1272
1286
  const groupId = dataMessage?.groupInfo?.groupId ?? reaction?.groupInfo?.groupId ?? void 0;
1273
1287
  const isGroup = Boolean(groupId);
1274
- const hasControlCommandInMessage = isControlCommandMessage(messageText, deps.cfg);
1288
+ const hasControlCommandInMessage = isControlCommandMessage(messageText, cfg);
1275
1289
  const senderDisplay = formatSignalSenderDisplay(sender);
1276
1290
  const resolveChannelIngress = async (contextBinding) => await resolveSignalAccessState({
1277
1291
  accountId: deps.accountId,
@@ -1282,14 +1296,14 @@ function createSignalEventHandler(deps) {
1282
1296
  sender,
1283
1297
  groupId,
1284
1298
  isGroup,
1285
- cfg: deps.cfg,
1299
+ cfg,
1286
1300
  hasControlCommand: hasControlCommandInMessage,
1287
1301
  contextBinding
1288
1302
  });
1289
1303
  const { senderAccess, commandAccess } = await resolveChannelIngress();
1290
1304
  const quoteText = normalizeOptionalString(dataMessage?.quote?.text) ?? "";
1291
1305
  const { contextVisibilityMode, quoteSenderAllowed, visibleQuoteText, visibleQuoteSender } = resolveSignalQuoteContext({
1292
- cfg: deps.cfg,
1306
+ cfg,
1293
1307
  accountId: deps.accountId,
1294
1308
  isGroup,
1295
1309
  dataMessage,
@@ -1298,6 +1312,7 @@ function createSignalEventHandler(deps) {
1298
1312
  if (quoteText && !visibleQuoteText && isGroup) logVerbose(`signal: drop quote context (mode=${contextVisibilityMode}, sender_allowed=${quoteSenderAllowed ? "yes" : "no"})`);
1299
1313
  const hasBodyContent = Boolean(messageText || visibleQuoteText) || Boolean(!reaction && dataMessage?.attachments?.length);
1300
1314
  if (reaction && await handleReactionOnlyInbound({
1315
+ cfg,
1301
1316
  envelope,
1302
1317
  sender,
1303
1318
  senderDisplay,
@@ -1323,7 +1338,7 @@ function createSignalEventHandler(deps) {
1323
1338
  accountId: deps.accountId,
1324
1339
  sendPairingReply: async (text) => {
1325
1340
  await sendMessageSignal(`signal:${senderRecipient}`, text, {
1326
- cfg: deps.cfg,
1341
+ cfg,
1327
1342
  baseUrl: deps.baseUrl,
1328
1343
  account: deps.account,
1329
1344
  maxBytes: deps.mediaMaxBytes,
@@ -1352,7 +1367,7 @@ function createSignalEventHandler(deps) {
1352
1367
  return;
1353
1368
  }
1354
1369
  const route = resolveSignalInboundRoute({
1355
- cfg: deps.cfg,
1370
+ cfg,
1356
1371
  accountId: deps.accountId,
1357
1372
  isGroup,
1358
1373
  groupId,
@@ -1364,12 +1379,12 @@ function createSignalEventHandler(deps) {
1364
1379
  const replyToId = typeof nativeReplyTargetTimestamp === "number" ? String(nativeReplyTargetTimestamp) : void 0;
1365
1380
  const signalToRaw = isGroup ? `group:${groupId}` : `signal:${senderRecipient}`;
1366
1381
  const signalTo = normalizeSignalMessagingTarget(signalToRaw) ?? signalToRaw;
1367
- const mentionRegexes = buildMentionRegexes(deps.cfg, route.agentId);
1382
+ const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
1368
1383
  const textWasMentioned = isGroup && matchesMentionPatterns(messageText, mentionRegexes);
1369
1384
  const nativeMentionFacts = resolveSignalMentionFacts(deps, rawMessage, dataMessage?.mentions);
1370
1385
  const wasMentioned = isGroup && (textWasMentioned || nativeMentionFacts.mentionsBot);
1371
1386
  const requireMention = isGroup && resolveChannelGroupRequireMention({
1372
- cfg: deps.cfg,
1387
+ cfg,
1373
1388
  channel: "signal",
1374
1389
  groupId,
1375
1390
  accountId: deps.accountId,
@@ -1394,10 +1409,12 @@ function createSignalEventHandler(deps) {
1394
1409
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
1395
1410
  if (isGroup && requireMention && canDetectMention && mentionDecision.shouldSkip) {
1396
1411
  logInboundDrop({
1397
- log: logVerbose,
1412
+ log: deps.runtime.log,
1398
1413
  channel: "signal",
1399
1414
  reason: "no mention",
1400
- target: senderDisplay
1415
+ target: groupId,
1416
+ onceKey: JSON.stringify([deps.accountId, groupId]),
1417
+ hint: `Mention patterns can be derived from the agent identity name. Set ${groupsConfigPath}[${JSON.stringify(groupId)}].requireMention=false to process messages without a mention. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.`
1401
1418
  });
1402
1419
  const pendingMedia = (dataMessage.attachments ?? []).map((attachment) => {
1403
1420
  const contentType = attachment?.contentType ?? void 0;
@@ -1430,32 +1447,29 @@ function createSignalEventHandler(deps) {
1430
1447
  sourceTimestamp: inboundTimestamp
1431
1448
  });
1432
1449
  const signalGroupPolicy = resolveChannelGroupPolicy({
1433
- cfg: deps.cfg,
1450
+ cfg,
1434
1451
  channel: "signal",
1435
1452
  groupId,
1436
1453
  accountId: deps.accountId
1437
1454
  });
1438
- if ((signalGroupPolicy.groupConfig?.ingest ?? signalGroupPolicy.defaultConfig?.ingest) === true) {
1439
- const canonicalGroupTarget = normalizeSignalMessagingTarget(`group:${groupId}`) ?? `group:${groupId}`;
1440
- fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "received", route.sessionKey, toInternalMessageReceivedContext({
1441
- from: `group:${groupId}`,
1442
- to: canonicalGroupTarget,
1443
- content: pendingBodyText,
1444
- timestamp: envelope.timestamp ?? void 0,
1445
- channelId: "signal",
1446
- accountId: deps.accountId,
1447
- conversationId: canonicalGroupTarget,
1448
- messageId: typeof envelope.timestamp === "number" ? String(envelope.timestamp) : void 0,
1449
- senderId: senderDisplay,
1450
- senderName: envelope.sourceName ?? void 0,
1451
- provider: "signal",
1452
- surface: "signal",
1453
- originatingChannel: "signal",
1454
- originatingTo: canonicalGroupTarget,
1455
- isGroup: true,
1456
- groupId: canonicalGroupTarget
1457
- }))), "signal: mention-skip message hook failed");
1458
- }
1455
+ if ((signalGroupPolicy.groupConfig?.ingest ?? signalGroupPolicy.defaultConfig?.ingest) === true) fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "received", route.sessionKey, toInternalMessageReceivedContext({
1456
+ from: `group:${groupId}`,
1457
+ to: signalTo,
1458
+ content: pendingBodyText,
1459
+ timestamp: envelope.timestamp ?? void 0,
1460
+ channelId: "signal",
1461
+ accountId: deps.accountId,
1462
+ conversationId: signalTo,
1463
+ messageId: typeof envelope.timestamp === "number" ? String(envelope.timestamp) : void 0,
1464
+ senderId: senderDisplay,
1465
+ senderName: envelope.sourceName ?? void 0,
1466
+ provider: "signal",
1467
+ surface: "signal",
1468
+ originatingChannel: "signal",
1469
+ originatingTo: signalTo,
1470
+ isGroup: true,
1471
+ groupId: signalTo
1472
+ }))), "signal: mention-skip message hook failed");
1459
1473
  return;
1460
1474
  }
1461
1475
  const attachments = dataMessage.attachments ?? [];
@@ -1506,7 +1520,7 @@ function createSignalEventHandler(deps) {
1506
1520
  if (!bodyText && mediaFacts.length === 0 && !visibleQuoteText) return;
1507
1521
  if (deps.sendReadReceipts && !deps.readReceiptsViaDaemon && !isGroup && inboundTimestamp) try {
1508
1522
  await sendReadReceiptSignal(`signal:${senderRecipient}`, inboundTimestamp, {
1509
- cfg: deps.cfg,
1523
+ cfg,
1510
1524
  baseUrl: deps.baseUrl,
1511
1525
  account: deps.account,
1512
1526
  accountId: deps.accountId
@@ -1526,6 +1540,7 @@ function createSignalEventHandler(deps) {
1526
1540
  sourceTimestamp: inboundTimestamp
1527
1541
  });
1528
1542
  const entry = {
1543
+ cfg,
1529
1544
  senderName,
1530
1545
  senderDisplay,
1531
1546
  senderRecipient,
@@ -1648,10 +1663,10 @@ async function startSignalIngressMonitor(params) {
1648
1663
  createClaimError: (_kind, claim) => new SignalIngressPermanentError("unsupported-event", `Signal ingress row ${claim.id} has an invalid payload`)
1649
1664
  },
1650
1665
  deliver: ([event, parsedPayload], lifecycle) => parsedPayload ? params.dispatch(event, lifecycle, parsedPayload) : void 0,
1651
- onDurableAdmission: async (_event, { facts }) => {
1666
+ onDurableAdmission: async (_event, { facts, isNew }) => {
1652
1667
  const { numberAliasEventId } = facts;
1653
1668
  if (!numberAliasEventId) return;
1654
- if (!await ingressQueue.complete(numberAliasEventId)) await ingressQueue.complete(facts.eventId);
1669
+ if (!await ingressQueue.complete(numberAliasEventId) && isNew) await ingressQueue.complete(facts.eventId);
1655
1670
  },
1656
1671
  pollIntervalMs: SIGNAL_INGRESS_DRAIN_INTERVAL_MS,
1657
1672
  retention: {
@@ -1684,6 +1699,14 @@ const DEFAULT_RECONNECT_POLICY = {
1684
1699
  factor: 2,
1685
1700
  jitter: .2
1686
1701
  };
1702
+ function isPermanentSignalSseRejection(error) {
1703
+ if (!(error instanceof SignalSseRejectionError)) return false;
1704
+ return error.status >= 400 && error.status < 500 && ![
1705
+ 408,
1706
+ 425,
1707
+ 429
1708
+ ].includes(error.status);
1709
+ }
1687
1710
  function publishSignalRecovering(statusSink, lastError) {
1688
1711
  statusSink?.({
1689
1712
  connected: false,
@@ -1731,6 +1754,12 @@ async function runSignalSseLoop({ baseUrl, account, abortSignal, runtime, onEven
1731
1754
  } catch (err) {
1732
1755
  if (abortSignal?.aborted) return;
1733
1756
  runtime.error?.(`Signal stream error: ${String(err)}`);
1757
+ if (isPermanentSignalSseRejection(err)) {
1758
+ const 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.`;
1759
+ runtime.log?.(`Signal reconnect stopped: ${lastError}`);
1760
+ statusSink?.(channelBlockedPatch(lastError, { connected: false }));
1761
+ return;
1762
+ }
1734
1763
  publishSignalRecovering(statusSink, String(err));
1735
1764
  reconnectAttempts += 1;
1736
1765
  const delayMs = computeBackoff(reconnectPolicy, reconnectAttempts);
@@ -1816,7 +1845,7 @@ function buildSignalReactionSystemEventText(params) {
1816
1845
  const withTarget = params.targetLabel ? `${base} from ${params.targetLabel}` : base;
1817
1846
  return params.groupLabel ? `${withTarget} in ${params.groupLabel}` : withTarget;
1818
1847
  }
1819
- const SIGNAL_ATTACHMENT_RPC_RESPONSE_HEADROOM_BYTES = 64 * 1024;
1848
+ const SIGNAL_ATTACHMENT_RPC_RESPONSE_HEADROOM_BYTES = 65536;
1820
1849
  const SIGNAL_BASE64_OVERHEAD_NUMERATOR = 4;
1821
1850
  const SIGNAL_BASE64_OVERHEAD_DENOMINATOR = 3;
1822
1851
  function deriveSignalAttachmentRpcMaxResponseBytes(maxBytes) {
@@ -1826,7 +1855,7 @@ function deriveSignalAttachmentRpcMaxResponseBytes(maxBytes) {
1826
1855
  async function fetchAttachment(params) {
1827
1856
  const { attachment } = params;
1828
1857
  if (!attachment?.id) return null;
1829
- if (typeof attachment.size === "number" && attachment.size > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / (1024 * 1024)).toFixed(0)}MB limit`);
1858
+ if (typeof attachment.size === "number" && attachment.size > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1830
1859
  const rpcParams = { id: attachment.id };
1831
1860
  if (params.account) rpcParams.account = params.account;
1832
1861
  if (params.groupId) rpcParams.groupId = params.groupId;
@@ -1838,15 +1867,16 @@ async function fetchAttachment(params) {
1838
1867
  transportKind: params.transportKind
1839
1868
  });
1840
1869
  if (!result?.data) return null;
1841
- if (estimateBase64DecodedBytes(result.data) > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / (1024 * 1024)).toFixed(0)}MB limit`);
1870
+ if (estimateBase64DecodedBytes(result.data) > params.maxBytes) throw new Error(`Signal attachment ${attachment.id} exceeds ${(params.maxBytes / 1048576).toFixed(0)}MB limit`);
1842
1871
  const canonicalData = canonicalizeBase64(result.data);
1843
1872
  if (!canonicalData) throw new Error(`Signal attachment ${attachment.id} returned malformed base64 data`);
1844
1873
  const buffer = Buffer.from(canonicalData, "base64");
1845
1874
  const originalFilename = normalizeOptionalString(attachment.filename ?? void 0);
1846
- const saved = await saveMediaBuffer(buffer, normalizeOptionalString(attachment.contentType ?? void 0) ?? await detectMime({
1875
+ const contentType = normalizeOptionalString(attachment.contentType ?? void 0) ?? await detectMime({
1847
1876
  buffer,
1848
1877
  filePath: originalFilename
1849
- }), "inbound", params.maxBytes, originalFilename);
1878
+ });
1879
+ const saved = await saveMediaBuffer(buffer, contentType, "inbound", params.maxBytes, originalFilename);
1850
1880
  return {
1851
1881
  path: saved.path,
1852
1882
  contentType: saved.contentType
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { t as detectSignalTransport } from "./transport-detection-B9TcrBCr.js";
3
- import { n as signalRpcRequest, t as signalCheck } from "./client-adapter-D9SNPaNx.js";
2
+ import { t as detectSignalTransport } from "./transport-detection-BcDiypLn.js";
3
+ import { n as signalRpcRequest, t as signalCheck } from "./client-adapter-CDyW9LrQ.js";
4
4
  import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  //#region extensions/signal/src/probe.ts
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { i as resolveSignalTarget } from "./approval-auth-BsYHLTHK.js";
3
- import { c as resolveSignalApprovalTargetAuthorKeys, s as resolveSignalApprovalConversationKey } from "./approval-reactions-D8x4WFYZ.js";
2
+ import { i as resolveSignalDeliveredConversationKey } from "./approval-auth-pNCKKotV.js";
3
+ import { c as resolveSignalApprovalTargetAuthorKeys } from "./approval-reactions-D-nkbsvm.js";
4
4
  import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
5
5
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
6
6
  //#region extensions/signal/src/question-reactions.ts
@@ -24,21 +24,10 @@ const questionReactionTargets = createQuestionReactionTargetStore({
24
24
  registerChannelDelivery: questionGatewayRuntime.registerChannelDelivery,
25
25
  resolveReaction: questionGatewayRuntime.resolveReaction
26
26
  });
27
- function resolveConversationKey(params) {
28
- try {
29
- return resolveSignalTarget({
30
- cfg: params.cfg,
31
- accountId: params.accountId,
32
- input: params.to
33
- })?.to ?? resolveSignalApprovalConversationKey(params.to);
34
- } catch {
35
- return resolveSignalApprovalConversationKey(params.to);
36
- }
37
- }
38
27
  function registerSignalQuestionReactionTargetForDeliveredPayload(params) {
39
28
  const binding = questionGatewayRuntime.readReactionBinding(params.payload);
40
29
  if (params.target.channel !== "signal" || !binding) return false;
41
- const conversationKey = resolveConversationKey({
30
+ const conversationKey = resolveSignalDeliveredConversationKey({
42
31
  cfg: params.cfg,
43
32
  ...params.target
44
33
  });
@@ -1,12 +1,13 @@
1
1
  import { i as resolveSignalAccount } from "./accounts-DO4HMqaK.js";
2
2
  import { r as normalizeSignalReactionRecipient } from "./normalize-l_b99hap.js";
3
- import { n as signalRpcRequest } from "./client-adapter-D9SNPaNx.js";
3
+ import { n as signalRpcRequest } from "./client-adapter-CDyW9LrQ.js";
4
4
  import { t as resolveSignalRpcContext } from "./rpc-context-dXSy4NtF.js";
5
5
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
6
6
  //#region extensions/signal/src/send-reactions.ts
7
7
  async function sendReactionSignalCore(params) {
8
+ const cfg = requireRuntimeConfig(params.opts.cfg, "Signal reactions");
8
9
  const accountInfo = resolveSignalAccount({
9
- cfg: requireRuntimeConfig(params.opts.cfg, "Signal reactions"),
10
+ cfg,
10
11
  accountId: params.opts.accountId
11
12
  });
12
13
  const { baseUrl, account } = resolveSignalRpcContext(params.opts, accountInfo);
@@ -1,2 +1,2 @@
1
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-Ch0dk2sh.js";
1
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-7FQFT_qb.js";
2
2
  export { removeReactionSignal, sendReactionSignal };
@@ -1,14 +1,14 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-DO4HMqaK.js";
2
2
  import { n as normalizeSignalMessagingTarget, t as looksLikeSignalTargetId } from "./normalize-l_b99hap.js";
3
- import { a as setSignalRuntime, c as resolveSignalReactionLevel, s as signalMessageActions } from "./transport-detection-B9TcrBCr.js";
4
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-Ch0dk2sh.js";
5
- import { n as SignalConfigSchema } from "./config-schema-CkMIK7zp.js";
6
- import { i as installSignalCli } from "./install-signal-cli-Cpiyk0k1.js";
7
- import { t as monitorSignalProvider } from "./monitor-BIMAiZNm.js";
8
- import { t as sendMessageSignal } from "./send-u8wH_fQM.js";
9
- import { t as probeSignal } from "./probe-C2Z15YNS.js";
3
+ import { a as setSignalRuntime, c as resolveSignalReactionLevel, s as signalMessageActions } from "./transport-detection-BcDiypLn.js";
4
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-7FQFT_qb.js";
5
+ import { n as SignalConfigSchema } from "./config-schema-CHihQTOp.js";
6
+ import { i as installSignalCli } from "./install-signal-cli-7tEL37_j.js";
7
+ import { t as monitorSignalProvider } from "./monitor-D7Slr0-A.js";
8
+ import { t as sendMessageSignal } from "./send-DuH2ntJt.js";
9
+ import { t as probeSignal } from "./probe-CtO2EIfg.js";
10
+ import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
10
11
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
11
- import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
12
12
  import { buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, collectStatusIssuesFromLastError, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
13
13
  import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
14
14
  import { DEFAULT_ACCOUNT_ID, applyAccountNameToChannelSection, deleteAccountFromConfigSection, emptyPluginConfigSchema, formatPairingApproveHint, getChatChannelMeta, migrateBaseNameToDefaultAccount, normalizeAccountId, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
@@ -1,12 +1,12 @@
1
1
  import { i as resolveSignalAccount } from "./accounts-DO4HMqaK.js";
2
2
  import { n as normalizeSignalMessagingTarget } from "./normalize-l_b99hap.js";
3
- import { n as registerSignalReplyContext, u as markdownToSignalText } from "./transport-detection-B9TcrBCr.js";
4
- import { n as signalRpcRequest } from "./client-adapter-D9SNPaNx.js";
3
+ import { n as registerSignalReplyContext, u as markdownToSignalText } from "./transport-detection-BcDiypLn.js";
4
+ import { n as signalRpcRequest } from "./client-adapter-CDyW9LrQ.js";
5
5
  import { t as resolveSignalRpcContext } from "./rpc-context-dXSy4NtF.js";
6
6
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
7
7
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
8
- import { kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
9
8
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
10
10
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
11
11
  //#region extensions/signal/src/send.ts
12
12
  function assertSignalRecipientDelivery(result, target) {
@@ -28,8 +28,9 @@ function assertSignalRecipientDelivery(result, target) {
28
28
  }
29
29
  async function resolveSignalRpcAccountInfo(opts) {
30
30
  if (!opts.cfg) throw new Error("Signal RPC account resolution requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
31
+ const cfg = requireRuntimeConfig(opts.cfg, "Signal RPC account resolution");
31
32
  return resolveSignalAccount({
32
- cfg: requireRuntimeConfig(opts.cfg, "Signal RPC account resolution"),
33
+ cfg,
33
34
  accountId: opts.accountId
34
35
  });
35
36
  }
@@ -111,7 +112,8 @@ function resolveSignalQuoteParams(opts) {
111
112
  };
112
113
  }
113
114
  function isSignalQuoteMetadataRejection(error) {
114
- const normalized = normalizeLowercaseStringOrEmpty(error instanceof Error ? error.message : String(error));
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ const normalized = normalizeLowercaseStringOrEmpty(message);
115
117
  const rpcCode = /^signal rpc (-?\d+):/u.exec(normalized)?.[1];
116
118
  if (rpcCode !== void 0) {
117
119
  if (rpcCode !== "-32602") return false;
@@ -142,7 +144,7 @@ async function sendMessageSignal(to, text, opts) {
142
144
  if (typeof opts.maxBytes === "number") return opts.maxBytes;
143
145
  if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
144
146
  if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
145
- return 8 * 1024 * 1024;
147
+ return 8388608;
146
148
  })();
147
149
  let attachments;
148
150
  if (opts.mediaUrl?.trim()) {
@@ -157,16 +159,18 @@ async function sendMessageSignal(to, text, opts) {
157
159
  kind: kindFromMime(resolved.contentType ?? void 0) ?? "unknown"
158
160
  };
159
161
  }
160
- if (message.trim()) if (textMode === "plain") textStyles = opts.textStyles ?? [];
161
- else {
162
- const tableMode = resolveMarkdownTableMode({
163
- cfg,
164
- channel: "signal",
165
- accountId: accountInfo.accountId
166
- });
167
- const formatted = markdownToSignalText(message, { tableMode });
168
- message = formatted.text;
169
- textStyles = formatted.styles;
162
+ if (message.trim()) {
163
+ if (textMode === "plain") textStyles = opts.textStyles ?? [];
164
+ else {
165
+ const tableMode = resolveMarkdownTableMode({
166
+ cfg,
167
+ channel: "signal",
168
+ accountId: accountInfo.accountId
169
+ });
170
+ const formatted = markdownToSignalText(message, { tableMode });
171
+ message = formatted.text;
172
+ textStyles = formatted.styles;
173
+ }
170
174
  }
171
175
  if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
172
176
  const params = { message };
@@ -1,2 +1,2 @@
1
- import { r as sendTypingSignal, t as sendMessageSignal } from "./send-u8wH_fQM.js";
1
+ import { r as sendTypingSignal, t as sendMessageSignal } from "./send-DuH2ntJt.js";
2
2
  export { sendMessageSignal, sendTypingSignal };