@openclaw/zalo 2026.8.1-beta.2 → 2026.8.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-BGGtv8x_.js";
1
+ import { t as sendMessageZalo } from "./send-Cb7ITYME.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 { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-T6qc2eAG.js";
2
- import { t as zaloPlugin } from "./channel-Dcp-XLf8.js";
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-CcCJ1E26.js";
2
+ import { t as zaloPlugin } from "./channel-A8nLvhBc.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 };
@@ -1,12 +1,12 @@
1
- import { c as inspectZaloAccount, d as resolveZaloAccount, i as zaloSetupContract, l as listZaloAccountIds, p as buildSecretInputSchema, t as createZaloSetupWizardProxy, u as resolveDefaultZaloAccountId } from "./setup-core-T6qc2eAG.js";
2
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Bxvjh0Hq.js";
1
+ import { c as inspectZaloAccount, d as resolveZaloAccount, i as zaloSetupContract, l as listZaloAccountIds, p as buildSecretInputSchema, t as createZaloSetupWizardProxy, u as resolveDefaultZaloAccountId } from "./setup-core-CcCJ1E26.js";
2
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Cpsqe5Cv.js";
3
3
  import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
4
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
5
5
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
6
6
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver, mapAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
7
7
  import { buildChannelConfigSchema, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
8
8
  import { defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
9
- import { buildOpenGroupPolicyRestrictSendersWarning, buildOpenGroupPolicyWarning, createOpenProviderGroupPolicyWarningCollector } from "openclaw/plugin-sdk/channel-policy";
9
+ import { buildOpenGroupPolicyRestrictSendersWarning, buildOpenGroupPolicyWarning, createConditionalWarningCollector, createOpenProviderGroupPolicyWarningCollector } from "openclaw/plugin-sdk/channel-policy";
10
10
  import { createAttachedChannelResultAdapter, createEmptyChannelResult } from "openclaw/plugin-sdk/channel-send-result";
11
11
  import { buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/channel-status";
12
12
  import { createStaticReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime";
@@ -16,15 +16,15 @@ import { sendPayloadWithChunkedTextAndMedia } from "openclaw/plugin-sdk/reply-pa
16
16
  import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, standardDmPolicyOpenIssue } from "openclaw/plugin-sdk/status-helpers";
17
17
  import { chunkTextForOutbound, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
18
18
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
19
- import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
20
19
  import { jsonResult, readStringParam } from "openclaw/plugin-sdk/channel-actions";
21
20
  import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
22
21
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
23
22
  import { AllowFromListSchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
24
23
  import { z } from "zod";
24
+ import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
25
25
  import { coerceStatusIssueAccountId, readStatusIssueFields } from "openclaw/plugin-sdk/extension-shared";
26
26
  //#region extensions/zalo/src/actions.ts
27
- const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-BRP15DNe.js"), "zaloActionsRuntime");
27
+ const loadZaloActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-3ZRHK2SE.js"), "zaloActionsRuntime");
28
28
  const providerId = "zalo";
29
29
  const ZALO_ACTIONS = /* @__PURE__ */ new Set(["send"]);
30
30
  function listEnabledAccounts(cfg, accountId) {
@@ -89,7 +89,9 @@ const zaloApprovalAuth = createChannelApprovalAuth({
89
89
  },
90
90
  normalizeApprover: normalizeZaloApproverId
91
91
  }).approvalAuth;
92
- const ZaloConfigSchema = buildMultiAccountChannelSchema(z.object({
92
+ //#endregion
93
+ //#region extensions/zalo/src/config-schema.ts
94
+ const zaloAccountSchema = z.object({
93
95
  name: z.string().optional(),
94
96
  enabled: z.boolean().optional(),
95
97
  configWrites: z.boolean().optional(),
@@ -106,7 +108,11 @@ const ZaloConfigSchema = buildMultiAccountChannelSchema(z.object({
106
108
  mediaMaxMb: z.number().optional(),
107
109
  proxy: z.string().optional(),
108
110
  responsePrefix: z.string().optional()
109
- }), { accountsMode: "catchall" });
111
+ });
112
+ const ZaloConfigSchema = buildMultiAccountChannelSchema(zaloAccountSchema.extend({ historyLimit: z.number().int().min(0).optional() }), {
113
+ accountSchema: zaloAccountSchema,
114
+ accountsMode: "catchall"
115
+ });
110
116
  //#endregion
111
117
  //#region extensions/zalo/src/session-route.ts
112
118
  function resolveZaloOutboundSessionRoute(params) {
@@ -180,8 +186,8 @@ function looksLikeZaloChatId(raw, normalized) {
180
186
  const target = normalizeZaloMessagingTarget(normalized ?? raw);
181
187
  return Boolean(target);
182
188
  }
183
- const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-5CivhE5e.js"));
184
- const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-edfRk3eu.js").then((n) => n.t)).zaloSetupWizard);
189
+ const loadZaloChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-gH4VQVIN.js"));
190
+ const zaloSetupWizard = createZaloSetupWizardProxy(async () => (await import("./setup-surface-CIpp_MDf.js").then((n) => n.t)).zaloSetupWizard);
185
191
  const zaloTextChunkLimit = 2e3;
186
192
  async function sendZaloDelivery(ctx) {
187
193
  const result = await (await loadZaloChannelRuntime()).sendZaloText({
@@ -260,6 +266,12 @@ const collectZaloSecurityWarnings = createOpenProviderGroupPolicyWarningCollecto
260
266
  })];
261
267
  }
262
268
  });
269
+ const collectZaloOpenGroupFindings = createConditionalWarningCollector.findings({
270
+ collectWarnings: collectZaloSecurityWarnings,
271
+ checkId: "channels.zalo.groups.open",
272
+ severity: "critical",
273
+ title: "Zalo security warning"
274
+ });
263
275
  const zaloPlugin = createChatChannelPlugin({
264
276
  base: {
265
277
  id: "zalo",
@@ -349,7 +361,7 @@ const zaloPlugin = createChatChannelPlugin({
349
361
  },
350
362
  security: {
351
363
  resolveDmPolicy: resolveZaloDmPolicy,
352
- collectWarnings: collectZaloSecurityWarnings
364
+ collectWarnings: collectZaloOpenGroupFindings
353
365
  },
354
366
  pairing: { text: {
355
367
  idLabel: "zaloUserId",
@@ -1,2 +1,2 @@
1
- import { t as zaloPlugin } from "./channel-Dcp-XLf8.js";
1
+ import { t as zaloPlugin } from "./channel-A8nLvhBc.js";
2
2
  export { zaloPlugin };
@@ -1,6 +1,6 @@
1
- import { m as normalizeSecretInputString } from "./setup-core-T6qc2eAG.js";
2
- import { i as PAIRING_APPROVED_MESSAGE } from "./runtime-api-qxdOyk3M.js";
3
- import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-BGGtv8x_.js";
1
+ import { m as normalizeSecretInputString } from "./setup-core-CcCJ1E26.js";
2
+ import { n as PAIRING_APPROVED_MESSAGE } from "./runtime-api-CN6fYNQK.js";
3
+ import { a as getMe, n as resolveZaloProxyFetch, r as ZaloApiError, t as sendMessageZalo } from "./send-Cb7ITYME.js";
4
4
  import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-outbound";
5
5
  import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
6
6
  //#region extensions/zalo/src/probe.ts
@@ -35,7 +35,7 @@ async function probeZalo(token, timeoutMs = 5e3, fetcher) {
35
35
  //#endregion
36
36
  //#region extensions/zalo/src/channel.runtime.ts
37
37
  async function notifyZaloPairingApproval(params) {
38
- const { resolveZaloAccount } = await import("./setup-core-T6qc2eAG.js").then((n) => n.s);
38
+ const { resolveZaloAccount } = await import("./setup-core-CcCJ1E26.js").then((n) => n.s);
39
39
  const account = resolveZaloAccount({ cfg: params.cfg });
40
40
  if (!account.token) throw new Error("Zalo token not configured");
41
41
  await sendMessageZalo(params.id, PAIRING_APPROVED_MESSAGE, { token: account.token });
@@ -69,7 +69,7 @@ async function startZaloGatewayAccount(ctx) {
69
69
  setStatus: ctx.setStatus
70
70
  });
71
71
  ctx.log?.info(`[${account.accountId}] starting provider${zaloBotLabel} mode=${mode}`);
72
- const { monitorZaloProvider } = await import("./monitor-Ckz5Mo22.js");
72
+ const { monitorZaloProvider } = await import("./monitor-ado96nMT.js");
73
73
  return monitorZaloProvider({
74
74
  token,
75
75
  account,
@@ -1,6 +1,6 @@
1
1
  import { n as resolveZaloRuntimeGroupPolicy, t as normalizeZaloAllowEntry } from "./group-access-8qHRzDHx.js";
2
- import { t as getZaloRuntime } from "./runtime-api-qxdOyk3M.js";
3
- import { c as sendChatAction, d as setWebhook, f as ZALO_OUTBOUND_MEDIA_TTL_MS, i as deleteWebhook, l as sendMessage, n as resolveZaloProxyFetch, o as getUpdates, r as ZaloApiError, s as getWebhookInfo, u as sendPhoto } from "./send-BGGtv8x_.js";
2
+ import { X as getZaloRuntime } from "./runtime-api-CN6fYNQK.js";
3
+ import { c as sendChatAction, d as setWebhook, f as ZALO_OUTBOUND_MEDIA_TTL_MS, i as deleteWebhook, l as sendMessage, n as resolveZaloProxyFetch, o as getUpdates, r as ZaloApiError, s as getWebhookInfo, u as sendPhoto } from "./send-Cb7ITYME.js";
4
4
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
5
5
  import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
6
6
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
@@ -174,8 +174,8 @@ function resolveZaloTimestampMs(date) {
174
174
  if (!date) return;
175
175
  return date >= UNIX_MILLISECONDS_THRESHOLD ? date : date * 1e3;
176
176
  }
177
- const loadZaloWebhookRuntime = createLazyRuntimeNamedExport(() => import("./monitor.webhook-S7nThy3R.js"), "zaloWebhookRuntime");
178
- const loadZaloWebhookIngressRuntime = createLazyRuntimeNamedExport(() => import("./webhook-spool-Bf17xwqi.js"), "zaloWebhookIngressRuntime");
177
+ const loadZaloWebhookRuntime = createLazyRuntimeNamedExport(() => import("./monitor.webhook-CcTwt1dC.js"), "zaloWebhookRuntime");
178
+ const loadZaloWebhookIngressRuntime = createLazyRuntimeNamedExport(() => import("./webhook-spool-DPgnFGD0.js"), "zaloWebhookIngressRuntime");
179
179
  const loadZaloWebhookModule = createLazyRuntimeModule(async () => ({
180
180
  ...await loadZaloWebhookRuntime(),
181
181
  ...await loadZaloWebhookIngressRuntime()
@@ -1,5 +1,5 @@
1
- import { B as registerWebhookTargetWithPluginRoute, G as resolveWebhookTargetWithAuthOrRejectSync, V as resolveClientIp, Z as withResolvedWebhookRequestPipeline, 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-qxdOyk3M.js";
2
- import { ZaloWebhookPayloadError } from "./webhook-spool-Bf17xwqi.js";
1
+ import { L as registerWebhookTarget, R as registerWebhookTargetWithPluginRoute, U as resolveWebhookTargetWithAuthOrRejectSync, Y 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-CN6fYNQK.js";
2
+ import { ZaloWebhookPayloadError } from "./webhook-spool-DPgnFGD0.js";
3
3
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
4
4
  import { readWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
5
5
  //#region extensions/zalo/src/monitor.webhook.ts
@@ -4,8 +4,8 @@ import { PAIRING_APPROVED_MESSAGE, buildTokenChannelStatusSummary as buildTokenC
4
4
  import { deliverTextOrMediaReply as deliverTextOrMediaReply$1, isNumericTargetId, sendPayloadWithChunkedTextAndMedia as sendPayloadWithChunkedTextAndMedia$1 } from "openclaw/plugin-sdk/reply-payload";
5
5
  import { buildBaseAccountStatusSnapshot } from "openclaw/plugin-sdk/status-helpers";
6
6
  import { chunkTextForOutbound as chunkTextForOutbound$1 } from "openclaw/plugin-sdk/text-chunking";
7
- 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
7
  import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$1, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
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";
9
9
  import { addWildcardAllowFrom, 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
10
  import { resolveDefaultGroupPolicy as resolveDefaultGroupPolicy$1, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce as warnMissingProviderGroupPolicyFallbackOnce$1 } from "openclaw/plugin-sdk/runtime-group-policy";
11
11
  import { createChannelPairingController as createChannelPairingController$1 } from "openclaw/plugin-sdk/channel-pairing";
@@ -19,4 +19,4 @@ const { setRuntime: setZaloRuntime, getRuntime: getZaloRuntime } = createPluginR
19
19
  errorMessage: "Zalo runtime not initialized"
20
20
  });
21
21
  //#endregion
22
- export { mergeAllowFromEntries$1 as A, registerWebhookTargetWithPluginRoute as B, formatAllowFromLowercase$1 as C, isNumericTargetId as D, isNormalizedSenderAllowed as E, promptSingleChannelSecretInput$1 as F, resolveWebhookTargetWithAuthOrRejectSync as G, resolveDefaultGroupPolicy$1 as H, readJsonWebhookBodyOrReject as I, setTopLevelChannelDmPolicyWithAllowFrom as J, runSingleChannelSecretStep$1 as K, readStringParam as L, normalizeAccountId$1 as M, normalizeResolvedSecretInputString as N, jsonResult as O, normalizeSecretInputString as P, registerPluginHttpRoute$1 as R, deliverTextOrMediaReply$1 as S, hasConfiguredSecretInput$1 as T, resolveOpenProviderRuntimeGroupPolicy$1 as U, resolveClientIp as V, resolveWebhookPath$1 as W, warnMissingProviderGroupPolicyFallbackOnce$1 as X, waitForAbortSignal as Y, withResolvedWebhookRequestPipeline as Z, createChannelMessageReplyPipeline as _, WEBHOOK_ANOMALY_COUNTER_DEFAULTS as a, createFixedWindowRateLimiter as b, applyAccountNameToChannelSection as c, buildBaseAccountStatusSnapshot as d, buildChannelConfigSchema as f, chunkTextForOutbound$1 as g, buildTokenChannelStatusSummary$1 as h, PAIRING_APPROVED_MESSAGE as i, migrateBaseNameToDefaultAccount as j, logTypingFailure$1 as k, applyBasicWebhookRequestGuards as l, buildSingleChannelSecretPromptState$1 as m, setZaloRuntime as n, WEBHOOK_RATE_LIMIT_DEFAULTS as o, buildSecretInputSchema as p, sendPayloadWithChunkedTextAndMedia$1 as q, DEFAULT_ACCOUNT_ID$1 as r, addWildcardAllowFrom as s, getZaloRuntime as t, applySetupAccountConfigPatch as u, createChannelPairingController$1 as v, formatPairingApproveHint as w, createWebhookAnomalyTracker as x, createDedupeCache as y, registerWebhookTarget as z };
22
+ export { normalizeAccountId$1 as A, resolveDefaultGroupPolicy$1 as B, hasConfiguredSecretInput$1 as C, logTypingFailure$1 as D, jsonResult as E, readStringParam as F, sendPayloadWithChunkedTextAndMedia$1 as G, resolveWebhookPath$1 as H, registerPluginHttpRoute$1 as I, warnMissingProviderGroupPolicyFallbackOnce$1 as J, setTopLevelChannelDmPolicyWithAllowFrom 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 as T, resolveWebhookTargetWithAuthOrRejectSync as U, resolveOpenProviderRuntimeGroupPolicy$1 as V, runSingleChannelSecretStep$1 as W, getZaloRuntime as X, withResolvedWebhookRequestPipeline as Y, setZaloRuntime as Z, createDedupeCache as _, addWildcardAllowFrom 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, waitForAbortSignal 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,2 +1,2 @@
1
- import { A as mergeAllowFromEntries, B as registerWebhookTargetWithPluginRoute, C as formatAllowFromLowercase, D as isNumericTargetId, E as isNormalizedSenderAllowed, F as promptSingleChannelSecretInput, G as resolveWebhookTargetWithAuthOrRejectSync, H as resolveDefaultGroupPolicy, I as readJsonWebhookBodyOrReject, J as setTopLevelChannelDmPolicyWithAllowFrom, K as runSingleChannelSecretStep, L as readStringParam, M as normalizeAccountId, N as normalizeResolvedSecretInputString, O as jsonResult, P as normalizeSecretInputString, R as registerPluginHttpRoute, S as deliverTextOrMediaReply, T as hasConfiguredSecretInput, U as resolveOpenProviderRuntimeGroupPolicy, V as resolveClientIp, W as resolveWebhookPath, X as warnMissingProviderGroupPolicyFallbackOnce, Y as waitForAbortSignal, Z as withResolvedWebhookRequestPipeline, _ as createChannelMessageReplyPipeline, a as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, b as createFixedWindowRateLimiter, c as applyAccountNameToChannelSection, d as buildBaseAccountStatusSnapshot, f as buildChannelConfigSchema, g as chunkTextForOutbound, h as buildTokenChannelStatusSummary, i as PAIRING_APPROVED_MESSAGE, j as migrateBaseNameToDefaultAccount, k as logTypingFailure, l as applyBasicWebhookRequestGuards, m as buildSingleChannelSecretPromptState, n as setZaloRuntime, o as WEBHOOK_RATE_LIMIT_DEFAULTS, p as buildSecretInputSchema, q as sendPayloadWithChunkedTextAndMedia, r as DEFAULT_ACCOUNT_ID, s as addWildcardAllowFrom, u as applySetupAccountConfigPatch, v as createChannelPairingController, w as formatPairingApproveHint, x as createWebhookAnomalyTracker, y as createDedupeCache, z as registerWebhookTarget } from "./runtime-api-qxdOyk3M.js";
1
+ import { A as normalizeAccountId, B as resolveDefaultGroupPolicy, C as hasConfiguredSecretInput, D as logTypingFailure, E as jsonResult, F as readStringParam, G as sendPayloadWithChunkedTextAndMedia, H as resolveWebhookPath, I as registerPluginHttpRoute, J as warnMissingProviderGroupPolicyFallbackOnce, K as setTopLevelChannelDmPolicyWithAllowFrom, 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 resolveWebhookTargetWithAuthOrRejectSync, V as resolveOpenProviderRuntimeGroupPolicy, W as runSingleChannelSecretStep, Y as withResolvedWebhookRequestPipeline, Z as setZaloRuntime, _ 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 waitForAbortSignal, 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-CN6fYNQK.js";
2
2
  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, resolveOpenProviderRuntimeGroupPolicy, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, runSingleChannelSecretStep, sendPayloadWithChunkedTextAndMedia, setTopLevelChannelDmPolicyWithAllowFrom, setZaloRuntime, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
@@ -1,4 +1,4 @@
1
- import { collectConditionalChannelFieldAssignments, createChannelSecretTargetRegistryEntries, getChannelSurface, hasOwnProperty } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
1
+ import { collectConditionalChannelFieldAssignments, createChannelSecretTargetRegistryEntries, getChannelSurface, hasOwnProperty, normalizeSecretStringValue } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
2
2
  //#region extensions/zalo/src/secret-contract.ts
3
3
  const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
4
4
  channelKey: "zalo",
@@ -17,7 +17,7 @@ function collectRuntimeConfigAssignments(params) {
17
17
  defaults: params.defaults,
18
18
  context: params.context,
19
19
  topLevelActiveWithoutAccounts: true,
20
- topLevelInheritedAccountActive: ({ account, enabled }) => enabled && !hasOwnProperty(account, "botToken"),
20
+ topLevelInheritedAccountActive: ({ account, enabled }) => enabled && !hasOwnProperty(account, "botToken") && !normalizeSecretStringValue(account.tokenFile),
21
21
  accountActive: ({ enabled }) => enabled,
22
22
  topInactiveReason: "no enabled Zalo surface inherits this top-level botToken.",
23
23
  accountInactiveReason: "Zalo account is disabled."
@@ -1,2 +1,2 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-Bxvjh0Hq.js";
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-Cpsqe5Cv.js";
2
2
  export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -1,10 +1,10 @@
1
- import { d as resolveZaloAccount, f as resolveZaloToken } from "./setup-core-T6qc2eAG.js";
1
+ import { d as resolveZaloAccount, f as resolveZaloToken } from "./setup-core-CcCJ1E26.js";
2
2
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
3
3
  import { stripChannelTargetPrefix, stripTargetKindPrefix } from "openclaw/plugin-sdk/core";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
6
6
  import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
7
- import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
7
+ import { assertOkOrThrowProviderError, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
8
8
  import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
9
9
  import { makeProxyFetch } from "openclaw/plugin-sdk/fetch-runtime";
10
10
  //#region extensions/zalo/src/timeouts.ts
@@ -55,12 +55,14 @@ async function callZaloApi(method, token, body, options) {
55
55
  const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
56
56
  const fetcher = options?.fetch ?? fetch;
57
57
  try {
58
- const data = await readProviderJsonResponse(await fetcher(url, {
58
+ const response = await fetcher(url, {
59
59
  method: "POST",
60
60
  headers: { "Content-Type": "application/json" },
61
61
  body: body ? JSON.stringify(body) : void 0,
62
62
  signal: controller.signal
63
- }), `zalo.${method}`);
63
+ });
64
+ await assertOkOrThrowProviderError(response, `zalo.${method}`);
65
+ const data = await readProviderJsonResponse(response, `zalo.${method}`);
64
66
  if (!data.ok) throw new ZaloApiError(data.description ?? `Zalo API error: ${method}`, data.error_code, data.description);
65
67
  return data;
66
68
  } finally {
@@ -262,11 +264,26 @@ async function sendMessageZalo(chatId, text, options = {}) {
262
264
  const resolved = resolveSendContextOrFailure(chatId, options);
263
265
  if ("failure" in resolved) return resolved.failure;
264
266
  const { context } = resolved;
265
- if (options.mediaUrl && (options.mediaUrl.trim() || !text)) return sendPhotoZalo(context.chatId, options.mediaUrl, {
266
- ...options,
267
- token: context.token,
268
- caption: text || options.caption
269
- });
267
+ if (options.mediaUrl && (options.mediaUrl.trim() || !text)) {
268
+ const photoUrl = options.mediaUrl.trim();
269
+ if (!photoUrl) return {
270
+ ok: false,
271
+ error: "No photo URL provided",
272
+ receipt: createZaloSendReceipt({
273
+ chatId: context.chatId,
274
+ kind: "media"
275
+ })
276
+ };
277
+ const caption = text || options.caption;
278
+ return await runZaloSend("Failed to send photo", {
279
+ chatId: context.chatId,
280
+ kind: "media"
281
+ }, () => sendPhoto(context.token, {
282
+ chat_id: context.chatId,
283
+ photo: photoUrl,
284
+ caption: caption !== void 0 ? truncateUtf16Safe(caption, 2e3) : void 0
285
+ }, context.fetcher));
286
+ }
270
287
  return await runZaloSend("Failed to send message", {
271
288
  chatId: context.chatId,
272
289
  kind: "text"
@@ -275,26 +292,5 @@ async function sendMessageZalo(chatId, text, options = {}) {
275
292
  text: truncateUtf16Safe(text, 2e3)
276
293
  }, context.fetcher));
277
294
  }
278
- async function sendPhotoZalo(chatId, photoUrl, options = {}) {
279
- const resolved = resolveSendContextOrFailure(chatId, options);
280
- if ("failure" in resolved) return resolved.failure;
281
- const { context } = resolved;
282
- if (!photoUrl?.trim()) return {
283
- ok: false,
284
- error: "No photo URL provided",
285
- receipt: createZaloSendReceipt({
286
- chatId: context.chatId,
287
- kind: "media"
288
- })
289
- };
290
- return await runZaloSend("Failed to send photo", {
291
- chatId: context.chatId,
292
- kind: "media"
293
- }, () => (async () => sendPhoto(context.token, {
294
- chat_id: context.chatId,
295
- photo: photoUrl.trim(),
296
- caption: options.caption !== void 0 ? truncateUtf16Safe(options.caption, 2e3) : void 0
297
- }, context.fetcher))());
298
- }
299
295
  //#endregion
300
296
  export { getMe as a, sendChatAction as c, setWebhook as d, ZALO_OUTBOUND_MEDIA_TTL_MS as f, deleteWebhook as i, sendMessage as l, resolveZaloProxyFetch as n, getUpdates as o, ZaloApiError as r, getWebhookInfo as s, sendMessageZalo as t, sendPhoto as u };
package/dist/setup-api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-T6qc2eAG.js";
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-CcCJ1E26.js";
2
2
  import { n as resolveZaloRuntimeGroupPolicy } from "./group-access-8qHRzDHx.js";
3
3
  import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
4
4
  //#region extensions/zalo/setup-api.ts
@@ -1,8 +1,8 @@
1
1
  import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
2
2
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
3
3
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
- import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
5
4
  import { resolveAccountEntry } from "openclaw/plugin-sdk/routing";
5
+ import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
6
6
  import { buildSecretInputSchema, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
7
7
  import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
8
8
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
@@ -20,8 +20,18 @@ var __exportAll = (all, no_symbols) => {
20
20
  };
21
21
  //#endregion
22
22
  //#region extensions/zalo/src/token.ts
23
- function readTokenFromFile(tokenFile) {
24
- return tryReadSecretFileSync(tokenFile, "Zalo token file", { rejectSymlink: true }) ?? "";
23
+ function readTokenFromFile(tokenFile, configPath) {
24
+ const result = tryReadSecretFileSync(tokenFile, "Zalo token file", { rejectSymlink: true }, { configPath });
25
+ return result.status === "available" ? {
26
+ token: result.value,
27
+ source: "configFile",
28
+ status: "available"
29
+ } : {
30
+ token: "",
31
+ source: "configFile",
32
+ status: "configured_unavailable",
33
+ credentialDiagnostics: [result.diagnostic]
34
+ };
25
35
  }
26
36
  function resolveZaloToken(config, accountId, options) {
27
37
  const resolvedAccountId = normalizeAccountId(accountId ?? config?.defaultAccount);
@@ -45,21 +55,8 @@ function resolveZaloToken(config, accountId, options) {
45
55
  source: "config",
46
56
  status: "configured_unavailable"
47
57
  };
48
- const fileToken = readTokenFromFile(accountConfig.tokenFile);
49
- if (fileToken) return {
50
- token: fileToken,
51
- source: "configFile",
52
- status: "available"
53
- };
54
- }
55
- if (!accountHasBotToken) {
56
- const fileToken = readTokenFromFile(accountConfig?.tokenFile);
57
- if (fileToken) return {
58
- token: fileToken,
59
- source: "configFile",
60
- status: "available"
61
- };
62
58
  }
59
+ if (accountConfig?.tokenFile?.trim()) return readTokenFromFile(accountConfig.tokenFile, `channels.zalo.accounts.${resolvedAccountId}.tokenFile`);
63
60
  if (!accountHasBotToken) {
64
61
  const token = resolveSecretInputString({
65
62
  value: baseConfig?.botToken,
@@ -76,12 +73,7 @@ function resolveZaloToken(config, accountId, options) {
76
73
  source: "config",
77
74
  status: "configured_unavailable"
78
75
  };
79
- const fileToken = readTokenFromFile(baseConfig?.tokenFile);
80
- if (fileToken) return {
81
- token: fileToken,
82
- source: "configFile",
83
- status: "available"
84
- };
76
+ if (baseConfig?.tokenFile?.trim()) return readTokenFromFile(baseConfig.tokenFile, "channels.zalo.tokenFile");
85
77
  }
86
78
  if (isDefaultAccount) {
87
79
  const envToken = process.env.ZALO_BOT_TOKEN?.trim();
@@ -126,6 +118,7 @@ function resolveZaloAccountWithMode(params) {
126
118
  token: tokenResolution.token,
127
119
  tokenSource: tokenResolution.source,
128
120
  tokenStatus: tokenResolution.status,
121
+ ...tokenResolution.credentialDiagnostics ? { credentialDiagnostics: tokenResolution.credentialDiagnostics } : {},
129
122
  config: merged
130
123
  };
131
124
  }
@@ -1,4 +1,4 @@
1
- import { a as noteZaloTokenHelp, c as inspectZaloAccount, h as __exportAll, n as zaloDmPolicy, o as promptZaloAllowFrom } from "./setup-core-T6qc2eAG.js";
1
+ import { a as noteZaloTokenHelp, c as inspectZaloAccount, h as __exportAll, n as zaloDmPolicy, o as promptZaloAllowFrom } from "./setup-core-CcCJ1E26.js";
2
2
  import { DEFAULT_ACCOUNT_ID, buildSingleChannelSecretPromptState, createSetupTranslator, createStandardChannelSetupStatus, hasConfiguredSecretInput, promptSingleChannelSecretInput, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
3
3
  //#region extensions/zalo/src/setup-surface.ts
4
4
  var setup_surface_exports = /* @__PURE__ */ __exportAll({ zaloSetupWizard: () => zaloSetupWizard });
@@ -1,3 +1,3 @@
1
- import { r as zaloSetupAdapter } from "./setup-core-T6qc2eAG.js";
2
- import { n as zaloSetupWizard } from "./setup-surface-edfRk3eu.js";
1
+ import { r as zaloSetupAdapter } from "./setup-core-CcCJ1E26.js";
2
+ import { n as zaloSetupWizard } from "./setup-surface-CIpp_MDf.js";
3
3
  export { zaloSetupAdapter, zaloSetupWizard };
@@ -0,0 +1,180 @@
1
+ import { X as getZaloRuntime } from "./runtime-api-CN6fYNQK.js";
2
+ import { r as ZaloApiError } from "./send-Cb7ITYME.js";
3
+ import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
4
+ import { normalizeNullableString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { z } from "zod";
6
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
+ import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
8
+ //#region extensions/zalo/src/webhook-spool.ts
9
+ const ZALO_WEBHOOK_SPOOL_VERSION = 1;
10
+ const ZALO_WEBHOOK_DRAIN_INTERVAL_MS = 500;
11
+ const ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES = 8;
12
+ const ZaloWebhookPayloadError = createChannelIngressError("ZaloWebhookPayloadError");
13
+ const nonEmptyWebhookStringSchema = z.string().transform((value) => normalizeNullableString(value)).pipe(z.string());
14
+ const optionalWebhookStringSchema = z.string().optional().catch(void 0);
15
+ const webhookEnvelopeSchema = z.looseObject({
16
+ ok: z.unknown().optional(),
17
+ result: z.looseObject({}).optional().catch(void 0)
18
+ }).transform((envelope) => envelope.ok === true && envelope.result ? envelope.result : envelope);
19
+ const webhookAdmissionSchema = z.looseObject({ message: z.looseObject({
20
+ message_id: nonEmptyWebhookStringSchema,
21
+ chat: z.looseObject({ id: nonEmptyWebhookStringSchema })
22
+ }) });
23
+ const webhookSenderSchema = z.object({
24
+ id: nonEmptyWebhookStringSchema,
25
+ name: optionalWebhookStringSchema,
26
+ display_name: optionalWebhookStringSchema,
27
+ avatar: optionalWebhookStringSchema,
28
+ is_bot: z.boolean().optional().catch(void 0)
29
+ });
30
+ const webhookChatSchema = z.object({
31
+ id: nonEmptyWebhookStringSchema,
32
+ chat_type: z.enum(["PRIVATE", "GROUP"])
33
+ });
34
+ const webhookMessageSchema = z.object({
35
+ message_id: nonEmptyWebhookStringSchema,
36
+ from: webhookSenderSchema,
37
+ chat: webhookChatSchema,
38
+ date: z.number().finite(),
39
+ text: optionalWebhookStringSchema,
40
+ photo_url: optionalWebhookStringSchema,
41
+ caption: optionalWebhookStringSchema,
42
+ sticker: optionalWebhookStringSchema,
43
+ message_type: optionalWebhookStringSchema
44
+ });
45
+ const webhookUpdateSchema = z.object({
46
+ event_name: z.enum([
47
+ "message.text.received",
48
+ "message.image.received",
49
+ "message.sticker.received",
50
+ "message.unsupported.received"
51
+ ]),
52
+ message: webhookMessageSchema
53
+ }).superRefine((update, context) => {
54
+ if (update.event_name === "message.text.received" && update.message.text === void 0) context.addIssue({
55
+ code: "custom",
56
+ path: ["message", "text"],
57
+ message: "text event requires message.text"
58
+ });
59
+ });
60
+ function parseRawRecord(rawEvent) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(rawEvent);
64
+ } catch (error) {
65
+ throw new ZaloWebhookPayloadError("Zalo webhook body contains invalid JSON.", { cause: error });
66
+ }
67
+ const envelope = webhookEnvelopeSchema.safeParse(parsed);
68
+ if (!envelope.success) throw new ZaloWebhookPayloadError("Zalo webhook body must be a JSON object.");
69
+ return envelope.data;
70
+ }
71
+ function inspectZaloWebhookEvent(rawEvent) {
72
+ const update = parseRawRecord(rawEvent);
73
+ const admission = webhookAdmissionSchema.safeParse(update);
74
+ if (!admission.success) {
75
+ if (admission.error.issues.some((issue) => issue.path[0] === "message" && (issue.path.length === 1 || issue.path[1] === "message_id"))) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.message_id.");
76
+ if (admission.error.issues.some((issue) => issue.path[0] === "message" && issue.path[1] === "chat")) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.chat.id.");
77
+ throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.message_id.");
78
+ }
79
+ return {
80
+ eventId: admission.data.message.message_id,
81
+ laneKey: `chat:${admission.data.message.chat.id}`,
82
+ update
83
+ };
84
+ }
85
+ function parseClaimedUpdate(payload, claimedId) {
86
+ if (payload.version !== ZALO_WEBHOOK_SPOOL_VERSION || typeof payload.rawEvent !== "string") throw new ZaloWebhookPayloadError("Zalo webhook spool payload is invalid.");
87
+ const facts = inspectZaloWebhookEvent(payload.rawEvent);
88
+ if (facts.eventId !== claimedId) throw new ZaloWebhookPayloadError("Zalo webhook message id changed after durable admission.");
89
+ const parsed = webhookUpdateSchema.safeParse(facts.update);
90
+ if (!parsed.success) {
91
+ const paths = parsed.error.issues.map((issue) => issue.path.join("."));
92
+ if (paths.some((path) => path === "event_name")) throw new ZaloWebhookPayloadError("Zalo webhook event_name is unsupported.");
93
+ if (paths.some((path) => path === "message.from" || path.startsWith("message.from.id"))) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.from.id.");
94
+ if (paths.some((path) => path === "message.chat" || path.startsWith("message.chat.id"))) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.chat.id.");
95
+ if (paths.some((path) => path.startsWith("message.chat.chat_type"))) throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid chat type.");
96
+ if (paths.some((path) => path.startsWith("message.date"))) throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid date.");
97
+ if (paths.some((path) => path.startsWith("message.text"))) throw new ZaloWebhookPayloadError("Zalo text event is missing message.text.");
98
+ throw new ZaloWebhookPayloadError("Zalo webhook event_name is unsupported.");
99
+ }
100
+ const { event_name: eventName, message } = parsed.data;
101
+ return {
102
+ event_name: eventName,
103
+ message: {
104
+ message_id: claimedId,
105
+ from: message.from,
106
+ chat: message.chat,
107
+ date: message.date,
108
+ ...message.text !== void 0 ? { text: message.text } : {},
109
+ ...message.photo_url !== void 0 ? { photo_url: message.photo_url } : {},
110
+ ...message.caption !== void 0 ? { caption: message.caption } : {},
111
+ ...message.sticker !== void 0 ? { sticker: message.sticker } : {},
112
+ ...message.message_type !== void 0 ? { message_type: message.message_type } : {}
113
+ }
114
+ };
115
+ }
116
+ function isZaloAuthenticationFailure(error) {
117
+ let current = error;
118
+ const seen = /* @__PURE__ */ new Set();
119
+ while (current && typeof current === "object" && !seen.has(current)) {
120
+ seen.add(current);
121
+ const candidate = current;
122
+ if (current instanceof ZaloApiError && (current.errorCode === 401 || current.errorCode === 403) || candidate.status === 401 || candidate.status === 403 || candidate.statusCode === 401 || candidate.statusCode === 403) return true;
123
+ current = candidate.cause;
124
+ }
125
+ return false;
126
+ }
127
+ function createZaloWebhookIngress(options) {
128
+ const monitor = createChannelIngressMonitor({
129
+ queue: options.queue ?? getZaloRuntime().state.openChannelIngressQueue({ accountId: options.accountId }),
130
+ inspect: (rawEvent) => inspectZaloWebhookEvent(rawEvent),
131
+ payload: {
132
+ storage: "raw-event",
133
+ version: ZALO_WEBHOOK_SPOOL_VERSION,
134
+ serialize: (rawEvent) => rawEvent,
135
+ deserialize: (rawEvent) => rawEvent,
136
+ createClaimError: (kind) => new ZaloWebhookPayloadError(kind === "invalid-version" ? "Zalo webhook spool payload is invalid." : "Zalo webhook identity changed after durable admission.")
137
+ },
138
+ deliver: async (_rawEvent, lifecycle, claim) => {
139
+ const update = parseClaimedUpdate(claim.payload, claim.id);
140
+ await options.deliver(update, bindIngressLifecycleToReplyOptions(lifecycle).turnAdoptionLifecycle);
141
+ },
142
+ pollIntervalMs: ZALO_WEBHOOK_DRAIN_INTERVAL_MS,
143
+ retention: { failedMaxEntries: 5e3 },
144
+ waitForDeliveryIdleBeforeRepump: false,
145
+ runPumpTask: runDetachedWebhookWork,
146
+ deferredClaims: "wait-on-stop",
147
+ drain: {
148
+ adoptionStallTimeoutMs: DEFAULT_INGRESS_ADOPTION_STALL_MS,
149
+ startLimit: ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES,
150
+ retryPolicy: {
151
+ maxAttempts: DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS,
152
+ deadLetterMinAgeMs: 0
153
+ },
154
+ resolveNonRetryableFailure: (error) => {
155
+ if (error instanceof ZaloWebhookPayloadError) return {
156
+ reason: "invalid-event",
157
+ message: error.message
158
+ };
159
+ if (isZaloAuthenticationFailure(error)) return {
160
+ reason: "authentication-failed",
161
+ message: formatErrorMessage(error)
162
+ };
163
+ return null;
164
+ },
165
+ onLog: (message) => options.runtime.error?.(`zalo ingress: ${message}`)
166
+ },
167
+ createStoppedError: () => /* @__PURE__ */ new Error("Zalo ingress stopped."),
168
+ onError: (error) => options.runtime.error?.(`zalo ingress drain failed: ${formatErrorMessage(error)}`)
169
+ });
170
+ return {
171
+ accept: async (rawEvent) => {
172
+ await monitor.admit(rawEvent);
173
+ },
174
+ start: monitor.start,
175
+ stop: monitor.stop
176
+ };
177
+ }
178
+ const zaloWebhookIngressRuntime = { createZaloWebhookIngress };
179
+ //#endregion
180
+ export { ZaloWebhookPayloadError, zaloWebhookIngressRuntime };
@@ -308,6 +308,11 @@
308
308
  "responsePrefix": {
309
309
  "type": "string"
310
310
  },
311
+ "historyLimit": {
312
+ "type": "integer",
313
+ "minimum": 0,
314
+ "maximum": 9007199254740991
315
+ },
311
316
  "accounts": {
312
317
  "type": "object",
313
318
  "properties": {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/zalo",
3
- "version": "2026.8.1-beta.2",
3
+ "version": "2026.8.1",
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.8.1-beta.2"
11
+ "openclaw": ">=2026.8.1"
12
12
  },
13
13
  "peerDependenciesMeta": {
14
14
  "openclaw": {
@@ -28,8 +28,7 @@
28
28
  "configuredState": {
29
29
  "env": {
30
30
  "anyOf": [
31
- "ZALO_BOT_TOKEN",
32
- "ZALO_WEBHOOK_SECRET"
31
+ "ZALO_BOT_TOKEN"
33
32
  ]
34
33
  }
35
34
  },
@@ -83,10 +82,10 @@
83
82
  "minHostVersion": ">=2026.4.10"
84
83
  },
85
84
  "compat": {
86
- "pluginApi": ">=2026.8.1-beta.2"
85
+ "pluginApi": ">=2026.8.1"
87
86
  },
88
87
  "build": {
89
- "openclawVersion": "2026.8.1-beta.2"
88
+ "openclawVersion": "2026.8.1"
90
89
  },
91
90
  "release": {
92
91
  "publishToClawHub": true,
@@ -1,119 +0,0 @@
1
- import { t as getZaloRuntime } from "./runtime-api-qxdOyk3M.js";
2
- import { r as ZaloApiError } from "./send-BGGtv8x_.js";
3
- import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
4
- import { normalizeNullableString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
- import { isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
6
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
- import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
8
- //#region extensions/zalo/src/webhook-spool.ts
9
- const ZALO_WEBHOOK_SPOOL_VERSION = 1;
10
- const ZALO_WEBHOOK_DRAIN_INTERVAL_MS = 500;
11
- const ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES = 8;
12
- const ZaloWebhookPayloadError = createChannelIngressError("ZaloWebhookPayloadError");
13
- function parseRawRecord(rawEvent) {
14
- let parsed;
15
- try {
16
- parsed = JSON.parse(rawEvent);
17
- } catch (error) {
18
- throw new ZaloWebhookPayloadError("Zalo webhook body contains invalid JSON.", { cause: error });
19
- }
20
- if (!isRecord(parsed)) throw new ZaloWebhookPayloadError("Zalo webhook body must be a JSON object.");
21
- return parsed;
22
- }
23
- function resolveUpdateRecord(envelope) {
24
- if (envelope.ok === true && isRecord(envelope.result)) return envelope.result;
25
- return envelope;
26
- }
27
- function inspectZaloWebhookEvent(rawEvent) {
28
- const update = resolveUpdateRecord(parseRawRecord(rawEvent));
29
- const message = isRecord(update.message) ? update.message : null;
30
- const eventId = normalizeNullableString(message?.message_id);
31
- if (!eventId) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.message_id.");
32
- const chatId = normalizeNullableString((isRecord(message?.chat) ? message.chat : null)?.id);
33
- if (!chatId) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.chat.id.");
34
- return {
35
- eventId,
36
- laneKey: `chat:${chatId}`,
37
- update
38
- };
39
- }
40
- function parseClaimedUpdate(payload, claimedId) {
41
- if (payload.version !== ZALO_WEBHOOK_SPOOL_VERSION || typeof payload.rawEvent !== "string") throw new ZaloWebhookPayloadError("Zalo webhook spool payload is invalid.");
42
- const facts = inspectZaloWebhookEvent(payload.rawEvent);
43
- if (facts.eventId !== claimedId) throw new ZaloWebhookPayloadError("Zalo webhook message id changed after durable admission.");
44
- const eventName = normalizeNullableString(facts.update.event_name);
45
- if (eventName !== "message.text.received" && eventName !== "message.image.received" && eventName !== "message.sticker.received" && eventName !== "message.unsupported.received") throw new ZaloWebhookPayloadError("Zalo webhook event_name is unsupported.");
46
- const message = facts.update.message;
47
- const from = isRecord(message.from) ? message.from : null;
48
- const chat = isRecord(message.chat) ? message.chat : null;
49
- if (!normalizeNullableString(from?.id)) throw new ZaloWebhookPayloadError("Zalo webhook message is missing message.from.id.");
50
- if (chat?.chat_type !== "PRIVATE" && chat?.chat_type !== "GROUP") throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid chat type.");
51
- if (typeof message.date !== "number" || !Number.isFinite(message.date)) throw new ZaloWebhookPayloadError("Zalo webhook message has an invalid date.");
52
- if (eventName === "message.text.received" && typeof message.text !== "string") throw new ZaloWebhookPayloadError("Zalo text event is missing message.text.");
53
- return facts.update;
54
- }
55
- function isZaloAuthenticationFailure(error) {
56
- let current = error;
57
- const seen = /* @__PURE__ */ new Set();
58
- while (current && typeof current === "object" && !seen.has(current)) {
59
- seen.add(current);
60
- const candidate = current;
61
- if (current instanceof ZaloApiError && (current.errorCode === 401 || current.errorCode === 403) || candidate.status === 401 || candidate.status === 403 || candidate.statusCode === 401 || candidate.statusCode === 403) return true;
62
- current = candidate.cause;
63
- }
64
- return false;
65
- }
66
- function createZaloWebhookIngress(options) {
67
- const monitor = createChannelIngressMonitor({
68
- queue: options.queue ?? getZaloRuntime().state.openChannelIngressQueue({ accountId: options.accountId }),
69
- inspect: (rawEvent) => inspectZaloWebhookEvent(rawEvent),
70
- payload: {
71
- storage: "raw-event",
72
- version: ZALO_WEBHOOK_SPOOL_VERSION,
73
- serialize: (rawEvent) => rawEvent,
74
- deserialize: (rawEvent) => rawEvent,
75
- createClaimError: (kind) => new ZaloWebhookPayloadError(kind === "invalid-version" ? "Zalo webhook spool payload is invalid." : "Zalo webhook identity changed after durable admission.")
76
- },
77
- deliver: async (_rawEvent, lifecycle, claim) => {
78
- const update = parseClaimedUpdate(claim.payload, claim.id);
79
- await options.deliver(update, bindIngressLifecycleToReplyOptions(lifecycle).turnAdoptionLifecycle);
80
- },
81
- pollIntervalMs: ZALO_WEBHOOK_DRAIN_INTERVAL_MS,
82
- retention: { failedMaxEntries: 5e3 },
83
- waitForDeliveryIdleBeforeRepump: false,
84
- runPumpTask: runDetachedWebhookWork,
85
- deferredClaims: "wait-on-stop",
86
- drain: {
87
- adoptionStallTimeoutMs: DEFAULT_INGRESS_ADOPTION_STALL_MS,
88
- startLimit: ZALO_WEBHOOK_MAX_CONCURRENT_DELIVERIES,
89
- retryPolicy: {
90
- maxAttempts: DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS,
91
- deadLetterMinAgeMs: 0
92
- },
93
- resolveNonRetryableFailure: (error) => {
94
- if (error instanceof ZaloWebhookPayloadError) return {
95
- reason: "invalid-event",
96
- message: error.message
97
- };
98
- if (isZaloAuthenticationFailure(error)) return {
99
- reason: "authentication-failed",
100
- message: formatErrorMessage(error)
101
- };
102
- return null;
103
- },
104
- onLog: (message) => options.runtime.error?.(`zalo ingress: ${message}`)
105
- },
106
- createStoppedError: () => /* @__PURE__ */ new Error("Zalo ingress stopped."),
107
- onError: (error) => options.runtime.error?.(`zalo ingress drain failed: ${formatErrorMessage(error)}`)
108
- });
109
- return {
110
- accept: async (rawEvent) => {
111
- await monitor.admit(rawEvent);
112
- },
113
- start: monitor.start,
114
- stop: monitor.stop
115
- };
116
- }
117
- const zaloWebhookIngressRuntime = { createZaloWebhookIngress };
118
- //#endregion
119
- export { ZaloWebhookPayloadError, zaloWebhookIngressRuntime };