@openclaw/whatsapp 2026.7.2-beta.2 → 2026.7.2-beta.4

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 (147) hide show
  1. package/dist/{access-control-DazDX3RB.js → access-control-CRz_v6bR.js} +4 -5
  2. package/dist/{accounts-CKOFMPf-.js → accounts-CxPuEFi-.js} +44 -4
  3. package/dist/{action-runtime-Bf-12TIH.js → action-runtime-CHU7xb6o.js} +2 -2
  4. package/dist/action-runtime-api.js +1 -1
  5. package/dist/action-runtime.runtime.js +1 -1
  6. package/dist/{active-listener-5kHap439.js → active-listener-BEjWsCNM.js} +1 -1
  7. package/dist/api.js +12 -15
  8. package/dist/{approval-handler.runtime-CBiGQuZ5.js → approval-handler.runtime-C4ddB7Kc.js} +4 -4
  9. package/dist/{approval-reactions-BGQoz25s.js → approval-reactions-P2HXjmln.js} +3 -3
  10. package/dist/{auth-store-BQi9pZqQ.js → auth-store-DirYMTbo.js} +4 -3
  11. package/dist/call-tool-api.js +4 -4
  12. package/dist/{channel-DbadDUDD.js → channel-B9xjrOGG.js} +37 -19
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel-react-action-PoRWfP9x.js → channel-react-action-CBW-PA8q.js} +10 -13
  16. package/dist/{channel.runtime-BDDy4Lhz.js → channel.runtime-DdKuhlNu.js} +5 -5
  17. package/dist/{channel.setup-BZ-po8fg.js → channel.setup-DXUCPHel.js} +3 -6
  18. package/dist/{config-schema-BZ8h1hWA.js → config-schema-C6IeKIC0.js} +6 -0
  19. package/dist/{connection-controller-C1rwfWb4.js → connection-controller-BVRExqrB.js} +186 -27
  20. package/dist/connection-controller-runtime-context-DG0tuYuw.js +23 -0
  21. package/dist/contract-api.js +1 -1
  22. package/dist/directory-config-BTbJkV1W.js +196 -0
  23. package/dist/directory-contract-api.js +1 -1
  24. package/dist/doctor-CwzVXKoE.js +10 -0
  25. package/dist/doctor-contract-api.js +35 -79
  26. package/dist/{group-session-key-CLTZeH_F.js → group-session-key-DJSdYfnb.js} +9 -4
  27. package/dist/{identity-D7L8LCuH.js → identity-BM4_wZeM.js} +2 -2
  28. package/dist/light-runtime-api.js +2 -2
  29. package/dist/{login-DGIoToX8.js → login-BKUm7KSk.js} +6 -7
  30. package/dist/{login-qr-X-CZHOk9.js → login-qr-DagJvMbg.js} +7 -8
  31. package/dist/login-qr-runtime.js +1 -1
  32. package/dist/{monitor-BZ8QvCeV.js → monitor-CBVNJ2lb.js} +623 -400
  33. package/dist/question-reactions-CaOrSYzU.js +142 -0
  34. package/dist/runtime-BLlToOi6.js +21 -0
  35. package/dist/runtime-api.js +10 -10
  36. package/dist/runtime-setter-api.js +1 -1
  37. package/dist/{send-D9Ydnn4o.js → send-DhJVN30g.js} +38 -85
  38. package/dist/{send-api-W33lI2nR.js → send-api-CpHwbor_.js} +38 -32
  39. package/dist/{setup-finalize-DBf1jjBj.js → setup-finalize-9XzKdFq6.js} +3 -3
  40. package/dist/setup-plugin-api.js +1 -1
  41. package/dist/{setup-surface-CiKGdNJv.js → setup-surface-Kv3mPki2.js} +3 -3
  42. package/dist/{setup-core-3aSOoK2T.js → shared-CuqHFnKd.js} +78 -56
  43. package/dist/{session-mxWFn0eS.js → socket-close-BoSHhQNJ.js} +226 -7
  44. package/dist/{targets-runtime-Bsh0HR9y.js → targets-runtime-CKFrhoX4.js} +142 -29
  45. package/dist/text-runtime-CU_IlbK5.js +4 -0
  46. package/node_modules/@protobufjs/utf8/CHANGELOG.md +8 -0
  47. package/node_modules/@protobufjs/utf8/index.js +29 -18
  48. package/node_modules/@protobufjs/utf8/package.json +2 -2
  49. package/node_modules/@protobufjs/utf8/tests/index.js +5 -0
  50. package/node_modules/@thi.ng/bitstream/package.json +4 -4
  51. package/node_modules/@thi.ng/errors/package.json +4 -4
  52. package/node_modules/@types/node/README.md +1 -1
  53. package/node_modules/@types/node/fs/promises.d.ts +16 -6
  54. package/node_modules/@types/node/package.json +2 -2
  55. package/node_modules/audio-type/package.json +3 -2
  56. package/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -1
  57. package/node_modules/lru-cache/dist/commonjs/browser/index.js +11 -4
  58. package/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -1
  59. package/node_modules/lru-cache/dist/commonjs/browser/index.min.js +1 -1
  60. package/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +3 -3
  61. package/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  62. package/node_modules/lru-cache/dist/commonjs/index.js +11 -4
  63. package/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  64. package/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  65. package/node_modules/lru-cache/dist/commonjs/index.min.js.map +3 -3
  66. package/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -1
  67. package/node_modules/lru-cache/dist/commonjs/node/index.js +11 -4
  68. package/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -1
  69. package/node_modules/lru-cache/dist/commonjs/node/index.min.js +1 -1
  70. package/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +3 -3
  71. package/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -1
  72. package/node_modules/lru-cache/dist/esm/browser/index.js +11 -4
  73. package/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -1
  74. package/node_modules/lru-cache/dist/esm/browser/index.min.js +1 -1
  75. package/node_modules/lru-cache/dist/esm/browser/index.min.js.map +3 -3
  76. package/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  77. package/node_modules/lru-cache/dist/esm/index.js +11 -4
  78. package/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  79. package/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  80. package/node_modules/lru-cache/dist/esm/index.min.js.map +3 -3
  81. package/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -1
  82. package/node_modules/lru-cache/dist/esm/node/index.js +11 -4
  83. package/node_modules/lru-cache/dist/esm/node/index.js.map +1 -1
  84. package/node_modules/lru-cache/dist/esm/node/index.min.js +1 -1
  85. package/node_modules/lru-cache/dist/esm/node/index.min.js.map +3 -3
  86. package/node_modules/lru-cache/package.json +1 -1
  87. package/node_modules/music-metadata/lib/common/Util.d.ts +1 -1
  88. package/node_modules/music-metadata/lib/common/Util.js +7 -1
  89. package/node_modules/music-metadata/lib/id3v2/FrameHeader.js +23 -3
  90. package/node_modules/music-metadata/lib/id3v2/FrameParser.js +2 -2
  91. package/node_modules/music-metadata/lib/id3v2/ID3v2Token.js +1 -1
  92. package/node_modules/music-metadata/lib/mp4/AtomToken.js +38 -13
  93. package/node_modules/music-metadata/lib/mpeg/MpegParser.js +1 -1
  94. package/node_modules/music-metadata/package.json +6 -6
  95. package/node_modules/p-queue/dist/index.js +17 -6
  96. package/node_modules/p-queue/package.json +1 -1
  97. package/node_modules/protobufjs/dist/light/protobuf.js +145 -188
  98. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  99. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  100. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  101. package/node_modules/protobufjs/dist/minimal/protobuf.js +33 -76
  102. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  103. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  104. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  105. package/node_modules/protobufjs/dist/protobuf.js +168 -208
  106. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  107. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  108. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  109. package/node_modules/protobufjs/index.d.ts +0 -8
  110. package/node_modules/protobufjs/package.json +2 -3
  111. package/node_modules/protobufjs/src/parse.js +3 -0
  112. package/node_modules/protobufjs/src/util/minimal.js +0 -3
  113. package/node_modules/typebox/build/compile/validator.d.mts +1 -4
  114. package/node_modules/typebox/build/guard/guard.d.mts +1 -1
  115. package/node_modules/typebox/build/guard/guard.mjs +5 -2
  116. package/node_modules/typebox/build/guard/string.mjs +16 -4
  117. package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
  118. package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
  119. package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
  120. package/node_modules/typebox/build/typebox.d.mts +1 -1
  121. package/node_modules/typebox/build/typebox.mjs +1 -1
  122. package/node_modules/typebox/package.json +1 -1
  123. package/node_modules/typebox/readme.md +29 -32
  124. package/node_modules/ws/lib/receiver.js +15 -32
  125. package/node_modules/ws/lib/websocket-server.js +4 -4
  126. package/node_modules/ws/lib/websocket.js +4 -4
  127. package/node_modules/ws/package.json +5 -1
  128. package/npm-shrinkwrap.json +41 -45
  129. package/openclaw.plugin.json +139 -179
  130. package/package.json +21 -11
  131. package/dist/account-config-LiT9wWH9.js +0 -43
  132. package/dist/connection-controller-runtime-context-CbTsqmPT.js +0 -14
  133. package/dist/directory-config-CLBQrjtC.js +0 -37
  134. package/dist/doctor-By3SB4lm.js +0 -48
  135. package/dist/runtime-BfAdAEYT.js +0 -8
  136. package/dist/text-runtime-BTGkMN0o.js +0 -4
  137. package/node_modules/@protobufjs/inquire/CHANGELOG.md +0 -8
  138. package/node_modules/@protobufjs/inquire/LICENSE +0 -26
  139. package/node_modules/@protobufjs/inquire/README.md +0 -13
  140. package/node_modules/@protobufjs/inquire/index.d.ts +0 -10
  141. package/node_modules/@protobufjs/inquire/index.js +0 -38
  142. package/node_modules/@protobufjs/inquire/package.json +0 -21
  143. package/node_modules/@protobufjs/inquire/tests/data/array.js +0 -1
  144. package/node_modules/@protobufjs/inquire/tests/data/emptyArray.js +0 -1
  145. package/node_modules/@protobufjs/inquire/tests/data/emptyObject.js +0 -1
  146. package/node_modules/@protobufjs/inquire/tests/data/object.js +0 -1
  147. package/node_modules/@protobufjs/inquire/tests/index.js +0 -20
