@openclaw/zalo 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.
@@ -1,4 +1,4 @@
1
- import { t as sendMessageZalo } from "./send-Gv3l5EGI.js";
1
+ import { t as sendMessageZalo } from "./send-znUV-Z2f.js";
2
2
  //#region extensions/zalo/src/actions.runtime.ts
3
3
  const zaloActionsRuntime = { sendMessageZalo };
4
4
  //#endregion
package/dist/api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as zaloPlugin } from "./channel-VPbtV3Oq.js";
1
+ import { t as zaloPlugin } from "./channel-CqYdiVMC.js";
2
2
  import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-DigRD3j1.js";
3
- import { r as resolveZaloRuntimeGroupPolicy, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
3
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-B_fAqJAN.js";
4
4
  import { zaloSetupWizard } from "./setup-api.js";
5
- export { createZaloSetupWizardProxy, evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
5
+ export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
@@ -6,6 +6,7 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
6
6
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
7
7
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, mapAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
8
8
  import { buildChannelConfigSchema, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
9
+ import { defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
9
10
  import { buildOpenGroupPolicyRestrictSendersWarning, buildOpenGroupPolicyWarning, createOpenProviderGroupPolicyWarningCollector } from "openclaw/plugin-sdk/channel-policy";
10
11
  import { createEmptyChannelResult, createRawChannelSendResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
11
12
  import { buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/channel-status";
@@ -24,7 +25,7 @@ import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigS
24
25
  import { z } from "openclaw/plugin-sdk/zod";
25
26
  import { coerceStatusIssueAccountId, readStatusIssueFields } from "openclaw/plugin-sdk/extension-shared";
26
27
  //#region extensions/zalo/src/actions.ts
27
- const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-kJ65ZxW7.js"), "zaloActionsRuntime");
28
+ const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-BQ5w5Doy.js"), "zaloActionsRuntime");
28
29
  const providerId = "zalo";
29
30
  function listEnabledAccounts(cfg, accountId) {
30
31
  return (accountId ? [resolveZaloAccount({
@@ -173,7 +174,7 @@ function normalizeZaloMessagingTarget(raw) {
173
174
  if (!trimmed) return;
174
175
  return trimmed.replace(/^(zalo|zl):/i, "").trim();
175
176
  }
176
- const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-BnTAWQx5.js"));
177
+ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-CAA9uPYO.js"));
177
178
  const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-2Up3yWov.js")).zaloSetupWizard);
178
179
  const zaloTextChunkLimit = 2e3;
179
180
  const zaloRawSendResultAdapter = createRawChannelSendResultAdapter({
@@ -192,6 +193,29 @@ const zaloRawSendResultAdapter = createRawChannelSendResultAdapter({
192
193
  cfg
193
194
  })
194
195
  });
196
+ const zaloMessageAdapter = defineChannelMessageAdapter({
197
+ id: "zalo",
198
+ durableFinal: { capabilities: {
199
+ text: true,
200
+ media: true,
201
+ messageSendingHooks: true
202
+ } },
203
+ send: {
204
+ text: async ({ to, text, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
205
+ to,
206
+ text,
207
+ accountId: accountId ?? void 0,
208
+ cfg
209
+ }),
210
+ media: async ({ to, text, mediaUrl, accountId, cfg }) => await (await loadZaloChannelRuntime()).sendZaloText({
211
+ to,
212
+ text,
213
+ accountId: accountId ?? void 0,
214
+ mediaUrl,
215
+ cfg
216
+ })
217
+ }
218
+ });
195
219
  const zaloConfigAdapter = createScopedChannelConfigAdapter({
196
220
  sectionKey: "zalo",
197
221
  listAccountIds: listZaloAccountIds,
@@ -310,7 +334,8 @@ const zaloPlugin = createChatChannelPlugin({
310
334
  };
311
335
  }
312
336
  }),
313
- gateway: { startAccount: async (ctx) => await (await loadZaloChannelRuntime()).startZaloGatewayAccount(ctx) }
337
+ gateway: { startAccount: async (ctx) => await (await loadZaloChannelRuntime()).startZaloGatewayAccount(ctx) },
338
+ message: zaloMessageAdapter
314
339
  },
315
340
  security: {
316
341
  resolveDmPolicy: resolveZaloDmPolicy,
@@ -1,2 +1,2 @@
1
- import { t as zaloPlugin } from "./channel-VPbtV3Oq.js";
1
+ import { t as zaloPlugin } from "./channel-CqYdiVMC.js";
2
2
  export { zaloPlugin };
@@ -1,7 +1,7 @@
1
1
  import { c as normalizeSecretInputString } from "./accounts-9NLDDlZ8.js";
2
- import { n as PAIRING_APPROVED_MESSAGE } from "./runtime-api-MOTmRW4F.js";
3
- import { i as getMe, n as ZaloApiError, t as resolveZaloProxyFetch } from "./proxy-CY8VuC6H.js";
4
- import { t as sendMessageZalo } from "./send-Gv3l5EGI.js";
2
+ import { n as PAIRING_APPROVED_MESSAGE } from "./runtime-api-df534_Ih.js";
3
+ import { i as getMe, n as ZaloApiError, t as resolveZaloProxyFetch } from "./proxy-D5AGEsI0.js";
4
+ import { t as sendMessageZalo } from "./send-znUV-Z2f.js";
5
5
  import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
6
6
  //#region extensions/zalo/src/probe.ts
7
7
  async function probeZalo(token, timeoutMs = 5e3, fetcher) {
@@ -87,7 +87,7 @@ async function startZaloGatewayAccount(ctx) {
87
87
  setStatus: ctx.setStatus
88
88
  });
89
89
  ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
90
- const { monitorZaloProvider } = await import("./monitor-DMysJBWa.js");
90
+ const { monitorZaloProvider } = await import("./monitor-CpIJP2uj.js");
91
91
  return monitorZaloProvider({
92
92
  token,
93
93
  account,
@@ -1,3 +1,3 @@
1
1
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Dw93tGo2.js";
2
- import { r as resolveZaloRuntimeGroupPolicy, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
3
- export { collectRuntimeConfigAssignments, evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy, secretTargetRegistryEntries };
2
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-B_fAqJAN.js";
3
+ export { collectRuntimeConfigAssignments, resolveZaloRuntimeGroupPolicy, secretTargetRegistryEntries };
@@ -0,0 +1,15 @@
1
+ import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
2
+ //#region extensions/zalo/src/group-access.ts
3
+ const ZALO_ALLOW_FROM_PREFIX_RE = /^(zalo|zl):/i;
4
+ function normalizeZaloAllowEntry(value) {
5
+ return value.trim().replace(ZALO_ALLOW_FROM_PREFIX_RE, "").trim().toLowerCase();
6
+ }
7
+ function resolveZaloRuntimeGroupPolicy(params) {
8
+ return resolveOpenProviderRuntimeGroupPolicy({
9
+ providerConfigPresent: params.providerConfigPresent,
10
+ groupPolicy: params.groupPolicy,
11
+ defaultGroupPolicy: params.defaultGroupPolicy
12
+ });
13
+ }
14
+ //#endregion
15
+ export { resolveZaloRuntimeGroupPolicy as n, normalizeZaloAllowEntry as t };
@@ -1,20 +1,36 @@
1
- import { n as isZaloSenderAllowed, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
1
+ import { n as resolveZaloRuntimeGroupPolicy, t as normalizeZaloAllowEntry } from "./group-access-B_fAqJAN.js";
2
2
  import { t as getZaloRuntime } from "./runtime-BRFxnYQx.js";
3
- import { a as getUpdates, c as sendMessage, l as sendPhoto, n as ZaloApiError, o as getWebhookInfo, r as deleteWebhook, s as sendChatAction, t as resolveZaloProxyFetch, u as setWebhook } from "./proxy-CY8VuC6H.js";
3
+ import { a as getUpdates, c as sendMessage, l as sendPhoto, n as ZaloApiError, o as getWebhookInfo, r as deleteWebhook, s as sendChatAction, t as resolveZaloProxyFetch, u as setWebhook } from "./proxy-D5AGEsI0.js";
4
4
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
5
+ import { normalizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
5
6
  import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
6
7
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
7
- import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
8
8
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
9
- import { resolveDirectDmAuthorizationOutcome, resolveSenderCommandAuthorizationWithRuntime } from "openclaw/plugin-sdk/command-auth";
10
9
  import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
11
10
  import { registerPluginHttpRoute, resolveWebhookPath } from "openclaw/plugin-sdk/webhook-ingress";
11
+ import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
12
12
  import { waitForAbortSignal } from "openclaw/plugin-sdk/runtime-env";
13
13
  import { randomBytes } from "node:crypto";
14
- import { chmod, mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
14
+ import { readFile, readdir, stat, unlink } from "node:fs/promises";
15
15
  import { join } from "node:path";
16
16
  import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
17
+ import { privateFileStore } from "openclaw/plugin-sdk/security-runtime";
17
18
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
19
+ //#region extensions/zalo/src/monitor-durable.ts
20
+ function prepareZaloDurableReplyPayload(params) {
21
+ if (!params.payload.text) return params.payload;
22
+ return {
23
+ ...params.payload,
24
+ text: params.convertMarkdownTables(params.payload.text, params.tableMode)
25
+ };
26
+ }
27
+ function resolveZaloDurableReplyOptions(params) {
28
+ if (params.infoKind !== "final") return false;
29
+ const reply = resolveSendableOutboundReplyParts(params.payload);
30
+ if (reply.hasMedia || !reply.hasText) return false;
31
+ return { to: params.chatId };
32
+ }
33
+ //#endregion
18
34
  //#region extensions/zalo/src/outbound-media.ts
19
35
  const ZALO_OUTBOUND_MEDIA_TTL_MS = 2 * 6e4;
20
36
  const ZALO_OUTBOUND_MEDIA_SEGMENT = "media";
@@ -34,11 +50,8 @@ function createHostedZaloMediaToken() {
34
50
  return randomBytes(24).toString("hex");
35
51
  }
36
52
  async function ensureHostedZaloMediaDir() {
37
- await mkdir(ZALO_OUTBOUND_MEDIA_DIR, {
38
- recursive: true,
39
- mode: 448
40
- });
41
- await chmod(ZALO_OUTBOUND_MEDIA_DIR, 448).catch(() => void 0);
53
+ await privateFileStore(ZALO_OUTBOUND_MEDIA_DIR).writeText(".ready", "");
54
+ await unlink(join(ZALO_OUTBOUND_MEDIA_DIR, ".ready")).catch(() => void 0);
42
55
  }
43
56
  async function deleteHostedZaloMediaEntry(id) {
44
57
  await Promise.all([unlink(resolveHostedZaloMediaMetadataPath(id)).catch(() => void 0), unlink(resolveHostedZaloMediaBufferPath(id)).catch(() => void 0)]);
@@ -97,16 +110,14 @@ async function prepareHostedZaloMediaUrl(params) {
97
110
  const id = createHostedZaloMediaId();
98
111
  const token = createHostedZaloMediaToken();
99
112
  const publicBaseUrl = new URL(params.webhookUrl).origin;
100
- await writeFile(resolveHostedZaloMediaBufferPath(id), media.buffer, { mode: 384 });
113
+ const store = privateFileStore(ZALO_OUTBOUND_MEDIA_DIR);
114
+ await store.writeText(`${id}.bin`, media.buffer);
101
115
  try {
102
- await writeFile(resolveHostedZaloMediaMetadataPath(id), JSON.stringify({
116
+ await store.writeJson(`${id}.json`, {
103
117
  routePath,
104
118
  token,
105
119
  contentType: media.contentType,
106
120
  expiresAt: Date.now() + ZALO_OUTBOUND_MEDIA_TTL_MS
107
- }), {
108
- encoding: "utf8",
109
- mode: 384
110
121
  });
111
122
  } catch (error) {
112
123
  await deleteHostedZaloMediaEntry(id);
@@ -175,7 +186,7 @@ const ZALO_TYPING_TIMEOUT_MS = 5e3;
175
186
  let zaloWebhookModulePromise;
176
187
  const hostedMediaRouteRefs = /* @__PURE__ */ new Map();
177
188
  function loadZaloWebhookModule() {
178
- zaloWebhookModulePromise ??= import("./monitor.webhook-DqnuvgjV.js");
189
+ zaloWebhookModulePromise ??= import("./monitor.webhook-DDHdDX2R.js");
179
190
  return zaloWebhookModulePromise;
180
191
  }
181
192
  function registerSharedHostedMediaRoute(params) {
@@ -397,56 +408,58 @@ async function authorizeZaloMessage(params) {
397
408
  const senderId = from.id;
398
409
  const senderName = from.display_name ?? from.name;
399
410
  const dmPolicy = account.config.dmPolicy ?? "pairing";
400
- const configAllowFrom = (account.config.allowFrom ?? []).map((v) => String(v));
401
- const configuredGroupAllowFrom = (account.config.groupAllowFrom ?? []).map((v) => String(v));
402
- const groupAllowFrom = configuredGroupAllowFrom.length > 0 ? configuredGroupAllowFrom : configAllowFrom;
403
411
  const defaultGroupPolicy = resolveDefaultGroupPolicy(config);
404
- const groupAccess = isGroup ? evaluateZaloGroupAccess({
412
+ const rawBody = text?.trim() || (mediaPath ? "<media:image>" : "");
413
+ const { groupPolicy, providerMissingFallbackApplied } = resolveZaloRuntimeGroupPolicy({
405
414
  providerConfigPresent: config.channels?.zalo !== void 0,
406
- configuredGroupPolicy: account.config.groupPolicy,
407
- defaultGroupPolicy,
408
- groupAllowFrom,
409
- senderId
410
- }) : void 0;
411
- if (groupAccess) {
415
+ groupPolicy: account.config.groupPolicy,
416
+ defaultGroupPolicy
417
+ });
418
+ const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, config);
419
+ const access = await resolveStableChannelMessageIngress({
420
+ channelId: "zalo",
421
+ accountId: account.accountId,
422
+ identity: {
423
+ key: "zalo-user-id",
424
+ normalize: normalizeZaloAllowEntry,
425
+ sensitivity: "pii",
426
+ entryIdPrefix: "zalo-entry"
427
+ },
428
+ cfg: config,
429
+ readStoreAllowFrom: async () => await pairing.readAllowFromStore(),
430
+ subject: { stableId: senderId },
431
+ conversation: {
432
+ kind: isGroup ? "group" : "direct",
433
+ id: chatId
434
+ },
435
+ providerMissingFallbackApplied,
436
+ dmPolicy,
437
+ groupPolicy,
438
+ policy: { groupAllowFromFallbackToAllowFrom: true },
439
+ allowFrom: normalizeStringEntries(account.config.allowFrom),
440
+ groupAllowFrom: normalizeStringEntries(account.config.groupAllowFrom),
441
+ command: shouldComputeAuth ? {} : void 0
442
+ });
443
+ const senderAccess = access.senderAccess;
444
+ if (isGroup) {
412
445
  warnMissingProviderGroupPolicyFallbackOnce({
413
- providerMissingFallbackApplied: groupAccess.providerMissingFallbackApplied,
446
+ providerMissingFallbackApplied: senderAccess.providerMissingFallbackApplied,
414
447
  providerKey: "zalo",
415
448
  accountId: account.accountId,
416
449
  log: (message) => logVerbose(core, runtime, message)
417
450
  });
418
- if (!groupAccess.allowed) {
419
- if (groupAccess.reason === "disabled") logVerbose(core, runtime, `zalo: drop group ${chatId} (groupPolicy=disabled)`);
420
- else if (groupAccess.reason === "empty_allowlist") logVerbose(core, runtime, `zalo: drop group ${chatId} (groupPolicy=allowlist, no groupAllowFrom)`);
421
- else if (groupAccess.reason === "sender_not_allowlisted") logVerbose(core, runtime, `zalo: drop group sender ${senderId} (groupPolicy=allowlist)`);
451
+ if (!senderAccess.allowed) {
452
+ if (senderAccess.reasonCode === "group_policy_disabled") logVerbose(core, runtime, `zalo: drop group ${chatId} (groupPolicy=disabled)`);
453
+ else if (senderAccess.reasonCode === "group_policy_empty_allowlist") logVerbose(core, runtime, `zalo: drop group ${chatId} (groupPolicy=allowlist, no groupAllowFrom)`);
454
+ else if (senderAccess.reasonCode === "group_policy_not_allowlisted") logVerbose(core, runtime, `zalo: drop group sender ${senderId} (groupPolicy=allowlist)`);
422
455
  return;
423
456
  }
424
457
  }
425
- const rawBody = text?.trim() || (mediaPath ? "<media:image>" : "");
426
- const { senderAllowedForCommands, commandAuthorized } = await resolveSenderCommandAuthorizationWithRuntime({
427
- cfg: config,
428
- rawBody,
429
- isGroup,
430
- dmPolicy,
431
- configuredAllowFrom: configAllowFrom,
432
- configuredGroupAllowFrom: groupAllowFrom,
433
- senderId,
434
- isSenderAllowed: isZaloSenderAllowed,
435
- channel: "zalo",
436
- accountId: account.accountId,
437
- readAllowFromStore: pairing.readAllowFromStore,
438
- runtime: core.channel.commands
439
- });
440
- const directDmOutcome = resolveDirectDmAuthorizationOutcome({
441
- isGroup,
442
- dmPolicy,
443
- senderAllowedForCommands
444
- });
445
- if (directDmOutcome === "disabled") {
458
+ if (!isGroup && senderAccess.decision === "block" && senderAccess.reasonCode === "dm_policy_disabled") {
446
459
  logVerbose(core, runtime, `Blocked zalo DM from ${senderId} (dmPolicy=disabled)`);
447
460
  return;
448
461
  }
449
- if (directDmOutcome === "unauthorized") {
462
+ if (!isGroup && senderAccess.decision !== "allow") {
450
463
  if (dmPolicy === "pairing") await pairing.issueChallenge({
451
464
  senderId,
452
465
  senderIdLine: `Your Zalo user id: ${senderId}`,
@@ -470,7 +483,7 @@ async function authorizeZaloMessage(params) {
470
483
  }
471
484
  return {
472
485
  chatId,
473
- commandAuthorized,
486
+ commandAuthorized: access.commandAccess.requested ? access.commandAccess.authorized : void 0,
474
487
  isGroup,
475
488
  rawBody,
476
489
  senderId,
@@ -559,12 +572,54 @@ async function processMessageWithPipeline(params) {
559
572
  channel: "zalo",
560
573
  accountId: account.accountId
561
574
  });
562
- const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
575
+ await core.channel.turn.runAssembled({
563
576
  cfg: config,
564
- agentId: route.agentId,
565
577
  channel: "zalo",
566
578
  accountId: account.accountId,
567
- typing: {
579
+ agentId: route.agentId,
580
+ routeSessionKey: route.sessionKey,
581
+ storePath,
582
+ ctxPayload,
583
+ recordInboundSession: core.channel.session.recordInboundSession,
584
+ dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
585
+ delivery: {
586
+ preparePayload: (payload) => prepareZaloDurableReplyPayload({
587
+ payload,
588
+ tableMode,
589
+ convertMarkdownTables: core.channel.text.convertMarkdownTables
590
+ }),
591
+ durable: (payload, info) => resolveZaloDurableReplyOptions({
592
+ payload,
593
+ infoKind: info.kind,
594
+ chatId
595
+ }),
596
+ deliver: async (payload) => {
597
+ await deliverZaloReply({
598
+ payload,
599
+ token,
600
+ chatId,
601
+ runtime,
602
+ core,
603
+ config,
604
+ webhookUrl: params.webhookUrl,
605
+ webhookPath: params.webhookPath,
606
+ proxyUrl: account.config.proxy,
607
+ mediaMaxBytes: params.mediaMaxMb * 1024 * 1024,
608
+ canHostMedia: params.canHostMedia,
609
+ accountId: account.accountId,
610
+ statusSink,
611
+ fetcher,
612
+ tableMode: "off"
613
+ });
614
+ },
615
+ onDelivered: () => {
616
+ statusSink?.({ lastOutboundAt: Date.now() });
617
+ },
618
+ onError: (err, info) => {
619
+ runtime.error?.(`[${account.accountId}] Zalo ${info.kind} reply failed: ${String(err)}`);
620
+ }
621
+ },
622
+ replyPipeline: { typing: {
568
623
  start: async () => {
569
624
  await sendChatAction(token, {
570
625
  chat_id: chatId,
@@ -580,62 +635,10 @@ async function processMessageWithPipeline(params) {
580
635
  error: err
581
636
  });
582
637
  }
583
- }
584
- });
585
- await core.channel.turn.run({
586
- channel: "zalo",
587
- accountId: account.accountId,
588
- raw: message,
589
- adapter: {
590
- ingest: () => ({
591
- id: message_id,
592
- timestamp: date ? date * 1e3 : void 0,
593
- rawText: rawBody,
594
- textForAgent: rawBody,
595
- textForCommands: rawBody,
596
- raw: message
597
- }),
598
- resolveTurn: () => ({
599
- cfg: config,
600
- channel: "zalo",
601
- accountId: account.accountId,
602
- agentId: route.agentId,
603
- routeSessionKey: route.sessionKey,
604
- storePath,
605
- ctxPayload,
606
- recordInboundSession: core.channel.session.recordInboundSession,
607
- dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
608
- delivery: {
609
- deliver: async (payload) => {
610
- await deliverZaloReply({
611
- payload,
612
- token,
613
- chatId,
614
- runtime,
615
- core,
616
- config,
617
- webhookUrl: params.webhookUrl,
618
- webhookPath: params.webhookPath,
619
- proxyUrl: account.config.proxy,
620
- mediaMaxBytes: params.mediaMaxMb * 1024 * 1024,
621
- canHostMedia: params.canHostMedia,
622
- accountId: account.accountId,
623
- statusSink,
624
- fetcher,
625
- tableMode
626
- });
627
- },
628
- onError: (err, info) => {
629
- runtime.error?.(`[${account.accountId}] Zalo ${info.kind} reply failed: ${String(err)}`);
630
- }
631
- },
632
- dispatcherOptions: replyPipeline,
633
- replyOptions: { onModelSelected },
634
- record: { onRecordError: (err) => {
635
- runtime.error?.(`zalo: failed updating session meta: ${String(err)}`);
636
- } }
637
- })
638
- }
638
+ } },
639
+ record: { onRecordError: (err) => {
640
+ runtime.error?.(`zalo: failed updating session meta: ${String(err)}`);
641
+ } }
639
642
  });
640
643
  }
641
644
  async function deliverZaloReply(params) {
@@ -1,6 +1,6 @@
1
- import { $ as withResolvedWebhookRequestPipeline, B as resolveClientIp, F as readJsonWebhookBodyOrReject, R as registerWebhookTarget, i as WEBHOOK_RATE_LIMIT_DEFAULTS, q as resolveWebhookTargetWithAuthOrRejectSync, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, v as createFixedWindowRateLimiter, y as createWebhookAnomalyTracker, z as registerWebhookTargetWithPluginRoute } from "./runtime-api-MOTmRW4F.js";
2
- import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
1
+ import { L as registerWebhookTarget, P as readJsonWebhookBodyOrReject, R as registerWebhookTargetWithPluginRoute, W as resolveWebhookTargetWithAuthOrRejectSync, X as withResolvedWebhookRequestPipeline, i as WEBHOOK_RATE_LIMIT_DEFAULTS, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, v as createFixedWindowRateLimiter, y as createWebhookAnomalyTracker, z as resolveClientIp } from "./runtime-api-df534_Ih.js";
3
2
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
3
+ import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
4
4
  //#region extensions/zalo/src/monitor.webhook.ts
5
5
  const ZALO_WEBHOOK_REPLAY_WINDOW_MS = 5 * 6e4;
6
6
  const webhookTargets = /* @__PURE__ */ new Map();
@@ -1,5 +1,5 @@
1
1
  import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
2
- import { ProxyAgent, fetch as fetch$1 } from "undici";
2
+ import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
3
3
  //#region extensions/zalo/src/api.ts
4
4
  /**
5
5
  * Zalo Bot API client
@@ -123,11 +123,7 @@ function resolveZaloProxyFetch(proxyUrl) {
123
123
  if (!trimmed) return;
124
124
  const cached = proxyCache.get(trimmed);
125
125
  if (cached) return cached;
126
- const agent = new ProxyAgent(trimmed);
127
- const fetcher = (input, init) => fetch$1(input, {
128
- ...init,
129
- dispatcher: agent
130
- });
126
+ const fetcher = makeProxyFetch(trimmed);
131
127
  proxyCache.set(trimmed, fetcher);
132
128
  return fetcher;
133
129
  }
@@ -1,5 +1,6 @@
1
1
  import "./runtime-BRFxnYQx.js";
2
- import { formatAllowFromLowercase as formatAllowFromLowercase$1, isNormalizedSenderAllowed as isNormalizedSenderAllowed$1 } from "openclaw/plugin-sdk/allow-from";
2
+ import { formatAllowFromLowercase as formatAllowFromLowercase$1, isNormalizedSenderAllowed } from "openclaw/plugin-sdk/allow-from";
3
+ import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-message";
3
4
  import { PAIRING_APPROVED_MESSAGE, buildTokenChannelStatusSummary as buildTokenChannelStatusSummary$1 } from "openclaw/plugin-sdk/channel-status";
4
5
  import { deliverTextOrMediaReply as deliverTextOrMediaReply$1, isNumericTargetId as isNumericTargetId$1, sendPayloadWithChunkedTextAndMedia as sendPayloadWithChunkedTextAndMedia$1 } from "openclaw/plugin-sdk/reply-payload";
5
6
  import { buildBaseAccountStatusSnapshot } from "openclaw/plugin-sdk/status-helpers";
@@ -7,13 +8,10 @@ import { chunkTextForOutbound as chunkTextForOutbound$1 } from "openclaw/plugin-
7
8
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildChannelConfigSchema, createDedupeCache, formatPairingApproveHint, jsonResult, normalizeAccountId as normalizeAccountId$1, readStringParam, resolveClientIp } from "openclaw/plugin-sdk/core";
8
9
  import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$1, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
9
10
  import { addWildcardAllowFrom as addWildcardAllowFrom$1, applyAccountNameToChannelSection, applySetupAccountConfigPatch, buildSingleChannelSecretPromptState as buildSingleChannelSecretPromptState$1, mergeAllowFromEntries as mergeAllowFromEntries$1, migrateBaseNameToDefaultAccount, promptSingleChannelSecretInput as promptSingleChannelSecretInput$1, runSingleChannelSecretStep as runSingleChannelSecretStep$1, setTopLevelChannelDmPolicyWithAllowFrom } from "openclaw/plugin-sdk/setup";
10
- import { evaluateSenderGroupAccess as evaluateSenderGroupAccess$1 } from "openclaw/plugin-sdk/group-access";
11
11
  import { resolveDefaultGroupPolicy as resolveDefaultGroupPolicy$1, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce as warnMissingProviderGroupPolicyFallbackOnce$1 } from "openclaw/plugin-sdk/runtime-group-policy";
12
12
  import { createChannelPairingController as createChannelPairingController$1 } from "openclaw/plugin-sdk/channel-pairing";
13
- import { createChannelReplyPipeline as createChannelReplyPipeline$1 } from "openclaw/plugin-sdk/channel-reply-pipeline";
14
13
  import { logTypingFailure as logTypingFailure$1 } from "openclaw/plugin-sdk/channel-feedback";
15
- import { resolveDirectDmAuthorizationOutcome as resolveDirectDmAuthorizationOutcome$1, resolveSenderCommandAuthorizationWithRuntime as resolveSenderCommandAuthorizationWithRuntime$1 } from "openclaw/plugin-sdk/command-auth";
16
14
  import { resolveInboundRouteEnvelopeBuilderWithRuntime as resolveInboundRouteEnvelopeBuilderWithRuntime$1 } from "openclaw/plugin-sdk/inbound-envelope";
17
15
  import { waitForAbortSignal } from "openclaw/plugin-sdk/runtime";
18
16
  import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, readJsonWebhookBodyOrReject, registerPluginHttpRoute as registerPluginHttpRoute$1, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveWebhookPath as resolveWebhookPath$1, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-ingress";
19
- export { withResolvedWebhookRequestPipeline as $, migrateBaseNameToDefaultAccount as A, resolveClientIp as B, formatPairingApproveHint as C, jsonResult as D, isNumericTargetId$1 as E, readJsonWebhookBodyOrReject as F, resolveSenderCommandAuthorizationWithRuntime$1 as G, resolveDirectDmAuthorizationOutcome$1 as H, readStringParam as I, runSingleChannelSecretStep$1 as J, resolveWebhookPath$1 as K, registerPluginHttpRoute$1 as L, normalizeResolvedSecretInputString as M, normalizeSecretInputString as N, logTypingFailure$1 as O, promptSingleChannelSecretInput$1 as P, warnMissingProviderGroupPolicyFallbackOnce$1 as Q, registerWebhookTarget as R, formatAllowFromLowercase$1 as S, isNormalizedSenderAllowed$1 as T, resolveInboundRouteEnvelopeBuilderWithRuntime$1 as U, resolveDefaultGroupPolicy$1 as V, resolveOpenProviderRuntimeGroupPolicy$1 as W, setTopLevelChannelDmPolicyWithAllowFrom as X, sendPayloadWithChunkedTextAndMedia$1 as Y, waitForAbortSignal as Z, createDedupeCache as _, addWildcardAllowFrom$1 as a, deliverTextOrMediaReply$1 as b, applySetupAccountConfigPatch as c, buildSecretInputSchema as d, buildSingleChannelSecretPromptState$1 as f, createChannelReplyPipeline$1 as g, createChannelPairingController$1 as h, WEBHOOK_RATE_LIMIT_DEFAULTS as i, normalizeAccountId$1 as j, mergeAllowFromEntries$1 as k, buildBaseAccountStatusSnapshot as l, chunkTextForOutbound$1 as m, PAIRING_APPROVED_MESSAGE as n, applyAccountNameToChannelSection as o, buildTokenChannelStatusSummary$1 as p, resolveWebhookTargetWithAuthOrRejectSync as q, WEBHOOK_ANOMALY_COUNTER_DEFAULTS as r, applyBasicWebhookRequestGuards as s, DEFAULT_ACCOUNT_ID$1 as t, buildChannelConfigSchema as u, createFixedWindowRateLimiter as v, hasConfiguredSecretInput$1 as w, evaluateSenderGroupAccess$1 as x, createWebhookAnomalyTracker as y, registerWebhookTargetWithPluginRoute as z };
17
+ export { normalizeAccountId$1 as A, resolveDefaultGroupPolicy$1 as B, hasConfiguredSecretInput$1 as C, logTypingFailure$1 as D, jsonResult as E, readStringParam as F, runSingleChannelSecretStep$1 as G, resolveOpenProviderRuntimeGroupPolicy$1 as H, registerPluginHttpRoute$1 as I, waitForAbortSignal as J, sendPayloadWithChunkedTextAndMedia$1 as K, registerWebhookTarget as L, normalizeSecretInputString as M, promptSingleChannelSecretInput$1 as N, mergeAllowFromEntries$1 as O, readJsonWebhookBodyOrReject as P, registerWebhookTargetWithPluginRoute as R, formatPairingApproveHint as S, isNumericTargetId$1 as T, resolveWebhookPath$1 as U, resolveInboundRouteEnvelopeBuilderWithRuntime$1 as V, resolveWebhookTargetWithAuthOrRejectSync as W, withResolvedWebhookRequestPipeline as X, warnMissingProviderGroupPolicyFallbackOnce$1 as Y, createDedupeCache as _, addWildcardAllowFrom$1 as a, deliverTextOrMediaReply$1 as b, applySetupAccountConfigPatch as c, buildSecretInputSchema as d, buildSingleChannelSecretPromptState$1 as f, createChannelPairingController$1 as g, createChannelMessageReplyPipeline as h, WEBHOOK_RATE_LIMIT_DEFAULTS as i, normalizeResolvedSecretInputString as j, migrateBaseNameToDefaultAccount as k, buildBaseAccountStatusSnapshot as l, chunkTextForOutbound$1 as m, PAIRING_APPROVED_MESSAGE as n, applyAccountNameToChannelSection as o, buildTokenChannelStatusSummary$1 as p, setTopLevelChannelDmPolicyWithAllowFrom as q, WEBHOOK_ANOMALY_COUNTER_DEFAULTS as r, applyBasicWebhookRequestGuards as s, DEFAULT_ACCOUNT_ID$1 as t, buildChannelConfigSchema as u, createFixedWindowRateLimiter as v, isNormalizedSenderAllowed as w, formatAllowFromLowercase$1 as x, createWebhookAnomalyTracker as y, resolveClientIp as z };
@@ -1,3 +1,3 @@
1
- import { $ as withResolvedWebhookRequestPipeline, A as migrateBaseNameToDefaultAccount, B as resolveClientIp, C as formatPairingApproveHint, D as jsonResult, E as isNumericTargetId, F as readJsonWebhookBodyOrReject, G as resolveSenderCommandAuthorizationWithRuntime, H as resolveDirectDmAuthorizationOutcome, I as readStringParam, J as runSingleChannelSecretStep, K as resolveWebhookPath, L as registerPluginHttpRoute, M as normalizeResolvedSecretInputString, N as normalizeSecretInputString, O as logTypingFailure, P as promptSingleChannelSecretInput, Q as warnMissingProviderGroupPolicyFallbackOnce, R as registerWebhookTarget, S as formatAllowFromLowercase, T as isNormalizedSenderAllowed, U as resolveInboundRouteEnvelopeBuilderWithRuntime, V as resolveDefaultGroupPolicy, W as resolveOpenProviderRuntimeGroupPolicy, X as setTopLevelChannelDmPolicyWithAllowFrom, Y as sendPayloadWithChunkedTextAndMedia, Z as waitForAbortSignal, _ as createDedupeCache, a as addWildcardAllowFrom, b as deliverTextOrMediaReply, c as applySetupAccountConfigPatch, d as buildSecretInputSchema, f as buildSingleChannelSecretPromptState, g as createChannelReplyPipeline, h as createChannelPairingController, i as WEBHOOK_RATE_LIMIT_DEFAULTS, j as normalizeAccountId, k as mergeAllowFromEntries, l as buildBaseAccountStatusSnapshot, m as chunkTextForOutbound, n as PAIRING_APPROVED_MESSAGE, o as applyAccountNameToChannelSection, p as buildTokenChannelStatusSummary, q as resolveWebhookTargetWithAuthOrRejectSync, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, t as DEFAULT_ACCOUNT_ID, u as buildChannelConfigSchema, v as createFixedWindowRateLimiter, w as hasConfiguredSecretInput, x as evaluateSenderGroupAccess, y as createWebhookAnomalyTracker, z as registerWebhookTargetWithPluginRoute } from "./runtime-api-MOTmRW4F.js";
1
+ import { A as normalizeAccountId, B as resolveDefaultGroupPolicy, C as hasConfiguredSecretInput, D as logTypingFailure, E as jsonResult, F as readStringParam, G as runSingleChannelSecretStep, H as resolveOpenProviderRuntimeGroupPolicy, I as registerPluginHttpRoute, J as waitForAbortSignal, K as sendPayloadWithChunkedTextAndMedia, L as registerWebhookTarget, M as normalizeSecretInputString, N as promptSingleChannelSecretInput, O as mergeAllowFromEntries, P as readJsonWebhookBodyOrReject, R as registerWebhookTargetWithPluginRoute, S as formatPairingApproveHint, T as isNumericTargetId, U as resolveWebhookPath, V as resolveInboundRouteEnvelopeBuilderWithRuntime, W as resolveWebhookTargetWithAuthOrRejectSync, X as withResolvedWebhookRequestPipeline, Y as warnMissingProviderGroupPolicyFallbackOnce, _ as createDedupeCache, a as addWildcardAllowFrom, b as deliverTextOrMediaReply, c as applySetupAccountConfigPatch, d as buildSecretInputSchema, f as buildSingleChannelSecretPromptState, g as createChannelPairingController, h as createChannelMessageReplyPipeline, i as WEBHOOK_RATE_LIMIT_DEFAULTS, j as normalizeResolvedSecretInputString, k as migrateBaseNameToDefaultAccount, l as buildBaseAccountStatusSnapshot, m as chunkTextForOutbound, n as PAIRING_APPROVED_MESSAGE, o as applyAccountNameToChannelSection, p as buildTokenChannelStatusSummary, q as setTopLevelChannelDmPolicyWithAllowFrom, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, t as DEFAULT_ACCOUNT_ID, u as buildChannelConfigSchema, v as createFixedWindowRateLimiter, w as isNormalizedSenderAllowed, x as formatAllowFromLowercase, y as createWebhookAnomalyTracker, z as resolveClientIp } from "./runtime-api-df534_Ih.js";
2
2
  import { n as setZaloRuntime } from "./runtime-BRFxnYQx.js";
3
- export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, addWildcardAllowFrom, applyAccountNameToChannelSection, applyBasicWebhookRequestGuards, applySetupAccountConfigPatch, buildBaseAccountStatusSnapshot, buildChannelConfigSchema, buildSecretInputSchema, buildSingleChannelSecretPromptState, buildTokenChannelStatusSummary, chunkTextForOutbound, createChannelPairingController, createChannelReplyPipeline, createDedupeCache, createFixedWindowRateLimiter, createWebhookAnomalyTracker, deliverTextOrMediaReply, evaluateSenderGroupAccess, formatAllowFromLowercase, formatPairingApproveHint, hasConfiguredSecretInput, isNormalizedSenderAllowed, isNumericTargetId, jsonResult, logTypingFailure, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeResolvedSecretInputString, normalizeSecretInputString, promptSingleChannelSecretInput, readJsonWebhookBodyOrReject, readStringParam, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveDefaultGroupPolicy, resolveDirectDmAuthorizationOutcome, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveOpenProviderRuntimeGroupPolicy, resolveSenderCommandAuthorizationWithRuntime, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, runSingleChannelSecretStep, sendPayloadWithChunkedTextAndMedia, setTopLevelChannelDmPolicyWithAllowFrom, setZaloRuntime, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
3
+ export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, addWildcardAllowFrom, applyAccountNameToChannelSection, applyBasicWebhookRequestGuards, applySetupAccountConfigPatch, buildBaseAccountStatusSnapshot, buildChannelConfigSchema, buildSecretInputSchema, buildSingleChannelSecretPromptState, buildTokenChannelStatusSummary, chunkTextForOutbound, createChannelMessageReplyPipeline, createChannelPairingController, createDedupeCache, createFixedWindowRateLimiter, createWebhookAnomalyTracker, deliverTextOrMediaReply, formatAllowFromLowercase, formatPairingApproveHint, hasConfiguredSecretInput, isNormalizedSenderAllowed, isNumericTargetId, jsonResult, logTypingFailure, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeResolvedSecretInputString, normalizeSecretInputString, promptSingleChannelSecretInput, readJsonWebhookBodyOrReject, readStringParam, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveDefaultGroupPolicy, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveOpenProviderRuntimeGroupPolicy, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, runSingleChannelSecretStep, sendPayloadWithChunkedTextAndMedia, setTopLevelChannelDmPolicyWithAllowFrom, setZaloRuntime, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
@@ -1,28 +1,54 @@
1
1
  import { a as resolveZaloAccount, o as resolveZaloToken } from "./accounts-9NLDDlZ8.js";
2
- import { c as sendMessage, l as sendPhoto, t as resolveZaloProxyFetch } from "./proxy-CY8VuC6H.js";
2
+ import { c as sendMessage, l as sendPhoto, t as resolveZaloProxyFetch } from "./proxy-D5AGEsI0.js";
3
+ import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
3
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
5
  //#region extensions/zalo/src/send.ts
5
- function toZaloSendResult(response) {
6
+ function createZaloSendReceipt(params) {
7
+ const messageId = params.messageId?.trim();
8
+ return createMessageReceiptFromOutboundResults({
9
+ results: messageId ? [{
10
+ channel: "zalo",
11
+ messageId,
12
+ chatId: params.chatId
13
+ }] : [],
14
+ kind: params.kind
15
+ });
16
+ }
17
+ function toZaloSendResult(response, params) {
6
18
  if (response.ok && response.result) return {
7
19
  ok: true,
8
- messageId: response.result.message_id
20
+ messageId: response.result.message_id,
21
+ receipt: createZaloSendReceipt({
22
+ messageId: response.result.message_id,
23
+ chatId: params.chatId,
24
+ kind: params.kind
25
+ })
9
26
  };
10
27
  return {
11
28
  ok: false,
12
- error: "Failed to send message"
29
+ error: "Failed to send message",
30
+ receipt: createZaloSendReceipt({
31
+ chatId: params.chatId,
32
+ kind: params.kind
33
+ })
13
34
  };
14
35
  }
15
- async function runZaloSend(failureMessage, send) {
36
+ async function runZaloSend(failureMessage, params, send) {
16
37
  try {
17
- const result = toZaloSendResult(await send());
38
+ const result = toZaloSendResult(await send(), params);
18
39
  return result.ok ? result : {
19
40
  ok: false,
20
- error: failureMessage
41
+ error: failureMessage,
42
+ receipt: result.receipt
21
43
  };
22
44
  } catch (err) {
23
45
  return {
24
46
  ok: false,
25
- error: formatErrorMessage(err)
47
+ error: formatErrorMessage(err),
48
+ receipt: createZaloSendReceipt({
49
+ chatId: params.chatId,
50
+ kind: params.kind
51
+ })
26
52
  };
27
53
  }
28
54
  }
@@ -66,7 +92,11 @@ function resolveSendContextOrFailure(chatId, options) {
66
92
  const context = resolveValidatedSendContext(chatId, options);
67
93
  return context.ok ? { context } : { failure: {
68
94
  ok: false,
69
- error: context.error
95
+ error: context.error,
96
+ receipt: createZaloSendReceipt({
97
+ chatId,
98
+ kind: "unknown"
99
+ })
70
100
  } };
71
101
  }
72
102
  async function sendMessageZalo(chatId, text, options = {}) {
@@ -78,7 +108,10 @@ async function sendMessageZalo(chatId, text, options = {}) {
78
108
  token: context.token,
79
109
  caption: text || options.caption
80
110
  });
81
- return await runZaloSend("Failed to send message", () => sendMessage(context.token, {
111
+ return await runZaloSend("Failed to send message", {
112
+ chatId: context.chatId,
113
+ kind: "text"
114
+ }, () => sendMessage(context.token, {
82
115
  chat_id: context.chatId,
83
116
  text: text.slice(0, 2e3)
84
117
  }, context.fetcher));
@@ -89,9 +122,16 @@ async function sendPhotoZalo(chatId, photoUrl, options = {}) {
89
122
  const { context } = resolved;
90
123
  if (!photoUrl?.trim()) return {
91
124
  ok: false,
92
- error: "No photo URL provided"
125
+ error: "No photo URL provided",
126
+ receipt: createZaloSendReceipt({
127
+ chatId: context.chatId,
128
+ kind: "media"
129
+ })
93
130
  };
94
- return await runZaloSend("Failed to send photo", () => (async () => sendPhoto(context.token, {
131
+ return await runZaloSend("Failed to send photo", {
132
+ chatId: context.chatId,
133
+ kind: "media"
134
+ }, () => (async () => sendPhoto(context.token, {
95
135
  chat_id: context.chatId,
96
136
  photo: photoUrl.trim(),
97
137
  caption: options.caption?.slice(0, 2e3)
package/dist/setup-api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-DigRD3j1.js";
2
- import { r as resolveZaloRuntimeGroupPolicy, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
2
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-B_fAqJAN.js";
3
3
  import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
4
4
  //#region extensions/zalo/setup-api.ts
5
5
  function createLazyObjectValue(load) {
@@ -27,4 +27,4 @@ function loadSetupSurfaceModule() {
27
27
  }
28
28
  const zaloSetupWizard = createLazyObjectValue(() => loadSetupSurfaceModule().zaloSetupWizard);
29
29
  //#endregion
30
- export { createZaloSetupWizardProxy, evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloSetupAdapter, zaloSetupWizard };
30
+ export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloSetupAdapter, zaloSetupWizard };
package/dist/test-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as resolveZaloRuntimeGroupPolicy, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
2
- export { evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy };
1
+ import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-B_fAqJAN.js";
2
+ export { resolveZaloRuntimeGroupPolicy };
package/package.json CHANGED
@@ -1,21 +1,18 @@
1
1
  {
2
2
  "name": "@openclaw/zalo",
3
- "version": "2026.5.7",
3
+ "version": "2026.5.10-beta.1",
4
4
  "description": "OpenClaw Zalo channel plugin",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/openclaw/openclaw"
8
8
  },
9
9
  "type": "module",
10
- "dependencies": {
11
- "undici": "8.2.0"
12
- },
13
10
  "devDependencies": {
14
11
  "@openclaw/plugin-sdk": "workspace:*",
15
12
  "openclaw": "workspace:*"
16
13
  },
17
14
  "peerDependencies": {
18
- "openclaw": ">=2026.5.7"
15
+ "openclaw": ">=2026.5.10-beta.1"
19
16
  },
20
17
  "peerDependenciesMeta": {
21
18
  "openclaw": {
@@ -46,10 +43,10 @@
46
43
  "minHostVersion": ">=2026.4.10"
47
44
  },
48
45
  "compat": {
49
- "pluginApi": ">=2026.5.7"
46
+ "pluginApi": ">=2026.5.10-beta.1"
50
47
  },
51
48
  "build": {
52
- "openclawVersion": "2026.5.7"
49
+ "openclawVersion": "2026.5.10-beta.1"
53
50
  },
54
51
  "release": {
55
52
  "publishToClawHub": true,
@@ -1,30 +0,0 @@
1
- import { isNormalizedSenderAllowed } from "openclaw/plugin-sdk/allow-from";
2
- import { evaluateSenderGroupAccess, resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/group-access";
3
- //#region extensions/zalo/src/group-access.ts
4
- const ZALO_ALLOW_FROM_PREFIX_RE = /^(zalo|zl):/i;
5
- function isZaloSenderAllowed(senderId, allowFrom) {
6
- return isNormalizedSenderAllowed({
7
- senderId,
8
- allowFrom,
9
- stripPrefixRe: ZALO_ALLOW_FROM_PREFIX_RE
10
- });
11
- }
12
- function resolveZaloRuntimeGroupPolicy(params) {
13
- return resolveOpenProviderRuntimeGroupPolicy({
14
- providerConfigPresent: params.providerConfigPresent,
15
- groupPolicy: params.groupPolicy,
16
- defaultGroupPolicy: params.defaultGroupPolicy
17
- });
18
- }
19
- function evaluateZaloGroupAccess(params) {
20
- return evaluateSenderGroupAccess({
21
- providerConfigPresent: params.providerConfigPresent,
22
- configuredGroupPolicy: params.configuredGroupPolicy,
23
- defaultGroupPolicy: params.defaultGroupPolicy,
24
- groupAllowFrom: params.groupAllowFrom,
25
- senderId: params.senderId,
26
- isSenderAllowed: isZaloSenderAllowed
27
- });
28
- }
29
- //#endregion
30
- export { isZaloSenderAllowed as n, resolveZaloRuntimeGroupPolicy as r, evaluateZaloGroupAccess as t };