@openclaw/whatsapp 2026.9.8 → 2026.10.1-beta.1

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 (44) hide show
  1. package/dist/.setup/{accounts-CtRPAQCL.mjs → accounts-F0vHuze2.mjs} +3 -11
  2. package/dist/.setup/{action-runtime-DeVk5V7r.mjs → action-runtime-CE3zGstN.mjs} +4 -5
  3. package/dist/.setup/{agent-tools-login-CL5C1rA2.mjs → agent-tools-login-B7VEvJO7.mjs} +17 -36
  4. package/dist/.setup/{approval-handler.runtime-CjqMlPOX.mjs → approval-handler.runtime-CKposl6w.mjs} +3 -3
  5. package/dist/.setup/{approval-reactions-DiOFvb1s.mjs → approval-reactions-CD6CDF9k.mjs} +9 -40
  6. package/dist/.setup/{auth-store-BBonwKks.mjs → auth-store-D2rN-hP5.mjs} +12 -19
  7. package/dist/.setup/{channel-CJFE0kD_.mjs → channel-DNgKHMCz.mjs} +15 -24
  8. package/dist/.setup/{channel-react-action-2r_hjLZV.mjs → channel-react-action-BaBV9X6M.mjs} +3 -3
  9. package/dist/.setup/{channel.runtime-C94goLw9.mjs → channel.runtime-BniF8Swm.mjs} +4 -4
  10. package/dist/.setup/{channel.setup-CfN1UfdY.mjs → channel.setup-CsS_NAKQ.mjs} +1 -1
  11. package/dist/.setup/{connection-controller-CHo1oRJA.mjs → connection-controller-COkcq48K.mjs} +5 -14
  12. package/dist/.setup/{creds-files-CDATZWWM.mjs → creds-files-B8WcOAmE.mjs} +1 -10
  13. package/dist/.setup/{directory-config-DUiJ5d8I.mjs → directory-config-RYQomHyB.mjs} +6 -14
  14. package/dist/.setup/{doctor-iMwg0Vhr.mjs → doctor-DKzeBEmH.mjs} +7 -1
  15. package/dist/.setup/{group-config-path-DZr3ucUO.mjs → group-config-path-BPrak_bJ.mjs} +3 -9
  16. package/dist/.setup/{group-session-key-2-mja4OF.mjs → group-session-key-BV8Le9r8.mjs} +5 -14
  17. package/dist/.setup/{login-BFlvSh9h.mjs → login-CV_4V021.mjs} +4 -4
  18. package/dist/.setup/{login-qr-Ayb8i25J.mjs → login-qr-DFhGc5yH.mjs} +11 -27
  19. package/dist/.setup/{monitor-CGpV2VNN.mjs → monitor-B5DS4PDV.mjs} +65 -186
  20. package/dist/.setup/{question-reactions-CPyyi5AY.mjs → question-reactions-LMeijbaq.mjs} +7 -37
  21. package/dist/.setup/{resolve-outbound-target-C2eUvsTc.mjs → resolve-outbound-target-CzyNKmrl.mjs} +2 -13
  22. package/dist/.setup/{send-BGdvf5cC.mjs → send-CGUsUdoX.mjs} +31 -6
  23. package/dist/.setup/{send-api-H4AkQJjT.mjs → send-api-B03VDwZ1.mjs} +35 -53
  24. package/dist/.setup/{setup-finalize-DmPX4EaW.mjs → setup-finalize-GtBmrO8p.mjs} +8 -20
  25. package/dist/.setup/setup-plugin-api-Bsp7jI-r.mjs +3 -0
  26. package/dist/.setup/{setup-surface-43fj197P.mjs → setup-surface-BuA39k6K.mjs} +3 -3
  27. package/dist/.setup/{shared-DgOG5Iqr.mjs → shared-CP_zs2U7.mjs} +12 -13
  28. package/dist/.setup/{socket-close-CT__V2fz.mjs → socket-close-DU7eerAa.mjs} +26 -54
  29. package/dist/.setup/{targets-runtime-DmiqXIXO.mjs → targets-runtime-Dls-Xh4F.mjs} +10 -34
  30. package/dist/action-runtime-api.js +1 -1
  31. package/dist/action-runtime.runtime.js +1 -1
  32. package/dist/agent-tools-api.js +3 -3
  33. package/dist/api.js +10 -10
  34. package/dist/auth-presence.js +3 -4
  35. package/dist/channel-plugin-api.js +1 -1
  36. package/dist/directory-contract-api.js +1 -1
  37. package/dist/doctor-contract-api.js +234 -59
  38. package/dist/light-runtime-api.js +2 -2
  39. package/dist/login-qr-runtime.js +1 -1
  40. package/dist/runtime-api.js +10 -10
  41. package/dist/setup-entry.js +1 -1
  42. package/dist/setup-plugin-api.js +1 -1
  43. package/package.json +5 -5
  44. package/dist/.setup/setup-plugin-api-vJlxKzca.mjs +0 -3
