@openclaw/whatsapp 2026.7.2-beta.7 → 2026.8.1-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 (48) hide show
  1. package/dist/{accounts-CSqLvCY5.js → accounts-BX2fZUyR.js} +1 -1
  2. package/dist/{action-runtime-BzfDFPbH.js → action-runtime-B4e3XFOY.js} +2 -2
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/{agent-tools-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-fgGZhALv.js} +4 -4
  8. package/dist/{approval-reactions-Cu66aYdq.js → approval-reactions-CmtJCxe_.js} +25 -73
  9. package/dist/auth-presence.js +1 -1
  10. package/dist/{auth-store-DcEpIOmE.js → auth-store-B_DQIdzp.js} +79 -12
  11. package/dist/call-tool-api.js +1 -1
  12. package/dist/{channel-Dg62olhy.js → channel-DJVQHz_V.js} +56 -160
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel-react-action-J_YtyMGX.js → channel-react-action-d6OacTNj.js} +3 -3
  15. package/dist/{channel.runtime-BP0xNmLi.js → channel.runtime-RxuNGZ7w.js} +4 -4
  16. package/dist/channel.setup-Cmvd967u.js +14 -0
  17. package/dist/{connection-controller-BOB2MVKY.js → connection-controller-CUyZgGpf.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-Pc4f07wB.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-D6_rfxTZ.js} +4 -4
  26. package/dist/{login-qr-Bncpb6js.js → login-qr-B0VyVSqQ.js} +4 -4
  27. package/dist/login-qr-runtime.js +1 -1
  28. package/dist/{monitor-CfkaUSL2.js → monitor-BCe5Mm-1.js} +574 -163
  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-api-7wk6eQyW.js → send-api-DimZST3E.js} +86 -107
  33. package/dist/{send-D37ddk22.js → send-prK8F7Vj.js} +277 -16
  34. package/dist/setup-entry.js +1 -8
  35. package/dist/{setup-finalize-BZucx24W.js → setup-finalize-D3n71Uyx.js} +4 -4
  36. package/dist/setup-plugin-api.js +1 -1
  37. package/dist/{setup-surface-BkVcd769.js → setup-surface-BCZszf3l.js} +3 -3
  38. package/dist/{shared-CAxd15bp.js → shared-Dp9M3ZPX.js} +5 -6
  39. package/dist/{socket-close-DridEuzz.js → socket-close-09FojDO2.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,4 +1,4 @@
1
- import { a as resolveWhatsAppAccount, c as resolveMergedWhatsAppAccountConfig, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CSqLvCY5.js";
1
+ import { a as resolveWhatsAppAccount, c as resolveMergedWhatsAppAccountConfig, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BX2fZUyR.js";
2
2
  import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
3
3
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-CugRyONv.js";
4
4
  import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-DG0tuYuw.js";
@@ -6,7 +6,7 @@ import "./normalize-C7Hkc_vE.js";
6
6
  import { a as markdownToWhatsAppChunks, c as toWhatsappJid } from "./targets-runtime-TMk0lGuy.js";
7
7
  import { a as stripToolCallXmlTags, i as sanitizeAssistantVisibleTextWithProfile, r as sanitizeAssistantVisibleText } from "./text-runtime-CaKLhowd.js";
8
8
  import path from "node:path";
9
- import { normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { normalizeStringEntries, normalizeUniqueStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
10
10
  import { resolveReactionLevel } from "openclaw/plugin-sdk/status-helpers";
11
11
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
12
12
  import { generateSecureUuid } from "openclaw/plugin-sdk/core";
@@ -17,9 +17,12 @@ import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
17
17
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
18
18
  import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
19
19
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
20
- import { createSubsystemLogger, getChildLogger as getChildLogger$1 } from "openclaw/plugin-sdk/runtime-env";
20
+ import { createSubsystemLogger as createSubsystemLogger$1, getChildLogger as getChildLogger$1 } from "openclaw/plugin-sdk/runtime-env";
21
21
  import { extensionForMime, mediaKindFromMime, mimeTypeFromFilePath, normalizeMimeType } from "openclaw/plugin-sdk/media-mime";
22
- import { sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
22
+ import { AsyncLocalStorage } from "node:async_hooks";
23
+ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
24
+ import { createChannelPartialDeliveryError, isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
25
+ import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
23
26
  import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, transcodeAudioBufferToOpus } from "openclaw/plugin-sdk/media-runtime";
24
27
  import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
25
28
  //#region extensions/whatsapp/src/reaction-level.ts
@@ -54,6 +57,245 @@ function stripAsciiControlCharacters(value) {
54
57
  return stripped;
55
58
  }
56
59
  //#endregion
60
+ //#region extensions/whatsapp/src/inbound/send-result.ts
61
+ const activityAccountedWhatsAppSendResults = /* @__PURE__ */ new WeakSet();
62
+ const logicalWhatsAppDeliveryActivity = new AsyncLocalStorage();
63
+ /** Keep all separately produced platform sends inside one logical reply accounting scope. */
64
+ async function withWhatsAppLogicalDeliveryActivity(run) {
65
+ if (logicalWhatsAppDeliveryActivity.getStore()) return await run();
66
+ return await logicalWhatsAppDeliveryActivity.run({ accountedAccountIds: /* @__PURE__ */ new Set() }, run);
67
+ }
68
+ function resolveWhatsAppReceiptKind(kind) {
69
+ if (kind === "media" || kind === "text") return kind;
70
+ return "unknown";
71
+ }
72
+ function toReceiptSourceResult(key) {
73
+ return {
74
+ channel: "whatsapp",
75
+ messageId: key.id,
76
+ ...key.remoteJid ? { toJid: key.remoteJid } : {},
77
+ meta: {
78
+ fromMe: key.fromMe,
79
+ participant: key.participant
80
+ }
81
+ };
82
+ }
83
+ function createWhatsAppSendReceipt(kind, keys) {
84
+ return createMessageReceiptFromOutboundResults({
85
+ kind: resolveWhatsAppReceiptKind(kind),
86
+ results: keys.map(toReceiptSourceResult)
87
+ });
88
+ }
89
+ function mergeWhatsAppReceiptSourceMetadata(sources) {
90
+ const defined = sources.filter((source) => Boolean(source));
91
+ const preferred = defined[0];
92
+ if (!preferred) return;
93
+ const merged = /* @__PURE__ */ new Map();
94
+ const meta = /* @__PURE__ */ new Map();
95
+ for (const source of defined.toReversed()) {
96
+ for (const [key, value] of Object.entries(source)) if (key !== "meta" && value !== void 0) merged.set(key, value);
97
+ for (const [key, value] of Object.entries(source.meta ?? {})) if (value !== void 0) meta.set(key, value);
98
+ }
99
+ const mergedSource = Object.assign({}, preferred, Object.fromEntries(merged));
100
+ if (meta.size > 0) mergedSource.meta = Object.fromEntries(meta);
101
+ else delete mergedSource.meta;
102
+ return mergedSource;
103
+ }
104
+ function enrichWhatsAppDeliveryReceipt(params) {
105
+ const seenPartIds = /* @__PURE__ */ new Set();
106
+ const parts = params.receipt.parts.flatMap((part) => {
107
+ if (seenPartIds.has(part.platformMessageId)) return [];
108
+ seenPartIds.add(part.platformMessageId);
109
+ const matchingParts = params.sources.flatMap((source) => source.parts.filter((candidate) => candidate.platformMessageId === part.platformMessageId));
110
+ const matchingRawSources = params.sources.flatMap((source) => source.raw?.filter((raw) => raw.messageId === part.platformMessageId) ?? []);
111
+ const raw = mergeWhatsAppReceiptSourceMetadata([
112
+ part.raw,
113
+ ...matchingParts.map((candidate) => candidate.raw),
114
+ ...matchingRawSources
115
+ ]);
116
+ const threadId = part.threadId ?? matchingParts.find((candidate) => candidate.threadId)?.threadId;
117
+ const replyToId = part.replyToId ?? matchingParts.find((candidate) => candidate.replyToId)?.replyToId;
118
+ return [{
119
+ ...part,
120
+ ...threadId ? { threadId } : {},
121
+ ...replyToId ? { replyToId } : {},
122
+ ...raw ? { raw } : {}
123
+ }];
124
+ });
125
+ const threadId = params.receipt.threadId ?? params.sources.find((source) => source.threadId)?.threadId;
126
+ const replyToId = params.receipt.replyToId ?? params.sources.find((source) => source.replyToId)?.replyToId;
127
+ const editToken = params.receipt.editToken ?? params.sources.find((source) => source.editToken)?.editToken;
128
+ const deleteToken = params.receipt.deleteToken ?? params.sources.find((source) => source.deleteToken)?.deleteToken;
129
+ return {
130
+ ...params.receipt,
131
+ parts,
132
+ raw: parts.map((part) => part.raw ?? {
133
+ channel: "whatsapp",
134
+ messageId: part.platformMessageId
135
+ }),
136
+ ...threadId ? { threadId } : {},
137
+ ...replyToId ? { replyToId } : {},
138
+ ...editToken ? { editToken } : {},
139
+ ...deleteToken ? { deleteToken } : {}
140
+ };
141
+ }
142
+ function normalizeKey(key) {
143
+ const id = typeof key?.id === "string" ? key.id.trim() : "";
144
+ if (!id) return;
145
+ return {
146
+ id,
147
+ remoteJid: key?.remoteJid,
148
+ fromMe: key?.fromMe,
149
+ participant: key?.participant
150
+ };
151
+ }
152
+ function createWhatsAppProviderNotAcceptedError(kind) {
153
+ const cause = /* @__PURE__ */ new Error("Baileys returned no accepted message key.");
154
+ return new PlatformMessageNotDispatchedError(`WhatsApp ${kind} send was not accepted by the provider: ${cause.message}`, { cause });
155
+ }
156
+ function normalizeWhatsAppSendResult(result, kind) {
157
+ const key = normalizeKey(result?.key);
158
+ if (!key) throw createWhatsAppProviderNotAcceptedError(kind);
159
+ return {
160
+ kind,
161
+ messageId: key.id,
162
+ receipt: createWhatsAppSendReceipt(kind, [key]),
163
+ keys: [key],
164
+ providerAccepted: true
165
+ };
166
+ }
167
+ /** Refuse synthetic ids from listener boundaries before callers create durable receipts. */
168
+ function requireWhatsAppAcceptedSendResult(result) {
169
+ if (result.providerAccepted && result.messageId !== "unknown" && result.keys.some((key) => key.id === result.messageId) && listWhatsAppSendResultMessageIds(result).includes(result.messageId)) return result;
170
+ throw createWhatsAppProviderNotAcceptedError(result.kind);
171
+ }
172
+ /** Persist accepted provider facts before account bookkeeping or later platform sends can fail. */
173
+ function rememberWhatsAppAcceptedSend(params) {
174
+ const accepted = requireWhatsAppAcceptedSendResult(params.result);
175
+ params.results.push(accepted);
176
+ if (params.results.length === 1) {
177
+ const deliveryActivity = logicalWhatsAppDeliveryActivity.getStore();
178
+ const alreadyAccounted = activityAccountedWhatsAppSendResults.has(accepted) || Boolean(deliveryActivity?.accountedAccountIds.has(params.accountId));
179
+ activityAccountedWhatsAppSendResults.add(accepted);
180
+ if (!alreadyAccounted) {
181
+ deliveryActivity?.accountedAccountIds.add(params.accountId);
182
+ recordChannelActivity({
183
+ channel: "whatsapp",
184
+ accountId: params.accountId,
185
+ direction: "outbound"
186
+ });
187
+ }
188
+ }
189
+ return accepted;
190
+ }
191
+ /** Nested owners already attempted activity accounting before exposing accepted delivery. */
192
+ function rememberWhatsAppPartialSend(params) {
193
+ if (!isChannelPartialDeliveryError(params.error)) return false;
194
+ const delivery = params.error.deliveryResult;
195
+ const messageIds = uniqueStrings([...delivery.receipt ? listMessageReceiptPlatformIds(delivery.receipt) : [], ...normalizeStringEntries(delivery.messageIds ?? [])]).filter((messageId) => messageId !== "unknown");
196
+ const existingIds = new Set(params.results.flatMap(listWhatsAppSendResultMessageIds));
197
+ const acceptedIds = messageIds.filter((messageId) => !existingIds.has(messageId));
198
+ const messageId = acceptedIds[0];
199
+ if (!messageId) return false;
200
+ const receiptSources = [];
201
+ if (delivery.receipt) receiptSources.push({
202
+ channel: "whatsapp",
203
+ messageId,
204
+ receipt: delivery.receipt
205
+ });
206
+ const receiptIds = new Set(delivery.receipt ? listMessageReceiptPlatformIds(delivery.receipt) : []);
207
+ for (const acceptedId of acceptedIds) if (!receiptIds.has(acceptedId)) receiptSources.push({
208
+ channel: "whatsapp",
209
+ messageId: acceptedId
210
+ });
211
+ const receipt = createMessageReceiptFromOutboundResults({
212
+ kind: resolveWhatsAppReceiptKind(params.kind),
213
+ results: receiptSources
214
+ });
215
+ const accepted = requireWhatsAppAcceptedSendResult({
216
+ kind: params.kind,
217
+ messageId,
218
+ receipt,
219
+ keys: acceptedIds.map((id) => ({ id })),
220
+ providerAccepted: true
221
+ });
222
+ activityAccountedWhatsAppSendResults.add(accepted);
223
+ params.results.push(accepted);
224
+ return true;
225
+ }
226
+ /** Merge accepted local sends with nested partial receipts without inventing provider ids. */
227
+ function mergeWhatsAppAcceptedSendError(params) {
228
+ const nested = isChannelPartialDeliveryError(params.error) ? params.error.deliveryResult : void 0;
229
+ const receiptSources = [];
230
+ const recordedIds = /* @__PURE__ */ new Set();
231
+ for (const result of params.results) {
232
+ const resultIds = listWhatsAppSendResultMessageIds(result);
233
+ if (resultIds.every((messageId) => recordedIds.has(messageId))) continue;
234
+ receiptSources.push({
235
+ channel: "whatsapp",
236
+ messageId: result.messageId,
237
+ ...result.receipt ? { receipt: result.receipt } : {}
238
+ });
239
+ for (const messageId of resultIds) recordedIds.add(messageId);
240
+ }
241
+ const unrecordedNestedIds = uniqueStrings([...nested?.receipt ? listMessageReceiptPlatformIds(nested.receipt) : [], ...normalizeStringEntries(nested?.messageIds ?? [])]).filter((messageId) => messageId !== "unknown").filter((messageId) => !recordedIds.has(messageId));
242
+ const nestedMessageId = unrecordedNestedIds[0];
243
+ if (nested?.receipt && nestedMessageId) {
244
+ receiptSources.push({
245
+ channel: "whatsapp",
246
+ messageId: nestedMessageId,
247
+ receipt: nested.receipt
248
+ });
249
+ for (const messageId of listMessageReceiptPlatformIds(nested.receipt)) recordedIds.add(messageId);
250
+ }
251
+ for (const messageId of unrecordedNestedIds) if (!recordedIds.has(messageId)) receiptSources.push({
252
+ channel: "whatsapp",
253
+ messageId
254
+ });
255
+ const receipt = enrichWhatsAppDeliveryReceipt({
256
+ receipt: createMessageReceiptFromOutboundResults({
257
+ kind: resolveWhatsAppReceiptKind(params.kind),
258
+ results: receiptSources
259
+ }),
260
+ sources: [...params.results.flatMap((result) => result.receipt ? [result.receipt] : []), ...nested?.receipt ? [nested.receipt] : []]
261
+ });
262
+ const messageIds = listMessageReceiptPlatformIds(receipt);
263
+ if (messageIds.length === 0) return params.error;
264
+ let cause = params.error;
265
+ const visitedPartialErrors = /* @__PURE__ */ new Set();
266
+ while (isChannelPartialDeliveryError(cause) && cause instanceof Error && cause.cause !== void 0 && !visitedPartialErrors.has(cause)) {
267
+ visitedPartialErrors.add(cause);
268
+ cause = cause.cause;
269
+ }
270
+ return createChannelPartialDeliveryError(cause, {
271
+ ...nested,
272
+ messageIds,
273
+ receipt,
274
+ visibleReplySent: true
275
+ });
276
+ }
277
+ function combineWhatsAppSendResults(kind, results) {
278
+ const messageId = uniqueStrings(results.flatMap(listWhatsAppSendResultMessageIds))[0];
279
+ if (!messageId) throw createWhatsAppProviderNotAcceptedError(kind);
280
+ const keys = results.flatMap((result) => result.keys);
281
+ const combined = {
282
+ kind,
283
+ messageId,
284
+ receipt: createWhatsAppSendReceipt(kind, keys),
285
+ keys,
286
+ providerAccepted: results.some((result) => result.providerAccepted)
287
+ };
288
+ if (results.some((result) => activityAccountedWhatsAppSendResults.has(result))) activityAccountedWhatsAppSendResults.add(combined);
289
+ return combined;
290
+ }
291
+ function listWhatsAppSendResultMessageIds(result) {
292
+ const receiptIds = result.receipt ? listMessageReceiptPlatformIds(result.receipt) : [];
293
+ if (receiptIds.length > 0) return receiptIds;
294
+ const keyIds = normalizeStringEntries(result.keys.map((key) => key.id));
295
+ if (keyIds.length > 0) return uniqueStrings(keyIds);
296
+ return [];
297
+ }
298
+ //#endregion
57
299
  //#region extensions/whatsapp/src/outbound-media-contract.ts
58
300
  const WHATSAPP_VOICE_FILE_NAME = "voice.ogg";
59
301
  const WHATSAPP_VOICE_SAMPLE_RATE_HZ = 48e3;
@@ -101,7 +343,7 @@ function inferWhatsAppMediaKind(media, resolvedContentType) {
101
343
  function normalizeWhatsAppLoadedMedia(media, mediaUrl) {
102
344
  const filenameMimeType = mimeTypeFromFilePath(media.fileName);
103
345
  const normalizedContentType = normalizeMimeType(media.contentType);
104
- const resolvedContentType = !normalizedContentType || normalizedContentType === "application/octet-stream" ? filenameMimeType ?? media.contentType : media.contentType;
346
+ const resolvedContentType = !normalizedContentType || normalizedContentType === "application/octet-stream" ? filenameMimeType ?? normalizedContentType : normalizedContentType;
105
347
  const kind = inferWhatsAppMediaKind(media, resolvedContentType);
106
348
  const mimetype = kind === "audio" && isWhatsAppNativeVoiceAudio({
107
349
  contentType: media.contentType,
@@ -167,13 +409,13 @@ function deriveWhatsAppDocumentFileName(mediaUrl) {
167
409
  }
168
410
  //#endregion
169
411
  //#region extensions/whatsapp/src/send.ts
170
- const outboundLog = createSubsystemLogger("gateway/channels/whatsapp").child("outbound");
412
+ const outboundLog = createSubsystemLogger$1("gateway/channels/whatsapp").child("outbound");
171
413
  function supportsForcedDocumentDelivery(kind) {
172
414
  return kind === "image" || kind === "video";
173
415
  }
174
416
  function buildWhatsAppMediaSendState(params) {
175
417
  const { media, caption } = params;
176
- const forceDocumentDelivery = Boolean(params.forceDocument && supportsForcedDocumentDelivery(media.kind));
418
+ const forceDocumentDelivery = Boolean(params.forceDocument && supportsForcedDocumentDelivery(media.kind)) || media.kind === "document" && (media.mimetype.startsWith("image/") || media.mimetype.startsWith("video/"));
177
419
  let text = caption ?? "";
178
420
  let documentFileName = media.kind === "document" ? media.fileName : void 0;
179
421
  let visibleTextAfterVoice;
@@ -219,6 +461,9 @@ function resolveActualSentRemoteJid(result, fallbackJid) {
219
461
  return fallbackJid;
220
462
  }
221
463
  async function sendMessageWhatsApp(to, body, options) {
464
+ return await withWhatsAppLogicalDeliveryActivity(() => sendMessageWhatsAppInActivityScope(to, body, options));
465
+ }
466
+ async function sendMessageWhatsAppInActivityScope(to, body, options) {
222
467
  let text = options.preserveLeadingWhitespace ? body : normalizeWhatsAppPayloadText(body);
223
468
  const jid = toWhatsappJid(to);
224
469
  const mediaUrls = resolveAdditiveWhatsAppMediaUrls(options);
@@ -259,6 +504,7 @@ async function sendMessageWhatsApp(to, body, options) {
259
504
  correlationId,
260
505
  to: redactedTo
261
506
  });
507
+ const acceptedResults = [];
262
508
  try {
263
509
  const redactedJid = redactIdentifier(jid);
264
510
  let mediaBuffer;
@@ -295,7 +541,14 @@ async function sendMessageWhatsApp(to, body, options) {
295
541
  }, "sending message");
296
542
  if (!isWhatsAppNewsletterJid(jid)) {
297
543
  await active.assertSendReady?.(to);
298
- await active.sendComposingTo(to);
544
+ try {
545
+ await active.sendComposingTo(to);
546
+ } catch (err) {
547
+ logger.warn({
548
+ err: String(err),
549
+ jid: redactedJid
550
+ }, "failed to send composing presence; continuing message delivery");
551
+ }
299
552
  }
300
553
  const accountId = Boolean(options.accountId?.trim()) ? resolvedAccountId : void 0;
301
554
  const sendOptions = options.gifPlayback || forceDocumentDelivery || accountId || documentFileName || options.quotedMessageKey ? {
@@ -305,8 +558,9 @@ async function sendMessageWhatsApp(to, body, options) {
305
558
  ...options.quotedMessageKey ? { quotedMessageKey: options.quotedMessageKey } : {},
306
559
  accountId
307
560
  } : void 0;
308
- const result = sendOptions ? await active.sendMessage(to, text, mediaBuffer, mediaType, sendOptions) : await active.sendMessage(to, text, mediaBuffer, mediaType);
309
- const messageId = result?.messageId ?? "unknown";
561
+ const result = requireWhatsAppAcceptedSendResult(sendOptions ? await active.sendMessage(to, text, mediaBuffer, mediaType, sendOptions) : await active.sendMessage(to, text, mediaBuffer, mediaType));
562
+ acceptedResults.push(result);
563
+ const messageId = result.messageId;
310
564
  const sentRemoteJid = resolveActualSentRemoteJid(result, jid);
311
565
  const trailingTextChunks = [visibleTextAfterVoice, ...textChunks].filter((chunk) => Boolean(chunk));
312
566
  if (trailingTextChunks.length > 0) {
@@ -315,9 +569,10 @@ async function sendMessageWhatsApp(to, body, options) {
315
569
  toJid: sentRemoteJid
316
570
  });
317
571
  for (const trailingText of trailingTextChunks) {
318
- const trailingResult = sendOptions ? await active.sendMessage(to, trailingText, void 0, void 0, sendOptions) : await active.sendMessage(to, trailingText, void 0, void 0);
572
+ const trailingResult = requireWhatsAppAcceptedSendResult(sendOptions ? await active.sendMessage(to, trailingText, void 0, void 0, sendOptions) : await active.sendMessage(to, trailingText, void 0, void 0));
573
+ acceptedResults.push(trailingResult);
319
574
  await options.onDeliveryResult?.({
320
- messageId: trailingResult?.messageId ?? "unknown",
575
+ messageId: trailingResult.messageId,
321
576
  toJid: resolveActualSentRemoteJid(trailingResult, jid)
322
577
  });
323
578
  }
@@ -338,7 +593,12 @@ async function sendMessageWhatsApp(to, body, options) {
338
593
  to: redactedTo,
339
594
  hasMedia
340
595
  }, "failed to send via web session");
341
- throw err;
596
+ const firstAccepted = acceptedResults[0];
597
+ throw mergeWhatsAppAcceptedSendError({
598
+ error: err,
599
+ kind: firstAccepted?.kind ?? (hasMedia ? "media" : "text"),
600
+ results: acceptedResults
601
+ });
342
602
  }
343
603
  }
344
604
  async function sendTypingWhatsApp(to, options) {
@@ -413,7 +673,8 @@ async function sendPollWhatsApp(to, poll, options) {
413
673
  maxSelections: normalized.maxSelections
414
674
  }, "sending poll");
415
675
  if (!isWhatsAppNewsletterJid(jid)) await active.assertSendReady?.(to);
416
- const messageId = (await active.sendPoll(to, normalized))?.messageId ?? "unknown";
676
+ const result = requireWhatsAppAcceptedSendResult(await active.sendPoll(to, normalized));
677
+ const messageId = result.messageId;
417
678
  const durationMs = Date.now() - startedAt;
418
679
  outboundLog.info(`Sent poll ${messageId} -> ${redactedJid} (${durationMs}ms)`);
419
680
  logger.info({
@@ -422,7 +683,7 @@ async function sendPollWhatsApp(to, poll, options) {
422
683
  }, "sent poll");
423
684
  return {
424
685
  messageId,
425
- toJid: jid
686
+ toJid: resolveActualSentRemoteJid(result, jid)
426
687
  };
427
688
  } catch (err) {
428
689
  logger.error({
@@ -433,4 +694,4 @@ async function sendPollWhatsApp(to, poll, options) {
433
694
  }
434
695
  }
435
696
  //#endregion
436
- export { normalizeWhatsAppOutboundPayload as a, prepareWhatsAppOutboundMedia as c, sendTypingWhatsApp as i, resolveWhatsAppDocumentFileName as l, sendPollWhatsApp as n, normalizeWhatsAppPayloadText as o, sendReactionWhatsApp as r, normalizeWhatsAppPayloadTextPreservingIndentation as s, sendMessageWhatsApp as t, resolveWhatsAppReactionLevel as u };
697
+ export { resolveWhatsAppReactionLevel as _, normalizeWhatsAppOutboundPayload as a, prepareWhatsAppOutboundMedia as c, mergeWhatsAppAcceptedSendError as d, normalizeWhatsAppSendResult as f, resolveWhatsAppDocumentFileName as g, withWhatsAppLogicalDeliveryActivity as h, sendTypingWhatsApp as i, combineWhatsAppSendResults as l, rememberWhatsAppPartialSend as m, sendPollWhatsApp as n, normalizeWhatsAppPayloadText as o, rememberWhatsAppAcceptedSend as p, sendReactionWhatsApp as r, normalizeWhatsAppPayloadTextPreservingIndentation as s, sendMessageWhatsApp as t, listWhatsAppSendResultMessageIds 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-Dp9M3ZPX.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-D6_rfxTZ.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-Cmvd967u.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-B_DQIdzp.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-D3n71Uyx.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";
@@ -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-B_DQIdzp.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-RxuNGZ7w.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-BCZszf3l.js");
119
119
  }
120
120
  const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
121
121
  const whatsappConfigAdapter = createScopedChannelConfigAdapter({
@@ -273,8 +273,7 @@ function createWhatsAppPluginBase(params) {
273
273
  collectWarnings: collectWhatsAppSecurityWarnings
274
274
  },
275
275
  doctor: whatsappDoctor,
276
- setup: params.setup,
277
- ...params.setupContract ? { setupContract: params.setupContract } : {},
276
+ setupContract: params.setupContract,
278
277
  groups: params.groups
279
278
  });
280
279
  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-B_DQIdzp.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.2",
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.2"
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.2"
70
69
  },
71
70
  "build": {
72
- "openclawVersion": "2026.7.2-beta.7"
71
+ "openclawVersion": "2026.8.1-beta.2"
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