@openclaw/whatsapp 2026.7.2-beta.1 → 2026.7.2-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 (43) hide show
  1. package/dist/{access-control-DazDX3RB.js → access-control-tjy_em01.js} +1 -1
  2. package/dist/{accounts-CKOFMPf-.js → accounts-ejK8sXPU.js} +43 -3
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/{action-runtime-Bf-12TIH.js → action-runtime-p0V390gA.js} +2 -2
  5. package/dist/action-runtime.runtime.js +1 -1
  6. package/dist/{active-listener-5kHap439.js → active-listener-BbdlXooT.js} +1 -1
  7. package/dist/api.js +10 -10
  8. package/dist/{approval-handler.runtime-CBiGQuZ5.js → approval-handler.runtime-BO1oJ-b8.js} +3 -3
  9. package/dist/{approval-reactions-BGQoz25s.js → approval-reactions-CrZFAyf-.js} +2 -2
  10. package/dist/call-tool-api.js +2 -2
  11. package/dist/{channel-D_1cK7-w.js → channel-B2I4Opzb.js} +30 -28
  12. package/dist/channel-plugin-api.js +1 -1
  13. package/dist/{channel-react-action-PoRWfP9x.js → channel-react-action-CPRQE-s3.js} +3 -3
  14. package/dist/{channel.runtime-BPrKzcYd.js → channel.runtime-CjkhQzk8.js} +4 -4
  15. package/dist/{channel.setup-eX64WZDx.js → channel.setup-BMWE9LmN.js} +2 -2
  16. package/dist/{connection-controller-C1rwfWb4.js → connection-controller-HGaffeFb.js} +183 -22
  17. package/dist/{connection-controller-runtime-context-CbTsqmPT.js → connection-controller-runtime-context-9mEeAvu1.js} +10 -1
  18. package/dist/contract-api.js +1 -1
  19. package/dist/directory-config-BZ775eGX.js +196 -0
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{group-session-key-CLTZeH_F.js → group-session-key-Dh8WZun_.js} +1 -1
  22. package/dist/legacy-state-migrations-api.js +1 -1
  23. package/dist/light-runtime-api.js +1 -1
  24. package/dist/{login-DGIoToX8.js → login-ifOXCBjo.js} +3 -3
  25. package/dist/{login-qr-X-CZHOk9.js → login-qr-DUHzVH_Y.js} +4 -4
  26. package/dist/login-qr-runtime.js +1 -1
  27. package/dist/{monitor-BKtA0lzq.js → monitor-_jqDKBXj.js} +586 -382
  28. package/dist/question-reactions-C0qJ6wGA.js +142 -0
  29. package/dist/runtime-api.js +7 -7
  30. package/dist/{send-D9Ydnn4o.js → send-SB-4JrVZ.js} +2 -3
  31. package/dist/{send-api-W33lI2nR.js → send-api-BG0MgoFI.js} +3 -3
  32. package/dist/{setup-core-BXDFGqky.js → setup-core-BbIgEl2M.js} +3 -3
  33. package/dist/{setup-finalize-DzOHXnPj.js → setup-finalize-Cc-4iWGj.js} +3 -3
  34. package/dist/setup-plugin-api.js +1 -1
  35. package/dist/{setup-surface-5EUmMLHS.js → setup-surface-DhnXEZir.js} +2 -2
  36. package/dist/{session-mxWFn0eS.js → socket-close-jNGyZY4T.js} +221 -1
  37. package/dist/{state-migrations-D_BmQUR9.js → state-migrations-CAMTrs2U.js} +1 -8
  38. package/npm-shrinkwrap.json +3 -3
  39. package/package.json +4 -4
  40. package/dist/account-config-LiT9wWH9.js +0 -43
  41. package/dist/constants.js +0 -2
  42. package/dist/directory-config-CLBQrjtC.js +0 -37
  43. package/dist/targets.js +0 -2
