@openclaw/whatsapp 2026.6.5-beta.2 → 2026.6.5-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.
package/dist/api.js CHANGED
@@ -3,33 +3,144 @@ import { a as resolveWhatsAppAccount, i as listWhatsAppAuthDirs, n as hasAnyWhat
3
3
  import { a as normalizeWhatsAppAllowFromEntries, c as normalizeWhatsAppTarget, i as looksLikeWhatsAppTargetId, r as isWhatsAppUserTarget, s as normalizeWhatsAppMessagingTarget, t as isWhatsAppGroupJid } from "./normalize-target-bVWjgftN.js";
4
4
  import { t as resolveWhatsAppOutboundTarget } from "./resolve-outbound-target-CcdugbDf.js";
5
5
  import "./send-DSlVXKL_.js";
6
+ import { n as getStatusCode, t as formatError } from "./session-errors-CbsoQqoy.js";
6
7
  import { c as assertWebChannel, d as markdownToWhatsApp, f as resolveJidToE164, l as isSelfChatMode, m as toWhatsappJidWithLid, n as normalizeE164, p as toWhatsappJid, r as resolveUserPath, u as jidToE164 } from "./text-runtime-Dk37KYHj.js";
7
- import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./channel-CK_4OwYn.js";
8
+ import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./channel-DpreE7Jm.js";
8
9
  import { t as whatsappCommandPolicy } from "./command-policy-BIOSHySD.js";
9
- import { a as resolveWhatsAppGroupRequireMention, o as resolveWhatsAppGroupToolPolicy, s as resolveWhatsAppGroupIntroHint } from "./setup-core-DLXi32yR.js";
10
+ import { a as resolveWhatsAppGroupRequireMention, o as resolveWhatsAppGroupToolPolicy, s as resolveWhatsAppGroupIntroHint } from "./setup-core-B4mgubMj.js";
10
11
  import "./config-schema-CROZuhT-.js";
11
- import { t as whatsappSetupPlugin } from "./channel.setup-I-zFGu02.js";
12
+ import { t as whatsappSetupPlugin } from "./channel.setup-D5dt8amX.js";
12
13
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
13
14
  import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./directory-config-Dijefxc3.js";
14
15
  import { n as testing } from "./access-control-C53zKYSN.js";
15
- import { m as extractText, t as createWebSendApi } from "./send-api-Bjn-h80j.js";
16
+ import { d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractLocationData, h as extractContextInfo, p as describeReplyContext, t as createWebSendApi, y as extractText } from "./send-api-CFbAgIbn.js";
16
17
  import { r as waitForWaConnection, t as createWaSocket } from "./session-CoxlXm2K.js";
17
18
  import "openclaw/plugin-sdk/channel-actions";
18
19
  import "openclaw/plugin-sdk/account-resolution";
19
20
  import "openclaw/plugin-sdk/core";
20
21
  import "openclaw/plugin-sdk/account-id";
22
+ import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
21
23
  //#region extensions/whatsapp/src/qa-driver.runtime.ts
24
+ function isRecord(value) {
25
+ return Boolean(value && typeof value === "object");
26
+ }
27
+ function readString(value) {
28
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
29
+ }
30
+ function readBoolean(value) {
31
+ return typeof value === "boolean" ? value : void 0;
32
+ }
33
+ function findMessageSection(message, sectionNames) {
34
+ if (!isRecord(message)) return;
35
+ const queue = [{
36
+ depth: 0,
37
+ value: message
38
+ }];
39
+ const seen = /* @__PURE__ */ new Set();
40
+ while (queue.length > 0) {
41
+ const current = queue.shift();
42
+ if (!current || seen.has(current.value)) continue;
43
+ seen.add(current.value);
44
+ for (const sectionName of sectionNames) {
45
+ const section = current.value[sectionName];
46
+ if (isRecord(section)) return section;
47
+ }
48
+ if (current.depth >= 4) continue;
49
+ for (const wrapperName of [
50
+ "botInvokeMessage",
51
+ "documentWithCaptionMessage",
52
+ "ephemeralMessage",
53
+ "groupMentionedMessage",
54
+ "viewOnceMessage",
55
+ "viewOnceMessageV2",
56
+ "viewOnceMessageV2Extension"
57
+ ]) {
58
+ const wrapper = current.value[wrapperName];
59
+ if (isRecord(wrapper) && isRecord(wrapper.message)) queue.push({
60
+ depth: current.depth + 1,
61
+ value: wrapper.message
62
+ });
63
+ }
64
+ }
65
+ }
66
+ function readReaction(message) {
67
+ const reaction = findMessageSection(message, ["reactionMessage"]);
68
+ if (!reaction) return;
69
+ const emoji = readString(reaction.text) ?? "";
70
+ const key = isRecord(reaction.key) ? reaction.key : void 0;
71
+ return {
72
+ emoji,
73
+ fromMe: readBoolean(key?.fromMe),
74
+ messageId: readString(key?.id),
75
+ participant: readString(key?.participant)
76
+ };
77
+ }
78
+ function readPoll(message) {
79
+ const poll = findMessageSection(message, [
80
+ "pollCreationMessage",
81
+ "pollCreationMessageV2",
82
+ "pollCreationMessageV3"
83
+ ]);
84
+ if (!poll) return;
85
+ return {
86
+ options: (Array.isArray(poll.options) ? poll.options : []).map((option) => isRecord(option) ? readString(option.optionName) : void 0).filter((option) => Boolean(option)),
87
+ question: readString(poll.name)
88
+ };
89
+ }
90
+ function readMedia(message) {
91
+ const normalizedMessage = isRecord(message) ? normalizeMessageContent(message) : void 0;
92
+ for (const sectionName of [
93
+ "imageMessage",
94
+ "videoMessage",
95
+ "audioMessage",
96
+ "documentMessage",
97
+ "stickerMessage"
98
+ ]) {
99
+ const section = findMessageSection(normalizedMessage ?? message, [sectionName]);
100
+ if (!section) continue;
101
+ const mediaMessage = { [sectionName]: section };
102
+ return {
103
+ fileName: readString(section.fileName),
104
+ mediaType: resolveInboundMediaMimetype(mediaMessage)
105
+ };
106
+ }
107
+ }
108
+ function readQuotedMessage(message) {
109
+ const contextInfo = extractContextInfo(message.message ?? void 0);
110
+ const replyContext = describeReplyContext(message.message);
111
+ if (!contextInfo && !replyContext) return;
112
+ if (!contextInfo?.stanzaId && !contextInfo?.participant && !replyContext?.body) return;
113
+ return {
114
+ messageId: replyContext?.id ?? contextInfo?.stanzaId ?? void 0,
115
+ participant: replyContext?.sender?.jid ?? contextInfo?.participant ?? void 0,
116
+ text: replyContext?.body
117
+ };
118
+ }
22
119
  function normalizeObservedMessage(message, authDir) {
23
120
  if (message.key.fromMe) return null;
24
- const text = extractText(message.message ?? void 0);
25
- if (!text) return null;
121
+ const extractedText = extractText(message.message ?? void 0);
122
+ const location = extractLocationData(message.message);
123
+ const text = [extractedText, location ? formatLocationText(location) : void 0].filter(Boolean).join("\n").trim() || void 0;
124
+ const reaction = readReaction(message.message);
125
+ const poll = readPoll(message.message);
126
+ const media = readMedia(message.message);
127
+ const quoted = readQuotedMessage(message);
128
+ const kind = reaction ? "reaction" : poll ? "poll" : media ? "media" : location ? "location" : text ? "text" : "unknown";
129
+ if (!text && kind === "unknown") return null;
26
130
  const fromJid = message.key.remoteJid ?? void 0;
27
131
  return {
28
132
  fromJid,
29
133
  fromPhoneE164: fromJid ? jidToE164(fromJid, { authDir }) : null,
134
+ hasMedia: media ? true : void 0,
135
+ kind,
136
+ mediaFileName: media?.fileName,
137
+ mediaType: media?.mediaType,
30
138
  messageId: message.key.id ?? void 0,
31
139
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
32
- text
140
+ poll,
141
+ quoted,
142
+ reaction,
143
+ text: text ?? ""
33
144
  };
34
145
  }
35
146
  function closeSocket(sock) {
@@ -41,16 +152,39 @@ function closeSocket(sock) {
41
152
  const maybeClose = sock.ws?.close;
42
153
  if (typeof maybeClose === "function") maybeClose.call(sock.ws);
43
154
  }
155
+ function createConnectionClosedError(update) {
156
+ const reason = update.lastDisconnect?.error;
157
+ const status = getStatusCode(reason);
158
+ const details = reason ? `: ${formatError(reason)}` : "";
159
+ const statusLabel = typeof status === "number" ? ` (status ${status})` : "";
160
+ return /* @__PURE__ */ new Error(`WhatsApp QA driver connection closed${statusLabel}${details}`);
161
+ }
44
162
  async function startWhatsAppQaDriverSession(params) {
45
163
  const sock = await createWaSocket(false, false, { authDir: params.authDir });
46
164
  const observedMessages = [];
165
+ const pendingNotificationsWaiters = [];
47
166
  const waiters = [];
48
167
  let closed = false;
168
+ let closedError;
169
+ let receivedPendingNotifications = false;
49
170
  const removeWaiter = (waiter) => {
50
171
  const index = waiters.indexOf(waiter);
51
172
  if (index >= 0) waiters.splice(index, 1);
52
173
  clearTimeout(waiter.timeout);
53
174
  };
175
+ const removePendingNotificationsWaiter = (waiter) => {
176
+ const index = pendingNotificationsWaiters.indexOf(waiter);
177
+ if (index >= 0) pendingNotificationsWaiters.splice(index, 1);
178
+ clearTimeout(waiter.timeout);
179
+ };
180
+ const markPendingNotificationsReceived = () => {
181
+ if (receivedPendingNotifications) return;
182
+ receivedPendingNotifications = true;
183
+ for (const waiter of pendingNotificationsWaiters.slice()) {
184
+ removePendingNotificationsWaiter(waiter);
185
+ waiter.resolve();
186
+ }
187
+ };
54
188
  const observe = (message) => {
55
189
  observedMessages.push(message);
56
190
  for (const waiter of waiters.slice()) {
@@ -65,12 +199,23 @@ async function startWhatsAppQaDriverSession(params) {
65
199
  if (observed) observe(observed);
66
200
  }
67
201
  };
202
+ const onConnectionUpdate = (event) => {
203
+ if (event.receivedPendingNotifications === true) markPendingNotificationsReceived();
204
+ if (event.connection === "close") closeSessionResources(createConnectionClosedError(event));
205
+ };
68
206
  const removeMessageListener = () => {
69
- sock.ev.off?.("messages.upsert", onMessagesUpsert);
207
+ const evWithOff = sock.ev;
208
+ evWithOff.off?.("messages.upsert", onMessagesUpsert);
209
+ evWithOff.off?.("connection.update", onConnectionUpdate);
70
210
  };
71
211
  const closeSessionResources = (waiterError) => {
72
212
  if (closed) return;
73
213
  closed = true;
214
+ closedError = waiterError;
215
+ for (const waiter of pendingNotificationsWaiters.slice()) {
216
+ removePendingNotificationsWaiter(waiter);
217
+ if (waiterError) waiter.reject(waiterError);
218
+ }
74
219
  for (const waiter of waiters.slice()) {
75
220
  removeWaiter(waiter);
76
221
  if (waiterError) waiter.reject(waiterError);
@@ -79,15 +224,37 @@ async function startWhatsAppQaDriverSession(params) {
79
224
  closeSocket(sock);
80
225
  };
81
226
  sock.ev.on("messages.upsert", onMessagesUpsert);
227
+ sock.ev.on("connection.update", onConnectionUpdate);
82
228
  try {
83
229
  await waitForWaConnection(sock, { timeoutMs: params.connectionTimeoutMs ?? 45e3 });
230
+ if (params.waitForPendingNotifications) await new Promise((resolve, reject) => {
231
+ if (receivedPendingNotifications) {
232
+ resolve();
233
+ return;
234
+ }
235
+ if (closed) {
236
+ reject(closedError ?? /* @__PURE__ */ new Error("WhatsApp QA driver session closed"));
237
+ return;
238
+ }
239
+ const timeoutMs = params.connectionTimeoutMs ?? 45e3;
240
+ const waiter = {
241
+ resolve,
242
+ reject,
243
+ timeout: setTimeout(() => {
244
+ removePendingNotificationsWaiter(waiter);
245
+ reject(/* @__PURE__ */ new Error(`timed out after ${timeoutMs}ms waiting for WhatsApp QA driver pending notifications`));
246
+ }, timeoutMs)
247
+ };
248
+ pendingNotificationsWaiters.push(waiter);
249
+ });
84
250
  } catch (error) {
85
251
  closeSessionResources(error instanceof Error ? error : /* @__PURE__ */ new Error("failed starting WhatsApp QA driver session"));
86
252
  throw error;
87
253
  }
88
254
  const sendApi = createWebSendApi({
89
255
  sock,
90
- defaultAccountId: "qa-driver"
256
+ defaultAccountId: "qa-driver",
257
+ authDir: params.authDir
91
258
  });
92
259
  return {
93
260
  async close() {
@@ -96,15 +263,35 @@ async function startWhatsAppQaDriverSession(params) {
96
263
  getObservedMessages() {
97
264
  return [...observedMessages];
98
265
  },
99
- async sendText(to, text) {
100
- return { messageId: (await sendApi.sendMessage(to, text)).messageId };
266
+ async sendContact(to, contact) {
267
+ return { messageId: (await sendApi.sendContact(to, contact)).messageId };
268
+ },
269
+ async sendLocation(to, location) {
270
+ return { messageId: (await sendApi.sendLocation(to, location)).messageId };
271
+ },
272
+ async sendMedia(to, text, mediaBuffer, mediaType, options) {
273
+ return { messageId: (await sendApi.sendMessage(to, text, mediaBuffer, mediaType, options)).messageId };
274
+ },
275
+ async sendPoll(to, poll) {
276
+ return { messageId: (await sendApi.sendPoll(to, poll)).messageId };
277
+ },
278
+ async sendReaction(chatJid, messageId, emoji, options) {
279
+ return { messageId: (await sendApi.sendReaction(chatJid, messageId, emoji, options.fromMe, options.participant)).messageId };
280
+ },
281
+ async sendSticker(to, stickerBuffer, options) {
282
+ return { messageId: (await sendApi.sendSticker(to, stickerBuffer, options)).messageId };
283
+ },
284
+ async sendText(to, text, options) {
285
+ return { messageId: (await sendApi.sendMessage(to, text, void 0, void 0, options)).messageId };
101
286
  },
102
287
  async waitForMessage(paramsLocal) {
103
- const existing = observedMessages.find(paramsLocal.match);
288
+ const predicate = (message) => (!paramsLocal.observedAfter || new Date(message.observedAt).getTime() >= paramsLocal.observedAfter.getTime()) && paramsLocal.match(message);
289
+ const existing = observedMessages.find(predicate);
104
290
  if (existing) return existing;
291
+ if (closed) throw closedError ?? /* @__PURE__ */ new Error("WhatsApp QA driver session closed");
105
292
  return await new Promise((resolve, reject) => {
106
293
  const waiter = {
107
- predicate: paramsLocal.match,
294
+ predicate,
108
295
  resolve,
109
296
  reject,
110
297
  timeout: setTimeout(() => {
@@ -9,7 +9,7 @@ import { p as toWhatsappJid } from "./text-runtime-Dk37KYHj.js";
9
9
  import { t as createWhatsAppLoginTool } from "./agent-tools-login-BX7eHgDm.js";
10
10
  import { i as lookupInboundMessageMetaForTarget } from "./quoted-message-CveINB35.js";
11
11
  import { t as whatsappCommandPolicy } from "./command-policy-BIOSHySD.js";
12
- import { a as resolveWhatsAppGroupRequireMention, c as resolveWhatsAppMentionStripRegexes, i as whatsappSetupWizardProxy, l as formatWhatsAppConfigAllowFromEntries, n as createWhatsAppPluginBase, o as resolveWhatsAppGroupToolPolicy, r as loadWhatsAppChannelRuntime, s as resolveWhatsAppGroupIntroHint, t as whatsappSetupAdapter } from "./setup-core-DLXi32yR.js";
12
+ import { a as resolveWhatsAppGroupRequireMention, c as resolveWhatsAppMentionStripRegexes, i as whatsappSetupWizardProxy, l as formatWhatsAppConfigAllowFromEntries, n as createWhatsAppPluginBase, o as resolveWhatsAppGroupToolPolicy, r as loadWhatsAppChannelRuntime, s as resolveWhatsAppGroupIntroHint, t as whatsappSetupAdapter } from "./setup-core-B4mgubMj.js";
13
13
  import { f as readWebAuthExistsForDecision, n as WHATSAPP_AUTH_UNSTABLE_CODE } from "./auth-store-B2ZibndQ.js";
14
14
  import { t as detectWhatsAppLegacyStateMigrations } from "./state-migrations-D_BmQUR9.js";
15
15
  import { createActionGate as createActionGate$1 } from "openclaw/plugin-sdk/channel-actions";
@@ -1,2 +1,2 @@
1
- import { t as whatsappPlugin } from "./channel-CK_4OwYn.js";
1
+ import { t as whatsappPlugin } from "./channel-DpreE7Jm.js";
2
2
  export { whatsappPlugin };
@@ -1,9 +1,9 @@
1
1
  import { startWebLoginWithQr as startWebLoginWithQr$1, waitForWebLogin as waitForWebLogin$1 } from "./login-qr-runtime.js";
2
2
  import { c as logoutWeb$1, d as readWebAuthExistsBestEffort$1, f as readWebAuthExistsForDecision$1, g as readWebSelfId$1, h as readWebAuthState$1, m as readWebAuthSnapshotBestEffort$1, o as getWebAuthAgeMs$1, p as readWebAuthSnapshot$1, s as logWebSelfId$1, x as webAuthExists$1 } from "./auth-store-B2ZibndQ.js";
3
3
  import { t as getActiveWebListener$1 } from "./active-listener-CFwkn3ho.js";
4
- import { t as monitorWebChannel$1 } from "./monitor-CFUAZhBS.js";
4
+ import { t as monitorWebChannel$1 } from "./monitor-CmA9YhPe.js";
5
5
  import { t as loginWeb$1 } from "./login-Bflq347x.js";
6
- import { whatsappSetupWizard as whatsappSetupWizard$1 } from "./setup-surface-YtBXJFy0.js";
6
+ import { whatsappSetupWizard as whatsappSetupWizard$1 } from "./setup-surface-DaWJR5kG.js";
7
7
  //#region extensions/whatsapp/src/channel.runtime.ts
8
8
  function getActiveWebListener(...args) {
9
9
  return getActiveWebListener$1(...args);
@@ -1,4 +1,4 @@
1
- import { a as resolveWhatsAppGroupRequireMention, i as whatsappSetupWizardProxy, n as createWhatsAppPluginBase, o as resolveWhatsAppGroupToolPolicy, s as resolveWhatsAppGroupIntroHint, t as whatsappSetupAdapter } from "./setup-core-DLXi32yR.js";
1
+ import { a as resolveWhatsAppGroupRequireMention, i as whatsappSetupWizardProxy, n as createWhatsAppPluginBase, o as resolveWhatsAppGroupToolPolicy, s as resolveWhatsAppGroupIntroHint, t as whatsappSetupAdapter } from "./setup-core-B4mgubMj.js";
2
2
  import { t as detectWhatsAppLegacyStateMigrations } from "./state-migrations-D_BmQUR9.js";
3
3
  //#region extensions/whatsapp/src/channel.setup.ts
4
4
  async function isWhatsAppAuthConfigured(account) {
@@ -9,7 +9,7 @@ import { c as logoutWeb, g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsApp
9
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";
10
10
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
11
11
  import { i as resolveWhatsAppInboundPolicy, r as resolveWhatsAppCommandAuthorized, t as checkInboundAccessControl } from "./access-control-C53zKYSN.js";
12
- import { a as mayContainWhatsAppOutboundMention, c as describeReplyContext, d as extractLocationData, f as extractMediaPlaceholder, h as hasInboundUserContent, i as addWhatsAppOutboundMentionsToContent, l as extractContactContext, m as extractText, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as extractMentionedJids, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as extractContextInfo } from "./send-api-Bjn-h80j.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
13
  import { a as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-CoxlXm2K.js";
14
14
  import { t as BufferJSON } from "./session.runtime-CyooSQvj.js";
15
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-BnRkIjlP.js";
@@ -30,8 +30,8 @@ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgen
30
30
  import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
31
31
  import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
32
32
  import { loadSessionStore, resolveStorePath, updateLastRoute, updateSessionStore } from "openclaw/plugin-sdk/session-store-runtime";
33
- import { DisconnectReason, downloadMediaMessage, isJidGroup, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
34
33
  import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatLocationText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, runChannelInboundEvent, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
34
+ import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
35
35
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
36
36
  import { createHash } from "node:crypto";
37
37
  import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContext, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
@@ -43,7 +43,6 @@ import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runti
43
43
  import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
44
44
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
45
45
  import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
46
- import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
47
46
  import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
48
47
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
49
48
  import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
@@ -204,24 +203,12 @@ var WhatsAppInboundMediaLimitExceededError = class extends Error {
204
203
  }
205
204
  };
206
205
  function unwrapMessage(message) {
207
- return normalizeMessageContent$1(message);
208
- }
209
- /**
210
- * Resolve the MIME type for an inbound media message.
211
- * Falls back to WhatsApp's standard formats when Baileys omits the MIME.
212
- */
213
- function resolveMediaMimetype(message) {
214
- const explicit = message.imageMessage?.mimetype ?? message.videoMessage?.mimetype ?? message.documentMessage?.mimetype ?? message.audioMessage?.mimetype ?? message.stickerMessage?.mimetype ?? void 0;
215
- if (explicit) return explicit;
216
- if (message.audioMessage) return "audio/ogg; codecs=opus";
217
- if (message.imageMessage) return "image/jpeg";
218
- if (message.videoMessage) return "video/mp4";
219
- if (message.stickerMessage) return "image/webp";
206
+ return normalizeMessageContent(message);
220
207
  }
221
208
  async function downloadInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024) {
222
209
  const message = unwrapMessage(msg.message);
223
210
  if (!message) return;
224
- const mimetype = resolveMediaMimetype(message);
211
+ const mimetype = resolveInboundMediaMimetype(message);
225
212
  const fileName = message.documentMessage?.fileName ?? void 0;
226
213
  if (!message.imageMessage && !message.videoMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
227
214
  try {
@@ -958,6 +945,19 @@ async function attachWebInboxToSocket(options) {
958
945
  logWhatsAppVerbose(options.verbose, `Failed to hydrate participating groups on connect: ${error}`);
959
946
  }
960
947
  })();
948
+ const sendApi = createWebSendApi({
949
+ sock: {
950
+ sendMessage: (jid, content, optionsLocal) => sendTrackedMessage(jid, content, optionsLocal),
951
+ sendPresenceUpdate: async (presenceLocal, jid) => {
952
+ const currentSock = getCurrentSock();
953
+ if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
954
+ return currentSock.sendPresenceUpdate(presenceLocal, jid);
955
+ }
956
+ },
957
+ defaultAccountId: options.accountId,
958
+ resolveOutboundMentions: ({ jid, text }) => resolveOutboundMentionsForGroup(jid, text),
959
+ authDir: options.authDir
960
+ });
961
961
  return {
962
962
  close: async () => {
963
963
  try {
@@ -981,19 +981,10 @@ async function attachWebInboxToSocket(options) {
981
981
  error: "closed"
982
982
  });
983
983
  },
984
- ...createWebSendApi({
985
- sock: {
986
- sendMessage: (jid, content, optionsLocal) => sendTrackedMessage(jid, content, optionsLocal),
987
- sendPresenceUpdate: async (presenceLocal, jid) => {
988
- const currentSock = getCurrentSock();
989
- if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
990
- return currentSock.sendPresenceUpdate(presenceLocal, jid);
991
- }
992
- },
993
- defaultAccountId: options.accountId,
994
- resolveOutboundMentions: ({ jid, text }) => resolveOutboundMentionsForGroup(jid, text),
995
- authDir: options.authDir
996
- })
984
+ sendComposingTo: sendApi.sendComposingTo,
985
+ sendMessage: sendApi.sendMessage,
986
+ sendPoll: sendApi.sendPoll,
987
+ sendReaction: sendApi.sendReaction
997
988
  };
998
989
  }
999
990
  async function monitorWebInbox(options) {
@@ -7,10 +7,10 @@ import { startWebLoginWithQr, waitForWebLogin } from "./login-qr-runtime.js";
7
7
  import { t as createWhatsAppLoginTool } from "./agent-tools-login-BX7eHgDm.js";
8
8
  import { C as waitForCredsSaveQueue, T as writeCredsJsonAtomically, _ as readWebSelfIdentity, a as formatWhatsAppWebAuthStatusState, b as restoreCredsFromBackupIfNeeded, c as logoutWeb, d as readWebAuthExistsBestEffort, f as readWebAuthExistsForDecision, g as readWebSelfId, h as readWebAuthState, l as pickWebChannel, m as readWebAuthSnapshotBestEffort, n as WHATSAPP_AUTH_UNSTABLE_CODE, o as getWebAuthAgeMs, p as readWebAuthSnapshot, r as WhatsAppAuthUnstableError, s as logWebSelfId, t as WA_WEB_AUTH_DIR, u as readCredsJsonRaw, v as readWebSelfIdentityForDecision, w as waitForCredsSaveQueueWithTimeout, x as webAuthExists, y as resolveDefaultWebAuthDir } from "./auth-store-B2ZibndQ.js";
9
9
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
10
- import { d as extractLocationData, f as extractMediaPlaceholder, l as extractContactContext, m as extractText } from "./send-api-Bjn-h80j.js";
10
+ import { _ as extractMediaPlaceholder, g as extractLocationData, m as extractContactContext, y as extractText } from "./send-api-CFbAgIbn.js";
11
11
  import { n as newConnectionId, r as waitForWaConnection, t as createWaSocket } from "./session-CoxlXm2K.js";
12
12
  import { n as resolveWebAccountId, t as getActiveWebListener } from "./active-listener-CFwkn3ho.js";
13
- import { a as loadWebMediaRaw, c as monitorWebInbox, i as loadWebMedia, l as resetWebInboundDedupe, n as LocalMediaAccessError, o as optimizeImageToJpeg, r as getDefaultLocalRoots, s as optimizeImageToPng, t as monitorWebChannel } from "./monitor-CFUAZhBS.js";
13
+ import { a as loadWebMediaRaw, c as monitorWebInbox, i as loadWebMedia, l as resetWebInboundDedupe, n as LocalMediaAccessError, o as optimizeImageToJpeg, r as getDefaultLocalRoots, s as optimizeImageToPng, t as monitorWebChannel } from "./monitor-CmA9YhPe.js";
14
14
  import { t as loginWeb } from "./login-Bflq347x.js";
15
15
  import { HEARTBEAT_PROMPT, HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN, stripHeartbeatToken } from "openclaw/plugin-sdk/reply-runtime";
16
16
  export { DEFAULT_WEB_MEDIA_BYTES, HEARTBEAT_PROMPT, HEARTBEAT_TOKEN, LocalMediaAccessError, SILENT_REPLY_TOKEN, WA_WEB_AUTH_DIR, WHATSAPP_AUTH_UNSTABLE_CODE, WhatsAppAuthUnstableError, createWaSocket, createWhatsAppLoginTool, extractContactContext, extractLocationData, extractMediaPlaceholder, extractText, formatError, formatWhatsAppWebAuthStatusState, getActiveWebListener, getDefaultLocalRoots, getStatusCode, getWebAuthAgeMs, handleWhatsAppAction, hasWebCredsSync, loadWebMedia, loadWebMediaRaw, logWebSelfId, loginWeb, logoutWeb, monitorWebChannel, monitorWebInbox, newConnectionId, optimizeImageToJpeg, optimizeImageToPng, pickWebChannel, readCredsJsonRaw, readWebAuthExistsBestEffort, readWebAuthExistsForDecision, readWebAuthSnapshot, readWebAuthSnapshotBestEffort, readWebAuthState, readWebSelfId, readWebSelfIdentity, readWebSelfIdentityForDecision, resetWebInboundDedupe, resolveDefaultWebAuthDir, resolveWebAccountId, resolveWebCredsBackupPath, resolveWebCredsPath, restoreCredsFromBackupIfNeeded, sendMessageWhatsApp, sendPollWhatsApp, sendReactionWhatsApp, sendTypingWhatsApp, setWhatsAppRuntime, startWebLoginWithQr, stripHeartbeatToken, waitForCredsSaveQueue, waitForCredsSaveQueueWithTimeout, waitForWaConnection, waitForWebLogin, webAuthExists, whatsAppActionRuntime, writeCredsJsonAtomically };
@@ -8,8 +8,9 @@ import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings
8
8
  import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound";
9
9
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
10
10
  import { getImageMetadata, resizeToJpeg } from "openclaw/plugin-sdk/media-runtime";
11
- import { extractMessageContent, getContentType, normalizeMessageContent } from "baileys";
12
11
  import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
12
+ import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
13
+ import "openclaw/plugin-sdk/media-store";
13
14
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
14
15
  //#region extensions/whatsapp/src/vcard.ts
15
16
  const ALLOWED_VCARD_KEYS = new Set([
@@ -364,6 +365,20 @@ function hasInboundUserContent(rawMessage) {
364
365
  return false;
365
366
  }
366
367
  //#endregion
368
+ //#region extensions/whatsapp/src/inbound/media-mimetype.ts
369
+ /**
370
+ * Resolve the MIME type for an inbound media message.
371
+ * Falls back to WhatsApp's standard formats when Baileys omits the MIME.
372
+ */
373
+ function resolveInboundMediaMimetype(message) {
374
+ const explicit = message.imageMessage?.mimetype ?? message.videoMessage?.mimetype ?? message.documentMessage?.mimetype ?? message.audioMessage?.mimetype ?? message.stickerMessage?.mimetype ?? void 0;
375
+ if (explicit) return explicit;
376
+ if (message.audioMessage) return "audio/ogg; codecs=opus";
377
+ if (message.imageMessage) return "image/jpeg";
378
+ if (message.videoMessage) return "video/mp4";
379
+ if (message.stickerMessage) return "image/webp";
380
+ }
381
+ //#endregion
367
382
  //#region extensions/whatsapp/src/image-preview.ts
368
383
  const WHATSAPP_IMAGE_THUMBNAIL_SIDE = 32;
369
384
  const WHATSAPP_IMAGE_THUMBNAIL_QUALITY = 50;
@@ -644,6 +659,12 @@ function createWebSendApi(params) {
644
659
  text,
645
660
  mentionedJids: []
646
661
  };
662
+ const sendStructuredMessage = async (to, content, kind) => {
663
+ const jid = resolveOutboundJid(to);
664
+ const result = await params.sock.sendMessage(jid, content);
665
+ recordWhatsAppOutbound(params.defaultAccountId);
666
+ return normalizeWhatsAppSendResult(result, kind);
667
+ };
647
668
  return {
648
669
  sendMessage: async (to, text, mediaBuffer, mediaTypeInput, sendOptions) => {
649
670
  let mediaType = mediaTypeInput;
@@ -711,14 +732,34 @@ function createWebSendApi(params) {
711
732
  return combineWhatsAppSendResults(mediaBuffer ? "media" : "text", results);
712
733
  },
713
734
  sendPoll: async (to, poll) => {
714
- const jid = resolveOutboundJid(to);
715
- const result = await params.sock.sendMessage(jid, { poll: {
735
+ return await sendStructuredMessage(to, { poll: {
716
736
  name: poll.question,
717
737
  values: poll.options,
718
738
  selectableCount: poll.maxSelections ?? 1
719
- } });
720
- recordWhatsAppOutbound(params.defaultAccountId);
721
- return normalizeWhatsAppSendResult(result, "poll");
739
+ } }, "poll");
740
+ },
741
+ sendContact: async (to, contact) => {
742
+ return await sendStructuredMessage(to, { contacts: {
743
+ displayName: contact.displayName,
744
+ contacts: [{
745
+ displayName: contact.displayName,
746
+ vcard: contact.vcard
747
+ }]
748
+ } }, "contact");
749
+ },
750
+ sendLocation: async (to, location) => {
751
+ return await sendStructuredMessage(to, { location: {
752
+ degreesLatitude: location.degreesLatitude,
753
+ degreesLongitude: location.degreesLongitude,
754
+ name: location.name,
755
+ address: location.address
756
+ } }, "location");
757
+ },
758
+ sendSticker: async (to, stickerBuffer, options) => {
759
+ return await sendStructuredMessage(to, {
760
+ sticker: stickerBuffer,
761
+ mimetype: options?.mimetype ?? "image/webp"
762
+ }, "sticker");
722
763
  },
723
764
  sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
724
765
  const jid = resolveOutboundJid(chatJid);
@@ -740,4 +781,4 @@ function createWebSendApi(params) {
740
781
  };
741
782
  }
742
783
  //#endregion
743
- export { mayContainWhatsAppOutboundMention as a, describeReplyContext as c, extractLocationData as d, extractMediaPlaceholder as f, hasInboundUserContent as h, addWhatsAppOutboundMentionsToContent as i, extractContactContext as l, extractText as m, listWhatsAppSendResultMessageIds as n, resolveWhatsAppOutboundMentions as o, extractMentionedJids as p, normalizeWhatsAppSendResult as r, addWhatsAppImagePreviewFields as s, createWebSendApi as t, extractContextInfo as u };
784
+ export { extractMediaPlaceholder as _, mayContainWhatsAppOutboundMention as a, hasInboundUserContent as b, DisconnectReason as c, normalizeMessageContent$1 as d, resolveInboundMediaMimetype as f, extractLocationData as g, extractContextInfo as h, addWhatsAppOutboundMentionsToContent as i, downloadMediaMessage as l, extractContactContext as m, listWhatsAppSendResultMessageIds as n, resolveWhatsAppOutboundMentions as o, describeReplyContext as p, normalizeWhatsAppSendResult as r, addWhatsAppImagePreviewFields as s, createWebSendApi as t, isJidGroup as u, extractMentionedJids as v, extractText as y };
@@ -99,10 +99,10 @@ async function applyWhatsAppSecurityConfigFixes(params) {
99
99
  //#region extensions/whatsapp/src/shared.ts
100
100
  const WHATSAPP_CHANNEL = "whatsapp";
101
101
  async function loadWhatsAppChannelRuntime() {
102
- return await import("./channel.runtime-DIAo-4Bj.js");
102
+ return await import("./channel.runtime-B534_h4x.js");
103
103
  }
104
104
  async function loadWhatsAppSetupSurface() {
105
- return await import("./setup-surface-YtBXJFy0.js");
105
+ return await import("./setup-surface-DaWJR5kG.js");
106
106
  }
107
107
  const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
108
108
  const whatsappConfigAdapter = createScopedChannelConfigAdapter({
@@ -2,7 +2,7 @@ import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js"
2
2
  import { r as hasWebCredsSync } from "./creds-files-B1kSWtBg.js";
3
3
  import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-DgViSyJx.js";
4
4
  import { a as normalizeWhatsAppAllowFromEntries, o as normalizeWhatsAppAllowFromEntry } from "./normalize-target-bVWjgftN.js";
5
- import { t as whatsappSetupAdapter } from "./setup-core-DLXi32yR.js";
5
+ import { t as whatsappSetupAdapter } from "./setup-core-B4mgubMj.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
@@ -1,2 +1,2 @@
1
- import { t as whatsappSetupPlugin } from "./channel.setup-I-zFGu02.js";
1
+ import { t as whatsappSetupPlugin } from "./channel.setup-D5dt8amX.js";
2
2
  export { whatsappSetupPlugin };
@@ -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-B-g0GzHp.js")).finalizeWhatsAppSetup(params),
39
+ finalize: async (params) => await (await import("./setup-finalize-B2EnzoZx.js")).finalizeWhatsAppSetup(params),
40
40
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
41
41
  onAccountRecorded: (accountId, options) => {
42
42
  options?.onAccountId?.(channel, accountId);
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@openclaw/whatsapp",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-beta.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/whatsapp",
9
- "version": "2026.6.5-beta.2",
9
+ "version": "2026.6.5-beta.3",
10
10
  "dependencies": {
11
11
  "audio-decode": "2.2.3",
12
12
  "baileys": "7.0.0-rc13",
13
13
  "typebox": "1.1.39"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.6.5-beta.2"
16
+ "openclaw": ">=2026.6.5-beta.3"
17
17
  },
18
18
  "peerDependenciesMeta": {
19
19
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/whatsapp",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-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.1.39"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.6.5-beta.2"
16
+ "openclaw": ">=2026.6.5-beta.3"
17
17
  },
18
18
  "peerDependenciesMeta": {
19
19
  "openclaw": {
@@ -56,10 +56,10 @@
56
56
  "minHostVersion": ">=2026.4.25"
57
57
  },
58
58
  "compat": {
59
- "pluginApi": ">=2026.6.5-beta.2"
59
+ "pluginApi": ">=2026.6.5-beta.3"
60
60
  },
61
61
  "build": {
62
- "openclawVersion": "2026.6.5-beta.2"
62
+ "openclawVersion": "2026.6.5-beta.3"
63
63
  },
64
64
  "release": {
65
65
  "publishToClawHub": true,