@openclaw/whatsapp 2026.5.7 → 2026.5.10-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 (60) hide show
  1. package/dist/{access-control-CTj7P7WP.js → access-control-CdP8S3rJ.js} +66 -111
  2. package/dist/{action-runtime-BqYZmBX_.js → action-runtime-CMhO8liB.js} +1 -1
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/api.js +120 -12
  6. package/dist/{auth-store-BNZmNP-s.js → auth-store-CjEimXKX.js} +19 -41
  7. package/dist/{channel-CTr3YjO8.js → channel-BxntAClG.js} +39 -12
  8. package/dist/channel-config-api.js +1 -1
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel-react-action-BuSTzmDX.js → channel-react-action-DJvzhIPw.js} +1 -1
  11. package/dist/{channel.runtime-C3i9Svfj.js → channel.runtime-CNdPCK3G.js} +5 -5
  12. package/dist/{channel.setup-BYtkDwHe.js → channel.setup-BqlcEVzR.js} +4 -4
  13. package/dist/{connection-controller-BCIuChXD.js → connection-controller-DX2phM7V.js} +6 -197
  14. package/dist/constants.js +1 -1
  15. package/dist/contract-api.js +7 -7
  16. package/dist/{directory-config-BNCbgnmy.js → directory-config-o-muXLgk.js} +1 -1
  17. package/dist/directory-contract-api.js +1 -1
  18. package/dist/doctor-contract-api.js +1 -1
  19. package/dist/{identity-DhaC0MoD.js → identity-KxdJfH7p.js} +1 -1
  20. package/dist/legacy-session-surface-api.js +1 -1
  21. package/dist/legacy-state-migrations-api.js +1 -1
  22. package/dist/light-runtime-api.js +3 -3
  23. package/dist/{login-DSUTCcJ7.js → login-DV99D0IN.js} +4 -3
  24. package/dist/{login-qr-dfs8tfGo.js → login-qr-cKndAXTv.js} +3 -2
  25. package/dist/login-qr-runtime.js +1 -1
  26. package/dist/{monitor-C5_C_RGJ.js → monitor-mfy3Mmq-.js} +111 -687
  27. package/dist/{outbound-adapter-CbR17Ehj.js → outbound-adapter-B9ZW8sht.js} +3 -3
  28. package/dist/{outbound-base-BmzhutvF.js → outbound-base-MVpg2zU2.js} +8 -3
  29. package/dist/{outbound-media-contract-BPdOq7Hb.js → outbound-media-contract-BzWwBppP.js} +37 -41
  30. package/dist/outbound-payload-test-api.js +1 -1
  31. package/dist/{quoted-message-BXs55Teh.js → quoted-message-DQ6hAr34.js} +1 -1
  32. package/dist/runtime-api.js +11 -10
  33. package/dist/security-contract-api.js +1 -1
  34. package/dist/send-api-DsbCqDre.js +697 -0
  35. package/dist/{send-CxctcFGT.js → send-u88yh8Hw.js} +2 -3
  36. package/dist/session-DeIU_vjJ.js +198 -0
  37. package/dist/{session.runtime-C1eSE_KK.js → session.runtime-B9VlYnWQ.js} +1 -1
  38. package/dist/{setup-surface-CF3l3gEZ.js → setup-finalize-D3YuMUBs.js} +5 -52
  39. package/dist/setup-plugin-api.js +1 -1
  40. package/dist/setup-surface-CHjYlKJc.js +45 -0
  41. package/dist/{shared-CTSTwprn.js → shared-CgYT3IzU.js} +7 -7
  42. package/dist/{state-migrations-DnCdvoYD.js → state-migrations-CtdIArlK.js} +2 -1
  43. package/dist/test-api.js +2 -2
  44. package/dist/{text-runtime-DFFwk5z6.js → text-runtime-DBKC9uEi.js} +27 -2
  45. package/package.json +6 -6
  46. package/dist/rolldown-runtime-RkAeH_Qm.js +0 -27
  47. /package/dist/{active-listener-N7GFKsuN.js → active-listener-D2NHQ5Og.js} +0 -0
  48. /package/dist/{agent-tools-login-DpRxLNRB.js → agent-tools-login-DYKYW5Nh.js} +0 -0
  49. /package/dist/{audio-preflight.runtime-Bcdsl_ri.js → audio-preflight.runtime-CIFjeaEN.js} +0 -0
  50. /package/dist/{command-policy-O8sRnqUV.js → command-policy-BgZRZ31o.js} +0 -0
  51. /package/dist/{config-schema-Dmo66Ubb.js → config-schema-cSC7M4YA.js} +0 -0
  52. /package/dist/{constants-DLGCIbfl.js → constants-yKXGCGU9.js} +0 -0
  53. /package/dist/{doctor-A9tXm9Om.js → doctor-BrgepDUP.js} +0 -0
  54. /package/dist/{group-session-contract-B8QBn0I2.js → group-session-contract-DL-SLclv.js} +0 -0
  55. /package/dist/{reply-resolver.runtime-BRCgoO4C.js → reply-resolver.runtime-CROyXXQh.js} +0 -0
  56. /package/dist/{runtime-6fZ67abR.js → runtime-DdodQB8G.js} +0 -0
  57. /package/dist/{runtime-group-policy-Gp1lQ0at.js → runtime-group-policy-BH_U23qp.js} +0 -0
  58. /package/dist/{security-contract-DqH8JDX6.js → security-contract-BzlZFreO.js} +0 -0
  59. /package/dist/{session-contract-B0efQf9P.js → session-contract-BNUscPf4.js} +0 -0
  60. /package/dist/{setup-core-DZKvpzbH.js → setup-core-DnhO0PjG.js} +0 -0
@@ -1,15 +1,20 @@
1
+ import { r as isSelfChatMode, t as text_runtime_exports } from "./text-runtime-DBKC9uEi.js";
1
2
  import { a as resolveWhatsAppAccount } from "./accounts-BDqgpFEB.js";
