@openclaw/whatsapp 2026.5.22 → 2026.5.24-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 (35) hide show
  1. package/dist/{action-runtime-D4UqEXqf.js → action-runtime-DGx8vCsS.js} +1 -1
  2. package/dist/action-runtime-api.js +1 -1
  3. package/dist/action-runtime.runtime.js +1 -1
  4. package/dist/api.js +6 -6
  5. package/dist/approval-handler.runtime-j8U6lgcp.js +171 -0
  6. package/dist/approval-resolver-e1hAVRRy.js +15 -0
  7. package/dist/{auth-store-GTQvJznL.js → auth-store-BrKb0mwT.js} +2 -2
  8. package/dist/{channel-BQqQ-Gp5.js → channel-DOxZCBxE.js} +415 -34
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel-react-action-CHdR3gbn.js → channel-react-action-O7asbQQ1.js} +2 -2
  11. package/dist/{channel.runtime-BZ_ya3mR.js → channel.runtime-B0cFknwD.js} +4 -4
  12. package/dist/{channel.setup-vWI0fm8Y.js → channel.setup-Bk7Hmp4a.js} +2 -2
  13. package/dist/{connection-controller-9TXWZRhx.js → connection-controller-ja6z4F_k.js} +2 -2
  14. package/dist/light-runtime-api.js +1 -1
  15. package/dist/{login-nJwaNuC-.js → login-BEkrpJbZ.js} +3 -3
  16. package/dist/{login-qr-l4UaR8Dj.js → login-qr-BVRx8j_M.js} +3 -3
  17. package/dist/login-qr-runtime.js +1 -1
  18. package/dist/{monitor-ClhD-fQ6.js → monitor-BU1ydkuF.js} +324 -87
  19. package/dist/{outbound-adapter-ltiZkW9L.js → outbound-adapter-BObZXXzU.js} +1 -1
  20. package/dist/outbound-payload-test-api.js +1 -1
  21. package/dist/{runtime-CboC4QhQ.js → runtime-BfAdAEYT.js} +2 -2
  22. package/dist/runtime-api.js +8 -8
  23. package/dist/runtime-setter-api.js +1 -1
  24. package/dist/send-C9jEw6hx.js +625 -0
  25. package/dist/{send-api-CXPUXbUL.js → send-api-CC-vHBWG.js} +2 -2
  26. package/dist/{session-CPsgRMMa.js → session-CDgpDc0l.js} +2 -2
  27. package/dist/{session.runtime-C6uBkEaI.js → session.runtime-LZFdq3yb.js} +1 -1
  28. package/dist/{setup-core-BTDKEc-x.js → setup-core-Ce-b_wzQ.js} +2 -2
  29. package/dist/{setup-finalize-DUYdQs-v.js → setup-finalize-BqOudqJw.js} +2 -2
  30. package/dist/setup-plugin-api.js +1 -1
  31. package/dist/{setup-surface-Uoh9W_29.js → setup-surface-CAchPwW5.js} +2 -2
  32. package/dist/test-api.js +1 -1
  33. package/npm-shrinkwrap.json +3 -3
  34. package/package.json +4 -4
  35. package/dist/send-E_kMiuZP.js +0 -269
@@ -1,16 +1,16 @@
1
1
  import { o as resolveWebCredsBackupPath, r as hasWebCredsSync, s as resolveWebCredsPath } from "./creds-files-B1kSWtBg.js";
