@openclaw/whatsapp 2026.10.1 → 2026.10.5-beta.1

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 (48) hide show
  1. package/dist/.setup/{action-runtime-CE3zGstN.mjs → action-runtime-DUSkWTiZ.mjs} +2 -2
  2. package/dist/.setup/{agent-tools-login-B7VEvJO7.mjs → agent-tools-login-CB7OQbli.mjs} +5 -15
  3. package/dist/.setup/{approval-handler.runtime-CKposl6w.mjs → approval-handler.runtime-BEaAKSoU.mjs} +3 -3
  4. package/dist/.setup/{approval-reactions-CD6CDF9k.mjs → approval-reactions-CWkZ_ham.mjs} +7 -16
  5. package/dist/.setup/audio-preflight.runtime-Dg8oZgre.mjs +2 -0
  6. package/dist/.setup/{auth-store-D2rN-hP5.mjs → auth-store-DOBi5BPe.mjs} +10 -11
  7. package/dist/.setup/{channel-DNgKHMCz.mjs → channel-DcHw3Up2.mjs} +136 -135
  8. package/dist/.setup/{channel-react-action-BaBV9X6M.mjs → channel-react-action-C5E8_iDk.mjs} +3 -3
  9. package/dist/.setup/{channel.runtime-BniF8Swm.mjs → channel.runtime-DTj-wkEP.mjs} +5 -11
  10. package/dist/.setup/channel.setup-BJWEyQlF.mjs +5 -0
  11. package/dist/.setup/{connection-controller-COkcq48K.mjs → connection-controller-CejM7bhn.mjs} +50 -84
  12. package/dist/.setup/{directory-config-RYQomHyB.mjs → directory-config-DPLTUAqy.mjs} +22 -29
  13. package/dist/.setup/{group-session-key-BV8Le9r8.mjs → group-session-key-BbTPosdy.mjs} +12 -18
  14. package/dist/.setup/{login-CV_4V021.mjs → login-DUx5JYuW.mjs} +3 -3
  15. package/dist/.setup/{login-qr-DFhGc5yH.mjs → login-qr-BAWbPonj.mjs} +24 -45
  16. package/dist/.setup/{monitor-B5DS4PDV.mjs → monitor-CmXEQad9.mjs} +218 -367
  17. package/dist/.setup/{normalize-target-BGra1ZnM.mjs → normalize-target-D67Kc3g6.mjs} +5 -8
  18. package/dist/.setup/{question-reactions-LMeijbaq.mjs → question-reactions-DywNaX8m.mjs} +3 -10
  19. package/dist/.setup/reaction-target-CSPO7gtu.mjs +11 -0
  20. package/dist/.setup/{resolve-outbound-target-CzyNKmrl.mjs → resolve-outbound-target-qikGL4r0.mjs} +3 -4
  21. package/dist/.setup/{send-CGUsUdoX.mjs → send-CLBfAzd6.mjs} +34 -42
  22. package/dist/.setup/{send-api-B03VDwZ1.mjs → send-api-COzRvqXx.mjs} +28 -60
  23. package/dist/.setup/{setup-finalize-GtBmrO8p.mjs → setup-finalize-DSiP_-wa.mjs} +53 -69
  24. package/dist/.setup/setup-plugin-api-8WdNCjRW.mjs +3 -0
  25. package/dist/.setup/{setup-surface-BuA39k6K.mjs → setup-surface-C_flQyHy.mjs} +2 -2
  26. package/dist/.setup/{shared-CP_zs2U7.mjs → shared-SqcOkt51.mjs} +4 -4
  27. package/dist/.setup/{socket-close-DU7eerAa.mjs → socket-close-C9OU9mVN.mjs} +67 -86
  28. package/dist/.setup/{targets-runtime-Dls-Xh4F.mjs → targets-runtime-D5ryyPxh.mjs} +16 -25
  29. package/dist/action-runtime-api.js +1 -1
  30. package/dist/action-runtime.runtime.js +1 -1
  31. package/dist/agent-tools-api.js +4 -8
  32. package/dist/api.js +13 -15
  33. package/dist/channel-plugin-api.js +1 -1
  34. package/dist/contract-api.js +1 -1
  35. package/dist/directory-contract-api.js +1 -1
  36. package/dist/doctor-contract-api.js +12 -32
  37. package/dist/light-runtime-api.js +2 -2
  38. package/dist/login-qr-runtime.js +2 -4
  39. package/dist/runtime-api.js +8 -8
  40. package/dist/setup-entry.js +1 -1
  41. package/dist/setup-plugin-api.js +1 -1
  42. package/node_modules/baileys/lib/Utils/messages-media.js +7 -11
  43. package/node_modules/baileys/node_modules/ws/lib/websocket.js +17 -3
  44. package/node_modules/baileys/node_modules/ws/package.json +1 -1
  45. package/package.json +4 -4
  46. package/dist/.setup/audio-preflight.runtime-C_glQhZY.mjs +0 -7
  47. package/dist/.setup/channel.setup-CsS_NAKQ.mjs +0 -5
  48. package/dist/.setup/setup-plugin-api-Bsp7jI-r.mjs +0 -3
@@ -1,19 +1,19 @@
1
1
  import { n as __reExport, t as __exportAll } from "./rolldown-runtime-BBjsoOtd.mjs";
2
2
  import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-F0vHuze2.mjs";
3
- import { _ as withWhatsAppLogicalDeliveryActivity, c as normalizeWhatsAppPayloadTextPreservingIndentation, f as listWhatsAppSendResultMessageIds, g as rememberWhatsAppPartialSend, h as rememberWhatsAppAcceptedSend, l as prepareWhatsAppOutboundMedia, m as normalizeWhatsAppSendResult, o as normalizeWhatsAppOutboundPayload, p as mergeWhatsAppAcceptedSendError, r as sendReactionWhatsApp, y as resolveWhatsAppReactionLevel } from "./send-CGUsUdoX.mjs";
3
+ import { c as prepareWhatsAppOutboundMedia, d as listWhatsAppSendResultMessageIds, f as mergeWhatsAppAcceptedSendError, g as withWhatsAppLogicalDeliveryActivity, h as rememberWhatsAppPartialSend, m as rememberWhatsAppAcceptedSend, o as normalizeWhatsAppOutboundPayload, p as normalizeWhatsAppSendResult, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation, v as resolveWhatsAppReactionLevel } from "./send-CLBfAzd6.mjs";
4
4
  import { i as getWhatsAppRuntime } from "./runtime-BLlToOi6.mjs";
5
5
  import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-hgLJoRc8.mjs";
6
- 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-Dls-Xh4F.mjs";
7
- import { a as lookupInboundMessageMeta, i as cacheInboundMessageMeta, n as resolveWhatsAppGroupSessionRoute, r as buildQuotedMessageOptions } from "./group-session-key-BV8Le9r8.mjs";
8
- import { C as getPrimaryIdentityId, D as identitiesOverlap, E as getSenderIdentity, O as resolveComparableIdentity, S as getMentionIdentities, T as getSelfIdentity, _ as readWebSelfIdentityForDecision, h as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, w as getReplyContext, x as getComparableIdentityValues } from "./auth-store-D2rN-hP5.mjs";
6
+ 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-D5ryyPxh.mjs";
7
+ import { a as lookupInboundMessageMeta, i as cacheInboundMessageMeta, n as resolveWhatsAppGroupSessionRoute, r as buildQuotedMessageOptions } from "./group-session-key-BbTPosdy.mjs";
8
+ import { C as getPrimaryIdentityId, D as identitiesOverlap, E as getSenderIdentity, O as resolveComparableIdentity, S as getMentionIdentities, T as getSelfIdentity, _ as readWebSelfIdentityForDecision, h as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, w as getReplyContext, x as getComparableIdentityValues } from "./auth-store-DOBi5BPe.mjs";
9
9
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BPrak_bJ.mjs";
10
- import { a as DEFAULT_RECONNECT_POLICY, c as resolveHeartbeatSeconds, f as createWhatsAppSocketOperationTimeoutAdapter, g as withWhatsAppSocketOperationTimeout, h as resolveWhatsAppSocketTiming, i as waitForWaConnection, l as resolveReconnectPolicy, m as resolveWhatsAppSocketOperationTimeoutMs, o as computeBackoff, p as isWhatsAppSocketOperationTimeoutError, r as createWaSocket, s as newConnectionId, u as sleepWithAbort$1 } from "./socket-close-DU7eerAa.mjs";
10
+ import { a as DEFAULT_RECONNECT_POLICY, c as resolveHeartbeatSeconds, f as createWhatsAppSocketOperationTimeoutAdapter, g as withWhatsAppSocketOperationTimeout, h as resolveWhatsAppSocketTiming, i as waitForWaConnection, l as resolveReconnectPolicy, m as resolveWhatsAppSocketOperationTimeoutMs, o as computeBackoff, p as isWhatsAppSocketOperationTimeoutError, r as createWaSocket, s as newConnectionId, u as sleepWithAbort$1 } from "./socket-close-C9OU9mVN.mjs";
11
11
  import { n as getStatusCode, t as formatError } from "./session-errors-B8UcAnZN.mjs";
