@openclaw/whatsapp 2026.7.2-beta.7 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{accounts-CSqLvCY5.js → accounts-BX2fZUyR.js} +1 -1
  2. package/dist/action-runtime-api.js +1 -1
  3. package/dist/{action-runtime-BzfDFPbH.js → action-runtime-pM-ExU0l.js} +2 -2
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/{agent-tools-login-DE9MU98T.js → agent-tools-login-BjRpD-JQ.js} +5 -4
  6. package/dist/api.js +69 -127
  7. package/dist/{approval-handler.runtime-DrA59QSA.js → approval-handler.runtime-CkMMvUjk.js} +4 -4
  8. package/dist/{approval-reactions-Cu66aYdq.js → approval-reactions-Jyr1JaAK.js} +29 -81
  9. package/dist/auth-presence.js +1 -1
  10. package/dist/{auth-store-DcEpIOmE.js → auth-store-CtpBqH5S.js} +82 -18
  11. package/dist/call-tool-api.js +1 -1
  12. package/dist/{channel-Dg62olhy.js → channel-DRMYRfZ8.js} +56 -160
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel-react-action-J_YtyMGX.js → channel-react-action-CstrR0CT.js} +14 -12
  15. package/dist/{channel.runtime-BP0xNmLi.js → channel.runtime-X3JC2gaq.js} +4 -4
  16. package/dist/channel.setup-DhBBhbQv.js +14 -0
  17. package/dist/{connection-controller-BOB2MVKY.js → connection-controller-CIYuBJrs.js} +2 -3
  18. package/dist/contract-api.js +2 -3
  19. package/dist/{creds-files-B1kSWtBg.js → creds-files-W6B3EMZ8.js} +17 -1
  20. package/dist/{directory-config-DalGrfGN.js → directory-config-P7_3U73Z.js} +3 -3
  21. package/dist/directory-contract-api.js +1 -1
  22. package/dist/doctor-contract-api.js +37 -2
  23. package/dist/{group-session-key-DVMZlO3x.js → group-session-key-DmFc_Cqi.js} +23 -4
  24. package/dist/light-runtime-api.js +2 -2
  25. package/dist/{login-CvY2Dhch.js → login-BkpKpndm.js} +4 -4
  26. package/dist/{login-qr-Bncpb6js.js → login-qr-1dpv6jjT.js} +4 -4
  27. package/dist/login-qr-runtime.js +1 -1
  28. package/dist/{monitor-CfkaUSL2.js → monitor-IzwQKizW.js} +545 -549
  29. package/dist/{question-reactions-CRsnyoMK.js → question-reactions-D3eAeB0i.js} +29 -78
  30. package/dist/runtime-api.js +9 -9
  31. package/dist/runtime-group-policy-DeSRFXEV.js +11 -0
  32. package/dist/{send-D37ddk22.js → send-CwvTU4RI.js} +298 -23
  33. package/dist/{send-api-7wk6eQyW.js → send-api-obOSm2cC.js} +126 -137
  34. package/dist/setup-entry.js +1 -8
  35. package/dist/{setup-finalize-BZucx24W.js → setup-finalize-n4K2I2gJ.js} +4 -4
  36. package/dist/setup-plugin-api.js +1 -1
  37. package/dist/{setup-surface-BkVcd769.js → setup-surface-Cwn3zyxK.js} +3 -3
  38. package/dist/{shared-CAxd15bp.js → shared-wg42NKFB.js} +13 -8
  39. package/dist/{socket-close-DridEuzz.js → socket-close-2T0_jZTN.js} +4 -10
  40. package/openclaw.plugin.json +4 -0
  41. package/package.json +4 -5
  42. package/skills/wacli/SKILL.md +8 -1
  43. package/dist/access-control-pZcxTmd5.js +0 -363
  44. package/dist/approval-resolver-DoeBBt22.js +0 -16
  45. package/dist/channel.setup-pMwNVkCA.js +0 -19
  46. package/dist/identity-CPrT5aEU.js +0 -76
  47. package/dist/legacy-state-migrations-api.js +0 -2
  48. package/dist/state-migrations-CAMTrs2U.js +0 -33
@@ -1,43 +1,26 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { a as resolveWhatsAppAccount } from "./accounts-CSqLvCY5.js";
3
- import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
2
+ import { a as resolveWhatsAppAccount } from "./accounts-BX2fZUyR.js";
3
+ import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
4
4
  //#region extensions/whatsapp/src/question-reactions.ts
5
5
  var question_reactions_exports = /* @__PURE__ */ __exportAll({
6
- clearWhatsAppQuestionReactionTargetsForTest: () => clearWhatsAppQuestionReactionTargetsForTest,
7
6
  maybeResolveWhatsAppQuestionReaction: () => maybeResolveWhatsAppQuestionReaction,
8
7
  registerWhatsAppQuestionReactionTargetForDeliveredPayload: () => registerWhatsAppQuestionReactionTargetForDeliveredPayload
9
8
  });
