@openclaw/whatsapp 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 (75) hide show
  1. package/dist/{accounts-BX2fZUyR.js → accounts-CiKHiuEe.js} +10 -27
  2. package/dist/{action-runtime-C075e6OP.js → action-runtime-CRZLhN3f.js} +2 -2
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/agent-tools-api.js +3 -3
  6. package/dist/api.js +10 -10
  7. package/dist/{approval-handler.runtime-CljxfPJz.js → approval-handler.runtime-C2cpkbpY.js} +8 -4
  8. package/dist/{approval-reactions-Jyr1JaAK.js → approval-reactions-DrZe-NmN.js} +7 -5
  9. package/dist/auth-presence.js +1 -1
  10. package/dist/{auth-store-CtpBqH5S.js → auth-store-ZWaclAbd.js} +8 -6
  11. package/dist/channel-config-api.js +1 -1
  12. package/dist/{channel-C4FMOZTE.js → channel-dwfgnadr.js} +56 -66
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel-react-action-Bhp2BYRr.js → channel-react-action-CHeJSG9F.js} +3 -3
  15. package/dist/{channel.runtime-CrahUvcb.js → channel.runtime-Bn3K5Ygb.js} +4 -4
  16. package/dist/{channel.setup-CGcBvnpP.js → channel.setup-DckQm9ma.js} +1 -1
  17. package/dist/{config-schema-BQP5YCeK.js → config-schema-BYA6e_Ii.js} +6 -4
  18. package/dist/{connection-controller-CIYuBJrs.js → connection-controller-DNp6U1NN.js} +2 -2
  19. package/dist/{constants-HU41RHGI.js → constants-C7m81BNe.js} +1 -1
  20. package/dist/{creds-files-W6B3EMZ8.js → creds-files-Drg3usj1.js} +1 -1
  21. package/dist/{directory-config-P7_3U73Z.js → directory-config-CoWFw1zn.js} +3 -3
  22. package/dist/directory-contract-api.js +1 -1
  23. package/dist/doctor-BPb-SjdE.js +65 -0
  24. package/dist/doctor-contract-api.js +21 -3
  25. package/dist/{group-session-key-DmFc_Cqi.js → group-session-key-BmVDSPr1.js} +11 -12
  26. package/dist/light-runtime-api.js +1 -1
  27. package/dist/{login-BkpKpndm.js → login-6j2iP9nR.js} +6 -5
  28. package/dist/{login-qr-DY4VFgOq.js → login-qr-DRFgI6k1.js} +9 -7
  29. package/dist/login-qr-runtime.js +1 -1
  30. package/dist/{monitor-C_Nw23PZ.js → monitor-Bc6DC5jl.js} +189 -156
  31. package/dist/{question-reactions-D3eAeB0i.js → question-reactions-bbe0Qnjn.js} +1 -1
  32. package/dist/runtime-api.js +9 -9
  33. package/dist/{send-DLMUc2uZ.js → send-BDDGPxav.js} +53 -28
  34. package/dist/{send-api-BtK45TmZ.js → send-api-C_Zn4Ij-.js} +48 -48
  35. package/dist/{setup-finalize-C1nij8NS.js → setup-finalize-gLmc3FTV.js} +7 -6
  36. package/dist/setup-plugin-api.js +1 -1
  37. package/dist/{setup-surface-CjjqIxQP.js → setup-surface-twIi4TdN.js} +3 -3
  38. package/dist/{shared-BekHi69d.js → shared-ByeKDmh0.js} +13 -8
  39. package/dist/{socket-close-2T0_jZTN.js → socket-close-B3X9uerq.js} +10 -7
  40. package/dist/{targets-runtime-TMk0lGuy.js → targets-runtime-RBjx3pg_.js} +1 -2
  41. package/dist/{text-runtime-CaKLhowd.js → text-runtime-DyfmcATV.js} +1 -1
  42. package/node_modules/baileys/node_modules/process-warning/.github/dependabot.yml +42 -2
  43. package/node_modules/baileys/node_modules/process-warning/.github/workflows/ci.yml +12 -1
  44. package/node_modules/baileys/node_modules/process-warning/.github/workflows/lock-threads.yml +19 -0
  45. package/node_modules/baileys/node_modules/process-warning/LICENSE +1 -1
  46. package/node_modules/baileys/node_modules/process-warning/README.md +41 -2
  47. package/node_modules/baileys/node_modules/process-warning/index.js +85 -17
  48. package/node_modules/baileys/node_modules/process-warning/package.json +6 -7
  49. package/node_modules/baileys/node_modules/process-warning/test/emit-once-only.test.js +3 -3
  50. package/node_modules/baileys/node_modules/process-warning/test/emit-reset.test.js +4 -4
  51. package/node_modules/baileys/node_modules/process-warning/test/emit-unlimited.test.js +2 -2
  52. package/node_modules/baileys/node_modules/process-warning/test/spy-warning.test.js +143 -0
  53. package/node_modules/baileys/node_modules/process-warning/types/index.d.ts +15 -1
  54. package/node_modules/baileys/node_modules/process-warning/types/index.tst.ts +48 -0
  55. package/node_modules/baileys/node_modules/ws/lib/permessage-deflate.js +3 -1
  56. package/node_modules/baileys/node_modules/ws/package.json +1 -1
  57. package/node_modules/typebox/build/format/email.mjs +1 -1
  58. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
  59. package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
  60. package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
  61. package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
  62. package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
  63. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  64. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  65. package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
  66. package/node_modules/typebox/build/system/memory/create.mjs +3 -4
  67. package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
  68. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  69. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  70. package/node_modules/typebox/build/system/memory/update.mjs +3 -2
  71. package/node_modules/typebox/package.json +1 -1
  72. package/openclaw.plugin.json +0 -1
  73. package/package.json +7 -6
  74. package/dist/doctor-CwzVXKoE.js +0 -10
  75. package/node_modules/baileys/node_modules/process-warning/types/index.test-d.ts +0 -36
