@openclaw/whatsapp 2026.5.7 → 2026.5.10-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 (60) hide show
  1. package/dist/{access-control-CTj7P7WP.js → access-control-CdP8S3rJ.js} +66 -111
  2. package/dist/{action-runtime-BqYZmBX_.js → action-runtime-CMhO8liB.js} +1 -1
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/api.js +120 -12
  6. package/dist/{auth-store-BNZmNP-s.js → auth-store-CjEimXKX.js} +19 -41
  7. package/dist/{channel-CTr3YjO8.js → channel-BxntAClG.js} +39 -12
  8. package/dist/channel-config-api.js +1 -1
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel-react-action-BuSTzmDX.js → channel-react-action-DJvzhIPw.js} +1 -1
  11. package/dist/{channel.runtime-C3i9Svfj.js → channel.runtime-CNdPCK3G.js} +5 -5
  12. package/dist/{channel.setup-BYtkDwHe.js → channel.setup-BqlcEVzR.js} +4 -4
  13. package/dist/{connection-controller-BCIuChXD.js → connection-controller-DX2phM7V.js} +6 -197
  14. package/dist/constants.js +1 -1
  15. package/dist/contract-api.js +7 -7
  16. package/dist/{directory-config-BNCbgnmy.js → directory-config-o-muXLgk.js} +1 -1
  17. package/dist/directory-contract-api.js +1 -1
  18. package/dist/doctor-contract-api.js +1 -1
  19. package/dist/{identity-DhaC0MoD.js → identity-KxdJfH7p.js} +1 -1
  20. package/dist/legacy-session-surface-api.js +1 -1
  21. package/dist/legacy-state-migrations-api.js +1 -1
  22. package/dist/light-runtime-api.js +3 -3
  23. package/dist/{login-DSUTCcJ7.js → login-DV99D0IN.js} +4 -3
  24. package/dist/{login-qr-dfs8tfGo.js → login-qr-cKndAXTv.js} +3 -2
  25. package/dist/login-qr-runtime.js +1 -1
  26. package/dist/{monitor-C5_C_RGJ.js → monitor-mfy3Mmq-.js} +111 -687
  27. package/dist/{outbound-adapter-CbR17Ehj.js → outbound-adapter-B9ZW8sht.js} +3 -3
  28. package/dist/{outbound-base-BmzhutvF.js → outbound-base-MVpg2zU2.js} +8 -3
  29. package/dist/{outbound-media-contract-BPdOq7Hb.js → outbound-media-contract-BzWwBppP.js} +37 -41
  30. package/dist/outbound-payload-test-api.js +1 -1
  31. package/dist/{quoted-message-BXs55Teh.js → quoted-message-DQ6hAr34.js} +1 -1
  32. package/dist/runtime-api.js +11 -10
  33. package/dist/security-contract-api.js +1 -1
  34. package/dist/send-api-DsbCqDre.js +697 -0
  35. package/dist/{send-CxctcFGT.js → send-u88yh8Hw.js} +2 -3
  36. package/dist/session-DeIU_vjJ.js +198 -0
  37. package/dist/{session.runtime-C1eSE_KK.js → session.runtime-B9VlYnWQ.js} +1 -1
  38. package/dist/{setup-surface-CF3l3gEZ.js → setup-finalize-D3YuMUBs.js} +5 -52
  39. package/dist/setup-plugin-api.js +1 -1
  40. package/dist/setup-surface-CHjYlKJc.js +45 -0
  41. package/dist/{shared-CTSTwprn.js → shared-CgYT3IzU.js} +7 -7
  42. package/dist/{state-migrations-DnCdvoYD.js → state-migrations-CtdIArlK.js} +2 -1
  43. package/dist/test-api.js +2 -2
  44. package/dist/{text-runtime-DFFwk5z6.js → text-runtime-DBKC9uEi.js} +27 -2
  45. package/package.json +6 -6
  46. package/dist/rolldown-runtime-RkAeH_Qm.js +0 -27
  47. /package/dist/{active-listener-N7GFKsuN.js → active-listener-D2NHQ5Og.js} +0 -0
  48. /package/dist/{agent-tools-login-DpRxLNRB.js → agent-tools-login-DYKYW5Nh.js} +0 -0
  49. /package/dist/{audio-preflight.runtime-Bcdsl_ri.js → audio-preflight.runtime-CIFjeaEN.js} +0 -0
  50. /package/dist/{command-policy-O8sRnqUV.js → command-policy-BgZRZ31o.js} +0 -0
  51. /package/dist/{config-schema-Dmo66Ubb.js → config-schema-cSC7M4YA.js} +0 -0
  52. /package/dist/{constants-DLGCIbfl.js → constants-yKXGCGU9.js} +0 -0
  53. /package/dist/{doctor-A9tXm9Om.js → doctor-BrgepDUP.js} +0 -0
  54. /package/dist/{group-session-contract-B8QBn0I2.js → group-session-contract-DL-SLclv.js} +0 -0
  55. /package/dist/{reply-resolver.runtime-BRCgoO4C.js → reply-resolver.runtime-CROyXXQh.js} +0 -0
  56. /package/dist/{runtime-6fZ67abR.js → runtime-DdodQB8G.js} +0 -0
  57. /package/dist/{runtime-group-policy-Gp1lQ0at.js → runtime-group-policy-BH_U23qp.js} +0 -0
  58. /package/dist/{security-contract-DqH8JDX6.js → security-contract-BzlZFreO.js} +0 -0
  59. /package/dist/{session-contract-B0efQf9P.js → session-contract-BNUscPf4.js} +0 -0
  60. /package/dist/{setup-core-DZKvpzbH.js → setup-core-DnhO0PjG.js} +0 -0
@@ -1,16 +1,17 @@
1
+ import { a as markdownToWhatsApp, i as jidToE164, o as resolveJidToE164, r as isSelfChatMode, t as text_runtime_exports } from "./text-runtime-DBKC9uEi.js";
1
2
  import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BDqgpFEB.js";
2
- import { n as isWhatsAppNewsletterJid, t as isWhatsAppGroupJid$1 } from "./normalize-target-nXxC_hxG.js";
3
+ import { t as isWhatsAppGroupJid } from "./normalize-target-nXxC_hxG.js";
3
4
  import { t as resolveWhatsAppReactionLevel } from "./reaction-level-CRVEVAfi.js";
4
- import "./normalize-Y3eBdM8a.js";
5
5
  import { n as getStatusCode, t as formatError } from "./session-errors-CSuKZPub.js";