10
- const TARGET_TTL_MS = 1440 * 60 * 1e3;
11
- const targets = /* @__PURE__ */ new Map();
12
- function storeTarget(key, binding) {
13
- const existing = targets.get(key);
14
- if (existing) clearTimeout(existing.cleanupTimer);
15
- const target = {
16
- ...binding,
17
- terminal: false,
18
- expiresAtMs: Date.now() + TARGET_TTL_MS,
19
- cleanupTimer: setTimeout(() => {
20
- if (targets.get(key) === target) targets.delete(key);
21
- }, TARGET_TTL_MS)
22
- };
23
- target.cleanupTimer.unref?.();
24
- targets.set(key, target);
25
- questionGatewayRuntime.registerChannelDelivery({
26
- questionId: binding.questionId,
27
- deliveryId: `whatsapp-reaction:${key}`,
28
- finalize: () => {
29
- target.terminal = true;
30
- }
31
- });
32
- }
33
- function buildKey(accountId, remoteJid, messageId) {
9
+ function buildKey(identity) {
34
10
  const parts = [
35
- accountId,
36
- remoteJid,
37
- messageId
11
+ identity.accountId,
12
+ identity.remoteJid,
13
+ identity.messageId
38
14
  ].map((part) => part.trim());
39
15
  return parts.every(Boolean) ? parts.join(":") : void 0;
40
16
  }
17
+ const questionReactionTargets = createQuestionReactionTargetStore({
18
+ channel: "whatsapp",
19
+ channelDisplayName: "WhatsApp",
20
+ buildKey,
21
+ registerChannelDelivery: questionGatewayRuntime.registerChannelDelivery,
22
+ resolveReaction: questionGatewayRuntime.resolveReaction
23
+ });
41
24
  function addCandidate(values, value) {
42
25
  const normalized = value?.trim();
43
26
  if (normalized && !values.includes(normalized)) values.push(normalized);
@@ -73,12 +56,10 @@ function registerWhatsAppQuestionReactionTargetForDeliveredPayload(params) {
73
56
  accountId: params.target.accountId
74
57
  }).accountId;
75
58
  let registered = false;
76
- for (const identity of listDeliveredIdentities(params.results)) {
77
- const key = buildKey(accountId, identity.remoteJid, identity.messageId);
78
- if (!key) continue;
79
- storeTarget(key, binding);
80
- registered = true;
81
- }
59
+ for (const identity of listDeliveredIdentities(params.results)) registered = questionReactionTargets.register(binding, {
60
+ accountId,
61
+ ...identity
62
+ }) || registered;
82
63
  return registered;
83
64
  }
84
65
  async function maybeResolveWhatsAppQuestionReaction(params) {
@@ -95,48 +76,18 @@ async function maybeResolveWhatsAppQuestionReaction(params) {
95
76
  addCandidate(candidates, remoteJid);
96
77
  for (const mapped of await params.resolveReactionTargetJids?.(remoteJid) ?? []) addCandidate(candidates, mapped);
97
78
  }
98
- let matched;
99
- for (const remoteJid of candidates) {
100
- const key = buildKey(params.accountId, remoteJid, messageId);
101
- const target = key ? targets.get(key) : void 0;
102
- if (key && target) {
103
- matched = {
104
- key,
105
- target
106
- };
107
- break;
108
- }
109
- }
110
- if (!matched) return false;
111
- if (matched.target.expiresAtMs <= Date.now() || matched.target.terminal) {
112
- matched.target.terminal = true;
113
- params.logDebug?.(`whatsapp: stale question reaction ignored id=${matched.target.questionId}`);
114
- return true;
115
- }
116
- const optionValue = matched.target.optionValues[optionIndex];
117
- if (!optionValue) {
118
- params.logDebug?.(`whatsapp: out-of-range question reaction ignored id=${matched.target.questionId}`);
119
- return true;
120
- }
121
- try {
122
- const result = await questionGatewayRuntime.resolveReaction({
123
- cfg: params.cfg,
124
- questionId: matched.target.questionId,
125
- optionValue,
126
- senderId: params.senderId,
127
- gatewayUrl: params.gatewayUrl,
128
- clientDisplayName: `WhatsApp question (${params.senderId})`
129
- });
130
- matched.target.terminal = result?.status === "answered" || result?.status === "already-terminal";
131
- if (result?.status === "already-terminal") params.logDebug?.(`whatsapp: stale question reaction ignored id=${matched.target.questionId}`);
132
- } catch (error) {
133
- params.logDebug?.(`whatsapp: question reaction failed id=${matched.target.questionId}: ${String(error)}`);
134
- }
135
- return true;
136
- }
137
- function clearWhatsAppQuestionReactionTargetsForTest() {
138
- for (const target of targets.values()) clearTimeout(target.cleanupTimer);
139
- targets.clear();
79
+ return await questionReactionTargets.resolve({
80
+ identities: candidates.map((remoteJid) => ({
81
+ accountId: params.accountId,
82
+ remoteJid,
83
+ messageId
84
+ })),
85
+ optionIndex,
86
+ cfg: params.cfg,
87
+ senderId: params.senderId,
88
+ gatewayUrl: params.gatewayUrl,
89
+ logDebug: params.logDebug
90
+ });
140
91
  }
141
92
  //#endregion
142
93
  export { question_reactions_exports as n, maybeResolveWhatsAppQuestionReaction as t };
