@openclaw/whatsapp 2026.7.1 → 2026.7.2-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 (62) hide show
  1. package/dist/{access-control-DYE5dXna.js → access-control-DazDX3RB.js} +4 -4
  2. package/dist/{account-config-6PTOSfaI.js → account-config-LiT9wWH9.js} +4 -8
  3. package/dist/{accounts-4YgwroRU.js → accounts-CKOFMPf-.js} +2 -3
  4. package/dist/{action-runtime-DPahAqxV.js → action-runtime-Bf-12TIH.js} +3 -3
  5. package/dist/action-runtime-api.js +1 -1
  6. package/dist/action-runtime.runtime.js +1 -1
  7. package/dist/{active-listener-B4SDebQ6.js → active-listener-5kHap439.js} +2 -2
  8. package/dist/{agent-tools-login-BX7eHgDm.js → agent-tools-login-DfyToBpG.js} +1 -4
  9. package/dist/api.js +17 -18
  10. package/dist/{approval-handler.runtime-DNWS4aYQ.js → approval-handler.runtime-CBiGQuZ5.js} +19 -13
  11. package/dist/approval-reactions-BGQoz25s.js +373 -0
  12. package/dist/{approval-resolver-DF-lu8k5.js → approval-resolver-DoeBBt22.js} +2 -1
  13. package/dist/{auth-store-Db-wfApd.js → auth-store-BQi9pZqQ.js} +31 -19
  14. package/dist/call-tool-api.js +6 -14
  15. package/dist/{channel-BWp7h1uY.js → channel-DbadDUDD.js} +84 -52
  16. package/dist/channel-config-api.js +1 -1
  17. package/dist/channel-plugin-api.js +1 -1
  18. package/dist/{channel-react-action-CKJHL_Wf.js → channel-react-action-PoRWfP9x.js} +7 -7
  19. package/dist/{channel.runtime-CAssfx9p.js → channel.runtime-BDDy4Lhz.js} +5 -5
  20. package/dist/{channel.setup-CURc5rEt.js → channel.setup-BZ-po8fg.js} +4 -5
  21. package/dist/config-schema-BZ8h1hWA.js +40 -0
  22. package/dist/{connection-controller-DkbrFSIn.js → connection-controller-C1rwfWb4.js} +52 -12
  23. package/dist/connection-controller-runtime-context-CbTsqmPT.js +14 -0
  24. package/dist/contract-api.js +2 -2
  25. package/dist/{directory-config-Dijefxc3.js → directory-config-CLBQrjtC.js} +3 -3
  26. package/dist/directory-contract-api.js +1 -1
  27. package/dist/doctor-contract-api.js +89 -4
  28. package/dist/{group-session-key-BhC1RQOE.js → group-session-key-CLTZeH_F.js} +31 -3
  29. package/dist/{identity-Dqft3mFA.js → identity-D7L8LCuH.js} +2 -2
  30. package/dist/legacy-state-migrations-api.js +1 -1
  31. package/dist/light-runtime-api.js +4 -4
  32. package/dist/{login-C48y0jlx.js → login-DGIoToX8.js} +48 -8
  33. package/dist/{login-qr-BjId7dll.js → login-qr-X-CZHOk9.js} +6 -7
  34. package/dist/login-qr-runtime.js +1 -1
  35. package/dist/{monitor-DD8bXohk.js → monitor-BZ8QvCeV.js} +145 -188
  36. package/dist/normalize-C-Z16y7Q.js +2 -0
  37. package/dist/{normalize-target-bVWjgftN.js → normalize-target-DSZnHkKF.js} +14 -10
  38. package/dist/{resolve-outbound-target-CcdugbDf.js → resolve-outbound-target-BR97fAbB.js} +1 -1
  39. package/dist/runtime-api.js +10 -10
  40. package/dist/{send-C5CpFcBG.js → send-D9Ydnn4o.js} +25 -320
  41. package/dist/{send-api-DLlXl8CH.js → send-api-W33lI2nR.js} +28 -88
  42. package/dist/{session-errors-BAj9D2La.js → session-errors-JuazczrA.js} +2 -1
  43. package/dist/{session-DriaHt7V.js → session-mxWFn0eS.js} +181 -19
  44. package/dist/session.runtime-Nsqaehbd.js +16 -0
  45. package/dist/{setup-core-DH-DFPLY.js → setup-core-3aSOoK2T.js} +18 -33
  46. package/dist/{setup-finalize-c8dl34sO.js → setup-finalize-DBf1jjBj.js} +41 -11
  47. package/dist/setup-plugin-api.js +1 -1
  48. package/dist/{setup-surface-DlWb0Z09.js → setup-surface-CiKGdNJv.js} +3 -3
  49. package/dist/{state-migrations-D_BmQUR9.js → state-migrations-CAMTrs2U.js} +1 -8
  50. package/dist/{targets-runtime-C-GiVn6Y.js → targets-runtime-Bsh0HR9y.js} +13 -6
  51. package/dist/text-runtime-BTGkMN0o.js +4 -0
  52. package/npm-shrinkwrap.json +3 -3
  53. package/openclaw.plugin.json +72 -48
  54. package/package.json +4 -4
  55. package/dist/config-schema-mBX2nmn1.js +0 -47
  56. package/dist/connection-controller-registry-TSX_udJp.js +0 -22
  57. package/dist/constants.js +0 -2
  58. package/dist/normalize-Bxwqo-bW.js +0 -2
  59. package/dist/session.runtime-CyooSQvj.js +0 -12
  60. package/dist/socket-timing-DhbVFjah.js +0 -77
  61. package/dist/targets.js +0 -2
  62. package/dist/text-runtime-DdX6-mC_.js +0 -4
@@ -1,53 +1,53 @@
1
- import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-4YgwroRU.js";
2
- import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, d as maybeResolveWhatsAppApprovalReaction, g as resolveWhatsAppReactionLevel, l as sendWhatsAppOutboundWithRetry, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation } from "./send-C5CpFcBG.js";
1
+ import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CKOFMPf-.js";
2
+ import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation, u as resolveWhatsAppReactionLevel } from "./send-D9Ydnn4o.js";
3
3
  import { n as getWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
4
- import { t as getRegisteredWhatsAppConnectionController } from "./connection-controller-registry-TSX_udJp.js";
5
- import { n as getStatusCode, t as formatError } from "./session-errors-BAj9D2La.js";
6
- import { a as resolveWhatsAppSocketTiming, i as resolveWhatsAppSocketOperationTimeoutMs, n as createWhatsAppSocketOperationTimeoutAdapter, o as withWhatsAppSocketOperationTimeout, r as isWhatsAppSocketOperationTimeoutError } from "./socket-timing-DhbVFjah.js";
7
- import { a as resolveEquivalentWhatsAppDirectChatJids, c as toWhatsappJidWithLid, i as markdownToWhatsApp, n as isSelfChatMode, o as resolveJidToE164, r as jidToE164, s as toWhatsappJid } from "./targets-runtime-C-GiVn6Y.js";
8
- import { n as normalizeE164, t as convertMarkdownTables$1 } from "./text-runtime-DdX6-mC_.js";
9
- import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-BhC1RQOE.js";
10
- import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-Db-wfApd.js";
11
- import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-Dqft3mFA.js";
4
+ import { n as getWhatsAppConnectionController } from "./connection-controller-runtime-context-CbTsqmPT.js";
5
+ import { a as resolveEquivalentWhatsAppDirectChatJids, c as toWhatsappJidWithLid, i as markdownToWhatsApp, n as isSelfChatMode, o as resolveJidToE164, r as jidToE164, s as toWhatsappJid } from "./targets-runtime-Bsh0HR9y.js";
6
+ import { n as normalizeE164, t as convertMarkdownTables$1 } from "./text-runtime-BTGkMN0o.js";
7
+ import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-CLTZeH_F.js";
8
+ import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-BQi9pZqQ.js";
9
+ import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-D7L8LCuH.js";
12
10
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
13
- import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-DYE5dXna.js";
14
- import { _ as extractLocationData, a as mayContainWhatsAppOutboundMention, b as extractText, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractExternalAdReplyContext, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMediaPlaceholder, x as hasInboundUserContent, y as extractMentionedJids } from "./send-api-DLlXl8CH.js";
15
- import { r as waitForWaConnection, t as createWaSocket } from "./session-DriaHt7V.js";
16
- import { t as BufferJSON } from "./session.runtime-CyooSQvj.js";
17
- import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-DkbrFSIn.js";
11
+ import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-DazDX3RB.js";
12
+ import { _ as extractLocationData, a as mayContainWhatsAppOutboundMention, b as extractText, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractExternalAdReplyContext, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMediaPlaceholder, x as hasInboundUserContent, y as extractMentionedJids } from "./send-api-W33lI2nR.js";
13
+ import { a as createWhatsAppSocketOperationTimeoutAdapter, c as resolveWhatsAppSocketTiming, l as withWhatsAppSocketOperationTimeout, o as isWhatsAppSocketOperationTimeoutError, r as waitForWaConnection, s as resolveWhatsAppSocketOperationTimeoutMs, t as createWaSocket } from "./session-mxWFn0eS.js";
14
+ import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
15
+ import { t as BufferJSON } from "./session.runtime-Nsqaehbd.js";
16
+ import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-C1rwfWb4.js";
17
+ import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-BGQoz25s.js";
18
18
  import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
19
- import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
20
- import { createChannelMessageReplyPipeline, createDurableInboundReceiveJournalFromQueue, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-outbound";
19
+ import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
21
20
  import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
22
21
  import { DEFAULT_TIMING, createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
23
22
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
23
+ import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
24
24
  import { getChildLogger } from "openclaw/plugin-sdk/logging-core";
25
25
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
26
26
  import { createSubsystemLogger, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1, warn } from "openclaw/plugin-sdk/runtime-env";
27
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
27
+ import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
28
+ import { createChannelMessageReplyPipeline, createDurableInboundReceiveJournalFromQueue, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
28
29
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
29
- import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
30
+ import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
30
31
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
31
32
  import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia as loadWebMedia$1, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
33
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
32
34
  import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
33
- import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
34
35
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId as normalizeAccountId$1, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
36
+ import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
35
37
  import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
36
38
  import { getSessionEntry, patchSessionEntry, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
37
- import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, runChannelInboundEvent, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
38
- import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
39
+ import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
39
40
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
40
41
  import { createHash } from "node:crypto";
41
- import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContext, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
42
+ import { dispatchReplyWithBufferedBlockDispatcher, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
42
43
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
43
44
  import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
44
- import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
45
- import { hasControlCommand, isControlCommandMessage, isControlCommandMessage as isControlCommandMessage$1, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
46
45
  import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
47
46
  import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
48
47
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
49
- import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
50
- import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
48
+ import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
49
+ import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
50
+ import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
51
51
  import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
52
52
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
53
53
  import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
@@ -55,60 +55,27 @@ import { createConnectedChannelStatusPatch, createTransportActivityStatusPatch }
55
55
  import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute } from "openclaw/plugin-sdk/conversation-binding-runtime";
56
56
  import { resolveAgentIdentity, resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
57
57
  import { normalizeGroupActivation, parseActivationCommand } from "openclaw/plugin-sdk/group-activation";
58
+ import { hasControlCommand, isControlCommandMessage, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
58
59
  import { createInternalHookEvent, deriveInboundMessageHookContext, fireAndForgetBoundedHook, toInternalMessageReceivedContext, toPluginMessageContext, toPluginMessageReceivedEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
59
60
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
60
61
  import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
62
+ import { createChannelApiRetryRunner } from "openclaw/plugin-sdk/retry-runtime";
61
63
  //#region extensions/whatsapp/src/inbound/dedupe.ts
62
64
  const WHATSAPP_INBOUND_DEDUPE_TTL_MS = 20 * 6e4;
63
65
  const RECENT_WEB_MESSAGE_MAX = 5e3;
64
66
  const RECENT_OUTBOUND_MESSAGE_TTL_MS = 20 * 6e4;
65
67
  const RECENT_OUTBOUND_MESSAGE_MAX = 5e3;
66
- const claimableInboundMessages = createClaimableDedupe({
67
- ttlMs: WHATSAPP_INBOUND_DEDUPE_TTL_MS,
68
- memoryMaxSize: RECENT_WEB_MESSAGE_MAX
68
+ const whatsAppInboundReplayGuard = createChannelReplayGuard({
69
+ dedupe: {
70
+ ttlMs: WHATSAPP_INBOUND_DEDUPE_TTL_MS,
71
+ memoryMaxSize: RECENT_WEB_MESSAGE_MAX
72
+ },
73
+ buildReplayKey: (keys) => keys
69
74
  });
70
- const recentOutboundMessages = createRecentMessageCache({
75
+ const recentOutboundMessages = createDedupeCache({
71
76
  ttlMs: RECENT_OUTBOUND_MESSAGE_TTL_MS,
72
77
  maxSize: RECENT_OUTBOUND_MESSAGE_MAX
73
78
  });
74
- function createRecentMessageCache(options) {
75
- const ttlMs = Math.max(0, options.ttlMs);
76
- const maxSize = Math.max(0, Math.floor(options.maxSize));
77
- const cache = /* @__PURE__ */ new Map();
78
- const prune = (now) => {
79
- if (ttlMs > 0) {
80
- const cutoff = now - ttlMs;
81
- for (const [key, timestamp] of cache) if (timestamp < cutoff) cache.delete(key);
82
- }
83
- while (cache.size > maxSize) {
84
- const oldest = cache.keys().next().value;
85
- if (!oldest) break;
86
- cache.delete(oldest);
87
- }
88
- };
89
- const peek = (key, now = Date.now()) => {
90
- if (!key) return false;
91
- const timestamp = cache.get(key);
92
- if (timestamp === void 0) return false;
93
- if (ttlMs > 0 && now - timestamp >= ttlMs) {
94
- cache.delete(key);
95
- return false;
96
- }
97
- return true;
98
- };
99
- return {
100
- check: (key, now = Date.now()) => {
101
- if (!key) return false;
102
- const existed = peek(key, now);
103
- cache.delete(key);
104
- cache.set(key, now);
105
- prune(now);
106
- return existed;
107
- },
108
- peek,
109
- clear: () => cache.clear()
110
- };
111
- }
112
79
  var WhatsAppRetryableInboundError = class extends Error {
113
80
  constructor(message, options) {
114
81
  super(message, options);
@@ -123,18 +90,9 @@ function buildMessageKey(params) {
123
90
  return `${accountId}:${remoteJid}:${messageId}`;
124
91
  }
125
92
  function resetWebInboundDedupe() {
126
- claimableInboundMessages.clearMemory();
93
+ whatsAppInboundReplayGuard.clearMemory();
127
94
  recentOutboundMessages.clear();
128
95
  }
129
- async function claimRecentInboundMessageDelivery(key) {
130
- return (await claimableInboundMessages.claim(key)).kind;
131
- }
132
- async function commitRecentInboundMessage(key) {
133
- await claimableInboundMessages.commit(key);
134
- }
135
- function releaseRecentInboundMessage(key, error) {
136
- claimableInboundMessages.release(key, { error });
137
- }
138
96
  function rememberRecentOutboundMessage(params) {
139
97
  const key = buildMessageKey(params);
140
98
  if (!key) return;
@@ -570,12 +528,13 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
570
528
  }
571
529
  //#endregion
572
530
  //#region extensions/whatsapp/src/inbound/monitor.ts
573
- const LOGGED_OUT_STATUS = DisconnectReason?.loggedOut ?? 401;
531
+ const LOGGED_OUT_STATUS = DisconnectReason.loggedOut;
574
532
  const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
575
533
  const GROUP_META_TTL_MS = 300 * 1e3;
576
534
  const BAILEYS_MESSAGE_TTL_MS = 600 * 1e3;
577
535
  const INBOUND_CLOSE_DRAIN_TIMEOUT_MS = 5e3;
578
536
  const REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE = /reply session initialization conflicted for \S+/u;
537
+ const WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES = 500;
579
538
  function resolveRetryableWhatsAppInboundError(error) {
580
539
  if (error instanceof WhatsAppRetryableInboundError) return error;
581
540
  if (!collectErrorGraphCandidates(error, (current) => [current.cause, current.error]).some((candidate) => REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE.test(formatErrorMessage(candidate)))) return null;
@@ -598,7 +557,7 @@ function rememberGroupMetadataCacheEntry(cache, jid, entry) {
598
557
  }
599
558
  if (cache.has(jid)) cache.delete(jid);
600
559
  cache.set(jid, entry);
601
- while (cache.size > 500) {
560
+ while (cache.size > WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES) {
602
561
  const oldest = cache.keys().next();
603
562
  if (oldest.done) break;
604
563
  cache.delete(oldest.value);
@@ -624,7 +583,7 @@ function rememberWhatsAppBaileysCacheEntry(cache, key, value, ttlMs) {
624
583
  expiresAt: Date.now() + ttlMs,
625
584
  value
626
585
  });
627
- while (cache.size > 500) {
586
+ while (cache.size > WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES) {
628
587
  const oldest = cache.keys().next();
629
588
  if (oldest.done) break;
630
589
  cache.delete(oldest.value);
@@ -645,9 +604,6 @@ function logWhatsAppVerbose(enabled, message) {
645
604
  if (!enabled) return;
646
605
  defaultRuntime.log(message);
647
606
  }
648
- function isGroupJid(jid) {
649
- return (typeof isJidGroup === "function" ? isJidGroup(jid) : jid.endsWith("@g.us")) === true;
650
- }
651
607
  function isDirectUserJid(jid) {
652
608
  return /^(\d+)(?::\d+)?@(s\.whatsapp\.net|c\.us|lid|hosted|hosted\.lid)$/i.test(jid.trim());
653
609
  }
@@ -711,7 +667,7 @@ async function attachWebInboxToSocket(options) {
711
667
  if (!options.socketRef) return sock;
712
668
  if (options.socketRef.current) return options.socketRef.current;
713
669
  if (!self.e164 && !self.jid && !self.lid) return null;
714
- const successor = getRegisteredWhatsAppConnectionController(options.accountId);
670
+ const successor = getWhatsAppConnectionController(options.accountId);
715
671
  if (!successor) return null;
716
672
  const successorIdentity = successor.getSelfIdentity();
717
673
  if (!successorIdentity || !identitiesOverlap(self, successorIdentity)) return null;
@@ -740,16 +696,16 @@ async function attachWebInboxToSocket(options) {
740
696
  return (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
741
697
  });
742
698
  const finalizeInboundDelivery = async (entries, error) => {
743
- const dedupeKeys = uniqueStrings(entries.map((entry) => entry.dedupeKey).filter(isNonEmptyString));
699
+ const replayClaims = entries.map((entry) => entry.replayClaim).filter((claim) => claim !== void 0);
744
700
  const durableEntries = entries.filter((entry) => isNonEmptyString(entry.durableId));
745
701
  const readReceiptEntries = entries.filter((entry) => Boolean(entry.readReceipt));
746
702
  const retryableError = resolveRetryableWhatsAppInboundError(error);
747
703
  if (retryableError) {
748
- dedupeKeys.forEach((dedupeKey) => releaseRecentInboundMessage(dedupeKey, retryableError));
704
+ for (const claim of replayClaims) claim.release({ error: retryableError });
749
705
  await Promise.all(durableEntries.map((entry) => durableInboundJournal.release(entry.durableId, { lastError: formatError(retryableError) })));
750
706
  return;
751
707
  }
752
- await Promise.all([...dedupeKeys.map((dedupeKey) => commitRecentInboundMessage(dedupeKey)), ...durableEntries.map((entry) => durableInboundJournal.complete(entry.durableId, entry.readReceipt ? { metadata: { readReceipt: entry.readReceipt } } : void 0))]);
708
+ await Promise.all([...replayClaims.map((claim) => claim.commit()), ...durableEntries.map((entry) => durableInboundJournal.complete(entry.durableId, entry.readReceipt ? { metadata: { readReceipt: entry.readReceipt } } : void 0))]);
753
709
  await Promise.all(readReceiptEntries.map((entry) => maybeMarkInboundAsRead(entry.readReceipt)));
754
710
  };
755
711
  const debouncer = createInboundDebouncer({
@@ -988,7 +944,7 @@ async function attachWebInboxToSocket(options) {
988
944
  }
989
945
  };
990
946
  const resolveOutboundMentionsForGroup = async (jid, text) => {
991
- if (!isGroupJid(jid) || !mayContainWhatsAppOutboundMention(text)) return {
947
+ if (isJidGroup(jid) !== true || !mayContainWhatsAppOutboundMention(text)) return {
992
948
  text,
993
949
  mentionedJids: []
994
950
  };
@@ -1021,7 +977,7 @@ async function attachWebInboxToSocket(options) {
1021
977
  const remoteJid = msg.key?.remoteJid;
1022
978
  if (!remoteJid) return null;
1023
979
  if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast")) return null;
1024
- const group = isGroupJid(remoteJid);
980
+ const group = isJidGroup(remoteJid) === true;
1025
981
  if (Boolean(msg.key?.fromMe) && id && isRecentOutboundMessage({
1026
982
  accountId: options.accountId,
1027
983
  remoteJid,
@@ -1162,9 +1118,9 @@ async function attachWebInboxToSocket(options) {
1162
1118
  return;
1163
1119
  }
1164
1120
  const dedupeKey = inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : "";
1165
- const dedupeClaim = dedupeKey ? await claimRecentInboundMessageDelivery(dedupeKey) : "claimed";
1166
- if (dedupeClaim !== "claimed") {
1167
- if (dedupeClaim === "duplicate") {
1121
+ const dedupeClaim = dedupeKey ? await whatsAppInboundReplayGuard.claim(dedupeKey) : { kind: "invalid" };
1122
+ if (dedupeClaim.kind === "duplicate" || dedupeClaim.kind === "inflight") {
1123
+ if (dedupeClaim.kind === "duplicate") {
1168
1124
  await completeUndeliverableDurableInbound(durableId, durableMetadata);
1169
1125
  await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
1170
1126
  }
@@ -1174,7 +1130,8 @@ async function attachWebInboxToSocket(options) {
1174
1130
  await enqueueInboundMessage(msg, inbound, enriched, {
1175
1131
  durableId,
1176
1132
  readReceipt: deliveryReadReceipt,
1177
- receiveOrder
1133
+ receiveOrder,
1134
+ ...dedupeClaim.kind === "claimed" ? { replayClaim: dedupeClaim.handle } : {}
1178
1135
  });
1179
1136
  };
1180
1137
  const replayPendingDurableInboundMessages = async () => {
@@ -1339,7 +1296,7 @@ async function attachWebInboxToSocket(options) {
1339
1296
  }
1340
1297
  } : void 0,
1341
1298
  group,
1342
- dedupeKey: inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : void 0,
1299
+ replayClaim: durable.replayClaim,
1343
1300
  durableId: durable.durableId,
1344
1301
  readReceipt: durable.readReceipt,
1345
1302
  receiveOrder: durable.receiveOrder
@@ -1770,7 +1727,7 @@ function createEchoTracker(params) {
1770
1727
  sessionKey: opts.combinedBodySessionKey,
1771
1728
  combinedBody: opts.combinedBody
1772
1729
  }));
1773
- if (opts.logVerboseMessage) params.logVerbose?.(`Added to echo detection set (size now: ${recentlySent.size}): ${text.slice(0, 50)}...`);
1730
+ if (opts.logVerboseMessage) params.logVerbose?.(`Added to echo detection set (size now: ${recentlySent.size}): ${truncateUtf16Safe(text, 50)}...`);
1774
1731
  trim();
1775
1732
  };
1776
1733
  return {
@@ -1831,19 +1788,20 @@ async function resolveGroupActivationFor(params) {
1831
1788
  sessionKey: params.sessionKey
1832
1789
  });
1833
1790
  const activation = (normalizeAccountId$1(params.accountId) === DEFAULT_ACCOUNT_ID$1 && hasNamedWhatsAppAccounts(params.cfg) && isActivationOnlyEntry(scopedEntry) ? void 0 : scopedEntry?.groupActivation) ?? legacyEntry?.groupActivation;
1834
- if (activation !== void 0 && scopedEntry?.groupActivation === void 0) await patchSessionEntry({
1835
- ...sessionScope,
1836
- sessionKey: params.sessionKey,
1837
- fallbackEntry: {},
1838
- replaceEntry: true,
1839
- update: (entry) => {
1840
- if (entry.groupActivation !== void 0) return null;
1841
- return {
1842
- ...entry,
1843
- groupActivation: activation
1844
- };
1845
- }
1846
- });
1791
+ if (activation !== void 0 && scopedEntry?.groupActivation === void 0) {
1792
+ if (scopedEntry) await patchSessionEntry({
1793
+ ...sessionScope,
1794
+ sessionKey: params.sessionKey,
1795
+ replaceEntry: true,
1796
+ update: (entry) => {
1797
+ if (entry.groupActivation !== void 0) return null;
1798
+ return {
1799
+ ...entry,
1800
+ groupActivation: activation
1801
+ };
1802
+ }
1803
+ });
1804
+ }
1847
1805
  const defaultActivation = !resolveWhatsAppInboundPolicy({
1848
1806
  cfg: params.cfg,
1849
1807
  accountId: params.accountId
@@ -2043,17 +2001,12 @@ function formatGroupMembers(params) {
2043
2001
  }
2044
2002
  //#endregion
2045
2003
  //#region extensions/whatsapp/src/auto-reply/monitor/group-gating.ts
2046
- const MAX_GROUP_DROP_WARNINGS = 100;
2047
- const groupDropWarned = /* @__PURE__ */ new Set();
2004
+ const groupDropWarned = createDedupeCache({
2005
+ ttlMs: 0,
2006
+ maxSize: 100
2007
+ });
2048
2008
  function shouldWarnForGroupDrop(warnKey) {
2049
- if (groupDropWarned.has(warnKey)) return false;
2050
- groupDropWarned.add(warnKey);
2051
- while (groupDropWarned.size > MAX_GROUP_DROP_WARNINGS) {
2052
- const oldest = groupDropWarned.values().next().value;
2053
- if (!oldest) break;
2054
- groupDropWarned.delete(oldest);
2055
- }
2056
- return true;
2009
+ return !groupDropWarned.check(warnKey);
2057
2010
  }
2058
2011
  function isOwnerSender(baseMentionConfig, msg, authDir) {
2059
2012
  const sender = normalizeE164(getSenderIdentity(msg, authDir).e164 ?? "");
@@ -2250,6 +2203,57 @@ function resolveWhatsAppDirectSystemPrompt(params) {
2250
2203
  return wildcard != null ? wildcard.trim() || void 0 : void 0;
2251
2204
  }
2252
2205
  //#endregion
2206
+ //#region extensions/whatsapp/src/outbound-retry.ts
2207
+ const WHATSAPP_OUTBOUND_MAX_ATTEMPTS = 3;
2208
+ const WHATSAPP_OUTBOUND_MIN_DELAY_MS = 500;
2209
+ const WHATSAPP_OUTBOUND_MAX_DELAY_MS = 1e3;
2210
+ const WHATSAPP_RETRYABLE_OUTBOUND_ERROR_PATTERN = /closed|reset|timed\s*out|disconnect/i;
2211
+ var WhatsAppOutboundRetryError = class extends Error {
2212
+ constructor(original) {
2213
+ super(formatError(original), { cause: original });
2214
+ this.original = original;
2215
+ }
2216
+ };
2217
+ function isRetryableWhatsAppOutboundError(error) {
2218
+ if (isWhatsAppSocketOperationTimeoutError(error)) return false;
2219
+ return WHATSAPP_RETRYABLE_OUTBOUND_ERROR_PATTERN.test(formatError(error));
2220
+ }
2221
+ async function sendWhatsAppOutboundWithRetry(params) {
2222
+ const runWithRetry = createChannelApiRetryRunner({
2223
+ retry: {
2224
+ attempts: WHATSAPP_OUTBOUND_MAX_ATTEMPTS,
2225
+ minDelayMs: WHATSAPP_OUTBOUND_MIN_DELAY_MS,
2226
+ maxDelayMs: WHATSAPP_OUTBOUND_MAX_DELAY_MS,
2227
+ jitter: 0
2228
+ },
2229
+ strictShouldRetry: true,
2230
+ retryAfterMs: () => void 0,
2231
+ shouldRetry: (error, attempt) => {
2232
+ if (!(error instanceof WhatsAppOutboundRetryError) || !isRetryableWhatsAppOutboundError(error.original)) return false;
2233
+ params.onRetry?.({
2234
+ attempt,
2235
+ maxAttempts: WHATSAPP_OUTBOUND_MAX_ATTEMPTS,
2236
+ backoffMs: Math.min(WHATSAPP_OUTBOUND_MIN_DELAY_MS * 2 ** (attempt - 1), WHATSAPP_OUTBOUND_MAX_DELAY_MS),
2237
+ error: error.original,
2238
+ errorText: formatError(error.original)
2239
+ });
2240
+ return true;
2241
+ }
2242
+ });
2243
+ try {
2244
+ return await runWithRetry(async () => {
2245
+ try {
2246
+ return await params.send();
2247
+ } catch (error) {
2248
+ throw new WhatsAppOutboundRetryError(error);
2249
+ }
2250
+ });
2251
+ } catch (error) {
2252
+ if (error instanceof WhatsAppOutboundRetryError) throw error.original;
2253
+ throw error;
2254
+ }
2255
+ }
2256
+ //#endregion
2253
2257
  //#region extensions/whatsapp/src/auto-reply/util.ts
2254
2258
  function elide(text, limit = 400) {
2255
2259
  if (!text) return text;
@@ -2369,11 +2373,10 @@ async function deliverWebReply(params) {
2369
2373
  messageText: cached?.body ?? ""
2370
2374
  });
2371
2375
  };
2372
- const sendWithRetry = async (fn, label, maxAttempts = 3) => {
2376
+ const sendWithRetry = async (fn, label) => {
2373
2377
  try {
2374
2378
  return await sendWhatsAppOutboundWithRetry({
2375
2379
  send: fn,
2376
- maxAttempts,
2377
2380
  onRetry: ({ attempt, maxAttempts: retryMaxAttempts, backoffMs, errorText }) => {
2378
2381
  logVerbose(`Retrying ${label} to ${conversationId} after failure (${attempt}/${retryMaxAttempts - 1}) in ${backoffMs}ms: ${errorText}`);
2379
2382
  }
@@ -2575,8 +2578,8 @@ function resolveWhatsAppDurableReplyToId(params) {
2575
2578
  return null;
2576
2579
  }
2577
2580
  function resolveWhatsAppDisableBlockStreaming(cfg) {
2578
- if (typeof cfg.channels?.whatsapp?.blockStreaming !== "boolean") return;
2579
- return !cfg.channels.whatsapp.blockStreaming;
2581
+ const enabled = resolveChannelStreamingBlockEnabled(cfg.channels?.whatsapp);
2582
+ return typeof enabled === "boolean" ? !enabled : void 0;
2580
2583
  }
2581
2584
  function resolveWhatsAppDeliverablePayload(payload, info) {
2582
2585
  if (payload.isReasoning === true || payload.isCompactionNotice === true) return null;
@@ -2659,7 +2662,6 @@ async function buildWhatsAppInboundContext(params) {
2659
2662
  }] : void 0, { transcribed: (_entry, index) => params.mediaTranscribedIndexes?.includes(index) === true });
2660
2663
  return buildChannelInboundEventContext({
2661
2664
  channel: "whatsapp",
2662
- finalize: finalizeInboundContext,
2663
2665
  supplemental: {
2664
2666
  quote: params.visibleReplyTo ? {
2665
2667
  id: params.visibleReplyTo.id,
@@ -2696,7 +2698,7 @@ async function buildWhatsAppInboundContext(params) {
2696
2698
  bodyForAgent: params.bodyForAgent ?? params.msg.payload.body,
2697
2699
  inboundHistory,
2698
2700
  rawBody: params.rawBody ?? params.msg.payload.body,
2699
- commandBody: params.commandBody ?? params.msg.payload.body
2701
+ commandBody: params.command?.body ?? params.msg.payload.body
2700
2702
  },
2701
2703
  access: {
2702
2704
  ...wasMentioned !== void 0 ? { mentions: {
@@ -2704,9 +2706,9 @@ async function buildWhatsAppInboundContext(params) {
2704
2706
  wasMentioned,
2705
2707
  requireMention: params.msg.groupMention?.requireMention
2706
2708
  } } : {},
2707
- commands: { authorized: params.commandAuthorized }
2709
+ commands: { authorized: params.command?.authorized === true }
2708
2710
  },
2709
- commandTurn: params.commandTurn,
2711
+ command: params.command,
2710
2712
  extra: {
2711
2713
  Transcript: params.transcript,
2712
2714
  GroupSubject: params.msg.group?.subject,
@@ -2716,40 +2718,12 @@ async function buildWhatsAppInboundContext(params) {
2716
2718
  fallbackE164: params.sender.e164
2717
2719
  }),
2718
2720
  SenderE164: params.sender.e164,
2719
- CommandSource: params.commandSource ?? (params.commandTurn?.source === "native" || params.commandTurn?.source === "text" ? params.commandTurn.source : void 0),
2720
2721
  ReplyThreading: params.replyThreading,
2721
2722
  SuppressMessageReceivedHooks: params.suppressMessageReceivedHooks,
2722
2723
  ...params.msg.payload.location ? toLocationContext(params.msg.payload.location) : {}
2723
2724
  }
2724
2725
  });
2725
2726
  }
2726
- function normalizeCommandTurnFromContext(value) {
2727
- if (!value || typeof value !== "object") return;
2728
- const record = value;
2729
- const kind = record.kind;
2730
- const source = record.source;
2731
- if (kind === "native" && source === "native" && typeof record.authorized === "boolean") return {
2732
- kind: "native",
2733
- source: "native",
2734
- authorized: record.authorized,
2735
- commandName: typeof record.commandName === "string" ? record.commandName : void 0,
2736
- body: typeof record.body === "string" ? record.body : void 0
2737
- };
2738
- if (kind === "text-slash" && source === "text" && typeof record.authorized === "boolean") return {
2739
- kind: "text-slash",
2740
- source: "text",
2741
- authorized: record.authorized,
2742
- commandName: typeof record.commandName === "string" ? record.commandName : void 0,
2743
- body: typeof record.body === "string" ? record.body : void 0
2744
- };
2745
- if (kind === "normal" && source === "message") return {
2746
- kind: "normal",
2747
- source: "message",
2748
- authorized: false,
2749
- commandName: typeof record.commandName === "string" ? record.commandName : void 0,
2750
- body: typeof record.body === "string" ? record.body : void 0
2751
- };
2752
- }
2753
2727
  function resolveWhatsAppDmRouteTarget(params) {
2754
2728
  const admission = requireWhatsAppInboundAdmission(params.msg);
2755
2729
  const conversationId = admission.conversation.id;
@@ -2798,18 +2772,9 @@ async function dispatchWhatsAppBufferedReply(params) {
2798
2772
  accountId: params.route.accountId
2799
2773
  });
2800
2774
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
2801
- const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : conversationKind;
2802
- const sourceReplyCommandSource = params.context.CommandSource === "native" || params.context.CommandSource === "text" ? params.context.CommandSource : void 0;
2803
- const sourceReplyCommandTurn = normalizeCommandTurnFromContext(params.context.CommandTurn);
2804
- const sourceReplyCommandAuthorized = typeof params.context.CommandAuthorized === "boolean" ? params.context.CommandAuthorized : void 0;
2805
- const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
2775
+ const sourceReplyDeliveryMode = params.context.ChatType === "group" || params.context.ChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
2806
2776
  cfg: params.cfg,
2807
- ctx: {
2808
- ChatType: sourceReplyChatType,
2809
- CommandTurn: sourceReplyCommandTurn,
2810
- CommandSource: sourceReplyCommandSource,
2811
- CommandAuthorized: sourceReplyCommandAuthorized
2812
- }
2777
+ ctx: params.context
2813
2778
  }) : void 0;
2814
2779
  const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
2815
2780
  const disableBlockStreaming = sourceRepliesAreToolOnly ? true : resolveWhatsAppDisableBlockStreaming(params.cfg);
@@ -3340,24 +3305,13 @@ async function processMessage(params) {
3340
3305
  normalizeE164
3341
3306
  });
3342
3307
  const shouldCheckCommandAuth = shouldComputeCommandAuthorized(commandBody, params.cfg);
3343
- const isTextCommand = isControlCommandMessage$1(commandBody, params.cfg);
3308
+ const isTextCommand = isControlCommandMessage(commandBody, params.cfg);
3344
3309
  const commandAuthorized = shouldCheckCommandAuth ? await resolveWhatsAppCommandAuthorized({
3345
3310
  cfg: params.cfg,
3346
3311
  msg: params.msg,
3347
3312
  policy: inboundPolicy,
3348
3313
  authDir: account.authDir
3349
3314
  }) : void 0;
3350
- const commandTurn = isTextCommand ? {
3351
- kind: "text-slash",
3352
- source: "text",
3353
- authorized: Boolean(commandAuthorized),
3354
- body: commandBody
3355
- } : {
3356
- kind: "normal",
3357
- source: "message",
3358
- authorized: false,
3359
- body: commandBody
3360
- };
3361
3315
  const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
3362
3316
  cfg: params.cfg,
3363
3317
  agentId: params.route.agentId,
@@ -3381,9 +3335,11 @@ async function processMessage(params) {
3381
3335
  const ctxPayload = await buildWhatsAppInboundContext({
3382
3336
  bodyForAgent: msgForAgent.payload.body,
3383
3337
  combinedBody,
3384
- commandBody,
3385
- commandAuthorized,
3386
- commandTurn,
3338
+ command: {
3339
+ kind: isTextCommand ? "text-slash" : "normal",
3340
+ body: commandBody,
3341
+ authorized: commandAuthorized
3342
+ },
3387
3343
  groupHistory: visibleGroupHistory,
3388
3344
  groupMemberRoster: params.groupMemberNames.get(params.groupHistoryKey),
3389
3345
  groupSystemPrompt: conversationSystemPrompt,
@@ -3811,9 +3767,8 @@ function resolveWebMonitorConfigSnapshot(params) {
3811
3767
  groupAllowFrom: account.groupAllowFrom,
3812
3768
  groupPolicy: account.groupPolicy,
3813
3769
  textChunkLimit: account.textChunkLimit,
3814
- chunkMode: account.chunkMode,
3770
+ streaming: account.streaming,
3815
3771
  mediaMaxMb: account.mediaMaxMb,
3816
- blockStreaming: account.blockStreaming,
3817
3772
  groups: account.groups
3818
3773
  }
3819
3774
  }
@@ -3928,10 +3883,12 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3928
3883
  });
3929
3884
  const shouldDebounce = (msg) => {
3930
3885
  const normalized = normalizeWebInboundMessage(msg);
3931
- if (normalized.payload.media?.path || normalized.payload.media?.type) return false;
3932
- if (normalized.payload.location) return false;
3933
- if (normalized.quote?.id || normalized.quote?.body) return false;
3934
- return !isControlCommandMessage(normalized.payload.commandBody ?? normalized.payload.body, cfg);
3886
+ return shouldDebounceTextInbound({
3887
+ text: normalized.payload.commandBody ?? normalized.payload.body,
3888
+ cfg,
3889
+ hasMedia: Boolean(normalized.payload.media?.path || normalized.payload.media?.type),
3890
+ allowDebounce: !(normalized.payload.location || normalized.quote?.id || normalized.quote?.body)
3891
+ });
3935
3892
  };
3936
3893
  let connection;
3937
3894
  try {
@@ -0,0 +1,2 @@
1
+ import "./normalize-target-DSZnHkKF.js";
2
+ export {};
@@ -1,4 +1,4 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
3
3
  //#region extensions/whatsapp/src/normalize-target.ts
4
4
  const WHATSAPP_USER_JID_RE = /^(\d+)(?::\d+)?@s\.whatsapp\.net$/i;
@@ -34,11 +34,20 @@ function isWhatsAppUserTarget(value) {
34
34
  }
35
35
  function extractUserJidPhone(jid) {
36
36
  const userMatch = jid.match(WHATSAPP_USER_JID_RE);
37
- if (userMatch) return userMatch[1];
37
+ if (userMatch) {
38
+ const phone = userMatch[1];
39
+ return phone ? phone : null;
40
+ }
38
41
  const legacyUserMatch = jid.match(WHATSAPP_LEGACY_USER_JID_RE);
39
- if (legacyUserMatch) return legacyUserMatch[1];
42
+ if (legacyUserMatch) {
43
+ const phone = legacyUserMatch[1];
44
+ return phone ? phone : null;
45
+ }
40
46
  const lidMatch = jid.match(WHATSAPP_LID_RE);
41
- if (lidMatch) return lidMatch[1];
47
+ if (lidMatch) {
48
+ const phone = lidMatch[1];
49
+ return phone ? phone : null;
50
+ }
42
51
  return null;
43
52
  }
44
53
  function normalizeWhatsAppTarget(value) {
@@ -67,12 +76,7 @@ function normalizeWhatsAppMessagingTarget(raw) {
67
76
  return normalizeWhatsAppTarget(trimmed) ?? void 0;
68
77
  }
69
78
  function normalizeWhatsAppAllowFromEntries(allowFrom) {
70
- const seen = /* @__PURE__ */ new Set();
71
- return allowFrom.map((entry) => String(entry).trim()).filter((entry) => Boolean(entry)).map(normalizeWhatsAppAllowFromEntry).filter((entry) => Boolean(entry)).filter((entry) => {
72
- if (seen.has(entry)) return false;
73
- seen.add(entry);
74
- return true;
75
- });
79
+ return uniqueStrings(normalizeStringEntries(allowFrom).map(normalizeWhatsAppAllowFromEntry).filter((entry) => Boolean(entry)));
76
80
  }
77
81
  function normalizeWhatsAppAllowFromEntry(entry) {
78
82
  if (entry === "*") return entry;