6
- import { a as markdownToWhatsApp, c as toWhatsappJidWithLid, i as jidToE164, o as resolveJidToE164, r as isSelfChatMode, s as toWhatsappJid, t as text_runtime_exports } from "./text-runtime-DFFwk5z6.js";
7
- import { i as prepareWhatsAppOutboundMedia, o as sendWhatsAppOutboundWithRetry, r as normalizeWhatsAppPayloadTextPreservingIndentation, t as normalizeWhatsAppOutboundPayload } from "./outbound-media-contract-BPdOq7Hb.js";
8
- import { r as sendReactionWhatsApp } from "./send-CxctcFGT.js";
9
- import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-BXs55Teh.js";
10
- import { _ as readWebSelfIdentityForDecision, a as getWebAuthAgeMs, h as readWebSelfId, r as WhatsAppAuthUnstableError, s as logoutWeb } from "./auth-store-BNZmNP-s.js";
11
- import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-DhaC0MoD.js";
12
- import { i as resolveWhatsAppInboundPolicy, n as checkInboundAccessControl, r as resolveWhatsAppCommandAuthorized } from "./access-control-CTj7P7WP.js";
13
- import { c as waitForWaConnection, d as DEFAULT_RECONNECT_POLICY, f as computeBackoff, g as sleepWithAbort, h as resolveReconnectPolicy, l as resolveWhatsAppSocketTiming, m as resolveHeartbeatSeconds, n as WhatsAppConnectionController, o as createWaSocket, p as newConnectionId } from "./connection-controller-BCIuChXD.js";
6
+ import { i as prepareWhatsAppOutboundMedia, o as sendWhatsAppOutboundWithRetry, r as normalizeWhatsAppPayloadTextPreservingIndentation, t as normalizeWhatsAppOutboundPayload } from "./outbound-media-contract-BzWwBppP.js";
7
+ import { r as sendReactionWhatsApp } from "./send-u88yh8Hw.js";
8
+ import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-DQ6hAr34.js";
9
+ import { _ as readWebSelfIdentityForDecision, a as getWebAuthAgeMs, h as readWebSelfId, r as WhatsAppAuthUnstableError, s as logoutWeb } from "./auth-store-CjEimXKX.js";
10
+ import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-KxdJfH7p.js";
11
+ import { i as resolveWhatsAppInboundPolicy, n as checkInboundAccessControl, r as resolveWhatsAppCommandAuthorized } from "./access-control-CdP8S3rJ.js";
12
+ import { a as mayContainWhatsAppOutboundMention, c as extractContactContext, d as extractMediaPlaceholder, f as extractMentionedJids, i as addWhatsAppOutboundMentionsToContent, l as extractContextInfo, m as hasInboundUserContent, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as extractText, r as normalizeWhatsAppSendResult, s as describeReplyContext, t as createWebSendApi, u as extractLocationData } from "./send-api-DsbCqDre.js";
13
+ import { i as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-DeIU_vjJ.js";
14
+ import { c as newConnectionId, d as sleepWithAbort, l as resolveHeartbeatSeconds, n as WhatsAppConnectionController, o as DEFAULT_RECONNECT_POLICY, s as computeBackoff, u as resolveReconnectPolicy } from "./connection-controller-DX2phM7V.js";
14
15
  import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
15
16
  import { getChildLogger, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
16
17
  import { createAckReactionHandle, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
@@ -18,22 +19,23 @@ import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
18
19
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
19
20
  import { createSubsystemLogger, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
20
21
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
22
+ import { evaluateSupplementalContextVisibility, filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
21
23
  import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia as loadWebMedia$1, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
24
+ import { createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
22
25
  import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
23
26
  import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
24
27
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId as normalizeAccountId$1, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
25
28
  import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
26
- import { evaluateSupplementalContextVisibility, filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
27
29
  import { loadSessionStore, resolveStorePath, updateLastRoute, updateSessionStore } from "openclaw/plugin-sdk/session-store-runtime";
30
+ import { DisconnectReason as DisconnectReason$1, downloadMediaMessage, isJidGroup, normalizeMessageContent as normalizeMessageContent$1 } from "@whiskeysockets/baileys";
31
+ import { formatLocationText, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
32
+ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
28
33
  import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContext, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
29
34
  import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
30
35
  import { hasControlCommand, hasControlCommand as hasControlCommand$1, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
31
36
  import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
32
37
  import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
33
38
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
34
- import { DisconnectReason as DisconnectReason$1, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent, normalizeMessageContent as normalizeMessageContent$1 } from "@whiskeysockets/baileys";
35
- import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
36
- import { formatLocationText, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
37
39
  import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
38
40
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
39
41
  import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
@@ -48,7 +50,6 @@ import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-re
48
50
  import { resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
49
51
  import { formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1 } from "openclaw/plugin-sdk/channel-envelope";
50
52
  import { toLocationContext } from "openclaw/plugin-sdk/channel-location";
51
- import { createChannelReplyPipeline, resolveChannelSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-reply-pipeline";
52
53
  //#region extensions/whatsapp/src/inbound/dedupe.ts
53
54
  const RECENT_WEB_MESSAGE_TTL_MS = 20 * 6e4;
54
55
  const RECENT_WEB_MESSAGE_MAX = 5e3;
@@ -137,359 +138,6 @@ function isRecentOutboundMessage(params) {
137
138
  return recentOutboundMessages.peek(key);
138
139
  }
139
140
  //#endregion
140
- //#region extensions/whatsapp/src/vcard.ts
141
- const ALLOWED_VCARD_KEYS = new Set([
142
- "FN",
143
- "N",
144
- "TEL"
145
- ]);
146
- function parseVcard(vcard) {
147
- if (!vcard) return { phones: [] };
148
- const lines = vcard.split(/\r?\n/);
149
- let nameFromN;
150
- let nameFromFn;
151
- const phones = [];
152
- for (const rawLine of lines) {
153
- const line = rawLine.trim();
154
- if (!line) continue;
155
- const colonIndex = line.indexOf(":");
156
- if (colonIndex === -1) continue;
157
- const key = line.slice(0, colonIndex).toUpperCase();
158
- const rawValue = line.slice(colonIndex + 1).trim();
159
- if (!rawValue) continue;
160
- const baseKey = normalizeVcardKey(key);
161
- if (!baseKey || !ALLOWED_VCARD_KEYS.has(baseKey)) continue;
162
- const value = cleanVcardValue(rawValue);
163
- if (!value) continue;
164
- if (baseKey === "FN" && !nameFromFn) {
165
- nameFromFn = normalizeVcardName(value);
166
- continue;
167
- }
168
- if (baseKey === "N" && !nameFromN) {
169
- nameFromN = normalizeVcardName(value);
170
- continue;
171
- }
172
- if (baseKey === "TEL") {
173
- const phone = normalizeVcardPhone(value);
174
- if (phone) phones.push(phone);
175
- }
176
- }
177
- return {
178
- name: nameFromFn ?? nameFromN,
179
- phones
180
- };
181
- }
182
- function normalizeVcardKey(key) {
183
- const [primary] = key.split(";");
184
- if (!primary) return;
185
- const segments = primary.split(".");
186
- return segments[segments.length - 1] || void 0;
187
- }
188
- function cleanVcardValue(value) {
189
- return value.replace(/\\n/gi, " ").replace(/\\,/g, ",").replace(/\\;/g, ";").trim();
190
- }
191
- function normalizeVcardName(value) {
192
- return value.replace(/;/g, " ").replace(/\s+/g, " ").trim();
193
- }
194
- function normalizeVcardPhone(value) {
195
- const trimmed = value.trim();
196
- if (!trimmed) return "";
197
- if (normalizeLowercaseStringOrEmpty(trimmed).startsWith("tel:")) return trimmed.slice(4).trim();
198
- return trimmed;
199
- }
200
- //#endregion
201
- //#region extensions/whatsapp/src/inbound/extract.ts
202
- const MESSAGE_WRAPPER_KEYS = [
203
- "botInvokeMessage",
204
- "ephemeralMessage",
205
- "viewOnceMessage",
206
- "viewOnceMessageV2",
207
- "viewOnceMessageV2Extension",
208
- "documentWithCaptionMessage",
209
- "groupMentionedMessage"
210
- ];
211
- const MESSAGE_CONTENT_KEYS = [
212
- "conversation",
213
- "extendedTextMessage",
214
- "imageMessage",
215
- "videoMessage",
216
- "audioMessage",
217
- "documentMessage",
218
- "stickerMessage",
219
- "locationMessage",
220
- "liveLocationMessage",
221
- "contactMessage",
222
- "contactsArrayMessage",
223
- "buttonsResponseMessage",
224
- "listResponseMessage",
225
- "templateButtonReplyMessage",
226
- "interactiveResponseMessage",
227
- "buttonsMessage",
228
- "listMessage"
229
- ];
230
- function fallbackNormalizeMessageContent(message) {
231
- let current = message;
232
- while (current && typeof current === "object") {
233
- let unwrapped = false;
234
- for (const key of MESSAGE_WRAPPER_KEYS) {
235
- const candidate = current[key];
236
- if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message) {
237
- current = candidate.message;
238
- unwrapped = true;
239
- break;
240
- }
241
- }
242
- if (!unwrapped) break;
243
- }
244
- return current;
245
- }
246
- function normalizeMessage(message) {
247
- if (typeof normalizeMessageContent === "function") return normalizeMessageContent(message);
248
- return fallbackNormalizeMessageContent(message);
249
- }
250
- function fallbackGetContentType(message) {
251
- const normalized = fallbackNormalizeMessageContent(message);
252
- if (!normalized || typeof normalized !== "object") return;
253
- for (const key of MESSAGE_CONTENT_KEYS) if (normalized[key] != null) return key;
254
- }
255
- function getMessageContentType(message) {
256
- if (typeof getContentType === "function") return getContentType(message);
257
- return fallbackGetContentType(message);
258
- }
259
- function extractMessage(message) {
260
- if (typeof extractMessageContent === "function") return extractMessageContent(message);
261
- const normalized = fallbackNormalizeMessageContent(message);
262
- const contentType = fallbackGetContentType(normalized);
263
- if (!normalized || !contentType || contentType === "conversation") return normalized;
264
- const candidate = normalized[contentType];
265
- return candidate && typeof candidate === "object" ? candidate : normalized;
266
- }
267
- function getFutureProofInnerMessage(message) {
268
- const contentType = getMessageContentType(message);
269
- const candidate = contentType ? message[contentType] : void 0;
270
- if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message && typeof candidate.message === "object") {
271
- const inner = normalizeMessage(candidate.message);
272
- if (inner) {
273
- const innerType = getMessageContentType(inner);
274
- if (innerType && innerType !== contentType) return inner;
275
- }
276
- }
277
- }
278
- function buildMessageChain(message) {
279
- const chain = [];
280
- let current = normalizeMessage(message);
281
- while (current && chain.length < 4) {
282
- chain.push(current);
283
- current = getFutureProofInnerMessage(current);
284
- }
285
- return chain;
286
- }
287
- function unwrapMessage$1(message) {
288
- return buildMessageChain(message).at(-1);
289
- }
290
- function extractContextInfoFromMessage(message) {
291
- const contentType = getMessageContentType(message);
292
- const candidate = contentType ? message[contentType] : void 0;
293
- const contextInfo = candidate && typeof candidate === "object" && "contextInfo" in candidate ? candidate.contextInfo : void 0;
294
- if (contextInfo) return contextInfo;
295
- const fallback = message.extendedTextMessage?.contextInfo ?? message.imageMessage?.contextInfo ?? message.videoMessage?.contextInfo ?? message.documentMessage?.contextInfo ?? message.audioMessage?.contextInfo ?? message.stickerMessage?.contextInfo ?? message.buttonsResponseMessage?.contextInfo ?? message.listResponseMessage?.contextInfo ?? message.templateButtonReplyMessage?.contextInfo ?? message.interactiveResponseMessage?.contextInfo ?? message.buttonsMessage?.contextInfo ?? message.listMessage?.contextInfo;
296
- if (fallback) return fallback;
297
- for (const value of Object.values(message)) {
298
- if (!value || typeof value !== "object") continue;
299
- if ("contextInfo" in value) {
300
- const candidateContext = value.contextInfo;
301
- if (candidateContext) return candidateContext;
302
- }
303
- if ("message" in value) {
304
- const inner = value.message;
305
- if (inner) {
306
- const innerCtx = extractContextInfo(inner);
307
- if (innerCtx) return innerCtx;
308
- }
309
- }
310
- }
311
- }
312
- function extractContextInfo(message) {
313
- for (const candidate of buildMessageChain(message)) {
314
- const contextInfo = extractContextInfoFromMessage(candidate);
315
- if (contextInfo) return contextInfo;
316
- }
317
- }
318
- function extractMentionedJids(rawMessage) {
319
- const message = unwrapMessage$1(rawMessage);
320
- if (!message) return;
321
- const flattened = [
322
- message.extendedTextMessage?.contextInfo?.mentionedJid,
323
- message.imageMessage?.contextInfo?.mentionedJid,
324
- message.videoMessage?.contextInfo?.mentionedJid,
325
- message.documentMessage?.contextInfo?.mentionedJid,
326
- message.audioMessage?.contextInfo?.mentionedJid,
327
- message.stickerMessage?.contextInfo?.mentionedJid,
328
- message.buttonsResponseMessage?.contextInfo?.mentionedJid,
329
- message.listResponseMessage?.contextInfo?.mentionedJid
330
- ].flatMap((arr) => arr ?? []).filter(Boolean);
331
- if (flattened.length === 0) return;
332
- return Array.from(new Set(flattened));
333
- }
334
- function extractText(rawMessage) {
335
- const message = unwrapMessage$1(rawMessage);
336
- if (!message) return;
337
- const extracted = extractMessage(message);
338
- const candidates = [message, extracted && extracted !== message ? extracted : void 0];
339
- for (const candidate of candidates) {
340
- if (!candidate) continue;
341
- if (typeof candidate.conversation === "string" && candidate.conversation.trim()) return candidate.conversation.trim();
342
- const extended = candidate.extendedTextMessage?.text;
343
- if (extended?.trim()) return extended.trim();
344
- const caption = candidate.imageMessage?.caption ?? candidate.videoMessage?.caption ?? candidate.documentMessage?.caption;
345
- if (caption?.trim()) return caption.trim();
346
- }
347
- const contactPlaceholder = extractContactPlaceholder(message) ?? (extracted && extracted !== message ? extractContactPlaceholder(extracted) : void 0);
348
- if (contactPlaceholder) return contactPlaceholder;
349
- }
350
- function extractMediaPlaceholder(rawMessage) {
351
- const message = unwrapMessage$1(rawMessage);
352
- if (!message) return;
353
- if (message.imageMessage) return "<media:image>";
354
- if (message.videoMessage) return "<media:video>";
355
- if (message.audioMessage) return "<media:audio>";
356
- if (message.documentMessage) return "<media:document>";
357
- if (message.stickerMessage) return "<media:sticker>";
358
- }
359
- function extractContactPlaceholder(rawMessage) {
360
- const contactContext = extractContactContext(rawMessage);
361
- if (!contactContext) return;
362
- if (contactContext.kind === "contact") return "<contact>";
363
- const suffix = contactContext.total === 1 ? "contact" : "contacts";
364
- return `<contacts: ${contactContext.total} ${suffix}>`;
365
- }
366
- function extractContactContext(rawMessage) {
367
- const message = unwrapMessage$1(rawMessage);
368
- if (!message) return;
369
- const contact = message.contactMessage ?? void 0;
370
- if (contact) {
371
- const { name, phones } = describeContact({
372
- displayName: contact.displayName,
373
- vcard: contact.vcard
374
- });
375
- return {
376
- kind: "contact",
377
- total: 1,
378
- contacts: [{
379
- name,
380
- phones
381
- }]
382
- };
383
- }
384
- const contactsArray = message.contactsArrayMessage?.contacts ?? void 0;
385
- if (!contactsArray || contactsArray.length === 0) return;
386
- return {
387
- kind: "contacts",
388
- total: contactsArray.length,
389
- contacts: contactsArray.map((entry) => describeContact({
390
- displayName: entry.displayName,
391
- vcard: entry.vcard
392
- }))
393
- };
394
- }
395
- function describeContact(input) {
396
- const displayName = (input.displayName ?? "").trim();
397
- const parsed = parseVcard(input.vcard ?? void 0);
398
- return {
399
- name: displayName || parsed.name,
400
- phones: parsed.phones
401
- };
402
- }
403
- function extractLocationData(rawMessage) {
404
- const message = unwrapMessage$1(rawMessage);
405
- if (!message) return null;
406
- const live = message.liveLocationMessage ?? void 0;
407
- if (live) {
408
- const latitudeRaw = live.degreesLatitude;
409
- const longitudeRaw = live.degreesLongitude;
410
- if (latitudeRaw != null && longitudeRaw != null) {
411
- const latitude = latitudeRaw;
412
- const longitude = longitudeRaw;
413
- if (Number.isFinite(latitude) && Number.isFinite(longitude)) return {
414
- latitude,
415
- longitude,
416
- accuracy: live.accuracyInMeters ?? void 0,
417
- caption: live.caption ?? void 0,
418
- source: "live",
419
- isLive: true
420
- };
421
- }
422
- }
423
- const location = message.locationMessage ?? void 0;
424
- if (location) {
425
- const latitudeRaw = location.degreesLatitude;
426
- const longitudeRaw = location.degreesLongitude;
427
- if (latitudeRaw != null && longitudeRaw != null) {
428
- const latitude = latitudeRaw;
429
- const longitude = longitudeRaw;
430
- if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
431
- const isLive = Boolean(location.isLive);
432
- return {
433
- latitude,
434
- longitude,
435
- accuracy: location.accuracyInMeters ?? void 0,
436
- name: location.name ?? void 0,
437
- address: location.address ?? void 0,
438
- caption: location.comment ?? void 0,
439
- source: isLive ? "live" : location.name || location.address ? "place" : "pin",
440
- isLive
441
- };
442
- }
443
- }
444
- }
445
- return null;
446
- }
447
- function describeReplyContext(rawMessage) {
448
- const message = unwrapMessage$1(rawMessage);
449
- if (!message) return null;
450
- const contextInfo = extractContextInfo(message);
451
- const quoted = normalizeMessage(contextInfo?.quotedMessage);
452
- if (!quoted) return null;
453
- const location = extractLocationData(quoted);
454
- const locationText = location ? formatLocationText(location) : void 0;
455
- let body = [extractText(quoted), locationText].filter(Boolean).join("\n").trim();
456
- if (!body) body = extractMediaPlaceholder(quoted);
457
- if (!body) {
458
- const quotedType = quoted ? getMessageContentType(quoted) : void 0;
459
- logVerbose(`Quoted message missing extractable body${quotedType ? ` (type ${quotedType})` : ""}`);
460
- return null;
461
- }
462
- const senderJid = contextInfo?.participant ?? void 0;
463
- const sender = resolveComparableIdentity({
464
- jid: senderJid,
465
- label: senderJid ? jidToE164(senderJid) ?? senderJid : "unknown sender"
466
- });
467
- return {
468
- id: contextInfo?.stanzaId || void 0,
469
- body,
470
- sender
471
- };
472
- }
473
- function hasInteractiveResponseContent(message) {
474
- if (!message) return false;
475
- return Boolean(message.buttonsResponseMessage || message.listResponseMessage || message.templateButtonReplyMessage || message.interactiveResponseMessage);
476
- }
477
- /**
478
- * Fast check that a Baileys message carries user-visible inbound content
479
- * (text, media, contact, location, button/list selection). Returns false for
480
- * protocol/receipt/typing notifications that arrive on the same
481
- * `messages.upsert` stream as real messages but should not trigger pairing
482
- * access-control side effects.
483
- */
484
- function hasInboundUserContent(rawMessage) {
485
- if (!rawMessage) return false;
486
- if (extractText(rawMessage)) return true;
487
- if (extractMediaPlaceholder(rawMessage)) return true;
488
- if (extractLocationData(rawMessage)) return true;
489
- for (const candidate of buildMessageChain(rawMessage)) if (hasInteractiveResponseContent(candidate)) return true;
490
- return false;
491
- }
492
- //#endregion
493
141
  //#region extensions/whatsapp/src/inbound/lifecycle.ts
494
142
  function attachEmitterListener(emitter, event, listener) {
495
143
  emitter.on(event, listener);
@@ -561,310 +209,6 @@ async function downloadQuotedInboundMedia(msg, sock) {
561
209
  }, sock);
562
210
  }
563
211
  //#endregion
564
- //#region extensions/whatsapp/src/inbound/outbound-mentions.ts
565
- const CODE_FENCE_RE = /```[\s\S]*?```/g;
566
- const INLINE_CODE_RE = /`[^`\n]+`/g;
567
- const OUTBOUND_MENTION_RE = /@(\+?\d+)/g;
568
- const KNOWN_USER_JID_RE = /^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted|lid|hosted\.lid|c\.us)$/i;
569
- const PHONE_JID_DOMAIN_RE = /^(s\.whatsapp\.net|hosted|c\.us)$/i;
570
- const LID_JID_DOMAIN_RE = /^(lid|hosted\.lid)$/i;
571
- function isWhatsAppGroupJid(jid) {
572
- return jid.endsWith("@g.us");
573
- }
574
- function mayContainWhatsAppOutboundMention(text) {
575
- return /@\+?\d/.test(text);
576
- }
577
- function collectCodeRanges(text) {
578
- const ranges = [];
579
- for (const match of text.matchAll(CODE_FENCE_RE)) ranges.push({
580
- start: match.index,
581
- end: match.index + match[0].length
582
- });
583
- for (const match of text.matchAll(INLINE_CODE_RE)) {
584
- const start = match.index;
585
- if (ranges.some((range) => start >= range.start && start < range.end)) continue;
586
- ranges.push({
587
- start,
588
- end: start + match[0].length
589
- });
590
- }
591
- return ranges.toSorted((a, b) => a.start - b.start);
592
- }
593
- function isInRange(index, ranges) {
594
- return ranges.some((range) => index >= range.start && index < range.end);
595
- }
596
- function normalizeKnownUserJid(value) {
597
- const trimmed = value.replace(/^whatsapp:/i, "").trim();
598
- const jidMatch = trimmed.match(KNOWN_USER_JID_RE);
599
- if (jidMatch) {
600
- const domain = jidMatch[2].toLowerCase() === "c.us" ? "s.whatsapp.net" : jidMatch[2].toLowerCase();
601
- return `${jidMatch[1]}@${domain}`;
602
- }
603
- const digits = trimmed.startsWith("+") ? trimmed.replace(/\D/g, "") : /^\d+$/.test(trimmed) ? trimmed : "";
604
- return digits ? `${digits}@s.whatsapp.net` : null;
605
- }
606
- function extractKnownJidParts(value) {
607
- const normalized = normalizeKnownUserJid(value);
608
- if (!normalized) return null;
609
- const match = normalized.match(/^(\d+)@(.+)$/);
610
- return match ? {
611
- user: match[1],
612
- domain: match[2]
613
- } : null;
614
- }
615
- function extractPhoneDigits(value) {
616
- if (!value) return null;
617
- const trimmed = value.replace(/^whatsapp:/i, "").trim();
618
- if (trimmed.startsWith("+") || /^\d+$/.test(trimmed)) return trimmed.replace(/\D/g, "") || null;
619
- const parts = extractKnownJidParts(trimmed);
620
- return parts && PHONE_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
621
- }
622
- function extractLidDigits(value) {
623
- if (!value) return null;
624
- const parts = extractKnownJidParts(value);
625
- return parts && LID_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
626
- }
627
- function isLidJid(jid) {
628
- const parts = extractKnownJidParts(jid);
629
- return Boolean(parts && LID_JID_DOMAIN_RE.test(parts.domain));
630
- }
631
- function lidReplacementText(jid) {
632
- const parts = extractKnownJidParts(jid);
633
- if (!parts || !LID_JID_DOMAIN_RE.test(parts.domain)) return;
634
- return `@${parts.user}`;
635
- }
636
- function participantValues(participant) {
637
- return typeof participant === "string" ? { id: participant } : participant;
638
- }
639
- function chooseMentionJid(participant) {
640
- const values = participantValues(participant);
641
- const idJid = normalizeKnownUserJid(values.id ?? "");
642
- const lidJid = normalizeKnownUserJid(values.lid ?? "");
643
- return (idJid && isLidJid(idJid) ? idJid : null) ?? (lidJid && isLidJid(lidJid) ? lidJid : null) ?? idJid ?? lidJid ?? normalizeKnownUserJid(values.phoneNumber ?? "") ?? normalizeKnownUserJid(values.e164 ?? "");
644
- }
645
- function buildMentionTargetMaps(participants) {
646
- const byPhone = /* @__PURE__ */ new Map();
647
- const byLid = /* @__PURE__ */ new Map();
648
- for (const participant of participants) {
649
- const mentionJid = chooseMentionJid(participant);
650
- if (!mentionJid) continue;
651
- const target = {
652
- mentionJid,
653
- ...isLidJid(mentionJid) ? { replacementText: lidReplacementText(mentionJid) } : {}
654
- };
655
- const values = participantValues(participant);
656
- for (const value of [
657
- values.id,
658
- values.phoneNumber,
659
- values.e164
660
- ]) {
661
- const digits = extractPhoneDigits(value);
662
- if (digits && !byPhone.has(digits)) byPhone.set(digits, target);
663
- }
664
- for (const value of [values.id, values.lid]) {
665
- const digits = extractLidDigits(value);
666
- if (digits && !byLid.has(digits)) byLid.set(digits, target);
667
- }
668
- }
669
- return {
670
- byPhone,
671
- byLid
672
- };
673
- }
674
- function shouldSkipMentionAt(text, index, end, codeRanges) {
675
- if (isInRange(index, codeRanges)) return true;
676
- const previous = index > 0 ? text[index - 1] : "";
677
- const next = text[end] ?? "";
678
- return Boolean(previous && /[\w@]/.test(previous) || next && /[\w@]/.test(next));
679
- }
680
- function resolveWhatsAppOutboundMentions(params) {
681
- if (!isWhatsAppGroupJid(params.chatJid) || !mayContainWhatsAppOutboundMention(params.text) || !params.participants?.length) return {
682
- text: params.text,
683
- mentionedJids: []
684
- };
685
- const { byPhone, byLid } = buildMentionTargetMaps(params.participants);
686
- if (byPhone.size === 0 && byLid.size === 0) return {
687
- text: params.text,
688
- mentionedJids: []
689
- };
690
- const codeRanges = collectCodeRanges(params.text);
691
- const replacements = [];
692
- const mentionedJids = [];
693
- const seenMentionJids = /* @__PURE__ */ new Set();
694
- for (const match of params.text.matchAll(OUTBOUND_MENTION_RE)) {
695
- const start = match.index;
696
- const token = match[0];
697
- if (shouldSkipMentionAt(params.text, start, start + token.length, codeRanges)) continue;
698
- const digits = match[1].replace(/\D/g, "");
699
- const target = token.startsWith("@+") ? byPhone.get(digits) ?? byLid.get(digits) : byLid.get(digits) ?? byPhone.get(digits);
700
- if (!target) continue;
701
- if (!seenMentionJids.has(target.mentionJid)) {
702
- seenMentionJids.add(target.mentionJid);
703
- mentionedJids.push(target.mentionJid);
704
- }
705
- if (target.replacementText && target.replacementText !== token) replacements.push({
706
- start,
707
- end: start + token.length,
708
- text: target.replacementText
709
- });
710
- }
711
- if (replacements.length === 0) return {
712
- text: params.text,
713
- mentionedJids
714
- };
715
- let text = "";
716
- let cursor = 0;
717
- for (const replacement of replacements) {
718
- text += params.text.slice(cursor, replacement.start);
719
- text += replacement.text;
720
- cursor = replacement.end;
721
- }
722
- text += params.text.slice(cursor);
723
- return {
724
- text,
725
- mentionedJids
726
- };
727
- }
728
- function addWhatsAppOutboundMentionsToContent(content, mentionedJids) {
729
- return mentionedJids.length > 0 ? {
730
- ...content,
731
- mentions: [...mentionedJids]
732
- } : content;
733
- }
734
- //#endregion
735
- //#region extensions/whatsapp/src/inbound/send-result.ts
736
- function normalizeKey(key) {
737
- const id = typeof key?.id === "string" ? key.id.trim() : "";
738
- if (!id) return;
739
- return {
740
- id,
741
- remoteJid: key?.remoteJid,
742
- fromMe: key?.fromMe,
743
- participant: key?.participant
744
- };
745
- }
746
- function normalizeWhatsAppSendResult(result, kind) {
747
- const key = normalizeKey(result?.key);
748
- return {
749
- kind,
750
- messageId: key?.id ?? "unknown",
751
- messageIds: key ? [key.id] : [],
752
- keys: key ? [key] : [],
753
- providerAccepted: Boolean(key)
754
- };
755
- }
756
- function combineWhatsAppSendResults(kind, results) {
757
- const messageIds = [...new Set(results.flatMap((result) => result.messageIds))];
758
- const keys = results.flatMap((result) => result.keys);
759
- return {
760
- kind,
761
- messageId: messageIds[0] ?? "unknown",
762
- messageIds,
763
- keys,
764
- providerAccepted: results.some((result) => result.providerAccepted)
765
- };
766
- }
767
- //#endregion
768
- //#region extensions/whatsapp/src/inbound/send-api.ts
769
- function recordWhatsAppOutbound(accountId) {
770
- recordChannelActivity({
771
- channel: "whatsapp",
772
- accountId,
773
- direction: "outbound"
774
- });
775
- }
776
- function createWebSendApi(params) {
777
- const resolveOutboundJid = (recipient) => params.authDir ? toWhatsappJidWithLid(recipient, { authDir: params.authDir }) : toWhatsappJid(recipient);
778
- const resolveMentions = async (jid, text) => params.resolveOutboundMentions ? await params.resolveOutboundMentions({
779
- jid,
780
- text
781
- }) : {
782
- text,
783
- mentionedJids: []
784
- };
785
- return {
786
- sendMessage: async (to, text, mediaBuffer, mediaType, sendOptions) => {
787
- const jid = resolveOutboundJid(to);
788
- let payload;
789
- if (mediaBuffer) mediaType ??= "application/octet-stream";
790
- const shouldSendAudioText = Boolean(mediaBuffer && mediaType?.startsWith("audio/") && text.trim());
791
- const resolvedPayloadText = shouldSendAudioText ? {
792
- text,
793
- mentionedJids: []
794
- } : await resolveMentions(jid, text);
795
- if (mediaBuffer && mediaType) if (mediaType.startsWith("image/")) payload = {
796
- image: mediaBuffer,
797
- caption: resolvedPayloadText.text || void 0,
798
- mimetype: mediaType
799
- };
800
- else if (mediaType.startsWith("audio/")) payload = {
801
- audio: mediaBuffer,
802
- ptt: true,
803
- mimetype: mediaType
804
- };
805
- else if (mediaType.startsWith("video/")) {
806
- const gifPlayback = sendOptions?.gifPlayback;
807
- payload = {
808
- video: mediaBuffer,
809
- caption: resolvedPayloadText.text || void 0,
810
- mimetype: mediaType,
811
- ...gifPlayback ? { gifPlayback: true } : {}
812
- };
813
- } else payload = {
814
- document: mediaBuffer,
815
- fileName: sendOptions?.fileName?.trim() || "file",
816
- caption: resolvedPayloadText.text || void 0,
817
- mimetype: mediaType
818
- };
819
- else payload = { text: resolvedPayloadText.text };
820
- payload = addWhatsAppOutboundMentionsToContent(payload, resolvedPayloadText.mentionedJids);
821
- const quotedOpts = buildQuotedMessageOptions({
822
- messageId: sendOptions?.quotedMessageKey?.id,
823
- remoteJid: sendOptions?.quotedMessageKey?.remoteJid,
824
- fromMe: sendOptions?.quotedMessageKey?.fromMe,
825
- participant: sendOptions?.quotedMessageKey?.participant,
826
- messageText: sendOptions?.quotedMessageKey?.messageText
827
- });
828
- const results = [normalizeWhatsAppSendResult(quotedOpts ? await params.sock.sendMessage(jid, payload, quotedOpts) : await params.sock.sendMessage(jid, payload), mediaBuffer ? "media" : "text")];
829
- if (shouldSendAudioText) {
830
- const resolvedAudioText = await resolveMentions(jid, text);
831
- const textPayload = addWhatsAppOutboundMentionsToContent({ text: resolvedAudioText.text }, resolvedAudioText.mentionedJids);
832
- const textResult = quotedOpts ? await params.sock.sendMessage(jid, textPayload, quotedOpts) : await params.sock.sendMessage(jid, textPayload);
833
- results.push(normalizeWhatsAppSendResult(textResult, "text"));
834
- }
835
- recordWhatsAppOutbound(sendOptions?.accountId ?? params.defaultAccountId);
836
- return combineWhatsAppSendResults(mediaBuffer ? "media" : "text", results);
837
- },
838
- sendPoll: async (to, poll) => {
839
- const jid = resolveOutboundJid(to);
840
- const result = await params.sock.sendMessage(jid, { poll: {
841
- name: poll.question,
842
- values: poll.options,
843
- selectableCount: poll.maxSelections ?? 1
844
- } });
845
- recordWhatsAppOutbound(params.defaultAccountId);
846
- return normalizeWhatsAppSendResult(result, "poll");
847
- },
848
- sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
849
- const jid = toWhatsappJid(chatJid);
850
- return normalizeWhatsAppSendResult(await params.sock.sendMessage(jid, { react: {
851
- text: emoji,
852
- key: {
853
- remoteJid: jid,
854
- id: messageId,
855
- fromMe,
856
- participant: participant ? toWhatsappJid(participant) : void 0
857
- }
858
- } }), "reaction");
859
- },
860
- sendComposingTo: async (to) => {
861
- const jid = resolveOutboundJid(to);
862
- if (isWhatsAppNewsletterJid(jid)) return;
863
- await params.sock.sendPresenceUpdate("composing", jid);
864
- }
865
- };
866
- }
867
- //#endregion
868
212
  //#region extensions/whatsapp/src/inbound/monitor.ts
869
213
  const LOGGED_OUT_STATUS = DisconnectReason$1?.loggedOut ?? 401;
870
214
  const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
@@ -896,6 +240,13 @@ function logWhatsAppVerbose(enabled, message) {
896
240
  function isGroupJid(jid) {
897
241
  return (typeof isJidGroup === "function" ? isJidGroup(jid) : jid.endsWith("@g.us")) === true;
898
242
  }
243
+ function recordAcceptedInboundActivity(accountId) {
244
+ recordChannelActivity({
245
+ channel: "whatsapp",
246
+ accountId,
247
+ direction: "inbound"
248
+ });
249
+ }
899
250
  function isRetryableSendDisconnectError(err) {
900
251
  return /closed|reset|timed\s*out|disconnect|no active socket/i.test(formatError(err));
901
252
  }
@@ -1310,11 +661,6 @@ async function attachWebInboxToSocket(options) {
1310
661
  const handleMessagesUpsert = async (upsert) => {
1311
662
  if (upsert.type !== "notify" && upsert.type !== "append") return;
1312
663
  for (const msg of upsert.messages ?? []) {
1313
- recordChannelActivity({
1314
- channel: "whatsapp",
1315
- accountId: options.accountId,
1316
- direction: "inbound"
1317
- });
1318
664
  const inbound = await normalizeInboundMessage(msg);
1319
665
  if (!inbound) continue;
1320
666
  await maybeMarkInboundAsRead(inbound);
@@ -1328,6 +674,7 @@ async function attachWebInboxToSocket(options) {
1328
674
  if (!enriched) continue;
1329
675
  const dedupeKey = inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : "";
1330
676
  if (dedupeKey && !await claimRecentInboundMessage(dedupeKey)) continue;
677
+ recordAcceptedInboundActivity(options.accountId);
1331
678
  await enqueueInboundMessage(msg, inbound, enriched);
1332
679
  }
1333
680
  };
@@ -1432,7 +779,7 @@ function resolveMentionTargets(msg, authDir) {
1432
779
  function isBotMentionedFromTargets(msg, mentionCfg, targets) {
1433
780
  const clean = (text) => normalizeMentionText(text);
1434
781
  const explicitSelfChatOverride = typeof mentionCfg.isSelfChat === "boolean";
1435
- const isGroupConversation = isWhatsAppGroupJid$1(msg.from);
782
+ const isGroupConversation = isWhatsAppGroupJid(msg.from);
1436
783
  const isSelfChat = explicitSelfChatOverride ? Boolean(mentionCfg.isSelfChat) : isSelfChatMode(targets.self.e164, mentionCfg.allowFrom) && !isGroupConversation;
1437
784
  const hasMentions = targets.normalizedMentions.length > 0;
1438
785
  if (hasMentions && !isSelfChat) {
@@ -2018,6 +1365,42 @@ function isLikelyWhatsAppCryptoError(reason) {
2018
1365
  }
2019
1366
  //#endregion
2020
1367
  //#region extensions/whatsapp/src/auto-reply/deliver-reply.ts
1368
+ function resolveWhatsAppReceiptKind(results) {
1369
+ if (results.length > 0 && results.every((result) => result.kind === "text")) return "text";
1370
+ if (results.length > 0 && results.every((result) => result.kind === "media")) return "media";
1371
+ return "unknown";
1372
+ }
1373
+ function createWhatsAppReplyDeliveryReceipt(results) {
1374
+ const receiptResultsById = /* @__PURE__ */ new Map();
1375
+ for (const result of results) {
1376
+ if (result.receipt?.parts.length) {
1377
+ for (const part of result.receipt.parts) receiptResultsById.set(part.platformMessageId, {
1378
+ ...part.raw ?? {
1379
+ channel: "whatsapp",
1380
+ messageId: part.platformMessageId
1381
+ },
1382
+ meta: {
1383
+ ...part.raw?.meta,
1384
+ kind: result.kind,
1385
+ providerAccepted: result.providerAccepted
1386
+ }
1387
+ });
1388
+ continue;
1389
+ }
1390
+ for (const messageId of listWhatsAppSendResultMessageIds(result)) receiptResultsById.set(messageId, {
1391
+ channel: "whatsapp",
1392
+ messageId,
1393
+ meta: {
1394
+ kind: result.kind,
1395
+ providerAccepted: result.providerAccepted
1396
+ }
1397
+ });
1398
+ }
1399
+ return createMessageReceiptFromOutboundResults({
1400
+ results: [...receiptResultsById.values()],
1401
+ kind: resolveWhatsAppReceiptKind(results)
1402
+ });
1403
+ }
2021
1404
  async function deliverWebReply(params) {
2022
1405
  const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
2023
1406
  const replyStarted = Date.now();
@@ -2028,7 +1411,7 @@ async function deliverWebReply(params) {
2028
1411
  const finishDelivery = () => {
2029
1412
  return {
2030
1413
  results: sendResults,
2031
- messageIds: [...new Set(sendResults.flatMap((result) => result.messageIds))],
1414
+ receipt: createWhatsAppReplyDeliveryReceipt(sendResults),
2032
1415
  providerAccepted: sendResults.some((result) => result.providerAccepted)
2033
1416
  };
2034
1417
  };
@@ -2361,7 +1744,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2361
1744
  });
2362
1745
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
2363
1746
  const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : params.msg.chatType;
2364
- const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ? resolveChannelSourceReplyDeliveryMode({
1747
+ const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
2365
1748
  cfg: params.cfg,
2366
1749
  ctx: {
2367
1750
  ChatType: sourceReplyChatType,
@@ -2435,6 +1818,33 @@ async function dispatchWhatsAppBufferedReply(params) {
2435
1818
  if (!reply.hasMedia && !reply.text.trim()) return;
2436
1819
  if (!reply.hasMedia) {
2437
1820
  logWhatsAppMediaOnlyFlushResult(await mediaOnlyCoalescer.flushAll());
1821
+ const durable = await deliverInboundReplyWithMessageSendContext({
1822
+ cfg: params.cfg,
1823
+ channel: "whatsapp",
1824
+ accountId: params.route.accountId,
1825
+ agentId: params.route.agentId,
1826
+ ctxPayload: params.context,
1827
+ payload: normalizedDeliveryPayload,
1828
+ info,
1829
+ to: params.msg.from,
1830
+ formatting: {
1831
+ textLimit,
1832
+ tableMode,
1833
+ chunkMode
1834
+ }
1835
+ });
1836
+ if (durable.status === "failed") throw durable.error;
1837
+ if (durable.status === "handled_visible") {
1838
+ didSendReply = true;
1839
+ const shouldLog = normalizedDeliveryPayload.text ? true : void 0;
1840
+ params.rememberSentText(normalizedDeliveryPayload.text, {
1841
+ combinedBody: params.context.Body,
1842
+ combinedBodySessionKey: params.route.sessionKey,
1843
+ logVerboseMessage: shouldLog
1844
+ });
1845
+ return;
1846
+ }
1847
+ if (durable.status === "handled_no_send") return;
2438
1848
  await deliverNormalizedPayload(normalizedDeliveryPayload, info);
2439
1849
  return;
2440
1850
  }
@@ -2593,11 +2003,18 @@ async function processMessage(params) {
2593
2003
  let audioTranscript = params.preflightAudioTranscript ?? void 0;
2594
2004
  const hasAudioBody = params.msg.mediaType?.startsWith("audio/") === true && params.msg.body === "<media:audio>";
2595
2005
  if (params.preflightAudioTranscript === void 0 && hasAudioBody && params.msg.mediaPath) try {
2596
- const { transcribeFirstAudio } = await import("./audio-preflight.runtime-Bcdsl_ri.js");
2006
+ const { transcribeFirstAudio } = await import("./audio-preflight.runtime-CIFjeaEN.js");
2597
2007
  audioTranscript = await transcribeFirstAudio({
2598
2008
  ctx: {
2599
2009
  MediaPaths: [params.msg.mediaPath],
2600
- MediaTypes: params.msg.mediaType ? [params.msg.mediaType] : void 0
2010
+ MediaTypes: params.msg.mediaType ? [params.msg.mediaType] : void 0,
2011
+ From: params.msg.from,
2012
+ To: params.msg.to,
2013
+ Provider: "whatsapp",
2014
+ Surface: "whatsapp",
2015
+ OriginatingChannel: "whatsapp",
2016
+ OriginatingTo: conversationId,
2017
+ AccountId: params.route.accountId
2601
2018
  },
2602
2019
  cfg: params.cfg
2603
2020
  });
@@ -2699,7 +2116,7 @@ async function processMessage(params) {
2699
2116
  msg: params.msg,
2700
2117
  policy: inboundPolicy
2701
2118
  }) : void 0;
2702
- const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
2119
+ const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
2703
2120
  cfg: params.cfg,
2704
2121
  agentId: params.route.agentId,
2705
2122
  channel: "whatsapp",
@@ -2907,11 +2324,18 @@ function createWebOnMessageHandler(params) {
2907
2324
  });
2908
2325
  ackAlreadySent = ackReaction !== null;
2909
2326
  try {
2910
- const { transcribeFirstAudio } = await import("./audio-preflight.runtime-Bcdsl_ri.js");
2327
+ const { transcribeFirstAudio } = await import("./audio-preflight.runtime-CIFjeaEN.js");
2911
2328
  preflightAudioTranscript = await transcribeFirstAudio({
2912
2329
  ctx: {
2913
2330
  MediaPaths: [msg.mediaPath],
2914
- MediaTypes: msg.mediaType ? [msg.mediaType] : void 0
2331
+ MediaTypes: msg.mediaType ? [msg.mediaType] : void 0,
2332
+ From: msg.from,
2333
+ To: msg.to,
2334
+ Provider: "whatsapp",
2335
+ Surface: "whatsapp",
2336
+ OriginatingChannel: "whatsapp",
2337
+ OriginatingTo: conversationId,
2338
+ AccountId: route.accountId
2915
2339
  },
2916
2340
  cfg: params.cfg
2917
2341
  }) ?? null;
@@ -3023,7 +2447,7 @@ function isNonRetryableWebCloseStatus(statusCode) {
3023
2447
  }
3024
2448
  let replyResolverRuntimePromise = null;
3025
2449
  function loadReplyResolverRuntime() {
3026
- replyResolverRuntimePromise ??= import("./reply-resolver.runtime-BRCgoO4C.js");
2450
+ replyResolverRuntimePromise ??= import("./reply-resolver.runtime-CROyXXQh.js");
3027
2451
  return replyResolverRuntimePromise;
3028
2452
  }
3029
2453
  function normalizeReconnectAccountId(accountId) {
@@ -3426,4 +2850,4 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3426
2850
  }
3427
2851
  }
3428
2852
  //#endregion
3429
- export { loadWebMediaRaw as a, monitorWebInbox as c, extractMediaPlaceholder as d, extractText as f, loadWebMedia$1 as i, extractContactContext as l, LocalMediaAccessError as n, optimizeImageToJpeg as o, resetWebInboundDedupe as p, getDefaultLocalRoots as r, optimizeImageToPng as s, monitorWebChannel as t, extractLocationData as u };
2853
+ export { loadWebMediaRaw as a, monitorWebInbox as c, loadWebMedia$1 as i, resetWebInboundDedupe as l, LocalMediaAccessError as n, optimizeImageToJpeg as o, getDefaultLocalRoots as r, optimizeImageToPng as s, monitorWebChannel as t };