@@ -1,16 +1,16 @@
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-BzfDFPbH.js";
3
- import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-D37ddk22.js";
1
+ import { c as resolveWebCredsPath, r as hasWebCredsSync, s as resolveWebCredsBackupPath } from "./creds-files-W6B3EMZ8.js";
2
+ import { n as whatsAppActionRuntime, t as handleWhatsAppAction } from "./action-runtime-pM-ExU0l.js";
3
+ import { i as sendTypingWhatsApp, n as sendPollWhatsApp, r as sendReactionWhatsApp, t as sendMessageWhatsApp } from "./send-CwvTU4RI.js";
4
4
  import { a as setWhatsAppRuntime } from "./runtime-BLlToOi6.js";
5
5
  import { startWebLoginWithQr, waitForWebLogin } from "./login-qr-runtime.js";
6
- import { t as createWhatsAppLoginTool } from "./agent-tools-login-DE9MU98T.js";
7
- import { C as waitForCredsSaveQueue, T as writeCredsJsonAtomically, _ as readWebSelfIdentity, a as formatWhatsAppWebAuthStatusState, b as restoreCredsFromBackupIfNeeded, c as logoutWeb, d as readWebAuthExistsBestEffort, f as readWebAuthExistsForDecision, g as readWebSelfId, h as readWebAuthState, l as pickWebChannel, m as readWebAuthSnapshotBestEffort, n as WHATSAPP_AUTH_UNSTABLE_CODE, o as getWebAuthAgeMs, p as readWebAuthSnapshot, r as WhatsAppAuthUnstableError, s as logWebSelfId, t as WA_WEB_AUTH_DIR, u as readCredsJsonRaw, v as readWebSelfIdentityForDecision, w as waitForCredsSaveQueueWithTimeout, x as webAuthExists, y as resolveDefaultWebAuthDir } from "./auth-store-DcEpIOmE.js";
6
+ import { t as createWhatsAppLoginTool } from "./agent-tools-login-BjRpD-JQ.js";
7
+ import { M as waitForCredsSaveQueueWithTimeout, N 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, j as waitForCredsSaveQueue, 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, x as webAuthExists, y as resolveDefaultWebAuthDir } from "./auth-store-CtpBqH5S.js";
8
8
  import { t as DEFAULT_WEB_MEDIA_BYTES } from "./constants-HU41RHGI.js";
9
9
  import { n as resolveWebAccountId, t as getActiveWebListener } from "./active-listener-BEjWsCNM.js";
10
- import { a as waitForWaConnection, i as newConnectionId, r as createWaSocket } from "./socket-close-DridEuzz.js";
10
+ import { a as waitForWaConnection, i as newConnectionId, r as createWaSocket } from "./socket-close-2T0_jZTN.js";
11
11
  import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
