@openclaw/zalo 2026.7.2-beta.1 → 2026.7.2-beta.2

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.
package/dist/api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-fwCh0QUi.js";
2
- import { t as zaloPlugin } from "./channel-C_73K8pR.js";
2
+ import { t as zaloPlugin } from "./channel-CBPOM3Pc.js";
3
3
  import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-8qHRzDHx.js";
4
4
  import { zaloSetupWizard } from "./setup-api.js";
5
5
  export { createZaloSetupWizardProxy, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloPlugin, zaloSetupAdapter, zaloSetupWizard };
@@ -13,19 +13,20 @@ import { createStaticReplyToModeResolver } from "openclaw/plugin-sdk/conversatio
13
13
  import { createChannelDirectoryAdapter, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
14
14
  import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
15
15
  import { sendPayloadWithChunkedTextAndMedia } from "openclaw/plugin-sdk/reply-payload";
16
- import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
16
+ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, standardDmPolicyOpenIssue } from "openclaw/plugin-sdk/status-helpers";
17
17
  import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
18
18
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
19
19
  import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
20
20
  import { jsonResult, readStringParam } from "openclaw/plugin-sdk/channel-actions";
21
21
  import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
22
22
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
23
- import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildCatchallMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
23
+ import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
24
24
  import { z } from "zod";
25
25
  import { coerceStatusIssueAccountId, readStatusIssueFields } from "openclaw/plugin-sdk/extension-shared";
26
26
  //#region extensions/zalo/src/actions.ts
27
27
  const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-OmfJFYZn.js"), "zaloActionsRuntime");
28
28
  const providerId = "zalo";