@@ -1,23 +1,23 @@
1
- import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BX2fZUyR.js";
2
- import { c as normalizeWhatsAppPayloadTextPreservingIndentation, d as listWhatsAppSendResultMessageIds, f as mergeWhatsAppAcceptedSendError, g as withWhatsAppLogicalDeliveryActivity, h as rememberWhatsAppPartialSend, l as prepareWhatsAppOutboundMedia, m as rememberWhatsAppAcceptedSend, o as normalizeWhatsAppOutboundPayload, p as normalizeWhatsAppSendResult, r as sendReactionWhatsApp, v as resolveWhatsAppReactionLevel } from "./send-DLMUc2uZ.js";
1
+ import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CiKHiuEe.js";
2
+ import { c as normalizeWhatsAppPayloadTextPreservingIndentation, d as listWhatsAppSendResultMessageIds, f as mergeWhatsAppAcceptedSendError, g as withWhatsAppLogicalDeliveryActivity, h as rememberWhatsAppPartialSend, l as prepareWhatsAppOutboundMedia, m as rememberWhatsAppAcceptedSend, o as normalizeWhatsAppOutboundPayload, p as normalizeWhatsAppSendResult, r as sendReactionWhatsApp, v as resolveWhatsAppReactionLevel } from "./send-BDDGPxav.js";
3
3
  import { i as getWhatsAppRuntime } from "./runtime-BLlToOi6.js";
4
4
  import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-DG0tuYuw.js";
5
- import { a as markdownToWhatsAppChunks, c as toWhatsappJid, l as toWhatsappJidWithLid, n as isSelfChatMode, o as resolveEquivalentWhatsAppDirectChatJids, r as jidToE164, s as resolveJidToE164 } from "./targets-runtime-TMk0lGuy.js";
6
- import { t as normalizeE164 } from "./text-runtime-CaKLhowd.js";
7
- import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-DmFc_Cqi.js";
8
- import { C as getMentionIdentities, D as getSenderIdentity, E as getSelfIdentity, O as identitiesOverlap, S as getComparableIdentityValues, T as getReplyContext, g as readWebSelfId, k as resolveComparableIdentity, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision, w as getPrimaryIdentityId } from "./auth-store-CtpBqH5S.js";
5
+ import { a as markdownToWhatsAppChunks, c as toWhatsappJid, l as toWhatsappJidWithLid, n as isSelfChatMode, o as resolveEquivalentWhatsAppDirectChatJids, r as jidToE164, s as resolveJidToE164 } from "./targets-runtime-RBjx3pg_.js";
6
+ import { t as normalizeE164 } from "./text-runtime-DyfmcATV.js";
7
+ import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-BmVDSPr1.js";
8
+ import { C as getMentionIdentities, D as getSenderIdentity, E as getSelfIdentity, O as identitiesOverlap, S as getComparableIdentityValues, T as getReplyContext, g as readWebSelfId, k as resolveComparableIdentity, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision, w as getPrimaryIdentityId } from "./auth-store-ZWaclAbd.js";
9
9
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
10
- import { a as waitForWaConnection, c as isWhatsAppSocketOperationTimeoutError, d as withWhatsAppSocketOperationTimeout, l as resolveWhatsAppSocketOperationTimeoutMs, r as createWaSocket, s as createWhatsAppSocketOperationTimeoutAdapter, u as resolveWhatsAppSocketTiming } from "./socket-close-2T0_jZTN.js";
10
+ import { a as waitForWaConnection, c as isWhatsAppSocketOperationTimeoutError, d as withWhatsAppSocketOperationTimeout, l as resolveWhatsAppSocketOperationTimeoutMs, r as createWaSocket, s as createWhatsAppSocketOperationTimeoutAdapter, u as resolveWhatsAppSocketTiming } from "./socket-close-B3X9uerq.js";
11
11
  import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
12
12
  import { t as BufferJSON } from "./session.runtime-Nsqaehbd.js";