2
- import { r as isSelfChatMode, t as text_runtime_exports } from "./text-runtime-DFFwk5z6.js";
3
- import { a as getSelfIdentity, o as getSenderIdentity } from "./identity-DhaC0MoD.js";
4
- import { t as resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy-Gp1lQ0at.js";
3
+ import { a as getSelfIdentity, o as getSenderIdentity } from "./identity-KxdJfH7p.js";
4
+ import { t as resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy-BH_U23qp.js";
5
5
  import { defaultRuntime } from "openclaw/plugin-sdk/runtime-env";
6
6
  import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
7
7
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
8
8
  import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
9
9
  import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
10
- import { expandAllowFromWithAccessGroups, readStoreAllowFromForDmPolicy, resolveDmGroupAccessWithCommandGate, resolveDmGroupAccessWithLists, resolveEffectiveAllowFromLists } from "openclaw/plugin-sdk/security-runtime";
10
+ import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
11
11
  import { resolveGroupSessionKey } from "openclaw/plugin-sdk/session-store-runtime";
12
12
  //#region extensions/whatsapp/src/inbound-policy.ts
13
+ function normalizeWhatsAppIngressPhone(value) {
14
+ const trimmed = value.trim();
15
+ if (!trimmed) return null;
16
+ return (0, text_runtime_exports.normalizeE164)(trimmed);
17
+ }
13
18
  function resolveGroupConversationId(conversationId) {
14
19
  return resolveGroupSessionKey({
15
20
  From: conversationId,
@@ -17,11 +22,9 @@ function resolveGroupConversationId(conversationId) {
17
22
  Provider: "whatsapp"
18
23
  })?.id ?? conversationId;
19
24
  }
20
- function isNormalizedSenderAllowed(allowEntries, sender) {
21
- if (allowEntries.includes("*")) return true;
22
- const normalizedSender = (0, text_runtime_exports.normalizeE164)(sender ?? "");
23
- if (!normalizedSender) return false;
24
- return new Set(allowEntries.map((entry) => (0, text_runtime_exports.normalizeE164)(entry)).filter((entry) => Boolean(entry))).has(normalizedSender);
25
+ function maybeSamePhoneDmAllowFrom(params) {
26
+ if (params.isGroup || !params.dmSenderId || !params.policy.isSamePhone(params.dmSenderId)) return [];
27
+ return [params.dmSenderId];
25
28
  }
26
29
  function buildResolvedWhatsAppGroupConfig(params) {
27
30
  return { channels: { whatsapp: {
@@ -37,11 +40,7 @@ function resolveWhatsAppInboundPolicy(params) {
37
40
  const configuredAllowFrom = account.allowFrom ?? [];
38
41
  const dmPolicy = account.dmPolicy ?? "pairing";
39
42
  const dmAllowFrom = configuredAllowFrom.length > 0 ? configuredAllowFrom : params.selfE164 ? [params.selfE164] : [];
40
- const groupAllowFrom = account.groupAllowFrom ?? (configuredAllowFrom.length > 0 ? configuredAllowFrom : void 0) ?? [];
41
- const { effectiveGroupAllowFrom } = resolveEffectiveAllowFromLists({
42
- allowFrom: configuredAllowFrom,
43
- groupAllowFrom
44
- });
43
+ const groupAllowFrom = (Array.isArray(account.groupAllowFrom) && account.groupAllowFrom.length > 0 ? account.groupAllowFrom : void 0) ?? (configuredAllowFrom.length > 0 ? configuredAllowFrom : void 0) ?? [];
45
44
  const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg);
46
45
  const { groupPolicy, providerMissingFallbackApplied } = resolveWhatsAppRuntimeGroupPolicy({
47
46
  providerConfigPresent: params.cfg.channels?.whatsapp !== void 0,
@@ -62,15 +61,12 @@ function resolveWhatsAppInboundPolicy(params) {
62
61
  groupAllowFrom,
63
62
  isSelfChat: account.selfChatMode ?? isSelfChatMode(params.selfE164, configuredAllowFrom),
64
63
  providerMissingFallbackApplied,
65
- shouldReadStorePairingApprovals: dmPolicy !== "allowlist",
66
64
  isSamePhone,
67
- isDmSenderAllowed: (allowEntries, sender) => isSamePhone(sender) || isNormalizedSenderAllowed(allowEntries, sender),
68
- isGroupSenderAllowed: (allowEntries, sender) => isNormalizedSenderAllowed(allowEntries, sender),
69
65
  resolveConversationGroupPolicy: (conversationId) => resolveChannelGroupPolicy({
70
66
  cfg: resolvedGroupCfg,
71
67
  channel: "whatsapp",
72
68
  groupId: resolveGroupConversationId(conversationId),
73
- hasGroupAllowFrom: effectiveGroupAllowFrom.length > 0
69
+ hasGroupAllowFrom: groupAllowFrom.length > 0
74
70
  }),
75
71
  resolveConversationRequireMention: (conversationId) => resolveChannelGroupRequireMention({
76
72
  cfg: resolvedGroupCfg,
@@ -79,9 +75,40 @@ function resolveWhatsAppInboundPolicy(params) {
79
75
  })
80
76
  };
81
77
  }
78
+ async function resolveWhatsAppIngressAccess(params) {
79
+ const samePhoneDmAllowFrom = maybeSamePhoneDmAllowFrom({
80
+ isGroup: params.isGroup,
81
+ policy: params.policy,
82
+ dmSenderId: params.dmSenderId
83
+ });
84
+ const dmAllowFrom = [...params.policy.dmAllowFrom, ...samePhoneDmAllowFrom];
85
+ return await resolveStableChannelMessageIngress({
86
+ channelId: "whatsapp",
87
+ accountId: params.policy.account.accountId,
88
+ identity: {
89
+ key: "whatsapp-sender-phone",
90
+ kind: "phone",
91
+ normalize: normalizeWhatsAppIngressPhone,
92
+ sensitivity: "pii",
93
+ entryIdPrefix: "whatsapp-entry"
94
+ },
95
+ cfg: params.cfg,
96
+ useDefaultPairingStore: true,
97
+ subject: { stableId: params.senderId ?? "" },
98
+ conversation: {
99
+ kind: params.isGroup ? "group" : "direct",
100
+ id: params.conversationId
101
+ },
102
+ dmPolicy: params.policy.dmPolicy,
103
+ groupPolicy: params.policy.groupPolicy,
104
+ policy: { groupAllowFromFallbackToAllowFrom: false },
105
+ allowFrom: dmAllowFrom,
106
+ groupAllowFrom: params.policy.groupAllowFrom,
107
+ command: params.includeCommand === true ? {} : void 0
108
+ });
109
+ }
82
110
  async function resolveWhatsAppCommandAuthorized(params) {
83
- const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
84
- if (!useAccessGroups) return true;
111
+ if (!(params.cfg.commands?.useAccessGroups !== false)) return true;
85
112
  const self = getSelfIdentity(params.msg);
86
113
  const policy = params.policy ?? resolveWhatsAppInboundPolicy({
87
114
  cfg: params.cfg,
@@ -92,52 +119,16 @@ async function resolveWhatsAppCommandAuthorized(params) {
92
119
  const sender = getSenderIdentity(params.msg);
93
120
  const dmSender = sender.e164 ?? params.msg.from ?? "";
94
121
  const groupSender = sender.e164 ?? "";
95
- const normalizedSender = (0, text_runtime_exports.normalizeE164)(isGroup ? groupSender : dmSender);
96
- if (!normalizedSender) return false;
97
- const storeAllowFrom = isGroup || !policy.shouldReadStorePairingApprovals ? [] : await readStoreAllowFromForDmPolicy({
98
- provider: "whatsapp",
99
- accountId: policy.account.accountId,
100
- dmPolicy: policy.dmPolicy,
101
- shouldRead: policy.shouldReadStorePairingApprovals
102
- });
103
- const isSenderAllowed = (senderId, allowEntries) => isGroup ? policy.isGroupSenderAllowed(allowEntries, senderId) : policy.isDmSenderAllowed(allowEntries, senderId);
104
- const [allowFrom, groupAllowFrom] = await Promise.all([expandAllowFromWithAccessGroups({
105
- cfg: params.cfg,
106
- allowFrom: policy.dmAllowFrom,
107
- channel: "whatsapp",
108
- accountId: policy.account.accountId,
109
- senderId: normalizedSender,
110
- isSenderAllowed
111
- }), expandAllowFromWithAccessGroups({
122
+ if (!(0, text_runtime_exports.normalizeE164)(isGroup ? groupSender : dmSender)) return false;
123
+ return (await resolveWhatsAppIngressAccess({
112
124
  cfg: params.cfg,
113
- allowFrom: policy.groupAllowFrom,
114
- channel: "whatsapp",
115
- accountId: policy.account.accountId,
116
- senderId: normalizedSender,
117
- isSenderAllowed
118
- })]);
119
- const dmStoreAllowFrom = isGroup ? [] : await expandAllowFromWithAccessGroups({
120
- cfg: params.cfg,
121
- allowFrom: storeAllowFrom,
122
- channel: "whatsapp",
123
- accountId: policy.account.accountId,
124
- senderId: normalizedSender,
125
- isSenderAllowed
126
- });
127
- return resolveDmGroupAccessWithCommandGate({
125
+ policy,
128
126
  isGroup,
129
- dmPolicy: policy.dmPolicy,
130
- groupPolicy: policy.groupPolicy,
131
- allowFrom,
132
- groupAllowFrom,
133
- storeAllowFrom: dmStoreAllowFrom,
134
- isSenderAllowed: (allowEntries) => isGroup ? policy.isGroupSenderAllowed(allowEntries, groupSender) : policy.isDmSenderAllowed(allowEntries, dmSender),
135
- command: {
136
- useAccessGroups,
137
- allowTextCommands: true,
138
- hasControlCommand: true
139
- }
140
- }).commandAuthorized;
127
+ conversationId: params.msg.conversationId ?? params.msg.chatId ?? params.msg.from,
128
+ senderId: isGroup ? groupSender : dmSender,
129
+ dmSenderId: dmSender,
130
+ includeCommand: true
131
+ })).commandAccess.authorized;
141
132
  }
142
133
  //#endregion
143
134
  //#region extensions/whatsapp/src/inbound/access-control.ts
@@ -152,12 +143,6 @@ async function checkInboundAccessControl(params) {
152
143
  accountId: params.accountId,
153
144
  selfE164: params.selfE164
154
145
  });
155
- const storeAllowFrom = params.group ? [] : await readStoreAllowFromForDmPolicy({
156
- provider: "whatsapp",
157
- accountId: policy.account.accountId,
158
- dmPolicy: policy.dmPolicy,
159
- shouldRead: policy.shouldReadStorePairingApprovals
160
- });
161
146
  const pairingGraceMs = typeof params.pairingGraceMs === "number" && params.pairingGraceMs > 0 ? params.pairingGraceMs : PAIRING_REPLY_HISTORY_GRACE_MS;
162
147
  const suppressPairingReply = typeof params.connectedAtMs === "number" && typeof params.messageTimestampMs === "number" && params.messageTimestampMs < params.connectedAtMs - pairingGraceMs;
163
148
  warnMissingProviderGroupPolicyFallbackOnce({
@@ -166,47 +151,17 @@ async function checkInboundAccessControl(params) {
166
151
  accountId: policy.account.accountId,
167
152
  log: (message) => logWhatsAppVerbose(params.verbose, message)
168
153
  });
169
- const accessGroupSenderId = params.group ? params.senderE164 ?? params.from : params.from;
170
- const isAccessGroupSenderAllowed = (senderId, allowEntries) => {
171
- return params.group ? policy.isGroupSenderAllowed(allowEntries, senderId) : policy.isDmSenderAllowed(allowEntries, senderId);
172
- };
173
- const [allowFrom, groupAllowFrom] = await Promise.all([expandAllowFromWithAccessGroups({
174
- cfg: params.cfg,
175
- allowFrom: params.group ? policy.configuredAllowFrom : policy.dmAllowFrom,
176
- channel: "whatsapp",
177
- accountId: policy.account.accountId,
178
- senderId: accessGroupSenderId,
179
- isSenderAllowed: isAccessGroupSenderAllowed
180
- }), expandAllowFromWithAccessGroups({
154
+ const { senderAccess } = await resolveWhatsAppIngressAccess({
181
155
  cfg: params.cfg,
182
- allowFrom: policy.groupAllowFrom,
183
- channel: "whatsapp",
184
- accountId: policy.account.accountId,
185
- senderId: accessGroupSenderId,
186
- isSenderAllowed: isAccessGroupSenderAllowed
187
- })]);
188
- const dmStoreAllowFrom = params.group ? [] : await expandAllowFromWithAccessGroups({
189
- cfg: params.cfg,
190
- allowFrom: storeAllowFrom,
191
- channel: "whatsapp",
192
- accountId: policy.account.accountId,
193
- senderId: accessGroupSenderId,
194
- isSenderAllowed: isAccessGroupSenderAllowed
195
- });
196
- const access = resolveDmGroupAccessWithLists({
156
+ policy,
197
157
  isGroup: params.group,
198
- dmPolicy: policy.dmPolicy,
199
- groupPolicy: policy.groupPolicy,
200
- allowFrom,
201
- groupAllowFrom,
202
- storeAllowFrom: dmStoreAllowFrom,
203
- isSenderAllowed: (allowEntries) => {
204
- return params.group ? policy.isGroupSenderAllowed(allowEntries, params.senderE164) : policy.isDmSenderAllowed(allowEntries, params.from);
205
- }
158
+ conversationId: params.remoteJid,
159
+ senderId: params.group ? params.senderE164 : params.from,
160
+ dmSenderId: params.from
206
161
  });
207
- if (params.group && access.decision !== "allow") {
208
- if (access.reason === "groupPolicy=disabled") logWhatsAppVerbose(params.verbose, "Blocked group message (groupPolicy: disabled)");
209
- else if (access.reason === "groupPolicy=allowlist (empty allowlist)") logWhatsAppVerbose(params.verbose, "Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
162
+ if (params.group && senderAccess.decision !== "allow") {
163
+ if (senderAccess.reasonCode === "group_policy_disabled") logWhatsAppVerbose(params.verbose, "Blocked group message (groupPolicy: disabled)");
164
+ else if (senderAccess.reasonCode === "group_policy_empty_allowlist") logWhatsAppVerbose(params.verbose, "Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
210
165
  else logWhatsAppVerbose(params.verbose, `Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`);
211
166
  return {
212
167
  allowed: false,
@@ -225,7 +180,7 @@ async function checkInboundAccessControl(params) {
225
180
  resolvedAccountId: policy.account.accountId
226
181
  };
227
182
  }
228
- if (access.decision === "block" && access.reason === "dmPolicy=disabled") {
183
+ if (senderAccess.decision === "block" && senderAccess.reasonCode === "dm_policy_disabled") {
229
184
  logWhatsAppVerbose(params.verbose, "Blocked dm (dmPolicy: disabled)");
230
185
  return {
231
186
  allowed: false,
@@ -234,7 +189,7 @@ async function checkInboundAccessControl(params) {
234
189
  resolvedAccountId: policy.account.accountId
235
190
  };
236
191
  }
237
- if (access.decision === "pairing" && !policy.isSamePhone(params.from)) {
192
+ if (senderAccess.decision === "pairing" && !policy.isSamePhone(params.from)) {
238
193
  const candidate = params.from;
239
194
  if (suppressPairingReply) logWhatsAppVerbose(params.verbose, `Skipping pairing reply for historical DM from ${candidate}.`);
240
195
  else await createChannelPairingChallengeIssuer({
@@ -266,7 +221,7 @@ async function checkInboundAccessControl(params) {
266
221
  resolvedAccountId: policy.account.accountId
267
222
  };
268
223
  }
269
- if (access.decision !== "allow") {
224
+ if (senderAccess.decision !== "allow") {
270
225
  logWhatsAppVerbose(params.verbose, `Blocked unauthorized sender ${params.from} (dmPolicy=${policy.dmPolicy})`);
271
226
  return {
272
227
  allowed: false,
@@ -1,7 +1,7 @@
1
1
  import { a as resolveWhatsAppAccount } from "./accounts-BDqgpFEB.js";
2
2
  import { t as resolveWhatsAppOutboundTarget } from "./resolve-outbound-target-CQ3Y1i0g.js";
3
3
  import { t as resolveWhatsAppReactionLevel } from "./reaction-level-CRVEVAfi.js";
4
- import { r as sendReactionWhatsApp } from "./send-CxctcFGT.js";
4
+ import { r as sendReactionWhatsApp } from "./send-u88yh8Hw.js";
5
5
  import { ToolAuthorizationError, createActionGate, jsonResult, readReactionParams, readStringParam } from "openclaw/plugin-sdk/channel-actions";
6
6
  //#region extensions/whatsapp/src/action-runtime-target-auth.ts
7
7
  function resolveAuthorizedWhatsAppOutboundTarget(params) {
@@ -1,2 +1,2 @@
1
- import { t as handleWhatsAppAction } from "./action-runtime-BqYZmBX_.js";
1
+ import { t as handleWhatsAppAction } from "./action-runtime-CMhO8liB.js";
2
2
  export { handleWhatsAppAction };
@@ -1,2 +1,2 @@
1
- import { t as handleWhatsAppAction } from "./action-runtime-BqYZmBX_.js";
1
+ import { t as handleWhatsAppAction } from "./action-runtime-CMhO8liB.js";
2
2
  export { handleWhatsAppAction };
package/dist/api.js CHANGED
@@ -1,23 +1,131 @@
1
+ import { a as markdownToWhatsApp, i as jidToE164, n as assertWebChannel, o as resolveJidToE164, r as isSelfChatMode, s as toWhatsappJid, t as text_runtime_exports } from "./text-runtime-DBKC9uEi.js";
1
2
  import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./account-ids-DG18xddX.js";
2
3
  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 "./accounts-BDqgpFEB.js";
3
4
  import { a as normalizeWhatsAppAllowFromEntries, c as normalizeWhatsAppTarget, i as looksLikeWhatsAppTargetId, r as isWhatsAppUserTarget, s as normalizeWhatsAppMessagingTarget, t as isWhatsAppGroupJid } from "./normalize-target-nXxC_hxG.js";
4
5
  import { t as resolveWhatsAppOutboundTarget } from "./resolve-outbound-target-CQ3Y1i0g.js";
5
6
  import "./reaction-level-CRVEVAfi.js";
6
- import { a as markdownToWhatsApp, i as jidToE164, n as assertWebChannel, o as resolveJidToE164, r as isSelfChatMode, s as toWhatsappJid, t as text_runtime_exports } from "./text-runtime-DFFwk5z6.js";
7
- import "./send-CxctcFGT.js";
8
- import { t as whatsappPlugin } from "./channel-CTr3YjO8.js";
9
- import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-base-BmzhutvF.js";
10
- import { t as whatsappCommandPolicy } from "./command-policy-O8sRnqUV.js";
11
- import { a as resolveWhatsAppGroupToolPolicy, i as resolveWhatsAppGroupRequireMention, o as resolveWhatsAppGroupIntroHint } from "./shared-CTSTwprn.js";
12
- import "./config-schema-Dmo66Ubb.js";
13
- import { t as whatsappSetupPlugin } from "./channel.setup-BYtkDwHe.js";
14
- import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-DLGCIbfl.js";
15
- import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./directory-config-BNCbgnmy.js";
16
- import { t as __testing } from "./access-control-CTj7P7WP.js";
7
+ import "./send-u88yh8Hw.js";
8
+ import { t as whatsappPlugin } from "./channel-BxntAClG.js";
9
+ import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-base-MVpg2zU2.js";
10
+ import { t as whatsappCommandPolicy } from "./command-policy-BgZRZ31o.js";
11
+ import { a as resolveWhatsAppGroupToolPolicy, i as resolveWhatsAppGroupRequireMention, o as resolveWhatsAppGroupIntroHint } from "./shared-CgYT3IzU.js";
12
+ import "./config-schema-cSC7M4YA.js";
13
+ import { t as whatsappSetupPlugin } from "./channel.setup-BqlcEVzR.js";
14
+ import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-yKXGCGU9.js";
15
+ import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./directory-config-o-muXLgk.js";
16
+ import { t as __testing } from "./access-control-CdP8S3rJ.js";
17
+ import { p as extractText, t as createWebSendApi } from "./send-api-DsbCqDre.js";
18
+ import { r as waitForWaConnection, t as createWaSocket } from "./session-DeIU_vjJ.js";
17
19
  import "openclaw/plugin-sdk/channel-actions";
18
20
  import "openclaw/plugin-sdk/account-resolution";
19
21
  import "openclaw/plugin-sdk/core";
20
22
  import "openclaw/plugin-sdk/account-id";
23
+ //#region extensions/whatsapp/src/qa-driver.runtime.ts
24
+ function normalizeObservedMessage(message, authDir) {
25
+ if (message.key.fromMe) return null;
26
+ const text = extractText(message.message ?? void 0);
27
+ if (!text) return null;
28
+ const fromJid = message.key.remoteJid ?? void 0;
29
+ return {
30
+ fromJid,
31
+ fromPhoneE164: fromJid ? jidToE164(fromJid, { authDir }) : null,
32
+ messageId: message.key.id ?? void 0,
33
+ observedAt: (/* @__PURE__ */ new Date()).toISOString(),
34
+ text
35
+ };
36
+ }
37
+ function closeSocket(sock) {
38
+ const maybeEnd = sock.end;
39
+ if (typeof maybeEnd === "function") {
40
+ maybeEnd.call(sock);
41
+ return;
42
+ }
43
+ const maybeClose = sock.ws?.close;
44
+ if (typeof maybeClose === "function") maybeClose.call(sock.ws);
45
+ }
46
+ async function startWhatsAppQaDriverSession(params) {
47
+ const sock = await createWaSocket(false, false, { authDir: params.authDir });
48
+ const observedMessages = [];
49
+ const waiters = [];
50
+ let closed = false;
51
+ const removeWaiter = (waiter) => {
52
+ const index = waiters.indexOf(waiter);
53
+ if (index >= 0) waiters.splice(index, 1);
54
+ clearTimeout(waiter.timeout);
55
+ };
56
+ const observe = (message) => {
57
+ observedMessages.push(message);
58
+ for (const waiter of waiters.slice()) {
59
+ if (!waiter.predicate(message)) continue;
60
+ removeWaiter(waiter);
61
+ waiter.resolve(message);
62
+ }
63
+ };
64
+ const onMessagesUpsert = (event) => {
65
+ for (const rawMessage of event.messages ?? []) {
66
+ const observed = normalizeObservedMessage(rawMessage, params.authDir);
67
+ if (observed) observe(observed);
68
+ }
69
+ };
70
+ const removeMessageListener = () => {
71
+ sock.ev.off?.("messages.upsert", onMessagesUpsert);
72
+ };
73
+ const closeSessionResources = (waiterError) => {
74
+ if (closed) return;
75
+ closed = true;
76
+ for (const waiter of waiters.slice()) {
77
+ removeWaiter(waiter);
78
+ if (waiterError) waiter.reject(waiterError);
79
+ }
80
+ removeMessageListener();
81
+ closeSocket(sock);
82
+ };
83
+ sock.ev.on("messages.upsert", onMessagesUpsert);
84
+ let connectionTimeout;
85
+ try {
86
+ await Promise.race([waitForWaConnection(sock), new Promise((_, reject) => {
87
+ connectionTimeout = setTimeout(() => reject(/* @__PURE__ */ new Error("timed out waiting for WhatsApp QA driver session")), params.connectionTimeoutMs ?? 45e3);
88
+ connectionTimeout.unref?.();
89
+ })]);
90
+ } catch (error) {
91
+ closeSessionResources(error instanceof Error ? error : /* @__PURE__ */ new Error("failed starting WhatsApp QA driver session"));
92
+ throw error;
93
+ } finally {
94
+ if (connectionTimeout) clearTimeout(connectionTimeout);
95
+ }
96
+ const sendApi = createWebSendApi({
97
+ sock,
98
+ defaultAccountId: "qa-driver"
99
+ });
100
+ return {
101
+ async close() {
102
+ closeSessionResources(/* @__PURE__ */ new Error("WhatsApp QA driver session closed"));
103
+ },
104
+ getObservedMessages() {
105
+ return [...observedMessages];
106
+ },
107
+ async sendText(to, text) {
108
+ return { messageId: (await sendApi.sendMessage(to, text)).messageId };
109
+ },
110
+ async waitForMessage(params) {
111
+ const existing = observedMessages.find(params.match);
112
+ if (existing) return existing;
113
+ return await new Promise((resolve, reject) => {
114
+ const waiter = {
115
+ predicate: params.match,
116
+ resolve,
117
+ reject,
118
+ timeout: setTimeout(() => {
119
+ removeWaiter(waiter);
120
+ reject(/* @__PURE__ */ new Error("timed out waiting for WhatsApp QA driver message"));
121
+ }, params.timeoutMs)
122
+ };
123
+ waiters.push(waiter);
124
+ });
125
+ }
126
+ };
127
+ }
128
+ //#endregion
21
129
  var CONFIG_DIR = text_runtime_exports.CONFIG_DIR;
22
130
  var DEFAULT_LOG_DIR = text_runtime_exports.DEFAULT_LOG_DIR;
23
131
  var DEFAULT_LOG_FILE = text_runtime_exports.DEFAULT_LOG_FILE;
@@ -145,4 +253,4 @@ var summarizeStringEntries = text_runtime_exports.summarizeStringEntries;
145
253
  var toPinoLikeLogger = text_runtime_exports.toPinoLikeLogger;
146
254
  var truncateUtf16Safe = text_runtime_exports.truncateUtf16Safe;
147
255
  var withTimeout = text_runtime_exports.withTimeout;
148
- export { CONFIG_DIR, DEFAULT_LOG_DIR, DEFAULT_LOG_FILE, DEFAULT_WEB_MEDIA_BYTES, DEFAULT_WHATSAPP_MEDIA_MAX_MB, FILE_REF_EXTENSIONS_WITH_TLD, SYSTEM_MARK, WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, __test__, asNullableObjectRecord, asNullableRecord, asOptionalObjectRecord, asOptionalRecord, asRecord, assertWebChannel, bindAbortRelay, buildTimeoutAbortSignal, chunkItems, chunkMarkdownIR, clamp, clampInt, clampNumber, convertMarkdownTables, createScopedExpiringIdCache, diagnosticLogger, displayPath, displayString, ensureDir, escapeRegExp, fetchWithTimeout, findCodeRegions, getChildLogger, getDefaultRedactPatterns, getDiagnosticSessionStateCountForTest, getLogger, getResolvedLoggerSettings, hasAnyWhatsAppAuth, hasNonEmptyString, hasOrphanReasoningCloseBoundary, hasSystemMark, isAutoLinkedFileRef, isFileLogLevelEnabled, isInsideCode, isRecord, isSelfChatMode, isWhatsAppGroupJid, isWhatsAppUserTarget, jidToE164, listEnabledWhatsAppAccounts, listAccountIds as listWhatsAppAccountIds, listWhatsAppAuthDirs, listWhatsAppDirectoryGroupsFromConfig, listWhatsAppDirectoryPeersFromConfig, localeLowercasePreservingWhitespace, logActiveRuns, logDebug, logError, logInfo, logLaneDequeue, logLaneEnqueue, logMessageProcessed, logMessageQueued, logRunAttempt, logSessionStateChange, logSuccess, logToolLoopAction, logWarn, logWebhookError, logWebhookProcessed, logWebhookReceived, looksLikeWhatsAppTargetId, lowercasePreservingWhitespace, markdownToIR, markdownToIRWithMeta, markdownToWhatsApp, normalizeArrayBackedTrimmedStringList, normalizeAtHashSlug, normalizeCsvOrLooseStringList, normalizeE164, normalizeFastMode, normalizeHyphenSlug, normalizeLowercaseStringOrEmpty, normalizeNullableString, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeOptionalThreadValue, normalizeOptionalTrimmedStringList, normalizeSingleOrTrimmedStringList, normalizeStringEntries, normalizeStringEntriesLower, normalizeStringifiedOptionalString, normalizeTrimmedStringList, normalizeWhatsAppAllowFromEntries, normalizeWhatsAppMessagingTarget, normalizeWhatsAppTarget, parseInlineDirectives, pathExists, prefixSystemMessage, readStringField, readStringValue, redactIdentifier, redactSensitiveLines, redactSensitiveText, redactToolDetail, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers, resetDiagnosticStateForTest, resetLogger, resolveConfigDir, resolveDefaultWhatsAppAccountId, resolveGlobalMap, resolveGlobalSingleton, resolveHomeDir, resolveJidToE164, resolvePrimaryStringValue, resolveReactionLevel, resolveRedactOptions, resolveStuckSessionWarnMs, resolveUserPath, resolveWhatsAppAccount, resolveWhatsAppAuthDir, resolveWhatsAppGroupIntroHint, resolveWhatsAppGroupRequireMention, resolveWhatsAppGroupToolPolicy, resolveWhatsAppMediaMaxBytes, resolveWhatsAppOutboundTarget, safeParseJson, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithOptions, sanitizeAssistantVisibleTextWithProfile, sanitizeReplyDirectiveId, sanitizeTerminalText, setLoggerOverride, sha256HexPrefix, shortenHomeInString, shortenHomePath, sleep, sliceMarkdownIR, sliceUtf16Safe, startDiagnosticHeartbeat, stopDiagnosticHeartbeat, stripAssistantInternalScaffolding, stripDowngradedToolCallText, stripInlineDirectiveTagsForDelivery, stripInlineDirectiveTagsForDisplay, stripInlineDirectiveTagsFromMessageForDisplay, stripMarkdown, stripMinimaxToolCallXml, stripReasoningTagsFromText, stripToolCallXmlTags, summarizeStringEntries, toPinoLikeLogger, toWhatsappJid, truncateUtf16Safe, __testing as whatsappAccessControlTesting, whatsappCommandPolicy, whatsappPlugin, whatsappSetupPlugin, withTimeout };
256
+ export { CONFIG_DIR, DEFAULT_LOG_DIR, DEFAULT_LOG_FILE, DEFAULT_WEB_MEDIA_BYTES, DEFAULT_WHATSAPP_MEDIA_MAX_MB, FILE_REF_EXTENSIONS_WITH_TLD, SYSTEM_MARK, WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, __test__, asNullableObjectRecord, asNullableRecord, asOptionalObjectRecord, asOptionalRecord, asRecord, assertWebChannel, bindAbortRelay, buildTimeoutAbortSignal, chunkItems, chunkMarkdownIR, clamp, clampInt, clampNumber, convertMarkdownTables, createScopedExpiringIdCache, diagnosticLogger, displayPath, displayString, ensureDir, escapeRegExp, fetchWithTimeout, findCodeRegions, getChildLogger, getDefaultRedactPatterns, getDiagnosticSessionStateCountForTest, getLogger, getResolvedLoggerSettings, hasAnyWhatsAppAuth, hasNonEmptyString, hasOrphanReasoningCloseBoundary, hasSystemMark, isAutoLinkedFileRef, isFileLogLevelEnabled, isInsideCode, isRecord, isSelfChatMode, isWhatsAppGroupJid, isWhatsAppUserTarget, jidToE164, listEnabledWhatsAppAccounts, listAccountIds as listWhatsAppAccountIds, listWhatsAppAuthDirs, listWhatsAppDirectoryGroupsFromConfig, listWhatsAppDirectoryPeersFromConfig, localeLowercasePreservingWhitespace, logActiveRuns, logDebug, logError, logInfo, logLaneDequeue, logLaneEnqueue, logMessageProcessed, logMessageQueued, logRunAttempt, logSessionStateChange, logSuccess, logToolLoopAction, logWarn, logWebhookError, logWebhookProcessed, logWebhookReceived, looksLikeWhatsAppTargetId, lowercasePreservingWhitespace, markdownToIR, markdownToIRWithMeta, markdownToWhatsApp, normalizeArrayBackedTrimmedStringList, normalizeAtHashSlug, normalizeCsvOrLooseStringList, normalizeE164, normalizeFastMode, normalizeHyphenSlug, normalizeLowercaseStringOrEmpty, normalizeNullableString, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeOptionalThreadValue, normalizeOptionalTrimmedStringList, normalizeSingleOrTrimmedStringList, normalizeStringEntries, normalizeStringEntriesLower, normalizeStringifiedOptionalString, normalizeTrimmedStringList, normalizeWhatsAppAllowFromEntries, normalizeWhatsAppMessagingTarget, normalizeWhatsAppTarget, parseInlineDirectives, pathExists, prefixSystemMessage, readStringField, readStringValue, redactIdentifier, redactSensitiveLines, redactSensitiveText, redactToolDetail, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers, resetDiagnosticStateForTest, resetLogger, resolveConfigDir, resolveDefaultWhatsAppAccountId, resolveGlobalMap, resolveGlobalSingleton, resolveHomeDir, resolveJidToE164, resolvePrimaryStringValue, resolveReactionLevel, resolveRedactOptions, resolveStuckSessionWarnMs, resolveUserPath, resolveWhatsAppAccount, resolveWhatsAppAuthDir, resolveWhatsAppGroupIntroHint, resolveWhatsAppGroupRequireMention, resolveWhatsAppGroupToolPolicy, resolveWhatsAppMediaMaxBytes, resolveWhatsAppOutboundTarget, safeParseJson, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithOptions, sanitizeAssistantVisibleTextWithProfile, sanitizeReplyDirectiveId, sanitizeTerminalText, setLoggerOverride, sha256HexPrefix, shortenHomeInString, shortenHomePath, sleep, sliceMarkdownIR, sliceUtf16Safe, startDiagnosticHeartbeat, startWhatsAppQaDriverSession, stopDiagnosticHeartbeat, stripAssistantInternalScaffolding, stripDowngradedToolCallText, stripInlineDirectiveTagsForDelivery, stripInlineDirectiveTagsForDisplay, stripInlineDirectiveTagsFromMessageForDisplay, stripMarkdown, stripMinimaxToolCallXml, stripReasoningTagsFromText, stripToolCallXmlTags, summarizeStringEntries, toPinoLikeLogger, toWhatsappJid, truncateUtf16Safe, __testing as whatsappAccessControlTesting, whatsappCommandPolicy, whatsappPlugin, whatsappSetupPlugin, withTimeout };
@@ -1,50 +1,32 @@
1
+ import { t as text_runtime_exports } from "./text-runtime-DBKC9uEi.js";
1
2
  import { n as resolveWebCredsBackupPath, r as resolveWebCredsPath } from "./creds-files-_2V8jzQa.js";
2
- import { t as text_runtime_exports } from "./text-runtime-DFFwk5z6.js";
3
- import { c as resolveComparableIdentity } from "./identity-DhaC0MoD.js";
3
+ import { c as resolveComparableIdentity } from "./identity-KxdJfH7p.js";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { resolveOAuthDir as resolveOAuthDir$1 } from "openclaw/plugin-sdk/state-paths";
7
7
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
8
8
  import { defaultRuntime, getChildLogger, info, success } from "openclaw/plugin-sdk/runtime-env";
9
+ import { replaceFileAtomic } from "openclaw/plugin-sdk/security-runtime";
9
10
  import fs$1 from "node:fs/promises";
10
- import { randomUUID } from "node:crypto";
11
11
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
12
12
  //#region extensions/whatsapp/src/creds-persistence.ts
13
13
  const CREDS_FILE_MODE = 384;
14
14
  const CREDS_SAVE_FLUSH_TIMEOUT_MS = 15e3;
15
15
  const credsSaveQueues = /* @__PURE__ */ new Map();
16
16
  async function stringifyCreds(creds) {
17
- const { BufferJSON } = await import("./session.runtime-C1eSE_KK.js").then((n) => n.a);
17
+ const { BufferJSON } = await import("./session.runtime-B9VlYnWQ.js").then((n) => n.a);
18
18
  return JSON.stringify(creds, BufferJSON.replacer);
19
19
  }
20
- async function syncDirectory(dirPath) {
21
- let handle;
22
- try {
23
- handle = await fs$1.open(dirPath, "r");
24
- await handle.sync();
25
- } catch {} finally {
26
- await handle?.close().catch(() => {});
27
- }
28
- }
29
20
  async function writeCredsJsonAtomically(authDir, creds) {
30
- const credsPath = resolveWebCredsPath(authDir);
31
- const tempPath = path.join(authDir, `.creds.${process.pid}.${randomUUID()}.tmp`);
32
- const json = await stringifyCreds(creds);
33
- let handle;
34
- try {
35
- handle = await fs$1.open(tempPath, "w", CREDS_FILE_MODE);
36
- await handle.writeFile(json, { encoding: "utf-8" });
37
- await handle.sync();
38
- await handle.close();
39
- handle = void 0;
40
- await fs$1.rename(tempPath, credsPath);
41
- await fs$1.chmod(credsPath, CREDS_FILE_MODE).catch(() => {});
42
- await syncDirectory(path.dirname(credsPath));
43
- } catch (error) {
44
- await handle?.close().catch(() => {});
45
- await fs$1.rm(tempPath, { force: true }).catch(() => {});
46
- throw error;
47
- }
21
+ await replaceFileAtomic({
22
+ filePath: resolveWebCredsPath(authDir),
23
+ content: await stringifyCreds(creds),
24
+ dirMode: 448,
25
+ mode: CREDS_FILE_MODE,
26
+ tempPrefix: ".creds",
27
+ syncTempFile: true,
28
+ syncParentDir: true
29
+ });
48
30
  }
49
31
  function enqueueCredsSave(authDir, saveCreds, onError) {
50
32
  const next = (credsSaveQueues.get(authDir) ?? Promise.resolve()).then(() => saveCreds()).catch((error) => {
@@ -106,7 +88,6 @@ async function waitForWebAuthBarrier(authDir, context) {
106
88
  }
107
89
  async function restoreCredsFromBackupIfNeeded(authDir) {
108
90
  const logger = getChildLogger({ module: "web-session" });
109
- let tempRestorePath = null;
110
91
  try {
111
92
  const credsPath = resolveWebCredsPath(authDir);
112
93
  const backupPath = resolveWebCredsBackupPath(authDir);
@@ -119,19 +100,16 @@ async function restoreCredsFromBackupIfNeeded(authDir) {
119
100
  if (!backupRaw) return false;
120
101
  if (!(await fs$1.lstat(backupPath).catch(() => null))?.isFile()) return false;
121
102
  JSON.parse(backupRaw);
122
- tempRestorePath = path.join(authDir, `.creds.restore-${randomUUID()}.tmp`);
123
- await fs$1.writeFile(tempRestorePath, backupRaw, {
124
- encoding: "utf-8",
103
+ await replaceFileAtomic({
104
+ filePath: credsPath,
105
+ content: backupRaw,
106
+ dirMode: 448,
125
107
  mode: 384,
126
- flag: "wx"
108
+ tempPrefix: ".creds.restore"
127
109
  });
128
- await fs$1.rename(tempRestorePath, credsPath);
129
- tempRestorePath = null;
130
110
  logger.warn({ credsPath }, "restored corrupted WhatsApp creds.json from backup");
131
111
  return true;
132
- } catch {} finally {
133
- if (tempRestorePath) await fs$1.rm(tempRestorePath, { force: true }).catch(() => {});
134
- }
112
+ } catch {}
135
113
  return false;
136
114
  }
137
115
  async function webAuthExists(authDir = resolveDefaultWebAuthDir()) {
@@ -4,16 +4,16 @@ import { c as normalizeWhatsAppTarget, i as looksLikeWhatsAppTargetId, n as isWh
4
4
  import { t as resolveWhatsAppOutboundTarget } from "./resolve-outbound-target-CQ3Y1i0g.js";
5
5
  import { t as resolveWhatsAppReactionLevel } from "./reaction-level-CRVEVAfi.js";
6
6
  import "./normalize-Y3eBdM8a.js";
7
- import { r as normalizeWhatsAppPayloadTextPreservingIndentation } from "./outbound-media-contract-BPdOq7Hb.js";
8
- import { i as sendTypingWhatsApp, n as sendPollWhatsApp, t as sendMessageWhatsApp } from "./send-CxctcFGT.js";
9
- import { t as createWhatsAppLoginTool } from "./agent-tools-login-DpRxLNRB.js";
10
- import { t as createWhatsAppOutboundBase } from "./outbound-base-BmzhutvF.js";
11
- import { t as getWhatsAppRuntime } from "./runtime-6fZ67abR.js";
12
- import { t as whatsappCommandPolicy } from "./command-policy-O8sRnqUV.js";
13
- import { a as resolveWhatsAppGroupToolPolicy, c as formatWhatsAppConfigAllowFromEntries, i as resolveWhatsAppGroupRequireMention, n as loadWhatsAppChannelRuntime, o as resolveWhatsAppGroupIntroHint, r as whatsappSetupWizardProxy, s as resolveWhatsAppMentionStripRegexes, t as createWhatsAppPluginBase } from "./shared-CTSTwprn.js";
14
- import { d as readWebAuthExistsForDecision, n as WHATSAPP_AUTH_UNSTABLE_CODE } from "./auth-store-BNZmNP-s.js";
15
- import { t as whatsappSetupAdapter } from "./setup-core-DZKvpzbH.js";
16
- import { t as detectWhatsAppLegacyStateMigrations } from "./state-migrations-DnCdvoYD.js";
7
+ import { r as normalizeWhatsAppPayloadTextPreservingIndentation } from "./outbound-media-contract-BzWwBppP.js";
8
+ import { i as sendTypingWhatsApp, n as sendPollWhatsApp, t as sendMessageWhatsApp } from "./send-u88yh8Hw.js";
9
+ import { t as createWhatsAppLoginTool } from "./agent-tools-login-DYKYW5Nh.js";
10
+ import { t as createWhatsAppOutboundBase } from "./outbound-base-MVpg2zU2.js";
11
+ import { t as getWhatsAppRuntime } from "./runtime-DdodQB8G.js";
12
+ import { t as whatsappCommandPolicy } from "./command-policy-BgZRZ31o.js";
13
+ import { a as resolveWhatsAppGroupToolPolicy, c as formatWhatsAppConfigAllowFromEntries, i as resolveWhatsAppGroupRequireMention, n as loadWhatsAppChannelRuntime, o as resolveWhatsAppGroupIntroHint, r as whatsappSetupWizardProxy, s as resolveWhatsAppMentionStripRegexes, t as createWhatsAppPluginBase } from "./shared-CgYT3IzU.js";
14
+ import { d as readWebAuthExistsForDecision, n as WHATSAPP_AUTH_UNSTABLE_CODE } from "./auth-store-CjEimXKX.js";
15
+ import { t as whatsappSetupAdapter } from "./setup-core-DnhO0PjG.js";
16
+ import { t as detectWhatsAppLegacyStateMigrations } from "./state-migrations-CtdIArlK.js";
17
17
  import { createActionGate as createActionGate$1 } from "openclaw/plugin-sdk/channel-actions";
18
18
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
19
19
  import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core";
@@ -23,6 +23,7 @@ import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
23
23
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
24
24
  import { asString, collectIssuesForEnabledAccounts, createAsyncComputedAccountStatusAdapter, createDefaultChannelRuntimeState, isRecord } from "openclaw/plugin-sdk/status-helpers";
25
25
  import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
26
+ import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
26
27
  import { chunkText } from "openclaw/plugin-sdk/reply-chunking";
27
28
  //#region extensions/whatsapp/src/approval-auth.ts
28
29
  function normalizeWhatsAppApproverId(value) {
@@ -120,6 +121,31 @@ const whatsappChannelOutbound = {
120
121
  text: normalizeWhatsAppChannelPayloadText(payload.text)
121
122
  })
122
123
  };
124
+ function toWhatsAppMessageSendResult(result, replyToId) {
125
+ const source = result;
126
+ const receipt = result.receipt ?? createMessageReceiptFromOutboundResults({
127
+ results: result.messageId ? [{
128
+ channel: "whatsapp",
129
+ messageId: result.messageId,
130
+ toJid: source.toJid
131
+ }] : [],
132
+ kind: "text",
133
+ ...replyToId ? { replyToId } : {}
134
+ });
135
+ return {
136
+ messageId: result.messageId || receipt.primaryPlatformMessageId,
137
+ receipt
138
+ };
139
+ }
140
+ const whatsappMessageAdapter = defineChannelMessageAdapter({
141
+ id: "whatsapp",
142
+ durableFinal: { capabilities: {
143
+ text: true,
144
+ replyTo: true,
145
+ messageSendingHooks: true
146
+ } },
147
+ send: { text: async (ctx) => toWhatsAppMessageSendResult(await whatsappChannelOutbound.sendText({ ...ctx }), ctx.replyToId) }
148
+ });
123
149
  //#endregion
124
150
  //#region extensions/whatsapp/src/heartbeat.ts
125
151
  async function checkWhatsAppHeartbeatReady(params) {
@@ -296,8 +322,8 @@ function collectWhatsAppStatusIssues(accounts) {
296
322
  }
297
323
  //#endregion
298
324
  //#region extensions/whatsapp/src/channel.ts
299
- const loadWhatsAppDirectoryConfig = createLazyRuntimeModule(() => import("./directory-config-BNCbgnmy.js").then((n) => n.t));
300
- const loadWhatsAppChannelReactAction = createLazyRuntimeModule(() => import("./channel-react-action-BuSTzmDX.js"));
325
+ const loadWhatsAppDirectoryConfig = createLazyRuntimeModule(() => import("./directory-config-o-muXLgk.js").then((n) => n.t));
326
+ const loadWhatsAppChannelReactAction = createLazyRuntimeModule(() => import("./channel-react-action-DJvzhIPw.js"));
301
327
  function parseWhatsAppExplicitTarget(raw) {
302
328
  const normalized = normalizeWhatsAppTarget(raw);
303
329
  if (!normalized) return null;
@@ -365,6 +391,7 @@ const whatsappPlugin = createChatChannelPlugin({
365
391
  hint: "<E.164|group JID|newsletter JID>"
366
392
  }
367
393
  },
394
+ message: whatsappMessageAdapter,
368
395
  directory: {
369
396
  self: async ({ cfg, accountId }) => {
370
397
  const account = resolveWhatsAppAccount({
@@ -1,2 +1,2 @@
1
- import { t as WhatsAppChannelConfigSchema } from "./config-schema-Dmo66Ubb.js";
1
+ import { t as WhatsAppChannelConfigSchema } from "./config-schema-cSC7M4YA.js";
2
2
  export { WhatsAppChannelConfigSchema };
@@ -1,2 +1,2 @@
1
- import { t as whatsappPlugin } from "./channel-CTr3YjO8.js";
1
+ import { t as whatsappPlugin } from "./channel-BxntAClG.js";
2
2
  export { whatsappPlugin };