@openclaw/whatsapp 2026.7.2-beta.7 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{accounts-CSqLvCY5.js → accounts-BX2fZUyR.js} +1 -1
  2. package/dist/action-runtime-api.js +1 -1
  3. package/dist/{action-runtime-BzfDFPbH.js → action-runtime-pM-ExU0l.js} +2 -2
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/{agent-tools-login-DE9MU98T.js → agent-tools-login-BjRpD-JQ.js} +5 -4
  6. package/dist/api.js +69 -127
  7. package/dist/{approval-handler.runtime-DrA59QSA.js → approval-handler.runtime-CkMMvUjk.js} +4 -4
  8. package/dist/{approval-reactions-Cu66aYdq.js → approval-reactions-Jyr1JaAK.js} +29 -81
  9. package/dist/auth-presence.js +1 -1
  10. package/dist/{auth-store-DcEpIOmE.js → auth-store-CtpBqH5S.js} +82 -18
  11. package/dist/call-tool-api.js +1 -1
  12. package/dist/{channel-Dg62olhy.js → channel-DRMYRfZ8.js} +56 -160
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel-react-action-J_YtyMGX.js → channel-react-action-CstrR0CT.js} +14 -12
  15. package/dist/{channel.runtime-BP0xNmLi.js → channel.runtime-X3JC2gaq.js} +4 -4
  16. package/dist/channel.setup-DhBBhbQv.js +14 -0
  17. package/dist/{connection-controller-BOB2MVKY.js → connection-controller-CIYuBJrs.js} +2 -3
  18. package/dist/contract-api.js +2 -3
  19. package/dist/{creds-files-B1kSWtBg.js → creds-files-W6B3EMZ8.js} +17 -1
  20. package/dist/{directory-config-DalGrfGN.js → directory-config-P7_3U73Z.js} +3 -3
  21. package/dist/directory-contract-api.js +1 -1
  22. package/dist/doctor-contract-api.js +37 -2
  23. package/dist/{group-session-key-DVMZlO3x.js → group-session-key-DmFc_Cqi.js} +23 -4
  24. package/dist/light-runtime-api.js +2 -2
  25. package/dist/{login-CvY2Dhch.js → login-BkpKpndm.js} +4 -4
  26. package/dist/{login-qr-Bncpb6js.js → login-qr-1dpv6jjT.js} +4 -4
  27. package/dist/login-qr-runtime.js +1 -1
  28. package/dist/{monitor-CfkaUSL2.js → monitor-IzwQKizW.js} +545 -549
  29. package/dist/{question-reactions-CRsnyoMK.js → question-reactions-D3eAeB0i.js} +29 -78
  30. package/dist/runtime-api.js +9 -9
  31. package/dist/runtime-group-policy-DeSRFXEV.js +11 -0
  32. package/dist/{send-D37ddk22.js → send-CwvTU4RI.js} +298 -23
  33. package/dist/{send-api-7wk6eQyW.js → send-api-obOSm2cC.js} +126 -137
  34. package/dist/setup-entry.js +1 -8
  35. package/dist/{setup-finalize-BZucx24W.js → setup-finalize-n4K2I2gJ.js} +4 -4
  36. package/dist/setup-plugin-api.js +1 -1
  37. package/dist/{setup-surface-BkVcd769.js → setup-surface-Cwn3zyxK.js} +3 -3
  38. package/dist/{shared-CAxd15bp.js → shared-wg42NKFB.js} +13 -8
  39. package/dist/{socket-close-DridEuzz.js → socket-close-2T0_jZTN.js} +4 -10
  40. package/openclaw.plugin.json +4 -0
  41. package/package.json +4 -5
  42. package/skills/wacli/SKILL.md +8 -1
  43. package/dist/access-control-pZcxTmd5.js +0 -363
  44. package/dist/approval-resolver-DoeBBt22.js +0 -16
  45. package/dist/channel.setup-pMwNVkCA.js +0 -19
  46. package/dist/identity-CPrT5aEU.js +0 -76
  47. package/dist/legacy-state-migrations-api.js +0 -2
  48. package/dist/state-migrations-CAMTrs2U.js +0 -33
@@ -1,17 +1,15 @@
1
1
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-CugRyONv.js";
2
- import { l as resolveWhatsAppDocumentFileName } from "./send-D37ddk22.js";
2
+ import { _ as resolveWhatsAppDocumentFileName, f as mergeWhatsAppAcceptedSendError, m as rememberWhatsAppAcceptedSend, p as normalizeWhatsAppSendResult, u as combineWhatsAppSendResults } from "./send-CwvTU4RI.js";
3
3
  import "./normalize-C7Hkc_vE.js";
4
4
  import { c as toWhatsappJid, l as toWhatsappJidWithLid, r as jidToE164 } from "./targets-runtime-TMk0lGuy.js";
5
5
  import "./text-runtime-CaKLhowd.js";