12
12
  import { t as BufferJSON } from "./session.runtime-st1konFI.mjs";
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-B03VDwZ1.mjs";
14
- import { n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController } from "./connection-controller-COkcq48K.mjs";
15
- import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-CD6CDF9k.mjs";
16
- import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-LMeijbaq.mjs";
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-COzRvqXx.mjs";
14
+ import { n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController } from "./connection-controller-CejM7bhn.mjs";
15
+ import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-CWkZ_ham.mjs";
16
+ import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-DywNaX8m.mjs";
17
17
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
18
18
  import { createAckReactionHandle, createStatusReactionController, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
19
19
  import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
@@ -24,11 +24,10 @@ import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw
24
24
  import { createSubsystemLogger as createSubsystemLogger$1, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1, warn } from "openclaw/plugin-sdk/runtime-env";
25
25
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
26
26
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
27
- import { buildChannelInboundEventContext, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, readAgentRunTerminalOutcome, resolveGroupThreadConfig, resolveInboundSessionEnvelopeContext, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, runGroupThread, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
27
+ import { buildChannelInboundEventContext, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, readAgentRunTerminalOutcome, resolveGroupThreadConfig, resolveInboundSessionEnvelopeContextAsync, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, runGroupThread, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
28
28
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
29
29
  import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
30
30
  import { normalizeE164, normalizeE164 as normalizeE164$1, normalizeE164 as normalizeE164$2, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
31
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
32
31
  import { DisconnectReason, downloadMediaMessage, isHostedLidUser, isHostedPnUser, isJidGroup, isJidGroup as isJidGroup$1, jidDecode, jidEncode, jidNormalizedUser, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
33
32
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
34
33
  import { DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
@@ -382,22 +381,14 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
382
381
  });
383
382
  };
384
383
  const applyOutboundMentions = async (jid, content) => {
385
- if ("text" in content && typeof content.text === "string") {
386
- const resolved = await resolveOutboundMentions(jid, content.text);
387
- return addWhatsAppOutboundMentionsToContent({
388
- ...content,
389
- text: resolved.text
390
- }, resolved.mentionedJids);
391
- }
392
- const caption = content.caption;
393
- if (typeof caption === "string") {
394
- const resolved = await resolveOutboundMentions(jid, caption);
395
- return addWhatsAppOutboundMentionsToContent({
396
- ...content,
397
- caption: resolved.text
398
- }, resolved.mentionedJids);
399
- }
400
- return content;
384
+ const field = "text" in content && typeof content.text === "string" ? "text" : "caption";
385
+ const text = content[field];
386
+ if (typeof text !== "string") return content;
387
+ const resolved = await resolveOutboundMentions(jid, text);
388
+ return addWhatsAppOutboundMentionsToContent({
389
+ ...content,
390
+ [field]: resolved.text
391
+ }, resolved.mentionedJids);
401
392
  };
402
393
  const start = () => {
403
394
  if (started || closed) return;
@@ -636,39 +627,33 @@ function createWhatsAppInboundMessageDebouncer(options) {
636
627
  const last = orderedEntries.at(-1);
637
628
  if (!last) return;
638
629
  try {
639
- if (orderedEntries.length === 1) {
640
- await options.onMessage(attachWhatsAppIngressLifecycle(last, admissionLifecycle));
641
- await settle();
642
- await Promise.all(orderedEntries.map((entry) => options.markRead(entry.readReceipt)));
643
- return;
630
+ let message = last;
631
+ if (orderedEntries.length > 1) {
632
+ const mentioned = /* @__PURE__ */ new Set();
633
+ for (const entry of orderedEntries) for (const jid of entry.group?.mentions?.jids ?? []) mentioned.add(jid);
634
+ const combinedMentions = mentioned.size > 0 ? {
635
+ ...last.group?.mentions,
636
+ jids: Array.from(mentioned)
637
+ } : last.group?.mentions;
638
+ message = {
639
+ ...last,
640
+ turnAdoptionLifecycle: admissionLifecycle,
641
+ payload: {
642
+ ...last.payload,
643
+ body: orderedEntries.map((entry) => entry.payload.body).filter(Boolean).join("\n"),
644
+ commandBody: orderedEntries.map((entry) => entry.payload.commandBody ?? entry.payload.body).filter(Boolean).join("\n")
645
+ },
646
+ group: last.group || combinedMentions ? {
647
+ ...last.group,
648
+ mentions: combinedMentions
649
+ } : void 0,
650
+ event: {
651
+ ...last.event,
652
+ isBatched: true
653
+ }
654
+ };
644
655
  }
645
- const mentioned = /* @__PURE__ */ new Set();
646
- for (const entry of orderedEntries) for (const jid of entry.group?.mentions?.jids ?? []) mentioned.add(jid);
647
- const combinedBody = orderedEntries.map((entry) => entry.payload.body).filter(Boolean).join("\n");
648
- const combinedCommandBody = orderedEntries.map((entry) => entry.payload.commandBody ?? entry.payload.body).filter(Boolean).join("\n");
649
- const combinedMentions = mentioned.size > 0 ? {
650
- ...last.group?.mentions,
651
- jids: Array.from(mentioned)
652
- } : last.group?.mentions;
653
- const combinedGroup = last.group || combinedMentions ? {
654
- ...last.group,
655
- mentions: combinedMentions
656
- } : void 0;
657
- const combinedMessage = attachWhatsAppIngressLifecycle({
658
- ...last,
659
- turnAdoptionLifecycle: admissionLifecycle,
660
- payload: {
661
- ...last.payload,
662
- body: combinedBody,
663
- commandBody: combinedCommandBody
664
- },
665
- group: combinedGroup,
666
- event: {
667
- ...last.event,
668
- isBatched: true
669
- }
670
- }, admissionLifecycle);
671
- await options.onMessage(combinedMessage);
656
+ await options.onMessage(attachWhatsAppIngressLifecycle(message, admissionLifecycle));
672
657
  await settle();
673
658
  await Promise.all(orderedEntries.map((entry) => options.markRead(entry.readReceipt)));
674
659
  } catch (error) {
@@ -827,15 +812,17 @@ async function enrichWhatsAppInboundMessage(params) {
827
812
  kind: mediaKind
828
813
  } : void 0;
829
814
  let mediaFileName;
815
+ let savedContentType;
830
816
  const maxBytes = (typeof params.mediaMaxMb === "number" && params.mediaMaxMb > 0 ? params.mediaMaxMb : 50) * 1024 * 1024;
831
- const saveInboundMedia = async (inboundMedia) => {
817
+ const saveInboundMedia = (inboundMedia) => {
832
818
  if (!inboundMedia) return;
833
819
  mediaPath = inboundMedia.saved.path;
834
820
  mediaType = inboundMedia.mimetype;
835
821
  mediaFileName = inboundMedia.fileName;
822
+ savedContentType = inboundMedia.saved.contentType;
836
823
  };
837
824
  try {
838
- await saveInboundMedia(await downloadInboundMedia(msg, sock, maxBytes, messageProjection[0]));
825
+ saveInboundMedia(await downloadInboundMedia(msg, sock, maxBytes, messageProjection[0]));
839
826
  } catch (error) {
840
827
  logMediaMaterializationFailure({
841
828
  messageId: msg.key?.id,
@@ -851,7 +838,7 @@ async function enrichWhatsAppInboundMessage(params) {
851
838
  });
852
839
  }
853
840
  if (!mediaPath && !mediaKind && replyContext?.media) try {
854
- await saveInboundMedia(await downloadQuotedInboundMedia(msg, sock, maxBytes));
841
+ saveInboundMedia(await downloadQuotedInboundMedia(msg, sock, maxBytes));
855
842
  mediaKind = replyContext.media.kind ?? void 0;
856
843
  mediaType = mediaType ?? replyContext.media.contentType ?? void 0;
857
844
  } catch (error) {
@@ -868,6 +855,7 @@ async function enrichWhatsAppInboundMessage(params) {
868
855
  notice: "[whatsapp quoted attachment unavailable]"
869
856
  });
870
857
  }
858
+ if (mediaKind === "document" && savedContentType?.startsWith("image/")) mediaKind = "image";
871
859
  return {
872
860
  body,
873
861
  commandBody,
@@ -1416,10 +1404,7 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
1416
1404
  }) : void 0;
1417
1405
  let resolvePrepared;
1418
1406
  if (durableId && !preparedInboundByDurableId.has(durableId)) {
1419
- if (preparedInboundByDurableId.size >= 1e3) {
1420
- const oldest = preparedInboundByDurableId.keys().next().value;
1421
- if (oldest !== void 0) preparedInboundByDurableId.delete(oldest);
1422
- }
1407
+ pruneMapToMaxSize(preparedInboundByDurableId, 999);
1423
1408
  const prepared = createDeferred();
1424
1409
  resolvePrepared = prepared.resolve;
1425
1410
  preparedInboundByDurableId.set(durableId, prepared.promise);
@@ -1599,13 +1584,17 @@ async function createWhatsAppAttachedSocketSession(options) {
1599
1584
  rememberOutboundMessage(jid, result);
1600
1585
  }, () => {});
1601
1586
  };
1587
+ const currentSocketOperations = (currentSock) => createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs, {
1588
+ assertCurrent: () => {
1589
+ if (getCurrentSock() !== currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
1590
+ },
1591
+ onSendMessageTimeout: ({ jid, promise }) => trackLateAcceptedSend(jid, promise)
1592
+ });
1602
1593
  let reachoutTimeLock;
1603
1594
  let reachoutTimeLockFetch;
1604
- let reachoutTimeLockVersion = 0;
1605
1595
  let verifiedSendReady;
1606
1596
  const rememberReachoutTimeLock = (state) => {
1607
1597
  reachoutTimeLock = state;
1608
- reachoutTimeLockVersion += 1;
1609
1598
  verifiedSendReady = void 0;
1610
1599
  };
1611
1600
  const fetchReachoutTimeLock = async (currentSock) => {
@@ -1620,28 +1609,22 @@ async function createWhatsAppAttachedSocketSession(options) {
1620
1609
  });
1621
1610
  return await reachoutTimeLockFetch;
1622
1611
  };
1623
- const rememberVerifiedSendReady = (jid, currentSock) => {
1624
- verifiedSendReady = {
1625
- jid,
1626
- sock: currentSock,
1627
- reachoutTimeLockVersion
1628
- };
1629
- };
1630
- const consumeVerifiedSendReady = (jid, currentSock) => {
1631
- if (verifiedSendReady?.jid !== jid || verifiedSendReady.sock !== currentSock || verifiedSendReady.reachoutTimeLockVersion !== reachoutTimeLockVersion) return false;
1632
- verifiedSendReady = void 0;
1633
- return true;
1634
- };
1635
1612
  const assertCanSendToJid = async (jid, currentSock, readinessOptions) => {
1636
1613
  if (!isDirectUserJid(jid)) return;
1637
- if (readinessOptions?.useVerifiedReady && consumeVerifiedSendReady(jid, currentSock)) return;
1614
+ if (readinessOptions?.useVerifiedReady && verifiedSendReady?.jid === jid && verifiedSendReady.sock === currentSock) {
1615
+ verifiedSendReady = void 0;
1616
+ return;
1617
+ }
1638
1618
  const state = getActiveReachoutTimelock(reachoutTimeLock) ?? await fetchReachoutTimeLock(currentSock);
1639
1619
  const activeState = getActiveReachoutTimelock(state);
1640
1620
  if (activeState) {
1641
1621
  const error = new Error(formatReachoutTimelockError(activeState));
1642
1622
  throw readinessOptions?.rememberReady ? new PlatformMessageNotDispatchedError(error.message, { cause: error }) : error;
1643
1623
  }
1644
- if (readinessOptions?.rememberReady && state) rememberVerifiedSendReady(jid, currentSock);
1624
+ if (readinessOptions?.rememberReady && state) verifiedSendReady = {
1625
+ jid,
1626
+ sock: currentSock
1627
+ };
1645
1628
  };
1646
1629
  const assertSendReady = async (to) => {
1647
1630
  const currentSock = getCurrentSock();
@@ -1655,9 +1638,7 @@ async function createWhatsAppAttachedSocketSession(options) {
1655
1638
  const currentSock = getCurrentSock();
1656
1639
  if (currentSock) try {
1657
1640
  await assertCanSendToJid(jid, currentSock, { useVerifiedReady: true });
1658
- const result = await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs, { onSendMessageTimeout: ({ jid: timedOutJid, promise }) => {
1659
- trackLateAcceptedSend(timedOutJid, promise);
1660
- } }).sendMessage(jid, content, sendOptions);
1641
+ const result = await currentSocketOperations(currentSock).sendMessage(jid, content, sendOptions);
1661
1642
  rememberOutboundMessage(jid, result);
1662
1643
  return result;
1663
1644
  } catch (error) {
@@ -1681,7 +1662,7 @@ async function createWhatsAppAttachedSocketSession(options) {
1681
1662
  sendPresenceUpdate: async (presenceLocal, jid) => {
1682
1663
  const currentSock = getCurrentSock();
1683
1664
  if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
1684
- return await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs).sendPresenceUpdate(presenceLocal, jid);
1665
+ return await currentSocketOperations(currentSock).sendPresenceUpdate(presenceLocal, jid);
1685
1666
  }
1686
1667
  };
1687
1668
  const markRead = async (target) => {
@@ -1921,9 +1902,10 @@ function createWebChannelStatusController(statusSink) {
1921
1902
  }
1922
1903
  emit();
1923
1904
  },
1924
- noteTransportActivity(at = Date.now()) {
1925
- if (status.lastTransportActivityAt === at) return;
1905
+ noteTransportActivity(at, authAgeMs) {
1906
+ if (status.lastTransportActivityAt === at && status.authAgeMs === authAgeMs) return;
1926
1907
  Object.assign(status, createTransportActivityStatusPatch(at));
1908
+ status.authAgeMs = authAgeMs;
1927
1909
  emit();
1928
1910
  },
1929
1911
  noteBusy(busy, at = Date.now()) {
@@ -2167,7 +2149,7 @@ function hasWhatsAppAudioBody(msg) {
2167
2149
  return (msg.payload.media?.kind === "audio" || msg.payload.media?.type?.startsWith("audio/") === true) && !msg.payload.body.trim();
2168
2150
  }
2169
2151
  async function transcribeWhatsAppAudioMessage(cfg, msg, accountId) {
2170
- const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.mjs");
2152
+ const { transcribeFirstAudio } = await import("./audio-preflight.runtime-Dg8oZgre.mjs");
2171
2153
  const media = msg.payload.media;
2172
2154
  const conversationId = msg.admission.conversation.id;
2173
2155
  return await transcribeFirstAudio({
@@ -2495,10 +2477,10 @@ function resolvePeerId(msg) {
2495
2477
  const admission = requireWhatsAppInboundAdmission(msg);
2496
2478
  if (admission.conversation.kind === "group") return admission.conversation.id;
2497
2479
  const sender = getSenderIdentity(msg);
2498
- if (sender.e164) return normalizeE164(sender.e164) ?? sender.e164;
2480
+ if (sender.e164) return sender.e164;
2499
2481
  const conversationId = admission.conversation.id;
2500
2482
  if (conversationId.includes("@")) return jidToE164(conversationId) ?? conversationId;
2501
- return normalizeE164(conversationId) ?? conversationId;
2483
+ return normalizeE164(conversationId);
2502
2484
  }
2503
2485
  //#endregion
2504
2486
  //#region extensions/whatsapp/src/system-prompt.ts
@@ -2629,24 +2611,17 @@ function resolveWhatsAppReceiptKind(results) {
2629
2611
  function createWhatsAppReplyDeliveryReceipt(results) {
2630
2612
  const receiptResultsById = /* @__PURE__ */ new Map();
2631
2613
  for (const result of results) {
2632
- if (result.receipt?.parts.length) {
2633
- for (const part of result.receipt.parts) receiptResultsById.set(part.platformMessageId, {
2634
- ...part.raw ?? {
2635
- channel: "whatsapp",
2636
- messageId: part.platformMessageId
2637
- },
2638
- meta: {
2639
- ...part.raw?.meta,
2640
- kind: result.kind,
2641
- providerAccepted: result.providerAccepted
2642
- }
2643
- });
2644
- continue;
2645
- }
2646
- for (const messageId of listWhatsAppSendResultMessageIds(result)) receiptResultsById.set(messageId, {
2647
- channel: "whatsapp",
2648
- messageId,
2614
+ const parts = result.receipt?.parts.length ? result.receipt.parts : listWhatsAppSendResultMessageIds(result).map((platformMessageId) => ({
2615
+ platformMessageId,
2616
+ raw: void 0
2617
+ }));
2618
+ for (const part of parts) receiptResultsById.set(part.platformMessageId, {
2619
+ ...part.raw ?? {
2620
+ channel: "whatsapp",
2621
+ messageId: part.platformMessageId
2622
+ },
2649
2623
  meta: {
2624
+ ...part.raw?.meta,
2650
2625
  kind: result.kind,
2651
2626
  providerAccepted: result.providerAccepted
2652
2627
  }
@@ -2744,12 +2719,15 @@ async function deliverWebReplyInActivityScope(params) {
2744
2719
  throw error;
2745
2720
  }
2746
2721
  };
2722
+ const sendText = async (chunk, label, quote = getQuote) => {
2723
+ rememberSendResult(await sendWithRetry(() => transport.reply(chunk, quote()), label, "text"));
2724
+ };
2747
2725
  if (mediaList.length === 0 && textChunks.length) {
2748
2726
  const totalChunks = textChunks.length;
2749
2727
  for (const [index, chunk] of textChunks.entries()) {
2750
2728
  const chunkStarted = Date.now();
2751
2729
  const quote = getQuote();
2752
- rememberSendResult(await sendWithRetry(() => transport.reply(chunk, quote), "text", "text"));
2730
+ await sendText(chunk, "text", () => quote);
2753
2731
  if (!skipLog) {
2754
2732
  const durationMs = Date.now() - chunkStarted;
2755
2733
  whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${conversationId} (${durationMs.toFixed(0)}ms)`);
@@ -2804,7 +2782,7 @@ async function deliverWebReplyInActivityScope(params) {
2804
2782
  ...mediaContent,
2805
2783
  mimetype: media.mimetype
2806
2784
  }, quote), `media:${media.kind}`, "media", mediaUrl), mediaUrl);
2807
- if (media.kind === "audio" && caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text", "text"));
2785
+ if (media.kind === "audio" && caption) await sendText(caption, "media:audio-text", () => quote);
2808
2786
  whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / 1048576).toFixed(2)}MB)`);
2809
2787
  replyLogger.info({
2810
2788
  correlationId: transport.correlationId ?? newConnectionId(),
@@ -2825,48 +2803,41 @@ async function deliverWebReplyInActivityScope(params) {
2825
2803
  err: error,
2826
2804
  mediaUrl
2827
2805
  }, "failed to send web media reply");
2828
- if (!isFirst) {
2829
- whatsappOutboundLog.warn(`Trailing media failed; sent warning to ${conversationId}`);
2830
- rememberSendResult(await sendWithRetry(() => transport.reply("⚠️ Media unavailable.", getQuote()), "media:fallback-unavailable", "text"));
2831
- return;
2832
- }
2833
- const fallbackText = [caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
2834
- whatsappOutboundLog.warn(`Media skipped; sent text-only to ${conversationId}`);
2835
- rememberSendResult(await sendWithRetry(() => transport.reply(fallbackText, getQuote()), "media:fallback-text", "text"));
2806
+ const fallbackText = isFirst ? [caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n") : "⚠️ Media unavailable.";
2807
+ whatsappOutboundLog.warn(isFirst ? `Media skipped; sent text-only to ${conversationId}` : `Trailing media failed; sent warning to ${conversationId}`);
2808
+ await sendText(fallbackText, isFirst ? "media:fallback-text" : "media:fallback-unavailable");
2836
2809
  }
2837
2810
  });
2838
- for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => transport.reply(chunk, getQuote()), "media:text", "text"));
2811
+ for (const chunk of remainingText) await sendText(chunk, "media:text");
2839
2812
  return finishDelivery();
2840
2813
  }
2841
2814
  //#endregion
2842
2815
  //#region extensions/whatsapp/src/auto-reply/monitor/inbound-context.ts
2843
- function isWhatsAppSupplementalSenderAllowed(params) {
2844
- if (params.allowFrom.includes("*")) return true;
2845
- const senderValues = new Set(getComparableIdentityValues(resolveComparableIdentity(params.sender, params.authDir)));
2846
- if (senderValues.size === 0) return false;
2847
- for (const entry of params.allowFrom) {
2848
- const rawEntry = entry.trim();
2849
- if (!rawEntry) continue;
2850
- const normalizedEntry = normalizeE164(rawEntry);
2851
- if (normalizedEntry && senderValues.has(normalizedEntry) || senderValues.has(rawEntry)) return true;
2852
- }
2853
- return false;
2816
+ function isWhatsAppContextSenderAllowed(params, isGroup, sender) {
2817
+ return resolveInboundSupplementalSenderAllowed({
2818
+ isGroup,
2819
+ groupPolicy: params.groupPolicy,
2820
+ allowFrom: params.groupAllowFrom,
2821
+ isSenderAllowed: (allowFrom) => {
2822
+ if (allowFrom.includes("*")) return true;
2823
+ const senderValues = new Set(getComparableIdentityValues(resolveComparableIdentity(sender, params.authDir)));
2824
+ if (senderValues.size === 0) return false;
2825
+ for (const entry of allowFrom) {
2826
+ const rawEntry = entry.trim();
2827
+ if (!rawEntry) continue;
2828
+ const normalizedEntry = normalizeE164(rawEntry);
2829
+ if (normalizedEntry && senderValues.has(normalizedEntry) || senderValues.has(rawEntry)) return true;
2830
+ }
2831
+ return false;
2832
+ }
2833
+ });
2854
2834
  }
2855
2835
  function resolveVisibleWhatsAppGroupHistory(params) {
2856
2836
  return filterSupplementalContextItems({
2857
2837
  items: params.history,
2858
2838
  mode: params.mode,
2859
2839
  kind: "history",
2860
- isSenderAllowed: (entry) => resolveInboundSupplementalSenderAllowed({
2861
- isGroup: true,
2862
- groupPolicy: params.groupPolicy,
2863
- allowFrom: params.groupAllowFrom,
2864
- isSenderAllowed: (allowFrom) => isWhatsAppSupplementalSenderAllowed({
2865
- allowFrom,
2866
- authDir: params.authDir,
2867
- sender: entry.senderJid ? { jid: entry.senderJid } : null
2868
- })
2869
- })
2840
+ isSenderAllowed: (entry) => isWhatsAppContextSenderAllowed(params, true, entry.senderJid ? { jid: entry.senderJid } : null)
2870
2841
  }).items;
2871
2842
  }
2872
2843
  function resolveVisibleWhatsAppReplyContext(params) {
@@ -2874,16 +2845,7 @@ function resolveVisibleWhatsAppReplyContext(params) {
2874
2845
  if (!replyTo) return null;
2875
2846
  const admission = requireWhatsAppInboundAdmission(params.msg);
2876
2847
  const previewBody = [replyTo.body, formatMediaPlaceholderText(replyTo.media ? [replyTo.media] : [])].filter(Boolean).join("\n");
2877
- const senderAllowed = resolveInboundSupplementalSenderAllowed({
2878
- isGroup: admission.conversation.kind === "group",
2879
- groupPolicy: params.groupPolicy,
2880
- allowFrom: params.groupAllowFrom,
2881
- isSenderAllowed: (allowFrom) => isWhatsAppSupplementalSenderAllowed({
2882
- allowFrom,
2883
- authDir: params.authDir,
2884
- sender: replyTo.sender
2885
- })
2886
- });
2848
+ const senderAllowed = isWhatsAppContextSenderAllowed(params, admission.conversation.kind === "group", replyTo.sender);
2887
2849
  const visible = filterChannelInboundQuoteContext(params.mode, {
2888
2850
  id: replyTo.id,
2889
2851
  body: previewBody,
@@ -2896,65 +2858,6 @@ function resolveVisibleWhatsAppReplyContext(params) {
2896
2858
  } : null;
2897
2859
  }
2898
2860
  //#endregion
2899
- //#region extensions/whatsapp/src/auto-reply/monitor/prepared-inbound.ts
2900
- function resolvePreparedCommandFacts(command) {
2901
- if (!command) return;
2902
- const { authorization, ...facts } = command;
2903
- return {
2904
- ...facts,
2905
- ...authorization.kind === "not_checked" ? {} : { authorized: authorization.kind === "authorized" }
2906
- };
2907
- }
2908
- function projectPreparedChannelInbound(params) {
2909
- const { inbound, control } = params;
2910
- const command = resolvePreparedCommandFacts(inbound.command);
2911
- const commandAuthorization = inbound.command?.authorization;
2912
- const commandAccess = commandAuthorization && commandAuthorization.kind !== "not_checked" ? { authorized: commandAuthorization.kind === "authorized" } : void 0;
2913
- return {
2914
- input: {
2915
- id: inbound.event.id,
2916
- timestamp: inbound.event.timestamp,
2917
- rawText: inbound.message.rawBody,
2918
- textForAgent: inbound.message.bodyForAgent,
2919
- textForCommands: inbound.message.commandBody,
2920
- raw: inbound
2921
- },
2922
- context: params.buildContext({
2923
- ...inbound,
2924
- messageId: inbound.event.id,
2925
- messageIdFull: inbound.event.fullId,
2926
- timestamp: inbound.event.timestamp,
2927
- command,
2928
- access: inbound.mentions || commandAccess ? {
2929
- mentions: inbound.mentions,
2930
- commands: commandAccess
2931
- } : void 0,
2932
- extra: {
2933
- Transcript: inbound.context?.transcript,
2934
- ...inbound.context && "groupSubject" in inbound.context ? { GroupSubject: inbound.context.groupSubject ?? void 0 } : {},
2935
- GroupMembers: inbound.context?.groupMembers,
2936
- SenderE164: inbound.context?.senderE164,
2937
- ReplyThreading: inbound.context?.replyThreading,
2938
- SuppressMessageReceivedHooks: control.messageReceivedHooks === "channel",
2939
- ...inbound.context?.location ? toLocationContext(inbound.context.location) : {}
2940
- }
2941
- })
2942
- };
2943
- }
2944
- /** Keeps WhatsApp reply-policy mapping beside the transport that applies it. */
2945
- function resolveWhatsAppInboundReplyPolicy(params) {
2946
- const sourceReplyDeliveryMode = params.ctx.ChatType === "group" || params.ctx.ChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
2947
- cfg: params.cfg,
2948
- ctx: params.ctx
2949
- }) : void 0;
2950
- const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
2951
- return {
2952
- sourceReplyDeliveryMode,
2953
- disableBlockStreaming: sourceRepliesAreToolOnly ? true : typeof params.blockStreamingEnabled === "boolean" ? !params.blockStreamingEnabled : void 0,
2954
- suppressTyping: sourceRepliesAreToolOnly && params.ctx.ChatType === "group" && params.ctx.WasMentioned !== true
2955
- };
2956
- }
2957
- //#endregion
2958
2861
  //#region extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.ts
2959
2862
  function normalizeErrForLog(err) {
2960
2863
  if (err instanceof Error) return {
@@ -2965,35 +2868,14 @@ function normalizeErrForLog(err) {
2965
2868
  };
2966
2869
  return err;
2967
2870
  }
2968
- function createWhatsAppChannelDeliveryResult(params) {
2969
- const messageIds = listMessageReceiptPlatformIds(params.delivery.receipt);
2970
- return {
2971
- receipt: params.delivery.receipt,
2972
- ...messageIds.length > 0 ? { messageIds } : {},
2973
- content: params.content,
2974
- visibleReplySent: params.delivery.providerAccepted
2975
- };
2976
- }
2977
2871
  function isWhatsAppVisibleDeliveryError(error) {
2978
2872
  return isRecord(error) && error.visibleReplySent === true || isChannelPartialDeliveryError(error) && error.deliveryResult.visibleReplySent;
2979
2873
  }
2980
- function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
2981
- if (flushResult.delivered === 0) return error;
2874
+ function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, delivered) {
2875
+ if (delivered === 0) return error;
2982
2876
  if (isWhatsAppVisibleDeliveryError(error)) return error;
2983
2877
  return markWhatsAppVisibleDeliveryError(new Error("deferred WhatsApp media delivery failed after an earlier visible send", { cause: error }));
2984
2878
  }
2985
- function logWhatsAppReplyDeliveryError(params) {
2986
- params.replyLogger.error({
2987
- err: normalizeErrForLog(params.err),
2988
- replyKind: params.info.kind,
2989
- correlationId: params.transport.correlationId ?? null,
2990
- connectionId: params.connectionId,
2991
- conversationId: params.transport.conversationId,
2992
- chatId: params.transport.chatJid,
2993
- to: params.transport.conversationId,
2994
- from: params.transport.recipientJid
2995
- }, "auto-reply delivery failed");
2996
- }
2997
2879
  function resolveWhatsAppDurableReplyToId(params) {
2998
2880
  if (params.payload.replyToId === null) return null;
2999
2881
  const explicitPayloadReplyToId = normalizeOptionalString(params.payload.replyToId);
@@ -3025,10 +2907,7 @@ function shouldDeferWhatsAppMediaOnlyPayload(params) {
3025
2907
  function createWhatsAppMediaOnlyReplyCoalescer(params) {
3026
2908
  const pendingMediaOnlyPayloads = [];
3027
2909
  const flushWhere = async (shouldFlush) => {
3028
- const flushResult = {
3029
- delivered: 0,
3030
- droppedDuplicateMedia: 0
3031
- };
2910
+ let delivered = 0;
3032
2911
  const candidates = [];
3033
2912
  const retained = [];
3034
2913
  for (const pending of pendingMediaOnlyPayloads.splice(0)) if (shouldFlush(pending)) candidates.push(pending);
@@ -3037,14 +2916,14 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
3037
2916
  for (const [index, candidate] of candidates.entries()) try {
3038
2917
  const delivery = await params.deliver(candidate);
3039
2918
  candidate.resolveFinalization(delivery);
3040
- if (delivery.visibleReplySent) flushResult.delivered += 1;
2919
+ if (delivery.visibleReplySent) delivered += 1;
3041
2920
  } catch (error) {
3042
- const visibleError = markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
2921
+ const visibleError = markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, delivered);
3043
2922
  candidate.rejectFinalization(error);
3044
2923
  for (const remaining of candidates.slice(index + 1)) remaining.rejectFinalization(new Error("deferred WhatsApp media delivery was not attempted", { cause: error }));
3045
2924
  throw visibleError;
3046
2925
  }
3047
- return flushResult;
2926
+ return delivered;
3048
2927
  };
3049
2928
  return {
3050
2929
  defer(pending) {
@@ -3058,14 +2937,11 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
3058
2937
  },
3059
2938
  flushNonDuplicateMedia: (mediaUrls) => flushWhere((pending) => !hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)),
3060
2939
  supersedeMedia(mediaUrl) {
3061
- const flushResult = {
3062
- delivered: 0,
3063
- droppedDuplicateMedia: 0
3064
- };
2940
+ let droppedDuplicateMedia = 0;
3065
2941
  const retained = [];
3066
2942
  for (const pending of pendingMediaOnlyPayloads.splice(0)) {
3067
2943
  if (pending.mediaUrls.delete(mediaUrl)) {
3068
- flushResult.droppedDuplicateMedia += 1;
2944
+ droppedDuplicateMedia += 1;
3069
2945
  const mediaUrls = [...pending.mediaUrls];
3070
2946
  pending.payload = {
3071
2947
  ...pending.payload,
@@ -3080,15 +2956,15 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
3080
2956
  retained.push(pending);
3081
2957
  }
3082
2958
  pendingMediaOnlyPayloads.push(...retained);
3083
- return flushResult;
2959
+ return droppedDuplicateMedia;
3084
2960
  },
3085
2961
  flushAll: () => flushWhere(() => true)
3086
2962
  };
3087
2963
  }
3088
- function logWhatsAppMediaOnlyFlushResult(result) {
2964
+ function logWhatsAppMediaOnlyFlushResult(delivered, droppedDuplicateMedia = 0) {
3089
2965
  if (!shouldLogVerbose$1()) return;
3090
- if (result.droppedDuplicateMedia > 0) logVerbose$1(`Superseded ${result.droppedDuplicateMedia} deferred WhatsApp attachment(s) with accepted replacement media`);
3091
- if (result.delivered > 0) logVerbose$1(`Flushed ${result.delivered} deferred media-only WhatsApp reply payload(s)`);
2966
+ if (droppedDuplicateMedia > 0) logVerbose$1(`Superseded ${droppedDuplicateMedia} deferred WhatsApp attachment(s) with accepted replacement media`);
2967
+ if (delivered > 0) logVerbose$1(`Flushed ${delivered} deferred media-only WhatsApp reply payload(s)`);
3092
2968
  }
3093
2969
  function resolveWhatsAppResponsePrefix(params) {
3094
2970
  const configuredResponsePrefix = params.cfg.messages?.responsePrefix;
@@ -3128,7 +3004,12 @@ async function prepareWhatsAppInboundContext(params) {
3128
3004
  contentType: params.msg.payload.media?.type,
3129
3005
  kind: params.msg.payload.media?.kind
3130
3006
  }] : void 0, { transcribed: (_entry, index) => params.mediaTranscribedIndexes?.includes(index) === true });
3131
- const control = { messageReceivedHooks: params.suppressMessageReceivedHooks ? "channel" : "core" };
3007
+ const mentions = wasMentioned !== void 0 ? {
3008
+ canDetectMention: conversationKind === "group",
3009
+ wasMentioned,
3010
+ requireMention: params.msg.groupMention?.requireMention
3011
+ } : void 0;
3012
+ const commands = params.command?.authorized !== void 0 ? { authorized: params.command.authorized } : void 0;
3132
3013
  const inbound = {
3133
3014
  channelIngress,
3134
3015
  channel: "whatsapp",
@@ -3142,10 +3023,8 @@ async function prepareWhatsAppInboundContext(params) {
3142
3023
  channelStructuredContext: params.msg.payload.channelStructuredContext
3143
3024
  },
3144
3025
  media,
3145
- event: {
3146
- id: eventId,
3147
- timestamp: params.msg.event.timestamp
3148
- },
3026
+ messageId: eventId,
3027
+ timestamp: params.msg.event.timestamp,
3149
3028
  from: conversationId,
3150
3029
  sender: {
3151
3030
  id: params.sender.id ?? params.sender.e164,
@@ -3176,35 +3055,36 @@ async function prepareWhatsAppInboundContext(params) {
3176
3055
  commandBody: params.command?.body ?? params.msg.payload.body
3177
3056
  },
3178
3057
  sessionTranscript: { historyLimit: conversationKind === "group" ? params.groupHistoryLimit ?? params.groupHistory?.length ?? 0 : 0 },
3179
- mentions: wasMentioned !== void 0 ? {
3180
- canDetectMention: conversationKind === "group",
3181
- wasMentioned,
3182
- requireMention: params.msg.groupMention?.requireMention
3058
+ access: mentions || commands ? {
3059
+ mentions,
3060
+ commands
3183
3061
  } : void 0,
3184
3062
  command: params.command,
3185
- context: {
3186
- transcript: params.transcript,
3187
- groupSubject: params.msg.group?.subject ?? null,
3188
- groupMembers: formatGroupMembers({
3063
+ extra: {
3064
+ Transcript: params.transcript,
3065
+ GroupSubject: params.msg.group?.subject ?? void 0,
3066
+ GroupMembers: formatGroupMembers({
3189
3067
  participants: params.msg.group?.participants,
3190
3068
  roster: params.groupMemberRoster,
3191
3069
  fallbackE164: params.sender.e164
3192
3070
  }),
3193
- senderE164: params.sender.e164,
3194
- replyThreading: params.replyThreading,
3195
- location: params.msg.payload.location
3071
+ SenderE164: params.sender.e164,
3072
+ ReplyThreading: params.replyThreading,
3073
+ SuppressMessageReceivedHooks: Boolean(params.suppressMessageReceivedHooks),
3074
+ ...params.msg.payload.location ? toLocationContext(params.msg.payload.location) : {}
3196
3075
  }
3197
3076
  };
3198
- const projected = projectPreparedChannelInbound({
3199
- inbound,
3200
- control,
3201
- buildContext: params.buildContext ?? buildChannelInboundEventContext
3202
- });
3203
3077
  return {
3204
3078
  inbound,
3205
- control,
3206
- turnInput: projected.input,
3207
- ctxPayload: projected.context
3079
+ turnInput: {
3080
+ id: eventId,
3081
+ timestamp: inbound.timestamp,
3082
+ rawText: inbound.message.rawBody,
3083
+ textForAgent: inbound.message.bodyForAgent,
3084
+ textForCommands: inbound.message.commandBody,
3085
+ raw: inbound
3086
+ },
3087
+ ctxPayload: (params.buildContext ?? buildChannelInboundEventContext)(inbound)
3208
3088
  };
3209
3089
  }
3210
3090
  function resolveWhatsAppDmRouteTarget(params) {
@@ -3221,7 +3101,8 @@ function updateWhatsAppMainLastRoute(params) {
3221
3101
  route: params.route,
3222
3102
  sessionKey: params.route.sessionKey
3223
3103
  });
3224
- if (params.dmRouteTarget && inboundLastRouteSessionKey === params.route.mainSessionKey && shouldUpdateMainLastRoute) {
3104
+ if (!params.dmRouteTarget || inboundLastRouteSessionKey !== params.route.mainSessionKey) return;
3105
+ if (shouldUpdateMainLastRoute) {
3225
3106
  params.updateLastRoute({
3226
3107
  cfg: params.cfg,
3227
3108
  backgroundTasks: params.backgroundTasks,
@@ -3235,12 +3116,12 @@ function updateWhatsAppMainLastRoute(params) {
3235
3116
  });
3236
3117
  return;
3237
3118
  }
3238
- if (params.dmRouteTarget && inboundLastRouteSessionKey === params.route.mainSessionKey && params.pinnedMainDmRecipient) logVerbose$1(`Skipping main-session last route update for ${params.dmRouteTarget} (pinned owner ${params.pinnedMainDmRecipient})`);
3119
+ if (params.pinnedMainDmRecipient) logVerbose$1(`Skipping main-session last route update for ${params.dmRouteTarget} (pinned owner ${params.pinnedMainDmRecipient})`);
3239
3120
  }
3240
3121
  function createWhatsAppReplyPlan(params) {
3241
- const conversationId = params.inbound.conversation.id;
3122
+ const conversationId = params.conversationId;
3242
3123
  const statusReactionController = params.statusReactionController ?? null;
3243
- const textLimit = params.maxMediaTextChunkLimit ?? resolveTextChunkLimit(params.cfg, "whatsapp");
3124
+ const textLimit = resolveTextChunkLimit(params.cfg, "whatsapp");
3244
3125
  const chunkMode = resolveChunkMode(params.cfg, "whatsapp", params.route.accountId);
3245
3126
  const tableMode = resolveMarkdownTableMode$1({
3246
3127
  cfg: params.cfg,
@@ -3248,11 +3129,12 @@ function createWhatsAppReplyPlan(params) {
3248
3129
  accountId: params.route.accountId
3249
3130
  });
3250
3131
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
3251
- const replyPolicy = resolveWhatsAppInboundReplyPolicy({
3132
+ const sourceReplyDeliveryMode = params.context.ChatType === "group" || params.context.ChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
3252
3133
  cfg: params.cfg,
3253
- ctx: params.context,
3254
- blockStreamingEnabled: resolveChannelStreamingBlockEnabled(params.cfg.channels?.whatsapp)
3255
- });
3134
+ ctx: params.context
3135
+ }) : void 0;
3136
+ const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
3137
+ const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(params.cfg.channels?.whatsapp);
3256
3138
  let didSendReply = false;
3257
3139
  let didLogHeartbeatStrip = false;
3258
3140
  const recordDeliveredPayload = (payload) => {
@@ -3289,10 +3171,13 @@ function createWhatsAppReplyPlan(params) {
3289
3171
  });
3290
3172
  throw error;
3291
3173
  }
3292
- const result = createWhatsAppChannelDeliveryResult({
3174
+ const messageIds = listMessageReceiptPlatformIds(delivery.receipt);
3175
+ const result = {
3176
+ receipt: delivery.receipt,
3177
+ ...messageIds.length > 0 ? { messageIds } : {},
3293
3178
  content: reply.text,
3294
- delivery
3295
- });
3179
+ visibleReplySent: delivery.providerAccepted
3180
+ };
3296
3181
  if (!result.visibleReplySent) {
3297
3182
  params.replyLogger.warn({
3298
3183
  correlationId: params.transport.correlationId ?? null,
@@ -3324,9 +3209,9 @@ function createWhatsAppReplyPlan(params) {
3324
3209
  }
3325
3210
  },
3326
3211
  onSettled: async () => {
3327
- const flushResult = await mediaOnlyCoalescer.flushAll();
3328
- logWhatsAppMediaOnlyFlushResult(flushResult);
3329
- return { visibleReplySent: didSendReply || flushResult.delivered > 0 };
3212
+ const delivered = await mediaOnlyCoalescer.flushAll();
3213
+ logWhatsAppMediaOnlyFlushResult(delivered);
3214
+ return { visibleReplySent: didSendReply || delivered > 0 };
3330
3215
  },
3331
3216
  onReplyStart: params.transport.sendComposing
3332
3217
  },
@@ -3347,10 +3232,7 @@ function createWhatsAppReplyPlan(params) {
3347
3232
  info,
3348
3233
  mediaUrls,
3349
3234
  reply
3350
- }) ? {
3351
- delivered: 0,
3352
- droppedDuplicateMedia: 0
3353
- } : await mediaOnlyCoalescer.flushNonDuplicateMedia(mediaUrls) : await mediaOnlyCoalescer.flushAll());
3235
+ }) ? 0 : await mediaOnlyCoalescer.flushNonDuplicateMedia(mediaUrls) : await mediaOnlyCoalescer.flushAll());
3354
3236
  return normalizedDeliveryPayload;
3355
3237
  },
3356
3238
  durable: (payload, info) => {
@@ -3391,7 +3273,7 @@ function createWhatsAppReplyPlan(params) {
3391
3273
  };
3392
3274
  return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { onMediaAccepted: (mediaUrl) => {
3393
3275
  didSendReply = true;
3394
- logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.supersedeMedia(mediaUrl));
3276
+ logWhatsAppMediaOnlyFlushResult(0, mediaOnlyCoalescer.supersedeMedia(mediaUrl));
3395
3277
  } });
3396
3278
  },
3397
3279
  onDelivered: (payload, _info, result) => {
@@ -3399,20 +3281,23 @@ function createWhatsAppReplyPlan(params) {
3399
3281
  },
3400
3282
  onError: (err, info) => {
3401
3283
  if (didSendReply) markWhatsAppVisibleDeliveryError(err);
3402
- logWhatsAppReplyDeliveryError({
3403
- err,
3404
- info,
3284
+ params.replyLogger.error({
3285
+ err: normalizeErrForLog(err),
3286
+ replyKind: info.kind,
3287
+ correlationId: params.transport.correlationId ?? null,
3405
3288
  connectionId: params.connectionId,
3406
- transport: params.transport,
3407
- replyLogger: params.replyLogger
3408
- });
3289
+ conversationId: params.transport.conversationId,
3290
+ chatId: params.transport.chatJid,
3291
+ to: params.transport.conversationId,
3292
+ from: params.transport.recipientJid
3293
+ }, "auto-reply delivery failed");
3409
3294
  }
3410
3295
  },
3411
3296
  replyOptions: {
3412
3297
  ...params.turnAdoptionLifecycle ? { turnAdoptionLifecycle: params.turnAdoptionLifecycle } : {},
3413
- suppressTyping: replyPolicy.suppressTyping,
3414
- disableBlockStreaming: replyPolicy.disableBlockStreaming,
3415
- ...replyPolicy.sourceReplyDeliveryMode ? { sourceReplyDeliveryMode: replyPolicy.sourceReplyDeliveryMode } : {},
3298
+ suppressTyping: sourceRepliesAreToolOnly && params.context.ChatType === "group" && params.context.WasMentioned !== true,
3299
+ disableBlockStreaming: sourceRepliesAreToolOnly ? true : typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0,
3300
+ ...sourceReplyDeliveryMode ? { sourceReplyDeliveryMode } : {},
3416
3301
  onModelSelected: params.onModelSelected,
3417
3302
  ...statusReactionController ? {
3418
3303
  onToolStart: async (payload) => {
@@ -3482,7 +3367,6 @@ function buildInboundLine(params) {
3482
3367
  //#endregion
3483
3368
  //#region extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts
3484
3369
  async function createWhatsAppStatusReactionController(params) {
3485
- if (!params.msg.event.id) return null;
3486
3370
  if ((params.cfg.messages?.statusReactions)?.enabled !== true) return null;
3487
3371
  const eligibility = await resolveWhatsAppReactionEligibility(params);
3488
3372
  if (eligibility.status === "disabled") return null;
@@ -3490,12 +3374,8 @@ async function createWhatsAppStatusReactionController(params) {
3490
3374
  return createStatusReactionController({
3491
3375
  enabled: true,
3492
3376
  adapter: {
3493
- setReaction: async (emoji) => {
3494
- await sendReactionWhatsApp(chatId, messageId, emoji, reactionOptions);
3495
- },
3496
- clearReaction: async () => {
3497
- await sendReactionWhatsApp(chatId, messageId, "", reactionOptions);
3498
- }
3377
+ setReaction: (emoji) => sendReactionWhatsApp(chatId, messageId, emoji, reactionOptions),
3378
+ clearReaction: () => sendReactionWhatsApp(chatId, messageId, "", reactionOptions)
3499
3379
  },
3500
3380
  initialEmoji,
3501
3381
  emojis: void 0,
@@ -3559,7 +3439,7 @@ async function processMessage(params) {
3559
3439
  channel: "whatsapp",
3560
3440
  accountId: account.accountId
3561
3441
  });
3562
- const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
3442
+ const { storePath, envelopeOptions, previousTimestamp } = await resolveInboundSessionEnvelopeContextAsync({
3563
3443
  cfg: params.cfg,
3564
3444
  agentId: params.route.agentId,
3565
3445
  sessionKey: params.route.sessionKey
@@ -3681,14 +3561,13 @@ async function processMessage(params) {
3681
3561
  accountConfig: account,
3682
3562
  peerId: dmRouteTarget ?? conversationId
3683
3563
  });
3684
- const commandAuthorization = commandAuthorized === void 0 ? { kind: "not_checked" } : commandAuthorized ? { kind: "authorized" } : { kind: "denied" };
3685
3564
  const { inbound, turnInput, ctxPayload } = await prepareWhatsAppInboundContext({
3686
3565
  bodyForAgent: msgForAgent.payload.body,
3687
3566
  combinedBody,
3688
3567
  command: {
3689
3568
  kind: isTextCommand ? "text-slash" : "normal",
3690
3569
  body: commandBody,
3691
- authorization: commandAuthorization
3570
+ ...commandAuthorized !== void 0 ? { authorized: commandAuthorized } : {}
3692
3571
  },
3693
3572
  groupHistory: visibleGroupHistory,
3694
3573
  groupHistoryLimit: params.groupHistoryLimit,
@@ -3754,8 +3633,7 @@ async function processMessage(params) {
3754
3633
  context: ctxPayload,
3755
3634
  deliverReply: deliverWebReply,
3756
3635
  maxMediaBytes: params.maxMediaBytes,
3757
- maxMediaTextChunkLimit: params.maxMediaTextChunkLimit,
3758
- inbound,
3636
+ conversationId,
3759
3637
  onModelSelected,
3760
3638
  replyLogger: params.replyLogger,
3761
3639
  replyPipeline: {
@@ -4034,7 +3912,6 @@ function createWebOnMessageHandler(params) {
4034
3912
  function isNonRetryableWebCloseStatus(statusCode) {
4035
3913
  return statusCode === 440;
4036
3914
  }
4037
- const loadReplyResolverRuntime = createLazyRuntimeModule(() => import("./reply-resolver.runtime-Ded8D58Y.mjs"));
4038
3915
  function resolveWebMonitorConfigSnapshot(params) {
4039
3916
  const account = resolveWhatsAppAccount({
4040
3917
  cfg: params.cfg,
@@ -4073,7 +3950,7 @@ function isRetryableAuthUnstableError(error) {
4073
3950
  const DEFAULT_TRANSPORT_TIMEOUT_MS = 3e5;
4074
3951
  const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS = 12e5;
4075
3952
  async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSocket, keepAlive = true, replyResolver, runtime = defaultRuntime, abortSignal, tuning = {}) {
4076
- const activeReplyResolver = replyResolver ?? (await loadReplyResolverRuntime()).getReplyFromConfig;
3953
+ const activeReplyResolver = replyResolver ?? (await import("./reply-resolver.runtime-Ded8D58Y.mjs")).getReplyFromConfig;
4077
3954
  const runId = newConnectionId();
4078
3955
  const replyLogger = getChildLogger$1({
4079
3956
  module: "web-auto-reply",
@@ -4220,7 +4097,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
4220
4097
  uptimeMs: snapshot.uptimeMs,
4221
4098
  ...minutesSinceLastMessage !== null && minutesSinceLastMessage > 30 ? { minutesSinceLastMessage } : {}
4222
4099
  };
4223
- statusController.noteTransportActivity(snapshot.lastTransportActivityAt);
4100
+ statusController.noteTransportActivity(snapshot.lastTransportActivityAt, authAgeMs);
4224
4101
  if (minutesSinceLastMessage && minutesSinceLastMessage > 30) heartbeatLogger.warn(logData, "⚠️ web gateway heartbeat - no messages in 30+ minutes");
4225
4102
  else heartbeatLogger.info(logData, "web gateway heartbeat");
4226
4103
  },
@@ -4250,67 +4127,41 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
4250
4127
  await controller.shutdown();
4251
4128
  break;
4252
4129
  }
4253
- if (setupDecision) {
4254
- statusController.noteReconnectAttempts(setupDecision.reconnectAttempts);
4255
- statusController.noteClose({
4256
- statusCode: setupDecision.normalized.statusCode,
4257
- error: formatError(error),
4258
- reconnectAttempts: setupDecision.reconnectAttempts,
4259
- healthState: setupDecision.healthState
4260
- });
4261
- if (setupDecision.action === "stop") {
4262
- reconnectLogger.warn({
4263
- connectionId,
4264
- status: setupDecision.normalized.statusLabel,
4265
- reconnectAttempts: setupDecision.reconnectAttempts,
4266
- maxAttempts: reconnectPolicy.maxAttempts
4267
- }, "web reconnect: setup status error; max attempts reached");
4268
- if (setupDecision.healthState === "logged-out") runtime.error(`WhatsApp session logged out during setup. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
4269
- else if (setupDecision.healthState === "conflict") runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then restart the channel. To force a fresh QR, run \`${formatCliCommand("openclaw channels logout --channel whatsapp")}\` before \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
4270
- else runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}) after ${setupDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` if the issue persists.`);
4271
- await controller.shutdown();
4272
- break;
4273
- }
4274
- reconnectLogger.info({
4275
- connectionId,
4276
- status: setupDecision.normalized.statusLabel,
4277
- reconnectAttempts: setupDecision.reconnectAttempts,
4278
- delayMs: setupDecision.delayMs
4279
- }, "web reconnect: setup status error; retrying");
4280
- runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}). Retry ${setupDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts || "∞"} in ${formatDurationPrecise(setupDecision.delayMs ?? 0)}.`);
4281
- try {
4282
- await controller.waitBeforeRetry(setupDecision.delayMs ?? 0);
4283
- } catch {
4284
- break;
4285
- }
4286
- continue;
4287
- }
4288
- if (!isRetryableAuthUnstableError(error)) throw error;
4289
- const retryDecision = controller.consumeReconnectAttempt();
4290
- statusController.noteReconnectAttempts(retryDecision.reconnectAttempts);
4130
+ const authError = !setupDecision && isRetryableAuthUnstableError(error) ? error : void 0;
4131
+ if (!setupDecision && !authError) throw error;
4132
+ const decision = setupDecision ?? controller.consumeReconnectAttempt();
4133
+ const normalized = setupDecision?.normalized;
4134
+ statusController.noteReconnectAttempts(decision.reconnectAttempts);
4291
4135
  statusController.noteClose({
4292
- error: error.message,
4293
- reconnectAttempts: retryDecision.reconnectAttempts,
4294
- healthState: retryDecision.healthState
4136
+ ...normalized ? { statusCode: normalized.statusCode } : {},
4137
+ error: setupDecision ? formatError(error) : authError?.message,
4138
+ reconnectAttempts: decision.reconnectAttempts,
4139
+ healthState: decision.healthState
4295
4140
  });
4296
- if (retryDecision.action === "stop") {
4141
+ const logData = {
4142
+ connectionId,
4143
+ ...normalized ? { status: normalized.statusLabel } : {},
4144
+ reconnectAttempts: decision.reconnectAttempts
4145
+ };
4146
+ if (decision.action === "stop") {
4297
4147
  reconnectLogger.warn({
4298
- connectionId,
4299
- reconnectAttempts: retryDecision.reconnectAttempts,
4148
+ ...logData,
4300
4149
  maxAttempts: reconnectPolicy.maxAttempts
4301
- }, "web reconnect: auth state stayed unstable; max attempts reached");
4302
- runtime.error(`WhatsApp auth state is still stabilizing after ${retryDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Stopping web monitoring.`);
4150
+ }, normalized ? "web reconnect: setup status error; max attempts reached" : "web reconnect: auth state stayed unstable; max attempts reached");
4151
+ if (!normalized) runtime.error(`WhatsApp auth state is still stabilizing after ${decision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Stopping web monitoring.`);
4152
+ else if (decision.healthState === "logged-out") runtime.error(`WhatsApp session logged out during setup. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
4153
+ else if (decision.healthState === "conflict") runtime.error(`WhatsApp Web connection closed during setup (status ${normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then restart the channel. To force a fresh QR, run \`${formatCliCommand("openclaw channels logout --channel whatsapp")}\` before \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
4154
+ else runtime.error(`WhatsApp Web connection closed during setup (status ${normalized.statusLabel}) after ${decision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` if the issue persists.`);
4303
4155
  await controller.shutdown();
4304
4156
  break;
4305
4157
  }
4306
4158
  reconnectLogger.info({
4307
- connectionId,
4308
- reconnectAttempts: retryDecision.reconnectAttempts,
4309
- delayMs: retryDecision.delayMs
4310
- }, "web reconnect: auth state still stabilizing during inbox attach; retrying");
4311
- runtime.error(`WhatsApp auth state is still stabilizing. Retry ${retryDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts || "∞"} for inbox attach in ${formatDurationPrecise(retryDecision.delayMs ?? 0)}.`);
4159
+ ...logData,
4160
+ delayMs: decision.delayMs
4161
+ }, normalized ? "web reconnect: setup status error; retrying" : "web reconnect: auth state still stabilizing during inbox attach; retrying");
4162
+ runtime.error(normalized ? `WhatsApp Web connection closed during setup (status ${normalized.statusLabel}). Retry ${decision.reconnectAttempts}/${reconnectPolicy.maxAttempts || "∞"} in ${formatDurationPrecise(decision.delayMs ?? 0)}.` : `WhatsApp auth state is still stabilizing. Retry ${decision.reconnectAttempts}/${reconnectPolicy.maxAttempts || "∞"} for inbox attach in ${formatDurationPrecise(decision.delayMs ?? 0)}.`);
4312
4163
  try {
4313
- await controller.waitBeforeRetry(retryDecision.delayMs ?? 0);
4164
+ await controller.waitBeforeRetry(decision.delayMs ?? 0);
4314
4165
  } catch {
4315
4166
  break;
4316
4167
  }