2
- import { n as whatsAppActionRuntime, t as handleWhatsAppAction } from "./action-runtime-D4UqEXqf.js";
2
+ import { n as whatsAppActionRuntime, t as handleWhatsAppAction } from "./action-runtime-DGx8vCsS.js";
3
+ import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-C9jEw6hx.js";
4
+ import { r as setWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
3
5
  import { n as getStatusCode, t as formatError } from "./session-errors-BAj9D2La.js";
4
- import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-E_kMiuZP.js";
5
6
  import { startWebLoginWithQr, waitForWebLogin } from "./login-qr-runtime.js";
6
7
  import { t as createWhatsAppLoginTool } from "./agent-tools-login-BDzvb3ee.js";
7
- import { n as setWhatsAppRuntime } from "./runtime-CboC4QhQ.js";
8
- import { C as waitForCredsSaveQueue, T as writeCredsJsonAtomically, _ as readWebSelfIdentity, a as formatWhatsAppWebAuthStatusState, b as restoreCredsFromBackupIfNeeded, c as logoutWeb, d as readWebAuthExistsBestEffort, f as readWebAuthExistsForDecision, g as readWebSelfId, h as readWebAuthState, l as pickWebChannel, m as readWebAuthSnapshotBestEffort, n as WHATSAPP_AUTH_UNSTABLE_CODE, o as getWebAuthAgeMs, p as readWebAuthSnapshot, r as WhatsAppAuthUnstableError, s as logWebSelfId, t as WA_WEB_AUTH_DIR, u as readCredsJsonRaw, v as readWebSelfIdentityForDecision, w as waitForCredsSaveQueueWithTimeout, x as webAuthExists, y as resolveDefaultWebAuthDir } from "./auth-store-GTQvJznL.js";
8
+ import { C as waitForCredsSaveQueue, T as writeCredsJsonAtomically, _ as readWebSelfIdentity, a as formatWhatsAppWebAuthStatusState, b as restoreCredsFromBackupIfNeeded, c as logoutWeb, d as readWebAuthExistsBestEffort, f as readWebAuthExistsForDecision, g as readWebSelfId, h as readWebAuthState, l as pickWebChannel, m as readWebAuthSnapshotBestEffort, n as WHATSAPP_AUTH_UNSTABLE_CODE, o as getWebAuthAgeMs, p as readWebAuthSnapshot, r as WhatsAppAuthUnstableError, s as logWebSelfId, t as WA_WEB_AUTH_DIR, u as readCredsJsonRaw, v as readWebSelfIdentityForDecision, w as waitForCredsSaveQueueWithTimeout, x as webAuthExists, y as resolveDefaultWebAuthDir } from "./auth-store-BrKb0mwT.js";
9
9
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
10
- import { c as extractContactContext, d as extractMediaPlaceholder, p as extractText, u as extractLocationData } from "./send-api-CXPUXbUL.js";
11
- import { n as newConnectionId, r as waitForWaConnection, t as createWaSocket } from "./session-CPsgRMMa.js";
10
+ import { c as extractContactContext, d as extractMediaPlaceholder, p as extractText, u as extractLocationData } from "./send-api-CC-vHBWG.js";
11
+ import { n as newConnectionId, r as waitForWaConnection, t as createWaSocket } from "./session-CDgpDc0l.js";
12
12
  import { n as resolveWebAccountId, t as getActiveWebListener } from "./active-listener-B4SDebQ6.js";
13
- import { a as loadWebMediaRaw, c as monitorWebInbox, i as loadWebMedia, l as resetWebInboundDedupe, n as LocalMediaAccessError, o as optimizeImageToJpeg, r as getDefaultLocalRoots, s as optimizeImageToPng, t as monitorWebChannel } from "./monitor-ClhD-fQ6.js";
14
- import { t as loginWeb } from "./login-nJwaNuC-.js";
13
+ import { a as loadWebMediaRaw, c as monitorWebInbox, i as loadWebMedia, l as resetWebInboundDedupe, n as LocalMediaAccessError, o as optimizeImageToJpeg, r as getDefaultLocalRoots, s as optimizeImageToPng, t as monitorWebChannel } from "./monitor-BU1ydkuF.js";
14
+ import { t as loginWeb } from "./login-BEkrpJbZ.js";
15
15
  import { HEARTBEAT_PROMPT, HEARTBEAT_TOKEN, SILENT_REPLY_TOKEN, stripHeartbeatToken } from "openclaw/plugin-sdk/reply-runtime";
16
16
  export { DEFAULT_WEB_MEDIA_BYTES, HEARTBEAT_PROMPT, HEARTBEAT_TOKEN, LocalMediaAccessError, SILENT_REPLY_TOKEN, WA_WEB_AUTH_DIR, WHATSAPP_AUTH_UNSTABLE_CODE, WhatsAppAuthUnstableError, createWaSocket, createWhatsAppLoginTool, extractContactContext, extractLocationData, extractMediaPlaceholder, extractText, formatError, formatWhatsAppWebAuthStatusState, getActiveWebListener, getDefaultLocalRoots, getStatusCode, getWebAuthAgeMs, handleWhatsAppAction, hasWebCredsSync, loadWebMedia, loadWebMediaRaw, logWebSelfId, loginWeb, logoutWeb, monitorWebChannel, monitorWebInbox, newConnectionId, optimizeImageToJpeg, optimizeImageToPng, pickWebChannel, readCredsJsonRaw, readWebAuthExistsBestEffort, readWebAuthExistsForDecision, readWebAuthSnapshot, readWebAuthSnapshotBestEffort, readWebAuthState, readWebSelfId, readWebSelfIdentity, readWebSelfIdentityForDecision, resetWebInboundDedupe, resolveDefaultWebAuthDir, resolveWebAccountId, resolveWebCredsBackupPath, resolveWebCredsPath, restoreCredsFromBackupIfNeeded, sendMessageWhatsApp, sendPollWhatsApp, sendReactionWhatsApp, sendTypingWhatsApp, setWhatsAppRuntime, startWebLoginWithQr, stripHeartbeatToken, waitForCredsSaveQueue, waitForCredsSaveQueueWithTimeout, waitForWaConnection, waitForWebLogin, webAuthExists, whatsAppActionRuntime, writeCredsJsonAtomically };
@@ -1,2 +1,2 @@
1
- import { n as setWhatsAppRuntime } from "./runtime-CboC4QhQ.js";
1
+ import { r as setWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
2
2
  export { setWhatsAppRuntime };
@@ -0,0 +1,625 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
3
+ import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BPYgj8Fv.js";
4
+ import { c as normalizeWhatsAppTarget, n as isWhatsAppNewsletterJid } from "./normalize-target-bVWjgftN.js";
5
+ import "./normalize-Bxwqo-bW.js";
6
+ import { t as getOptionalWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
7
+ import { t as getRegisteredWhatsAppConnectionController } from "./connection-controller-registry-TSX_udJp.js";
8
+ import { a as resolveWhatsAppOutboundMediaUrls, i as prepareWhatsAppOutboundMedia, n as normalizeWhatsAppPayloadText, s as resolveWhatsAppDocumentFileName } from "./outbound-media-contract-ioTkt8tn.js";
9
+ import { d as markdownToWhatsApp, p as toWhatsappJid } from "./text-runtime-Dk37KYHj.js";
10
+ import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
11
+ import { generateSecureUuid } from "openclaw/plugin-sdk/core";
12
+ import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
13
+ import { convertMarkdownTables, resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
14
+ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
15
+ import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
16
+ import { createSubsystemLogger, getChildLogger as getChildLogger$1 } from "openclaw/plugin-sdk/runtime-env";
17
+ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
18
+ import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
19
+ //#region extensions/whatsapp/src/approval-auth.ts
20
+ function normalizeWhatsAppApproverId(value) {
21
+ const normalized = normalizeWhatsAppTarget(String(value));
22
+ if (!normalized || normalized.endsWith("@g.us")) return;
23
+ return normalized;
24
+ }
25
+ function normalizeWhatsAppApproverEntry(value) {
26
+ return String(value).trim() === "*" ? "*" : normalizeWhatsAppApproverId(value);
27
+ }
28
+ function getWhatsAppApprovalApprovers(params) {
29
+ return resolveApprovalApprovers({
30
+ allowFrom: resolveWhatsAppAccount({
31
+ cfg: params.cfg,
32
+ accountId: params.accountId
33
+ }).allowFrom,
34
+ normalizeApprover: normalizeWhatsAppApproverEntry
35
+ });
36
+ }
37
+ const whatsappResolvedApproverAuth = createResolvedApproverActionAuthAdapter({
38
+ channelLabel: "WhatsApp",
39
+ resolveApprovers: ({ cfg, accountId }) => getWhatsAppApprovalApprovers({
40
+ cfg,
41
+ accountId
42
+ }),
43
+ normalizeSenderId: (value) => normalizeWhatsAppApproverId(value)
44
+ });
45
+ const whatsappApprovalAuth = { authorizeActorAction({ cfg, accountId, senderId, approvalKind }) {
46
+ if (getWhatsAppApprovalApprovers({
47
+ cfg,
48
+ accountId
49
+ }).includes("*")) return { authorized: true };
50
+ return whatsappResolvedApproverAuth.authorizeActorAction({
51
+ cfg,
52
+ accountId,
53
+ senderId,
54
+ action: "approve",
55
+ approvalKind
56
+ });
57
+ } };
58
+ //#endregion
59
+ //#region extensions/whatsapp/src/approval-reactions.ts
60
+ const WHATSAPP_APPROVAL_REACTION_META = {
61
+ "allow-once": {
62
+ emoji: "👍",
63
+ label: "Allow Once"
64
+ },
65
+ deny: {
66
+ emoji: "👎",
67
+ label: "Deny"
68
+ }
69
+ };
70
+ const WHATSAPP_APPROVAL_REACTION_ORDER = ["allow-once", "deny"];
71
+ const PERSISTENT_NAMESPACE = "whatsapp.approval-reactions";
72
+ const PERSISTENT_MAX_ENTRIES = 1e3;
73
+ const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
74
+ const whatsappApprovalReactionTargets = /* @__PURE__ */ new Map();
75
+ let persistentStore;
76
+ let persistentStoreDisabled = false;
77
+ let resolverRuntimePromise;
78
+ function loadApprovalResolver() {
79
+ resolverRuntimePromise ??= import("./approval-resolver-e1hAVRRy.js");
80
+ return resolverRuntimePromise;
81
+ }
82
+ function buildReactionTargetKey(params) {
83
+ const accountId = params.accountId.trim();
84
+ const remoteJid = params.remoteJid.trim();
85
+ const messageId = params.messageId.trim();
86
+ if (!accountId || !remoteJid || !messageId) return null;
87
+ return `${accountId}:${remoteJid}:${messageId}`;
88
+ }
89
+ function reportPersistentApprovalReactionError(error) {
90
+ try {
91
+ getOptionalWhatsAppRuntime()?.logging.getChildLogger({
92
+ plugin: "whatsapp",
93
+ feature: "approval-reaction-state"
94
+ }).warn("WhatsApp persistent approval reaction state failed", { error: String(error) });
95
+ } catch {}
96
+ }
97
+ function disablePersistentApprovalReactionStore(error) {
98
+ persistentStoreDisabled = true;
99
+ persistentStore = void 0;
100
+ reportPersistentApprovalReactionError(error);
101
+ }
102
+ function getPersistentApprovalReactionStore() {
103
+ if (persistentStoreDisabled) return;
104
+ if (persistentStore) return persistentStore;
105
+ const runtime = getOptionalWhatsAppRuntime();
106
+ if (!runtime) return;
107
+ try {
108
+ persistentStore = runtime.state.openKeyedStore({
109
+ namespace: PERSISTENT_NAMESPACE,
110
+ maxEntries: PERSISTENT_MAX_ENTRIES,
111
+ defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS
112
+ });
113
+ return persistentStore;
114
+ } catch (error) {
115
+ disablePersistentApprovalReactionStore(error);
116
+ return;
117
+ }
118
+ }
119
+ function readPersistedTarget(value) {
120
+ const persisted = value;
121
+ if (persisted?.version !== 1 || !persisted.target || typeof persisted.target.approvalId !== "string" || !Array.isArray(persisted.target.allowedDecisions)) return null;
122
+ return persisted.target;
123
+ }
124
+ function rememberPersistentApprovalReactionTarget(params) {
125
+ const ttlMs = params.ttlMs == null ? DEFAULT_REACTION_TARGET_TTL_MS : Math.max(1, params.ttlMs);
126
+ const store = getPersistentApprovalReactionStore();
127
+ if (!store) return;
128
+ store.register(params.key, {
129
+ version: 1,
130
+ target: params.target
131
+ }, { ttlMs }).catch(disablePersistentApprovalReactionStore);
132
+ }
133
+ function forgetPersistentApprovalReactionTarget(key) {
134
+ const store = getPersistentApprovalReactionStore();
135
+ if (!store) return;
136
+ store.delete(key).catch(disablePersistentApprovalReactionStore);
137
+ }
138
+ async function lookupPersistentApprovalReactionTarget(key) {
139
+ const store = getPersistentApprovalReactionStore();
140
+ if (!store) return null;
141
+ try {
142
+ return readPersistedTarget(await store.lookup(key));
143
+ } catch (error) {
144
+ disablePersistentApprovalReactionStore(error);
145
+ return null;
146
+ }
147
+ }
148
+ function listWhatsAppApprovalReactionBindings(allowedDecisions) {
149
+ const allowed = new Set(allowedDecisions);
150
+ return WHATSAPP_APPROVAL_REACTION_ORDER.filter((decision) => allowed.has(decision)).map((decision) => ({
151
+ decision,
152
+ emoji: WHATSAPP_APPROVAL_REACTION_META[decision].emoji,
153
+ label: WHATSAPP_APPROVAL_REACTION_META[decision].label
154
+ }));
155
+ }
156
+ function buildWhatsAppApprovalReactionHint(allowedDecisions) {
157
+ const bindings = listWhatsAppApprovalReactionBindings(allowedDecisions);
158
+ if (bindings.length === 0) return null;
159
+ return `React with:\n\n${bindings.map((binding) => `${binding.emoji} ${binding.label}`).join("\n")}`;
160
+ }
161
+ function insertWhatsAppApprovalReactionHintNearHeader(params) {
162
+ const lines = params.text.split(/\r?\n/);
163
+ const idLineIndex = lines.findIndex((line) => /^ID:\s*\S+/.test(line.trim()));
164
+ if (idLineIndex >= 0) {
165
+ const before = lines.slice(0, idLineIndex + 1).join("\n");
166
+ const after = lines.slice(idLineIndex + 1).join("\n").replace(/^\n+/, "");
167
+ return after ? `${before}\n\n${params.hint}\n\n${after}` : `${before}\n\n${params.hint}`;
168
+ }
169
+ return `${params.hint}\n\n${params.text}`;
170
+ }
171
+ function addWhatsAppApprovalReactionHintToText(params) {
172
+ if (/(^|\n)React with:\s*(\n|$)/i.test(params.text)) return params.text;
173
+ const hint = buildWhatsAppApprovalReactionHint(params.allowedDecisions);
174
+ return hint ? insertWhatsAppApprovalReactionHintNearHeader({
175
+ text: params.text,
176
+ hint
177
+ }) : params.text;
178
+ }
179
+ function appendWhatsAppApprovalReactionHintForOutboundMessage(text) {
180
+ if (/(^|\n)React with:\s*(\n|$)/i.test(text)) return text;
181
+ const binding = extractWhatsAppApprovalPromptBinding(text);
182
+ if (!binding) return text;
183
+ return addWhatsAppApprovalReactionHintToText({
184
+ text,
185
+ allowedDecisions: binding.allowedDecisions
186
+ });
187
+ }
188
+ function resolveWhatsAppApprovalReactionDecision(reactionKey, allowedDecisions) {
189
+ const normalizedReaction = reactionKey.trim();
190
+ if (!normalizedReaction) return null;
191
+ const allowed = new Set(allowedDecisions);
192
+ for (const decision of WHATSAPP_APPROVAL_REACTION_ORDER) {
193
+ if (!allowed.has(decision)) continue;
194
+ if (WHATSAPP_APPROVAL_REACTION_META[decision].emoji === normalizedReaction) return decision;
195
+ }
196
+ return null;
197
+ }
198
+ function normalizeApprovalDecision(value) {
199
+ const normalized = value.trim().toLowerCase();
200
+ if (normalized === "always") return "allow-always";
201
+ if (normalized === "allow-once" || normalized === "allow-always" || normalized === "deny") return normalized;
202
+ return null;
203
+ }
204
+ function extractWhatsAppApprovalPromptBinding(text) {
205
+ const allowedDecisions = [];
206
+ let approvalId = "";
207
+ for (const line of text.split(/\r?\n/)) {
208
+ const match = line.match(/\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i);
209
+ if (!match) continue;
210
+ if (approvalId && match[1] !== approvalId) continue;
211
+ approvalId ||= match[1];
212
+ const decisions = match[2].split(/[\s|,]+/);
213
+ for (const decisionText of decisions) {
214
+ const decision = normalizeApprovalDecision(decisionText);
215
+ if (decision && !allowedDecisions.includes(decision)) allowedDecisions.push(decision);
216
+ }
217
+ }
218
+ return approvalId && allowedDecisions.length > 0 ? {
219
+ approvalId,
220
+ allowedDecisions
221
+ } : null;
222
+ }
223
+ function registerWhatsAppApprovalReactionTarget(params) {
224
+ const key = buildReactionTargetKey(params);
225
+ const approvalId = params.approvalId.trim();
226
+ const allowedDecisions = listWhatsAppApprovalReactionBindings(params.allowedDecisions).map((binding) => binding.decision);
227
+ if (!key || !approvalId || allowedDecisions.length === 0) return null;
228
+ const target = {
229
+ approvalId,
230
+ allowedDecisions
231
+ };
232
+ whatsappApprovalReactionTargets.set(key, target);
233
+ rememberPersistentApprovalReactionTarget({
234
+ key,
235
+ target,
236
+ ttlMs: params.ttlMs
237
+ });
238
+ return target;
239
+ }
240
+ function registerWhatsAppApprovalReactionTargetForOutboundMessage(params) {
241
+ const binding = extractWhatsAppApprovalPromptBinding(params.text);
242
+ if (!binding) return false;
243
+ return Boolean(registerWhatsAppApprovalReactionTarget({
244
+ accountId: params.accountId,
245
+ remoteJid: params.remoteJid,
246
+ messageId: params.messageId,
247
+ approvalId: binding.approvalId,
248
+ allowedDecisions: binding.allowedDecisions,
249
+ ttlMs: params.ttlMs
250
+ }));
251
+ }
252
+ function unregisterWhatsAppApprovalReactionTarget(params) {
253
+ const key = buildReactionTargetKey(params);
254
+ if (!key) return;
255
+ whatsappApprovalReactionTargets.delete(key);
256
+ forgetPersistentApprovalReactionTarget(key);
257
+ }
258
+ function resolveTarget(params) {
259
+ const target = params.target;
260
+ if (!target) return null;
261
+ const decision = resolveWhatsAppApprovalReactionDecision(params.reactionKey, target.allowedDecisions);
262
+ return decision ? {
263
+ approvalId: target.approvalId,
264
+ decision
265
+ } : null;
266
+ }
267
+ async function resolveWhatsAppApprovalReactionTargetWithPersistence(params) {
268
+ const key = buildReactionTargetKey(params);
269
+ if (!key) return null;
270
+ const inMemory = resolveTarget({
271
+ target: whatsappApprovalReactionTargets.get(key),
272
+ reactionKey: params.reactionKey
273
+ });
274
+ if (inMemory) return inMemory;
275
+ return resolveTarget({
276
+ target: await lookupPersistentApprovalReactionTarget(key),
277
+ reactionKey: params.reactionKey
278
+ });
279
+ }
280
+ function readWhatsAppApprovalReactionEvent(params) {
281
+ const msg = params.msg;
282
+ const reaction = msg.message?.reactionMessage;
283
+ const reactionKey = reaction?.text?.trim() ?? "";
284
+ const messageId = reaction?.key?.id?.trim() ?? "";
285
+ const remoteJid = (reaction?.key?.remoteJid ?? msg.key?.remoteJid ?? "").trim();
286
+ const actorJid = msg.key?.participant?.trim() || (msg.key?.fromMe ? params.selfLid?.trim() ?? params.selfJid?.trim() ?? "" : msg.key?.remoteJid?.trim() ?? "");
287
+ if (!reactionKey || !messageId || !remoteJid || !actorJid) return null;
288
+ return {
289
+ remoteJid,
290
+ messageId,
291
+ actorJid,
292
+ reactionKey
293
+ };
294
+ }
295
+ async function maybeResolveWhatsAppApprovalReaction(params) {
296
+ const event = readWhatsAppApprovalReactionEvent({
297
+ msg: params.msg,
298
+ selfJid: params.selfJid,
299
+ selfLid: params.selfLid
300
+ });
301
+ if (!event) return false;
302
+ const target = await resolveWhatsAppApprovalReactionTargetWithPersistence({
303
+ accountId: params.accountId,
304
+ remoteJid: event.remoteJid,
305
+ messageId: event.messageId,
306
+ reactionKey: event.reactionKey
307
+ });
308
+ if (!target) return false;
309
+ const actorId = await params.resolveInboundJid(event.actorJid);
310
+ if (!actorId) {
311
+ params.logVerboseMessage?.(`whatsapp: approval reaction ignored for ${target.approvalId}; missing actor identity`);
312
+ return true;
313
+ }
314
+ const approvalKind = target.approvalId.startsWith("plugin:") ? "plugin" : "exec";
315
+ if (getWhatsAppApprovalApprovers({
316
+ cfg: params.cfg,
317
+ accountId: params.accountId
318
+ }).length === 0) {
319
+ params.logVerboseMessage?.(`whatsapp: approval reaction denied id=${target.approvalId}; reactions require explicit approvers`);
320
+ return true;
321
+ }
322
+ if (!whatsappApprovalAuth.authorizeActorAction({
323
+ cfg: params.cfg,
324
+ accountId: params.accountId,
325
+ senderId: actorId,
326
+ action: "approve",
327
+ approvalKind
328
+ }).authorized) {
329
+ params.logVerboseMessage?.(`whatsapp: approval reaction denied id=${target.approvalId} sender=${actorId}`);
330
+ return true;
331
+ }
332
+ const { isApprovalNotFoundError, resolveWhatsAppApproval } = await loadApprovalResolver();
333
+ try {
334
+ await resolveWhatsAppApproval({
335
+ cfg: params.cfg,
336
+ approvalId: target.approvalId,
337
+ decision: target.decision,
338
+ senderId: actorId,
339
+ gatewayUrl: params.gatewayUrl
340
+ });
341
+ params.logVerboseMessage?.(`whatsapp: approval reaction resolved id=${target.approvalId} sender=${actorId} decision=${target.decision}`);
342
+ return true;
343
+ } catch (error) {
344
+ if (isApprovalNotFoundError(error)) {
345
+ unregisterWhatsAppApprovalReactionTarget({
346
+ accountId: params.accountId,
347
+ remoteJid: event.remoteJid,
348
+ messageId: event.messageId
349
+ });
350
+ params.logVerboseMessage?.(`whatsapp: approval reaction ignored for expired approval id=${target.approvalId} sender=${actorId}`);
351
+ return true;
352
+ }
353
+ params.logVerboseMessage?.(`whatsapp: approval reaction failed id=${target.approvalId} sender=${actorId}: ${String(error)}`);
354
+ return true;
355
+ }
356
+ }
357
+ //#endregion
358
+ //#region extensions/whatsapp/src/outbound-media.runtime.ts
359
+ async function loadOutboundMediaFromUrl(mediaUrl, options = {}) {
360
+ const readFile = options.mediaAccess?.readFile ?? options.mediaReadFile;
361
+ const localRoots = options.mediaAccess?.localRoots?.length && options.mediaAccess.localRoots.length > 0 ? options.mediaAccess.localRoots : options.mediaLocalRoots && options.mediaLocalRoots.length > 0 ? options.mediaLocalRoots : void 0;
362
+ const sharedOptions = {
363
+ ...options.maxBytes !== void 0 ? { maxBytes: options.maxBytes } : {},
364
+ ...options.optimizeImages !== void 0 ? { optimizeImages: options.optimizeImages } : {}
365
+ };
366
+ return await loadWebMedia(mediaUrl, readFile ? {
367
+ ...sharedOptions,
368
+ localRoots: "any",
369
+ readFile,
370
+ hostReadCapability: true
371
+ } : {
372
+ ...sharedOptions,
373
+ ...localRoots ? { localRoots } : {}
374
+ });
375
+ }
376
+ //#endregion
377
+ //#region extensions/whatsapp/src/send.ts
378
+ var send_exports = /* @__PURE__ */ __exportAll({
379
+ sendMessageWhatsApp: () => sendMessageWhatsApp,
380
+ sendPollWhatsApp: () => sendPollWhatsApp,
381
+ sendReactionWhatsApp: () => sendReactionWhatsApp,
382
+ sendTypingWhatsApp: () => sendTypingWhatsApp
383
+ });
384
+ const outboundLog = createSubsystemLogger("gateway/channels/whatsapp").child("outbound");
385
+ function supportsForcedDocumentDelivery(kind) {
386
+ return kind === "image" || kind === "video";
387
+ }
388
+ function resolveOutboundWhatsAppAccountId(params) {
389
+ const explicitAccountId = params.accountId?.trim();
390
+ if (explicitAccountId) return explicitAccountId;
391
+ return resolveDefaultWhatsAppAccountId(params.cfg);
392
+ }
393
+ function requireOutboundActiveWebListener(params) {
394
+ const resolvedAccountId = resolveOutboundWhatsAppAccountId(params) ?? resolveDefaultWhatsAppAccountId(params.cfg);
395
+ const listener = getRegisteredWhatsAppConnectionController(resolvedAccountId)?.getActiveListener() ?? null;
396
+ if (!listener) throw new Error(`No active WhatsApp Web listener (account: ${resolvedAccountId}). Start the gateway, then link WhatsApp with: ${formatCliCommand(`openclaw channels login --channel whatsapp --account ${resolvedAccountId}`)}.`);
397
+ return {
398
+ accountId: resolvedAccountId,
399
+ listener
400
+ };
401
+ }
402
+ function resolveActualSentRemoteJid(result, fallbackJid) {
403
+ if (!result || typeof result !== "object") return fallbackJid;
404
+ const rawKeys = result.keys;
405
+ const keys = Array.isArray(rawKeys) ? rawKeys : [];
406
+ for (const key of keys) if (typeof key?.remoteJid === "string" && key.remoteJid.trim()) return key.remoteJid.trim();
407
+ return fallbackJid;
408
+ }
409
+ async function sendMessageWhatsApp(to, body, options) {
410
+ let text = options.preserveLeadingWhitespace ? body : normalizeWhatsAppPayloadText(body);
411
+ const jid = toWhatsappJid(to);
412
+ const mediaUrls = resolveWhatsAppOutboundMediaUrls(options);
413
+ const mediaPayload = options.mediaPayload;
414
+ const primaryMediaUrl = mediaUrls[0] ?? mediaPayload?.fileName;
415
+ const hasMedia = Boolean(mediaPayload || primaryMediaUrl);
416
+ if (!text && !hasMedia) return {
417
+ messageId: "",
418
+ toJid: jid
419
+ };
420
+ const correlationId = generateSecureUuid();
421
+ const startedAt = Date.now();
422
+ const cfg = requireRuntimeConfig(options.cfg, "WhatsApp send");
423
+ const { listener: active, accountId: resolvedAccountId } = requireOutboundActiveWebListener({
424
+ cfg,
425
+ accountId: options.accountId
426
+ });
427
+ const account = resolveWhatsAppAccount({
428
+ cfg,
429
+ accountId: resolvedAccountId ?? options.accountId
430
+ });
431
+ const tableMode = resolveMarkdownTableMode({
432
+ cfg,
433
+ channel: "whatsapp",
434
+ accountId: resolvedAccountId ?? options.accountId
435
+ });
436
+ text = convertMarkdownTables(text ?? "", tableMode);
437
+ text = markdownToWhatsApp(text);
438
+ const redactedTo = redactIdentifier(to);
439
+ const logger = getChildLogger$1({
440
+ module: "web-outbound",
441
+ correlationId,
442
+ to: redactedTo
443
+ });
444
+ try {
445
+ const redactedJid = redactIdentifier(jid);
446
+ let mediaBuffer;
447
+ let mediaType;
448
+ let documentFileName;
449
+ let visibleTextAfterVoice;
450
+ let forceDocumentDelivery = false;
451
+ if (mediaPayload) {
452
+ const media = await prepareWhatsAppOutboundMedia(mediaPayload, primaryMediaUrl);
453
+ const caption = text || void 0;
454
+ mediaBuffer = media.buffer;
455
+ mediaType = media.mimetype;
456
+ forceDocumentDelivery = Boolean(options.forceDocument && supportsForcedDocumentDelivery(media.kind));
457
+ if (media.kind === "audio" && caption) {
458
+ visibleTextAfterVoice = caption;
459
+ text = "";
460
+ } else if (media.kind === "document") {
461
+ text = caption ?? "";
462
+ documentFileName = media.fileName;
463
+ } else text = caption ?? "";
464
+ if (forceDocumentDelivery) documentFileName ??= resolveWhatsAppDocumentFileName({
465
+ fileName: media.fileName,
466
+ mimetype: media.mimetype
467
+ });
468
+ } else if (primaryMediaUrl) {
469
+ const media = await prepareWhatsAppOutboundMedia(await loadOutboundMediaFromUrl(primaryMediaUrl, {
470
+ maxBytes: resolveWhatsAppMediaMaxBytes(account),
471
+ optimizeImages: options.forceDocument ? false : void 0,
472
+ mediaAccess: options.mediaAccess,
473
+ mediaLocalRoots: options.mediaLocalRoots,
474
+ mediaReadFile: options.mediaReadFile
475
+ }), primaryMediaUrl);
476
+ const caption = text || void 0;
477
+ mediaBuffer = media.buffer;
478
+ mediaType = media.mimetype;
479
+ forceDocumentDelivery = Boolean(options.forceDocument && supportsForcedDocumentDelivery(media.kind));
480
+ if (media.kind === "audio" && caption) {
481
+ visibleTextAfterVoice = caption;
482
+ text = "";
483
+ } else if (media.kind === "document") {
484
+ text = caption ?? "";
485
+ documentFileName = media.fileName;
486
+ } else text = caption ?? "";
487
+ if (forceDocumentDelivery) documentFileName ??= resolveWhatsAppDocumentFileName({
488
+ fileName: media.fileName,
489
+ mimetype: media.mimetype
490
+ });
491
+ }
492
+ outboundLog.info(`Sending message -> ${redactedJid}${hasMedia ? " (media)" : ""}`);
493
+ logger.info({
494
+ jid: redactedJid,
495
+ hasMedia
496
+ }, "sending message");
497
+ if (!isWhatsAppNewsletterJid(jid)) await active.sendComposingTo(to);
498
+ const accountId = Boolean(options.accountId?.trim()) ? resolvedAccountId : void 0;
499
+ const sendOptions = options.gifPlayback || forceDocumentDelivery || accountId || documentFileName || options.quotedMessageKey ? {
500
+ ...options.gifPlayback ? { gifPlayback: true } : {},
501
+ ...forceDocumentDelivery ? { asDocument: true } : {},
502
+ ...documentFileName ? { fileName: documentFileName } : {},
503
+ ...options.quotedMessageKey ? { quotedMessageKey: options.quotedMessageKey } : {},
504
+ accountId
505
+ } : void 0;
506
+ const outboundText = text ? appendWhatsAppApprovalReactionHintForOutboundMessage(text) : text;
507
+ const result = sendOptions ? await active.sendMessage(to, outboundText, mediaBuffer, mediaType, sendOptions) : await active.sendMessage(to, outboundText, mediaBuffer, mediaType);
508
+ if (visibleTextAfterVoice) if (sendOptions) await active.sendMessage(to, visibleTextAfterVoice, void 0, void 0, sendOptions);
509
+ else await active.sendMessage(to, visibleTextAfterVoice, void 0, void 0);
510
+ const messageId = result?.messageId ?? "unknown";
511
+ const sentRemoteJid = resolveActualSentRemoteJid(result, jid);
512
+ if (messageId && messageId !== "unknown" && outboundText) registerWhatsAppApprovalReactionTargetForOutboundMessage({
513
+ accountId: resolvedAccountId,
514
+ remoteJid: sentRemoteJid,
515
+ messageId,
516
+ text: outboundText
517
+ });
518
+ const durationMs = Date.now() - startedAt;
519
+ outboundLog.info(`Sent message ${messageId} -> ${redactedJid}${hasMedia ? " (media)" : ""} (${durationMs}ms)`);
520
+ logger.info({
521
+ jid: redactedJid,
522
+ messageId
523
+ }, "sent message");
524
+ return {
525
+ messageId,
526
+ toJid: sentRemoteJid
527
+ };
528
+ } catch (err) {
529
+ logger.error({
530
+ err: String(err),
531
+ to: redactedTo,
532
+ hasMedia
533
+ }, "failed to send via web session");
534
+ throw err;
535
+ }
536
+ }
537
+ async function sendTypingWhatsApp(to, options) {
538
+ const { listener: active } = requireOutboundActiveWebListener({
539
+ cfg: requireRuntimeConfig(options.cfg, "WhatsApp typing send"),
540
+ accountId: options.accountId
541
+ });
542
+ if (!isWhatsAppNewsletterJid(toWhatsappJid(to))) await active.sendComposingTo(to);
543
+ }
544
+ async function sendReactionWhatsApp(chatJid, messageId, emoji, options) {
545
+ const correlationId = generateSecureUuid();
546
+ const { listener: active } = requireOutboundActiveWebListener({
547
+ cfg: requireRuntimeConfig(options.cfg, "WhatsApp reaction"),
548
+ accountId: options.accountId
549
+ });
550
+ const redactedChatJid = redactIdentifier(chatJid);
551
+ const logger = getChildLogger$1({
552
+ module: "web-outbound",
553
+ correlationId,
554
+ chatJid: redactedChatJid,
555
+ messageId
556
+ });
557
+ try {
558
+ const redactedJid = redactIdentifier(toWhatsappJid(chatJid));
559
+ outboundLog.info(`Sending reaction "${emoji}" -> message ${messageId}`);
560
+ logger.info({
561
+ chatJid: redactedJid,
562
+ messageId,
563
+ emoji
564
+ }, "sending reaction");
565
+ await active.sendReaction(chatJid, messageId, emoji, options.fromMe ?? false, options.participant);
566
+ outboundLog.info(`Sent reaction "${emoji}" -> message ${messageId}`);
567
+ logger.info({
568
+ chatJid: redactedJid,
569
+ messageId,
570
+ emoji
571
+ }, "sent reaction");
572
+ } catch (err) {
573
+ logger.error({
574
+ err: String(err),
575
+ chatJid: redactedChatJid,
576
+ messageId,
577
+ emoji
578
+ }, "failed to send reaction via web session");
579
+ throw err;
580
+ }
581
+ }
582
+ async function sendPollWhatsApp(to, poll, options) {
583
+ const correlationId = generateSecureUuid();
584
+ const startedAt = Date.now();
585
+ const { listener: active } = requireOutboundActiveWebListener({
586
+ cfg: requireRuntimeConfig(options.cfg, "WhatsApp poll"),
587
+ accountId: options.accountId
588
+ });
589
+ const redactedTo = redactIdentifier(to);
590
+ const logger = getChildLogger$1({
591
+ module: "web-outbound",
592
+ correlationId,
593
+ to: redactedTo
594
+ });
595
+ try {
596
+ const jid = toWhatsappJid(to);
597
+ const redactedJid = redactIdentifier(jid);
598
+ const normalized = normalizePollInput(poll, { maxOptions: 12 });
599
+ outboundLog.info(`Sending poll -> ${redactedJid}`);
600
+ logger.info({
601
+ jid: redactedJid,
602
+ optionCount: normalized.options.length,
603
+ maxSelections: normalized.maxSelections
604
+ }, "sending poll");
605
+ const messageId = (await active.sendPoll(to, normalized))?.messageId ?? "unknown";
606
+ const durationMs = Date.now() - startedAt;
607
+ outboundLog.info(`Sent poll ${messageId} -> ${redactedJid} (${durationMs}ms)`);
608
+ logger.info({
609
+ jid: redactedJid,
610
+ messageId
611
+ }, "sent poll");
612
+ return {
613
+ messageId,
614
+ toJid: jid
615
+ };
616
+ } catch (err) {
617
+ logger.error({
618
+ err: String(err),
619
+ to: redactedTo
620
+ }, "failed to send poll via web session");
621
+ throw err;
622
+ }
623
+ }
624
+ //#endregion
625
+ export { send_exports as a, maybeResolveWhatsAppApprovalReaction as c, getWhatsAppApprovalApprovers as d, whatsappApprovalAuth as f, sendTypingWhatsApp as i, registerWhatsAppApprovalReactionTarget as l, sendPollWhatsApp as n, addWhatsAppApprovalReactionHintToText as o, sendReactionWhatsApp as r, buildWhatsAppApprovalReactionHint as s, sendMessageWhatsApp as t, unregisterWhatsAppApprovalReactionTarget as u };
@@ -1,6 +1,6 @@
1
1
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-bVWjgftN.js";
2
- import { s as resolveWhatsAppDocumentFileName } from "./outbound-media-contract-ioTkt8tn.js";
3
2
  import "./normalize-Bxwqo-bW.js";
3
+ import { s as resolveWhatsAppDocumentFileName } from "./outbound-media-contract-ioTkt8tn.js";
4
4
  import { m as toWhatsappJidWithLid, p as toWhatsappJid, u as jidToE164 } from "./text-runtime-Dk37KYHj.js";
5
5
  import { t as buildQuotedMessageOptions } from "./quoted-message-CveINB35.js";
6
6
  import { c as resolveComparableIdentity } from "./identity-xoLLdqEv.js";
@@ -691,7 +691,7 @@ function createWebSendApi(params) {
691
691
  return normalizeWhatsAppSendResult(result, "poll");
692
692
  },
693
693
  sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
694
- const jid = toWhatsappJid(chatJid);
694
+ const jid = resolveOutboundJid(chatJid);
695
695
  return normalizeWhatsAppSendResult(await params.sock.sendMessage(jid, { react: {
696
696
  text: emoji,
697
697
  key: {