@openclaw/whatsapp 2026.6.6 → 2026.6.8-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/{access-control-C53zKYSN.js → access-control-BKaMOvIk.js} +2 -2
  2. package/dist/api.js +6 -6
  3. package/dist/{auth-store-B2ZibndQ.js → auth-store-e9VzLZ-S.js} +1 -1
  4. package/dist/{channel-DhMmokKH.js → channel-DyUm8hqs.js} +15 -2
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/{channel.runtime-v3vXe7C0.js → channel.runtime-BXkzo0j1.js} +4 -4
  7. package/dist/{channel.setup-Cq3cdnzK.js → channel.setup-CpyAie9O.js} +2 -2
  8. package/dist/{connection-controller-TvY2F126.js → connection-controller-BWBoVQr4.js} +16 -3
  9. package/dist/contract-api.js +1 -1
  10. package/dist/{identity-xoLLdqEv.js → identity-Bupuq9vF.js} +18 -18
  11. package/dist/light-runtime-api.js +1 -1
  12. package/dist/{login-BExWg_jW.js → login-Xv3ujrl0.js} +3 -3
  13. package/dist/{login-qr-DiZ0U-P8.js → login-qr-CaJziL93.js} +3 -3
  14. package/dist/login-qr-runtime.js +1 -1
  15. package/dist/{monitor-CXuZBVgk.js → monitor-Ggwn3FDi.js} +587 -237
  16. package/dist/runtime-api.js +5 -5
  17. package/dist/{send-api-CFbAgIbn.js → send-api-Cp9CUH5P.js} +1 -1
  18. package/dist/{session-CoxlXm2K.js → session-BCKUPRkN.js} +1 -1
  19. package/dist/{setup-core-D0Zm67Lv.js → setup-core-Cp0NMO89.js} +2 -2
  20. package/dist/{setup-finalize-nTMa6s13.js → setup-finalize-DnV-KlkQ.js} +2 -2
  21. package/dist/setup-plugin-api.js +1 -1
  22. package/dist/{setup-surface-vIg4EcCu.js → setup-surface-BmPcAhIF.js} +2 -2
  23. package/node_modules/protobufjs/README.md +45 -31
  24. package/node_modules/protobufjs/dist/light/protobuf.js +98 -66
  25. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  26. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  27. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  28. package/node_modules/protobufjs/dist/minimal/protobuf.js +30 -10
  29. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  30. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  31. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  32. package/node_modules/protobufjs/dist/protobuf.js +99 -67
  33. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  34. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  35. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  36. package/node_modules/protobufjs/index.d.ts +20 -8
  37. package/node_modules/protobufjs/package.json +1 -1
  38. package/node_modules/protobufjs/src/converter.js +9 -6
  39. package/node_modules/protobufjs/src/decoder.js +3 -1
  40. package/node_modules/protobufjs/src/encoder.js +8 -5
  41. package/node_modules/protobufjs/src/enum.js +2 -2
  42. package/node_modules/protobufjs/src/field.js +2 -5
  43. package/node_modules/protobufjs/src/index-light.js +1 -1
  44. package/node_modules/protobufjs/src/message.js +1 -1
  45. package/node_modules/protobufjs/src/object.js +6 -6
  46. package/node_modules/protobufjs/src/parse.js +1 -1
  47. package/node_modules/protobufjs/src/root.js +14 -8
  48. package/node_modules/protobufjs/src/type.js +8 -8
  49. package/node_modules/protobufjs/src/typescript.js +6 -0
  50. package/node_modules/protobufjs/src/util/eventemitter.js +4 -2
  51. package/node_modules/protobufjs/src/util/minimal.js +24 -6
  52. package/node_modules/protobufjs/src/util/patterns.js +0 -1
  53. package/node_modules/protobufjs/src/util.js +2 -3
  54. package/node_modules/protobufjs/src/wrappers.js +11 -8
  55. package/npm-shrinkwrap.json +6 -6
  56. package/package.json +5 -5