@@ -0,0 +1,142 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { a as resolveWhatsAppAccount } from "./accounts-ejK8sXPU.js";
3
+ import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
4
+ //#region extensions/whatsapp/src/question-reactions.ts
5
+ var question_reactions_exports = /* @__PURE__ */ __exportAll({
6
+ clearWhatsAppQuestionReactionTargetsForTest: () => clearWhatsAppQuestionReactionTargetsForTest,
7
+ maybeResolveWhatsAppQuestionReaction: () => maybeResolveWhatsAppQuestionReaction,
8
+ registerWhatsAppQuestionReactionTargetForDeliveredPayload: () => registerWhatsAppQuestionReactionTargetForDeliveredPayload
9
+ });
10
+ const TARGET_TTL_MS = 1440 * 60 * 1e3;
11
+ const targets = /* @__PURE__ */ new Map();
12
+ function storeTarget(key, binding) {
13
+ const existing = targets.get(key);
14
+ if (existing) clearTimeout(existing.cleanupTimer);
15
+ const target = {
16
+ ...binding,
17
+ terminal: false,
18
+ expiresAtMs: Date.now() + TARGET_TTL_MS,
19
+ cleanupTimer: setTimeout(() => {
20
+ if (targets.get(key) === target) targets.delete(key);
21
+ }, TARGET_TTL_MS)
22
+ };
23
+ target.cleanupTimer.unref?.();
24
+ targets.set(key, target);
25
+ questionGatewayRuntime.registerChannelDelivery({
26
+ questionId: binding.questionId,
27
+ deliveryId: `whatsapp-reaction:${key}`,
28
+ finalize: () => {
29
+ target.terminal = true;
30
+ }
31
+ });
32
+ }
33
+ function buildKey(accountId, remoteJid, messageId) {
34
+ const parts = [
35
+ accountId,
36
+ remoteJid,
37
+ messageId
38
+ ].map((part) => part.trim());
39
+ return parts.every(Boolean) ? parts.join(":") : void 0;
40
+ }
41
+ function addCandidate(values, value) {
42
+ const normalized = value?.trim();
43
+ if (normalized && !values.includes(normalized)) values.push(normalized);
44
+ }
45
+ function listDeliveredIdentities(results) {
46
+ const identities = [];
47
+ const seen = /* @__PURE__ */ new Set();
48
+ const add = (messageId, remoteJid) => {
49
+ const id = messageId?.trim() ?? "";
50
+ const jid = remoteJid?.trim() ?? "";
51
+ const key = `${jid}:${id}`;
52
+ if (id && id !== "unknown" && jid && !seen.has(key)) {
53
+ seen.add(key);
54
+ identities.push({
55
+ messageId: id,
56
+ remoteJid: jid
57
+ });
58
+ }
59
+ };
60
+ for (const result of results) {
61
+ if (result.channel !== "whatsapp") continue;
62
+ add(result.messageId, result.toJid);
63
+ for (const raw of result.receipt?.raw ?? []) add(raw.messageId, raw.toJid);
64
+ for (const part of result.receipt?.parts ?? []) add(part.raw?.messageId ?? part.platformMessageId, part.raw?.toJid);
65
+ }
66
+ return identities;
67
+ }
68
+ function registerWhatsAppQuestionReactionTargetForDeliveredPayload(params) {
69
+ const binding = questionGatewayRuntime.readReactionBinding(params.payload);
70
+ if (params.target.channel !== "whatsapp" || !binding) return false;
71
+ const accountId = resolveWhatsAppAccount({
72
+ cfg: params.cfg,
73
+ accountId: params.target.accountId
74
+ }).accountId;
75
+ let registered = false;
76
+ for (const identity of listDeliveredIdentities(params.results)) {
77
+ const key = buildKey(accountId, identity.remoteJid, identity.messageId);
78
+ if (!key) continue;
79
+ storeTarget(key, binding);
80
+ registered = true;
81
+ }
82
+ return registered;
83
+ }
84
+ async function maybeResolveWhatsAppQuestionReaction(params) {
85
+ const reaction = params.msg.message?.reactionMessage;
86
+ const reactionKey = reaction?.text?.trim() ?? "";
87
+ const messageId = reaction?.key?.id?.trim() ?? "";
88
+ const optionIndex = questionGatewayRuntime.resolveReactionIndex(reactionKey);
89
+ if (optionIndex === void 0 || !messageId) return false;
90
+ const remoteJids = [];
91
+ addCandidate(remoteJids, reaction?.key?.remoteJid);
92
+ addCandidate(remoteJids, params.msg.key?.remoteJid);
93
+ const candidates = [];
94
+ for (const remoteJid of remoteJids) {
95
+ addCandidate(candidates, remoteJid);
96
+ for (const mapped of await params.resolveReactionTargetJids?.(remoteJid) ?? []) addCandidate(candidates, mapped);
97
+ }
98
+ let matched;
99
+ for (const remoteJid of candidates) {
100
+ const key = buildKey(params.accountId, remoteJid, messageId);
101
+ const target = key ? targets.get(key) : void 0;
102
+ if (key && target) {
103
+ matched = {
104
+ key,
105
+ target
106
+ };
107
+ break;
108
+ }
109
+ }
110
+ if (!matched) return false;
111
+ if (matched.target.expiresAtMs <= Date.now() || matched.target.terminal) {
112
+ matched.target.terminal = true;
113
+ params.logDebug?.(`whatsapp: stale question reaction ignored id=${matched.target.questionId}`);
114
+ return true;
115
+ }
116
+ const optionValue = matched.target.optionValues[optionIndex];
117
+ if (!optionValue) {
118
+ params.logDebug?.(`whatsapp: out-of-range question reaction ignored id=${matched.target.questionId}`);
119
+ return true;
120
+ }
121
+ try {
122
+ const result = await questionGatewayRuntime.resolveReaction({
123
+ cfg: params.cfg,
124
+ questionId: matched.target.questionId,
125
+ optionValue,
126
+ senderId: params.senderId,
127
+ gatewayUrl: params.gatewayUrl,
128
+ clientDisplayName: `WhatsApp question (${params.senderId})`
129
+ });
130
+ matched.target.terminal = result?.status === "answered" || result?.status === "already-terminal";
131
+ if (result?.status === "already-terminal") params.logDebug?.(`whatsapp: stale question reaction ignored id=${matched.target.questionId}`);
132
+ } catch (error) {
133
+ params.logDebug?.(`whatsapp: question reaction failed id=${matched.target.questionId}: ${String(error)}`);
134
+ }
135
+ return true;
136
+ }
137
+ function clearWhatsAppQuestionReactionTargetsForTest() {
138
+ for (const target of targets.values()) clearTimeout(target.cleanupTimer);
139
+ targets.clear();
140
+ }
141
+ //#endregion
142
+ export { question_reactions_exports as n, maybeResolveWhatsAppQuestionReaction as t };
@@ -1,16 +1,16 @@
1
1
  import { o as resolveWebCredsBackupPath, r as hasWebCredsSync, s as resolveWebCredsPath } from "./creds-files-B1kSWtBg.js";