6
- import { i as buildQuotedMessageOptions } from "./group-session-key-DVMZlO3x.js";
7
- import { c as resolveComparableIdentity } from "./identity-CPrT5aEU.js";
8
- import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
6
+ import { i as buildQuotedMessageOptions } from "./group-session-key-DmFc_Cqi.js";
7
+ import { k as resolveComparableIdentity } from "./auth-store-CtpBqH5S.js";
8
+ import { isRecord, normalizeLowercaseStringOrEmpty, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
9
9
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
10
- import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound";
11
- import { getImageMetadata, resizeToJpeg } from "openclaw/plugin-sdk/media-runtime";
12
10
  import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
13
- import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
14
- import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
11
+ import { getImageMetadata, resizeToJpeg } from "openclaw/plugin-sdk/media-runtime";
12
+ import { extractMessageContent, getContentType, normalizeMessageContent } from "baileys";
15
13
  //#region extensions/whatsapp/src/vcard.ts
16
14
  const ALLOWED_VCARD_KEYS = /* @__PURE__ */ new Set([
17
15
  "FN",
@@ -79,11 +77,11 @@ function normalizeVcardPhone(value) {
79
77
  * Falls back to WhatsApp's standard formats when Baileys omits the MIME.
80
78
  */
81
79
  function resolveInboundMediaMimetype(message) {
82
- const explicit = message.imageMessage?.mimetype ?? message.videoMessage?.mimetype ?? message.documentMessage?.mimetype ?? message.audioMessage?.mimetype ?? message.stickerMessage?.mimetype ?? void 0;
80
+ const explicit = message.imageMessage?.mimetype ?? message.videoMessage?.mimetype ?? message.ptvMessage?.mimetype ?? message.documentMessage?.mimetype ?? message.audioMessage?.mimetype ?? message.stickerMessage?.mimetype ?? void 0;
83
81
  if (explicit) return explicit;
84
82
  if (message.audioMessage) return "audio/ogg; codecs=opus";
85
83
  if (message.imageMessage) return "image/jpeg";
86
- if (message.videoMessage) return "video/mp4";
84
+ if (message.videoMessage || message.ptvMessage) return "video/mp4";
87
85
  if (message.stickerMessage) return "image/webp";
88
86
  }
89
87
  //#endregion
@@ -99,7 +97,7 @@ function getFutureProofInnerMessage(message) {
99
97
  }
100
98
  }
101
99
  }
102
- function buildMessageChain(message) {
100
+ function projectWhatsAppInboundMessage(message) {
103
101
  const chain = [];
104
102
  let current = normalizeMessageContent(message);
105
103
  while (current && chain.length < 4) {
@@ -108,8 +106,24 @@ function buildMessageChain(message) {
108
106
  }
109
107
  return chain;
110
108
  }
109
+ function isWhatsAppInboundMessageProjection(message) {
110
+ return Array.isArray(message);
111
+ }
112
+ function resolveWhatsAppInboundMessageProjection(message) {
113
+ return isWhatsAppInboundMessageProjection(message) ? message : projectWhatsAppInboundMessage(message);
114
+ }
115
+ function findMessageSection(rawMessage, sectionNames) {
116
+ const chain = projectWhatsAppInboundMessage(rawMessage);
117
+ for (const name of sectionNames) for (const message of chain) {
118
+ const value = message[name];
119
+ if (isRecord(value)) return {
120
+ name,
121
+ value
122
+ };
123
+ }
124
+ }
111
125
  function unwrapMessage(message) {
112
- return buildMessageChain(message).at(-1);
126
+ return resolveWhatsAppInboundMessageProjection(message).at(-1);
113
127
  }
114
128
  function extractContextInfoFromMessage(message) {
115
129
  const contentType = getContentType(message);
@@ -134,29 +148,30 @@ function extractContextInfoFromMessage(message) {
134
148
  }
135
149
  }
136
150
  function extractContextInfo(message) {
137
- for (const candidate of buildMessageChain(message)) {
151
+ for (const candidate of resolveWhatsAppInboundMessageProjection(message)) {
138
152
  const contextInfo = extractContextInfoFromMessage(candidate);
139
153
  if (contextInfo) return contextInfo;
140
154
  }
141
155
  }
142
- function extractMentionedJids(rawMessage) {
143
- const message = unwrapMessage(rawMessage);
144
- if (!message) return;
145
- const flattened = [
146
- message.extendedTextMessage?.contextInfo?.mentionedJid,
147
- message.imageMessage?.contextInfo?.mentionedJid,
148
- message.videoMessage?.contextInfo?.mentionedJid,
149
- message.documentMessage?.contextInfo?.mentionedJid,
150
- message.audioMessage?.contextInfo?.mentionedJid,
151
- message.stickerMessage?.contextInfo?.mentionedJid,
152
- message.buttonsResponseMessage?.contextInfo?.mentionedJid,
153
- message.listResponseMessage?.contextInfo?.mentionedJid
154
- ].flatMap((arr) => arr ?? []).filter(Boolean);
155
- if (flattened.length === 0) return;
156
- return uniqueStrings(flattened);
157
- }
158
- function extractText(rawMessage) {
159
- const message = unwrapMessage(rawMessage);
156
+ function extractMentionedJids(message) {
157
+ const mentionedJids = extractContextInfo(message)?.mentionedJid?.filter(Boolean);
158
+ if (!mentionedJids?.length) return;
159
+ return uniqueStrings(mentionedJids);
160
+ }
161
+ function extractNativeFlowResponseText(response) {
162
+ const paramsJson = response?.nativeFlowResponseMessage?.paramsJson;
163
+ if (!paramsJson) return;
164
+ try {
165
+ const params = JSON.parse(paramsJson);
166
+ if (!isRecord(params)) return;
167
+ return [params.title, params.id].find((value) => typeof value === "string" && Boolean(value.trim()));
168
+ } catch {
169
+ return;
170
+ }
171
+ }
172
+ function extractText(source) {
173
+ const projection = resolveWhatsAppInboundMessageProjection(source);
174
+ const message = unwrapMessage(projection);
160
175
  if (!message) return;
161
176
  const extracted = extractMessageContent(message);
162
177
  const candidates = [message, extracted && extracted !== message ? extracted : void 0];
@@ -165,14 +180,30 @@ function extractText(rawMessage) {
165
180
  if (typeof candidate.conversation === "string" && candidate.conversation.trim()) return candidate.conversation.trim();
166
181
  const extended = candidate.extendedTextMessage?.text;
167
182
  if (extended?.trim()) return extended.trim();
168
- const caption = candidate.imageMessage?.caption ?? candidate.videoMessage?.caption ?? candidate.documentMessage?.caption;
183
+ const caption = candidate.imageMessage?.caption ?? candidate.videoMessage?.caption ?? candidate.ptvMessage?.caption ?? candidate.documentMessage?.caption;
169
184
  if (caption?.trim()) return caption.trim();
185
+ const interactiveSelection = [
186
+ candidate.buttonsResponseMessage?.selectedDisplayText,
187
+ candidate.buttonsResponseMessage?.selectedButtonId,
188
+ candidate.listResponseMessage?.title,
189
+ candidate.listResponseMessage?.singleSelectReply?.selectedRowId,
190
+ candidate.templateButtonReplyMessage?.selectedDisplayText,
191
+ candidate.templateButtonReplyMessage?.selectedId,
192
+ candidate.interactiveResponseMessage?.body?.text,
193
+ extractNativeFlowResponseText(candidate.interactiveResponseMessage)
194
+ ].find((value) => Boolean(value?.trim()));
195
+ if (interactiveSelection) return interactiveSelection.trim();
196
+ const poll = candidate.pollCreationMessage ?? candidate.pollCreationMessageV2 ?? candidate.pollCreationMessageV3 ?? candidate.pollCreationMessageV5;
197
+ if (poll) {
198
+ const pollText = [poll.name?.trim(), ...(poll.options ?? []).map((option) => option.optionName?.trim()).filter((option) => Boolean(option)).map((option) => `- ${option}`)].filter(Boolean).join("\n");
199
+ if (pollText) return pollText;
200
+ }
170
201
  }
171
- const contactPlaceholder = extractContactPlaceholder(message) ?? (extracted && extracted !== message ? extractContactPlaceholder(extracted) : void 0);
202
+ const contactPlaceholder = extractContactPlaceholder(projection) ?? (extracted && extracted !== message ? extractContactPlaceholder(extracted) : void 0);
172
203
  if (contactPlaceholder) return contactPlaceholder;
173
204
  }
174
- function extractExternalAdReplyContext(rawMessage) {
175
- const message = unwrapMessage(rawMessage);
205
+ function extractExternalAdReplyContext(source) {
206
+ const message = unwrapMessage(source);
176
207
  const adReply = message?.imageMessage?.contextInfo?.externalAdReply ?? message?.videoMessage?.contextInfo?.externalAdReply;
177
208
  if (!adReply) return;
178
209
  const title = adReply.title?.trim() || void 0;
@@ -184,24 +215,24 @@ function extractExternalAdReplyContext(rawMessage) {
184
215
  body
185
216
  } : void 0;
186
217
  }
187
- function extractMediaKind(rawMessage) {
188
- const message = unwrapMessage(rawMessage);
218
+ function extractMediaKind(source) {
219
+ const message = unwrapMessage(source);
189
220
  if (!message) return;
190
221
  if (message.imageMessage) return "image";
191
- if (message.videoMessage) return "video";
222
+ if (message.videoMessage || message.ptvMessage) return "video";
192
223
  if (message.audioMessage) return "audio";
193
224
  if (message.documentMessage) return "document";
194
225
  if (message.stickerMessage) return "sticker";
195
226
  }
196
- function extractContactPlaceholder(rawMessage) {
197
- const contactContext = extractContactContext(rawMessage);
227
+ function extractContactPlaceholder(source) {
228
+ const contactContext = extractContactContext(source);
198
229
  if (!contactContext) return;
199
230
  if (contactContext.kind === "contact") return "<contact>";
200
231
  const suffix = contactContext.total === 1 ? "contact" : "contacts";
201
232
  return `<contacts: ${contactContext.total} ${suffix}>`;
202
233
  }
203
- function extractContactContext(rawMessage) {
204
- const message = unwrapMessage(rawMessage);
234
+ function extractContactContext(source) {
235
+ const message = unwrapMessage(source);
205
236
  if (!message) return;
206
237
  const contact = message.contactMessage ?? void 0;
207
238
  if (contact) {
@@ -237,8 +268,8 @@ function describeContact(input) {
237
268
  phones: parsed.phones
238
269
  };
239
270
  }
240
- function extractLocationData(rawMessage) {
241
- const message = unwrapMessage(rawMessage);
271
+ function extractLocationData(source) {
272
+ const message = unwrapMessage(source);
242
273
  if (!message) return null;
243
274
  const live = message.liveLocationMessage ?? void 0;
244
275
  if (live) {
@@ -281,10 +312,11 @@ function extractLocationData(rawMessage) {
281
312
  }
282
313
  return null;
283
314
  }
284
- function describeReplyContext(rawMessage) {
285
- const message = unwrapMessage(rawMessage);
315
+ function describeReplyContext(source) {
316
+ const projection = resolveWhatsAppInboundMessageProjection(source);
317
+ const message = unwrapMessage(projection);
286
318
  if (!message) return null;
287
- const contextInfo = extractContextInfo(message);
319
+ const contextInfo = extractContextInfo(projection.length === 1 && projection[0] === message ? projection : projectWhatsAppInboundMessage(message));
288
320
  const quoted = normalizeMessageContent(contextInfo?.quotedMessage);
289
321
  if (!quoted && !contextInfo?.stanzaId) return null;
290
322
  const senderJid = contextInfo?.participant ?? void 0;
@@ -297,10 +329,11 @@ function describeReplyContext(rawMessage) {
297
329
  body: "[quoted message unavailable]",
298
330
  sender
299
331
  };
300
- const location = extractLocationData(quoted);
332
+ const quotedProjection = projectWhatsAppInboundMessage(quoted);
333
+ const location = extractLocationData(quotedProjection);
301
334
  const locationText = location ? formatLocationText(location) : void 0;
302
- const body = [extractText(quoted), locationText].filter(Boolean).join("\n").trim();
303
- const mediaKind = extractMediaKind(quoted);
335
+ const body = [extractText(quotedProjection), locationText].filter(Boolean).join("\n").trim();
336
+ const mediaKind = extractMediaKind(quotedProjection);
304
337
  const media = mediaKind ? {
305
338
  kind: mediaKind,
306
339
  contentType: resolveInboundMediaMimetype(quoted)
@@ -328,12 +361,13 @@ function hasInteractiveResponseContent(message) {
328
361
  * `messages.upsert` stream as real messages but should not trigger pairing
329
362
  * access-control side effects.
330
363
  */
331
- function hasInboundUserContent(rawMessage) {
332
- if (!rawMessage) return false;
333
- if (extractText(rawMessage)) return true;
334
- if (extractMediaKind(rawMessage)) return true;
335
- if (extractLocationData(rawMessage)) return true;
336
- for (const candidate of buildMessageChain(rawMessage)) if (hasInteractiveResponseContent(candidate)) return true;
364
+ function hasInboundUserContent(source) {
365
+ const projection = resolveWhatsAppInboundMessageProjection(source);
366
+ if (projection.length === 0) return false;
367
+ if (extractText(projection)) return true;
368
+ if (extractMediaKind(projection)) return true;
369
+ if (extractLocationData(projection)) return true;
370
+ for (const candidate of projection) if (hasInteractiveResponseContent(candidate)) return true;
337
371
  return false;
338
372
  }
339
373
  //#endregion
@@ -542,75 +576,7 @@ function addWhatsAppOutboundMentionsToContent(content, mentionedJids) {
542
576
  } : content;
543
577
  }
544
578
  //#endregion
545
- //#region extensions/whatsapp/src/inbound/send-result.ts
546
- function resolveWhatsAppReceiptKind(kind) {
547
- if (kind === "media" || kind === "text") return kind;
548
- return "unknown";
549
- }
550
- function toReceiptSourceResult(key) {
551
- return {
552
- channel: "whatsapp",
553
- messageId: key.id,
554
- ...key.remoteJid ? { toJid: key.remoteJid } : {},
555
- meta: {
556
- fromMe: key.fromMe,
557
- participant: key.participant
558
- }
559
- };
560
- }
561
- function createWhatsAppSendReceipt(kind, keys) {
562
- return createMessageReceiptFromOutboundResults({
563
- kind: resolveWhatsAppReceiptKind(kind),
564
- results: keys.map(toReceiptSourceResult)
565
- });
566
- }
567
- function normalizeKey(key) {
568
- const id = typeof key?.id === "string" ? key.id.trim() : "";
569
- if (!id) return;
570
- return {
571
- id,
572
- remoteJid: key?.remoteJid,
573
- fromMe: key?.fromMe,
574
- participant: key?.participant
575
- };
576
- }
577
- function normalizeWhatsAppSendResult(result, kind) {
578
- const key = normalizeKey(result?.key);
579
- return {
580
- kind,
581
- messageId: key?.id ?? "unknown",
582
- receipt: createWhatsAppSendReceipt(kind, key ? [key] : []),
583
- keys: key ? [key] : [],
584
- providerAccepted: Boolean(key)
585
- };
586
- }
587
- function combineWhatsAppSendResults(kind, results) {
588
- const messageIds = uniqueStrings(results.flatMap(listWhatsAppSendResultMessageIds));
589
- const keys = results.flatMap((result) => result.keys);
590
- return {
591
- kind,
592
- messageId: messageIds[0] ?? "unknown",
593
- receipt: createWhatsAppSendReceipt(kind, keys),
594
- keys,
595
- providerAccepted: results.some((result) => result.providerAccepted)
596
- };
597
- }
598
- function listWhatsAppSendResultMessageIds(result) {
599
- const receiptIds = result.receipt ? listMessageReceiptPlatformIds(result.receipt) : [];
600
- if (receiptIds.length > 0) return receiptIds;
601
- const keyIds = normalizeStringEntries(result.keys.map((key) => key.id));
602
- if (keyIds.length > 0) return uniqueStrings(keyIds);
603
- return [];
604
- }
605
- //#endregion
606
579
  //#region extensions/whatsapp/src/inbound/send-api.ts
607
- function recordWhatsAppOutbound(accountId) {
608
- recordChannelActivity({
609
- channel: "whatsapp",
610
- accountId,
611
- direction: "outbound"
612
- });
613
- }
614
580
  function supportsForcedDocumentMediaType(mediaType) {
615
581
  return mediaType.startsWith("image/") || mediaType.startsWith("video/");
616
582
  }
@@ -623,11 +589,30 @@ function createWebSendApi(params) {
623
589
  text,
624
590
  mentionedJids: []
625
591
  };
592
+ const runAcceptedSend = async (kind, accountId, send) => {
593
+ const results = [];
594
+ try {
595
+ await send((result, sendKind) => {
596
+ rememberWhatsAppAcceptedSend({
597
+ accountId,
598
+ result: normalizeWhatsAppSendResult(result, sendKind),
599
+ results
600
+ });
601
+ });
602
+ return combineWhatsAppSendResults(kind, results);
603
+ } catch (error) {
604
+ throw mergeWhatsAppAcceptedSendError({
605
+ error,
606
+ kind,
607
+ results
608
+ });
609
+ }
610
+ };
626
611
  const sendStructuredMessage = async (to, content, kind) => {
627
612
  const jid = resolveOutboundJid(to);
628
- const result = await params.sock.sendMessage(jid, content);
629
- recordWhatsAppOutbound(params.defaultAccountId);
630
- return normalizeWhatsAppSendResult(result, kind);
613
+ return await runAcceptedSend(kind, params.defaultAccountId, async (capture) => {
614
+ capture(await params.sock.sendMessage(jid, content), kind);
615
+ });
631
616
  };
632
617
  return {
633
618
  sendMessage: async (to, text, mediaBuffer, mediaTypeInput, sendOptions) => {
@@ -683,18 +668,22 @@ function createWebSendApi(params) {
683
668
  remoteJid: sendOptions?.quotedMessageKey?.remoteJid,
684
669
  fromMe: sendOptions?.quotedMessageKey?.fromMe,
685
670
  participant: sendOptions?.quotedMessageKey?.participant,
671
+ destinationJid: jid,
672
+ requestedJid: toWhatsappJid(to),
673
+ lookupTargetJid: sendOptions?.quotedMessageKey?.lookupTargetJid,
686
674
  messageText: sendOptions?.quotedMessageKey?.messageText,
687
675
  media: sendOptions?.quotedMessageKey?.media
688
676
  });
689
- const results = [normalizeWhatsAppSendResult(quotedOpts ? await params.sock.sendMessage(jid, payload, quotedOpts) : await params.sock.sendMessage(jid, payload), mediaBuffer ? "media" : "text")];
690
- if (shouldSendAudioText) {
691
- const resolvedAudioText = await resolveMentions(jid, text);
692
- const textPayload = addWhatsAppOutboundMentionsToContent({ text: resolvedAudioText.text }, resolvedAudioText.mentionedJids);
693
- const textResult = quotedOpts ? await params.sock.sendMessage(jid, textPayload, quotedOpts) : await params.sock.sendMessage(jid, textPayload);
694
- results.push(normalizeWhatsAppSendResult(textResult, "text"));
695
- }
696
- recordWhatsAppOutbound(sendOptions?.accountId ?? params.defaultAccountId);
697
- return combineWhatsAppSendResults(mediaBuffer ? "media" : "text", results);
677
+ const kind = mediaBuffer ? "media" : "text";
678
+ const accountId = sendOptions?.accountId ?? params.defaultAccountId;
679
+ return await runAcceptedSend(kind, accountId, async (capture) => {
680
+ const sendPayload = async (content) => quotedOpts ? await params.sock.sendMessage(jid, content, quotedOpts) : await params.sock.sendMessage(jid, content);
681
+ capture(await sendPayload(payload), kind);
682
+ if (shouldSendAudioText) {
683
+ const resolvedAudioText = await resolveMentions(jid, text);
684
+ capture(await sendPayload(addWhatsAppOutboundMentionsToContent({ text: resolvedAudioText.text }, resolvedAudioText.mentionedJids)), "text");
685
+ }
686
+ });
698
687
  },
699
688
  sendPoll: async (to, poll) => {
700
689
  return await sendStructuredMessage(to, { poll: {
@@ -746,4 +735,4 @@ function createWebSendApi(params) {
746
735
  };
747
736
  }
748
737
  //#endregion
749
- export { extractMediaKind as _, mayContainWhatsAppOutboundMention as a, hasInboundUserContent as b, DisconnectReason as c, normalizeMessageContent$1 as d, describeReplyContext as f, extractLocationData as g, extractExternalAdReplyContext as h, addWhatsAppOutboundMentionsToContent as i, downloadMediaMessage as l, extractContextInfo as m, listWhatsAppSendResultMessageIds as n, resolveWhatsAppOutboundMentions as o, extractContactContext as p, normalizeWhatsAppSendResult as r, addWhatsAppImagePreviewFields as s, createWebSendApi as t, isJidGroup as u, extractMentionedJids as v, resolveInboundMediaMimetype as x, extractText as y };
738
+ export { resolveInboundMediaMimetype as _, addWhatsAppImagePreviewFields as a, extractContextInfo as c, extractMediaKind as d, extractMentionedJids as f, projectWhatsAppInboundMessage as g, hasInboundUserContent as h, resolveWhatsAppOutboundMentions as i, extractExternalAdReplyContext as l, findMessageSection as m, addWhatsAppOutboundMentionsToContent as n, describeReplyContext as o, extractText as p, mayContainWhatsAppOutboundMention as r, extractContactContext as s, createWebSendApi as t, extractLocationData as u };
@@ -2,18 +2,11 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
2
2
  //#region extensions/whatsapp/setup-entry.ts
3
3
  var setup_entry_default = defineBundledChannelSetupEntry({
4
4
  importMetaUrl: import.meta.url,
5
- features: {
6
- legacyStateMigrations: true,
7
- legacySessionSurfaces: true
8
- },
5
+ features: { legacySessionSurfaces: true },
9
6
  plugin: {
10
7
  specifier: "./setup-plugin-api.js",
11
8
  exportName: "whatsappSetupPlugin"
12
9
  },
13
- legacyStateMigrations: {
14
- specifier: "./legacy-state-migrations-api.js",
15
- exportName: "detectWhatsAppLegacyStateMigrations"
16
- },
17
10
  legacySessionSurface: {
18
11
  specifier: "./legacy-session-surface-api.js",
19
12
  exportName: "whatsappLegacySessionSurface"
@@ -1,8 +1,8 @@
1
- import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-CSqLvCY5.js";
1
+ import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-BX2fZUyR.js";
2
2
  import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
3
- import { r as hasWebCredsSync } from "./creds-files-B1kSWtBg.js";
3
+ import { r as hasWebCredsSync } from "./creds-files-W6B3EMZ8.js";
4
4
  import { a as normalizeWhatsAppAllowFromEntries, o as normalizeWhatsAppAllowFromEntry } from "./normalize-target-CugRyONv.js";
5
- import { r as whatsappSetupAdapter } from "./shared-CAxd15bp.js";
5
+ import { r as whatsappSetupAdapter } from "./shared-wg42NKFB.js";
6
6
  import { DEFAULT_ACCOUNT_ID, createSetupTranslator, splitSetupEntries } from "openclaw/plugin-sdk/setup";
7
7
  import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
8
8
  //#region extensions/whatsapp/src/setup-finalize.ts
@@ -295,7 +295,7 @@ async function finalizeWhatsAppSetup(params) {
295
295
  };
296
296
  let loginWeb;
297
297
  try {
298
- ({loginWeb} = await import("./login-CvY2Dhch.js").then((n) => n.n));
298
+ ({loginWeb} = await import("./login-BkpKpndm.js").then((n) => n.n));
299
299
  } catch (error) {
300
300
  await reportLoginFailure(error);
301
301
  }
@@ -1,2 +1,2 @@
1
- import { t as whatsappSetupPlugin } from "./channel.setup-pMwNVkCA.js";
1
+ import { t as whatsappSetupPlugin } from "./channel.setup-DhBBhbQv.js";
2
2
  export { whatsappSetupPlugin };
@@ -1,6 +1,6 @@
1
- import { o as resolveWhatsAppAuthDir } from "./accounts-CSqLvCY5.js";
1
+ import { o as resolveWhatsAppAuthDir } from "./accounts-BX2fZUyR.js";
2
2
  import { t as listAccountIds } from "./account-ids-CB5SOWjc.js";
3
- import { a as formatWhatsAppWebAuthStatusState, h as readWebAuthState } from "./auth-store-DcEpIOmE.js";
3
+ import { a as formatWhatsAppWebAuthStatusState, h as readWebAuthState } from "./auth-store-CtpBqH5S.js";
4
4
  import { DEFAULT_ACCOUNT_ID, createSetupTranslator, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
5
5
  //#region extensions/whatsapp/src/setup-surface.ts
6
6
  const t = createSetupTranslator();
@@ -36,7 +36,7 @@ const whatsappSetupWizard = {
36
36
  },
37
37
  resolveShouldPromptAccountIds: ({ shouldPromptAccountIds }) => shouldPromptAccountIds,
38
38
  credentials: [],
39
- finalize: async (params) => await (await import("./setup-finalize-BZucx24W.js")).finalizeWhatsAppSetup(params),
39
+ finalize: async (params) => await (await import("./setup-finalize-n4K2I2gJ.js")).finalizeWhatsAppSetup(params),
40
40
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
41
41
  onAccountRecorded: (accountId, options) => {
42
42
  options?.onAccountId?.(channel, accountId);
@@ -1,4 +1,4 @@
1
- import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-CSqLvCY5.js";
1
+ import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-BX2fZUyR.js";
2
2
  import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./account-ids-CB5SOWjc.js";
3
3
  import { a as normalizeWhatsAppAllowFromEntries } from "./normalize-target-CugRyONv.js";
4
4
  import { t as WhatsAppChannelConfigSchema } from "./config-schema-BQP5YCeK.js";
@@ -12,7 +12,7 @@ import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
12
12
  import { createChannelPluginBase } from "openclaw/plugin-sdk/core";
13
13
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
14
14
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
15
- import { buildChannelGroupsScopeTree, collectOpenGroupPolicyRouteAllowlistWarnings, createAllowlistProviderGroupPolicyWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
15
+ import { buildChannelGroupsScopeTree, collectOpenGroupPolicyRouteAllowlistWarnings, createAllowlistProviderGroupPolicyWarningCollector, createConditionalWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
16
16
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
17
17
  import { createPatchedAccountSetupAdapter } from "openclaw/plugin-sdk/setup";
18
18
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
@@ -20,14 +20,14 @@ import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createSco
20
20
  import { createDelegatedSetupWizardProxy } from "openclaw/plugin-sdk/setup-runtime";
21
21
  import { readChannelAllowFromStore } from "openclaw/plugin-sdk/channel-pairing";
22
22
  //#region extensions/whatsapp/src/channel-runtime-loader.ts
23
- const loadWhatsAppAuthStore = createLazyRuntimeModule(() => import("./auth-store-DcEpIOmE.js").then((n) => n.i));
23
+ const loadWhatsAppAuthStore = createLazyRuntimeModule(() => import("./auth-store-CtpBqH5S.js").then((n) => n.i));
24
24
  async function readWhatsAppAccountLinkState(authDir) {
25
25
  const state = await (await loadWhatsAppAuthStore()).readWebAuthState(authDir);
26
26
  return state === "unstable" ? "unknown" : state;
27
27
  }
28
28
  const loadWhatsAppChannelRuntime = createLazyRuntimeModule(async () => {
29
29
  await loadWhatsAppAuthStore();
30
- return await import("./channel.runtime-BP0xNmLi.js");
30
+ return await import("./channel.runtime-X3JC2gaq.js");
31
31
  });
32
32
  //#endregion
33
33
  //#region extensions/whatsapp/src/config-accessors.ts
@@ -115,7 +115,7 @@ async function applyWhatsAppSecurityConfigFixes(params) {
115
115
  //#region extensions/whatsapp/src/shared.ts
116
116
  const WHATSAPP_CHANNEL = "whatsapp";
117
117
  async function loadWhatsAppSetupSurface() {
118
- return await import("./setup-surface-BkVcd769.js");
118
+ return await import("./setup-surface-Cwn3zyxK.js");
119
119
  }
120
120
  const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
121
121
  const whatsappConfigAdapter = createScopedChannelConfigAdapter({
@@ -209,6 +209,12 @@ function createWhatsAppPluginBase(params) {
209
209
  }
210
210
  })
211
211
  });
212
+ const collectWhatsAppOpenGroupFindings = createConditionalWarningCollector.findings({
213
+ collectWarnings: collectWhatsAppSecurityWarnings,
214
+ checkId: "channels.whatsapp.groups.open",
215
+ severity: "critical",
216
+ title: "WhatsApp security warning"
217
+ });
212
218
  const base = createChannelPluginBase({
213
219
  id: WHATSAPP_CHANNEL,
214
220
  meta: {
@@ -270,11 +276,10 @@ function createWhatsAppPluginBase(params) {
270
276
  security: {
271
277
  applyConfigFixes: applyWhatsAppSecurityConfigFixes,
272
278
  resolveDmPolicy: whatsappResolveDmPolicy,
273
- collectWarnings: collectWhatsAppSecurityWarnings
279
+ collectWarnings: collectWhatsAppOpenGroupFindings
274
280
  },
275
281
  doctor: whatsappDoctor,
276
- setup: params.setup,
277
- ...params.setupContract ? { setupContract: params.setupContract } : {},
282
+ setupContract: params.setupContract,
278
283
  groups: params.groups
279
284
  });
280
285
  return {
@@ -1,8 +1,9 @@
1
- import { o as resolveWebCredsBackupPath, s as resolveWebCredsPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-B1kSWtBg.js";
2
- import { E as writeWebCredsRawAtomically, S as enqueueCredsSave, T as writeCredsJsonAtomically, b as restoreCredsFromBackupIfNeeded, u as readCredsJsonRaw, w as waitForCredsSaveQueueWithTimeout, y as resolveDefaultWebAuthDir } from "./auth-store-DcEpIOmE.js";
1
+ import { c as resolveWebCredsPath, s as resolveWebCredsBackupPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-W6B3EMZ8.js";
2
+ import { A as enqueueCredsSave, M as waitForCredsSaveQueueWithTimeout, N as writeCredsJsonAtomically, P as writeWebCredsRawAtomically, b as restoreCredsFromBackupIfNeeded, u as readCredsJsonRaw, y as resolveDefaultWebAuthDir } from "./auth-store-CtpBqH5S.js";
3
3
  import { n as getStatusCode } from "./session-errors-JuazczrA.js";
4
4
  import { a as makeWASocket, i as makeCacheableSignalKeyStore, n as createBaileysSignalRepository, r as fetchLatestBaileysVersion, s as useMultiFileAuthState } from "./session.runtime-Nsqaehbd.js";
5
5
  import { VERSION, formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
6
+ import { toErrorObject } from "openclaw/plugin-sdk/error-runtime";
6
7
  import { danger, getChildLogger, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
7
8
  import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
8
9
  import { ensureDir, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -541,7 +542,7 @@ async function waitForWaConnection(sock, options = { timeout: "none" }) {
541
542
  }
542
543
  if (update.connection === "close") {
543
544
  cleanup();
544
- reject(toLintErrorObject(update.lastDisconnect?.error ?? update.lastDisconnect ?? /* @__PURE__ */ new Error("Connection closed"), "Non-Error rejection"));
545
+ reject(toErrorObject(update.lastDisconnect?.error ?? update.lastDisconnect ?? /* @__PURE__ */ new Error("Connection closed"), "Non-Error rejection"));
545
546
  }
546
547
  };
547
548
  sock.ev.on("connection.update", handler);
@@ -558,13 +559,6 @@ async function waitForWaConnection(sock, options = { timeout: "none" }) {
558
559
  function newConnectionId() {
559
560
  return randomUUID();
560
561
  }
561
- function toLintErrorObject(value, fallbackMessage) {
562
- if (value instanceof Error) return value;
563
- if (typeof value === "string") return new Error(value);
564
- const error = new Error(fallbackMessage, { cause: value });
565
- if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
566
- return error;
567
- }
568
562
  function createConnectionTimeoutError(timeoutMs) {
569
563
  const error = /* @__PURE__ */ new Error(`WhatsApp connection timed out after ${timeoutMs}ms`);
570
564
  Object.assign(error, { output: { statusCode: 408 } });
@@ -1,5 +1,9 @@
1
1
  {
2
2
  "id": "whatsapp",
3
+ "doctorContract": {
4
+ "configRepair": true,
5
+ "stateMigrations": true
6
+ },
3
7
  "name": "WhatsApp",
4
8
  "description": "OpenClaw WhatsApp channel plugin for WhatsApp Web chats.",
5
9
  "icon": "https://cdn.simpleicons.org/whatsapp",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/whatsapp",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.8.1-beta.3",
4
4
  "description": "OpenClaw WhatsApp channel plugin for WhatsApp Web chats.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,7 +13,7 @@
13
13
  "typebox": "1.3.6"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.7.2-beta.7"
16
+ "openclaw": ">=2026.8.1-beta.3"
17
17
  },
18
18
  "peerDependenciesMeta": {
19
19
  "openclaw": {
@@ -27,7 +27,6 @@
27
27
  "setupEntry": "./dist/setup-entry.js",
28
28
  "setupFeatures": {
29
29
  "configPromotion": true,
30
- "legacyStateMigrations": true,
31
30
  "legacySessionSurfaces": true
32
31
  },
33
32
  "channel": {
@@ -66,10 +65,10 @@
66
65
  "minHostVersion": ">=2026.4.25"
67
66
  },
68
67
  "compat": {
69
- "pluginApi": ">=2026.7.2-beta.7"
68
+ "pluginApi": ">=2026.8.1-beta.3"
70
69
  },
71
70
  "build": {
72
- "openclawVersion": "2026.7.2-beta.7"
71
+ "openclawVersion": "2026.8.1-beta.3"
73
72
  },
74
73
  "release": {
75
74
  "publishToClawHub": true,
@@ -20,7 +20,7 @@ metadata:
20
20
  {
21
21
  "id": "go",
22
22
  "kind": "go",
23
- "module": "github.com/steipete/wacli/cmd/wacli@latest",
23
+ "module": "github.com/openclaw/wacli/cmd/wacli@latest",
24
24
  "bins": ["wacli"],
25
25
  "label": "Install wacli (go)",
26
26
  },
@@ -53,6 +53,12 @@ Find chats + messages
53
53
  - `wacli messages search "query" --limit 20 --chat <jid>`
54
54
  - `wacli messages search "invoice" --after 2025-01-01 --before 2025-12-31`
55
55
 
56
+ Inspect / verify messages
57
+
58
+ - `wacli messages show --chat <jid> --id <message_id> --json --full`
59
+ - `messages show` requires both `--chat` and `--id`; do not pass the message ID as a positional argument.
60
+ - After sending, verify the stored message when exact formatting matters, especially multiline text.
61
+
56
62
  History backfill
57
63
 
58
64
  - `wacli history backfill --chat <jid> --requests 2 --count 50`
@@ -62,6 +68,7 @@ Send
62
68
  - Text: `wacli send text --to "+14155551212" --message "Hello! Are you free at 3pm?"`
63
69
  - Group: `wacli send text --to "1234567890-123456789@g.us" --message "Running 5 min late."`
64
70
  - File: `wacli send file --to "+14155551212" --file /path/agenda.pdf --caption "Agenda"`
71
+ - Multiline: `--message` is literal by default; pass `--message-escapes` to interpret `\n`, `\r`, `\t`.
65
72
 
66
73
  Notes
67
74