@@ -5,14 +5,14 @@ import { n as getWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
5
5
  import { n as getStatusCode, r as getRegisteredWhatsAppConnectionController, t as formatError } from "./session-errors-CbsoQqoy.js";
6
6
  import { d as markdownToWhatsApp, f as resolveJidToE164, l as isSelfChatMode, n as normalizeE164, t as convertMarkdownTables$1, u as jidToE164 } from "./text-runtime-Dk37KYHj.js";
7
7
  import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-CveINB35.js";
8
- import { c as logoutWeb, g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-B2ZibndQ.js";
9
- import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-xoLLdqEv.js";
8
+ import { c as logoutWeb, g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-e9VzLZ-S.js";
9
+ import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-Bupuq9vF.js";
10
10
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
11
- import { i as resolveWhatsAppInboundPolicy, r as resolveWhatsAppCommandAuthorized, t as checkInboundAccessControl } from "./access-control-C53zKYSN.js";
12
- import { _ as extractMediaPlaceholder, a as mayContainWhatsAppOutboundMention, b as hasInboundUserContent, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractLocationData, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMentionedJids, y as extractText } from "./send-api-CFbAgIbn.js";
13
- import { a as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-CoxlXm2K.js";
11
+ import { i as resolveWhatsAppInboundPolicy, r as resolveWhatsAppCommandAuthorized, t as checkInboundAccessControl } from "./access-control-BKaMOvIk.js";
12
+ import { _ as extractMediaPlaceholder, a as mayContainWhatsAppOutboundMention, b as hasInboundUserContent, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractLocationData, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMentionedJids, y as extractText } from "./send-api-Cp9CUH5P.js";
13
+ import { a as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-BCKUPRkN.js";
14
14
  import { t as BufferJSON } from "./session.runtime-CyooSQvj.js";
15
- import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-TvY2F126.js";
15
+ import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-BWBoVQr4.js";
16
16
  import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
17
17
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
18
18
  import { createChannelMessageReplyPipeline, createDurableInboundReceiveJournalFromQueue, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-outbound";
@@ -47,11 +47,281 @@ import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot
47
47
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
48
48
  import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
49
49
  import { createConnectedChannelStatusPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
50
+ import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute } from "openclaw/plugin-sdk/conversation-binding-runtime";
50
51
  import { resolveAgentIdentity, resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
51
52
  import { normalizeGroupActivation, parseActivationCommand } from "openclaw/plugin-sdk/group-activation";
52
53
  import { createInternalHookEvent, deriveInboundMessageHookContext, fireAndForgetBoundedHook, toInternalMessageReceivedContext, toPluginMessageContext, toPluginMessageReceivedEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
53
54
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
54
55
  import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
56
+ //#region extensions/whatsapp/src/inbound/message-aliases.ts
57
+ function normalizeQuoteSender(sender) {
58
+ if (!sender?.displayName && !sender?.jid && !sender?.e164) return;
59
+ return sender;
60
+ }
61
+ function buildQuoteFromFlatAliases(msg) {
62
+ if (msg.replyTo) return {
63
+ context: msg.replyTo,
64
+ id: msg.replyTo.id,
65
+ body: msg.replyTo.body,
66
+ sender: normalizeQuoteSender({
67
+ displayName: msg.replyTo.sender?.label ?? msg.replyToSender,
68
+ jid: msg.replyTo.sender?.jid ?? msg.replyToSenderJid,
69
+ e164: msg.replyTo.sender?.e164 ?? msg.replyToSenderE164
70
+ })
71
+ };
72
+ if (!msg.replyToId && !msg.replyToBody && !msg.replyToSender && !msg.replyToSenderJid && !msg.replyToSenderE164) return;
73
+ return {
74
+ id: msg.replyToId,
75
+ body: msg.replyToBody,
76
+ sender: normalizeQuoteSender({
77
+ displayName: msg.replyToSender,
78
+ jid: msg.replyToSenderJid,
79
+ e164: msg.replyToSenderE164
80
+ })
81
+ };
82
+ }
83
+ function buildGroupFromFlatAliases(msg) {
84
+ const mentionJids = msg.mentions ?? msg.mentionedJids;
85
+ if (!msg.groupSubject && !msg.groupParticipants?.length && !mentionJids?.length) return;
86
+ return {
87
+ subject: msg.groupSubject,
88
+ participants: msg.groupParticipants,
89
+ mentions: mentionJids?.length ? { jids: mentionJids } : void 0
90
+ };
91
+ }
92
+ function ensureQuote(msg) {
93
+ return msg.quote ??= {};
94
+ }
95
+ function ensureQuoteSender(msg) {
96
+ const quote = ensureQuote(msg);
97
+ return quote.sender ??= {};
98
+ }
99
+ function ensureGroup(msg) {
100
+ return msg.group ??= {};
101
+ }
102
+ function ensureGroupMentions(msg) {
103
+ const group = ensureGroup(msg);
104
+ return group.mentions ??= {};
105
+ }
106
+ function ensureMedia(msg) {
107
+ return msg.payload.media ??= {};
108
+ }
109
+ function setMediaField(msg, key, value) {
110
+ if (value === void 0 && !msg.payload.media) return;
111
+ ensureMedia(msg)[key] = value;
112
+ }
113
+ function defineDeprecatedAliasAccessors(msg, descriptors) {
114
+ Object.defineProperties(msg, Object.fromEntries(Object.entries(descriptors).map(([key, descriptor]) => [key, {
115
+ configurable: true,
116
+ enumerable: true,
117
+ get: descriptor.get,
118
+ set: descriptor.set
119
+ }])));
120
+ return msg;
121
+ }
122
+ function withDeprecatedWebInboundMessageFlatAliases(msg) {
123
+ return defineDeprecatedAliasAccessors(msg, {
124
+ id: {
125
+ get: () => msg.event.id,
126
+ set: (value) => msg.event.id = value
127
+ },
128
+ to: {
129
+ get: () => msg.platform.recipientJid,
130
+ set: (value) => msg.platform.recipientJid = value
131
+ },
132
+ body: {
133
+ get: () => msg.payload.body,
134
+ set: (value) => msg.payload.body = value
135
+ },
136
+ pushName: {
137
+ get: () => msg.platform.pushName,
138
+ set: (value) => msg.platform.pushName = value
139
+ },
140
+ timestamp: {
141
+ get: () => msg.event.timestamp,
142
+ set: (value) => msg.event.timestamp = value
143
+ },
144
+ chatId: {
145
+ get: () => msg.platform.chatJid,
146
+ set: (value) => msg.platform.chatJid = value
147
+ },
148
+ sender: {
149
+ get: () => msg.platform.sender,
150
+ set: (value) => msg.platform.sender = value
151
+ },
152
+ senderJid: {
153
+ get: () => msg.platform.senderJid,
154
+ set: (value) => msg.platform.senderJid = value
155
+ },
156
+ senderE164: {
157
+ get: () => msg.platform.senderE164,
158
+ set: (value) => msg.platform.senderE164 = value
159
+ },
160
+ senderName: {
161
+ get: () => msg.platform.senderName,
162
+ set: (value) => msg.platform.senderName = value
163
+ },
164
+ replyTo: {
165
+ get: () => msg.quote?.context,
166
+ set: (value) => ensureQuote(msg).context = value
167
+ },
168
+ replyToId: {
169
+ get: () => msg.quote?.id ?? msg.quote?.context?.id,
170
+ set: (value) => ensureQuote(msg).id = value
171
+ },
172
+ replyToBody: {
173
+ get: () => msg.quote?.body ?? msg.quote?.context?.body,
174
+ set: (value) => ensureQuote(msg).body = value
175
+ },
176
+ replyToSender: {
177
+ get: () => msg.quote?.context?.sender?.label ?? msg.quote?.sender?.displayName,
178
+ set: (value) => {
179
+ const sender = ensureQuoteSender(msg);
180
+ sender.displayName = value;
181
+ if (msg.quote?.context?.sender) msg.quote.context.sender.label = value;
182
+ }
183
+ },
184
+ replyToSenderJid: {
185
+ get: () => msg.quote?.context?.sender?.jid ?? msg.quote?.sender?.jid,
186
+ set: (value) => {
187
+ const jid = value;
188
+ ensureQuoteSender(msg).jid = jid;
189
+ if (msg.quote?.context?.sender) msg.quote.context.sender.jid = jid;
190
+ }
191
+ },
192
+ replyToSenderE164: {
193
+ get: () => msg.quote?.context?.sender?.e164 ?? msg.quote?.sender?.e164,
194
+ set: (value) => {
195
+ const e164 = value;
196
+ ensureQuoteSender(msg).e164 = e164;
197
+ if (msg.quote?.context?.sender) msg.quote.context.sender.e164 = e164;
198
+ }
199
+ },
200
+ groupSubject: {
201
+ get: () => msg.group?.subject,
202
+ set: (value) => ensureGroup(msg).subject = value
203
+ },
204
+ groupParticipants: {
205
+ get: () => msg.group?.participants,
206
+ set: (value) => ensureGroup(msg).participants = value
207
+ },
208
+ mentions: {
209
+ get: () => msg.group?.mentions?.jids,
210
+ set: (value) => ensureGroupMentions(msg).jids = value
211
+ },
212
+ mentionedJids: {
213
+ get: () => msg.group?.mentions?.jids,
214
+ set: (value) => ensureGroupMentions(msg).jids = value
215
+ },
216
+ self: {
217
+ get: () => msg.platform.self,
218
+ set: (value) => msg.platform.self = value
219
+ },
220
+ selfJid: {
221
+ get: () => msg.platform.selfJid,
222
+ set: (value) => msg.platform.selfJid = value
223
+ },
224
+ selfLid: {
225
+ get: () => msg.platform.selfLid,
226
+ set: (value) => msg.platform.selfLid = value
227
+ },
228
+ selfE164: {
229
+ get: () => msg.platform.selfE164,
230
+ set: (value) => msg.platform.selfE164 = value
231
+ },
232
+ fromMe: {
233
+ get: () => msg.platform.fromMe,
234
+ set: (value) => msg.platform.fromMe = value
235
+ },
236
+ location: {
237
+ get: () => msg.payload.location,
238
+ set: (value) => msg.payload.location = value
239
+ },
240
+ sendComposing: {
241
+ get: () => msg.platform.sendComposing,
242
+ set: (value) => msg.platform.sendComposing = value
243
+ },
244
+ reply: {
245
+ get: () => msg.platform.reply,
246
+ set: (value) => msg.platform.reply = value
247
+ },
248
+ sendMedia: {
249
+ get: () => msg.platform.sendMedia,
250
+ set: (value) => msg.platform.sendMedia = value
251
+ },
252
+ mediaPath: {
253
+ get: () => msg.payload.media?.path,
254
+ set: (value) => setMediaField(msg, "path", value)
255
+ },
256
+ mediaType: {
257
+ get: () => msg.payload.media?.type,
258
+ set: (value) => setMediaField(msg, "type", value)
259
+ },
260
+ mediaFileName: {
261
+ get: () => msg.payload.media?.fileName,
262
+ set: (value) => setMediaField(msg, "fileName", value)
263
+ },
264
+ mediaUrl: {
265
+ get: () => msg.payload.media?.url,
266
+ set: (value) => setMediaField(msg, "url", value)
267
+ },
268
+ untrustedStructuredContext: {
269
+ get: () => msg.payload.untrustedStructuredContext,
270
+ set: (value) => msg.payload.untrustedStructuredContext = value
271
+ },
272
+ isBatched: {
273
+ get: () => msg.event.isBatched,
274
+ set: (value) => msg.event.isBatched = value
275
+ }
276
+ });
277
+ }
278
+ function normalizeLegacyFlatWebInboundMessage(msg) {
279
+ const media = msg.mediaPath || msg.mediaType || msg.mediaFileName || msg.mediaUrl ? {
280
+ path: msg.mediaPath,
281
+ type: msg.mediaType,
282
+ fileName: msg.mediaFileName,
283
+ url: msg.mediaUrl
284
+ } : void 0;
285
+ return withDeprecatedWebInboundMessageFlatAliases({
286
+ ...msg,
287
+ event: {
288
+ id: msg.id,
289
+ timestamp: msg.timestamp,
290
+ isBatched: msg.isBatched
291
+ },
292
+ payload: {
293
+ body: msg.body,
294
+ media,
295
+ location: msg.location,
296
+ untrustedStructuredContext: msg.untrustedStructuredContext
297
+ },
298
+ platform: {
299
+ chatJid: msg.chatId,
300
+ recipientJid: msg.to,
301
+ sender: msg.sender,
302
+ senderJid: msg.senderJid,
303
+ senderE164: msg.senderE164,
304
+ senderName: msg.senderName,
305
+ pushName: msg.pushName,
306
+ self: msg.self,
307
+ selfJid: msg.selfJid,
308
+ selfLid: msg.selfLid,
309
+ selfE164: msg.selfE164,
310
+ fromMe: msg.fromMe,
311
+ sendComposing: msg.sendComposing,
312
+ reply: msg.reply,
313
+ sendMedia: msg.sendMedia
314
+ },
315
+ quote: buildQuoteFromFlatAliases(msg),
316
+ group: buildGroupFromFlatAliases(msg)
317
+ });
318
+ }
319
+ function normalizeWebInboundMessage(msg) {
320
+ if (msg.event && msg.payload && msg.platform) return withDeprecatedWebInboundMessageFlatAliases(msg);
321
+ if (msg.event || msg.payload || msg.platform || msg.quote || msg.group) throw new Error("WhatsApp inbound messages must be either legacy flat or canonical nested; partial nested contexts are not supported.");
322
+ return normalizeLegacyFlatWebInboundMessage(msg);
323
+ }
324
+ //#endregion
55
325
  //#region extensions/whatsapp/src/inbound/dedupe.ts
56
326
  const RECENT_WEB_MESSAGE_TTL_MS = 20 * 6e4;
57
327
  const RECENT_WEB_MESSAGE_MAX = 5e3;
@@ -353,15 +623,15 @@ async function attachWebInboxToSocket(options) {
353
623
  const pendingDebounceKeys = /* @__PURE__ */ new Set();
354
624
  const activeInboundFlushes = /* @__PURE__ */ new Set();
355
625
  const buildInboundDebounceKey = (msg) => {
356
- const sender = msg.sender;
357
- const senderKey = msg.chatType === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.senderJid ?? msg.senderE164 ?? msg.senderName ?? msg.from : msg.from;
626
+ const sender = msg.platform.sender;
627
+ const senderKey = msg.chatType === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.platform.senderJid ?? msg.platform.senderE164 ?? msg.platform.senderName ?? msg.from : msg.from;
358
628
  if (!senderKey) return null;
359
- const conversationKey = msg.chatType === "group" ? msg.chatId : msg.from;
629
+ const conversationKey = msg.chatType === "group" ? msg.platform.chatJid : msg.from;
360
630
  return `${msg.accountId}:${conversationKey}:${senderKey}`;
361
631
  };
362
632
  const shouldDebounceInboundMessage = (msg) => options.shouldDebounce?.(msg) ?? true;
363
633
  const orderDebouncedInboundEntries = (entries) => entries.toSorted((a, b) => {
364
- const timestampDiff = (a.timestamp ?? 0) - (b.timestamp ?? 0);
634
+ const timestampDiff = (a.event.timestamp ?? 0) - (b.event.timestamp ?? 0);
365
635
  if (timestampDiff !== 0) return timestampDiff;
366
636
  return (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
367
637
  });
@@ -398,15 +668,28 @@ async function attachWebInboxToSocket(options) {
398
668
  return;
399
669
  }
400
670
  const mentioned = /* @__PURE__ */ new Set();
401
- for (const entry of orderedEntries) for (const jid of entry.mentions ?? entry.mentionedJids ?? []) mentioned.add(jid);
402
- const combinedBody = orderedEntries.map((entry) => entry.body).filter(Boolean).join("\n");
403
- const combinedMessage = {
671
+ for (const entry of orderedEntries) for (const jid of entry.group?.mentions?.jids ?? []) mentioned.add(jid);
672
+ const combinedBody = orderedEntries.map((entry) => entry.payload.body).filter(Boolean).join("\n");
673
+ const combinedMentions = mentioned.size > 0 ? {
674
+ ...last.group?.mentions,
675
+ jids: Array.from(mentioned)
676
+ } : last.group?.mentions;
677
+ const combinedGroup = last.group || combinedMentions ? {
678
+ ...last.group,
679
+ mentions: combinedMentions
680
+ } : void 0;
681
+ const combinedMessage = withDeprecatedWebInboundMessageFlatAliases({
404
682
  ...last,
405
- body: combinedBody,
406
- mentions: mentioned.size > 0 ? Array.from(mentioned) : void 0,
407
- mentionedJids: mentioned.size > 0 ? Array.from(mentioned) : void 0,
408
- isBatched: true
409
- };
683
+ payload: {
684
+ ...last.payload,
685
+ body: combinedBody
686
+ },
687
+ group: combinedGroup,
688
+ event: {
689
+ ...last.event,
690
+ isBatched: true
691
+ }
692
+ });
410
693
  await options.onMessage(combinedMessage);
411
694
  await finalizeInboundDelivery(orderedEntries);
412
695
  } catch (error) {
@@ -781,69 +1064,85 @@ async function attachWebInboxToSocket(options) {
781
1064
  mediaFileName: enriched.mediaFileName,
782
1065
  timestamp
783
1066
  }, "inbound message");
784
- const inboundMessage = {
785
- id: inbound.id,
1067
+ const media = enriched.mediaPath || enriched.mediaType || enriched.mediaFileName ? {
1068
+ path: enriched.mediaPath,
1069
+ type: enriched.mediaType,
1070
+ fileName: enriched.mediaFileName
1071
+ } : void 0;
1072
+ const groupMentions = mentionedJids ? { jids: mentionedJids } : void 0;
1073
+ const group = inbound.group && (inbound.groupSubject || inbound.groupParticipants?.length || groupMentions) ? {
1074
+ subject: inbound.groupSubject,
1075
+ participants: inbound.groupParticipants,
1076
+ mentions: groupMentions
1077
+ } : void 0;
1078
+ const inboundMessage = withDeprecatedWebInboundMessageFlatAliases({
1079
+ event: {
1080
+ id: inbound.id,
1081
+ timestamp
1082
+ },
1083
+ payload: {
1084
+ body: enriched.body,
1085
+ location: enriched.location ?? void 0,
1086
+ untrustedStructuredContext: enriched.contactContext ? [{
1087
+ label: "WhatsApp contact",
1088
+ source: "whatsapp",
1089
+ type: enriched.contactContext.kind,
1090
+ payload: enriched.contactContext
1091
+ }] : void 0,
1092
+ media
1093
+ },
1094
+ platform: {
1095
+ chatJid: inbound.remoteJid,
1096
+ recipientJid: self.e164 ?? "me",
1097
+ pushName: senderName,
1098
+ sender: resolveComparableIdentity({
1099
+ jid: inbound.participantJid,
1100
+ e164: inbound.senderE164 ?? void 0,
1101
+ name: senderName
1102
+ }),
1103
+ senderJid: inbound.participantJid,
1104
+ senderE164: inbound.senderE164 ?? void 0,
1105
+ senderName,
1106
+ self,
1107
+ selfJid: self.jid ?? void 0,
1108
+ selfLid: self.lid ?? void 0,
1109
+ selfE164: self.e164 ?? void 0,
1110
+ fromMe: Boolean(msg.key?.fromMe),
1111
+ sendComposing,
1112
+ reply,
1113
+ sendMedia
1114
+ },
786
1115
  from: inbound.from,
787
1116
  conversationId: inbound.from,
788
- to: self.e164 ?? "me",
789
1117
  accountId: inbound.access.resolvedAccountId,
790
1118
  accessControlPassed: true,
791
- body: enriched.body,
792
- pushName: senderName,
793
- timestamp,
794
1119
  chatType: inbound.group ? "group" : "direct",
795
- chatId: inbound.remoteJid,
796
- sender: resolveComparableIdentity({
797
- jid: inbound.participantJid,
798
- e164: inbound.senderE164 ?? void 0,
799
- name: senderName
800
- }),
801
- senderJid: inbound.participantJid,
802
- senderE164: inbound.senderE164 ?? void 0,
803
- senderName,
804
- replyTo: enriched.replyContext ?? void 0,
805
- replyToId: enriched.replyContext?.id,
806
- replyToBody: enriched.replyContext?.body,
807
- replyToSender: enriched.replyContext?.sender?.label ?? void 0,
808
- replyToSenderJid: enriched.replyContext?.sender?.jid ?? void 0,
809
- replyToSenderE164: enriched.replyContext?.sender?.e164 ?? void 0,
810
- groupSubject: inbound.groupSubject,
811
- groupParticipants: inbound.groupParticipants,
812
- mentions: mentionedJids ?? void 0,
813
- mentionedJids: mentionedJids ?? void 0,
814
- self,
815
- selfJid: self.jid ?? void 0,
816
- selfLid: self.lid ?? void 0,
817
- selfE164: self.e164 ?? void 0,
818
- fromMe: Boolean(msg.key?.fromMe),
819
- location: enriched.location ?? void 0,
820
- untrustedStructuredContext: enriched.contactContext ? [{
821
- label: "WhatsApp contact",
822
- source: "whatsapp",
823
- type: enriched.contactContext.kind,
824
- payload: enriched.contactContext
825
- }] : void 0,
826
- sendComposing,
827
- reply,
828
- sendMedia,
829
- mediaPath: enriched.mediaPath,
830
- mediaType: enriched.mediaType,
831
- mediaFileName: enriched.mediaFileName,
1120
+ quote: enriched.replyContext ? {
1121
+ context: enriched.replyContext,
1122
+ id: enriched.replyContext.id,
1123
+ body: enriched.replyContext.body,
1124
+ sender: {
1125
+ displayName: enriched.replyContext.sender?.label ?? void 0,
1126
+ jid: enriched.replyContext.sender?.jid ?? void 0,
1127
+ e164: enriched.replyContext.sender?.e164 ?? void 0
1128
+ }
1129
+ } : void 0,
1130
+ group,
832
1131
  dedupeKey: inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : void 0,
833
1132
  durableId: durable.durableId,
834
1133
  readReceipt: durable.readReceipt,
835
1134
  receiveOrder: durable.receiveOrder
836
- };
1135
+ });
837
1136
  const debounceKey = buildInboundDebounceKey(inboundMessage);
838
1137
  if (debounceKey) {
839
1138
  inboundMessage.debounceKey = debounceKey;
840
1139
  if (inboundDebounceMs > 0 && shouldDebounceInboundMessage(inboundMessage)) pendingDebounceKeys.add(debounceKey);
841
1140
  }
842
- if (inboundMessage.id) cacheInboundMessageMeta(inboundMessage.accountId, inboundMessage.chatId, inboundMessage.id, {
843
- participant: inboundMessage.senderJid,
844
- participantE164: inboundMessage.chatType === "direct" ? inboundMessage.senderE164 : void 0,
845
- body: inboundMessage.body,
846
- fromMe: inboundMessage.fromMe
1141
+ if (inboundMessage.event.id) cacheInboundMessageMeta(inboundMessage.accountId, inboundMessage.platform.chatJid, inboundMessage.event.id, {
1142
+ participant: inboundMessage.platform.senderJid,
1143
+ participantE164: inboundMessage.chatType === "direct" ? inboundMessage.platform.senderE164 : void 0,
1144
+ body: inboundMessage.payload.body,
1145
+ fromMe: inboundMessage.platform.fromMe
847
1146
  });
848
1147
  try {
849
1148
  Promise.resolve(debouncer.enqueue(inboundMessage)).catch((err) => {
@@ -1008,8 +1307,13 @@ async function monitorWebInbox(options) {
1008
1307
  closeInboundMonitorSocket(sock);
1009
1308
  throw err;
1010
1309
  }
1310
+ const shouldDebounce = options.shouldDebounce;
1011
1311
  return attachWebInboxToSocket({
1012
1312
  ...options,
1313
+ onMessage: async (msg) => {
1314
+ await options.onMessage(normalizeWebInboundMessage(msg));
1315
+ },
1316
+ shouldDebounce: shouldDebounce ? (msg) => shouldDebounce(normalizeWebInboundMessage(msg)) : void 0,
1013
1317
  sock
1014
1318
  });
1015
1319
  }
@@ -1043,13 +1347,13 @@ function isBotMentionedFromTargets(msg, mentionCfg, targets) {
1043
1347
  for (const mention of targets.normalizedMentions) if (identitiesOverlap(targets.self, mention)) return true;
1044
1348
  return false;
1045
1349
  } else if (hasMentions && isSelfChat) {}
1046
- const bodyClean = clean(msg.body);
1350
+ const bodyClean = clean(msg.payload.body);
1047
1351
  if (mentionCfg.mentionRegexes.some((re) => re.test(bodyClean))) return true;
1048
1352
  if (targets.self.e164) {
1049
1353
  const selfDigits = targets.self.e164.replace(/\D/g, "");
1050
1354
  if (selfDigits) {
1051
1355
  if (bodyClean.replace(/[^\d]/g, "").includes(selfDigits)) return true;
1052
- const bodyNoSpace = msg.body.replace(/[\s-]/g, "");
1356
+ const bodyNoSpace = msg.payload.body.replace(/[\s-]/g, "");
1053
1357
  if (new RegExp(`\\+?${selfDigits}`, "i").test(bodyNoSpace)) return true;
1054
1358
  }
1055
1359
  }
@@ -1061,13 +1365,13 @@ function debugMention(msg, mentionCfg, authDir) {
1061
1365
  wasMentioned: isBotMentionedFromTargets(msg, mentionCfg, mentionTargets),
1062
1366
  details: {
1063
1367
  from: msg.from,
1064
- body: msg.body,
1065
- bodyClean: normalizeMentionText(msg.body),
1066
- mentionedJids: msg.mentions ?? msg.mentionedJids ?? null,
1368
+ body: msg.payload.body,
1369
+ bodyClean: normalizeMentionText(msg.payload.body),
1370
+ mentionedJids: msg.group?.mentions?.jids ?? null,
1067
1371
  normalizedMentionedJids: mentionTargets.normalizedMentions.length ? mentionTargets.normalizedMentions.map((identity) => getComparableIdentityValues(identity)) : null,
1068
- selfJid: msg.self?.jid ?? msg.selfJid ?? null,
1069
- selfLid: msg.self?.lid ?? msg.selfLid ?? null,
1070
- selfE164: msg.self?.e164 ?? msg.selfE164 ?? null,
1372
+ selfJid: msg.platform.self?.jid ?? msg.platform.selfJid ?? null,
1373
+ selfLid: msg.platform.self?.lid ?? msg.platform.selfLid ?? null,
1374
+ selfE164: msg.platform.self?.e164 ?? msg.platform.selfE164 ?? null,
1071
1375
  resolvedSelf: mentionTargets.self
1072
1376
  }
1073
1377
  };
@@ -1277,7 +1581,7 @@ async function resolveGroupActivationFor(params) {
1277
1581
  //#endregion
1278
1582
  //#region extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts
1279
1583
  async function maybeSendAckReaction(params) {
1280
- if (!params.msg.id) return null;
1584
+ if (!params.msg.event.id) return null;
1281
1585
  if (resolveWhatsAppReactionLevel({
1282
1586
  cfg: params.cfg,
1283
1587
  accountId: params.accountId
@@ -1309,8 +1613,8 @@ async function maybeSendAckReaction(params) {
1309
1613
  });
1310
1614
  if (!shouldSendReaction()) return null;
1311
1615
  params.info({
1312
- chatId: params.msg.chatId,
1313
- messageId: params.msg.id,
1616
+ chatId: params.msg.platform.chatJid,
1617
+ messageId: params.msg.event.id,
1314
1618
  emoji
1315
1619
  }, "sending ack reaction");
1316
1620
  const sender = getSenderIdentity(params.msg);
@@ -1323,15 +1627,15 @@ async function maybeSendAckReaction(params) {
1323
1627
  };
1324
1628
  return createAckReactionHandle({
1325
1629
  ackReactionValue: emoji,
1326
- send: () => sendReactionWhatsApp(params.msg.chatId, params.msg.id, emoji, reactionOptions),
1327
- remove: () => sendReactionWhatsApp(params.msg.chatId, params.msg.id, "", reactionOptions),
1630
+ send: () => sendReactionWhatsApp(params.msg.platform.chatJid, params.msg.event.id, emoji, reactionOptions),
1631
+ remove: () => sendReactionWhatsApp(params.msg.platform.chatJid, params.msg.event.id, "", reactionOptions),
1328
1632
  onSendError: (err) => {
1329
1633
  params.warn({
1330
1634
  error: formatError(err),
1331
- chatId: params.msg.chatId,
1332
- messageId: params.msg.id
1635
+ chatId: params.msg.platform.chatJid,
1636
+ messageId: params.msg.event.id
1333
1637
  }, "failed to send ack reaction");
1334
- logVerbose(`WhatsApp ack reaction failed for chat ${params.msg.chatId}: ${formatError(err)}`);
1638
+ logVerbose(`WhatsApp ack reaction failed for chat ${params.msg.platform.chatJid}: ${formatError(err)}`);
1335
1639
  }
1336
1640
  });
1337
1641
  }
@@ -1487,10 +1791,10 @@ function recordPendingGroupHistoryEntry(params) {
1487
1791
  limit: params.groupHistoryLimit,
1488
1792
  entry: {
1489
1793
  sender,
1490
- body: params.body ?? params.msg.body,
1491
- timestamp: params.msg.timestamp,
1492
- id: params.msg.id,
1493
- senderJid: senderIdentity.jid ?? params.msg.senderJid
1794
+ body: params.body ?? params.msg.payload.body,
1795
+ timestamp: params.msg.event.timestamp,
1796
+ id: params.msg.event.id,
1797
+ senderJid: senderIdentity.jid ?? params.msg.platform.senderJid
1494
1798
  }
1495
1799
  });
1496
1800
  }
@@ -1545,9 +1849,12 @@ async function applyGroupGating(params) {
1545
1849
  };
1546
1850
  const mentionMsg = params.mentionText !== void 0 ? {
1547
1851
  ...params.msg,
1548
- body: params.mentionText
1852
+ payload: {
1853
+ ...params.msg.payload,
1854
+ body: params.mentionText
1855
+ }
1549
1856
  } : params.msg;
1550
- const commandBody = stripMentionsForCommand(mentionMsg.body, mentionConfig.mentionRegexes, self.e164);
1857
+ const commandBody = stripMentionsForCommand(mentionMsg.payload.body, mentionConfig.mentionRegexes, self.e164);
1551
1858
  const activationCommand = parseActivationCommand(commandBody);
1552
1859
  const owner = isOwnerSender(baseMentionConfig, params.msg);
1553
1860
  const shouldBypassMention = owner && hasControlCommand(commandBody, params.cfg);
@@ -1591,7 +1898,7 @@ async function applyGroupGating(params) {
1591
1898
  needsMentionText: true
1592
1899
  };
1593
1900
  }
1594
- return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${params.conversationId}: ${mentionMsg.body}`, params.mentionText);
1901
+ return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${params.conversationId}: ${mentionMsg.payload.body}`, params.mentionText);
1595
1902
  }
1596
1903
  return { shouldProcess: true };
1597
1904
  }
@@ -1659,6 +1966,21 @@ function elide(text, limit = 400) {
1659
1966
  if (text.length <= limit) return text;
1660
1967
  return `${text.slice(0, limit)}… (truncated ${text.length - limit} chars)`;
1661
1968
  }
1969
+ function markWhatsAppVisibleDeliveryError(error) {
1970
+ if (typeof error === "object" && error !== null && !Array.isArray(error)) try {
1971
+ Object.assign(error, {
1972
+ sentBeforeError: true,
1973
+ visibleReplySent: true
1974
+ });
1975
+ return error;
1976
+ } catch {}
1977
+ const visibleError = new Error("visible WhatsApp reply delivery failed", { cause: error });
1978
+ Object.assign(visibleError, {
1979
+ sentBeforeError: true,
1980
+ visibleReplySent: true
1981
+ });
1982
+ return visibleError;
1983
+ }
1662
1984
  function isLikelyWhatsAppCryptoError(reason) {
1663
1985
  const formatReason = (value) => {
1664
1986
  if (value == null) return "";
@@ -1718,21 +2040,6 @@ function createWhatsAppReplyDeliveryReceipt(results) {
1718
2040
  kind: resolveWhatsAppReceiptKind(results)
1719
2041
  });
1720
2042
  }
1721
- function markWhatsAppVisibleDeliveryError(error) {
1722
- if (typeof error === "object" && error !== null && !Array.isArray(error)) try {
1723
- Object.assign(error, {
1724
- sentBeforeError: true,
1725
- visibleReplySent: true
1726
- });
1727
- return error;
1728
- } catch {}
1729
- const visibleError = new Error("visible WhatsApp reply delivery failed", { cause: error });
1730
- Object.assign(visibleError, {
1731
- sentBeforeError: true,
1732
- visibleReplySent: true
1733
- });
1734
- return visibleError;
1735
- }
1736
2043
  async function deliverWebReply(params) {
1737
2044
  const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
1738
2045
  const replyStarted = Date.now();
@@ -1758,12 +2065,12 @@ async function deliverWebReply(params) {
1758
2065
  const mediaList = normalizedReply.mediaUrls ?? [];
1759
2066
  const getQuote = () => {
1760
2067
  if (!replyResult.replyToId) return;
1761
- const cached = lookupInboundMessageMeta(msg.accountId, msg.chatId, replyResult.replyToId);
2068
+ const cached = lookupInboundMessageMeta(msg.accountId, msg.platform.chatJid, replyResult.replyToId);
1762
2069
  return buildQuotedMessageOptions({
1763
2070
  messageId: replyResult.replyToId,
1764
- remoteJid: msg.chatId,
2071
+ remoteJid: msg.platform.chatJid,
1765
2072
  fromMe: cached?.fromMe ?? false,
1766
- participant: cached?.participant ?? (msg.chatType === "group" ? msg.senderJid : void 0),
2073
+ participant: cached?.participant ?? (msg.chatType === "group" ? msg.platform.senderJid : void 0),
1767
2074
  messageText: cached?.body ?? ""
1768
2075
  });
1769
2076
  };
@@ -1786,7 +2093,7 @@ async function deliverWebReply(params) {
1786
2093
  for (const [index, chunk] of textChunks.entries()) {
1787
2094
  const chunkStarted = Date.now();
1788
2095
  const quote = getQuote();
1789
- rememberSendResult(await sendWithRetry(() => msg.reply(chunk, quote), "text"));
2096
+ rememberSendResult(await sendWithRetry(() => msg.platform.reply(chunk, quote), "text"));
1790
2097
  if (!skipLog) {
1791
2098
  const durationMs = Date.now() - chunkStarted;
1792
2099
  whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${msg.from} (${durationMs.toFixed(0)}ms)`);
@@ -1794,10 +2101,10 @@ async function deliverWebReply(params) {
1794
2101
  }
1795
2102
  const delivery = finishDelivery();
1796
2103
  const logPayload = {
1797
- correlationId: msg.id ?? newConnectionId(),
2104
+ correlationId: msg.event.id ?? newConnectionId(),
1798
2105
  connectionId: connectionId ?? null,
1799
2106
  to: msg.from,
1800
- from: msg.to,
2107
+ from: msg.platform.recipientJid,
1801
2108
  text: elide(replyResult.text, 240),
1802
2109
  mediaUrl: null,
1803
2110
  mediaSizeBytes: null,
@@ -1823,29 +2130,29 @@ async function deliverWebReply(params) {
1823
2130
  }
1824
2131
  if (media.kind === "image") {
1825
2132
  const quote = getQuote();
1826
- rememberSendResult(await sendWithRetry(() => msg.sendMedia({
2133
+ rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
1827
2134
  image: media.buffer,
1828
2135
  caption,
1829
2136
  mimetype: media.mimetype
1830
2137
  }, quote), "media:image"));
1831
2138
  } else if (media.kind === "audio") {
1832
2139
  const quote = getQuote();
1833
- rememberSendResult(await sendWithRetry(() => msg.sendMedia({
2140
+ rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
1834
2141
  audio: media.buffer,
1835
2142
  ptt: true,
1836
2143
  mimetype: media.mimetype
1837
2144
  }, quote), "media:audio"));
1838
- if (caption) rememberSendResult(await sendWithRetry(() => msg.reply(caption, quote), "media:audio-text"));
2145
+ if (caption) rememberSendResult(await sendWithRetry(() => msg.platform.reply(caption, quote), "media:audio-text"));
1839
2146
  } else if (media.kind === "video") {
1840
2147
  const quote = getQuote();
1841
- rememberSendResult(await sendWithRetry(() => msg.sendMedia({
2148
+ rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
1842
2149
  video: media.buffer,
1843
2150
  caption,
1844
2151
  mimetype: media.mimetype
1845
2152
  }, quote), "media:video"));
1846
2153
  } else {
1847
2154
  const quote = getQuote();
1848
- rememberSendResult(await sendWithRetry(() => msg.sendMedia({
2155
+ rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
1849
2156
  document: media.buffer,
1850
2157
  fileName: media.fileName,
1851
2158
  caption,
@@ -1854,10 +2161,10 @@ async function deliverWebReply(params) {
1854
2161
  }
1855
2162
  whatsappOutboundLog.info(`Sent media reply to ${msg.from} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
1856
2163
  replyLogger.info({
1857
- correlationId: msg.id ?? newConnectionId(),
2164
+ correlationId: msg.event.id ?? newConnectionId(),
1858
2165
  connectionId: connectionId ?? null,
1859
2166
  to: msg.from,
1860
- from: msg.to,
2167
+ from: msg.platform.recipientJid,
1861
2168
  text: caption ?? null,
1862
2169
  mediaUrl,
1863
2170
  mediaSizeBytes: media.buffer.length,
@@ -1875,17 +2182,17 @@ async function deliverWebReply(params) {
1875
2182
  const fallbackText = [remainingText.shift() ?? caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
1876
2183
  if (!fallbackText) return;
1877
2184
  whatsappOutboundLog.warn(`Media skipped; sent text-only to ${msg.from}`);
1878
- rememberSendResult(await sendWithRetry(() => msg.reply(fallbackText, getQuote()), "media:fallback-text"));
2185
+ rememberSendResult(await sendWithRetry(() => msg.platform.reply(fallbackText, getQuote()), "media:fallback-text"));
1879
2186
  }
1880
2187
  });
1881
- for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => msg.reply(chunk, getQuote()), "media:text"));
2188
+ for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => msg.platform.reply(chunk, getQuote()), "media:text"));
1882
2189
  return finishDelivery();
1883
2190
  }
1884
2191
  //#endregion
1885
2192
  //#region extensions/whatsapp/src/auto-reply/monitor/inbound-context.ts
1886
2193
  function isWhatsAppSupplementalSenderAllowed(params) {
1887
2194
  if (params.allowFrom.includes("*")) return true;
1888
- const senderValues = new Set(getComparableIdentityValues(params.sender));
2195
+ const senderValues = new Set(getComparableIdentityValues(resolveComparableIdentity(params.sender, params.authDir)));
1889
2196
  if (senderValues.size === 0) return false;
1890
2197
  for (const entry of params.allowFrom) {
1891
2198
  const rawEntry = entry.trim();
@@ -1903,6 +2210,7 @@ function resolveVisibleWhatsAppGroupHistory(params) {
1903
2210
  kind: "history",
1904
2211
  isSenderAllowed: (entry) => isWhatsAppSupplementalSenderAllowed({
1905
2212
  allowFrom: params.groupAllowFrom,
2213
+ authDir: params.authDir,
1906
2214
  sender: entry.senderJid ? { jid: entry.senderJid } : null
1907
2215
  })
1908
2216
  }).items;
@@ -1912,6 +2220,7 @@ function resolveVisibleWhatsAppReplyContext(params) {
1912
2220
  if (!replyTo) return null;
1913
2221
  const senderAllowed = params.msg.chatType !== "group" || params.groupPolicy !== "allowlist" ? true : isWhatsAppSupplementalSenderAllowed({
1914
2222
  allowFrom: params.groupAllowFrom,
2223
+ authDir: params.authDir,
1915
2224
  sender: replyTo.sender
1916
2225
  });
1917
2226
  return filterChannelInboundQuoteContext(params.mode, {
@@ -1938,34 +2247,19 @@ function whatsAppReplyDeliveryVisibility(visibleReplySent) {
1938
2247
  function whatsAppReplyDeliveryVisibilityFromDurableResult(result) {
1939
2248
  return whatsAppReplyDeliveryVisibility(result.visibleReplySent === true);
1940
2249
  }
1941
- function markWhatsAppReplyDeliveryErrorVisible(error) {
1942
- if (typeof error === "object" && error !== null && !Array.isArray(error)) try {
1943
- Object.assign(error, {
1944
- sentBeforeError: true,
1945
- visibleReplySent: true
1946
- });
1947
- return error;
1948
- } catch {}
1949
- const visibleError = new Error("visible WhatsApp reply delivery failed", { cause: error });
1950
- Object.assign(visibleError, {
1951
- sentBeforeError: true,
1952
- visibleReplySent: true
1953
- });
1954
- return visibleError;
1955
- }
1956
2250
  function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
1957
- return flushResult.delivered > 0 ? markWhatsAppReplyDeliveryErrorVisible(error) : error;
2251
+ return flushResult.delivered > 0 ? markWhatsAppVisibleDeliveryError(error) : error;
1958
2252
  }
1959
2253
  function logWhatsAppReplyDeliveryError(params) {
1960
2254
  params.replyLogger.error({
1961
2255
  err: normalizeErrForLog(params.err),
1962
2256
  replyKind: params.info.kind,
1963
- correlationId: params.msg.id ?? null,
2257
+ correlationId: params.msg.event.id ?? null,
1964
2258
  connectionId: params.connectionId,
1965
2259
  conversationId: params.conversationId,
1966
- chatId: params.msg.chatId ?? null,
2260
+ chatId: params.msg.platform.chatJid ?? null,
1967
2261
  to: params.msg.from ?? null,
1968
- from: params.msg.to ?? null
2262
+ from: params.msg.platform.recipientJid ?? null
1969
2263
  }, "auto-reply delivery failed");
1970
2264
  }
1971
2265
  function resolveWhatsAppDisableBlockStreaming(cfg) {
@@ -2042,10 +2336,10 @@ async function buildWhatsAppInboundContext(params) {
2042
2336
  })),
2043
2337
  limit: params.groupHistory?.length ?? 1
2044
2338
  }) : void 0;
2045
- const media = toInboundMediaFacts(params.msg.mediaPath || params.msg.mediaUrl ? [{
2046
- path: params.msg.mediaPath,
2047
- url: params.msg.mediaUrl ?? params.msg.mediaPath,
2048
- contentType: params.msg.mediaType
2339
+ const media = toInboundMediaFacts(params.msg.payload.media?.path || params.msg.payload.media?.url ? [{
2340
+ path: params.msg.payload.media?.path,
2341
+ url: params.msg.payload.media?.url ?? params.msg.payload.media?.path,
2342
+ contentType: params.msg.payload.media?.type
2049
2343
  }] : void 0, { transcribed: (_entry, index) => params.mediaTranscribedIndexes?.includes(index) === true });
2050
2344
  return buildChannelInboundEventContext({
2051
2345
  channel: "whatsapp",
@@ -2057,11 +2351,11 @@ async function buildWhatsAppInboundContext(params) {
2057
2351
  sender: params.visibleReplyTo.sender?.label ?? void 0
2058
2352
  } : void 0,
2059
2353
  groupSystemPrompt: params.groupSystemPrompt,
2060
- untrustedContext: params.msg.untrustedStructuredContext
2354
+ untrustedContext: params.msg.payload.untrustedStructuredContext
2061
2355
  },
2062
2356
  media,
2063
- messageId: params.msg.id,
2064
- timestamp: params.msg.timestamp,
2357
+ messageId: params.msg.event.id,
2358
+ timestamp: params.msg.event.timestamp,
2065
2359
  from: params.msg.from,
2066
2360
  sender: {
2067
2361
  id: params.sender.id ?? params.sender.e164,
@@ -2078,15 +2372,15 @@ async function buildWhatsAppInboundContext(params) {
2078
2372
  routeSessionKey: params.route.sessionKey
2079
2373
  },
2080
2374
  reply: {
2081
- to: params.msg.to,
2375
+ to: params.msg.platform.recipientJid,
2082
2376
  originatingTo: params.msg.from
2083
2377
  },
2084
2378
  message: {
2085
2379
  body: params.combinedBody,
2086
- bodyForAgent: params.bodyForAgent ?? params.msg.body,
2380
+ bodyForAgent: params.bodyForAgent ?? params.msg.payload.body,
2087
2381
  inboundHistory,
2088
- rawBody: params.rawBody ?? params.msg.body,
2089
- commandBody: params.commandBody ?? params.msg.body
2382
+ rawBody: params.rawBody ?? params.msg.payload.body,
2383
+ commandBody: params.commandBody ?? params.msg.payload.body
2090
2384
  },
2091
2385
  access: {
2092
2386
  ...params.msg.wasMentioned !== void 0 ? { mentions: {
@@ -2098,16 +2392,17 @@ async function buildWhatsAppInboundContext(params) {
2098
2392
  commandTurn: params.commandTurn,
2099
2393
  extra: {
2100
2394
  Transcript: params.transcript,
2101
- GroupSubject: params.msg.groupSubject,
2395
+ GroupSubject: params.msg.group?.subject,
2102
2396
  GroupMembers: formatGroupMembers({
2103
- participants: params.msg.groupParticipants,
2397
+ participants: params.msg.group?.participants,
2104
2398
  roster: params.groupMemberRoster,
2105
2399
  fallbackE164: params.sender.e164
2106
2400
  }),
2107
2401
  SenderE164: params.sender.e164,
2108
2402
  CommandSource: params.commandSource ?? (params.commandTurn?.source === "native" || params.commandTurn?.source === "text" ? params.commandTurn.source : void 0),
2109
2403
  ReplyThreading: params.replyThreading,
2110
- ...params.msg.location ? toLocationContext(params.msg.location) : {}
2404
+ SuppressMessageReceivedHooks: params.suppressMessageReceivedHooks,
2405
+ ...params.msg.payload.location ? toLocationContext(params.msg.payload.location) : {}
2111
2406
  }
2112
2407
  });
2113
2408
  }
@@ -2215,12 +2510,12 @@ async function dispatchWhatsAppBufferedReply(params) {
2215
2510
  tableMode
2216
2511
  })).providerAccepted) {
2217
2512
  params.replyLogger.warn({
2218
- correlationId: params.msg.id ?? null,
2513
+ correlationId: params.msg.event.id ?? null,
2219
2514
  connectionId: params.connectionId,
2220
2515
  conversationId: params.conversationId,
2221
- chatId: params.msg.chatId,
2516
+ chatId: params.msg.platform.chatJid,
2222
2517
  to: params.msg.from,
2223
- from: params.msg.to,
2518
+ from: params.msg.platform.recipientJid,
2224
2519
  replyKind: info.kind
2225
2520
  }, "auto-reply was not accepted by WhatsApp provider");
2226
2521
  return whatsAppReplyDeliveryVisibility(false);
@@ -2282,7 +2577,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2282
2577
  }
2283
2578
  });
2284
2579
  if (durable.status === "failed") {
2285
- if (durable.sentBeforeError === true) throw markWhatsAppReplyDeliveryErrorVisible(durable.error);
2580
+ if (durable.sentBeforeError === true) throw markWhatsAppVisibleDeliveryError(durable.error);
2286
2581
  throw durable.error;
2287
2582
  }
2288
2583
  if (durable.status === "handled_visible") {
@@ -2329,7 +2624,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2329
2624
  logWhatsAppMediaOnlyFlushResult(flushResult);
2330
2625
  return whatsAppReplyDeliveryVisibility(flushResult.delivered > 0);
2331
2626
  },
2332
- onReplyStart: params.msg.sendComposing,
2627
+ onReplyStart: params.msg.platform.sendComposing,
2333
2628
  ...statusReactionController ? {
2334
2629
  onCompactionStart: async () => {
2335
2630
  await statusReactionController.setCompacting();
@@ -2429,11 +2724,13 @@ function resolveMessagePrefix(cfg, agentId, opts) {
2429
2724
  }
2430
2725
  //#endregion
2431
2726
  //#region extensions/whatsapp/src/auto-reply/monitor/message-line.ts
2432
- function formatReplyContext(msg) {
2433
- const replyTo = getReplyContext(msg);
2727
+ function formatReplyTarget(replyTo) {
2434
2728
  if (!replyTo?.body) return null;
2435
2729
  return `[Replying to ${replyTo.sender?.label ?? replyTo.sender?.e164 ?? "unknown sender"}${replyTo.id ? ` id:${replyTo.id}` : ""}]\n${replyTo.body}\n[/Replying]`;
2436
2730
  }
2731
+ function formatReplyContext(msg) {
2732
+ return formatReplyTarget(getReplyContext(msg));
2733
+ }
2437
2734
  function buildInboundLine(params) {
2438
2735
  const { cfg, msg, agentId, previousTimestamp, envelope } = params;
2439
2736
  const messagePrefix = resolveMessagePrefix(cfg, agentId, {
@@ -2441,13 +2738,13 @@ function buildInboundLine(params) {
2441
2738
  hasAllowFrom: (cfg.channels?.whatsapp?.allowFrom?.length ?? 0) > 0
2442
2739
  });
2443
2740
  const prefixStr = messagePrefix ? `${messagePrefix} ` : "";
2444
- const replyContext = formatReplyContext(msg);
2445
- const baseLine = `${prefixStr}${msg.body}${replyContext ? `\n\n${replyContext}` : ""}`;
2741
+ const replyContext = params.visibleReplyTo === void 0 ? formatReplyContext(msg) : formatReplyTarget(params.visibleReplyTo);
2742
+ const baseLine = `${prefixStr}${msg.payload.body}${replyContext ? `\n\n${replyContext}` : ""}`;
2446
2743
  const sender = getSenderIdentity(msg);
2447
2744
  return formatInboundEnvelope({
2448
2745
  channel: "WhatsApp",
2449
2746
  from: msg.chatType === "group" ? msg.from : msg.from?.replace(/^whatsapp:/, ""),
2450
- timestamp: msg.timestamp,
2747
+ timestamp: msg.event.timestamp,
2451
2748
  body: baseLine,
2452
2749
  chatType: msg.chatType,
2453
2750
  sender: {
@@ -2457,13 +2754,13 @@ function buildInboundLine(params) {
2457
2754
  },
2458
2755
  previousTimestamp,
2459
2756
  envelope,
2460
- fromMe: msg.fromMe
2757
+ fromMe: msg.platform.fromMe
2461
2758
  });
2462
2759
  }
2463
2760
  //#endregion
2464
2761
  //#region extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts
2465
2762
  async function createWhatsAppStatusReactionController(params) {
2466
- if (!params.msg.id) return null;
2763
+ if (!params.msg.event.id) return null;
2467
2764
  const statusReactionsConfig = params.cfg.messages?.statusReactions;
2468
2765
  if (statusReactionsConfig?.enabled !== true) return null;
2469
2766
  if (resolveWhatsAppReactionLevel({
@@ -2504,8 +2801,8 @@ async function createWhatsAppStatusReactionController(params) {
2504
2801
  ...params.accountId ? { accountId: params.accountId } : {},
2505
2802
  cfg: params.cfg
2506
2803
  };
2507
- const chatId = params.msg.chatId;
2508
- const msgId = params.msg.id;
2804
+ const chatId = params.msg.platform.chatJid;
2805
+ const msgId = params.msg.event.id;
2509
2806
  return createStatusReactionController({
2510
2807
  enabled: true,
2511
2808
  adapter: {
@@ -2584,15 +2881,15 @@ async function processMessage(params) {
2584
2881
  sessionKey: params.route.sessionKey
2585
2882
  });
2586
2883
  let audioTranscript = params.preflightAudioTranscript ?? void 0;
2587
- const hasAudioBody = params.msg.mediaType?.startsWith("audio/") === true && params.msg.body === "<media:audio>";
2588
- if (params.preflightAudioTranscript === void 0 && hasAudioBody && params.msg.mediaPath) try {
2884
+ const hasAudioBody = params.msg.payload.media?.type?.startsWith("audio/") === true && params.msg.payload.body === "<media:audio>";
2885
+ if (params.preflightAudioTranscript === void 0 && hasAudioBody && params.msg.payload.media?.path) try {
2589
2886
  const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.js");
2590
2887
  audioTranscript = await transcribeFirstAudio({
2591
2888
  ctx: {
2592
- MediaPaths: [params.msg.mediaPath],
2593
- MediaTypes: params.msg.mediaType ? [params.msg.mediaType] : void 0,
2889
+ MediaPaths: [params.msg.payload.media?.path],
2890
+ MediaTypes: params.msg.payload.media?.type ? [params.msg.payload.media?.type] : void 0,
2594
2891
  From: params.msg.from,
2595
- To: params.msg.to,
2892
+ To: params.msg.platform.recipientJid,
2596
2893
  Provider: "whatsapp",
2597
2894
  Surface: "whatsapp",
2598
2895
  OriginatingChannel: "whatsapp",
@@ -2606,21 +2903,33 @@ async function processMessage(params) {
2606
2903
  }
2607
2904
  const msgForAgent = audioTranscript !== void 0 ? {
2608
2905
  ...params.msg,
2609
- body: audioTranscript
2906
+ payload: {
2907
+ ...params.msg.payload,
2908
+ body: audioTranscript
2909
+ }
2610
2910
  } : params.msg;
2911
+ const visibleReplyTo = resolveVisibleWhatsAppReplyContext({
2912
+ msg: params.msg,
2913
+ authDir: account.authDir,
2914
+ mode: contextVisibilityMode,
2915
+ groupPolicy: inboundPolicy.groupPolicy,
2916
+ groupAllowFrom: inboundPolicy.groupAllowFrom
2917
+ });
2611
2918
  let combinedBody = buildInboundLine({
2612
2919
  cfg: params.cfg,
2613
2920
  msg: msgForAgent,
2614
2921
  agentId: params.route.agentId,
2615
2922
  previousTimestamp,
2616
- envelope: envelopeOptions
2923
+ envelope: envelopeOptions,
2924
+ visibleReplyTo
2617
2925
  });
2618
2926
  let shouldClearGroupHistory = false;
2619
2927
  const visibleGroupHistory = params.msg.chatType === "group" ? resolveVisibleWhatsAppGroupHistory({
2620
2928
  history: params.groupHistory ?? params.groupHistories.get(params.groupHistoryKey) ?? [],
2621
2929
  mode: contextVisibilityMode,
2622
2930
  groupPolicy: inboundPolicy.groupPolicy,
2623
- groupAllowFrom: inboundPolicy.groupAllowFrom
2931
+ groupAllowFrom: inboundPolicy.groupAllowFrom,
2932
+ authDir: account.authDir
2624
2933
  }) : void 0;
2625
2934
  if (params.msg.chatType === "group") {
2626
2935
  const history = visibleGroupHistory ?? [];
@@ -2677,35 +2986,28 @@ async function processMessage(params) {
2677
2986
  info: params.replyLogger.info.bind(params.replyLogger),
2678
2987
  warn: params.replyLogger.warn.bind(params.replyLogger)
2679
2988
  });
2680
- const correlationId = params.msg.id ?? newConnectionId();
2989
+ const correlationId = params.msg.event.id ?? newConnectionId();
2681
2990
  params.replyLogger.info({
2682
2991
  connectionId: params.connectionId,
2683
2992
  correlationId,
2684
2993
  from: params.msg.chatType === "group" ? conversationId : params.msg.from,
2685
- to: params.msg.to,
2994
+ to: params.msg.platform.recipientJid,
2686
2995
  body: elide(combinedBody, 240),
2687
- mediaType: params.msg.mediaType ?? null,
2688
- mediaPath: params.msg.mediaPath ?? null
2996
+ mediaType: params.msg.payload.media?.type ?? null,
2997
+ mediaPath: params.msg.payload.media?.path ?? null
2689
2998
  }, "inbound web message");
2690
2999
  const fromDisplay = params.msg.chatType === "group" ? conversationId : params.msg.from;
2691
- const kindLabel = params.msg.mediaType ? `, ${params.msg.mediaType}` : "";
2692
- whatsappInboundLog.info(`Inbound message ${fromDisplay} -> ${params.msg.to} (${params.msg.chatType}${kindLabel}, ${combinedBody.length} chars)`);
3000
+ const kindLabel = params.msg.payload.media?.type ? `, ${params.msg.payload.media?.type}` : "";
3001
+ whatsappInboundLog.info(`Inbound message ${fromDisplay} -> ${params.msg.platform.recipientJid} (${params.msg.chatType}${kindLabel}, ${combinedBody.length} chars)`);
2693
3002
  if (shouldLogVerbose$1()) whatsappInboundLog.debug(`Inbound body: ${elide(combinedBody, 400)}`);
2694
3003
  const sender = getSenderIdentity(params.msg);
2695
- const visibleReplyTo = resolveVisibleWhatsAppReplyContext({
2696
- msg: params.msg,
2697
- authDir: account.authDir,
2698
- mode: contextVisibilityMode,
2699
- groupPolicy: inboundPolicy.groupPolicy,
2700
- groupAllowFrom: inboundPolicy.groupAllowFrom
2701
- });
2702
3004
  const dmRouteTarget = resolveWhatsAppDmRouteTarget({
2703
3005
  msg: params.msg,
2704
3006
  senderE164: sender.e164 ?? void 0,
2705
3007
  normalizeE164
2706
3008
  });
2707
- const shouldCheckCommandAuth = shouldComputeCommandAuthorized(params.msg.body, params.cfg);
2708
- const isTextCommand = isControlCommandMessage$1(params.msg.body, params.cfg);
3009
+ const shouldCheckCommandAuth = shouldComputeCommandAuthorized(params.msg.payload.body, params.cfg);
3010
+ const isTextCommand = isControlCommandMessage$1(params.msg.payload.body, params.cfg);
2709
3011
  const commandAuthorized = shouldCheckCommandAuth ? await resolveWhatsAppCommandAuthorized({
2710
3012
  cfg: params.cfg,
2711
3013
  msg: params.msg,
@@ -2715,12 +3017,12 @@ async function processMessage(params) {
2715
3017
  kind: "text-slash",
2716
3018
  source: "text",
2717
3019
  authorized: Boolean(commandAuthorized),
2718
- body: params.msg.body
3020
+ body: params.msg.payload.body
2719
3021
  } : {
2720
3022
  kind: "normal",
2721
3023
  source: "message",
2722
3024
  authorized: false,
2723
- body: params.msg.body
3025
+ body: params.msg.payload.body
2724
3026
  };
2725
3027
  const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
2726
3028
  cfg: params.cfg,
@@ -2734,7 +3036,7 @@ async function processMessage(params) {
2734
3036
  isSelfChat: params.msg.chatType !== "group" && inboundPolicy.isSelfChat,
2735
3037
  pipelineResponsePrefix: replyPipeline.responsePrefix
2736
3038
  });
2737
- const replyThreading = resolveBatchedReplyThreadingPolicy(account.replyToMode ?? "off", params.msg.isBatched === true);
3039
+ const replyThreading = resolveBatchedReplyThreadingPolicy(account.replyToMode ?? "off", params.msg.event.isBatched === true);
2738
3040
  const conversationSystemPrompt = params.msg.chatType === "group" ? resolveWhatsAppGroupSystemPrompt({
2739
3041
  accountConfig: account,
2740
3042
  groupId: conversationId
@@ -2743,9 +3045,9 @@ async function processMessage(params) {
2743
3045
  peerId: dmRouteTarget ?? params.msg.from
2744
3046
  });
2745
3047
  const ctxPayload = await buildWhatsAppInboundContext({
2746
- bodyForAgent: msgForAgent.body,
3048
+ bodyForAgent: msgForAgent.payload.body,
2747
3049
  combinedBody,
2748
- commandBody: params.msg.body,
3050
+ commandBody: params.msg.payload.body,
2749
3051
  commandAuthorized,
2750
3052
  commandTurn,
2751
3053
  conversationId,
@@ -2753,7 +3055,7 @@ async function processMessage(params) {
2753
3055
  groupMemberRoster: params.groupMemberNames.get(params.groupHistoryKey),
2754
3056
  groupSystemPrompt: conversationSystemPrompt,
2755
3057
  msg: params.msg,
2756
- rawBody: params.msg.body,
3058
+ rawBody: params.msg.payload.body,
2757
3059
  route: params.route,
2758
3060
  sender: {
2759
3061
  id: getPrimaryIdentityId(sender) ?? void 0,
@@ -2763,7 +3065,8 @@ async function processMessage(params) {
2763
3065
  ...audioTranscript !== void 0 ? { transcript: audioTranscript } : {},
2764
3066
  ...audioTranscript !== void 0 ? { mediaTranscribedIndexes: [0] } : {},
2765
3067
  replyThreading,
2766
- visibleReplyTo: visibleReplyTo ?? void 0
3068
+ visibleReplyTo: visibleReplyTo ?? void 0,
3069
+ suppressMessageReceivedHooks: true
2767
3070
  });
2768
3071
  emitWhatsAppMessageReceivedHooksIfEnabled({
2769
3072
  cfg: params.cfg,
@@ -2791,8 +3094,8 @@ async function processMessage(params) {
2791
3094
  raw: params.msg,
2792
3095
  adapter: {
2793
3096
  ingest: () => ({
2794
- id: params.msg.id ?? `${conversationId}:${Date.now()}`,
2795
- timestamp: params.msg.timestamp,
3097
+ id: params.msg.event.id ?? `${conversationId}:${Date.now()}`,
3098
+ timestamp: params.msg.event.timestamp,
2796
3099
  rawText: ctxPayload.RawBody ?? "",
2797
3100
  textForAgent: ctxPayload.BodyForAgent,
2798
3101
  textForCommands: ctxPayload.CommandBody,
@@ -2851,7 +3154,7 @@ async function processMessage(params) {
2851
3154
  logAckFailure({
2852
3155
  log: logVerbose$1,
2853
3156
  channel: "whatsapp",
2854
- target: `${params.msg.chatId ?? conversationId}/${params.msg.id ?? "unknown"}`,
3157
+ target: `${params.msg.platform.chatJid ?? conversationId}/${params.msg.event.id ?? "unknown"}`,
2855
3158
  error: err
2856
3159
  });
2857
3160
  }
@@ -2861,6 +3164,22 @@ async function processMessage(params) {
2861
3164
  //#endregion
2862
3165
  //#region extensions/whatsapp/src/auto-reply/monitor/on-message.ts
2863
3166
  function createWebOnMessageHandler(params) {
3167
+ const withDirectSenderPeer = (msg, peerId) => {
3168
+ if (msg.chatType === "group" || msg.platform.sender?.e164 || msg.platform.senderE164 || !peerId.startsWith("+")) return msg;
3169
+ const normalized = normalizeE164(peerId);
3170
+ if (!normalized) return msg;
3171
+ return withDeprecatedWebInboundMessageFlatAliases({
3172
+ ...msg,
3173
+ platform: {
3174
+ ...msg.platform,
3175
+ sender: {
3176
+ ...msg.platform.sender,
3177
+ e164: normalized
3178
+ },
3179
+ senderE164: normalized
3180
+ }
3181
+ });
3182
+ };
2864
3183
  const processForRoute = async (cfg, msg, route, groupHistoryKey, opts) => {
2865
3184
  const processParams = {
2866
3185
  cfg,
@@ -2888,10 +3207,12 @@ function createWebOnMessageHandler(params) {
2888
3207
  if (opts?.statusReactionController !== void 0) processParams.statusReactionController = opts.statusReactionController;
2889
3208
  return processMessage(processParams);
2890
3209
  };
2891
- return async (msg) => {
3210
+ return async (rawMsg) => {
3211
+ const normalizedMsg = normalizeWebInboundMessage(rawMsg);
2892
3212
  const cfg = params.loadConfig?.() ?? params.cfg;
3213
+ const peerId = resolvePeerId(normalizedMsg);
3214
+ const msg = withDirectSenderPeer(normalizedMsg, peerId);
2893
3215
  const conversationId = msg.conversationId ?? msg.from;
2894
- const peerId = resolvePeerId(msg);
2895
3216
  const baseRoute = resolveAgentRoute({
2896
3217
  cfg,
2897
3218
  channel: "whatsapp",
@@ -2901,32 +3222,54 @@ function createWebOnMessageHandler(params) {
2901
3222
  id: peerId
2902
3223
  }
2903
3224
  });
2904
- const route = msg.chatType === "group" ? resolveWhatsAppGroupSessionRoute(baseRoute) : baseRoute;
2905
- const groupHistoryKey = msg.chatType === "group" ? buildGroupHistoryKey({
2906
- channel: "whatsapp",
2907
- accountId: route.accountId,
2908
- peerKind: "group",
2909
- peerId
2910
- }) : route.sessionKey;
3225
+ const baseConversationRoute = msg.chatType === "group" ? resolveWhatsAppGroupSessionRoute(baseRoute) : baseRoute;
3226
+ const routeAccountId = baseConversationRoute.accountId ?? msg.accountId ?? params.account.accountId ?? "default";
2911
3227
  const account = resolveWhatsAppAccount({
2912
3228
  cfg,
2913
- accountId: route.accountId ?? msg.accountId ?? params.account.accountId
3229
+ accountId: routeAccountId
2914
3230
  });
2915
3231
  const baseMentionConfig = buildMentionConfig(cfg);
2916
- if (msg.from === msg.to) logVerbose(`📱 Same-phone mode detected (from === to: ${msg.from})`);
2917
- if (params.echoTracker.has(msg.body)) {
3232
+ if (msg.from === msg.platform.recipientJid) logVerbose(`📱 Same-phone mode detected (from === to: ${msg.from})`);
3233
+ if (params.echoTracker.has(msg.payload.body)) {
2918
3234
  logVerbose("Skipping auto-reply: detected echo (message matches recently sent text)");
2919
- params.echoTracker.forget(msg.body);
3235
+ params.echoTracker.forget(msg.payload.body);
2920
3236
  return;
2921
3237
  }
3238
+ const configuredRoute = resolveConfiguredBindingRoute({
3239
+ cfg,
3240
+ route: baseConversationRoute,
3241
+ channel: "whatsapp",
3242
+ accountId: routeAccountId,
3243
+ conversationId: peerId
3244
+ });
3245
+ const route = configuredRoute.route;
3246
+ const groupHistoryKey = msg.chatType === "group" ? buildGroupHistoryKey({
3247
+ channel: "whatsapp",
3248
+ accountId: route.accountId,
3249
+ peerKind: "group",
3250
+ peerId
3251
+ }) : route.sessionKey;
2922
3252
  let preflightAudioTranscript;
2923
- const hasAudioBody = msg.mediaType?.startsWith("audio/") === true && msg.body === "<media:audio>";
3253
+ const hasAudioBody = msg.payload.media?.type?.startsWith("audio/") === true && msg.payload.body === "<media:audio>";
2924
3254
  const canRunEarlyAudioPreflight = msg.chatType === "group" || msg.accessControlPassed === true;
2925
3255
  let ackAlreadySent = false;
2926
3256
  let ackReaction = null;
2927
3257
  let statusReactionController = null;
3258
+ let recordAcceptedConfiguredGroupRoute = null;
3259
+ const clearPreDispatchReaction = async () => {
3260
+ try {
3261
+ if (statusReactionController) {
3262
+ statusReactionController.cancelPending();
3263
+ await statusReactionController.clear();
3264
+ return;
3265
+ }
3266
+ if (ackReaction && await ackReaction.ackReactionPromise) await ackReaction.remove();
3267
+ } catch (err) {
3268
+ params.replyLogger.warn({ error: String(err) }, "whatsapp: failed to clear pre-dispatch reaction after configured ACP readiness failure");
3269
+ }
3270
+ };
2928
3271
  const runAudioPreflightOnce = async () => {
2929
- if (preflightAudioTranscript !== void 0 || !canRunEarlyAudioPreflight || !hasAudioBody || !msg.mediaPath) return;
3272
+ if (preflightAudioTranscript !== void 0 || !canRunEarlyAudioPreflight || !hasAudioBody || !msg.payload.media?.path) return;
2930
3273
  if (cfg.messages?.statusReactions?.enabled === true) {
2931
3274
  statusReactionController = await createWhatsAppStatusReactionController({
2932
3275
  cfg,
@@ -2956,10 +3299,10 @@ function createWebOnMessageHandler(params) {
2956
3299
  const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.js");
2957
3300
  preflightAudioTranscript = await transcribeFirstAudio({
2958
3301
  ctx: {
2959
- MediaPaths: [msg.mediaPath],
2960
- MediaTypes: msg.mediaType ? [msg.mediaType] : void 0,
3302
+ MediaPaths: [msg.payload.media?.path],
3303
+ MediaTypes: msg.payload.media?.type ? [msg.payload.media?.type] : void 0,
2961
3304
  From: msg.from,
2962
- To: msg.to,
3305
+ To: msg.platform.recipientJid,
2963
3306
  Provider: "whatsapp",
2964
3307
  Surface: "whatsapp",
2965
3308
  OriginatingChannel: "whatsapp",
@@ -2976,12 +3319,12 @@ function createWebOnMessageHandler(params) {
2976
3319
  const sender = getSenderIdentity(msg);
2977
3320
  const metaCtx = {
2978
3321
  From: msg.from,
2979
- To: msg.to,
3322
+ To: msg.platform.recipientJid,
2980
3323
  SessionKey: route.sessionKey,
2981
3324
  AccountId: route.accountId,
2982
3325
  ChatType: msg.chatType,
2983
3326
  ConversationLabel: conversationId,
2984
- GroupSubject: msg.groupSubject,
3327
+ GroupSubject: msg.group?.subject,
2985
3328
  SenderName: sender.name ?? void 0,
2986
3329
  SenderId: getPrimaryIdentityId(sender) ?? void 0,
2987
3330
  SenderE164: sender.e164 ?? void 0,
@@ -2990,7 +3333,7 @@ function createWebOnMessageHandler(params) {
2990
3333
  OriginatingChannel: "whatsapp",
2991
3334
  OriginatingTo: conversationId
2992
3335
  };
2993
- updateLastRouteInBackground({
3336
+ const recordGroupRoute = () => updateLastRouteInBackground({
2994
3337
  cfg,
2995
3338
  backgroundTasks: params.backgroundTasks,
2996
3339
  storeAgentId: route.agentId,
@@ -3001,10 +3344,12 @@ function createWebOnMessageHandler(params) {
3001
3344
  ctx: metaCtx,
3002
3345
  warn: params.replyLogger.warn.bind(params.replyLogger)
3003
3346
  });
3347
+ if (configuredRoute.bindingResolution) recordAcceptedConfiguredGroupRoute = recordGroupRoute;
3348
+ else recordGroupRoute();
3004
3349
  let gating = await applyGroupGating({
3005
3350
  cfg,
3006
3351
  msg,
3007
- deferMissingMention: hasAudioBody && Boolean(msg.mediaPath),
3352
+ deferMissingMention: hasAudioBody && Boolean(msg.payload.media?.path),
3008
3353
  conversationId,
3009
3354
  groupHistoryKey,
3010
3355
  agentId: route.agentId,
@@ -3041,18 +3386,21 @@ function createWebOnMessageHandler(params) {
3041
3386
  });
3042
3387
  }
3043
3388
  if (!gating.shouldProcess) return;
3044
- } else if (!msg.sender?.e164 && !msg.senderE164 && peerId && peerId.startsWith("+")) {
3045
- const normalized = normalizeE164(peerId);
3046
- if (normalized) {
3047
- msg.sender = {
3048
- ...msg.sender,
3049
- e164: normalized
3050
- };
3051
- msg.senderE164 = normalized;
3389
+ }
3390
+ if (configuredRoute.bindingResolution) {
3391
+ const ensured = await ensureConfiguredBindingRouteReady({
3392
+ cfg,
3393
+ bindingResolution: configuredRoute.bindingResolution
3394
+ });
3395
+ if (!ensured.ok) {
3396
+ params.replyLogger.warn(`whatsapp: configured ACP binding unavailable for conversation ${configuredRoute.bindingResolution.record.conversation.conversationId}: ${ensured.error}`);
3397
+ await clearPreDispatchReaction();
3398
+ return;
3052
3399
  }
3053
3400
  }
3401
+ recordAcceptedConfiguredGroupRoute?.();
3054
3402
  await runAudioPreflightOnce();
3055
- if (await maybeBroadcastMessage({
3403
+ if (!configuredRoute.bindingResolution && await maybeBroadcastMessage({
3056
3404
  cfg,
3057
3405
  msg,
3058
3406
  peerId,
@@ -3237,10 +3585,11 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3237
3585
  })
3238
3586
  });
3239
3587
  const shouldDebounce = (msg) => {
3240
- if (msg.mediaPath || msg.mediaType) return false;
3241
- if (msg.location) return false;
3242
- if (msg.replyToId || msg.replyToBody) return false;
3243
- return !isControlCommandMessage(msg.body, cfg);
3588
+ const normalized = normalizeWebInboundMessage(msg);
3589
+ if (normalized.payload.media?.path || normalized.payload.media?.type) return false;
3590
+ if (normalized.payload.location) return false;
3591
+ if (normalized.quote?.id || normalized.quote?.body) return false;
3592
+ return !isControlCommandMessage(normalized.payload.body, cfg);
3244
3593
  };
3245
3594
  let connection;
3246
3595
  try {
@@ -3280,10 +3629,11 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3280
3629
  disconnectRetryAbortSignal: controller.getDisconnectRetryAbortSignal(),
3281
3630
  groupMetadataCache,
3282
3631
  onMessage: async (msg) => {
3632
+ const normalized = normalizeWebInboundMessage(msg);
3283
3633
  const inboundAt = Date.now();
3284
3634
  controller.noteInbound(inboundAt);
3285
3635
  statusController.noteInbound(inboundAt);
3286
- await onMessage(msg);
3636
+ await onMessage(normalized);
3287
3637
  },
3288
3638
  sock
3289
3639
  });