@@ -1,8 +1,8 @@
1
+ import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-CxPuEFi-.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
- import { t as WhatsAppChannelConfigSchema } from "./config-schema-BZ8h1hWA.js";
5
- import { n as whatsappDoctor } from "./doctor-By3SB4lm.js";
4
+ import { t as WhatsAppChannelConfigSchema } from "./config-schema-C6IeKIC0.js";
5
+ import { n as whatsappDoctor } from "./doctor-CwzVXKoE.js";
6
6
  import { t as resolveWhatsAppConfigPath } from "./group-config-path-BGyzT9Lg.js";
7
7
  import { t as resolveLegacyGroupSessionKey } from "./group-session-contract-DDnZSsJ1.js";
8
8
  import { n as unsupportedSecretRefSurfacePatterns, t as collectUnsupportedSecretRefConfigCandidates } from "./security-contract-nAzD945y.js";
@@ -11,12 +11,24 @@ import { normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-
11
11
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
12
12
  import { createChannelPluginBase } from "openclaw/plugin-sdk/core";
13
13
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
14
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
14
15
  import { buildChannelGroupsScopeTree, collectOpenGroupPolicyRouteAllowlistWarnings, createAllowlistProviderGroupPolicyWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
16
+ import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
17
+ import { applyAccountNameToChannelSection, migrateBaseNameToDefaultAccount, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/setup";
15
18
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
16
19
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
17
20
  import { createDelegatedSetupWizardProxy } from "openclaw/plugin-sdk/setup-runtime";
18
21
  import { readChannelAllowFromStore } from "openclaw/plugin-sdk/channel-pairing";
19
- import { applyAccountNameToChannelSection, migrateBaseNameToDefaultAccount, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/setup";
22
+ //#region extensions/whatsapp/src/channel-runtime-loader.ts
23
+ const loadWhatsAppAuthStore = createLazyRuntimeModule(() => import("./auth-store-DirYMTbo.js").then((n) => n.i));
24
+ async function isWhatsAppAuthConfigured(authDir) {
25
+ return await (await loadWhatsAppAuthStore()).readWebAuthState(authDir) === "linked";
26
+ }
27
+ const loadWhatsAppChannelRuntime = createLazyRuntimeModule(async () => {
28
+ await loadWhatsAppAuthStore();
29
+ return await import("./channel.runtime-DdKuhlNu.js");
30
+ });
31
+ //#endregion
20
32
  //#region extensions/whatsapp/src/config-accessors.ts
21
33
  function formatWhatsAppConfigAllowFromEntries(allowFrom) {
22
34
  return normalizeWhatsAppAllowFromEntries(allowFrom);
@@ -41,6 +53,62 @@ function resolveWhatsAppGroupToolPolicy(params) {
41
53
  });
42
54
  }
43
55
  //#endregion
56
+ //#region extensions/whatsapp/src/setup-core.ts
57
+ const channel = "whatsapp";
58
+ const whatsappSetupAdapter = {
59
+ singleAccountKeysToMove: ["authDir"],
60
+ resolveAccountId: ({ accountId }) => normalizeAccountId$1(accountId),
61
+ applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
62
+ cfg,
63
+ channelKey: channel,
64
+ accountId,
65
+ name,
66
+ alwaysUseAccounts: true
67
+ }),
68
+ applyAccountConfig: ({ cfg, accountId, input }) => {
69
+ const setupInput = input;
70
+ const next = migrateBaseNameToDefaultAccount({
71
+ cfg: applyAccountNameToChannelSection({
72
+ cfg,
73
+ channelKey: channel,
74
+ accountId,
75
+ name: setupInput.name,
76
+ alwaysUseAccounts: true
77
+ }),
78
+ channelKey: channel,
79
+ alwaysUseAccounts: true
80
+ });
81
+ const entry = {
82
+ ...next.channels?.whatsapp?.accounts?.[accountId],
83
+ ...setupInput.authDir ? { authDir: setupInput.authDir } : {},
84
+ enabled: true
85
+ };
86
+ return {
87
+ ...next,
88
+ channels: {
89
+ ...next.channels,
90
+ whatsapp: {
91
+ ...next.channels?.whatsapp,
92
+ accounts: {
93
+ ...next.channels?.whatsapp?.accounts,
94
+ [accountId]: entry
95
+ }
96
+ }
97
+ }
98
+ };
99
+ }
100
+ };
101
+ const whatsappSetupContract = defineChannelSetupContract({
102
+ fields: { authDir: {
103
+ kind: "string",
104
+ cli: {
105
+ flags: "--auth-dir <path>",
106
+ description: "WhatsApp auth directory override"
107
+ }
108
+ } },
109
+ legacyAdapter: whatsappSetupAdapter
110
+ });
111
+ //#endregion
44
112
  //#region extensions/whatsapp/src/security-fix.ts
45
113
  function applyGroupAllowFromFromStore(params) {
46
114
  const next = structuredClone(params.cfg ?? {});
@@ -83,11 +151,8 @@ async function applyWhatsAppSecurityConfigFixes(params) {
83
151
  //#endregion
84
152
  //#region extensions/whatsapp/src/shared.ts
85
153
  const WHATSAPP_CHANNEL = "whatsapp";
86
- async function loadWhatsAppChannelRuntime() {
87
- return await import("./channel.runtime-BDDy4Lhz.js");
88
- }
89
154
  async function loadWhatsAppSetupSurface() {
90
- return await import("./setup-surface-CiKGdNJv.js");
155
+ return await import("./setup-surface-Kv3mPki2.js");
91
156
  }
92
157
  const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
93
158
  const whatsappConfigAdapter = createScopedChannelConfigAdapter({
@@ -191,7 +256,7 @@ function createWhatsAppPluginBase(params) {
191
256
  docsLabel: "whatsapp",
192
257
  blurb: "works with your own number; recommend a separate phone + eSIM.",
193
258
  systemImage: "message",
194
- showConfigured: false,
259
+ exposure: { configured: false },
195
260
  quickstartAllowFrom: true,
196
261
  forceAccountBinding: true,
197
262
  preferSessionLookupForAnnounceTarget: true
@@ -213,7 +278,7 @@ function createWhatsAppPluginBase(params) {
213
278
  },
214
279
  reload: {
215
280
  configPrefixes: [
216
- "web",
281
+ "channels.whatsapp.enabled",
217
282
  "channels.whatsapp.accounts",
218
283
  "channels.whatsapp.selfChatMode"
219
284
  ],
@@ -223,7 +288,7 @@ function createWhatsAppPluginBase(params) {
223
288
  configSchema: WhatsAppChannelConfigSchema,
224
289
  config: {
225
290
  ...whatsappConfigAdapter,
226
- isEnabled: (account, cfg) => account.enabled && cfg.web?.enabled !== false,
291
+ isEnabled: (account) => account.enabled,
227
292
  disabledReason: () => "disabled",
228
293
  isConfigured: params.isConfigured,
229
294
  hasPersistedAuthState: ({ cfg }) => hasAnyWhatsAppAuth(cfg),
@@ -245,6 +310,7 @@ function createWhatsAppPluginBase(params) {
245
310
  },
246
311
  doctor: whatsappDoctor,
247
312
  setup: params.setup,
313
+ ...params.setupContract ? { setupContract: params.setupContract } : {},
248
314
  groups: params.groups
249
315
  });
250
316
  return {
@@ -274,48 +340,4 @@ function createWhatsAppPluginBase(params) {
274
340
  };
275
341
  }
276
342
  //#endregion
277
- //#region extensions/whatsapp/src/setup-core.ts
278
- const channel = "whatsapp";
279
- const whatsappSetupAdapter = {
280
- resolveAccountId: ({ accountId }) => normalizeAccountId$1(accountId),
281
- applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
282
- cfg,
283
- channelKey: channel,
284
- accountId,
285
- name,
286
- alwaysUseAccounts: true
287
- }),
288
- applyAccountConfig: ({ cfg, accountId, input }) => {
289
- const next = migrateBaseNameToDefaultAccount({
290
- cfg: applyAccountNameToChannelSection({
291
- cfg,
292
- channelKey: channel,
293
- accountId,
294
- name: input.name,
295
- alwaysUseAccounts: true
296
- }),
297
- channelKey: channel,
298
- alwaysUseAccounts: true
299
- });
300
- const entry = {
301
- ...next.channels?.whatsapp?.accounts?.[accountId],
302
- ...input.authDir ? { authDir: input.authDir } : {},
303
- enabled: true
304
- };
305
- return {
306
- ...next,
307
- channels: {
308
- ...next.channels,
309
- whatsapp: {
310
- ...next.channels?.whatsapp,
311
- accounts: {
312
- ...next.channels?.whatsapp?.accounts,
313
- [accountId]: entry
314
- }
315
- }
316
- }
317
- };
318
- }
319
- };
320
- //#endregion
321
- export { resolveWhatsAppGroupRequireMention as a, whatsappSetupWizardProxy as i, createWhatsAppPluginBase as n, resolveWhatsAppGroupToolPolicy as o, loadWhatsAppChannelRuntime as r, formatWhatsAppConfigAllowFromEntries as s, whatsappSetupAdapter as t };
343
+ export { resolveWhatsAppGroupRequireMention as a, isWhatsAppAuthConfigured as c, whatsappSetupContract as i, loadWhatsAppChannelRuntime as l, whatsappSetupWizardProxy as n, resolveWhatsAppGroupToolPolicy as o, whatsappSetupAdapter as r, formatWhatsAppConfigAllowFromEntries as s, createWhatsAppPluginBase as t };
@@ -1,14 +1,166 @@
1
1
  import { o as resolveWebCredsBackupPath, s as resolveWebCredsPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-B1kSWtBg.js";
2
- import { E as writeWebCredsRawAtomically, S as enqueueCredsSave, T as writeCredsJsonAtomically, b as restoreCredsFromBackupIfNeeded, u as readCredsJsonRaw, w as waitForCredsSaveQueueWithTimeout, y as resolveDefaultWebAuthDir } from "./auth-store-BQi9pZqQ.js";
2
+ import { E as writeWebCredsRawAtomically, S as enqueueCredsSave, T as writeCredsJsonAtomically, b as restoreCredsFromBackupIfNeeded, u as readCredsJsonRaw, w as waitForCredsSaveQueueWithTimeout, y as resolveDefaultWebAuthDir } from "./auth-store-DirYMTbo.js";
3
3
  import { n as getStatusCode } from "./session-errors-JuazczrA.js";
4
4
  import { a as makeWASocket, i as makeCacheableSignalKeyStore, n as createBaileysSignalRepository, r as fetchLatestBaileysVersion, s as useMultiFileAuthState } from "./session.runtime-Nsqaehbd.js";
5
5
  import { VERSION, formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
6
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 = {
@@ -25,12 +177,11 @@ var WhatsAppSocketOperationTimeoutError = class extends Error {
25
177
  this.name = "WhatsAppSocketOperationTimeoutError";
26
178
  }
27
179
  };
28
- function resolveWhatsAppSocketTiming(cfg, overrides) {
29
- const configured = cfg.web?.whatsapp;
180
+ function resolveWhatsAppSocketTiming(overrides) {
30
181
  return {
31
- keepAliveIntervalMs: parseStrictPositiveInteger(overrides?.keepAliveIntervalMs) ?? parseStrictPositiveInteger(configured?.keepAliveIntervalMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.keepAliveIntervalMs,
32
- connectTimeoutMs: parseStrictPositiveInteger(overrides?.connectTimeoutMs) ?? parseStrictPositiveInteger(configured?.connectTimeoutMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.connectTimeoutMs,
33
- defaultQueryTimeoutMs: parseStrictPositiveInteger(overrides?.defaultQueryTimeoutMs) ?? parseStrictPositiveInteger(configured?.defaultQueryTimeoutMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.defaultQueryTimeoutMs
182
+ keepAliveIntervalMs: parseStrictPositiveInteger(overrides?.keepAliveIntervalMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.keepAliveIntervalMs,
183
+ connectTimeoutMs: parseStrictPositiveInteger(overrides?.connectTimeoutMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.connectTimeoutMs,
184
+ defaultQueryTimeoutMs: parseStrictPositiveInteger(overrides?.defaultQueryTimeoutMs) ?? DEFAULT_WHATSAPP_SOCKET_TIMING.defaultQueryTimeoutMs
34
185
  };
35
186
  }
36
187
  function isWhatsAppSocketOperationTimeoutError(error) {
@@ -170,6 +321,9 @@ function resolveEnvWaWebSocketUrl() {
170
321
  * Consumers can opt into QR printing for interactive login flows.
171
322
  */
172
323
  async function createWaSocket(printQr, verbose, opts = {}) {
324
+ return await createWaSocketInternal(printQr, verbose, opts, "normal");
325
+ }
326
+ async function createWaSocketInternal(printQr, verbose, opts, receiveMode) {
173
327
  const logger = toPinoLikeLogger(getChildLogger({ module: "baileys" }, { level: verbose ? "info" : "silent" }), verbose ? "info" : "silent");
174
328
  const authDir = resolveUserPath(opts.authDir ?? resolveDefaultWebAuthDir());
175
329
  await rejectUnsafeWebCredsPath(authDir);
@@ -261,6 +415,7 @@ async function createWaSocket(printQr, verbose, opts = {}) {
261
415
  VERSION
262
416
  ],
263
417
  syncFullHistory: false,
418
+ fireInitQueries: receiveMode !== "directory",
264
419
  markOnlineOnConnect: false,
265
420
  ...socketTiming,
266
421
  agent,
@@ -270,6 +425,19 @@ async function createWaSocket(printQr, verbose, opts = {}) {
270
425
  ...opts.getMessage ? { getMessage: opts.getMessage } : {},
271
426
  ...opts.cachedGroupMetadata ? { cachedGroupMetadata: opts.cachedGroupMetadata } : {}
272
427
  });
428
+ if (receiveMode === "directory") for (const event of [
429
+ "CB:message",
430
+ "CB:call",
431
+ "CB:receipt",
432
+ "CB:notification",
433
+ "CB:ack,class:message",
434
+ "CB:presence",
435
+ "CB:chatstate",
436
+ "CB:ib,,dirty",
437
+ "CB:ib,,offline_preview",
438
+ "CB:ib,,offline",
439
+ "CB:ib,,edge_routing"
440
+ ]) sock.ws.removeAllListeners(event);
273
441
  socketRef.current = sock;
274
442
  if (pendingSocketAbort) abortSocketAfterCredentialPersistenceFailure(sock, pendingSocketAbort.error);
275
443
  sock.ev.on("creds.update", () => enqueueSaveCreds(authDir, saveCreds, sessionLogger, {
@@ -303,6 +471,9 @@ async function createWaSocket(printQr, verbose, opts = {}) {
303
471
  });
304
472
  return sock;
305
473
  }
474
+ async function createWaDirectorySocket(authDir) {
475
+ return await createWaSocketInternal(false, false, { authDir }, "directory");
476
+ }
306
477
  async function resolveEnvProxyAgent(logger) {
307
478
  try {
308
479
  const agent = createNodeProxyAgent({
@@ -400,4 +571,52 @@ function createConnectionTimeoutError(timeoutMs) {
400
571
  return error;
401
572
  }
402
573
  //#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 };
574
+ //#region extensions/whatsapp/src/socket-close.ts
575
+ const SOCKET_CLOSE_TIMEOUT_MS = 15e3;
576
+ async function withCloseTimeout(task, operationName) {
577
+ let timer;
578
+ await Promise.race([task, new Promise((_resolve, reject) => {
579
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`WhatsApp ${operationName} timed out`)), SOCKET_CLOSE_TIMEOUT_MS);
580
+ })]).finally(() => {
581
+ if (timer) clearTimeout(timer);
582
+ });
583
+ }
584
+ async function waitForTransportClose(ws, operation, operationName) {
585
+ let onClose;
586
+ const closed = ws.isClosed ? Promise.resolve() : new Promise((resolve) => {
587
+ onClose = resolve;
588
+ ws.once("close", onClose);
589
+ if (ws.isClosed) onClose();
590
+ });
591
+ try {
592
+ await withCloseTimeout(Promise.all([Promise.resolve(operation), closed]), operationName);
593
+ } finally {
594
+ if (onClose) ws.removeListener("close", onClose);
595
+ }
596
+ }
597
+ /** Close Baileys and verify the transport state before connection ownership moves. */
598
+ async function closeWhatsAppSocketAndWait(sock, reason) {
599
+ const errors = [];
600
+ try {
601
+ const endResult = sock.end(new Error(reason));
602
+ if (sock.ws.isClosed) {
603
+ await waitForTransportClose(sock.ws, endResult, "socket end");
604
+ return;
605
+ }
606
+ if (sock.ws.isClosing) await waitForTransportClose(sock.ws, endResult, "socket end");
607
+ else await withCloseTimeout(Promise.resolve(endResult), "socket end");
608
+ } catch (error) {
609
+ errors.push(error);
610
+ }
611
+ if (sock.ws.isClosed) return;
612
+ try {
613
+ const closeResult = sock.ws.close();
614
+ await waitForTransportClose(sock.ws, closeResult, "WebSocket close");
615
+ } catch (error) {
616
+ errors.push(error);
617
+ }
618
+ if (sock.ws.isClosed) return;
619
+ throw new AggregateError(errors, "WhatsApp socket close could not be confirmed");
620
+ }
621
+ //#endregion
622
+ 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,12 +1,66 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
4
+ import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
4
5
  import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
5
- import { CONFIG_DIR, escapeRegExp, resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/text-utility-runtime";
6
+ import { markdownToIRWithMeta, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers, sliceMarkdownIR } from "openclaw/plugin-sdk/text-chunking";
7
+ import { CONFIG_DIR, resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/text-utility-runtime";
6
8
  //#region extensions/whatsapp/src/targets-runtime.ts
7
- const WHATSAPP_FENCE_PLACEHOLDER = "\0FENCE";
8
- const WHATSAPP_INLINE_CODE_PLACEHOLDER = "\0CODE";
9
- const WHATSAPP_PLACEHOLDER_TERMINATOR = "\0";
9
+ const WHATSAPP_FORMAT_CAPABILITIES = {
10
+ mechanism: "markdown",
11
+ constructs: {
12
+ bold: "native",
13
+ italic: "native",
14
+ underline: "strip",
15
+ strikethrough: "native",
16
+ spoiler: "fallback",
17
+ codeInline: "native",
18
+ codeBlock: "native",
19
+ codeLanguage: "fallback",
20
+ linkLabel: "fallback",
21
+ heading: "fallback",
22
+ bulletList: "native",
23
+ orderedList: "native",
24
+ taskList: "fallback",
25
+ table: "fallback",
26
+ blockquote: "native",
27
+ image: "fallback",
28
+ mention: "native"
29
+ },
30
+ chunk: {
31
+ limit: 4096,
32
+ unit: "chars"
33
+ }
34
+ };
35
+ const WHATSAPP_STYLE_MARKERS = {
36
+ bold: {
37
+ open: "*",
38
+ close: "*"
39
+ },
40
+ italic: {
41
+ open: "_",
42
+ close: "_"
43
+ },
44
+ strikethrough: {
45
+ open: "~",
46
+ close: "~"
47
+ },
48
+ code: {
49
+ open: "```",
50
+ close: "```"
51
+ },
52
+ code_block: {
53
+ open: "```\n",
54
+ close: "```"
55
+ }
56
+ };
57
+ const WHATSAPP_INDENT_GUARD = "⁠";
58
+ const WHATSAPP_MARKERS = [
59
+ "*",
60
+ "_",
61
+ "~",
62
+ "`"
63
+ ];
10
64
  function assertWebChannel(input) {
11
65
  if (input !== "web") throw new Error("Web channel must be 'web'");
12
66
  }
@@ -165,31 +219,90 @@ async function resolveJidToE164(jid, opts) {
165
219
  return null;
166
220
  }
167
221
  }
168
- function markdownToWhatsApp(text) {
169
- if (!text) return text;
170
- const fences = [];
171
- let result = text.replace(/```[\s\S]*?```/g, (match) => {
172
- fences.push(match);
173
- return `${WHATSAPP_FENCE_PLACEHOLDER}${fences.length - 1}${WHATSAPP_PLACEHOLDER_TERMINATOR}`;
174
- });
175
- const inlineCodes = [];
176
- result = result.replace(/`[^`\n]+`/g, (match) => {
177
- inlineCodes.push(match);
178
- return `${WHATSAPP_INLINE_CODE_PLACEHOLDER}${inlineCodes.length - 1}${WHATSAPP_PLACEHOLDER_TERMINATOR}`;
222
+ function protectWhatsAppEscapedMarkers(text) {
223
+ const placeholders = [];
224
+ for (const [start, end] of [[57344, 63743], [983040, 1048573]]) for (let codePoint = start; codePoint <= end && placeholders.length < WHATSAPP_MARKERS.length; codePoint += 1) {
225
+ const candidate = String.fromCodePoint(codePoint);
226
+ if (!text.includes(candidate)) placeholders.push(candidate);
227
+ }
228
+ if (placeholders.length < WHATSAPP_MARKERS.length) throw new Error("Unable to reserve WhatsApp formatting placeholders");
229
+ const markers = WHATSAPP_MARKERS.map((marker, index) => ({
230
+ source: `\\${marker}`,
231
+ placeholder: placeholders[index] ?? ""
232
+ }));
233
+ let protectedText = text;
234
+ for (const { source, placeholder } of markers) protectedText = protectedText.replaceAll(source, placeholder);
235
+ return {
236
+ text: protectedText,
237
+ markers
238
+ };
239
+ }
240
+ function restoreWhatsAppEscapedMarkers(text, markers) {
241
+ let restored = text;
242
+ for (const { source, placeholder } of markers) restored = restored.replaceAll(placeholder, source);
243
+ return restored;
244
+ }
245
+ function renderWhatsAppMarkdownIR(ir, escapedMarkers) {
246
+ return renderMarkdownWithMarkers(ir, {
247
+ styleMarkers: WHATSAPP_STYLE_MARKERS,
248
+ escapeText: (value) => restoreWhatsAppEscapedMarkers(value, escapedMarkers)
249
+ }, WHATSAPP_FORMAT_CAPABILITIES);
250
+ }
251
+ function prepareWhatsAppMarkdown(text, tableMode) {
252
+ const guardedIndent = /^[\t ]/u.test(text);
253
+ const escaped = protectWhatsAppEscapedMarkers(text);
254
+ const markdown = guardedIndent ? `${WHATSAPP_INDENT_GUARD}${escaped.text}` : escaped.text;
255
+ const trailingWhitespace = text.match(/\s+$/u)?.[0] ?? "";
256
+ const { ir: parsedIr, hasTables } = markdownToIRWithMeta(markdown, {
257
+ linkify: false,
258
+ autolink: false,
259
+ enableSpoilers: true,
260
+ enableHtmlUnderline: true,
261
+ enableTaskLists: true,
262
+ headingStyle: "rich",
263
+ blockquotePrefix: "> ",
264
+ tableMode: tableMode === "block" ? "code" : tableMode,
265
+ preserveSourceBlockSpacing: true
179
266
  });
180
- result = result.replace(/\*\*\*(.+?)\*\*\*/g, "*_$1_*");
181
- result = result.replace(/___(.+?)___/g, "*_$1_*");
182
- result = result.replace(/\*\*_(.+?)_\*\*/g, "*_$1_*");
183
- result = result.replace(/__\*(.+?)\*__/g, "*_$1_*");
184
- result = result.replace(/_\*\*(.+?)\*\*_/g, "*_$1_*");
185
- result = result.replace(/\*__(.+?)__\*/g, "*_$1_*");
186
- result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
187
- result = result.replace(/__(.+?)__/g, "*$1*");
188
- result = result.replace(/~~(.+?)~~/g, "~$1~");
189
- const terminator = escapeRegExp(WHATSAPP_PLACEHOLDER_TERMINATOR);
190
- result = result.replace(new RegExp(`${escapeRegExp(WHATSAPP_INLINE_CODE_PLACEHOLDER)}(\\d+)${terminator}`, "g"), (_, idx) => inlineCodes[Number(idx)] ?? "");
191
- result = result.replace(new RegExp(`${escapeRegExp(WHATSAPP_FENCE_PLACEHOLDER)}(\\d+)${terminator}`, "g"), (_, idx) => fences[Number(idx)] ?? "");
192
- return result;
267
+ let ir = parsedIr;
268
+ if (guardedIndent && ir.text.startsWith(WHATSAPP_INDENT_GUARD)) ir = sliceMarkdownIR(ir, 1, ir.text.length);
269
+ if (!hasTables && trailingWhitespace) ir.text = `${ir.text.trimEnd()}${trailingWhitespace}`;
270
+ return {
271
+ ir,
272
+ escapedMarkers: escaped.markers
273
+ };
274
+ }
275
+ function splitWhatsAppIRForChunkMode(ir, limit, chunkMode) {
276
+ if (chunkMode !== "newline") return [ir];
277
+ const chunkTexts = chunkMarkdownTextWithMode(ir.text, limit, chunkMode);
278
+ const chunks = [];
279
+ let cursor = 0;
280
+ for (const text of chunkTexts) {
281
+ const start = ir.text.indexOf(text, cursor);
282
+ if (start < 0) return [ir];
283
+ const end = start + text.length;
284
+ chunks.push(sliceMarkdownIR(ir, start, end));
285
+ cursor = end;
286
+ }
287
+ return chunks;
288
+ }
289
+ function markdownToWhatsAppChunks(text, limit, tableMode = "bullets", chunkMode = "length") {
290
+ if (!text) return [];
291
+ if (!text.trim()) return chunkMarkdownTextWithMode(text, limit, chunkMode);
292
+ const { ir, escapedMarkers } = prepareWhatsAppMarkdown(text, tableMode);
293
+ const render = (chunk) => renderWhatsAppMarkdownIR(chunk, escapedMarkers);
294
+ const rendered = render(ir);
295
+ let chunks = ir.styles.length === 0 && ir.links.length === 0 ? chunkMarkdownTextWithMode(rendered, limit, chunkMode) : splitWhatsAppIRForChunkMode(ir, limit, chunkMode).flatMap((source) => renderMarkdownIRChunksWithinLimit({
296
+ ir: source,
297
+ limit,
298
+ renderChunk: render,
299
+ measureRendered: (value) => value.length
300
+ }).map((chunk) => chunk.rendered));
301
+ if (chunkMode === "newline") chunks = chunks.map((chunk) => chunk.trimEnd()).filter(Boolean);
302
+ return chunks;
303
+ }
304
+ function markdownToWhatsApp(text, tableMode = "bullets") {
305
+ return markdownToWhatsAppChunks(text, Number.POSITIVE_INFINITY, tableMode).join("");
193
306
  }
194
307
  //#endregion
195
- export { resolveEquivalentWhatsAppDirectChatJids as a, toWhatsappJidWithLid as c, markdownToWhatsApp as i, isSelfChatMode as n, resolveJidToE164 as o, jidToE164 as r, toWhatsappJid as s, assertWebChannel as t };
308
+ export { markdownToWhatsAppChunks as a, toWhatsappJid as c, markdownToWhatsApp as i, toWhatsappJidWithLid as l, isSelfChatMode as n, resolveEquivalentWhatsAppDirectChatJids as o, jidToE164 as r, resolveJidToE164 as s, assertWebChannel as t };
@@ -0,0 +1,4 @@
1
+ import "./targets-runtime-CKFrhoX4.js";
2
+ import { sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithProfile, stripToolCallXmlTags } from "openclaw/plugin-sdk/text-chunking";
3
+ import { normalizeE164, resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/text-utility-runtime";
4
+ export { stripToolCallXmlTags as a, sanitizeAssistantVisibleTextWithProfile as i, resolveUserPath$1 as n, sanitizeAssistantVisibleText as r, normalizeE164 as t };