@@ -1,14 +1,16 @@
1
- import { c as resolveWebCredsPath, s as resolveWebCredsBackupPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-CDATZWWM.mjs";
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-BBonwKks.mjs";
1
+ import { a as readWebCredsJsonRawSync, o as resolveWebCredsBackupPath, s as resolveWebCredsPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-B8WcOAmE.mjs";
2
+ import { M as writeCredsJsonAtomically, N as writeWebCredsRawAtomically, j as waitForCredsSaveQueueWithTimeout, k as enqueueCredsSave, v as resolveDefaultWebAuthDir, y as restoreCredsFromBackupIfNeeded } from "./auth-store-D2rN-hP5.mjs";
3
3
  import { n as getStatusCode } from "./session-errors-B8UcAnZN.mjs";
4
4
  import { a as makeWASocket, i as makeCacheableSignalKeyStore, n as createBaileysSignalRepository, r as fetchLatestBaileysVersion, s as useMultiFileAuthState } from "./session.runtime-st1konFI.mjs";
5
5
  import { VERSION, formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
6
6
  import { toErrorObject } from "openclaw/plugin-sdk/error-runtime";
7
- import { computeBackoff, danger, getChildLogger, sleepWithAbort, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
7
+ import { computeBackoff, danger, getChildLogger, sleepWithAbort, sleepWithAbort as sleepWithAbort$1, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
8
8
  import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
9
9
  import { clamp, ensureDir, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
10
10
  import fs from "node:fs/promises";
11
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
11
12
  import { parseStrictPositiveInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
13
+ import { raceWithTimeout, withTimeout } from "openclaw/plugin-sdk/time-runtime";
12
14
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
13
15
  import { FILE_LOCK_STALE_ERROR_CODE, FILE_LOCK_TIMEOUT_ERROR_CODE, acquireFileLock } from "openclaw/plugin-sdk/file-lock";
14
16
  import { createNodeProxyAgent } from "openclaw/plugin-sdk/fetch-runtime";
@@ -30,20 +32,6 @@ function ownershipCancelledError(signal) {
30
32
  const reason = signal?.reason;
31
33
  return reason instanceof Error ? reason : new Error("WhatsApp connection ownership cancelled", reason === void 0 ? {} : { cause: reason });
32
34
  }
33
- async function waitForAbortableDelay(delayMs, signal) {
34
- if (signal?.aborted) throw ownershipCancelledError(signal);
35
- await new Promise((resolve, reject) => {
36
- const onAbort = () => {
37
- clearTimeout(timer);
38
- reject(ownershipCancelledError(signal));
39
- };
40
- const timer = setTimeout(() => {
41
- signal?.removeEventListener("abort", onAbort);
42
- resolve();
43
- }, delayMs);
44
- signal?.addEventListener("abort", onAbort, { once: true });
45
- });
46
- }
47
35
  async function reserveProcessOwner(params) {
48
36
  while (true) {
49
37
  if (params.signal?.aborted) throw ownershipCancelledError(params.signal);
@@ -126,9 +114,9 @@ async function acquireOwnerLease(params) {
126
114
  }
127
115
  const delayMs = Math.min(100 * 1.5 ** attempt, 1e3);
128
116
  attempt += 1;
129
- await waitForAbortableDelay(delayMs, params.signal).catch((delayError) => {
117
+ await sleepWithAbort(delayMs, params.signal).catch(() => {
130
118
  abandonProcessOwner(ownerPath, processOwner);
131
- throw delayError;
119
+ throw ownershipCancelledError(params.signal);
132
120
  });
133
121
  }
134
122
  }
@@ -162,7 +150,7 @@ async function acquireWhatsAppStandaloneConnectionOwner(authDir) {
162
150
  }
163
151
  //#endregion
164
152
  //#region extensions/whatsapp/src/socket-timing.ts
165
- const socketSendMessageQueueTails = /* @__PURE__ */ new WeakMap();
153
+ const socketSendMessageQueues = /* @__PURE__ */ new WeakMap();
166
154
  const DEFAULT_WHATSAPP_SOCKET_TIMING = {
167
155
  keepAliveIntervalMs: 25e3,
168
156
  connectTimeoutMs: 6e4,
@@ -190,29 +178,20 @@ function isWhatsAppSocketOperationTimeoutError(error) {
190
178
  function resolveWhatsAppSocketOperationTimeoutMs(timeoutMs) {
191
179
  return resolveTimerTimeoutMs(timeoutMs, DEFAULT_WHATSAPP_SOCKET_TIMING.defaultQueryTimeoutMs);
192
180
  }
193
- async function runSerializedSocketSendMessage(sock, run) {
194
- const result = (socketSendMessageQueueTails.get(sock) ?? Promise.resolve()).then(run);
195
- const tail = result.then(() => void 0, () => void 0);
196
- socketSendMessageQueueTails.set(sock, tail);
197
- tail.then(() => {
198
- if (socketSendMessageQueueTails.get(sock) === tail) socketSendMessageQueueTails.delete(sock);
199
- });
200
- return await result;
181
+ function runSerializedSocketSendMessage(sock, run) {
182
+ let queue = socketSendMessageQueues.get(sock);
183
+ if (!queue) {
184
+ queue = new KeyedAsyncQueue();
185
+ socketSendMessageQueues.set(sock, queue);
186
+ }
187
+ return queue.enqueue("sendMessage", run);
201
188
  }
202
189
  async function withWhatsAppSocketOperationTimeout(operation, promise, timeoutMs, onTimeout) {
203
190
  const resolvedTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(timeoutMs);
204
- let timeout = null;
205
- try {
206
- return await Promise.race([promise, new Promise((_, reject) => {
207
- timeout = setTimeout(() => {
208
- onTimeout?.();
209
- reject(new WhatsAppSocketOperationTimeoutError(operation, resolvedTimeoutMs));
210
- }, resolvedTimeoutMs);
211
- timeout.unref?.();
212
- })]);
213
- } finally {
214
- if (timeout) clearTimeout(timeout);
215
- }
191
+ return await raceWithTimeout(promise, resolvedTimeoutMs, () => {
192
+ onTimeout?.();
193
+ throw new WhatsAppSocketOperationTimeoutError(operation, resolvedTimeoutMs);
194
+ }, { ref: false });
216
195
  }
217
196
  function createWhatsAppSocketOperationTimeoutAdapter(sock, timeoutMs, hooks) {
218
197
  const operationTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(timeoutMs);
@@ -241,16 +220,14 @@ const DEFAULT_RECONNECT_POLICY = {
241
220
  jitter: .25,
242
221
  maxAttempts: 12
243
222
  };
244
- function resolveHeartbeatSeconds(cfg, overrideSeconds) {
245
- const candidate = overrideSeconds;
246
- if (typeof candidate === "number" && candidate > 0) return candidate;
223
+ function resolveHeartbeatSeconds(overrideSeconds) {
224
+ if (typeof overrideSeconds === "number" && overrideSeconds > 0) return overrideSeconds;
247
225
  return DEFAULT_HEARTBEAT_SECONDS;
248
226
  }
249
- function resolveReconnectPolicy(cfg, overrides) {
250
- const overrideConfig = overrides ?? {};
227
+ function resolveReconnectPolicy(overrides) {
251
228
  const merged = {
252
229
  ...DEFAULT_RECONNECT_POLICY,
253
- ...overrideConfig
230
+ ...overrides
254
231
  };
255
232
  merged.initialMs = Math.max(250, merged.initialMs);
256
233
  merged.maxMs = Math.max(merged.initialMs, merged.maxMs);
@@ -276,7 +253,7 @@ async function safeSaveCreds(params) {
276
253
  try {
277
254
  const credsPath = resolveWebCredsPath(params.authDir);
278
255
  const backupPath = resolveWebCredsBackupPath(params.authDir);
279
- const raw = readCredsJsonRaw(credsPath);
256
+ const raw = readWebCredsJsonRawSync(credsPath);
280
257
  if (raw) try {
281
258
  JSON.parse(raw);
282
259
  backup = {
@@ -559,12 +536,7 @@ function createConnectionTimeoutError(timeoutMs) {
559
536
  //#region extensions/whatsapp/src/socket-close.ts
560
537
  const SOCKET_CLOSE_TIMEOUT_MS = 15e3;
561
538
  async function withCloseTimeout(task, operationName) {
562
- let timer;
563
- await Promise.race([task, new Promise((_resolve, reject) => {
564
- timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`WhatsApp ${operationName} timed out`)), SOCKET_CLOSE_TIMEOUT_MS);
565
- })]).finally(() => {
566
- if (timer) clearTimeout(timer);
567
- });
539
+ await withTimeout(task, SOCKET_CLOSE_TIMEOUT_MS, { message: `WhatsApp ${operationName} timed out` });
568
540
  }
569
541
  async function waitForTransportClose(ws, operation, operationName) {
570
542
  let onClose;
@@ -604,4 +576,4 @@ async function closeWhatsAppSocketAndWait(sock, reason) {
604
576
  throw new AggregateError(errors, "WhatsApp socket close could not be confirmed");
605
577
  }
606
578
  //#endregion
607
- export { renderQrTerminal as _, DEFAULT_RECONNECT_POLICY as a, acquireWhatsAppStandaloneConnectionOwner as b, resolveHeartbeatSeconds as c, DEFAULT_WHATSAPP_SOCKET_TIMING as d, createWhatsAppSocketOperationTimeoutAdapter as f, withWhatsAppSocketOperationTimeout as g, resolveWhatsAppSocketTiming as h, waitForWaConnection as i, resolveReconnectPolicy as l, resolveWhatsAppSocketOperationTimeoutMs as m, createWaDirectorySocket as n, computeBackoff as o, isWhatsAppSocketOperationTimeoutError as p, createWaSocket as r, newConnectionId as s, closeWhatsAppSocketAndWait as t, sleepWithAbort as u, WhatsAppConnectionOwnerBusyError as v, acquireWhatsAppGatewayConnectionOwner as y };
579
+ export { renderQrTerminal as _, DEFAULT_RECONNECT_POLICY as a, acquireWhatsAppStandaloneConnectionOwner as b, resolveHeartbeatSeconds as c, DEFAULT_WHATSAPP_SOCKET_TIMING as d, createWhatsAppSocketOperationTimeoutAdapter as f, withWhatsAppSocketOperationTimeout as g, resolveWhatsAppSocketTiming as h, waitForWaConnection as i, resolveReconnectPolicy as l, resolveWhatsAppSocketOperationTimeoutMs as m, createWaDirectorySocket as n, computeBackoff as o, isWhatsAppSocketOperationTimeoutError as p, createWaSocket as r, newConnectionId as s, closeWhatsAppSocketAndWait as t, sleepWithAbort$1 as u, WhatsAppConnectionOwnerBusyError as v, acquireWhatsAppGatewayConnectionOwner as y };
@@ -56,17 +56,9 @@ function assertWebChannel(input) {
56
56
  if (input !== "web") throw new Error("Web channel must be 'web'");
57
57
  }
58
58
  function isSelfChatMode(selfE164, allowFrom) {
59
- if (!selfE164) return false;
60
- if (!Array.isArray(allowFrom) || allowFrom.length === 0) return false;
59
+ if (!selfE164 || !Array.isArray(allowFrom) || allowFrom.length === 0) return false;
61
60
  const normalizedSelf = normalizeE164(selfE164);
62
- return allowFrom.some((n) => {
63
- if (n === "*") return false;
64
- try {
65
- return normalizeE164(String(n)) === normalizedSelf;
66
- } catch {
67
- return false;
68
- }
69
- });
61
+ return allowFrom.some((entry) => entry !== "*" && normalizeE164(String(entry)) === normalizedSelf);
70
62
  }
71
63
  function toWhatsappJid(number) {
72
64
  const withoutPrefix = number.replace(/^whatsapp:/i, "").trim();
@@ -147,34 +139,21 @@ function resolveLidMappingDirs(params) {
147
139
  addDir(path.join(CONFIG_DIR, "credentials"));
148
140
  return [...dirs];
149
141
  }
150
- function readLidReverseMapping(params) {
151
- const mappingFilename = `lid-mapping-${params.lid}_reverse.json`;
152
- const mappingDirs = resolveLidMappingDirs({ opts: params.opts });
153
- for (const dir of mappingDirs) {
142
+ function readLidMapping(mappingFilename, opts, normalize) {
143
+ for (const dir of resolveLidMappingDirs({ opts })) {
154
144
  const mappingPath = path.join(dir, mappingFilename);
155
145
  try {
156
146
  const data = fs.readFileSync(mappingPath, "utf8");
157
- const phone = JSON.parse(data);
158
- if (phone === null || phone === void 0) continue;
159
- return normalizeE164(String(phone));
147
+ const value = JSON.parse(data);
148
+ if (value === null || value === void 0) continue;
149
+ const normalized = normalize(String(value));
150
+ if (normalized !== null) return normalized;
160
151
  } catch {}
161
152
  }
162
153
  return null;
163
154
  }
164
155
  function readLidForwardMapping(params) {
165
- const mappingFilename = `lid-mapping-${params.phoneDigits}.json`;
166
- const mappingDirs = resolveLidMappingDirs({ opts: params.opts });
167
- for (const dir of mappingDirs) {
168
- const mappingPath = path.join(dir, mappingFilename);
169
- try {
170
- const data = fs.readFileSync(mappingPath, "utf8");
171
- const lid = JSON.parse(data);
172
- if (lid === null || lid === void 0) continue;
173
- const digits = String(lid).replace(/\D/g, "");
174
- if (digits) return digits;
175
- } catch {}
176
- }
177
- return null;
156
+ return readLidMapping(`lid-mapping-${params.phoneDigits}.json`, params.opts, (value) => value.replace(/\D/g, "") || null);
178
157
  }
179
158
  function jidToE164(jid, opts) {
180
159
  const phoneDigits = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted)$/)?.[1];
@@ -183,10 +162,7 @@ function jidToE164(jid, opts) {
183
162
  if (!lidMatch) return null;
184
163
  const lid = lidMatch[1];
185
164
  if (!lid) return null;
186
- const phone = readLidReverseMapping({
187
- lid,
188
- opts
189
- });
165
+ const phone = readLidMapping(`lid-mapping-${lid}_reverse.json`, opts, normalizeE164);
190
166
  if (phone) return phone;
191
167
  if (opts?.logMissing ?? shouldLogVerbose()) logVerbose(`LID mapping not found for ${lidMatch[1]}; skipping inbound message`);
192
168
  return null;
@@ -1,2 +1,2 @@
1
- import { t as handleWhatsAppAction } from "./.setup/action-runtime-DeVk5V7r.mjs";
1
+ import { t as handleWhatsAppAction } from "./.setup/action-runtime-CE3zGstN.mjs";
2
2
  export { handleWhatsAppAction };
@@ -1,2 +1,2 @@
1
- import { t as handleWhatsAppAction } from "./.setup/action-runtime-DeVk5V7r.mjs";
1
+ import { t as handleWhatsAppAction } from "./.setup/action-runtime-CE3zGstN.mjs";
2
2
  export { handleWhatsAppAction };
@@ -1,7 +1,7 @@
1
- import { a as resolveWhatsAppAccount } from "./.setup/accounts-CtRPAQCL.mjs";
1
+ import { a as resolveWhatsAppAccount } from "./.setup/accounts-F0vHuze2.mjs";
2
2
  import { r as getWhatsAppConnectionController } from "./.setup/connection-controller-runtime-context-hgLJoRc8.mjs";
3
- import { s as resolveJidToE164 } from "./.setup/targets-runtime-DmiqXIXO.mjs";
4
- import { n as registerWhatsAppLoginTool } from "./.setup/agent-tools-login-CL5C1rA2.mjs";
3
+ import { s as resolveJidToE164 } from "./.setup/targets-runtime-Dls-Xh4F.mjs";
4
+ import { n as registerWhatsAppLoginTool } from "./.setup/agent-tools-login-B7VEvJO7.mjs";
5
5
  import { createActionGate, stringEnum } from "openclaw/plugin-sdk/channel-actions";
6
6
  import path from "node:path";
7
7
  import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
package/dist/api.js CHANGED
@@ -1,17 +1,17 @@
1
- import { a as resolveWhatsAppAccount, i as listWhatsAppAuthDirs, n as hasAnyWhatsAppAuth, o as resolveWhatsAppAuthDir, r as listEnabledWhatsAppAccounts, s as resolveWhatsAppMediaMaxBytes, t as DEFAULT_WHATSAPP_MEDIA_MAX_MB } from "./.setup/accounts-CtRPAQCL.mjs";
1
+ import { a as resolveWhatsAppAccount, i as listWhatsAppAuthDirs, n as hasAnyWhatsAppAuth, o as resolveWhatsAppAuthDir, r as listEnabledWhatsAppAccounts, s as resolveWhatsAppMediaMaxBytes, t as DEFAULT_WHATSAPP_MEDIA_MAX_MB } from "./.setup/accounts-F0vHuze2.mjs";
2
2
  import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./.setup/account-ids-CB5SOWjc.mjs";
3
3
  import { a as normalizeWhatsAppAllowFromEntries, c as normalizeWhatsAppTarget, i as looksLikeWhatsAppTargetId, r as isWhatsAppUserTarget, s as normalizeWhatsAppMessagingTarget, t as isWhatsAppGroupJid } from "./.setup/normalize-target-BGra1ZnM.mjs";
4
- import { t as resolveWhatsAppOutboundTarget } from "./.setup/resolve-outbound-target-C2eUvsTc.mjs";
5
- import { c as toWhatsappJid, i as markdownToWhatsApp, l as toWhatsappJidWithLid, n as isSelfChatMode, r as jidToE164, s as resolveJidToE164, t as assertWebChannel } from "./.setup/targets-runtime-DmiqXIXO.mjs";
6
- import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./.setup/channel-CJFE0kD_.mjs";
7
- import { i as resolveWhatsAppGroupToolPolicy, r as resolveWhatsAppGroupRequireMention } from "./.setup/shared-DgOG5Iqr.mjs";
4
+ import { t as resolveWhatsAppOutboundTarget } from "./.setup/resolve-outbound-target-CzyNKmrl.mjs";
5
+ import { c as toWhatsappJid, i as markdownToWhatsApp, l as toWhatsappJidWithLid, n as isSelfChatMode, r as jidToE164, s as resolveJidToE164, t as assertWebChannel } from "./.setup/targets-runtime-Dls-Xh4F.mjs";
6
+ import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./.setup/channel-DNgKHMCz.mjs";
7
+ import { i as resolveWhatsAppGroupToolPolicy, r as resolveWhatsAppGroupRequireMention } from "./.setup/shared-CP_zs2U7.mjs";
8
8
  import { t as whatsappCommandPolicy } from "./.setup/command-policy-BIOSHySD.mjs";
9
- import { t as whatsappSetupPlugin } from "./.setup/channel.setup-CfN1UfdY.mjs";
9
+ import { t as whatsappSetupPlugin } from "./.setup/channel.setup-CsS_NAKQ.mjs";
10
10
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./.setup/constants-C7m81BNe.mjs";
11
- import { d as DEFAULT_WHATSAPP_SOCKET_TIMING, f as createWhatsAppSocketOperationTimeoutAdapter, i as waitForWaConnection, r as createWaSocket } from "./.setup/socket-close-CT__V2fz.mjs";
11
+ import { d as DEFAULT_WHATSAPP_SOCKET_TIMING, f as createWhatsAppSocketOperationTimeoutAdapter, i as waitForWaConnection, r as createWaSocket } from "./.setup/socket-close-DU7eerAa.mjs";
12
12
  import { n as getStatusCode, t as formatError } from "./.setup/session-errors-B8UcAnZN.mjs";
13
- import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-DUiJ5d8I.mjs";
14
- import { _ as resolveInboundMediaMimetype, c as extractContextInfo, m as findMessageSection, o as describeReplyContext, p as extractText, t as createWebSendApi, u as extractLocationData } from "./.setup/send-api-H4AkQJjT.mjs";
13
+ import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-RYQomHyB.mjs";
14
+ import { _ as resolveInboundMediaMimetype, c as extractContextInfo, m as findMessageSection, o as describeReplyContext, p as extractText, t as createWebSendApi, u as extractLocationData } from "./.setup/send-api-B03VDwZ1.mjs";
15
15
  import { asBoolean, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
16
16
  import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
17
17
  import { normalizeE164, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -155,7 +155,7 @@ async function startWhatsAppQaDriverSession(params) {
155
155
  settlePendingNotifications(waiterError);
156
156
  for (const waiter of waiters) {
157
157
  removeWaiter(waiter);
158
- if (waiterError) waiter.reject(waiterError);
158
+ waiter.reject(waiterError);
159
159
  }
160
160
  removeMessageListener();
161
161
  sock.end(void 0);
@@ -1,9 +1,9 @@
1
- import { r as hasWebCredsSync } from "./.setup/creds-files-CDATZWWM.mjs";
1
+ import { n as hasWebCredsSync } from "./.setup/creds-files-B8WcOAmE.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
5
5
  import { resolveUserPath } from "openclaw/plugin-sdk/account-resolution";
6
- import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
6
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
7
7
  //#region extensions/whatsapp/auth-presence.ts
8
8
  function addAccountAuthDirs(authDirs, accountId, authDir, accountsRoot, env) {
9
9
  authDirs.add(path.join(accountsRoot, normalizeAccountId(accountId)));
@@ -14,8 +14,7 @@ function listWhatsAppAuthDirs(cfg, env = process.env) {
14
14
  const oauthDir = resolveOAuthDir(env);
15
15
  const accountsRoot = path.join(oauthDir, "whatsapp");
16
16
  const channel = cfg.channels?.whatsapp;
17
- const authDirs = /* @__PURE__ */ new Set([oauthDir, path.join(accountsRoot, DEFAULT_ACCOUNT_ID)]);
18
- addAccountAuthDirs(authDirs, DEFAULT_ACCOUNT_ID, void 0, accountsRoot, env);
17
+ const authDirs = /* @__PURE__ */ new Set([path.join(accountsRoot, DEFAULT_ACCOUNT_ID$1)]);
19
18
  if (channel?.defaultAccount?.trim()) addAccountAuthDirs(authDirs, channel.defaultAccount, channel.accounts?.[channel.defaultAccount]?.authDir, accountsRoot, env);
20
19
  const accounts = channel?.accounts;
21
20
  if (accounts) for (const [accountId, account] of Object.entries(accounts)) addAccountAuthDirs(authDirs, accountId, account?.authDir, accountsRoot, env);
@@ -1,2 +1,2 @@
1
- import { t as whatsappPlugin } from "./.setup/channel-CJFE0kD_.mjs";
1
+ import { t as whatsappPlugin } from "./.setup/channel-DNgKHMCz.mjs";
2
2
  export { whatsappPlugin };
@@ -1,4 +1,4 @@
1
- import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-DUiJ5d8I.mjs";
1
+ import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-RYQomHyB.mjs";
2
2
  //#region extensions/whatsapp/directory-contract-api.ts
3
3
  const whatsappDirectoryContractPlugin = {
4
4
  id: "whatsapp",
@@ -1,31 +1,205 @@
1
- import { i as isWhatsAppBaileysAuthFileName } from "./.setup/creds-files-CDATZWWM.mjs";
2
- import { r as runtime_doctor_migrations_exports, t as normalizeCompatibilityConfig$1 } from "./.setup/doctor-iMwg0Vhr.mjs";
3
- import fs from "node:fs";
1
+ import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./.setup/account-ids-CB5SOWjc.mjs";
2
+ import { r as isWhatsAppBaileysAuthFileName } from "./.setup/creds-files-B8WcOAmE.mjs";
3
+ import { r as runtime_doctor_migrations_exports, t as normalizeCompatibilityConfig$1 } from "./.setup/doctor-DKzeBEmH.mjs";
4
+ import fs, { realpathSync } from "node:fs";
4
5
  import path from "node:path";
5
- import { fileExists } from "openclaw/plugin-sdk/file-access-runtime";
6
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
6
+ import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-core";
7
+ import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
8
+ import { assertNoSymlinkParentsSync, readRegularFileSync, root } from "openclaw/plugin-sdk/file-access-runtime";
9
+ import { resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/account-resolution";
10
+ import { extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
11
+ import fs$1 from "node:fs/promises";
12
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2 } from "openclaw/plugin-sdk/account-id";
13
+ import { createHash } from "node:crypto";
7
14
  //#region extensions/whatsapp/src/state-migrations.ts
8
- function detectWhatsAppLegacyStateMigrations(params) {
9
- const targetDir = path.join(params.oauthDir, "whatsapp", DEFAULT_ACCOUNT_ID);
10
- return (() => {
11
- try {
12
- return fs.readdirSync(params.oauthDir, { withFileTypes: true });
13
- } catch {
14
- return [];
15
- }
16
- })().flatMap((entry) => {
17
- if (!entry.isFile() || !isWhatsAppBaileysAuthFileName(entry.name)) return [];
18
- const sourcePath = path.join(params.oauthDir, entry.name);
19
- const targetPath = path.join(targetDir, entry.name);
20
- if (fileExists(targetPath)) return [];
21
- return [{
22
- kind: "move",
23
- label: `WhatsApp auth ${entry.name}`,
24
- sourcePath,
25
- targetPath
26
- }];
15
+ function credentialSetDigest(files) {
16
+ return createHash("sha256").update(JSON.stringify([...files].toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0))).digest("hex");
17
+ }
18
+ function comparablePath(value) {
19
+ try {
20
+ return realpathSync.native(value);
21
+ } catch {
22
+ return path.resolve(value);
23
+ }
24
+ }
25
+ function explicitlyOwnsRoot(params) {
26
+ const channel = params.config.channels?.whatsapp;
27
+ const oauthRoot = comparablePath(params.oauthDir);
28
+ return Object.values(channel?.accounts ?? {}).some((owner) => {
29
+ const configured = owner?.authDir?.trim();
30
+ return configured && comparablePath(resolveUserPath$1(configured, params.env)) === oauthRoot;
27
31
  });
28
32
  }
33
+ async function listSources(params) {
34
+ try {
35
+ if (!(await fs$1.lstat(params.oauthDir)).isDirectory()) throw new Error("WhatsApp legacy credential root must be a regular directory.");
36
+ } catch (error) {
37
+ if (extractErrorCode(error) === "ENOENT") return [];
38
+ throw error;
39
+ }
40
+ const sources = /* @__PURE__ */ new Map();
41
+ for (const entry of await fs$1.readdir(params.oauthDir, { withFileTypes: true })) {
42
+ const name = runtime_doctor_migrations_exports.resolveLegacyMigrationSourcePath?.(entry.name) ?? entry.name;
43
+ if (!entry.isFile() || !isWhatsAppBaileysAuthFileName(name)) continue;
44
+ const source = sources.get(name) ?? {
45
+ name,
46
+ filePath: path.join(params.oauthDir, name),
47
+ claimPaths: []
48
+ };
49
+ if (name !== entry.name) source.claimPaths.push(path.join(params.oauthDir, entry.name));
50
+ sources.set(name, source);
51
+ }
52
+ return [...sources.values()].filter((source) => !explicitlyOwnsRoot(params) || source.claimPaths.length > 0).toSorted((left, right) => Number(left.name === "creds.json") - Number(right.name === "creds.json") || left.name.localeCompare(right.name));
53
+ }
54
+ const whatsappLegacyStateMigration = {
55
+ id: "whatsapp-legacy-state",
56
+ label: "WhatsApp legacy state",
57
+ collectBackupResources: ({ env, stateDir }) => [{
58
+ path: resolveOAuthDir(env, stateDir),
59
+ kind: "directory"
60
+ }, {
61
+ path: path.join(stateDir, "state", "openclaw.sqlite"),
62
+ kind: "sqlite"
63
+ }],
64
+ async detectLegacyState(params) {
65
+ const sources = await listSources(params);
66
+ return sources.length > 0 ? { preview: [explicitlyOwnsRoot(params) ? `- Restore ${sources.length} interrupted WhatsApp credential claims in the configured root` : `- Move ${sources.length} WhatsApp credential files into the default account`] } : null;
67
+ },
68
+ async migrateLegacyState(params) {
69
+ const sources = await listSources(params);
70
+ const changes = [];
71
+ const warnings = [];
72
+ if (sources.length === 0) return {
73
+ changes,
74
+ warnings
75
+ };
76
+ const assertCurrent = params.context.channelIngressQueues?.find((entry) => entry.channelId === "whatsapp")?.assertCurrent;
77
+ const { backupLegacyStateSource } = runtime_doctor_migrations_exports;
78
+ if (!assertCurrent || !backupLegacyStateSource) return {
79
+ changes,
80
+ warnings: ["WhatsApp credential import requires offline Doctor repair. Update OpenClaw core and run openclaw doctor --fix; original files remain unchanged."]
81
+ };
82
+ const backups = [];
83
+ for (const source of sources) backups.push({
84
+ source,
85
+ backup: await backupLegacyStateSource({
86
+ filePath: source.filePath,
87
+ claimPaths: source.claimPaths,
88
+ assertCurrent
89
+ })
90
+ });
91
+ if (explicitlyOwnsRoot(params)) return {
92
+ changes: backups.map(({ source, backup }) => `Restored WhatsApp auth ${source.name} in the configured root; backup: ${backup.backupPath}`),
93
+ warnings
94
+ };
95
+ const credentials = await root(params.oauthDir, {
96
+ assertBeforeMutation: assertCurrent,
97
+ symlinks: "reject",
98
+ hardlinks: "reject",
99
+ maxBytes: Number.MAX_SAFE_INTEGER
100
+ });
101
+ const targetDir = path.join("whatsapp", DEFAULT_ACCOUNT_ID$2);
102
+ const targetCreds = path.join(targetDir, "creds.json");
103
+ const canonical = await credentials.exists(targetCreds) ? await credentials.read(targetCreds) : void 0;
104
+ const legacy = backups.find(({ source }) => source.name === "creds.json");
105
+ if (!legacy) return {
106
+ changes,
107
+ warnings: ["WhatsApp shared-root key files have no creds.json identity. Kept all original files and private .migrated backups; restore the original creds.json before retrying Doctor."],
108
+ ...canonical ? { warningDisposition: "recoverable" } : {}
109
+ };
110
+ if (canonical && !canonical.buffer.equals(legacy.backup.bytes)) return {
111
+ changes,
112
+ warnings: ["WhatsApp canonical credentials differ from the legacy shared root. Kept both complete sets and private .migrated backups; choose the intended authDir before removing either set."],
113
+ warningDisposition: "recoverable"
114
+ };
115
+ const targetNames = await fs$1.readdir(path.join(params.oauthDir, targetDir)).catch((error) => {
116
+ if (extractErrorCode(error) === "ENOENT") return [];
117
+ throw error;
118
+ });
119
+ const targetFiles = /* @__PURE__ */ new Map();
120
+ for (const name of targetNames.filter(isWhatsAppBaileysAuthFileName)) {
121
+ const target = await credentials.read(path.join(targetDir, name));
122
+ targetFiles.set(name, createHash("sha256").update(target.buffer).digest("hex"));
123
+ }
124
+ const receiptStore = params.context.openPluginStateKeyedStore({
125
+ namespace: "auth-directory-migrations",
126
+ maxEntries: 4096,
127
+ overflowPolicy: "reject-new"
128
+ });
129
+ if (!receiptStore.withCurrent) return {
130
+ changes,
131
+ warnings: ["WhatsApp credential import requires current offline storage authority. Update OpenClaw core and rerun Doctor; original credentials remain unchanged."]
132
+ };
133
+ const receipts = receiptStore.withCurrent({ assertCurrent });
134
+ const sourceIdentity = legacy.backup.snapshot.sha256;
135
+ const previous = await receipts.lookup(sourceIdentity);
136
+ if (previous && (!canonical || previous.targetDigest !== credentialSetDigest(targetFiles))) return {
137
+ changes,
138
+ warnings: ["WhatsApp credential migration previously started, but its complete canonical credential set is absent or changed. Kept source files and backups; restore the complete intended account or select its authDir explicitly. Doctor will not recreate credentials after logout."],
139
+ ...canonical ? { warningDisposition: "recoverable" } : {}
140
+ };
141
+ if (!canonical) {
142
+ const sourceNames = new Set(sources.map((source) => source.name));
143
+ if (targetNames.some((name) => isWhatsAppBaileysAuthFileName(name) && !sourceNames.has(name))) return {
144
+ changes,
145
+ warnings: ["WhatsApp default account contains unmatched auth files without creds.json. Kept the incomplete account and legacy credentials separate; choose the intended credential set before retrying Doctor."]
146
+ };
147
+ }
148
+ for (const { source, backup } of backups) {
149
+ const relativePath = path.join(targetDir, source.name);
150
+ if (await credentials.exists(relativePath) && !(await credentials.read(relativePath)).buffer.equals(backup.bytes)) return {
151
+ changes,
152
+ warnings: [`WhatsApp auth ${source.name} differs between the default account and legacy shared root. Kept both copies and a private .migrated backup; choose the intended credential set before retrying Doctor.`],
153
+ ...canonical ? { warningDisposition: "recoverable" } : {}
154
+ };
155
+ }
156
+ if (!previous) {
157
+ const expected = new Map(targetFiles);
158
+ for (const { source, backup } of backups) {
159
+ backup.assertUnchanged();
160
+ expected.set(source.name, backup.snapshot.sha256);
161
+ }
162
+ if (!await receipts.registerIfAbsent(sourceIdentity, { targetDigest: credentialSetDigest(expected) })) throw new Error("WhatsApp credential migration receipt changed; rerun Doctor.");
163
+ }
164
+ await credentials.mkdir(targetDir, { private: true });
165
+ const targets = [];
166
+ for (const { source, backup } of backups) {
167
+ backup.assertUnchanged();
168
+ const relativePath = path.join(targetDir, source.name);
169
+ if (!await credentials.exists(relativePath)) await credentials.create(relativePath, backup.bytes, {
170
+ atomic: true,
171
+ durable: true,
172
+ mode: 384,
173
+ private: true
174
+ });
175
+ const target = await credentials.read(relativePath);
176
+ if (!target.buffer.equals(backup.bytes)) throw new Error("WhatsApp canonical credentials changed during import; original files retained.");
177
+ targets.push({
178
+ source,
179
+ backup,
180
+ target,
181
+ targetPath: path.join(params.oauthDir, relativePath)
182
+ });
183
+ }
184
+ for (const { source, backup, target, targetPath } of targets) {
185
+ backup.removeSource(() => {
186
+ assertCurrent();
187
+ assertNoSymlinkParentsSync({
188
+ rootDir: params.oauthDir,
189
+ targetPath: path.dirname(targetPath),
190
+ requireDirectories: true
191
+ });
192
+ const current = readRegularFileSync({ filePath: targetPath });
193
+ if (current.stat.dev !== target.stat.dev || current.stat.ino !== target.stat.ino || !current.buffer.equals(target.buffer)) throw new Error("WhatsApp canonical credentials changed during import; original backup retained.");
194
+ });
195
+ changes.push(`Moved WhatsApp auth ${source.name} into the default account; backup: ${backup.backupPath}`);
196
+ }
197
+ return {
198
+ changes,
199
+ warnings
200
+ };
201
+ }
202
+ };
29
203
  //#endregion
30
204
  //#region extensions/whatsapp/src/doctor-contract.ts
31
205
  const streamingAliasMigration = (0, runtime_doctor_migrations_exports.defineChannelAliasMigration)({
@@ -36,8 +210,35 @@ const streamingAliasMigration = (0, runtime_doctor_migrations_exports.defineChan
36
210
  },
37
211
  accountStreamingInheritsDefaultAccount: true
38
212
  });
39
- const hasExposeErrorText = (value) => Object.hasOwn((0, runtime_doctor_migrations_exports.asObjectRecord)(value) ?? {}, "exposeErrorText");
40
213
  const hasAckReaction = (value) => Boolean((0, runtime_doctor_migrations_exports.asObjectRecord)((0, runtime_doctor_migrations_exports.asObjectRecord)(value)?.ackReaction));
214
+ const legacyDefaultPolicyKeys = /* @__PURE__ */ new Set([
215
+ "dmPolicy",
216
+ "allowFrom",
217
+ "groupPolicy",
218
+ "groupAllowFrom"
219
+ ]);
220
+ function hasPossibleLeftoverDefault(cfg) {
221
+ const channel = (0, runtime_doctor_migrations_exports.asObjectRecord)(cfg.channels?.whatsapp);
222
+ const accounts = (0, runtime_doctor_migrations_exports.asObjectRecord)(channel?.accounts);
223
+ const fallback = (0, runtime_doctor_migrations_exports.asObjectRecord)(accounts?.default);
224
+ if (!channel || !accounts || !fallback || channel.authDir !== void 0 || typeof channel.defaultAccount === "string" && channel.defaultAccount.trim().toLowerCase() === "default" || Object.keys(accounts).length < 2) return false;
225
+ const keys = Object.keys(fallback);
226
+ if (keys.length === 0 || keys.some((key) => !legacyDefaultPolicyKeys.has(key)) || Object.values(accounts).some((account) => !(0, runtime_doctor_migrations_exports.asObjectRecord)(account)) || cfg.bindings?.some((binding) => binding.match.channel.trim().toLowerCase() === "whatsapp" && binding.match.accountId?.trim().toLowerCase() === "default")) return false;
227
+ const oauthDir = resolveOAuthDir();
228
+ for (const dir of [oauthDir, path.join(oauthDir, "whatsapp", "default")]) try {
229
+ if (fs.readdirSync(dir).some(isWhatsAppBaileysAuthFileName)) return false;
230
+ } catch (error) {
231
+ if ((0, runtime_doctor_migrations_exports.asObjectRecord)(error)?.code !== "ENOENT") return false;
232
+ }
233
+ return true;
234
+ }
235
+ function collectDefaultAccountWarnings(cfg) {
236
+ if (!hasPossibleLeftoverDefault(cfg)) return [];
237
+ const namedAccountId = listAccountIds(cfg).find((id) => id !== "default" && normalizeOptionalAccountId(id) === id);
238
+ if (!namedAccountId) return [];
239
+ const currentAccountId = resolveDefaultWhatsAppAccountId(cfg);
240
+ return [`channels.whatsapp.accounts.default contains only shared policy and has no detected credentials. It may be a leftover of an earlier Doctor migration or an intentional account awaiting login. Unqualified WhatsApp operations currently select "${currentAccountId}". Doctor left all accounts and routing unchanged. To select a named account while keeping all accounts and shared policy, run \`openclaw config set channels.whatsapp.defaultAccount '${JSON.stringify(currentAccountId === "default" ? namedAccountId : currentAccountId)}' --strict-json\`. Only if "default" is unwanted, preserve any shared policy you still need, then run \`openclaw channels remove --channel whatsapp --account default --delete\`.`];
241
+ }
41
242
  const legacyConfigRules = [
42
243
  ...streamingAliasMigration.legacyConfigRules,
43
244
  {
@@ -56,48 +257,22 @@ const legacyConfigRules = [
56
257
  ],
57
258
  message: "channels.whatsapp.accounts.<id>.ackReaction moved to global message acknowledgement settings. Run \"openclaw doctor --fix\".",
58
259
  match: (value) => (0, runtime_doctor_migrations_exports.hasLegacyAccountStreamingAliases)(value, hasAckReaction)
59
- },
60
- {
61
- path: [
62
- "channels",
63
- "whatsapp",
64
- "exposeErrorText"
65
- ],
66
- message: "channels.whatsapp.exposeErrorText is retired and ignored. Run \"openclaw doctor --fix\"."
67
- },
68
- {
69
- path: [
70
- "channels",
71
- "whatsapp",
72
- "accounts"
73
- ],
74
- message: "channels.whatsapp.accounts.<id>.exposeErrorText is retired and ignored. Run \"openclaw doctor --fix\".",
75
- match: (value) => (0, runtime_doctor_migrations_exports.hasLegacyAccountStreamingAliases)(value, hasExposeErrorText)
76
260
  }
77
261
  ];
78
- function removeExposeErrorText(cfg, changes) {
79
- return (0, runtime_doctor_migrations_exports.stripRetiredChannelKeys)({
80
- cfg,
81
- channelId: "whatsapp",
82
- keys: /* @__PURE__ */ new Set(["exposeErrorText"]),
83
- scope: "root-and-accounts",
84
- onRemove: ({ key, pathPrefix }) => changes.push(`Removed retired ${pathPrefix}.${key}.`)
85
- }).config;
86
- }
87
262
  function normalizeCompatibilityConfig({ cfg }) {
88
263
  const ackReaction = normalizeCompatibilityConfig$1({ cfg });
89
- const retiredConfig = removeExposeErrorText(ackReaction.config, ackReaction.changes);
90
- return streamingAliasMigration.normalizeChannelConfig({
91
- cfg: retiredConfig,
264
+ const normalized = streamingAliasMigration.normalizeChannelConfig({
265
+ cfg: ackReaction.config,
92
266
  changes: ackReaction.changes
93
267
  });
268
+ const warnings = collectDefaultAccountWarnings(normalized.config);
269
+ return {
270
+ ...normalized,
271
+ ...warnings.length ? { warnings } : {}
272
+ };
94
273
  }
95
274
  //#endregion
96
275
  //#region extensions/whatsapp/doctor-contract-api.ts
97
- const stateMigrations = [(0, runtime_doctor_migrations_exports.definePluginDoctorMigrationFromPlans)({
98
- id: "whatsapp-legacy-state",
99
- label: "WhatsApp legacy state",
100
- resolvePlans: detectWhatsAppLegacyStateMigrations
101
- })];
276
+ const stateMigrations = [whatsappLegacyStateMigration];
102
277
  //#endregion
103
278
  export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };