@openclaw/feishu 2026.7.2-beta.1 → 2026.7.2-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import { t as readFeishuJsonResponse } from "./json-response-CheljwGr.js";
2
2
  import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
3
+ import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
3
4
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
4
5
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
5
- import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
6
6
  //#region extensions/feishu/src/app-registration.ts
7
7
  /**
8
8
  * Feishu app registration via OAuth device-code flow.
@@ -14,7 +14,7 @@ import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
14
14
  const FEISHU_ACCOUNTS_URL = "https://accounts.feishu.cn";
15
15
  const LARK_ACCOUNTS_URL = "https://accounts.larksuite.com";
16
16
  const REGISTRATION_PATH = "/oauth/v1/app/registration";
17
- const REQUEST_TIMEOUT_MS = 1e4;
17
+ const APP_REGISTRATION_REQUEST_TIMEOUT_MS = 1e4;
18
18
  const DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS = 5;
19
19
  const DEFAULT_REGISTRATION_EXPIRE_SECONDS = 600;
20
20
  function accountsBaseUrl(domain) {
@@ -26,20 +26,22 @@ async function postRegistration(baseUrl, body, options) {
26
26
  init: {
27
27
  method: "POST",
28
28
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
29
- body: new URLSearchParams(body).toString(),
30
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
29
+ body: new URLSearchParams(body).toString()
31
30
  },
32
31
  auditContext: "feishu.app-registration.post",
33
32
  fetchImpl: options?.fetchImpl,
34
- lookupFn: options?.lookupFn
33
+ lookupFn: options?.lookupFn,
34
+ timeoutMs: options?.timeoutMs
35
35
  });
36
36
  }
37
37
  async function fetchFeishuJson(params) {
38
+ const timeoutMs = params.timeoutMs ?? APP_REGISTRATION_REQUEST_TIMEOUT_MS;
38
39
  const { response, release } = await fetchWithSsrFGuard({
39
40
  url: params.url,
40
41
  init: params.init,
41
42
  fetchImpl: params.fetchImpl,
42
43
  lookupFn: params.lookupFn,
44
+ timeoutMs,
43
45
  policy: { allowedHostnames: [new URL(params.url).hostname] },
44
46
  auditContext: params.auditContext
45
47
  });
@@ -90,7 +92,7 @@ async function pollAppRegistration(params) {
90
92
  let currentInterval = params.interval;
91
93
  let domain = initialDomain;
92
94
  let domainSwitched = false;
93
- const expireInMs = finiteSecondsToTimerSafeMilliseconds(expireIn) ?? finiteSecondsToTimerSafeMilliseconds(DEFAULT_REGISTRATION_EXPIRE_SECONDS) ?? REQUEST_TIMEOUT_MS;
95
+ const expireInMs = finiteSecondsToTimerSafeMilliseconds(expireIn) ?? DEFAULT_REGISTRATION_EXPIRE_SECONDS * 1e3;
94
96
  const deadline = Date.now() + expireInMs;
95
97
  while (Date.now() < deadline) {
96
98
  if (abortSignal?.aborted) return { status: "timeout" };
@@ -164,8 +166,7 @@ async function getAppOwnerOpenId(params) {
164
166
  body: JSON.stringify({
165
167
  app_id: params.appId,
166
168
  app_secret: params.appSecret
167
- }),
168
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
169
+ })
169
170
  },
170
171
  auditContext: "feishu.app-registration.owner-token",
171
172
  fetchImpl: params.fetchImpl,
@@ -179,8 +180,7 @@ async function getAppOwnerOpenId(params) {
179
180
  headers: {
180
181
  Authorization: `Bearer ${tokenData.tenant_access_token}`,
181
182
  "Content-Type": "application/json"
182
- },
183
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
183
+ }
184
184
  },
185
185
  auditContext: "feishu.app-registration.owner-app",
186
186
  fetchImpl: params.fetchImpl,
@@ -195,7 +195,7 @@ async function getAppOwnerOpenId(params) {
195
195
  }
196
196
  }
197
197
  function sleepRegistrationPollInterval(intervalSeconds) {
198
- return sleep(finiteSecondsToTimerSafeMilliseconds(intervalSeconds) ?? finiteSecondsToTimerSafeMilliseconds(DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS) ?? REQUEST_TIMEOUT_MS);
198
+ return sleep(finiteSecondsToTimerSafeMilliseconds(intervalSeconds) ?? DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS * 1e3);
199
199
  }
200
200
  //#endregion
201
201
  export { beginAppRegistration, getAppOwnerOpenId, initAppRegistration, pollAppRegistration, printQrCode };
@@ -1,10 +1,10 @@
1
1
  import { a as resolveDefaultFeishuAccountId, d as isRecord$1, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-BDoGHJDk.js";
2
2
  import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BUjQ1TcA.js";
3
- import { C as resolveFeishuGroupConfig, E as resolveFeishuGroupToolPolicy, M as createFeishuCardInteractionEnvelope, O as normalizeFeishuChatType, _ as isFeishuGroupReadAllowed, a as readNativeFeishuCardJson, g as canEnumerateAllFeishuPeers, h as canEnumerateAllFeishuGroups, k as resolveFeishuChatType, m as authorizeFeishuChatMemberRead, n as createFeishuSendReceipt, p as assertFeishuChatReadAllowed, u as chunkFeishuMarkdown, v as isFeishuGroupReadEnabled, y as resolveFeishuChatReadPreliminaryAuthorization } from "./send-result-CPS7AozW.js";
3
+ import { A as resolveFeishuChatType, D as resolveFeishuGroupToolPolicy, N as createFeishuCardInteractionEnvelope, _ as canEnumerateAllFeishuPeers, a as readNativeFeishuCardJson, b as resolveFeishuChatReadPreliminaryAuthorization, g as canEnumerateAllFeishuGroups, h as authorizeFeishuChatMemberRead, k as normalizeFeishuChatType, m as assertFeishuChatReadAllowed, n as createFeishuSendReceipt, u as chunkFeishuMarkdown, v as isFeishuGroupReadAllowed, w as resolveFeishuGroupConfig, y as isFeishuGroupReadEnabled } from "./send-result-DfE5Fhz6.js";
4
4
  import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, o as resolveConfiguredFeishuGroupSessionScope, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-BeJL-wq7.js";
5
5
  import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-DXH5hux1.js";
6
6
  import { t as messageActionTargetAliases } from "./security-audit-BIeA3W3Q.js";
7
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DnlMcrn5.js";
7
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-BCpDLdg9.js";
8
8
  import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-BIHeF-S_.js";
9
9
  import { t as resolveFeishuSessionConversation } from "./session-conversation-BksWrfzm.js";
10
10
  import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
@@ -18,16 +18,16 @@ import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing
18
18
  import { createAllowlistProviderGroupPolicyWarningCollector, projectConfigAccountIdWarningCollector } from "openclaw/plugin-sdk/channel-policy";
19
19
  import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
20
20
  import { applyDirectoryQueryAndLimit, createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter, listDirectoryGroupEntriesFromMapKeysAndAllowFrom, listDirectoryUserEntriesFromAllowFrom, listDirectoryUserEntriesFromAllowFromAndMapKeys } from "openclaw/plugin-sdk/directory-runtime";
21
- import { interactiveReplyToPresentation, normalizeInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, resolveInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
21
+ import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
22
22
  import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
23
23
  import { buildProbeChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
24
24
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
25
25
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
26
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2 } from "openclaw/plugin-sdk/account-resolution";
26
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$3 } from "openclaw/plugin-sdk/account-resolution";
27
27
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
28
28
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
29
29
  import { normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/account-id";
30
- import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
30
+ import { DmPolicySchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
31
31
  import { z } from "zod";
32
32
  import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$2 } from "openclaw/plugin-sdk/secret-input";
33
33
  import fs from "node:fs";
@@ -36,7 +36,7 @@ import path from "node:path";
36
36
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
37
37
  import { deleteSessionEntry, isValidAgentHarnessSessionStoreEntry, listSessionEntries, loadTranscriptEventsSync, parseSqliteSessionFileMarker, resolveSessionStoreBackupPaths, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
38
38
  import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
39
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, splitSetupEntries } from "openclaw/plugin-sdk/setup";
39
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
40
40
  //#region extensions/feishu/src/approval-auth.ts
41
41
  function normalizeFeishuApproverId(value) {
42
42
  const trimmed = normalizeOptionalLowercaseString(normalizeFeishuTarget(String(value)));
@@ -55,16 +55,7 @@ const feishuApprovalAuth = createChannelApprovalAuth({
55
55
  //#endregion
56
56
  //#region extensions/feishu/src/config-schema.ts
57
57
  const ChannelActionsSchema = z.object({ reactions: z.boolean().optional() }).strict().optional();
58
- const DmPolicySchema = z.enum([
59
- "open",
60
- "pairing",
61
- "allowlist"
62
- ]);
63
- const GroupPolicySchema = z.union([z.enum([
64
- "open",
65
- "allowlist",
66
- "disabled"
67
- ]), z.literal("allowall").transform(() => "open")]);
58
+ const FeishuGroupPolicySchema = z.union([GroupPolicySchema, z.literal("allowall").transform(() => "open")]);
68
59
  const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().url().startsWith("https://")]);
69
60
  const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
70
61
  const TtsOverrideSchema = z.object({
@@ -196,17 +187,12 @@ const ReactionNotificationModeSchema = z.enum([
196
187
  * causing the reply to appear as a topic (话题) under the original message.
197
188
  */