13
- import { _ as resolveInboundMediaMimetype, a as addWhatsAppImagePreviewFields, c as extractContextInfo, d as extractMediaKind, f as extractMentionedJids, g as projectWhatsAppInboundMessage, h as hasInboundUserContent, i as resolveWhatsAppOutboundMentions, l as extractExternalAdReplyContext, n as addWhatsAppOutboundMentionsToContent, o as describeReplyContext, p as extractText, r as mayContainWhatsAppOutboundMention, s as extractContactContext, t as createWebSendApi, u as extractLocationData } from "./send-api-BtK45TmZ.js";
13
+ import { _ as resolveInboundMediaMimetype, a as addWhatsAppImagePreviewFields, c as extractContextInfo, d as extractMediaKind, f as extractMentionedJids, g as projectWhatsAppInboundMessage, h as hasInboundUserContent, i as resolveWhatsAppOutboundMentions, l as extractExternalAdReplyContext, n as addWhatsAppOutboundMentionsToContent, o as describeReplyContext, p as extractText, r as mayContainWhatsAppOutboundMention, s as extractContactContext, t as createWebSendApi, u as extractLocationData } from "./send-api-C_Zn4Ij-.js";
14
14
  import { t as resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy-DeSRFXEV.js";
15
- 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-CIYuBJrs.js";
16
- import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-Jyr1JaAK.js";
17
- import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-D3eAeB0i.js";
18
- import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
19
- import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
15
+ 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-DNp6U1NN.js";
16
+ import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-DrZe-NmN.js";
17
+ import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-bbe0Qnjn.js";
18
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
20
19
  import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
20
+ import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
21
21
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
22
22
  import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
23
23
  import { createSubsystemLogger, getChildLogger, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
@@ -26,7 +26,6 @@ import { createSubsystemLogger as createSubsystemLogger$1, defaultRuntime, forma
26
26
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
27
27
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
28
28
  import { buildChannelInboundEventContext, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, readAgentRunTerminalOutcome, resolveInboundSessionEnvelopeContext, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
29
- import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
30
29
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
31
30
  import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
32
31
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -35,17 +34,18 @@ import { DisconnectReason, downloadMediaMessage, isHostedLidUser, isHostedPnUser
35
34
  import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
36
35
  import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
37
36
  import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
37
+ import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
38
38
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
39
39
  import { createHash } from "node:crypto";
40
40
  import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
41
41
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
42
42
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
43
- import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
44
43
  import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
45
44
  import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
46
45
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
47
46
  import { fanInChannelIngressLifecycles, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
48
47
  import { getSessionEntry, patchSessionEntry, resolveGroupSessionKey, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
48
+ import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
49
49
  import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
50
50
  import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
51
51
  import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
@@ -357,7 +357,7 @@ function createWhatsAppIngressMonitor(params) {
357
357
  deliver: (admission, lifecycle) => params.dispatch(admission, lifecycle),
358
358
  pollIntervalMs: params.pollIntervalMs,
359
359
  retention: {
360
- completedTtlMs: 10080 * 60 * 1e3,
360
+ completedTtlMs: 6048e5,
361
361
  completedMaxEntries: 5e3,
362
362
  failedMaxEntries: 450
363
363
  },
@@ -381,7 +381,7 @@ function createWhatsAppIngressMonitor(params) {
381
381
  }
382
382
  //#endregion
383
383
  //#region extensions/whatsapp/src/inbound/group-metadata-cache.ts
384
- const GROUP_META_TTL_MS = 300 * 1e3;
384
+ const GROUP_META_TTL_MS = 3e5;
385
385
  const WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES = 500;
386
386
  function resolveGroupMetadataExpiresAt(nowRaw = Date.now()) {
387
387
  const now = asDateTimestampMs(nowRaw);
@@ -498,10 +498,11 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
498
498
  text,
499
499
  mentionedJids: []
500
500
  };
501
+ const meta = await get(jid);
501
502
  return resolveWhatsAppOutboundMentions({
502
503
  chatJid: jid,
503
504
  text,
504
- participants: (await get(jid)).mentionParticipants
505
+ participants: meta.mentionParticipants
505
506
  });
506
507
  };
507
508
  const applyOutboundMentions = async (jid, content) => {
@@ -528,10 +529,10 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
528
529
  const listen = (event, listener) => {
529
530
  detachListeners.push(params.listen(event, listener));
530
531
  };
531
- listen("groups.upsert", ((groups) => {
532
+ listen("groups.upsert", (groups) => {
532
533
  for (const group of groups) if (group.id) rememberFullUpdate(group.id, group);
533
- }));
534
- listen("groups.update", ((updates) => {
534
+ });
535
+ listen("groups.update", (updates) => {
535
536
  for (const update of updates) {
536
537
  if (!update.id) continue;
537
538
  if (typeof update.subject === "string" && Array.isArray(update.participants)) {
@@ -540,10 +541,10 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
540
541
  }
541
542
  forgetFullMetadata(update.id);
542
543
  }
543
- }));
544
- listen("group-participants.update", ((update) => {
544
+ });
545
+ listen("group-participants.update", (update) => {
545
546
  forgetFullMetadata(update.id);
546
- }));
547
+ });
547
548
  (async () => {
548
549
  try {
549
550
  const groups = await params.sock.groupFetchAllParticipating();
@@ -577,13 +578,7 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
577
578
  //#region extensions/whatsapp/src/inbound/lifecycle.ts
578
579
  function attachEmitterListener(emitter, event, listener) {
579
580
  emitter.on(event, listener);
580
- return () => {
581
- if (typeof emitter.off === "function") {
582
- emitter.off(event, listener);
583
- return;
584
- }
585
- if (typeof emitter.removeListener === "function") emitter.removeListener(event, listener);
586
- };
581
+ return () => emitter.off(event, listener);
587
582
  }
588
583
  function closeInboundMonitorSocket(sock) {
589
584
  if (typeof sock.end === "function") {
@@ -605,7 +600,6 @@ function resolveWhatsAppIngressLifecycle(message) {
605
600
  //#endregion
606
601
  //#region extensions/whatsapp/src/inbound/message-debounce.ts
607
602
  function createWhatsAppInboundMessageDebouncer(options) {
608
- const debounceMs = Math.max(0, Math.trunc(options.debounceMs ?? 0));
609
603
  const pendingKeys = /* @__PURE__ */ new Map();
610
604
  const activeFlushes = /* @__PURE__ */ new Set();
611
605
  const workWaiters = /* @__PURE__ */ new Set();
@@ -635,7 +629,8 @@ function createWhatsAppInboundMessageDebouncer(options) {
635
629
  return timestampDiff !== 0 ? timestampDiff : (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
636
630
  });
637
631
  const debouncer = createInboundDebouncer({
638
- debounceMs,
632
+ debounceMs: options.resolveDebounceMs(),
633
+ resolveDebounceMs: (entry) => entry.debounceMs,
639
634
  buildKey: (msg) => msg.debounceKey ?? buildKey(msg),
640
635
  shouldDebounce,
641
636
  onFlush: (entries, createFlush) => {
@@ -701,11 +696,15 @@ function createWhatsAppInboundMessageDebouncer(options) {
701
696
  },
702
697
  onError: options.onError
703
698
  });
704
- const enqueue = async (message) => {
699
+ const enqueue = async (input) => {
700
+ const message = {
701
+ ...input,
702
+ debounceMs: options.resolveDebounceMs()
703
+ };
705
704
  const key = buildKey(message);
706
705
  if (key) {
707
706
  message.debounceKey = key;
708
- if (debounceMs > 0 && shouldDebounce(message)) {
707
+ if (message.debounceMs > 0 && shouldDebounce(message)) {
709
708
  message.debounceKeyTracked = true;
710
709
  trackKey(key);
711
710
  options.onPendingWorkChanged();
@@ -748,26 +747,25 @@ function createWhatsAppInboundMessageDebouncer(options) {
748
747
  function unwrapMessage(message) {
749
748
  return normalizeMessageContent$1(message);
750
749
  }
751
- async function downloadInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024, normalizedMessage) {
750
+ async function downloadInboundMedia(msg, sock, maxBytes = 52428800, normalizedMessage) {
752
751
  const message = normalizedMessage ?? unwrapMessage(msg.message);
753
752
  if (!message) return;
754
753
  const mimetype = resolveInboundMediaMimetype(message);
755
754
  const fileName = message.documentMessage?.fileName ?? void 0;
756
755
  if (!message.imageMessage && !message.videoMessage && !message.ptvMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
756
+ const stream = await downloadMediaMessage(msg, "stream", {}, {
757
+ reuploadRequest: sock.updateMediaMessage,
758
+ logger: sock.logger
759
+ });
757
760
  return {
758
- saved: await saveMediaStream(await downloadMediaMessage(msg, "stream", {}, {
759
- reuploadRequest: sock.updateMediaMessage,
760
- logger: sock.logger
761
- }), mimetype, "inbound", maxBytes, fileName).catch((err) => {
762
- if (err instanceof Error && /Media exceeds/i.test(err.message)) throw new Error(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
763
- throw err;
764
- }),
761
+ saved: await saveMediaStream(stream, mimetype, "inbound", maxBytes, fileName),
765
762
  mimetype,
766
763
  fileName
767
764
  };
768
765
  }
769
- async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024) {
770
- const contextInfo = extractContextInfo(unwrapMessage(msg.message));
766
+ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 52428800) {
767
+ const message = unwrapMessage(msg.message);
768
+ const contextInfo = extractContextInfo(message);
771
769
  if (!contextInfo?.quotedMessage) return;
772
770
  const quotedMessage = contextInfo.quotedMessage;
773
771
  const self = sock.user;
@@ -792,7 +790,9 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
792
790
  const inboundMediaLogger = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
793
791
  const MAX_MEDIA_ERROR_MESSAGE_CHARS = 256;
794
792
  function sanitizeMediaErrorMessage(error) {
795
- return truncateUtf16Safe(redactToolPayloadText(error instanceof Error ? error.message : String(error)).replace(/https?:\/\/\S+/giu, "[redacted-url]").replace(/\b[^@\s]+@[a-z][a-z\d.-]*\b/giu, "[redacted-jid]").replace(/\+?\d[\d ().-]{6,}\d/gu, "[redacted-phone]").split("\n", 1)[0]?.trim() || "unknown error", MAX_MEDIA_ERROR_MESSAGE_CHARS);
793
+ const rawMessage = error instanceof Error ? error.message : String(error);
794
+ const redacted = redactToolPayloadText(rawMessage).replace(/https?:\/\/\S+/giu, "[redacted-url]").replace(/\b[^@\s]+@[a-z][a-z\d.-]*\b/giu, "[redacted-jid]").replace(/\+?\d[\d ().-]{6,}\d/gu, "[redacted-phone]");
795
+ return truncateUtf16Safe(redacted.split("\n", 1)[0]?.trim() || "unknown error", MAX_MEDIA_ERROR_MESSAGE_CHARS);
796
796
  }
797
797
  function logMediaMaterializationFailure(params) {
798
798
  const errorClass = params.error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u.test(params.error.name) ? params.error.name : "Error";
@@ -1001,7 +1001,7 @@ async function checkInboundAccessControl(params) {
1001
1001
  //#endregion
1002
1002
  //#region extensions/whatsapp/src/inbound/dedupe.ts
1003
1003
  const recentOutboundMessages = createDedupeCache({
1004
- ttlMs: 20 * 6e4,
1004
+ ttlMs: 12e5,
1005
1005
  maxSize: 5e3
1006
1006
  });
1007
1007
  function buildMessageKey(params) {
@@ -1182,7 +1182,11 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
1182
1182
  await maybeMarkInboundAsRead(target);
1183
1183
  };
1184
1184
  const messageDebouncer = createWhatsAppInboundMessageDebouncer({
1185
- debounceMs: options.debounceMs,
1185
+ resolveDebounceMs: () => resolveInboundDebounceMs({
1186
+ cfg: options.loadConfig?.() ?? options.cfg,
1187
+ channel: "whatsapp",
1188
+ overrideMs: options.debounceMs
1189
+ }),
1186
1190
  onMessage: options.onMessage,
1187
1191
  shouldDebounce: options.shouldDebounce,
1188
1192
  markRead: maybeMarkInboundAsRead,
@@ -1215,11 +1219,13 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
1215
1219
  };
1216
1220
  const reply = async (text, optionsResult) => {
1217
1221
  const resolved = await groupMetadata.resolveOutboundMentions(chatJid, text);
1218
- return normalizeWhatsAppSendResult(await sendTrackedMessage(chatJid, addWhatsAppOutboundMentionsToContent({ text: resolved.text }, resolved.mentionedJids), optionsResult), "text");
1222
+ const result = await sendTrackedMessage(chatJid, addWhatsAppOutboundMentionsToContent({ text: resolved.text }, resolved.mentionedJids), optionsResult);
1223
+ return normalizeWhatsAppSendResult(result, "text");
1219
1224
  };
1220
1225
  const sendMedia = async (payload, optionsValue) => {
1221
1226
  const previewPayload = await addWhatsAppImagePreviewFields(payload);
1222
- return normalizeWhatsAppSendResult(await sendTrackedMessage(chatJid, await groupMetadata.applyOutboundMentions(chatJid, previewPayload), optionsValue), "media");
1227
+ const result = await sendTrackedMessage(chatJid, await groupMetadata.applyOutboundMentions(chatJid, previewPayload), optionsValue);
1228
+ return normalizeWhatsAppSendResult(result, "media");
1223
1229
  };
1224
1230
  const timestamp = inbound.messageTimestampMs;
1225
1231
  const mentionedJids = enriched.mentionedJids;
@@ -1439,14 +1445,15 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
1439
1445
  finishPreparation(void 0);
1440
1446
  const inbound = await normalizeInboundMessage(msg);
1441
1447
  if (inbound) await maybeMarkNonSelfChatReadReceipt(inbound, buildReadReceiptTarget(inbound));
1442
- } else if (result.kind === "durable" && result.queueResult.kind === "accepted") if (skipRecentOutboundEcho) finishPreparation(null);
1443
- else try {
1444
- finishPreparation(await normalizeInboundMessage(msg), true);
1445
- } catch (error) {
1446
- finishPreparation(void 0);
1447
- inboundLogger.warn({ error: formatError(error) }, "failed preparing WhatsApp inbound identity; durable drain will normalize again");
1448
- }
1449
- else finishPreparation(void 0);
1448
+ } else if (result.kind === "durable" && result.queueResult.kind === "accepted") {
1449
+ if (skipRecentOutboundEcho) finishPreparation(null);
1450
+ else try {
1451
+ finishPreparation(await normalizeInboundMessage(msg), true);
1452
+ } catch (error) {
1453
+ finishPreparation(void 0);
1454
+ inboundLogger.warn({ error: formatError(error) }, "failed preparing WhatsApp inbound identity; durable drain will normalize again");
1455
+ }
1456
+ } else finishPreparation(void 0);
1450
1457
  }
1451
1458
  };
1452
1459
  const handleMessagesUpsertEvent = (upsert) => {
@@ -1514,7 +1521,7 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
1514
1521
  //#region extensions/whatsapp/src/inbound/socket-session.ts
1515
1522
  const LOGGED_OUT_STATUS = DisconnectReason.loggedOut;
1516
1523
  const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
1517
- const BAILEYS_MESSAGE_TTL_MS = 600 * 1e3;
1524
+ const BAILEYS_MESSAGE_TTL_MS = 6e5;
1518
1525
  function isDirectUserJid(jid) {
1519
1526
  return /^(\d+)(?::\d+)?@(s\.whatsapp\.net|c\.us|lid|hosted|hosted\.lid)$/i.test(jid.trim());
1520
1527
  }
@@ -2410,7 +2417,11 @@ async function applyGroupGating(params) {
2410
2417
  const conversationGroupPolicy = inboundPolicy.resolveConversationGroupPolicy(conversationId);
2411
2418
  if (conversationGroupPolicy.allowlistEnabled && !conversationGroupPolicy.allowed) {
2412
2419
  const accountId = inboundPolicy.account.accountId;
2413
- if (shouldWarnForGroupDrop(`${accountId}:${conversationId}`)) {
2420
+ if (shouldWarnForGroupDrop(JSON.stringify([
2421
+ accountId,
2422
+ conversationId,
2423
+ "group registry"
2424
+ ]))) {
2414
2425
  const groupsPath = resolveWhatsAppGroupsConfigPath({
2415
2426
  cfg: params.cfg,
2416
2427
  accountId
@@ -2470,11 +2481,13 @@ async function applyGroupGating(params) {
2470
2481
  conversationId
2471
2482
  }) !== "always";
2472
2483
  const replyContext = getReplyContext(params.msg, params.authDir);
2484
+ const implicitReplyToSelf = params.selfChatMode === true && identitiesOverlap(self, sender);
2485
+ const implicitMentionKinds = implicitMentionKindWhen("quoted_bot", !implicitReplyToSelf && identitiesOverlap(self, replyContext?.sender));
2473
2486
  const mentionDecision = resolveInboundMentionDecision({
2474
2487
  facts: {
2475
2488
  canDetectMention: true,
2476
2489
  wasMentioned,
2477
- implicitMentionKinds: implicitMentionKindWhen("quoted_bot", !(params.selfChatMode === true && identitiesOverlap(self, sender)) && identitiesOverlap(self, replyContext?.sender))
2490
+ implicitMentionKinds
2478
2491
  },
2479
2492
  policy: {
2480
2493
  isGroup: true,
@@ -2497,6 +2510,22 @@ async function applyGroupGating(params) {
2497
2510
  needsMentionText: true
2498
2511
  };
2499
2512
  }
2513
+ const accountId = inboundPolicy.account.accountId;
2514
+ if (shouldWarnForGroupDrop(JSON.stringify([
2515
+ accountId,
2516
+ conversationId,
2517
+ "no mention"
2518
+ ]))) {
2519
+ const groupsPath = resolveWhatsAppGroupsConfigPath({
2520
+ cfg: params.cfg,
2521
+ accountId
2522
+ });
2523
+ params.replyLogger.warn({
2524
+ conversationId,
2525
+ accountId,
2526
+ groupsPath
2527
+ }, `WhatsApp group ${conversationId}: skipping messages without a mention. Mention patterns can be derived from the agent identity name. Use /activation always for this session, or set ${groupsPath}[${JSON.stringify(conversationId)}].requireMention=false for the default. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.`);
2528
+ }
2500
2529
  const pendingHistoryBody = params.mentionText === void 0 ? void 0 : formatAudioTranscriptForAgent(params.mentionText);
2501
2530
  return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${conversationId}: ${mentionMsg.payload.body}`, pendingHistoryBody);
2502
2531
  }
@@ -2652,7 +2681,8 @@ function isLikelyWhatsAppCryptoError(reason) {
2652
2681
  if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
2653
2682
  return Object.prototype.toString.call(value);
2654
2683
  };
2655
- const haystack = normalizeLowercaseStringOrEmpty(reason instanceof Error ? `${reason.message}\n${reason.stack ?? ""}` : formatReason(reason));
2684
+ const raw = reason instanceof Error ? `${reason.message}\n${reason.stack ?? ""}` : formatReason(reason);
2685
+ const haystack = normalizeLowercaseStringOrEmpty(raw);
2656
2686
  if (!(haystack.includes("unsupported state or unable to authenticate data") || haystack.includes("bad mac"))) return false;
2657
2687
  return haystack.includes("baileys") || haystack.includes("noise-handler") || haystack.includes("aesdecryptgcm");
2658
2688
  }
@@ -2717,6 +2747,11 @@ async function deliverWebReplyInActivityScope(params) {
2717
2747
  const isGroupConversation = transport.conversationKind === "group";
2718
2748
  const replyStarted = Date.now();
2719
2749
  const sendResults = [];
2750
+ const acceptedMediaUrls = /* @__PURE__ */ new Set();
2751
+ const recordMediaAccepted = (mediaUrl) => {
2752
+ acceptedMediaUrls.add(mediaUrl);
2753
+ params.onMediaAccepted?.(mediaUrl);
2754
+ };
2720
2755
  const finishDelivery = () => {
2721
2756
  const receipt = createWhatsAppReplyDeliveryReceipt(sendResults);
2722
2757
  return {
@@ -2725,20 +2760,22 @@ async function deliverWebReplyInActivityScope(params) {
2725
2760
  providerAccepted: sendResults.some((result) => result.providerAccepted)
2726
2761
  };
2727
2762
  };
2728
- const preserveAcceptedDeliveryError = (error, kind) => {
2763
+ const preserveAcceptedDeliveryError = (error, kind, mediaUrl) => {
2729
2764
  const sendKind = kind ?? sendResults[0]?.kind ?? "text";
2730
- if (isChannelPartialDeliveryError(error)) rememberWhatsAppPartialSend({
2731
- error,
2732
- kind: sendKind,
2733
- results: sendResults
2734
- });
2765
+ if (isChannelPartialDeliveryError(error)) {
2766
+ if (rememberWhatsAppPartialSend({
2767
+ error,
2768
+ kind: sendKind,
2769
+ results: sendResults
2770
+ }) && mediaUrl) recordMediaAccepted(mediaUrl);
2771
+ }
2735
2772
  return mergeWhatsAppAcceptedSendError({
2736
2773
  error,
2737
2774
  kind: sendKind,
2738
2775
  results: sendResults
2739
2776
  });
2740
2777
  };
2741
- const rememberSendResult = (result) => {
2778
+ const rememberSendResult = (result, mediaUrl) => {
2742
2779
  if (!result) return;
2743
2780
  try {
2744
2781
  rememberWhatsAppAcceptedSend({
@@ -2749,6 +2786,8 @@ async function deliverWebReplyInActivityScope(params) {
2749
2786
  } catch (error) {
2750
2787
  if (sendResults.some((accepted) => accepted.providerAccepted)) throw preserveAcceptedDeliveryError(error, result.kind);
2751
2788
  throw error;
2789
+ } finally {
2790
+ if (mediaUrl && sendResults.includes(result)) recordMediaAccepted(mediaUrl);
2752
2791
  }
2753
2792
  };
2754
2793
  if (isReasoningReplyPayload(replyResult)) {
@@ -2773,7 +2812,7 @@ async function deliverWebReplyInActivityScope(params) {
2773
2812
  media: cached?.media
2774
2813
  });
2775
2814
  };
2776
- const sendWithRetry = async (fn, label, kind) => {
2815
+ const sendWithRetry = async (fn, label, kind, mediaUrl) => {
2777
2816
  try {
2778
2817
  return await sendWhatsAppOutboundWithRetry({
2779
2818
  send: fn,
@@ -2782,7 +2821,7 @@ async function deliverWebReplyInActivityScope(params) {
2782
2821
  }
2783
2822
  });
2784
2823
  } catch (error) {
2785
- if (isChannelPartialDeliveryError(error) || sendResults.some((result) => result.providerAccepted)) throw preserveAcceptedDeliveryError(error, kind);
2824
+ if (isChannelPartialDeliveryError(error) || sendResults.some((result) => result.providerAccepted)) throw preserveAcceptedDeliveryError(error, kind, mediaUrl);
2786
2825
  throw error;
2787
2826
  }
2788
2827
  };
@@ -2815,52 +2854,39 @@ async function deliverWebReplyInActivityScope(params) {
2815
2854
  }
2816
2855
  const remainingText = [...textChunks];
2817
2856
  const leadingCaption = remainingText.shift() || "";
2818
- let acceptedIdsBeforeCurrentMedia = /* @__PURE__ */ new Set();
2819
2857
  await sendMediaWithLeadingCaption({
2820
2858
  mediaUrls: mediaList,
2821
2859
  caption: leadingCaption,
2822
2860
  send: async ({ mediaUrl, caption }) => {
2823
- acceptedIdsBeforeCurrentMedia = new Set(sendResults.flatMap(listWhatsAppSendResultMessageIds));
2824
2861
  const media = await prepareWhatsAppOutboundMedia(await loadWebMedia(mediaUrl, {
2825
2862
  maxBytes: maxMediaBytes,
2826
2863
  localRoots: params.mediaLocalRoots
2827
2864
  }), mediaUrl);
2828
2865
  if (shouldLogVerbose()) {
2829
- logVerbose(`Web auto-reply media size: ${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB`);
2866
+ logVerbose(`Web auto-reply media size: ${(media.buffer.length / 1048576).toFixed(2)}MB`);
2830
2867
  logVerbose(`Web auto-reply media source: ${mediaUrl} (kind ${media.kind})`);
2831
2868
  }
2832
- if (media.kind === "image") {
2833
- const quote = getQuote();
2834
- rememberSendResult(await sendWithRetry(() => transport.sendMedia({
2835
- image: media.buffer,
2836
- caption,
2837
- mimetype: media.mimetype
2838
- }, quote), "media:image", "media"));
2839
- } else if (media.kind === "audio") {
2840
- const quote = getQuote();
2841
- rememberSendResult(await sendWithRetry(() => transport.sendMedia({
2842
- audio: media.buffer,
2843
- ptt: true,
2844
- mimetype: media.mimetype
2845
- }, quote), "media:audio", "media"));
2846
- if (caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text", "text"));
2847
- } else if (media.kind === "video") {
2848
- const quote = getQuote();
2849
- rememberSendResult(await sendWithRetry(() => transport.sendMedia({
2850
- video: media.buffer,
2851
- caption,
2852
- mimetype: media.mimetype
2853
- }, quote), "media:video", "media"));
2854
- } else {
2855
- const quote = getQuote();
2856
- rememberSendResult(await sendWithRetry(() => transport.sendMedia({
2857
- document: media.buffer,
2858
- fileName: media.fileName,
2859
- caption,
2860
- mimetype: media.mimetype
2861
- }, quote), "media:document", "media"));
2862
- }
2863
- whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
2869
+ const quote = getQuote();
2870
+ const mediaContent = media.kind === "image" ? {
2871
+ image: media.buffer,
2872
+ caption
2873
+ } : media.kind === "audio" ? {
2874
+ audio: media.buffer,
2875
+ ptt: true
2876
+ } : media.kind === "video" ? {
2877
+ video: media.buffer,
2878
+ caption
2879
+ } : {
2880
+ document: media.buffer,
2881
+ fileName: media.fileName,
2882
+ caption
2883
+ };
2884
+ rememberSendResult(await sendWithRetry(() => transport.sendMedia({
2885
+ ...mediaContent,
2886
+ mimetype: media.mimetype
2887
+ }, quote), `media:${media.kind}`, "media", mediaUrl), mediaUrl);
2888
+ if (media.kind === "audio" && caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text", "text"));
2889
+ whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / 1048576).toFixed(2)}MB)`);
2864
2890
  replyLogger.info({
2865
2891
  correlationId: transport.correlationId ?? newConnectionId(),
2866
2892
  connectionId: connectionId ?? null,
@@ -2874,7 +2900,7 @@ async function deliverWebReplyInActivityScope(params) {
2874
2900
  }, "auto-reply sent (media)");
2875
2901
  },
2876
2902
  onError: async ({ error, mediaUrl, caption, isFirst }) => {
2877
- if (sendResults.some((result) => listWhatsAppSendResultMessageIds(result).some((messageId) => !acceptedIdsBeforeCurrentMedia.has(messageId)))) throw preserveAcceptedDeliveryError(error);
2903
+ if (acceptedMediaUrls.has(mediaUrl)) throw preserveAcceptedDeliveryError(error);
2878
2904
  whatsappOutboundLog.error(`Failed sending web media to ${conversationId}: ${formatError(error)}`);
2879
2905
  replyLogger.warn({
2880
2906
  err: error,
@@ -3067,7 +3093,7 @@ function resolveWhatsAppDurableReplyToId(params) {
3067
3093
  }
3068
3094
  function resolveWhatsAppDeliverablePayload(payload, info) {
3069
3095
  if (payload.isReasoning === true || payload.isCompactionNotice === true) return null;
3070
- if (payload.isError === true) return null;
3096
+ if (payload.isError === true && info.kind !== "final") return null;
3071
3097
  if (info.kind === "tool") {
3072
3098
  if (!resolveSendableOutboundReplyParts(payload).hasMedia) return null;
3073
3099
  return {
@@ -3077,9 +3103,6 @@ function resolveWhatsAppDeliverablePayload(payload, info) {
3077
3103
  }
3078
3104
  return payload;
3079
3105
  }
3080
- function getWhatsAppPayloadMediaUrls(payload) {
3081
- return new Set(normalizeStringEntries([...Array.isArray(payload.mediaUrls) ? payload.mediaUrls : [], ...typeof payload.mediaUrl === "string" ? [payload.mediaUrl] : []]));
3082
- }
3083
3106
  function hasWhatsAppMediaUrlOverlap(left, right) {
3084
3107
  for (const url of left) if (right.has(url)) return true;
3085
3108
  return false;
@@ -3127,16 +3150,28 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
3127
3150
  return finalization;
3128
3151
  },
3129
3152
  flushNonDuplicateMedia: (mediaUrls) => flushWhere((pending) => !hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)),
3130
- dropDuplicateMedia(mediaUrls) {
3153
+ supersedeMedia(mediaUrl) {
3131
3154
  const flushResult = {
3132
3155
  delivered: 0,
3133
3156
  droppedDuplicateMedia: 0
3134
3157
  };
3135
3158
  const retained = [];
3136
- for (const pending of pendingMediaOnlyPayloads.splice(0)) if (hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)) {
3137
- pending.resolveFinalization(whatsAppReplyDeliveryVisibility(false));
3138
- flushResult.droppedDuplicateMedia += 1;
3139
- } else retained.push(pending);
3159
+ for (const pending of pendingMediaOnlyPayloads.splice(0)) {
3160
+ if (pending.mediaUrls.delete(mediaUrl)) {
3161
+ flushResult.droppedDuplicateMedia += 1;
3162
+ const mediaUrls = [...pending.mediaUrls];
3163
+ pending.payload = {
3164
+ ...pending.payload,
3165
+ mediaUrl: mediaUrls[0],
3166
+ mediaUrls
3167
+ };
3168
+ }
3169
+ if (pending.mediaUrls.size === 0) {
3170
+ pending.resolveFinalization(whatsAppReplyDeliveryVisibility(false));
3171
+ continue;
3172
+ }
3173
+ retained.push(pending);
3174
+ }
3140
3175
  pendingMediaOnlyPayloads.push(...retained);
3141
3176
  return flushResult;
3142
3177
  },
@@ -3145,7 +3180,7 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
3145
3180
  }
3146
3181
  function logWhatsAppMediaOnlyFlushResult(result) {
3147
3182
  if (!shouldLogVerbose$1()) return;
3148
- if (result.droppedDuplicateMedia > 0) logVerbose$1(`Dropped ${result.droppedDuplicateMedia} deferred media-only WhatsApp reply payload(s) superseded by captioned media`);
3183
+ if (result.droppedDuplicateMedia > 0) logVerbose$1(`Superseded ${result.droppedDuplicateMedia} deferred WhatsApp attachment(s) with accepted replacement media`);
3149
3184
  if (result.delivered > 0) logVerbose$1(`Flushed ${result.delivered} deferred media-only WhatsApp reply payload(s)`);
3150
3185
  }
3151
3186
  function resolveWhatsAppResponsePrefix(params) {
@@ -3207,7 +3242,8 @@ async function prepareWhatsAppInboundContext(params) {
3207
3242
  from: conversationId,
3208
3243
  sender: {
3209
3244
  id: params.sender.id ?? params.sender.e164,
3210
- name: params.sender.name
3245
+ name: params.sender.name,
3246
+ isSelf: params.msg.platform.fromMe === true
3211
3247
  },
3212
3248
  conversation: {
3213
3249
  kind: conversationKind,
@@ -3316,7 +3352,8 @@ function createWhatsAppReplyPlan(params) {
3316
3352
  didSendReply = true;
3317
3353
  if (shouldLogVerbose$1()) {
3318
3354
  const reply = resolveSendableOutboundReplyParts(payload);
3319
- logVerbose$1(`Reply body: ${payload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
3355
+ const preview = payload.text != null ? reply.text : "<media>";
3356
+ logVerbose$1(`Reply body: ${preview}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
3320
3357
  }
3321
3358
  };
3322
3359
  const deliverNormalizedPayload = async (normalizedDeliveryPayload, info, options) => {
@@ -3335,7 +3372,8 @@ function createWhatsAppReplyPlan(params) {
3335
3372
  replyLogger: params.replyLogger,
3336
3373
  connectionId: params.connectionId,
3337
3374
  skipLog: false,
3338
- tableMode
3375
+ tableMode,
3376
+ onMediaAccepted: options?.onMediaAccepted
3339
3377
  });
3340
3378
  } catch (error) {
3341
3379
  if (isWhatsAppVisibleDeliveryError(error) && !isChannelPartialDeliveryError(error)) throw createChannelPartialDeliveryError(error, {
@@ -3397,7 +3435,7 @@ function createWhatsAppReplyPlan(params) {
3397
3435
  } : normalizedOutboundPayload;
3398
3436
  const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
3399
3437
  if (!reply.hasMedia && !reply.text.trim()) return normalizedDeliveryPayload;
3400
- const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
3438
+ const mediaUrls = new Set(normalizedDeliveryPayload.mediaUrls);
3401
3439
  logWhatsAppMediaOnlyFlushResult(reply.hasMedia ? shouldDeferWhatsAppMediaOnlyPayload({
3402
3440
  info,
3403
3441
  mediaUrls,
@@ -3431,7 +3469,7 @@ function createWhatsAppReplyPlan(params) {
3431
3469
  const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
3432
3470
  if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
3433
3471
  if (!reply.hasMedia) return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { recordDelivery: false });
3434
- const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
3472
+ const mediaUrls = new Set(normalizedDeliveryPayload.mediaUrls);
3435
3473
  if (shouldDeferWhatsAppMediaOnlyPayload({
3436
3474
  info,
3437
3475
  mediaUrls,
@@ -3444,14 +3482,10 @@ function createWhatsAppReplyPlan(params) {
3444
3482
  payload: normalizedDeliveryPayload
3445
3483
  })
3446
3484
  };
3447
- try {
3448
- const result = await deliverNormalizedPayload(normalizedDeliveryPayload, info);
3449
- if (result.visibleReplySent) logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.dropDuplicateMedia(mediaUrls));
3450
- return result;
3451
- } catch (error) {
3452
- if (isWhatsAppVisibleDeliveryError(error)) logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.dropDuplicateMedia(mediaUrls));
3453
- throw error;
3454
- }
3485
+ return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { onMediaAccepted: (mediaUrl) => {
3486
+ didSendReply = true;
3487
+ logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.supersedeMedia(mediaUrl));
3488
+ } });
3455
3489
  },
3456
3490
  onDelivered: (payload, _info, result) => {
3457
3491
  if (!resolveSendableOutboundReplyParts(payload).hasMedia && result?.visibleReplySent === true) recordDeliveredPayload(payload);
@@ -3716,27 +3750,30 @@ async function processMessage(params) {
3716
3750
  }) : void 0;
3717
3751
  if (conversationKind === "group") {
3718
3752
  const history = visibleGroupHistory ?? [];
3719
- if (history.length > 0) combinedBody = buildHistoryContextFromEntries({
3720
- entries: history.map((m) => ({
3753
+ if (history.length > 0) {
3754
+ const historyEntries = history.map((m) => ({
3721
3755
  sender: m.sender,
3722
3756
  body: m.body,
3723
3757
  timestamp: m.timestamp,
3724
3758
  media: m.media
3725
- })),
3726
- currentMessage: combinedBody,
3727
- excludeLast: false,
3728
- formatEntry: (entry) => {
3729
- return formatInboundEnvelope$1({
3730
- channel: "WhatsApp",
3731
- from: conversationId,
3732
- timestamp: entry.timestamp,
3733
- body: [entry.body, formatMediaPlaceholderText(entry.media ?? [])].filter(Boolean).join("\n"),
3734
- chatType: "group",
3735
- senderLabel: entry.sender,
3736
- envelope: envelopeOptions
3737
- });
3738
- }
3739
- });
3759
+ }));
3760
+ combinedBody = buildHistoryContextFromEntries({
3761
+ entries: historyEntries,
3762
+ currentMessage: combinedBody,
3763
+ excludeLast: false,
3764
+ formatEntry: (entry) => {
3765
+ return formatInboundEnvelope$1({
3766
+ channel: "WhatsApp",
3767
+ from: conversationId,
3768
+ timestamp: entry.timestamp,
3769
+ body: [entry.body, formatMediaPlaceholderText(entry.media ?? [])].filter(Boolean).join("\n"),
3770
+ chatType: "group",
3771
+ senderLabel: entry.sender,
3772
+ envelope: envelopeOptions
3773
+ });
3774
+ }
3775
+ });
3776
+ }
3740
3777
  shouldClearGroupHistory = !(params.suppressGroupHistoryClear ?? false);
3741
3778
  }
3742
3779
  const statusReactionController = params.statusReactionController ?? (params.cfg.messages?.statusReactions?.enabled === true && !params.ackAlreadySent ? await createWhatsAppStatusReactionController({
@@ -4263,8 +4300,8 @@ function normalizeReconnectAccountId(accountId) {
4263
4300
  function isRetryableAuthUnstableError(error) {
4264
4301
  return error instanceof WhatsAppAuthUnstableError || typeof error === "object" && error !== null && "code" in error && error.code === "whatsapp-auth-unstable";
4265
4302
  }
4266
- const DEFAULT_TRANSPORT_TIMEOUT_MS = 300 * 1e3;
4267
- const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS = 20 * 6e4;
4303
+ const DEFAULT_TRANSPORT_TIMEOUT_MS = 3e5;
4304
+ const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS = 12e5;
4268
4305
  async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSocket, keepAlive = true, replyResolver, runtime = defaultRuntime, abortSignal, tuning = {}) {
4269
4306
  const activeReplyResolver = replyResolver ?? (await loadReplyResolverRuntime()).getReplyFromConfig;
4270
4307
  const runId = newConnectionId();
@@ -4309,9 +4346,9 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
4309
4346
  };
4310
4347
  process.once("SIGINT", handleSigint);
4311
4348
  const transportTimeoutMs = tuning.transportTimeoutMs ?? DEFAULT_TRANSPORT_TIMEOUT_MS;
4312
- const messageTimeoutMs = tuning.messageTimeoutMs ?? 1800 * 1e3;
4349
+ const messageTimeoutMs = tuning.messageTimeoutMs ?? 18e5;
4313
4350
  const reconnectCatchUpWindowMs = Math.min(Math.max(messageTimeoutMs, 6e4), WHATSAPP_RECONNECT_CATCH_UP_MAX_MS);
4314
- const watchdogCheckMs = tuning.watchdogCheckMs ?? 60 * 1e3;
4351
+ const watchdogCheckMs = tuning.watchdogCheckMs ?? 6e4;
4315
4352
  const controller = new WhatsAppConnectionController({
4316
4353
  accountId: account.accountId,
4317
4354
  authDir: account.authDir,
@@ -4333,10 +4370,6 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
4333
4370
  while (true) {
4334
4371
  if (stopRequested()) break;
4335
4372
  const connectionId = newConnectionId();
4336
- const inboundDebounceMs = resolveInboundDebounceMs({
4337
- cfg,
4338
- channel: "whatsapp"
4339
- });
4340
4373
  const shouldDebounce = (msg) => shouldDebounceTextInbound({
4341
4374
  text: msg.payload.commandBody ?? msg.payload.body,
4342
4375
  cfg,
@@ -4381,7 +4414,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
4381
4414
  selfChatMode: account.selfChatMode,
4382
4415
  sendReadReceipts: account.sendReadReceipts,
4383
4416
  socketTiming,
4384
- debounceMs: inboundDebounceMs,
4417
+ debounceMs: tuning.debounceMs,
4385
4418
  appendReplyWindow: connectionLocal.openedAfterRecentInbound ? {
4386
4419
  afterMs: connectionLocal.startedAt - reconnectCatchUpWindowMs,
4387
4420
  untilMs: connectionLocal.startedAt + reconnectCatchUpWindowMs,