29
+ const ZALO_ACTIONS = /* @__PURE__ */ new Set(["send"]);
29
30
  function listEnabledAccounts(cfg, accountId) {
30
31
  return (accountId ? [resolveZaloAccount({
31
32
  cfg,
@@ -35,12 +36,12 @@ function listEnabledAccounts(cfg, accountId) {
35
36
  const zaloMessageActions = {
36
37
  describeMessageTool: ({ cfg, accountId }) => {
37
38
  if (listEnabledAccounts(cfg, accountId).length === 0) return null;
38
- const actions = /* @__PURE__ */ new Set(["send"]);
39
39
  return {
40
- actions: Array.from(actions),
40
+ actions: Array.from(ZALO_ACTIONS),
41
41
  capabilities: []
42
42
  };
43
43
  },
44
+ supportsAction: ({ action }) => ZALO_ACTIONS.has(action),
44
45
  extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
45
46
  handleAction: async ({ action, params, cfg, accountId }) => {
46
47
  if (action === "send") {
@@ -85,7 +86,7 @@ const zaloApprovalAuth = createChannelApprovalAuth({
85
86
  },
86
87
  normalizeApprover: normalizeZaloApproverId
87
88
  }).approvalAuth;
88
- const ZaloConfigSchema = buildCatchallMultiAccountChannelSchema(z.object({
89
+ const ZaloConfigSchema = buildMultiAccountChannelSchema(z.object({
89
90
  name: z.string().optional(),
90
91
  enabled: z.boolean().optional(),
91
92
  markdown: MarkdownConfigSchema,
@@ -101,7 +102,7 @@ const ZaloConfigSchema = buildCatchallMultiAccountChannelSchema(z.object({
101
102
  mediaMaxMb: z.number().optional(),
102
103
  proxy: z.string().optional(),
103
104
  responsePrefix: z.string().optional()
104
- }));
105
+ }), { accountsMode: "catchall" });
105
106
  //#endregion
106
107
  //#region extensions/zalo/src/session-route.ts
107
108
  function resolveZaloOutboundSessionRoute(params) {
@@ -144,13 +145,12 @@ function collectZaloStatusIssues(accounts) {
144
145
  const enabled = account.enabled !== false;
145
146
  const configured = account.configured === true;
146
147
  if (!enabled || !configured) continue;
147
- if (account.dmPolicy === "open") issues.push({
148
+ if (account.dmPolicy === "open") issues.push(standardDmPolicyOpenIssue({
148
149
  channel: "zalo",
149
150
  accountId,
150
- kind: "config",
151
- message: "Zalo dmPolicy is \"open\", allowing any user to message the bot without pairing.",
152
- fix: "Set channels.zalo.dmPolicy to \"pairing\" or \"allowlist\" to restrict access."
153
- });
151
+ channelLabel: "Zalo",
152
+ configPath: "channels.zalo"
153
+ }));
154
154
  }
155
155
  return issues;
156
156
  }
@@ -176,7 +176,7 @@ function looksLikeZaloChatId(raw, normalized) {
176
176
  const target = normalizeZaloMessagingTarget(normalized ?? raw);
177
177
  return Boolean(target);
178
178
  }
179
- const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-DiilvQ99.js"));
179
+ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-BsZBukDW.js"));
180
180
  const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-8eRimod9.js")).zaloSetupWizard);
181
181
  const zaloTextChunkLimit = 2e3;
182
182
  async function sendZaloDelivery(ctx) {
@@ -1,2 +1,2 @@
1
- import { t as zaloPlugin } from "./channel-C_73K8pR.js";
1
+ import { t as zaloPlugin } from "./channel-CBPOM3Pc.js";
2
2
  export { zaloPlugin };
@@ -86,7 +86,7 @@ async function startZaloGatewayAccount(ctx) {
86
86
  setStatus: ctx.setStatus
87
87
  });
88
88
  ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
89
- const { monitorZaloProvider } = await import("./monitor-alx9kzIY.js");
89
+ const { monitorZaloProvider } = await import("./monitor-DTi243P3.js");
90
90
  return monitorZaloProvider({
91
91
  token,
92
92
  account,
@@ -14,6 +14,7 @@ import { formatInboundMediaUnavailableText } from "openclaw/plugin-sdk/channel-i
14
14
  import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
15
15
  import { sleepWithAbort, waitForAbortSignal } from "openclaw/plugin-sdk/runtime-env";
16
16
  import { createHostedOutboundMediaStore } from "openclaw/plugin-sdk/outbound-media";
17
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
17
18
  //#region extensions/zalo/src/monitor-durable.ts
18
19
  function prepareZaloDurableReplyPayload(params) {
19
20
  if (!params.payload.text) return params.payload;
@@ -129,7 +130,7 @@ async function tryHandleHostedZaloMediaRequest(req, res) {
129
130
  res.end("Expired");
130
131
  return true;
131
132
  }
132
- if (url.searchParams.get("token") !== entry.metadata.token) {
133
+ if (!safeEqualSecret(url.searchParams.get("token"), entry.metadata.token)) {
133
134
  res.statusCode = 401;
134
135
  res.end("Unauthorized");
135
136
  return true;
@@ -164,7 +165,7 @@ function resolveZaloTimestampMs(date) {
164
165
  if (!date) return;
165
166
  return date >= UNIX_MILLISECONDS_THRESHOLD ? date : date * 1e3;
166
167
  }
167
- const loadZaloWebhookModule = createLazyRuntimeModule(() => import("./monitor.webhook-AYcIUBBP.js"));
168
+ const loadZaloWebhookModule = createLazyRuntimeModule(() => import("./monitor.webhook-BFkM-Syx.js"));
168
169
  function releaseSharedHostedMediaRouteRef(routePath) {
169
170
  const current = hostedMediaRouteRefs.get(routePath);
170
171
  if (!current) return;
@@ -1,6 +1,7 @@
1
1
  import { B as registerWebhookTargetWithPluginRoute, I as readJsonWebhookBodyOrReject, K as resolveWebhookTargetWithAuthOrRejectSync, Q as withResolvedWebhookRequestPipeline, V as resolveClientIp, a as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, b as createFixedWindowRateLimiter, l as applyBasicWebhookRequestGuards, o as WEBHOOK_RATE_LIMIT_DEFAULTS, x as createWebhookAnomalyTracker, z as registerWebhookTarget } from "./runtime-api-CAA4ZtqF.js";
2
- import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
3
2
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
3
+ import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
4
+ import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
4
5
  //#region extensions/zalo/src/monitor.webhook.ts
5
6
  const ZALO_WEBHOOK_REPLAY_WINDOW_MS = 5 * 6e4;
6
7
  const webhookTargets = /* @__PURE__ */ new Map();
@@ -9,10 +10,6 @@ const webhookRateLimiter = createFixedWindowRateLimiter({
9
10
  maxRequests: WEBHOOK_RATE_LIMIT_DEFAULTS.maxRequests,
10
11
  maxTrackedKeys: WEBHOOK_RATE_LIMIT_DEFAULTS.maxTrackedKeys
11
12
  });
12
- const recentWebhookEvents = createClaimableDedupe({
13
- ttlMs: ZALO_WEBHOOK_REPLAY_WINDOW_MS,
14
- memoryMaxSize: 5e3
15
- });
16
13
  const webhookAnomalyTracker = createWebhookAnomalyTracker({
17
14
  maxTrackedKeys: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.maxTrackedKeys,
18
15
  ttlMs: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.ttlMs,
@@ -43,6 +40,13 @@ function buildReplayEventCacheKey(target, update) {
43
40
  messageId
44
41
  ]);
45
42
  }
43
+ const recentWebhookEvents = createChannelReplayGuard({
44
+ dedupe: {
45
+ ttlMs: ZALO_WEBHOOK_REPLAY_WINDOW_MS,
46
+ memoryMaxSize: 5e3
47
+ },
48
+ buildReplayKey: ({ target, update }) => buildReplayEventCacheKey(target, update)
49
+ });
46
50
  var ZaloRetryableWebhookError = class extends Error {
47
51
  constructor(message, options) {
48
52
  super(message, options);
@@ -50,23 +54,17 @@ var ZaloRetryableWebhookError = class extends Error {
50
54
  }
51
55
  };
52
56
  async function processZaloReplayGuardedUpdate(params) {
53
- const replayEventKey = buildReplayEventCacheKey(params.target, params.update);
54
- if (replayEventKey) {
55
- if ((await recentWebhookEvents.claim(replayEventKey, { now: params.nowMs })).kind !== "claimed") return "duplicate";
56
- }
57
- params.target.statusSink?.({ lastInboundAt: Date.now() });
58
- try {
59
- await params.processUpdate({
60
- update: params.update,
61
- target: params.target
62
- });
63
- if (replayEventKey) await recentWebhookEvents.commit(replayEventKey);
64
- return "processed";
65
- } catch (error) {
66
- if (replayEventKey) if (error instanceof ZaloRetryableWebhookError) recentWebhookEvents.release(replayEventKey, { error });
67
- else await recentWebhookEvents.commit(replayEventKey);
68
- throw error;
69
- }
57
+ const event = {
58
+ target: params.target,
59
+ update: params.update
60
+ };
61
+ return (await recentWebhookEvents.processGuarded(event, async () => {
62
+ params.target.statusSink?.({ lastInboundAt: Date.now() });
63
+ await params.processUpdate(event);
64
+ }, {
65
+ dedupe: { now: params.nowMs },
66
+ onError: (error) => error instanceof ZaloRetryableWebhookError ? "release" : "commit"
67
+ })).kind === "processed" ? "processed" : "duplicate";
70
68
  }
71
69
  function recordWebhookStatus(runtime, path, statusCode) {
72
70
  webhookAnomalyTracker.record({
@@ -154,12 +152,12 @@ async function handleZaloWebhookRequest(req, res, processUpdate) {
154
152
  recordWebhookStatus(target.runtime, path, res.statusCode);
155
153
  return true;
156
154
  }
157
- processZaloReplayGuardedUpdate({
155
+ runDetachedWebhookWork(() => processZaloReplayGuardedUpdate({
158
156
  target,
159
157
  update,
160
158
  processUpdate,
161
159
  nowMs
162
- }).catch((err) => {
160
+ })).catch((err) => {
163
161
  target.runtime.error?.(`[${target.account.accountId}] Zalo webhook failed: ${String(err)}`);
164
162
  });
165
163
  res.statusCode = 200;
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@openclaw/zalo",
3
- "version": "2026.7.2-beta.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/zalo",
9
- "version": "2026.7.2-beta.1",
9
+ "version": "2026.7.2-beta.2",
10
10
  "dependencies": {
11
11
  "zod": "4.4.3"
12
12
  },
13
13
  "peerDependencies": {
14
- "openclaw": ">=2026.7.2-beta.1"
14
+ "openclaw": ">=2026.7.2-beta.2"
15
15
  },
16
16
  "peerDependenciesMeta": {
17
17
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/zalo",
3
- "version": "2026.7.2-beta.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "description": "OpenClaw Zalo channel plugin for bot and webhook chats.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "type": "module",
10
10
  "peerDependencies": {
11
- "openclaw": ">=2026.7.2-beta.1"
11
+ "openclaw": ">=2026.7.2-beta.2"
12
12
  },
13
13
  "peerDependenciesMeta": {
14
14
  "openclaw": {
@@ -39,10 +39,10 @@
39
39
  "minHostVersion": ">=2026.4.10"
40
40
  },
41
41
  "compat": {
42
- "pluginApi": ">=2026.7.2-beta.1"
42
+ "pluginApi": ">=2026.7.2-beta.2"
43
43
  },
44
44
  "build": {
45
- "openclawVersion": "2026.7.2-beta.1"
45
+ "openclawVersion": "2026.7.2-beta.2"
46
46
  },
47
47
  "release": {
48
48
  "publishToClawHub": true,