198
189
  const ReplyInThreadSchema = z.enum(["disabled", "enabled"]).optional();
199
- const FeishuGroupSchema = z.object({
200
- requireMention: z.boolean().optional(),
190
+ const FeishuGroupSchema = buildGroupEntrySchema({
201
191
  tools: ToolPolicySchema,
202
- skills: z.array(z.string()).optional(),
203
- enabled: z.boolean().optional(),
204
- allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
205
- systemPrompt: z.string().optional(),
206
192
  groupSessionScope: GroupSessionScopeSchema,
207
193
  topicSessionMode: TopicSessionModeSchema,
208
194
  replyInThread: ReplyInThreadSchema
209
- }).strict();
195
+ }).omit({ toolsBySender: true });
210
196
  const FeishuSharedConfigShape = {
211
197
  webhookHost: z.string().optional(),
212
198
  webhookPort: z.number().int().positive().optional(),
@@ -215,7 +201,7 @@ const FeishuSharedConfigShape = {
215
201
  configWrites: z.boolean().optional(),
216
202
  dmPolicy: DmPolicySchema.optional(),
217
203
  allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
218
- groupPolicy: GroupPolicySchema.optional(),
204
+ groupPolicy: FeishuGroupPolicySchema.optional(),
219
205
  groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
220
206
  groupSenderAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
221
207
  requireMention: z.boolean().optional(),
@@ -235,6 +221,8 @@ const FeishuSharedConfigShape = {
235
221
  reactionNotifications: ReactionNotificationModeSchema,
236
222
  typingIndicator: z.boolean().optional(),
237
223
  resolveSenderNames: z.boolean().optional(),
224
+ allowBots: z.boolean().optional(),
225
+ vcAutoJoin: z.boolean().optional(),
238
226
  tts: TtsOverrideSchema
239
227
  };
240
228
  /**
@@ -255,7 +243,7 @@ const FeishuAccountConfigSchema = z.object({
255
243
  groupSessionScope: GroupSessionScopeSchema,
256
244
  topicSessionMode: TopicSessionModeSchema
257
245
  }).strict();
258
- const FeishuChannelConfigSchema = buildChannelConfigSchema(z.object({
246
+ const FeishuChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(z.object({
259
247
  enabled: z.boolean().optional(),
260
248
  defaultAccount: z.string().optional(),
261
249
  appId: z.string().optional(),
@@ -268,15 +256,17 @@ const FeishuChannelConfigSchema = buildChannelConfigSchema(z.object({
268
256
  ...FeishuSharedConfigShape,
269
257
  dmPolicy: DmPolicySchema.optional().default("pairing"),
270
258
  reactionNotifications: ReactionNotificationModeSchema.optional().default("own"),
271
- groupPolicy: GroupPolicySchema.optional().default("allowlist"),
259
+ groupPolicy: FeishuGroupPolicySchema.optional().default("allowlist"),
272
260
  requireMention: z.boolean().optional(),
273
261
  groupSessionScope: GroupSessionScopeSchema,
274
262
  topicSessionMode: TopicSessionModeSchema,
275
263
  dynamicAgentCreation: DynamicAgentCreationSchema,
276
264
  typingIndicator: z.boolean().optional().default(true),
277
- resolveSenderNames: z.boolean().optional().default(true),
278
- accounts: z.record(z.string(), FeishuAccountConfigSchema.optional()).optional()
279
- }).strict().superRefine((value, ctx) => {
265
+ resolveSenderNames: z.boolean().optional().default(true)
266
+ }).strict(), {
267
+ accountSchema: FeishuAccountConfigSchema,
268
+ optionalAccount: true
269
+ }).superRefine((value, ctx) => {
280
270
  const defaultAccount = value.defaultAccount?.trim();
281
271
  if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
282
272
  const normalizedDefaultAccount = normalizeAccountId$1(defaultAccount);
@@ -1229,7 +1219,7 @@ function setFeishuNamedAccountEnabled$1(cfg, accountId, enabled) {
1229
1219
  const feishuSetupAdapter = {
1230
1220
  resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
1231
1221
  applyAccountConfig: ({ cfg, accountId }) => {
1232
- if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return {
1222
+ if (!accountId || accountId === DEFAULT_ACCOUNT_ID$2) return {
1233
1223
  ...cfg,
1234
1224
  channels: {
1235
1225
  ...cfg.channels,
@@ -1281,7 +1271,7 @@ function formatFeishuStatusLine(status) {
1281
1271
  */
1282
1272
  function patchFeishuConfig(cfg, accountId, patch) {
1283
1273
  const feishuCfg = cfg.channels?.feishu;
1284
- if (accountId === DEFAULT_ACCOUNT_ID$1) return patchTopLevelChannelConfigSection({
1274
+ if (accountId === DEFAULT_ACCOUNT_ID$2) return patchTopLevelChannelConfigSection({
1285
1275
  cfg,
1286
1276
  channel,
1287
1277
  enabled: true,
@@ -1305,7 +1295,7 @@ function patchFeishuConfig(cfg, accountId, patch) {
1305
1295
  async function promptFeishuAllowFrom(params) {
1306
1296
  const feishuCfg = params.cfg.channels?.feishu;
1307
1297
  const resolvedAccountId = params.accountId ?? resolveDefaultFeishuAccountId(params.cfg);
1308
- const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$1 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
1298
+ const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$2 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
1309
1299
  await params.prompter.note([
1310
1300
  t("wizard.feishu.allowlistIntro"),
1311
1301
  t("wizard.feishu.allowlistFindUser"),
@@ -1345,7 +1335,7 @@ const feishuDmPolicy = {
1345
1335
  allowFromKey: "channels.feishu.allowFrom",
1346
1336
  resolveConfigKeys: (_cfg, accountId) => {
1347
1337
  const resolvedAccountId = accountId ?? resolveDefaultFeishuAccountId(_cfg);
1348
- return resolvedAccountId !== DEFAULT_ACCOUNT_ID$1 ? {
1338
+ return resolvedAccountId !== DEFAULT_ACCOUNT_ID$2 ? {
1349
1339
  policyKey: `channels.feishu.accounts.${resolvedAccountId}.dmPolicy`,
1350
1340
  allowFromKey: `channels.feishu.accounts.${resolvedAccountId}.allowFrom`
1351
1341
  } : {
@@ -1356,7 +1346,7 @@ const feishuDmPolicy = {
1356
1346
  getCurrent: (cfg, accountId) => {
1357
1347
  const feishuCfg = cfg.channels?.feishu;
1358
1348
  const resolvedAccountId = accountId ?? resolveDefaultFeishuAccountId(cfg);
1359
- if (resolvedAccountId !== DEFAULT_ACCOUNT_ID$1) {
1349
+ if (resolvedAccountId !== DEFAULT_ACCOUNT_ID$2) {
1360
1350
  const account = feishuCfg?.accounts?.[resolvedAccountId];
1361
1351
  if (account?.dmPolicy) return account.dmPolicy;
1362
1352
  }
@@ -1381,7 +1371,7 @@ function applyNewAppSecurityPolicy(cfg, accountId, openId, groupPolicy) {
1381
1371
  next = patchFeishuConfig(next, accountId, groupPatch);
1382
1372
  return next;
1383
1373
  }
1384
- const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-BwQ-aGTe.js"));
1374
+ const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-BzkyrVZu.js"));
1385
1375
  async function promptFeishuDomain(params) {
1386
1376
  return await params.prompter.select({
1387
1377
  message: t("wizard.feishu.domainPrompt"),
@@ -1606,7 +1596,7 @@ const feishuSetupWizard = {
1606
1596
  });
1607
1597
  let probeResult = null;
1608
1598
  if (configured && account.configured) try {
1609
- const { probeFeishu } = await import("./probe-CAloGqgG.js").then((n) => n.n);
1599
+ const { probeFeishu } = await import("./probe-BgboTHIn.js").then((n) => n.n);
1610
1600
  probeResult = await probeFeishu(account);
1611
1601
  } catch {}
1612
1602
  if (!configured) return [formatFeishuStatusLine("needs-credentials")];
@@ -1642,11 +1632,7 @@ const feishuSetupWizard = {
1642
1632
  });
1643
1633
  },
1644
1634
  dmPolicy: feishuDmPolicy,
1645
- disable: (cfg) => patchTopLevelChannelConfigSection({
1646
- cfg,
1647
- channel,
1648
- patch: { enabled: false }
1649
- })
1635
+ disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
1650
1636
  };
1651
1637
  //#endregion
1652
1638
  //#region extensions/feishu/src/channel.ts
@@ -1682,7 +1668,7 @@ const meta = {
1682
1668
  order: 70,
1683
1669
  preferSessionLookupForAnnounceTarget: true
1684
1670
  };
1685
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-DJ_B-bpG.js"), "feishuChannelRuntime");
1671
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CVIzo2dV.js"), "feishuChannelRuntime");
1686
1672
  function toFeishuMessageSendResult(result, kind) {
1687
1673
  const receipt = result.receipt ?? createFeishuSendReceipt({
1688
1674
  messageId: result.messageId,
@@ -1726,7 +1712,7 @@ const feishuMessageAdapter = defineChannelMessageAdapter({
1726
1712
  }
1727
1713
  });
1728
1714
  async function createFeishuActionClient(account) {
1729
- const { createFeishuClient } = await import("./client-BUC-R2Wi.js").then((n) => n.t);
1715
+ const { createFeishuClient } = await import("./client-87BmeMpj.js").then((n) => n.t);
1730
1716
  return createFeishuClient(account);
1731
1717
  }
1732
1718
  async function resolveFeishuChatTypeById(params) {
@@ -2164,7 +2150,7 @@ const feishuPlugin = createChatChannelPlugin({
2164
2150
  config: {
2165
2151
  ...feishuConfigAdapter,
2166
2152
  setAccountEnabled: ({ cfg, accountId, enabled }) => {
2167
- if (accountId === DEFAULT_ACCOUNT_ID$2) return {
2153
+ if (accountId === DEFAULT_ACCOUNT_ID$3) return {
2168
2154
  ...cfg,
2169
2155
  channels: {
2170
2156
  ...cfg.channels,
@@ -2177,7 +2163,7 @@ const feishuPlugin = createChatChannelPlugin({
2177
2163
  return setFeishuNamedAccountEnabled(cfg, accountId, enabled);
2178
2164
  },
2179
2165
  deleteAccount: ({ cfg, accountId }) => {
2180
- if (accountId === DEFAULT_ACCOUNT_ID$2) {
2166
+ if (accountId === DEFAULT_ACCOUNT_ID$3) {
2181
2167
  const next = { ...cfg };
2182
2168
  const nextChannels = { ...cfg.channels };
2183
2169
  delete nextChannels.feishu;
@@ -2233,14 +2219,14 @@ const feishuPlugin = createChatChannelPlugin({
2233
2219
  if (ctx.action === "thread-reply" && !replyToMessageId) throw new Error("Feishu thread-reply requires messageId.");
2234
2220
  const text = readFirstString(ctx.params, ["text", "message"]);
2235
2221
  const textCard = readNativeFeishuCardJson(text, { responsePrefix: resolveFeishuMessageActionResponsePrefix(ctx) });
2236
- const interactive = normalizeInteractiveReply(ctx.params.interactive);
2237
- const presentation = normalizeMessagePresentation(ctx.params.presentation) ?? (interactive ? interactiveReplyToPresentation(interactive) : void 0);
2222
+ const interactive = normalizeLegacyInteractiveReply(ctx.params.interactive);
2223
+ const presentation = normalizeMessagePresentation(ctx.params.presentation) ?? (interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0);
2238
2224
  const mediaUrl = readFeishuMediaParam(ctx.params);
2239
2225
  const audioAsVoice = readBooleanParam(ctx.params, ["asVoice", "audioAsVoice"]);
2240
2226
  if (textCard && !presentation) assertFeishuCardWithinEnvelope(textCard, "Feishu native card");
2241
2227
  const generatedCard = presentation ? buildFeishuPresentationCard({
2242
2228
  presentation,
2243
- fallbackText: textCard ? void 0 : resolveInteractiveTextFallback({
2229
+ fallbackText: textCard ? void 0 : resolveLegacyInteractiveTextFallback({
2244
2230
  text,
2245
2231
  interactive
2246
2232
  })
@@ -2755,7 +2741,7 @@ const feishuPlugin = createChatChannelPlugin({
2755
2741
  })
2756
2742
  }),
2757
2743
  status: createComputedAccountStatusAdapter({
2758
- defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID$2, { port: null }),
2744
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID$3, { port: null }),
2759
2745
  buildChannelSummary: ({ snapshot }) => buildProbeChannelStatusSummary(snapshot, { port: snapshot.port ?? null }),
2760
2746
  probeAccount: async ({ account }) => await (await loadFeishuChannelRuntime()).probeFeishu(account),
2761
2747
  resolveAccountSnapshot: ({ account, runtime }) => ({
@@ -2771,7 +2757,7 @@ const feishuPlugin = createChatChannelPlugin({
2771
2757
  })
2772
2758
  }),
2773
2759
  gateway: { startAccount: async (ctx) => {
2774
- const { monitorFeishuProvider } = await import("./monitor-M3iYtoh4.js");
2760
+ const { monitorFeishuProvider } = await import("./monitor-BVQf7-Bl.js");
2775
2761
  const account = resolveFeishuRuntimeAccount({
2776
2762
  cfg: ctx.cfg,
2777
2763
  accountId: ctx.accountId
@@ -1,2 +1,2 @@
1
- import { t as feishuPlugin } from "./channel-DdPQ2HGh.js";
1
+ import { t as feishuPlugin } from "./channel-BdnXYU6g.js";
2
2
  export { feishuPlugin };
@@ -1,19 +1,19 @@
1
1
  import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount, v as parseFeishuCommentTarget } from "./accounts-BDoGHJDk.js";
2
- import { c as isFeishuCardWithinEnvelope, l as listFeishuDirectoryGroups, o as assertFeishuCardWithinEnvelope, s as buildFeishuPresentationCardElements, u as listFeishuDirectoryPeers } from "./channel-DdPQ2HGh.js";
3
- import { a as readNativeFeishuCardJson, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, o as resolveFeishuCardTemplate, s as sanitizeNativeFeishuCard, u as chunkFeishuMarkdown } from "./send-result-CPS7AozW.js";
4
- import { r as createFeishuClient } from "./client-BUC-R2Wi.js";
5
- import { c as buildFeishuDirectChatMembers, d as getFeishuMemberInfo, l as getChatInfo, r as cleanupAmbientCommentTypingReaction, s as assertFeishuChatMember, t as deliverCommentThreadText, u as getChatMembers } from "./drive-Bpe8ONK_.js";
6
- import { a as sendMarkdownCardFeishu, h as shouldSuppressFeishuTextForVoiceMedia, i as sendCardFeishu, m as sendMediaFeishu, n as getMessageFeishu, o as sendMessageFeishu, s as sendStructuredCardFeishu, t as editMessageFeishu, v as resolveFeishuIdentityHeaderTitle } from "./send-CpnLYueT.js";
7
- import { t as probeFeishu } from "./probe-CAloGqgG.js";
2
+ import { c as isFeishuCardWithinEnvelope, l as listFeishuDirectoryGroups, o as assertFeishuCardWithinEnvelope, s as buildFeishuPresentationCardElements, u as listFeishuDirectoryPeers } from "./channel-BdnXYU6g.js";
3
+ import { a as readNativeFeishuCardJson, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, o as resolveFeishuCardTemplate, s as sanitizeNativeFeishuCard, u as chunkFeishuMarkdown } from "./send-result-DfE5Fhz6.js";
4
+ import { r as createFeishuClient } from "./client-87BmeMpj.js";
5
+ import { c as buildFeishuDirectChatMembers, d as getFeishuMemberInfo, l as getChatInfo, r as cleanupAmbientCommentTypingReaction, s as assertFeishuChatMember, t as deliverCommentThreadText, u as getChatMembers } from "./drive-BHv8WW9i.js";
6
+ import { _ as buildFeishuMediaFallbackText, a as sendMarkdownCardFeishu, h as shouldSuppressFeishuTextForVoiceMedia, i as sendCardFeishu, m as sendMediaFeishu, n as getMessageFeishu, o as sendMessageFeishu, s as sendStructuredCardFeishu, t as editMessageFeishu, y as resolveFeishuIdentityHeaderTitle } from "./send-DcrXbqqA.js";
7
+ import { t as probeFeishu } from "./probe-BgboTHIn.js";
8
8
  import { createReplyToFanout } from "openclaw/plugin-sdk/channel-outbound";
9
- import { interactiveReplyToPresentation, normalizeInteractiveReply, normalizeMessagePresentation, renderMessagePresentationFallbackText, resolveInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
9
+ import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationFallbackText, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
10
10
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
11
11
  import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
12
12
  import path from "node:path";
13
13
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
14
14
  import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
15
15
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
16
- import { resolvePayloadMediaUrls, sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
16
+ import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
17
17
  import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
18
18
  //#region extensions/feishu/src/directory.ts
19
19
  const MAX_FEISHU_DIRECTORY_PAGES = 100;
@@ -167,14 +167,14 @@ function buildFeishuPayloadCard(params) {
167
167
  }
168
168
  const rawText = params.text ?? params.payload.text;
169
169
  const textCard = readNativeFeishuCardJson(rawText);
170
- const interactive = normalizeInteractiveReply(params.payload.interactive);
171
- const presentation = normalizeMessagePresentation(params.payload.presentation) ?? (interactive ? interactiveReplyToPresentation(interactive) : void 0);
170
+ const interactive = normalizeLegacyInteractiveReply(params.payload.interactive);
171
+ const presentation = normalizeMessagePresentation(params.payload.presentation) ?? (interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0);
172
172
  if (!presentation && !interactive) {
173
173
  if (!textCard) return;
174
174
  assertFeishuCardWithinEnvelope(textCard, "Feishu native card");
175
175
  return markRenderedFeishuCard(textCard);
176
176
  }
177
- const text = textCard ? void 0 : resolveInteractiveTextFallback({
177
+ const text = textCard ? void 0 : resolveLegacyInteractiveTextFallback({
178
178
  text: rawText,
179
179
  interactive
180
180
  });
@@ -393,6 +393,51 @@ async function sendFeishuFallbackPayload(params) {
393
393
  }
394
394
  return lastResult;
395
395
  }
396
+ async function sendFeishuTtsSupplementPayload(params) {
397
+ const sendMedia = feishuOutbound.sendMedia;
398
+ const sendText = feishuOutbound.sendText;
399
+ if (!sendMedia || !sendText) throw new Error("Feishu TTS supplement delivery is not available.");
400
+ const { normalizedReplyToId } = resolveFeishuReplyMode({
401
+ replyToId: params.ctx.replyToId,
402
+ threadId: params.ctx.threadId
403
+ });
404
+ const nextReplyToId = createReplyToFanout({
405
+ replyToId: normalizedReplyToId,
406
+ replyToIdSource: params.ctx.replyToIdSource,
407
+ replyToMode: params.ctx.replyToMode
408
+ });
409
+ const ctx = {
410
+ ...params.ctx,
411
+ payload: params.payload
412
+ };
413
+ let lastResult;
414
+ if (params.sendVisiblePayload) {
415
+ lastResult = await params.sendVisiblePayload(nextReplyToId());
416
+ await ctx.onDeliveryResult?.(lastResult);
417
+ } else if (params.supplement.visibleTextAlreadyDelivered !== true) {
418
+ const text = params.payload.text?.trim() ? params.payload.text : params.supplement.spokenText;
419
+ for (const chunk of chunkFeishuMarkdown(text, FEISHU_TEXT_CHUNK_LIMIT)) {
420
+ lastResult = await sendText({
421
+ ...ctx,
422
+ text: chunk,
423
+ replyToId: nextReplyToId(),
424
+ onDeliveryResult: void 0
425
+ });
426
+ await ctx.onDeliveryResult?.(lastResult);
427
+ }
428
+ }
429
+ for (const mediaUrl of normalizeStringEntries(resolvePayloadMediaUrls(params.payload))) lastResult = await sendMedia({
430
+ ...ctx,
431
+ text: "",
432
+ mediaUrl,
433
+ replyToId: nextReplyToId(),
434
+ audioAsVoice: params.payload.audioAsVoice ?? ctx.audioAsVoice
435
+ });
436
+ return lastResult ?? {
437
+ channel: "feishu",
438
+ messageId: ""
439
+ };
440
+ }
396
441
  const feishuOutbound = {
397
442
  deliveryMode: "direct",
398
443
  chunker: chunkFeishuMarkdown,
@@ -421,9 +466,10 @@ const feishuOutbound = {
421
466
  renderPresentation: renderFeishuPresentationPayload,
422
467
  sendPayload: async (ctx) => {
423
468
  const { payload, presentationFallback } = consumeFeishuPresentationFallbackMarker(ctx.payload);
469
+ const ttsSupplement = getReplyPayloadTtsSupplement(payload);
424
470
  if (parseFeishuCommentTarget(ctx.to)) {
425
- const interactive = normalizeInteractiveReply(payload.interactive);
426
- const normalizedPresentation = normalizeMessagePresentation(payload.presentation) ?? (interactive ? interactiveReplyToPresentation(interactive) : void 0);
471
+ const interactive = normalizeLegacyInteractiveReply(payload.interactive);
472
+ const normalizedPresentation = normalizeMessagePresentation(payload.presentation) ?? (interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0);
427
473
  const textCard = readNativeFeishuCardJson(payload.text);
428
474
  const presentationFallbackText = renderMessagePresentationFallbackText({
429
475
  text: textCard ? void 0 : payload.text,
@@ -450,8 +496,13 @@ const feishuOutbound = {
450
496
  identity: ctx.identity
451
497
  });
452
498
  if (!card) {
453
- const interactive = normalizeInteractiveReply(payload.interactive);
454
- const presentation = normalizeMessagePresentation(payload.presentation) ?? (interactive ? interactiveReplyToPresentation(interactive) : void 0);
499
+ if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
500
+ ctx,
501
+ payload,
502
+ supplement: ttsSupplement
503
+ });
504
+ const interactive = normalizeLegacyInteractiveReply(payload.interactive);
505
+ const presentation = normalizeMessagePresentation(payload.presentation) ?? (interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0);
455
506
  return await sendFeishuFallbackPayload({
456
507
  ctx,
457
508
  payload: presentation ? {
@@ -466,6 +517,25 @@ const feishuOutbound = {
466
517
  separateMediaAndText: presentationFallback || presentation !== void 0
467
518
  });
468
519
  }
520
+ if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
521
+ ctx,
522
+ payload,
523
+ supplement: ttsSupplement,
524
+ sendVisiblePayload: async (replyToId) => {
525
+ const { replyToMessageId, replyInThread } = resolveFeishuReplyMode({
526
+ replyToId,
527
+ threadId: ctx.threadId
528
+ });
529
+ return attachChannelToResult("feishu", await sendCardFeishu({
530
+ cfg: ctx.cfg,
531
+ to: ctx.to,
532
+ card,
533
+ replyToMessageId,
534
+ replyInThread,
535
+ accountId: ctx.accountId ?? void 0
536
+ }));
537
+ }
538
+ });
469
539
  const { normalizedReplyToId } = resolveFeishuReplyMode({
470
540
  replyToId: ctx.replyToId,
471
541
  threadId: ctx.threadId
@@ -532,6 +602,14 @@ const feishuOutbound = {
532
602
  });
533
603
  } catch (err) {
534
604
  console.error(`[feishu] local image path auto-send failed:`, err);
605
+ return await sendOutboundText({
606
+ cfg,
607
+ to,
608
+ text: await buildFeishuMediaFallbackText({}),
609
+ accountId: accountId ?? void 0,
610
+ replyToMessageId,
611
+ replyInThread
612
+ });
535
613
  }
536
614
  if (parseFeishuCommentTarget(to)) return await sendOutboundText({
537
615
  cfg,
@@ -589,7 +667,11 @@ const feishuOutbound = {
589
667
  if (parseFeishuCommentTarget(to)) return await sendOutboundText({
590
668
  cfg,
591
669
  to,
592
- text: [text?.trim(), mediaUrl?.trim()].filter(Boolean).join("\n\n") || mediaUrl || text || "",
670
+ text: mediaUrl?.trim() ? await buildFeishuMediaFallbackText({
671
+ text,
672
+ mediaUrl,
673
+ mediaLinkStyle: "plain"
674
+ }) : text?.trim() ?? "",
593
675
  accountId: accountId ?? void 0,
594
676
  replyToMessageId,
595
677
  replyInThread
@@ -632,7 +714,10 @@ const feishuOutbound = {
632
714
  const fallbackResult = await sendOutboundText({
633
715
  cfg,
634
716
  to,
635
- text: [textSent ? void 0 : text?.trim(), `📎 ${mediaUrl}`].filter(Boolean).join("\n\n"),
717
+ text: await buildFeishuMediaFallbackText({
718
+ text: textSent ? void 0 : text,
719
+ mediaUrl
720
+ }),
636
721
  accountId: accountId ?? void 0,
637
722
  replyToMessageId,
638
723
  replyInThread
@@ -62,8 +62,28 @@ function setRequestUserAgent(req) {
62
62
  return req;
63
63
  }
64
64
  Lark.defaultHttpInstance.interceptors?.request?.use(setRequestUserAgent);
65
- async function getWsProxyAgent() {
66
- return resolveAmbientNodeProxyAgent();
65
+ function isManagedProxyActive() {
66
+ return process.env["OPENCLAW_PROXY_ACTIVE"] === "1";
67
+ }
68
+ let cachedFeishuProxyAgent;
69
+ let pendingFeishuProxyAgent;
70
+ async function getFeishuProxyAgent() {
71
+ if (cachedFeishuProxyAgent) return cachedFeishuProxyAgent;
72
+ if (pendingFeishuProxyAgent) return pendingFeishuProxyAgent;
73
+ let resolutionError;
74
+ const pending = resolveAmbientNodeProxyAgent({ onError: (error) => {
75
+ resolutionError = error;
76
+ } }).then((agent) => {
77
+ if (!agent && isManagedProxyActive()) throw new Error("Feishu managed proxy is active but no proxy agent could be created", { cause: resolutionError });
78
+ cachedFeishuProxyAgent = agent;
79
+ return agent;
80
+ });
81
+ pendingFeishuProxyAgent = pending;
82
+ try {
83
+ return await pending;
84
+ } finally {
85
+ if (pendingFeishuProxyAgent === pending) pendingFeishuProxyAgent = void 0;
86
+ }
67
87
  }
68
88
  const clientCache = /* @__PURE__ */ new Map();
69
89
  function resolveDomain(domain) {
@@ -74,25 +94,38 @@ function resolveDomain(domain) {
74
94
  /**
75
95
  * Create an HTTP instance that delegates to the Lark SDK's default instance
76
96
  * but injects a default request timeout and User-Agent header to prevent
77
- * indefinite hangs and set a standardized User-Agent per OAPI best practices.
97
+ * indefinite hangs, set a standardized User-Agent per OAPI best practices, and
98
+ * keep axios from taking a separate ambient proxy path for HTTPS requests.
78
99
  */
79
- function createTimeoutHttpInstance(defaultTimeoutMs) {
100
+ function createFeishuHttpInstance(defaultTimeoutMs) {
80
101
  const base = feishuClientSdk.defaultHttpInstance;
81
- function injectTimeout(opts) {
82
- return {
102
+ async function injectRequestOptions(opts) {
103
+ const next = {
83
104
  timeout: defaultTimeoutMs,
84
105
  ...opts
85
106
  };
107
+ const agent = await getFeishuProxyAgent();
108
+ if (agent) {
109
+ if (isManagedProxyActive()) {
110
+ next.httpAgent = agent;
111
+ next.httpsAgent = agent;
112
+ } else {
113
+ next.httpAgent ??= agent;
114
+ next.httpsAgent ??= agent;
115
+ }
116
+ next.proxy = false;
117
+ }
118
+ return next;
86
119
  }
87
120
  return {
88
- request: (opts) => base.request(injectTimeout(opts)),
89
- get: (url, opts) => base.get(url, injectTimeout(opts)),
90
- post: (url, data, opts) => base.post(url, data, injectTimeout(opts)),
91
- put: (url, data, opts) => base.put(url, data, injectTimeout(opts)),
92
- patch: (url, data, opts) => base.patch(url, data, injectTimeout(opts)),
93
- delete: (url, opts) => base.delete(url, injectTimeout(opts)),
94
- head: (url, opts) => base.head(url, injectTimeout(opts)),
95
- options: (url, opts) => base.options(url, injectTimeout(opts))
121
+ request: async (opts) => base.request(await injectRequestOptions(opts)),
122
+ get: async (url, opts) => base.get(url, await injectRequestOptions(opts)),
123
+ post: async (url, data, opts) => base.post(url, data, await injectRequestOptions(opts)),
124
+ put: async (url, data, opts) => base.put(url, data, await injectRequestOptions(opts)),
125
+ patch: async (url, data, opts) => base.patch(url, data, await injectRequestOptions(opts)),
126
+ delete: async (url, opts) => base.delete(url, await injectRequestOptions(opts)),
127
+ head: async (url, opts) => base.head(url, await injectRequestOptions(opts)),
128
+ options: async (url, opts) => base.options(url, await injectRequestOptions(opts))
96
129
  };
97
130
  }
98
131
  /**
@@ -110,7 +143,7 @@ function createFeishuClient(creds) {
110
143
  appSecret,
111
144
  appType: feishuClientSdk.AppType.SelfBuild,
112
145
  domain: resolveDomain(domain),
113
- httpInstance: createTimeoutHttpInstance(defaultHttpTimeoutMs)
146
+ httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs)
114
147
  });
115
148
  clientCache.set(accountId, {
116
149
  client,
@@ -130,11 +163,13 @@ function createFeishuClient(creds) {
130
163
  async function createFeishuWSClient(account, callbacks = {}) {
131
164
  const { accountId, appId, appSecret, domain } = account;
132
165
  if (!appId || !appSecret) throw new Error(`Feishu credentials not configured for account "${accountId}"`);
133
- const agent = await getWsProxyAgent();
166
+ const agent = await getFeishuProxyAgent();
167
+ const defaultHttpTimeoutMs = resolveConfiguredHttpTimeoutMs(account);
134
168
  return new feishuClientSdk.WSClient({
135
169
  appId,
136
170
  appSecret,
137
171
  domain: resolveDomain(domain),
172
+ httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs),
138
173
  ...callbacks,
139
174
  loggerLevel: feishuClientSdk.LoggerLevel.info,
140
175
  wsConfig: FEISHU_WS_CONFIG,