12
- import { g as extractLocationData, p as extractContactContext, y as extractText } from "./send-api-7wk6eQyW.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-CfkaUSL2.js";
14
- import { t as loginWeb } from "./login-CvY2Dhch.js";
12
+ import { p as extractText, s as extractContactContext, u as extractLocationData } from "./send-api-obOSm2cC.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-IzwQKizW.js";
14
+ import { t as loginWeb } from "./login-BkpKpndm.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, 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 };
@@ -0,0 +1,11 @@
1
+ import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
2
+ //#region extensions/whatsapp/src/runtime-group-policy.ts
3
+ function resolveWhatsAppRuntimeGroupPolicy(params) {
4
+ return resolveOpenProviderRuntimeGroupPolicy({
5
+ providerConfigPresent: params.providerConfigPresent,
6
+ groupPolicy: params.groupPolicy,
7
+ defaultGroupPolicy: params.defaultGroupPolicy
8
+ });
9
+ }
10
+ //#endregion
11
+ export { resolveWhatsAppRuntimeGroupPolicy as t };
@@ -1,4 +1,4 @@
1
- import { a as resolveWhatsAppAccount, c as resolveMergedWhatsAppAccountConfig, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CSqLvCY5.js";
1
+ import { a as resolveWhatsAppAccount, c as resolveMergedWhatsAppAccountConfig, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BX2fZUyR.js";
2
2
  import { r as resolveDefaultWhatsAppAccountId } from "./account-ids-CB5SOWjc.js";
3
3
  import { n as isWhatsAppNewsletterJid } from "./normalize-target-CugRyONv.js";
4
4
  import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-DG0tuYuw.js";
@@ -6,7 +6,7 @@ import "./normalize-C7Hkc_vE.js";
6
6
  import { a as markdownToWhatsAppChunks, c as toWhatsappJid } from "./targets-runtime-TMk0lGuy.js";
7
7
  import { a as stripToolCallXmlTags, i as sanitizeAssistantVisibleTextWithProfile, r as sanitizeAssistantVisibleText } from "./text-runtime-CaKLhowd.js";
8
8
  import path from "node:path";
9
- import { normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { normalizeStringEntries, normalizeUniqueStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
10
10
  import { resolveReactionLevel } from "openclaw/plugin-sdk/status-helpers";
11
11
  import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
12
12
  import { generateSecureUuid } from "openclaw/plugin-sdk/core";
@@ -17,9 +17,12 @@ import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
17
17
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
18
18
  import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
19
19
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
20
- import { createSubsystemLogger, getChildLogger as getChildLogger$1 } from "openclaw/plugin-sdk/runtime-env";
20
+ import { createSubsystemLogger as createSubsystemLogger$1, getChildLogger as getChildLogger$1 } from "openclaw/plugin-sdk/runtime-env";
21
21
  import { extensionForMime, mediaKindFromMime, mimeTypeFromFilePath, normalizeMimeType } from "openclaw/plugin-sdk/media-mime";
22
- import { sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
22
+ import { AsyncLocalStorage } from "node:async_hooks";
23
+ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
24
+ import { createChannelPartialDeliveryError, isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
25
+ import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
23
26
  import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, transcodeAudioBufferToOpus } from "openclaw/plugin-sdk/media-runtime";
24
27
  import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
25
28
  //#region extensions/whatsapp/src/reaction-level.ts
@@ -54,6 +57,245 @@ function stripAsciiControlCharacters(value) {
54
57
  return stripped;
55
58
  }
56
59
  //#endregion
60
+ //#region extensions/whatsapp/src/inbound/send-result.ts
61
+ const activityAccountedWhatsAppSendResults = /* @__PURE__ */ new WeakSet();
62
+ const logicalWhatsAppDeliveryActivity = new AsyncLocalStorage();
63
+ /** Keep all separately produced platform sends inside one logical reply accounting scope. */
64
+ async function withWhatsAppLogicalDeliveryActivity(run) {
65
+ if (logicalWhatsAppDeliveryActivity.getStore()) return await run();
66
+ return await logicalWhatsAppDeliveryActivity.run({ accountedAccountIds: /* @__PURE__ */ new Set() }, run);
67
+ }
68
+ function resolveWhatsAppReceiptKind(kind) {
69
+ if (kind === "media" || kind === "text") return kind;
70
+ return "unknown";
71
+ }
72
+ function toReceiptSourceResult(key) {
73
+ return {
74
+ channel: "whatsapp",
75
+ messageId: key.id,
76
+ ...key.remoteJid ? { toJid: key.remoteJid } : {},
77
+ meta: {
78
+ fromMe: key.fromMe,
79
+ participant: key.participant
80
+ }
81
+ };
82
+ }
83
+ function createWhatsAppSendReceipt(kind, keys) {
84
+ return createMessageReceiptFromOutboundResults({
85
+ kind: resolveWhatsAppReceiptKind(kind),
86
+ results: keys.map(toReceiptSourceResult)
87
+ });
88
+ }
89
+ function mergeWhatsAppReceiptSourceMetadata(sources) {
90
+ const defined = sources.filter((source) => Boolean(source));
91
+ const preferred = defined[0];
92
+ if (!preferred) return;
93
+ const merged = /* @__PURE__ */ new Map();
94
+ const meta = /* @__PURE__ */ new Map();
95
+ for (const source of defined.toReversed()) {
96
+ for (const [key, value] of Object.entries(source)) if (key !== "meta" && value !== void 0) merged.set(key, value);
97
+ for (const [key, value] of Object.entries(source.meta ?? {})) if (value !== void 0) meta.set(key, value);
98
+ }
99
+ const mergedSource = Object.assign({}, preferred, Object.fromEntries(merged));
100
+ if (meta.size > 0) mergedSource.meta = Object.fromEntries(meta);
101
+ else delete mergedSource.meta;
102
+ return mergedSource;
103
+ }
104
+ function enrichWhatsAppDeliveryReceipt(params) {
105
+ const seenPartIds = /* @__PURE__ */ new Set();
106
+ const parts = params.receipt.parts.flatMap((part) => {
107
+ if (seenPartIds.has(part.platformMessageId)) return [];
108
+ seenPartIds.add(part.platformMessageId);
109
+ const matchingParts = params.sources.flatMap((source) => source.parts.filter((candidate) => candidate.platformMessageId === part.platformMessageId));
110
+ const matchingRawSources = params.sources.flatMap((source) => source.raw?.filter((raw) => raw.messageId === part.platformMessageId) ?? []);
111
+ const raw = mergeWhatsAppReceiptSourceMetadata([
112
+ part.raw,
113
+ ...matchingParts.map((candidate) => candidate.raw),
114
+ ...matchingRawSources
115
+ ]);
116
+ const threadId = part.threadId ?? matchingParts.find((candidate) => candidate.threadId)?.threadId;
117
+ const replyToId = part.replyToId ?? matchingParts.find((candidate) => candidate.replyToId)?.replyToId;
118
+ return [{
119
+ ...part,
120
+ ...threadId ? { threadId } : {},
121
+ ...replyToId ? { replyToId } : {},
122
+ ...raw ? { raw } : {}
123
+ }];
124
+ });
125
+ const threadId = params.receipt.threadId ?? params.sources.find((source) => source.threadId)?.threadId;
126
+ const replyToId = params.receipt.replyToId ?? params.sources.find((source) => source.replyToId)?.replyToId;
127
+ const editToken = params.receipt.editToken ?? params.sources.find((source) => source.editToken)?.editToken;
128
+ const deleteToken = params.receipt.deleteToken ?? params.sources.find((source) => source.deleteToken)?.deleteToken;
129
+ return {
130
+ ...params.receipt,
131
+ parts,
132
+ raw: parts.map((part) => part.raw ?? {
133
+ channel: "whatsapp",
134
+ messageId: part.platformMessageId
135
+ }),
136
+ ...threadId ? { threadId } : {},
137
+ ...replyToId ? { replyToId } : {},
138
+ ...editToken ? { editToken } : {},
139
+ ...deleteToken ? { deleteToken } : {}
140
+ };
141
+ }
142
+ function normalizeKey(key) {
143
+ const id = typeof key?.id === "string" ? key.id.trim() : "";
144
+ if (!id) return;
145
+ return {
146
+ id,
147
+ remoteJid: key?.remoteJid,
148
+ fromMe: key?.fromMe,
149
+ participant: key?.participant
150
+ };
151
+ }
152
+ function createWhatsAppProviderNotAcceptedError(kind) {
153
+ const cause = /* @__PURE__ */ new Error("Baileys returned no accepted message key.");
154
+ return new PlatformMessageNotDispatchedError(`WhatsApp ${kind} send was not accepted by the provider: ${cause.message}`, { cause });
155
+ }
156
+ function normalizeWhatsAppSendResult(result, kind) {
157
+ const key = normalizeKey(result?.key);
158
+ if (!key) throw createWhatsAppProviderNotAcceptedError(kind);
159
+ return {
160
+ kind,
161
+ messageId: key.id,
162
+ receipt: createWhatsAppSendReceipt(kind, [key]),
163
+ keys: [key],
164
+ providerAccepted: true
165
+ };
166
+ }
167
+ /** Refuse synthetic ids from listener boundaries before callers create durable receipts. */
168
+ function requireWhatsAppAcceptedSendResult(result) {
169
+ if (result.providerAccepted && result.messageId !== "unknown" && result.keys.some((key) => key.id === result.messageId) && listWhatsAppSendResultMessageIds(result).includes(result.messageId)) return result;
170
+ throw createWhatsAppProviderNotAcceptedError(result.kind);
171
+ }
172
+ /** Persist accepted provider facts before account bookkeeping or later platform sends can fail. */
173
+ function rememberWhatsAppAcceptedSend(params) {
174
+ const accepted = requireWhatsAppAcceptedSendResult(params.result);
175
+ params.results.push(accepted);
176
+ if (params.results.length === 1) {
177
+ const deliveryActivity = logicalWhatsAppDeliveryActivity.getStore();
178
+ const alreadyAccounted = activityAccountedWhatsAppSendResults.has(accepted) || Boolean(deliveryActivity?.accountedAccountIds.has(params.accountId));
179
+ activityAccountedWhatsAppSendResults.add(accepted);
180
+ if (!alreadyAccounted) {
181
+ deliveryActivity?.accountedAccountIds.add(params.accountId);
182
+ recordChannelActivity({
183
+ channel: "whatsapp",
184
+ accountId: params.accountId,
185
+ direction: "outbound"
186
+ });
187
+ }
188
+ }
189
+ return accepted;
190
+ }
191
+ /** Nested owners already attempted activity accounting before exposing accepted delivery. */
192
+ function rememberWhatsAppPartialSend(params) {
193
+ if (!isChannelPartialDeliveryError(params.error)) return false;
194
+ const delivery = params.error.deliveryResult;
195
+ const messageIds = uniqueStrings([...delivery.receipt ? listMessageReceiptPlatformIds(delivery.receipt) : [], ...normalizeStringEntries(delivery.messageIds ?? [])]).filter((messageId) => messageId !== "unknown");
196
+ const existingIds = new Set(params.results.flatMap(listWhatsAppSendResultMessageIds));
197
+ const acceptedIds = messageIds.filter((messageId) => !existingIds.has(messageId));
198
+ const messageId = acceptedIds[0];
199
+ if (!messageId) return false;
200
+ const receiptSources = [];
201
+ if (delivery.receipt) receiptSources.push({
202
+ channel: "whatsapp",
203
+ messageId,
204
+ receipt: delivery.receipt
205
+ });
206
+ const receiptIds = new Set(delivery.receipt ? listMessageReceiptPlatformIds(delivery.receipt) : []);
207
+ for (const acceptedId of acceptedIds) if (!receiptIds.has(acceptedId)) receiptSources.push({
208
+ channel: "whatsapp",
209
+ messageId: acceptedId
210
+ });
211
+ const receipt = createMessageReceiptFromOutboundResults({
212
+ kind: resolveWhatsAppReceiptKind(params.kind),
213
+ results: receiptSources
214
+ });
215
+ const accepted = requireWhatsAppAcceptedSendResult({
216
+ kind: params.kind,
217
+ messageId,
218
+ receipt,
219
+ keys: acceptedIds.map((id) => ({ id })),
220
+ providerAccepted: true
221
+ });
222
+ activityAccountedWhatsAppSendResults.add(accepted);
223
+ params.results.push(accepted);
224
+ return true;
225
+ }
226
+ /** Merge accepted local sends with nested partial receipts without inventing provider ids. */
227
+ function mergeWhatsAppAcceptedSendError(params) {
228
+ const nested = isChannelPartialDeliveryError(params.error) ? params.error.deliveryResult : void 0;
229
+ const receiptSources = [];
230
+ const recordedIds = /* @__PURE__ */ new Set();
231
+ for (const result of params.results) {
232
+ const resultIds = listWhatsAppSendResultMessageIds(result);
233
+ if (resultIds.every((messageId) => recordedIds.has(messageId))) continue;
234
+ receiptSources.push({
235
+ channel: "whatsapp",
236
+ messageId: result.messageId,
237
+ ...result.receipt ? { receipt: result.receipt } : {}
238
+ });
239
+ for (const messageId of resultIds) recordedIds.add(messageId);
240
+ }
241
+ const unrecordedNestedIds = uniqueStrings([...nested?.receipt ? listMessageReceiptPlatformIds(nested.receipt) : [], ...normalizeStringEntries(nested?.messageIds ?? [])]).filter((messageId) => messageId !== "unknown").filter((messageId) => !recordedIds.has(messageId));
242
+ const nestedMessageId = unrecordedNestedIds[0];
243
+ if (nested?.receipt && nestedMessageId) {
244
+ receiptSources.push({
245
+ channel: "whatsapp",
246
+ messageId: nestedMessageId,
247
+ receipt: nested.receipt
248
+ });
249
+ for (const messageId of listMessageReceiptPlatformIds(nested.receipt)) recordedIds.add(messageId);
250
+ }
251
+ for (const messageId of unrecordedNestedIds) if (!recordedIds.has(messageId)) receiptSources.push({
252
+ channel: "whatsapp",
253
+ messageId
254
+ });
255
+ const receipt = enrichWhatsAppDeliveryReceipt({
256
+ receipt: createMessageReceiptFromOutboundResults({
257
+ kind: resolveWhatsAppReceiptKind(params.kind),
258
+ results: receiptSources
259
+ }),
260
+ sources: [...params.results.flatMap((result) => result.receipt ? [result.receipt] : []), ...nested?.receipt ? [nested.receipt] : []]
261
+ });
262
+ const messageIds = listMessageReceiptPlatformIds(receipt);
263
+ if (messageIds.length === 0) return params.error;
264
+ let cause = params.error;
265
+ const visitedPartialErrors = /* @__PURE__ */ new Set();
266
+ while (isChannelPartialDeliveryError(cause) && cause instanceof Error && cause.cause !== void 0 && !visitedPartialErrors.has(cause)) {
267
+ visitedPartialErrors.add(cause);
268
+ cause = cause.cause;
269
+ }
270
+ return createChannelPartialDeliveryError(cause, {
271
+ ...nested,
272
+ messageIds,
273
+ receipt,
274
+ visibleReplySent: true
275
+ });
276
+ }
277
+ function combineWhatsAppSendResults(kind, results) {
278
+ const messageId = uniqueStrings(results.flatMap(listWhatsAppSendResultMessageIds))[0];
279
+ if (!messageId) throw createWhatsAppProviderNotAcceptedError(kind);
280
+ const keys = results.flatMap((result) => result.keys);
281
+ const combined = {
282
+ kind,
283
+ messageId,
284
+ receipt: createWhatsAppSendReceipt(kind, keys),
285
+ keys,
286
+ providerAccepted: results.some((result) => result.providerAccepted)
287
+ };
288
+ if (results.some((result) => activityAccountedWhatsAppSendResults.has(result))) activityAccountedWhatsAppSendResults.add(combined);
289
+ return combined;
290
+ }
291
+ function listWhatsAppSendResultMessageIds(result) {
292
+ const receiptIds = result.receipt ? listMessageReceiptPlatformIds(result.receipt) : [];
293
+ if (receiptIds.length > 0) return receiptIds;
294
+ const keyIds = normalizeStringEntries(result.keys.map((key) => key.id));
295
+ if (keyIds.length > 0) return uniqueStrings(keyIds);
296
+ return [];
297
+ }
298
+ //#endregion
57
299
  //#region extensions/whatsapp/src/outbound-media-contract.ts
58
300
  const WHATSAPP_VOICE_FILE_NAME = "voice.ogg";
59
301
  const WHATSAPP_VOICE_SAMPLE_RATE_HZ = 48e3;
@@ -101,7 +343,7 @@ function inferWhatsAppMediaKind(media, resolvedContentType) {
101
343
  function normalizeWhatsAppLoadedMedia(media, mediaUrl) {
102
344
  const filenameMimeType = mimeTypeFromFilePath(media.fileName);
103
345
  const normalizedContentType = normalizeMimeType(media.contentType);
104
- const resolvedContentType = !normalizedContentType || normalizedContentType === "application/octet-stream" ? filenameMimeType ?? media.contentType : media.contentType;
346
+ const resolvedContentType = !normalizedContentType || normalizedContentType === "application/octet-stream" ? filenameMimeType ?? normalizedContentType : normalizedContentType;
105
347
  const kind = inferWhatsAppMediaKind(media, resolvedContentType);
106
348
  const mimetype = kind === "audio" && isWhatsAppNativeVoiceAudio({
107
349
  contentType: media.contentType,
@@ -167,13 +409,13 @@ function deriveWhatsAppDocumentFileName(mediaUrl) {
167
409
  }
168
410
  //#endregion
169
411
  //#region extensions/whatsapp/src/send.ts
170
- const outboundLog = createSubsystemLogger("gateway/channels/whatsapp").child("outbound");
412
+ const outboundLog = createSubsystemLogger$1("gateway/channels/whatsapp").child("outbound");
171
413
  function supportsForcedDocumentDelivery(kind) {
172
414
  return kind === "image" || kind === "video";
173
415
  }
174
416
  function buildWhatsAppMediaSendState(params) {
175
417
  const { media, caption } = params;
176
- const forceDocumentDelivery = Boolean(params.forceDocument && supportsForcedDocumentDelivery(media.kind));
418
+ const forceDocumentDelivery = Boolean(params.forceDocument && supportsForcedDocumentDelivery(media.kind)) || media.kind === "document" && (media.mimetype.startsWith("image/") || media.mimetype.startsWith("video/"));
177
419
  let text = caption ?? "";
178
420
  let documentFileName = media.kind === "document" ? media.fileName : void 0;
179
421
  let visibleTextAfterVoice;
@@ -219,6 +461,12 @@ function resolveActualSentRemoteJid(result, fallbackJid) {
219
461
  return fallbackJid;
220
462
  }
221
463
  async function sendMessageWhatsApp(to, body, options) {
464
+ return await sendWhatsAppUploadFile(to, body, options);
465
+ }
466
+ async function sendWhatsAppUploadFile(to, body, options) {
467
+ return await withWhatsAppLogicalDeliveryActivity(() => sendMessageWhatsAppInActivityScope(to, body, options));
468
+ }
469
+ async function sendMessageWhatsAppInActivityScope(to, body, options) {
222
470
  let text = options.preserveLeadingWhitespace ? body : normalizeWhatsAppPayloadText(body);
223
471
  const jid = toWhatsappJid(to);
224
472
  const mediaUrls = resolveAdditiveWhatsAppMediaUrls(options);
@@ -259,6 +507,7 @@ async function sendMessageWhatsApp(to, body, options) {
259
507
  correlationId,
260
508
  to: redactedTo
261
509
  });
510
+ const acceptedResults = [];
262
511
  try {
263
512
  const redactedJid = redactIdentifier(jid);
264
513
  let mediaBuffer;
@@ -268,13 +517,24 @@ async function sendMessageWhatsApp(to, body, options) {
268
517
  let forceDocumentDelivery = false;
269
518
  let media;
270
519
  if (mediaPayload) media = await prepareWhatsAppOutboundMedia(mediaPayload, primaryMediaUrl);
271
- else if (primaryMediaUrl) media = await prepareWhatsAppOutboundMedia(await loadOutboundMediaFromUrl(primaryMediaUrl, {
272
- maxBytes: resolveWhatsAppMediaMaxBytes(account),
273
- optimizeImages: options.forceDocument ? false : void 0,
274
- mediaAccess: options.mediaAccess,
275
- mediaLocalRoots: options.mediaLocalRoots,
276
- mediaReadFile: options.mediaReadFile
277
- }), primaryMediaUrl);
520
+ else if (primaryMediaUrl) {
521
+ const loadedMedia = await loadOutboundMediaFromUrl(primaryMediaUrl, {
522
+ maxBytes: resolveWhatsAppMediaMaxBytes(account),
523
+ optimizeImages: options.forceDocument ? false : void 0,
524
+ mediaAccess: options.mediaAccess,
525
+ mediaLocalRoots: options.mediaLocalRoots,
526
+ mediaReadFile: options.mediaReadFile
527
+ });
528
+ const mediaWithRequestedType = options.contentType ? {
529
+ ...loadedMedia,
530
+ contentType: options.contentType,
531
+ kind: void 0
532
+ } : loadedMedia;
533
+ media = await prepareWhatsAppOutboundMedia(options.fileName ? {
534
+ ...mediaWithRequestedType,
535
+ fileName: options.fileName
536
+ } : mediaWithRequestedType, primaryMediaUrl);
537
+ }
278
538
  if (media) {
279
539
  const mediaSendState = buildWhatsAppMediaSendState({
280
540
  media,
@@ -295,7 +555,14 @@ async function sendMessageWhatsApp(to, body, options) {
295
555
  }, "sending message");
296
556
  if (!isWhatsAppNewsletterJid(jid)) {
297
557
  await active.assertSendReady?.(to);
298
- await active.sendComposingTo(to);
558
+ try {
559
+ await active.sendComposingTo(to);
560
+ } catch (err) {
561
+ logger.warn({
562
+ err: String(err),
563
+ jid: redactedJid
564
+ }, "failed to send composing presence; continuing message delivery");
565
+ }
299
566
  }
300
567
  const accountId = Boolean(options.accountId?.trim()) ? resolvedAccountId : void 0;
301
568
  const sendOptions = options.gifPlayback || forceDocumentDelivery || accountId || documentFileName || options.quotedMessageKey ? {
@@ -305,8 +572,9 @@ async function sendMessageWhatsApp(to, body, options) {
305
572
  ...options.quotedMessageKey ? { quotedMessageKey: options.quotedMessageKey } : {},
306
573
  accountId
307
574
  } : void 0;
308
- const result = sendOptions ? await active.sendMessage(to, text, mediaBuffer, mediaType, sendOptions) : await active.sendMessage(to, text, mediaBuffer, mediaType);
309
- const messageId = result?.messageId ?? "unknown";
575
+ const result = requireWhatsAppAcceptedSendResult(sendOptions ? await active.sendMessage(to, text, mediaBuffer, mediaType, sendOptions) : await active.sendMessage(to, text, mediaBuffer, mediaType));
576
+ acceptedResults.push(result);
577
+ const messageId = result.messageId;
310
578
  const sentRemoteJid = resolveActualSentRemoteJid(result, jid);
311
579
  const trailingTextChunks = [visibleTextAfterVoice, ...textChunks].filter((chunk) => Boolean(chunk));
312
580
  if (trailingTextChunks.length > 0) {
@@ -315,9 +583,10 @@ async function sendMessageWhatsApp(to, body, options) {
315
583
  toJid: sentRemoteJid
316
584
  });
317
585
  for (const trailingText of trailingTextChunks) {
318
- const trailingResult = sendOptions ? await active.sendMessage(to, trailingText, void 0, void 0, sendOptions) : await active.sendMessage(to, trailingText, void 0, void 0);
586
+ const trailingResult = requireWhatsAppAcceptedSendResult(sendOptions ? await active.sendMessage(to, trailingText, void 0, void 0, sendOptions) : await active.sendMessage(to, trailingText, void 0, void 0));
587
+ acceptedResults.push(trailingResult);
319
588
  await options.onDeliveryResult?.({
320
- messageId: trailingResult?.messageId ?? "unknown",
589
+ messageId: trailingResult.messageId,
321
590
  toJid: resolveActualSentRemoteJid(trailingResult, jid)
322
591
  });
323
592
  }
@@ -338,7 +607,12 @@ async function sendMessageWhatsApp(to, body, options) {
338
607
  to: redactedTo,
339
608
  hasMedia
340
609
  }, "failed to send via web session");
341
- throw err;
610
+ const firstAccepted = acceptedResults[0];
611
+ throw mergeWhatsAppAcceptedSendError({
612
+ error: err,
613
+ kind: firstAccepted?.kind ?? (hasMedia ? "media" : "text"),
614
+ results: acceptedResults
615
+ });
342
616
  }
343
617
  }
344
618
  async function sendTypingWhatsApp(to, options) {
@@ -413,7 +687,8 @@ async function sendPollWhatsApp(to, poll, options) {
413
687
  maxSelections: normalized.maxSelections
414
688
  }, "sending poll");
415
689
  if (!isWhatsAppNewsletterJid(jid)) await active.assertSendReady?.(to);
416
- const messageId = (await active.sendPoll(to, normalized))?.messageId ?? "unknown";
690
+ const result = requireWhatsAppAcceptedSendResult(await active.sendPoll(to, normalized));
691
+ const messageId = result.messageId;
417
692
  const durationMs = Date.now() - startedAt;
418
693
  outboundLog.info(`Sent poll ${messageId} -> ${redactedJid} (${durationMs}ms)`);
419
694
  logger.info({
@@ -422,7 +697,7 @@ async function sendPollWhatsApp(to, poll, options) {
422
697
  }, "sent poll");
423
698
  return {
424
699
  messageId,
425
- toJid: jid
700
+ toJid: resolveActualSentRemoteJid(result, jid)
426
701
  };
427
702
  } catch (err) {
428
703
  logger.error({
@@ -433,4 +708,4 @@ async function sendPollWhatsApp(to, poll, options) {
433
708
  }
434
709
  }
435
710
  //#endregion
436
- export { normalizeWhatsAppOutboundPayload as a, prepareWhatsAppOutboundMedia as c, sendTypingWhatsApp as i, resolveWhatsAppDocumentFileName as l, sendPollWhatsApp as n, normalizeWhatsAppPayloadText as o, sendReactionWhatsApp as r, normalizeWhatsAppPayloadTextPreservingIndentation as s, sendMessageWhatsApp as t, resolveWhatsAppReactionLevel as u };
711
+ export { resolveWhatsAppDocumentFileName as _, sendWhatsAppUploadFile as a, normalizeWhatsAppPayloadTextPreservingIndentation as c, listWhatsAppSendResultMessageIds as d, mergeWhatsAppAcceptedSendError as f, withWhatsAppLogicalDeliveryActivity as g, rememberWhatsAppPartialSend as h, sendTypingWhatsApp as i, prepareWhatsAppOutboundMedia as l, rememberWhatsAppAcceptedSend as m, sendPollWhatsApp as n, normalizeWhatsAppOutboundPayload as o, normalizeWhatsAppSendResult as p, sendReactionWhatsApp as r, normalizeWhatsAppPayloadText as s, sendMessageWhatsApp as t, combineWhatsAppSendResults as u, resolveWhatsAppReactionLevel as v };