2
- import { n as whatsAppActionRuntime, t as handleWhatsAppAction } from "./action-runtime-Bf-12TIH.js";
3
- import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-D9Ydnn4o.js";
2
+ import { n as whatsAppActionRuntime, t as handleWhatsAppAction } from "./action-runtime-p0V390gA.js";
3
+ import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-SB-4JrVZ.js";
4
4
  import { r as setWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
5
5
  import { startWebLoginWithQr, waitForWebLogin } from "./login-qr-runtime.js";
6
6
  import { t as createWhatsAppLoginTool } from "./agent-tools-login-DfyToBpG.js";
7
7
  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-BQi9pZqQ.js";
8
8
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
9
- import { _ as extractLocationData, b as extractText, m as extractContactContext, v as extractMediaPlaceholder } from "./send-api-W33lI2nR.js";
10
- import { n as newConnectionId, r as waitForWaConnection, t as createWaSocket } from "./session-mxWFn0eS.js";
9
+ import { n as resolveWebAccountId, t as getActiveWebListener } from "./active-listener-BbdlXooT.js";
10
+ import { a as waitForWaConnection, i as newConnectionId, r as createWaSocket } from "./socket-close-jNGyZY4T.js";
11
11
  import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
12
- import { n as resolveWebAccountId, t as getActiveWebListener } from "./active-listener-5kHap439.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-BKtA0lzq.js";
14
- import { t as loginWeb } from "./login-DGIoToX8.js";
12
+ import { _ as extractLocationData, b as extractText, m as extractContactContext, v as extractMediaPlaceholder } from "./send-api-BG0MgoFI.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-_jqDKBXj.js";
14
+ import { t as loginWeb } from "./login-ifOXCBjo.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 };
@@ -1,8 +1,7 @@
1
- import { t as resolveMergedWhatsAppAccountConfig } from "./account-config-LiT9wWH9.js";
1
+ import { a as resolveWhatsAppAccount, c as resolveMergedWhatsAppAccountConfig, s as resolveWhatsAppMediaMaxBytes } from "./accounts-ejK8sXPU.js";
2
2
  import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
3
- import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CKOFMPf-.js";
4
3
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-DSZnHkKF.js";
5
- import { n as getWhatsAppConnectionController } from "./connection-controller-runtime-context-CbTsqmPT.js";
4
+ import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-9mEeAvu1.js";
6
5
  import "./normalize-C-Z16y7Q.js";
7
6
  import { i as markdownToWhatsApp, s as toWhatsappJid } from "./targets-runtime-Bsh0HR9y.js";
8
7
  import { a as sanitizeAssistantVisibleTextWithProfile, i as sanitizeAssistantVisibleText, o as stripToolCallXmlTags } from "./text-runtime-BTGkMN0o.js";
@@ -1,16 +1,16 @@
1
1
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-DSZnHkKF.js";
2
- import { l as resolveWhatsAppDocumentFileName } from "./send-D9Ydnn4o.js";
2
+ import { l as resolveWhatsAppDocumentFileName } from "./send-SB-4JrVZ.js";
3
3
  import "./normalize-C-Z16y7Q.js";
4
4
  import { c as toWhatsappJidWithLid, r as jidToE164, s as toWhatsappJid } from "./targets-runtime-Bsh0HR9y.js";
5
5
  import "./text-runtime-BTGkMN0o.js";
6
- import { i as buildQuotedMessageOptions } from "./group-session-key-CLTZeH_F.js";
6
+ import { i as buildQuotedMessageOptions } from "./group-session-key-Dh8WZun_.js";
7
7
  import { c as resolveComparableIdentity } from "./identity-D7L8LCuH.js";
8
8
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
9
9
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
10
10
  import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound";
11
11
  import { getImageMetadata, resizeToJpeg } from "openclaw/plugin-sdk/media-runtime";
12
- import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
13
12
  import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
13
+ import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
14
14
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
15
15
  //#region extensions/whatsapp/src/vcard.ts
16
16
  const ALLOWED_VCARD_KEYS = /* @__PURE__ */ new Set([
@@ -1,5 +1,5 @@
1
+ import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-ejK8sXPU.js";
1
2
  import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./account-ids-CB5SOWjc.js";
2
- import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-CKOFMPf-.js";
3
3
  import { a as normalizeWhatsAppAllowFromEntries } from "./normalize-target-DSZnHkKF.js";
4
4
  import { t as WhatsAppChannelConfigSchema } from "./config-schema-BZ8h1hWA.js";
5
5
  import { n as whatsappDoctor } from "./doctor-By3SB4lm.js";
@@ -84,10 +84,10 @@ async function applyWhatsAppSecurityConfigFixes(params) {
84
84
  //#region extensions/whatsapp/src/shared.ts
85
85
  const WHATSAPP_CHANNEL = "whatsapp";
86
86
  async function loadWhatsAppChannelRuntime() {
87
- return await import("./channel.runtime-BPrKzcYd.js");
87
+ return await import("./channel.runtime-CjkhQzk8.js");
88
88
  }
89
89
  async function loadWhatsAppSetupSurface() {
90
- return await import("./setup-surface-5EUmMLHS.js");
90
+ return await import("./setup-surface-DhnXEZir.js");
91
91
  }
92
92
  const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
93
93
  const whatsappConfigAdapter = createScopedChannelConfigAdapter({
@@ -1,8 +1,8 @@
1
+ import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-ejK8sXPU.js";
1
2
  import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
2
3
  import { r as hasWebCredsSync } from "./creds-files-B1kSWtBg.js";
3
- import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-CKOFMPf-.js";
4
4
  import { a as normalizeWhatsAppAllowFromEntries, o as normalizeWhatsAppAllowFromEntry } from "./normalize-target-DSZnHkKF.js";
5
- import { t as whatsappSetupAdapter } from "./setup-core-BXDFGqky.js";
5
+ import { t as whatsappSetupAdapter } from "./setup-core-BbIgEl2M.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-DGIoToX8.js").then((n) => n.n));
298
+ ({loginWeb} = await import("./login-ifOXCBjo.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-eX64WZDx.js";
1
+ import { t as whatsappSetupPlugin } from "./channel.setup-BMWE9LmN.js";
2
2
  export { whatsappSetupPlugin };
@@ -1,5 +1,5 @@
1
+ import { o as resolveWhatsAppAuthDir } from "./accounts-ejK8sXPU.js";
1
2
  import { t as listAccountIds } from "./account-ids-CB5SOWjc.js";
2
- import { o as resolveWhatsAppAuthDir } from "./accounts-CKOFMPf-.js";
3
3
  import { a as formatWhatsAppWebAuthStatusState, h as readWebAuthState } from "./auth-store-BQi9pZqQ.js";
4
4
  import { DEFAULT_ACCOUNT_ID, createSetupTranslator, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
5
5
  //#region extensions/whatsapp/src/setup-surface.ts
@@ -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-DzOHXnPj.js")).finalizeWhatsAppSetup(params),
39
+ finalize: async (params) => await (await import("./setup-finalize-Cc-4iWGj.js")).finalizeWhatsAppSetup(params),
40
40
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
41
41
  onAccountRecorded: (accountId, options) => {
42
42
  options?.onAccountId?.(channel, accountId);
@@ -6,9 +6,161 @@ import { VERSION, formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
6
6
  import { danger, getChildLogger, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
7
7
  import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
8
8
  import { ensureDir, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
9
+ import fs from "node:fs/promises";
9
10
  import { parseStrictPositiveInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
11
+ import { FILE_LOCK_STALE_ERROR_CODE, FILE_LOCK_TIMEOUT_ERROR_CODE, acquireFileLock } from "openclaw/plugin-sdk/file-lock";
10
12
  import { randomUUID } from "node:crypto";
11
13
  import { createHttp1EnvHttpProxyAgent, createHttp1ProxyAgent, createNodeProxyAgent } from "openclaw/plugin-sdk/fetch-runtime";
14
+ //#region extensions/whatsapp/src/connection-owner.ts
15
+ const WHATSAPP_CONNECTION_OWNER_BUSY_CODE = "whatsapp_connection_owner_busy";
16
+ var WhatsAppConnectionOwnerBusyError = class extends Error {
17
+ constructor(authDir, options) {
18
+ super("Another process owns this WhatsApp connection.", options);
19
+ this.authDir = authDir;
20
+ this.code = WHATSAPP_CONNECTION_OWNER_BUSY_CODE;
21
+ this.name = "WhatsAppConnectionOwnerBusyError";
22
+ }
23
+ };
24
+ const OWNER_LOCK_STALE_MS = 5 * 6e4;
25
+ const GATEWAY_LOCAL_OWNER_WAIT_MS = 15e4;
26
+ const processOwners = /* @__PURE__ */ new Map();
27
+ function ownershipCancelledError(signal) {
28
+ const reason = signal?.reason;
29
+ return reason instanceof Error ? reason : new Error("WhatsApp connection ownership cancelled", reason === void 0 ? {} : { cause: reason });
30
+ }
31
+ async function waitForAbortableDelay(delayMs, signal) {
32
+ if (signal?.aborted) throw ownershipCancelledError(signal);
33
+ await new Promise((resolve, reject) => {
34
+ const onAbort = () => {
35
+ clearTimeout(timer);
36
+ reject(ownershipCancelledError(signal));
37
+ };
38
+ const timer = setTimeout(() => {
39
+ signal?.removeEventListener("abort", onAbort);
40
+ resolve();
41
+ }, delayMs);
42
+ signal?.addEventListener("abort", onAbort, { once: true });
43
+ });
44
+ }
45
+ async function reserveProcessOwner(params) {
46
+ while (true) {
47
+ if (params.signal?.aborted) throw ownershipCancelledError(params.signal);
48
+ const current = processOwners.get(params.ownerPath);
49
+ if (!current) {
50
+ let resolveReleased = () => {};
51
+ const owner = {
52
+ released: new Promise((resolve) => {
53
+ resolveReleased = resolve;
54
+ }),
55
+ resolveReleased,
56
+ token: Symbol(params.ownerPath)
57
+ };
58
+ processOwners.set(params.ownerPath, owner);
59
+ return owner;
60
+ }
61
+ if (!params.waitForLocalOwner) throw new WhatsAppConnectionOwnerBusyError(params.authDir);
62
+ let timer;
63
+ let onAbort;
64
+ const outcome = await Promise.race([
65
+ current.released.then(() => "released"),
66
+ new Promise((resolve) => {
67
+ timer = setTimeout(() => resolve("timed_out"), GATEWAY_LOCAL_OWNER_WAIT_MS);
68
+ timer.unref?.();
69
+ }),
70
+ new Promise((resolve) => {
71
+ onAbort = () => resolve("aborted");
72
+ params.signal?.addEventListener("abort", onAbort, { once: true });
73
+ })
74
+ ]).finally(() => {
75
+ if (timer) clearTimeout(timer);
76
+ if (onAbort) params.signal?.removeEventListener("abort", onAbort);
77
+ });
78
+ if (outcome === "aborted") throw ownershipCancelledError(params.signal);
79
+ if (outcome === "timed_out") throw new WhatsAppConnectionOwnerBusyError(params.authDir);
80
+ }
81
+ }
82
+ function abandonProcessOwner(ownerPath, owner) {
83
+ if (processOwners.get(ownerPath)?.token !== owner.token) return;
84
+ processOwners.delete(ownerPath);
85
+ owner.resolveReleased();
86
+ }
87
+ async function acquireOwnerLease(params) {
88
+ const resolvedOwnerPath = resolveUserPath(params.authDir);
89
+ await fs.mkdir(resolvedOwnerPath, { recursive: true });
90
+ const ownerPath = await fs.realpath(resolvedOwnerPath);
91
+ const processOwner = await reserveProcessOwner({
92
+ authDir: params.authDir,
93
+ ownerPath,
94
+ signal: params.signal,
95
+ waitForLocalOwner: params.waitForLocalOwner
96
+ });
97
+ let fileLock;
98
+ let attempt = 0;
99
+ while (true) {
100
+ if (params.signal?.aborted) {
101
+ abandonProcessOwner(ownerPath, processOwner);
102
+ throw ownershipCancelledError(params.signal);
103
+ }
104
+ try {
105
+ fileLock = await acquireFileLock(ownerPath, {
106
+ retries: {
107
+ retries: 0,
108
+ factor: 1,
109
+ minTimeout: 1,
110
+ maxTimeout: 1
111
+ },
112
+ stale: OWNER_LOCK_STALE_MS,
113
+ staleRecovery: "remove-if-unchanged"
114
+ });
115
+ break;
116
+ } catch (error) {
117
+ const code = error.code;
118
+ if (code === FILE_LOCK_STALE_ERROR_CODE) {
119
+ abandonProcessOwner(ownerPath, processOwner);
120
+ throw new WhatsAppConnectionOwnerBusyError(params.authDir, { cause: error });
121
+ }
122
+ if (code !== FILE_LOCK_TIMEOUT_ERROR_CODE || attempt >= params.retries) {
123
+ abandonProcessOwner(ownerPath, processOwner);
124
+ if (code === FILE_LOCK_TIMEOUT_ERROR_CODE) throw new WhatsAppConnectionOwnerBusyError(params.authDir, { cause: error });
125
+ throw error;
126
+ }
127
+ const delayMs = Math.min(100 * 1.5 ** attempt, 1e3);
128
+ attempt += 1;
129
+ await waitForAbortableDelay(delayMs, params.signal).catch((delayError) => {
130
+ abandonProcessOwner(ownerPath, processOwner);
131
+ throw delayError;
132
+ });
133
+ }
134
+ }
135
+ let releasePromise = null;
136
+ return { release: async () => {
137
+ if (!releasePromise) releasePromise = fileLock.release().then(() => {
138
+ abandonProcessOwner(ownerPath, processOwner);
139
+ }).catch((releaseError) => {
140
+ releasePromise = null;
141
+ throw releaseError;
142
+ });
143
+ await releasePromise;
144
+ } };
145
+ }
146
+ /** Gateway owner waits for a bounded standalone lookup to finish before startup. */
147
+ async function acquireWhatsAppGatewayConnectionOwner(authDir, signal) {
148
+ return await acquireOwnerLease({
149
+ authDir,
150
+ retries: 150,
151
+ signal,
152
+ waitForLocalOwner: true
153
+ });
154
+ }
155
+ /** Standalone lookup fails quickly when a gateway already owns the account. */
156
+ async function acquireWhatsAppStandaloneConnectionOwner(authDir) {
157
+ return await acquireOwnerLease({
158
+ authDir,
159
+ retries: 3,
160
+ waitForLocalOwner: false
161
+ });
162
+ }
163
+ //#endregion
12
164
  //#region extensions/whatsapp/src/socket-timing.ts
13
165
  const socketSendMessageQueueTails = /* @__PURE__ */ new WeakMap();
14
166
  const DEFAULT_WHATSAPP_SOCKET_TIMING = {
@@ -170,6 +322,9 @@ function resolveEnvWaWebSocketUrl() {
170
322
  * Consumers can opt into QR printing for interactive login flows.
171
323
  */
172
324
  async function createWaSocket(printQr, verbose, opts = {}) {
325
+ return await createWaSocketInternal(printQr, verbose, opts, "normal");
326
+ }
327
+ async function createWaSocketInternal(printQr, verbose, opts, receiveMode) {
173
328
  const logger = toPinoLikeLogger(getChildLogger({ module: "baileys" }, { level: verbose ? "info" : "silent" }), verbose ? "info" : "silent");
174
329
  const authDir = resolveUserPath(opts.authDir ?? resolveDefaultWebAuthDir());
175
330
  await rejectUnsafeWebCredsPath(authDir);
@@ -261,6 +416,7 @@ async function createWaSocket(printQr, verbose, opts = {}) {
261
416
  VERSION
262
417
  ],
263
418
  syncFullHistory: false,
419
+ fireInitQueries: receiveMode !== "directory",
264
420
  markOnlineOnConnect: false,
265
421
  ...socketTiming,
266
422
  agent,
@@ -270,6 +426,19 @@ async function createWaSocket(printQr, verbose, opts = {}) {
270
426
  ...opts.getMessage ? { getMessage: opts.getMessage } : {},
271
427
  ...opts.cachedGroupMetadata ? { cachedGroupMetadata: opts.cachedGroupMetadata } : {}
272
428
  });
429
+ if (receiveMode === "directory") for (const event of [
430
+ "CB:message",
431
+ "CB:call",
432
+ "CB:receipt",
433
+ "CB:notification",
434
+ "CB:ack,class:message",
435
+ "CB:presence",
436
+ "CB:chatstate",
437
+ "CB:ib,,dirty",
438
+ "CB:ib,,offline_preview",
439
+ "CB:ib,,offline",
440
+ "CB:ib,,edge_routing"
441
+ ]) sock.ws.removeAllListeners(event);
273
442
  socketRef.current = sock;
274
443
  if (pendingSocketAbort) abortSocketAfterCredentialPersistenceFailure(sock, pendingSocketAbort.error);
275
444
  sock.ev.on("creds.update", () => enqueueSaveCreds(authDir, saveCreds, sessionLogger, {
@@ -303,6 +472,9 @@ async function createWaSocket(printQr, verbose, opts = {}) {
303
472
  });
304
473
  return sock;
305
474
  }
475
+ async function createWaDirectorySocket(authDir) {
476
+ return await createWaSocketInternal(false, false, { authDir }, "directory");
477
+ }
306
478
  async function resolveEnvProxyAgent(logger) {
307
479
  try {
308
480
  const agent = createNodeProxyAgent({
@@ -400,4 +572,52 @@ function createConnectionTimeoutError(timeoutMs) {
400
572
  return error;
401
573
  }
402
574
  //#endregion
403
- export { createWhatsAppSocketOperationTimeoutAdapter as a, resolveWhatsAppSocketTiming as c, DEFAULT_WHATSAPP_SOCKET_TIMING as i, withWhatsAppSocketOperationTimeout as l, newConnectionId as n, isWhatsAppSocketOperationTimeoutError as o, waitForWaConnection as r, resolveWhatsAppSocketOperationTimeoutMs as s, createWaSocket as t, renderQrTerminal as u };
575
+ //#region extensions/whatsapp/src/socket-close.ts
576
+ const SOCKET_CLOSE_TIMEOUT_MS = 15e3;
577
+ async function withCloseTimeout(task, operationName) {
578
+ let timer;
579
+ await Promise.race([task, new Promise((_resolve, reject) => {
580
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`WhatsApp ${operationName} timed out`)), SOCKET_CLOSE_TIMEOUT_MS);
581
+ })]).finally(() => {
582
+ if (timer) clearTimeout(timer);
583
+ });
584
+ }
585
+ async function waitForTransportClose(ws, operation, operationName) {
586
+ let onClose;
587
+ const closed = ws.isClosed ? Promise.resolve() : new Promise((resolve) => {
588
+ onClose = resolve;
589
+ ws.once("close", onClose);
590
+ if (ws.isClosed) onClose();
591
+ });
592
+ try {
593
+ await withCloseTimeout(Promise.all([Promise.resolve(operation), closed]), operationName);
594
+ } finally {
595
+ if (onClose) ws.removeListener("close", onClose);
596
+ }
597
+ }
598
+ /** Close Baileys and verify the transport state before connection ownership moves. */
599
+ async function closeWhatsAppSocketAndWait(sock, reason) {
600
+ const errors = [];
601
+ try {
602
+ const endResult = sock.end(new Error(reason));
603
+ if (sock.ws.isClosed) {
604
+ await waitForTransportClose(sock.ws, endResult, "socket end");
605
+ return;
606
+ }
607
+ if (sock.ws.isClosing) await waitForTransportClose(sock.ws, endResult, "socket end");
608
+ else await withCloseTimeout(Promise.resolve(endResult), "socket end");
609
+ } catch (error) {
610
+ errors.push(error);
611
+ }
612
+ if (sock.ws.isClosed) return;
613
+ try {
614
+ const closeResult = sock.ws.close();
615
+ await waitForTransportClose(sock.ws, closeResult, "WebSocket close");
616
+ } catch (error) {
617
+ errors.push(error);
618
+ }
619
+ if (sock.ws.isClosed) return;
620
+ throw new AggregateError(errors, "WhatsApp socket close could not be confirmed");
621
+ }
622
+ //#endregion
623
+ export { waitForWaConnection as a, isWhatsAppSocketOperationTimeoutError as c, withWhatsAppSocketOperationTimeout as d, renderQrTerminal as f, acquireWhatsAppStandaloneConnectionOwner as h, newConnectionId as i, resolveWhatsAppSocketOperationTimeoutMs as l, acquireWhatsAppGatewayConnectionOwner as m, createWaDirectorySocket as n, DEFAULT_WHATSAPP_SOCKET_TIMING as o, WhatsAppConnectionOwnerBusyError as p, createWaSocket as r, createWhatsAppSocketOperationTimeoutAdapter as s, closeWhatsAppSocketAndWait as t, resolveWhatsAppSocketTiming as u };
@@ -1,15 +1,8 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
3
+ import { fileExists } from "openclaw/plugin-sdk/security-runtime";
4
4
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
5
5
  //#region extensions/whatsapp/src/state-migrations.ts
6
- function fileExists(pathValue) {
7
- try {
8
- return !statRegularFileSync(pathValue).missing;
9
- } catch {
10
- return false;
11
- }
12
- }
13
6
  function isLegacyWhatsAppAuthFile(name) {
14
7
  if (name === "creds.json" || name === "creds.json.bak") return true;
15
8
  if (!name.endsWith(".json")) return false;
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@openclaw/whatsapp",
3
- "version": "2026.7.2-beta.1",
3
+ "version": "2026.7.2-beta.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/whatsapp",
9
- "version": "2026.7.2-beta.1",
9
+ "version": "2026.7.2-beta.3",
10
10
  "dependencies": {
11
11
  "audio-decode": "2.2.3",
12
12
  "baileys": "7.0.0-rc13",
13
13
  "typebox": "1.3.3"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.7.2-beta.1"
16
+ "openclaw": ">=2026.7.2-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.7.2-beta.1",
3
+ "version": "2026.7.2-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.3"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.7.2-beta.1"
16
+ "openclaw": ">=2026.7.2-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.7.2-beta.1"
59
+ "pluginApi": ">=2026.7.2-beta.3"
60
60
  },
61
61
  "build": {
62
- "openclawVersion": "2026.7.2-beta.1"
62
+ "openclawVersion": "2026.7.2-beta.3"
63
63
  },
64
64
  "release": {
65
65
  "publishToClawHub": true,
@@ -1,43 +0,0 @@
1
- import { DEFAULT_ACCOUNT_ID, mergeAccountConfig, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-core";
2
- //#region extensions/whatsapp/src/account-config.ts
3
- function resolveWhatsAppDefaultAccountSharedConfig(cfg) {
4
- const defaultAccount = resolveAccountEntry(cfg.channels?.whatsapp?.accounts, DEFAULT_ACCOUNT_ID);
5
- if (!defaultAccount) return;
6
- const { enabled: _ignoredEnabled, name: _ignoredName, authDir: _ignoredAuthDir, selfChatMode: _ignoredSelfChatMode, ...sharedDefaults } = defaultAccount;
7
- return sharedDefaults;
8
- }
9
- function resolveWhatsAppAccountConfigForTest(cfg, accountId) {
10
- return resolveAccountEntry(cfg.channels?.whatsapp?.accounts, accountId);
11
- }
12
- function resolveMergedNamedWhatsAppAccountConfig(params) {
13
- const rootCfg = params.cfg.channels?.whatsapp;
14
- const accountConfig = resolveWhatsAppAccountConfigForTest(params.cfg, params.accountId);
15
- return {
16
- ...mergeAccountConfig({
17
- channelConfig: rootCfg,
18
- accountConfig: void 0,
19
- omitKeys: ["defaultAccount"]
20
- }),
21
- ...resolveWhatsAppDefaultAccountSharedConfig(params.cfg),
22
- ...accountConfig
23
- };
24
- }
25
- function resolveMergedWhatsAppAccountConfig(params) {
26
- const rootCfg = params.cfg.channels?.whatsapp;
27
- const accountId = params.accountId?.trim() || rootCfg?.defaultAccount || DEFAULT_ACCOUNT_ID;
28
- const base = resolveMergedAccountConfig({
29
- channelConfig: rootCfg,
30
- accounts: rootCfg?.accounts,
31
- accountId,
32
- omitKeys: ["defaultAccount"]
33
- });
34
- return {
35
- accountId,
36
- ...accountId === DEFAULT_ACCOUNT_ID ? base : resolveMergedNamedWhatsAppAccountConfig({
37
- cfg: params.cfg,
38
- accountId
39
- })
40
- };
41
- }
42
- //#endregion
43
- export { resolveMergedWhatsAppAccountConfig as t };
package/dist/constants.js DELETED
@@ -1,2 +0,0 @@
1
- import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
2
- export { DEFAULT_WEB_MEDIA_BYTES };