@openclaw/feishu 2026.9.2 → 2026.9.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.
package/dist/api.js CHANGED
@@ -1,9 +1,9 @@
1
- import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-VytDDz_B.js";
2
- import { G as createFeishuToolClient, H as feishuExternalToolResult, K as resolveAnyEnabledFeishuToolsConfig, U as toolExecutionErrorResult, V as registerFeishuDriveTools, W as unknownToolActionResult, lt as parseFeishuMarkdown, ot as chunkFeishuMarkdown, q as resolveFeishuToolAccount } from "./reply-delivery-result-CYaKkbUN.js";
1
+ import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-DrAqd5ED.js";
2
+ import { G as feishuExternalToolResult, J as createFeishuToolClient, K as toolExecutionErrorResult, W as registerFeishuDriveTools, X as resolveFeishuToolAccount, Y as resolveAnyEnabledFeishuToolsConfig, ft as parseFeishuMarkdown, lt as chunkFeishuMarkdown, q as unknownToolActionResult } from "./reply-delivery-result-BXEmtKpf.js";
3
3
  import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
4
4
  import { s as resolveConfiguredHttpTimeoutMs } from "./client-DMbIL3UH.js";
5
5
  import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
6
- import { o as registerFeishuChatTools } from "./chat-DDhIcPQy.js";
6
+ import { o as registerFeishuChatTools } from "./chat-BZSnEtgb.js";
7
7
  import { n as getFeishuThreadBindingManager, t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
8
8
  import { n as handleFeishuSubagentEnded, t as handleFeishuSubagentDeliveryTarget } from "./subagent-hooks-Buadpie_.js";
9
9
  import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
@@ -553,19 +553,13 @@ function resolveRemoteImageUrl(value) {
553
553
  }
554
554
  function collectMarkdownImages(root) {
555
555
  const definitions = /* @__PURE__ */ new Map();
556
+ const imageNodes = [];
556
557
  visitMarkdown(root, (node) => {
557
- if (node.type !== "definition" || !node.identifier || !node.url) return;
558
- if (!definitions.has(node.identifier)) definitions.set(node.identifier, node.url);
558
+ if (node.type === "definition" && node.identifier && node.url) {
559
+ if (!definitions.has(node.identifier)) definitions.set(node.identifier, node.url);
560
+ } else if (node.type === "image" || node.type === "imageReference") imageNodes.push(node);
559
561
  });
560
- const images = [];
561
- visitMarkdown(root, (node) => {
562
- if (node.type === "image") {
563
- images.push({ url: resolveRemoteImageUrl(node.url) });
564
- return;
565
- }
566
- if (node.type === "imageReference") images.push({ url: resolveRemoteImageUrl(node.identifier ? definitions.get(node.identifier) : void 0) });
567
- });
568
- return images;
562
+ return imageNodes.map((node) => ({ url: resolveRemoteImageUrl(node.type === "image" ? node.url : node.identifier ? definitions.get(node.identifier) : void 0) }));
569
563
  }
570
564
  function splitSourceAtOffsets(source, offsets) {
571
565
  const chunks = [];
@@ -658,7 +652,11 @@ function createDocxMarkdownChunk(markdown) {
658
652
  };
659
653
  }
660
654
  function createDocxMarkdownPlan(markdown) {
661
- return { chunks: splitSourceAtOffsets(markdown, headingOffsets(markdown, parseFeishuMarkdown(markdown))).map(createDocxMarkdownChunk) };
655
+ const root = parseFeishuMarkdown(markdown);
656
+ return { chunks: splitSourceAtOffsets(markdown, headingOffsets(markdown, root)).map((chunk) => chunk === markdown ? {
657
+ markdown,
658
+ images: collectMarkdownImages(root)
659
+ } : createDocxMarkdownChunk(chunk)) };
662
660
  }
663
661
  function splitDocxMarkdownBySize(markdown, maxChars) {
664
662
  if (markdown.length <= maxChars) return [markdown];
@@ -1,6 +1,6 @@
1
1
  import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
2
2
  import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BTQCYRZQ.js";
3
- import { B as deliverCommentThreadText, Ct as resolveFeishuGroupToolPolicy, D as sendMessageFeishu, E as sendCardFeishu, Et as resolveFeishuChatType, F as createFeishuSendReceipt, J as cleanupAmbientCommentTypingReaction, L as buildFeishuMediaFallbackText, O as sendStructuredCardFeishu, S as chunkFeishuCardMarkdown, Tt as normalizeFeishuChatType, _ as resolveFeishuRichReply, a as FEISHU_PRESENTATION_CAPABILITIES, bt as resolveFeishuGroupConfig, c as buildFeishuPresentationCard, ct as materializeFeishuPostMarkdownSoftBreaks, d as isFeishuCardWithinEnvelope, dt as authorizeFeishuChatMemberRead, f as markRenderedFeishuCard, ft as canEnumerateAllFeishuGroups, gt as resolveFeishuChatReadPreliminaryAuthorization, h as renderFeishuPresentationPayload, ht as isFeishuGroupReadEnabled, it as readNativeFeishuCardJson, l as buildFeishuPresentationFallback, m as renderFeishuPresentationFallbackText, mt as isFeishuGroupReadAllowed, n as createFeishuReplyDeliveryResult, o as assertFeishuCardWithinEnvelope, ot as chunkFeishuMarkdown, p as readNativeFeishuCard, pt as canEnumerateAllFeishuPeers, rt as parseFeishuCommentTarget, s as buildFeishuPayloadCard, st as chunkFeishuPostMarkdown, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as assertFeishuChatReadAllowed, x as shouldSuppressFeishuTextForVoiceMedia, y as sendMediaFeishu, z as resolveFeishuIdentityHeaderTitle } from "./reply-delivery-result-CYaKkbUN.js";
3
+ import { A as sendStructuredCardFeishu, B as buildFeishuMediaFallbackText, C as shouldSuppressFeishuTextForVoiceMedia, Ct as resolveFeishuGroupConfig, Et as resolveFeishuGroupToolPolicy, H as resolveFeishuIdentityHeaderTitle, O as sendCardFeishu, Ot as normalizeFeishuChatType, R as createFeishuSendReceipt, U as deliverCommentThreadText, Z as cleanupAmbientCommentTypingReaction, _t as isFeishuGroupReadAllowed, a as FEISHU_PRESENTATION_CAPABILITIES, c as buildFeishuPresentationCard, d as feishuCardWithinTableLimit, dt as materializeFeishuPostMarkdownSoftBreaks, f as isFeishuCardWithinEnvelope, g as renderFeishuPresentationPayload, gt as canEnumerateAllFeishuPeers, h as renderFeishuPresentationFallbackText, ht as canEnumerateAllFeishuGroups, k as sendMessageFeishu, kt as resolveFeishuChatType, l as buildFeishuPresentationFallback, lt as chunkFeishuMarkdown, m as readNativeFeishuCard, mt as authorizeFeishuChatMemberRead, n as createFeishuReplyDeliveryResult, o as assertFeishuCardWithinEnvelope, ot as parseFeishuCommentTarget, p as markRenderedFeishuCard, pt as assertFeishuChatReadAllowed, s as buildFeishuPayloadCard, st as readNativeFeishuCardJson, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as chunkFeishuPostMarkdown, v as resolveFeishuRichReply, vt as isFeishuGroupReadEnabled, w as chunkFeishuCardMarkdown, x as sendMediaFeishu, y as withinCardTableLimit, yt as resolveFeishuChatReadPreliminaryAuthorization } from "./reply-delivery-result-BXEmtKpf.js";
4
4
  import { n as normalizeCompatibilityConfig, o as normalizeFeishuExternalKey, r as FeishuChannelConfigSchema, t as legacyConfigRules } from "./doctor-contract-w6FpMypv.js";
5
5
  import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, o as resolveConfiguredFeishuGroupSessionScope, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
6
6
  import { t as messageActionTargetAliases } from "./security-audit-DKfR4Cv3.js";
@@ -44,7 +44,7 @@ import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMedia
44
44
  import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
45
45
  import { readStringParam } from "openclaw/plugin-sdk/param-readers";
46
46
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
47
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput, mergeAllowFromEntries, patchScopedAccountConfig, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
47
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput, mergeAllowFromEntries, patchScopedAccountConfig, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
48
48
  import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
49
49
  //#region extensions/feishu/src/approval-auth.ts
50
50
  function normalizeFeishuApproverId(value) {
@@ -799,10 +799,6 @@ function partialFeishuSendError(error, results) {
799
799
  function readFeishuPropagateMediaUploadFailure(payload) {
800
800
  return (isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.[FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER] === true;
801
801
  }
802
- function buildFeishuPropagationOnlyChannelData(payload) {
803
- if ((isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.["__openclawPropagateMediaUploadFailure"] !== true) return;
804
- return { feishu: { [FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER]: true } };
805
- }
806
802
  function resolveFeishuReplyMode(params) {
807
803
  const replyToMessageId = params.replyToId?.trim();
808
804
  if (replyToMessageId) return {
@@ -867,7 +863,7 @@ async function sendOutboundText(params) {
867
863
  cfg,
868
864
  accountId
869
865
  }).config?.renderMode ?? "auto";
870
- const useCard = renderMode === "card" || renderMode === "auto" && shouldUseCard(text);
866
+ const useCard = (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) && withinCardTableLimit(text);
871
867
  const tableMode = resolveMarkdownTableMode({
872
868
  cfg,
873
869
  channel: "feishu"
@@ -1023,7 +1019,7 @@ const feishuOutbound = {
1023
1019
  text,
1024
1020
  interactive: void 0,
1025
1021
  presentation: void 0,
1026
- channelData: buildFeishuPropagationOnlyChannelData(payload)
1022
+ channelData: void 0
1027
1023
  },
1028
1024
  separateMediaAndText: true
1029
1025
  });
@@ -1355,36 +1351,22 @@ function resolveFeishuOutboundSessionRoute(params) {
1355
1351
  //#region extensions/feishu/src/setup-core.ts
1356
1352
  function setFeishuNamedAccountEnabled(cfg, accountId, enabled) {
1357
1353
  const feishuCfg = cfg.channels?.feishu;
1358
- return {
1359
- ...cfg,
1360
- channels: {
1361
- ...cfg.channels,
1362
- feishu: {
1363
- ...feishuCfg,
1364
- accounts: {
1365
- ...feishuCfg?.accounts,
1366
- [accountId]: {
1367
- ...feishuCfg?.accounts?.[accountId],
1368
- enabled
1369
- }
1370
- }
1354
+ return patchTopLevelChannelConfigSection({
1355
+ cfg,
1356
+ channel: "feishu",
1357
+ patch: { accounts: {
1358
+ ...feishuCfg?.accounts,
1359
+ [accountId]: {
1360
+ ...feishuCfg?.accounts?.[accountId],
1361
+ enabled
1371
1362
  }
1372
- }
1373
- };
1363
+ } }
1364
+ });
1374
1365
  }
1375
1366
  const feishuSetupAdapter = {
1376
1367
  resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
1377
1368
  applyAccountConfig: ({ cfg, accountId }) => {
1378
- if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return {
1379
- ...cfg,
1380
- channels: {
1381
- ...cfg.channels,
1382
- feishu: {
1383
- ...cfg.channels?.feishu,
1384
- enabled: true
1385
- }
1386
- }
1387
- };
1369
+ if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return setSetupChannelEnabled(cfg, "feishu", true);
1388
1370
  return setFeishuNamedAccountEnabled(cfg, accountId, true);
1389
1371
  }
1390
1372
  };
@@ -1900,7 +1882,7 @@ const meta = {
1900
1882
  order: 70,
1901
1883
  preferSessionLookupForAnnounceTarget: true
1902
1884
  };
1903
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CAD74kBO.js"), "feishuChannelRuntime");
1885
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BZzuDlOS.js"), "feishuChannelRuntime");
1904
1886
  async function resolveFeishuMessageSender(params) {
1905
1887
  try {
1906
1888
  const sender = params.resolve(await loadFeishuChannelRuntime());
@@ -2490,7 +2472,7 @@ const feishuPlugin = createChatChannelPlugin({
2490
2472
  interactive
2491
2473
  })
2492
2474
  }) : void 0;
2493
- const presentationCard = generatedCard && isFeishuCardWithinEnvelope(generatedCard) ? generatedCard : void 0;
2475
+ const presentationCard = generatedCard && feishuCardWithinTableLimit(generatedCard) && isFeishuCardWithinEnvelope(generatedCard) ? generatedCard : void 0;
2494
2476
  const presentationFellBack = Boolean(generatedCard && !presentationCard);
2495
2477
  const card = presentation ? presentationCard : textCard;
2496
2478
  if (card && mediaUrl) throw new Error(`Feishu ${ctx.action} does not support card with media.`);
@@ -3027,7 +3009,7 @@ const feishuPlugin = createChatChannelPlugin({
3027
3009
  })
3028
3010
  }),
3029
3011
  gateway: { startAccount: async (ctx) => {
3030
- const { monitorFeishuProvider } = await import("./monitor-rPJ_Mz4M.js");
3012
+ const { monitorFeishuProvider } = await import("./monitor-DSGtOPio.js");
3031
3013
  const account = resolveFeishuRuntimeAccount({
3032
3014
  cfg: ctx.cfg,
3033
3015
  accountId: ctx.accountId
@@ -1,2 +1,2 @@
1
- import { t as feishuPlugin } from "./channel-VytDDz_B.js";
1
+ import { t as feishuPlugin } from "./channel-DrAqd5ED.js";
2
2
  export { feishuPlugin };
@@ -1,8 +1,8 @@
1
1
  import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
2
- import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-VytDDz_B.js";
3
- import { C as editMessageFeishu, D as sendMessageFeishu, E as sendCardFeishu, I as assertFeishuApiSuccess, b as sendStickerFeishu, w as getMessageFeishu } from "./reply-delivery-result-CYaKkbUN.js";
2
+ import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-DrAqd5ED.js";
3
+ import { E as getMessageFeishu, O as sendCardFeishu, S as sendStickerFeishu, T as editMessageFeishu, k as sendMessageFeishu, z as assertFeishuApiSuccess } from "./reply-delivery-result-BXEmtKpf.js";
4
4
  import { r as createFeishuClient } from "./client-DMbIL3UH.js";
5
- import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-DDhIcPQy.js";
5
+ import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-BZSnEtgb.js";
6
6
  import { t as probeFeishu } from "./probe-D_SatdZl.js";
7
7
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
8
8
  //#region extensions/feishu/src/directory.ts
@@ -1,4 +1,4 @@
1
- import { Et as resolveFeishuChatType, H as feishuExternalToolResult, K as resolveAnyEnabledFeishuToolsConfig, Q as formatFeishuApiError, dt as authorizeFeishuChatMemberRead, gt as resolveFeishuChatReadPreliminaryAuthorization, q as resolveFeishuToolAccount, ut as assertFeishuChatReadAllowed } from "./reply-delivery-result-CYaKkbUN.js";
1
+ import { G as feishuExternalToolResult, X as resolveFeishuToolAccount, Y as resolveAnyEnabledFeishuToolsConfig, kt as resolveFeishuChatType, mt as authorizeFeishuChatMemberRead, pt as assertFeishuChatReadAllowed, tt as formatFeishuApiError, yt as resolveFeishuChatReadPreliminaryAuthorization } from "./reply-delivery-result-BXEmtKpf.js";
2
2
  import { r as createFeishuClient } from "./client-DMbIL3UH.js";
3
3
  import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
4
4
  import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
@@ -143,7 +143,7 @@ async function fetchBotIdentityForMonitor(account, options = {}) {
143
143
  }
144
144
  //#endregion
145
145
  //#region extensions/feishu/src/monitor.ts
146
- const loadMonitorAccountRuntime = createLazyRuntimeModule(() => import("./monitor.account-v0XIDyfs.js"));
146
+ const loadMonitorAccountRuntime = createLazyRuntimeModule(() => import("./monitor.account-BRDUsS_q.js"));
147
147
  async function monitorFeishuProvider(opts = {}) {
148
148
  const cfg = opts.config;
149
149
  if (!cfg) throw new Error("Config is required for Feishu monitor");
@@ -1,16 +1,16 @@
1
1
  import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
2
2
  import { i as resolveReceiveIdType } from "./targets-BTQCYRZQ.js";
3
- import { $ as parseCommentContentElements, A as isFeishuBroadcastMention, At as decodeFeishuCardAction, B as deliverCommentThreadText, D as sendMessageFeishu, E as sendCardFeishu, Et as resolveFeishuChatType, L as buildFeishuMediaFallbackText, M as isFeishuGroupChatType, N as parseInteractiveCardContent, O as sendStructuredCardFeishu, Ot as buildFeishuCardActionTextFallback, P as parsePostContent, R as resolveFeishuIdentityEmoji, S as chunkFeishuCardMarkdown, St as resolveFeishuGroupSenderActivationIngressAccess, T as listFeishuThreadMessages, Tt as normalizeFeishuChatType$2, X as encodeQuery, Y as createCommentTypingReactionLifecycle, Z as extractReplyText, _ as resolveFeishuRichReply, _t as hasExplicitFeishuGroupConfig, at as resolveFeishuCardTemplate, bt as resolveFeishuGroupConfig, ct as materializeFeishuPostMarkdownSoftBreaks, et as requestFeishuApi, g as renderFeishuReplyPayload, i as noVisibleFeishuReplyDelivery, j as isMentionForwardRequest, k as extractMentionTargets, kt as createFeishuCardInteractionEnvelope, l as buildFeishuPresentationFallback, n as createFeishuReplyDeliveryResult, nt as normalizeCommentFileType, r as mergeFeishuReplyDeliveryResults, st as chunkFeishuPostMarkdown, t as createFeishuPartialReplyDeliveryError, tt as buildFeishuCommentTarget, u as consumeFeishuPresentationFallbackMarker, v as saveMessageResourceFeishu, vt as normalizeFeishuAllowEntry, w as getMessageFeishu, wt as resolveFeishuReplyPolicy, x as shouldSuppressFeishuTextForVoiceMedia, xt as resolveFeishuGroupConversationIngressAccess, y as sendMediaFeishu, yt as resolveFeishuDmIngressAccess } from "./reply-delivery-result-CYaKkbUN.js";
3
+ import { $ as encodeQuery, A as sendStructuredCardFeishu, B as buildFeishuMediaFallbackText, C as shouldSuppressFeishuTextForVoiceMedia, Ct as resolveFeishuGroupConfig, D as listFeishuThreadMessages, Dt as resolveFeishuReplyPolicy, E as getMessageFeishu, F as isFeishuGroupChatType, I as parseInteractiveCardContent, L as parsePostContent, M as extractMentionTargets, Mt as createFeishuCardInteractionEnvelope, N as isFeishuBroadcastMention, Nt as decodeFeishuCardAction, O as sendCardFeishu, Ot as normalizeFeishuChatType$2, P as isMentionForwardRequest, Q as createCommentTypingReactionLifecycle, St as resolveFeishuDmIngressAccess, Tt as resolveFeishuGroupSenderActivationIngressAccess, U as deliverCommentThreadText, V as resolveFeishuIdentityEmoji, _ as renderFeishuReplyPayload, at as normalizeCommentFileType, b as saveMessageResourceFeishu, bt as hasExplicitFeishuGroupConfig, ct as resolveFeishuCardTemplate, dt as materializeFeishuPostMarkdownSoftBreaks, et as extractReplyText, i as noVisibleFeishuReplyDelivery, it as buildFeishuCommentTarget, j as formatFeishuMediaContent, jt as buildFeishuCardActionTextFallback, k as sendMessageFeishu, kt as resolveFeishuChatType, l as buildFeishuPresentationFallback, n as createFeishuReplyDeliveryResult, nt as parseCommentContentElements, r as mergeFeishuReplyDeliveryResults, rt as requestFeishuApi, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as chunkFeishuPostMarkdown, v as resolveFeishuRichReply, w as chunkFeishuCardMarkdown, wt as resolveFeishuGroupConversationIngressAccess, x as sendMediaFeishu, xt as normalizeFeishuAllowEntry, y as withinCardTableLimit } from "./reply-delivery-result-BXEmtKpf.js";
4
4
  import { a as normalizeFeishuWebhookPath, o as normalizeFeishuExternalKey } from "./doctor-contract-w6FpMypv.js";
5
5
  import { o as resolveConfiguredFeishuGroupSessionScope, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
6
6
  import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient, s as resolveConfiguredHttpTimeoutMs } from "./client-DMbIL3UH.js";
7
7
  import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
8
- import { r as getChatInfo } from "./chat-DDhIcPQy.js";
8
+ import { r as getChatInfo } from "./chat-BZSnEtgb.js";
9
9
  import { t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
10
10
  import { evaluateSupplementalContextVisibility, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode } from "./runtime-api.js";
11
11
  import { t as readFeishuJsonResponse } from "./json-response-DXajw_FU.js";
12
12
  import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-D_SatdZl.js";
13
- import { t as fetchBotIdentityForMonitor } from "./monitor-rPJ_Mz4M.js";
13
+ import { t as fetchBotIdentityForMonitor } from "./monitor-DSGtOPio.js";
14
14
  import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
15
15
  import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createReplyPrefixContext, formatChannelProgressDraftLineForEntry, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
16
16
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
@@ -32,11 +32,11 @@ import { resolvePinnedMainDmOwnerFromAllowlist, safeEqualSecret } from "openclaw
32
32
  import * as Lark from "@larksuiteoapi/node-sdk";
33
33
  import { isRecord as isRecord$1 } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
34
34
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
35
+ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
35
36
  import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
36
37
  import * as crypto$1 from "node:crypto";
37
38
  import crypto, { createHash } from "node:crypto";
38
39
  import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
39
- import { escapeHtml, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
40
40
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
41
41
  import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
42
42
  import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
@@ -337,56 +337,6 @@ const FEISHU_MEDIA_MESSAGE_TYPES = /* @__PURE__ */ new Set([
337
337
  "media",
338
338
  "sticker"
339
339
  ]);
340
- function formatFeishuMediaContent(parsed, messageType) {
341
- if (messageType === "sticker") {
342
- const fileKey = normalizeFeishuExternalKey(parsed?.file_key);
343
- return fileKey ? `<sticker key="${escapeHtml(fileKey)}"/>` : "[Sticker]";
344
- }
345
- const speechToText = messageType === "audio" && typeof parsed.speech_to_text === "string" ? parsed.speech_to_text.trim() : "";
346
- if (speechToText) return speechToText;
347
- return "";
348
- }
349
- function formatSubMessageContent(content, contentType) {
350
- try {
351
- const parsed = JSON.parse(content);
352
- switch (contentType) {
353
- case "text": return parsed.text || content;
354
- case "post": return parsePostContent(content).textContent;
355
- case "interactive": return parseInteractiveCardContent(parsed);
356
- case "image": return "[Image]";
357
- case "file": return `[File: ${parsed.file_name || "unknown"}]`;
358
- case "audio": return "[Audio]";
359
- case "video": return "[Video]";
360
- case "sticker": return formatFeishuMediaContent(parsed, contentType);
361
- case "merge_forward": return "[Nested Merged Forward]";
362
- default: return `[${contentType}]`;
363
- }
364
- } catch {
365
- return content;
366
- }
367
- }
368
- function parseMergeForwardContent(params) {
369
- const { content, log } = params;
370
- const maxMessages = 50;
371
- log?.("feishu: parsing merge_forward sub-messages from API response");
372
- let items;
373
- try {
374
- items = JSON.parse(content);
375
- } catch {
376
- log?.("feishu: merge_forward items parse failed");
377
- return "[Merged and Forwarded Message - parse error]";
378
- }
379
- if (!Array.isArray(items) || items.length === 0) return "[Merged and Forwarded Message - no sub-messages]";
380
- const container = items.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id);
381
- const subMessages = container ? items.filter((item) => item !== container) : items.filter((item) => item.upper_message_id);
382
- if (subMessages.length === 0) return "[Merged and Forwarded Message - no sub-messages found]";
383
- log?.(`feishu: merge_forward contains ${subMessages.length} sub-messages`);
384
- subMessages.sort((a, b) => (parseStrictNonNegativeInteger(a.create_time) ?? 0) - (parseStrictNonNegativeInteger(b.create_time) ?? 0));
385
- const lines = ["[Merged and Forwarded Messages]"];
386
- for (const item of subMessages.slice(0, maxMessages)) lines.push(`- ${formatSubMessageContent(item.body?.content || "", item.msg_type || "text")}`);
387
- if (subMessages.length > maxMessages) lines.push(`... and ${subMessages.length - maxMessages} more messages`);
388
- return lines.join("\n");
389
- }
390
340
  function checkBotMentioned(event, botOpenId) {
391
341
  if (!botOpenId) return false;
392
342
  const mentions = event.message.mentions ?? [];
@@ -398,13 +348,14 @@ function normalizeMentions(text, mentions, botStripId) {
398
348
  if (!mentions || mentions.length === 0) return text;
399
349
  const escaped = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
400
350
  const escapeName = (value) => value.replace(/</g, "&lt;").replace(/>/g, "&gt;");
401
- let result = text;
351
+ const replacements = /* @__PURE__ */ new Map();
402
352
  for (const mention of mentions) {
403
353
  const mentionId = mention.id.open_id;
404
354
  const replacement = botStripId && mentionId === botStripId ? "" : mentionId ? `<at user_id="${mentionId}">${escapeName(mention.name)}</at>` : `@${mention.name}`;
405
- result = result.replace(new RegExp(escaped(mention.key), "g"), () => replacement).trim();
355
+ replacements.set(mention.key, replacement);
406
356
  }
407
- return result;
357
+ const keys = [...replacements.keys()].toSorted((a, b) => b.length - a.length).map(escaped);
358
+ return text.replace(new RegExp(keys.join("|"), "g"), (key) => replacements.get(key)).trim();
408
359
  }
409
360
  function normalizeFeishuCommandProbeBody(text) {
410
361
  if (!text) return "";
@@ -440,12 +391,6 @@ function parseMediaKeys(content, messageType) {
440
391
  function toMessageResourceType(messageType) {
441
392
  return messageType === "image" ? "image" : "file";
442
393
  }
443
- async function resolveSavedFeishuMedia(params) {
444
- if ("saved" in params.result) return params.result.saved;
445
- const core = getFeishuRuntime();
446
- const contentType = params.result.contentType ?? await core.media.detectMime({ buffer: params.result.buffer });
447
- return await core.channel.media.saveMediaBuffer(params.result.buffer, contentType, "inbound", params.maxBytes, params.result.fileName ?? params.originalFilename);
448
- }
449
394
  function resolveFeishuMediaKind(messageType) {
450
395
  switch (messageType) {
451
396
  case "image": return "image";
@@ -479,16 +424,12 @@ async function resolveFeishuMediaList(params) {
479
424
  const fileName = attachment.kind === "file" ? attachment.fileName : void 0;
480
425
  const mediaKind = attachment.kind === "image" ? "image" : "video";
481
426
  try {
482
- const saved = await resolveSavedFeishuMedia({
483
- result: await saveMessageResourceFeishu({
484
- cfg,
485
- messageId,
486
- fileKey: attachment.key,
487
- type: attachment.kind,
488
- accountId,
489
- maxBytes,
490
- ...fileName ? { originalFilename: fileName } : {}
491
- }),
427
+ const { saved } = await saveMessageResourceFeishu({
428
+ cfg,
429
+ messageId,
430
+ fileKey: attachment.key,
431
+ type: attachment.kind,
432
+ accountId,
492
433
  maxBytes,
493
434
  ...fileName ? { originalFilename: fileName } : {}
494
435
  });
@@ -510,16 +451,12 @@ async function resolveFeishuMediaList(params) {
510
451
  try {
511
452
  const fileKey = mediaKeys.fileKey || mediaKeys.imageKey;
512
453
  if (!fileKey) return [{ kind: resolveFeishuMediaKind(messageType) }];
513
- const saved = await resolveSavedFeishuMedia({
514
- result: await saveMessageResourceFeishu({
515
- cfg,
516
- messageId,
517
- fileKey,
518
- type: toMessageResourceType(messageType),
519
- accountId,
520
- maxBytes,
521
- originalFilename: mediaKeys.fileName
522
- }),
454
+ const { saved } = await saveMessageResourceFeishu({
455
+ cfg,
456
+ messageId,
457
+ fileKey,
458
+ type: toMessageResourceType(messageType),
459
+ accountId,
523
460
  maxBytes,
524
461
  originalFilename: mediaKeys.fileName
525
462
  });
@@ -2288,14 +2225,15 @@ function createFeishuReplyDispatcher(params) {
2288
2225
  if (result?.visibleReplySent === true || !content?.trim()) return result;
2289
2226
  const cardHeader = resolveCardHeader(agentId, identity);
2290
2227
  const cardNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
2228
+ const useRecoveryCard = withinCardTableLimit(content);
2291
2229
  return await sendChunkedTextReply({
2292
2230
  text: content,
2293
- useCard: true,
2231
+ useCard: useRecoveryCard,
2294
2232
  infoKind,
2295
2233
  header: cardHeader,
2296
2234
  note: cardNote,
2297
2235
  chunkMentions: requiredMentionTargets,
2298
- sendChunk: async ({ chunk, mentions }) => await sendStructuredCardFeishu({
2236
+ sendChunk: async ({ chunk, mentions }) => useRecoveryCard ? await sendStructuredCardFeishu({
2299
2237
  cfg,
2300
2238
  to: sendTarget,
2301
2239
  text: chunk,
@@ -2306,6 +2244,16 @@ function createFeishuReplyDispatcher(params) {
2306
2244
  header: cardHeader,
2307
2245
  note: cardNote,
2308
2246
  ...mentions ? { mentions } : {}
2247
+ }) : await sendMessageFeishu({
2248
+ cfg,
2249
+ to: sendTarget,
2250
+ text: chunk,
2251
+ preparedPostText: true,
2252
+ replyToMessageId: sendReplyToMessageId,
2253
+ replyInThread: effectiveReplyInThread,
2254
+ allowTopLevelReplyFallback,
2255
+ accountId,
2256
+ ...mentions ? { mentions } : {}
2309
2257
  })
2310
2258
  });
2311
2259
  };
@@ -2483,9 +2431,9 @@ function createFeishuReplyDispatcher(params) {
2483
2431
  ttsSupplement
2484
2432
  }));
2485
2433
  const finalTextExceedsStreamingLimit = info?.kind === "final" && hasText && text.length > textChunkLimit;
2486
- const useStaticCard = hasText && (renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text));
2487
- const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || useStaticCard);
2488
- const useCard = useStaticCard || useStreamingCard;
2434
+ const cardRenderingRequested = renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text);
2435
+ const useStaticCard = hasText && cardRenderingRequested && withinCardTableLimit(text);
2436
+ const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || cardRenderingRequested);
2489
2437
  const skipTextForDuplicateFinal = !hasIndependentPresentation && info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
2490
2438
  const shouldDeliverText = hasText && (!hasVoiceMedia || hasPresentationFallback) && !skipTextForDuplicateFinal;
2491
2439
  const shouldDiscardStreamingPreview = info?.kind === "final" && !(hasIndependentPresentation && payload.isError === true && hasStreamingFinalText) && (hasIndependentPresentation || finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
@@ -2577,7 +2525,7 @@ function createFeishuReplyDispatcher(params) {
2577
2525
  }
2578
2526
  return deferStreamingDelivery(mergeFeishuReplyDeliveryResults(deliveredResults, text), info?.kind, ownerGeneration);
2579
2527
  }
2580
- if (useCard) {
2528
+ if (useStaticCard || useStreamingCard && !isStreamingStartBackedOff(account.accountId) && withinCardTableLimit(text)) {
2581
2529
  const cardHeader = resolveCardHeader(agentId, identity);
2582
2530
  const cardNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
2583
2531
  deliveredResults.push(await sendChunkedTextReply({
@@ -2704,11 +2652,10 @@ async function resolveFeishuAudioTranscript(params) {
2704
2652
  /**
2705
2653
  * Parse an inbound Feishu event into its caption and routing metadata.
2706
2654
  */
2707
- function parseFeishuMessageEvent(event, botOpenId, _botName) {
2708
- const rawContent = parseMessageContent(event.message.content, event.message.message_type);
2655
+ function parseFeishuMessageEvent(event, botOpenId, _botName, preparedContent) {
2709
2656
  const mentionedBot = checkBotMentioned(event, botOpenId);
2710
2657
  const hasAnyMention = (event.message.mentions?.length ?? 0) > 0;
2711
- const content = normalizeMentions(rawContent, event.message.mentions, botOpenId);
2658
+ const content = preparedContent ?? normalizeMentions(parseMessageContent(event.message.content, event.message.message_type), event.message.mentions, botOpenId);
2712
2659
  const senderOpenId = event.sender.sender_id.open_id?.trim();
2713
2660
  const senderUserId = event.sender.sender_id.user_id?.trim();
2714
2661
  const senderFallbackId = senderOpenId || senderUserId || "";
@@ -2770,7 +2717,8 @@ async function filterFetchedGroupContextMessages(messages, params) {
2770
2717
  }) ? message : void 0))).filter((message) => message !== void 0);
2771
2718
  }
2772
2719
  async function handleFeishuMessage(params) {
2773
- const { cfg, event, botOpenId, botName, runtime, channelRuntime, chatHistories, accountId, processingClaim, messageDedupeKey: messageDedupeKeyOverride, turnAdoptionLifecycle } = params;
2720
+ const { event, preparedContent, botOpenId, botName, runtime, channelRuntime, chatHistories, accountId, processingClaim, messageDedupeKey: messageDedupeKeyOverride, turnAdoptionLifecycle } = params;
2721
+ const cfg = getFeishuRuntime().config.current();
2774
2722
  const account = resolveFeishuRuntimeAccount({
2775
2723
  cfg,
2776
2724
  accountId
@@ -2789,7 +2737,7 @@ async function handleFeishuMessage(params) {
2789
2737
  log(`feishu: skipping duplicate message ${messageId}`);
2790
2738
  return;
2791
2739
  }
2792
- let ctx = parseFeishuMessageEvent(event, botOpenId, botName);
2740
+ let ctx = parseFeishuMessageEvent(event, botOpenId, botName, preparedContent);
2793
2741
  const isGroup = isFeishuGroupChatType(ctx.chatType);
2794
2742
  const isDirect = !isGroup;
2795
2743
  const directPreDispatchTarget = isDirect ? getFeishuSyntheticDirectPreDispatchTarget(event) : void 0;
@@ -2839,7 +2787,7 @@ async function handleFeishuMessage(params) {
2839
2787
  log(`feishu[${account.accountId}]: dropping bot message ${ctx.messageId} (local mention not verifiable)`);
2840
2788
  return;
2841
2789
  }
2842
- const deliveredCtx = parseFeishuMessageEvent(verifiedEvent, localBotOpenId, botName);
2790
+ const deliveredCtx = parseFeishuMessageEvent(verifiedEvent, localBotOpenId, botName, preparedContent);
2843
2791
  ctx = {
2844
2792
  ...deliveredCtx,
2845
2793
  mentionedBot: true,
@@ -2851,22 +2799,17 @@ async function handleFeishuMessage(params) {
2851
2799
  if (event.message.message_type === "merge_forward") {
2852
2800
  log(`feishu[${account.accountId}]: processing merge_forward message, fetching full content via API`);
2853
2801
  try {
2854
- const response = await createFeishuClient(account).im.message.get({
2855
- params: { card_msg_content_type: "user_card_content" },
2856
- path: { message_id: event.message.message_id }
2802
+ const messageInfo = await getMessageFeishu({
2803
+ cfg,
2804
+ messageId: event.message.message_id,
2805
+ accountId: account.accountId
2857
2806
  });
2858
- if (response.code === 0 && response.data?.items && response.data.items.length > 0) {
2859
- log(`feishu[${account.accountId}]: merge_forward API returned ${response.data.items.length} items`);
2860
- const expandedContent = parseMergeForwardContent({
2861
- content: JSON.stringify(response.data.items),
2862
- log
2863
- });
2864
- ctx = {
2865
- ...ctx,
2866
- content: expandedContent
2867
- };
2868
- } else {
2869
- log(`feishu[${account.accountId}]: merge_forward API returned no items`);
2807
+ if (messageInfo) ctx = {
2808
+ ...ctx,
2809
+ content: messageInfo.content
2810
+ };
2811
+ else {
2812
+ log(`feishu[${account.accountId}]: merge_forward message retrieval returned no result`);
2870
2813
  ctx = {
2871
2814
  ...ctx,
2872
2815
  content: "[Merged and Forwarded Message - could not fetch]"
@@ -6140,10 +6083,11 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
6140
6083
  const enqueue = createSequentialQueue({ onTaskTimeout: (key, timeoutMs) => {
6141
6084
  log(`feishu[${accountId}]: per-chat task exceeded ${timeoutMs}ms cap (key=${key}); evicting from queue so later same-key messages can proceed (#70133)`);
6142
6085
  } });
6143
- const dispatchFeishuMessage = async (event, messageDedupeKey, processingClaim, turnAdoptionLifecycle) => {
6086
+ const dispatchFeishuMessage = async (event, messageDedupeKey, processingClaim, turnAdoptionLifecycle, preparedContent) => {
6144
6087
  const sequentialKey = resolveSequentialKey({
6145
6088
  accountId,
6146
6089
  event,
6090
+ preparedContent,
6147
6091
  botOpenId: getBotOpenId(accountId),
6148
6092
  botName: getBotName(accountId)
6149
6093
  });
@@ -6155,6 +6099,7 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
6155
6099
  await handleMessage({
6156
6100
  cfg,
6157
6101
  event,
6102
+ preparedContent,
6158
6103
  botOpenId: getBotOpenId(accountId),
6159
6104
  botName: getBotName(accountId),
6160
6105
  runtime,
@@ -6245,13 +6190,9 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
6245
6190
  ...dispatchEntry.event,
6246
6191
  message: {
6247
6192
  ...dispatchEntry.event.message,
6248
- ...combinedText.trim() ? {
6249
- message_type: "text",
6250
- content: JSON.stringify({ text: combinedText })
6251
- } : {},
6252
6193
  mentions: mergedMentions ?? dispatchEntry.event.message.mentions
6253
6194
  }
6254
- }, dispatchDedupeKey, dispatchEntry.processingClaim, admissionLifecycle);
6195
+ }, dispatchDedupeKey, dispatchEntry.processingClaim, admissionLifecycle, combinedText);
6255
6196
  await settle();
6256
6197
  } catch (err) {
6257
6198
  await admissionLifecycle.onAbandoned();
@@ -6822,9 +6763,9 @@ function createFeishuVcMeetingInvitedHandler(params) {
6822
6763
  //#endregion
6823
6764
  //#region extensions/feishu/src/sequential-key.ts
6824
6765
  function getFeishuSequentialKey(params) {
6825
- const { accountId, event, botOpenId, botName } = params;
6766
+ const { accountId, event, botOpenId, botName, preparedContent } = params;
6826
6767
  const baseKey = `feishu:${accountId}:${event.message.chat_id?.trim() || "unknown"}`;
6827
- const text = parseFeishuMessageEvent(event, botOpenId, botName).content.trim();
6768
+ const text = (preparedContent ?? parseFeishuMessageEvent(event, botOpenId, botName).content).trim();
6828
6769
  if (isAbortRequestText(text)) return `${baseKey}:control`;
6829
6770
  if (isBtwRequestText(text)) return `${baseKey}:btw`;
6830
6771
  return baseKey;
@@ -11,7 +11,7 @@ import { PlatformMessageNotDispatchedError, formatErrorMessage } from "openclaw/
11
11
  import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, renderPresentationForDelivery, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
12
12
  import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
13
13
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
14
- import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
14
+ import { convertMarkdownTables, markdownToIRWithMeta } from "openclaw/plugin-sdk/text-chunking";
15
15
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
16
16
  import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
17
17
  import fs from "node:fs";
@@ -20,7 +20,7 @@ import { fromMarkdown } from "mdast-util-from-markdown";
20
20
  import { gfmTableFromMarkdown } from "mdast-util-gfm-table";
21
21
  import { gfmTable } from "micromark-extension-gfm-table";
22
22
  import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
23
- import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
23
+ import { createAcceptedChannelDeliveryResult, createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
24
24
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
25
25
  import { readRegularFile, wrapExternalContent, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
26
26
  import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
@@ -34,6 +34,7 @@ import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, buildOutboundMediaLoadOptions, ru
34
34
  import { saveMediaBuffer, saveMediaStream } from "openclaw/plugin-sdk/media-store";
35
35
  import { resolvePreferredOpenClawTmpDir, withTempDownloadPath, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
36
36
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
37
+ import { escapeHtml, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
37
38
  //#region extensions/feishu/src/card-interaction.ts
38
39
  const FEISHU_CARD_INTERACTION_VERSION = "ocf1";
39
40
  function isInteractionKind(value) {
@@ -2282,6 +2283,57 @@ function buildMentionedCardContent(targets, message) {
2282
2283
  return `${targets.map((t) => formatMentionForCard(t)).join(" ")} ${message}`;
2283
2284
  }
2284
2285
  //#endregion
2286
+ //#region extensions/feishu/src/message-content.ts
2287
+ function formatFeishuMediaContent(parsed, messageType) {
2288
+ if (messageType === "sticker") {
2289
+ const fileKey = normalizeFeishuExternalKey(parsed?.file_key);
2290
+ return fileKey ? `<sticker key="${escapeHtml(fileKey)}"/>` : "[Sticker]";
2291
+ }
2292
+ const speechToText = messageType === "audio" && typeof parsed.speech_to_text === "string" ? parsed.speech_to_text.trim() : "";
2293
+ if (speechToText) return speechToText;
2294
+ return "";
2295
+ }
2296
+ function formatSubMessageContent(content, contentType) {
2297
+ try {
2298
+ const parsed = JSON.parse(content);
2299
+ switch (contentType) {
2300
+ case "text": return parsed.text || content;
2301
+ case "post": return parsePostContent(content).textContent;
2302
+ case "interactive": return parseInteractiveCardContent(parsed);
2303
+ case "image": return "[Image]";
2304
+ case "file": return `[File: ${parsed.file_name || "unknown"}]`;
2305
+ case "audio": return "[Audio]";
2306
+ case "video": return "[Video]";
2307
+ case "sticker": return formatFeishuMediaContent(parsed, contentType);
2308
+ case "merge_forward": return "[Nested Merged Forward]";
2309
+ default: return `[${contentType}]`;
2310
+ }
2311
+ } catch {
2312
+ return content;
2313
+ }
2314
+ }
2315
+ function parseMergeForwardContent(params) {
2316
+ const { content } = params;
2317
+ const maxMessages = 50;
2318
+ let items;
2319
+ try {
2320
+ items = JSON.parse(content);
2321
+ } catch {
2322
+ return "[Merged and Forwarded Message - parse error]";
2323
+ }
2324
+ if (!Array.isArray(items) || items.length === 0) return "[Merged and Forwarded Message - no sub-messages]";
2325
+ const container = items.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id);
2326
+ const subMessages = container ? items.filter((item) => item !== container) : items.filter((item) => item.upper_message_id);
2327
+ if (subMessages.length === 0) return "[Merged and Forwarded Message - no sub-messages found]";
2328
+ subMessages.sort((a, b) => (parseStrictNonNegativeInteger(a.create_time) ?? 0) - (parseStrictNonNegativeInteger(b.create_time) ?? 0));
2329
+ const lines = ["[Merged and Forwarded Messages]"];
2330
+ for (const item of subMessages.slice(0, maxMessages)) lines.push(`- ${formatSubMessageContent(item.body?.content || "", item.msg_type || "text")}`);
2331
+ if (subMessages.length > maxMessages) lines.push(`... and ${subMessages.length - maxMessages} more messages`);
2332
+ const rendered = lines.join("\n");
2333
+ return rendered.length <= 2e4 ? rendered : `${truncateUtf16Safe(rendered, 19961).trimEnd()}
2334
+ ... [Merged-forward content truncated]`;
2335
+ }
2336
+ //#endregion
2285
2337
  //#region extensions/feishu/src/send.ts
2286
2338
  const WITHDRAWN_REPLY_ERROR_CODES = /* @__PURE__ */ new Set([230011, 231003]);
2287
2339
  function shouldFallbackFromReplyTarget(response) {
@@ -2396,10 +2448,16 @@ async function getMessageFeishu(params) {
2396
2448
  path: { message_id: messageId }
2397
2449
  });
2398
2450
  if (response.code !== 0) return null;
2399
- const rawItem = response.data?.items?.[0] ?? response.data;
2451
+ const responseItems = response.data?.items;
2452
+ const rawItem = responseItems?.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id) ?? responseItems?.[0] ?? response.data;
2400
2453
  const item = rawItem && (rawItem.body !== void 0 || rawItem.message_id !== void 0) ? rawItem : null;
2401
2454
  if (!item) return null;
2402
- return parseFeishuMessageItem(item, messageId);
2455
+ const parsedItem = parseFeishuMessageItem(item, messageId);
2456
+ if (parsedItem.contentType === "merge_forward" && responseItems) return {
2457
+ ...parsedItem,
2458
+ content: parseMergeForwardContent({ content: JSON.stringify(responseItems) })
2459
+ };
2460
+ return parsedItem;
2403
2461
  } catch {
2404
2462
  return null;
2405
2463
  }
@@ -3292,6 +3350,28 @@ function isFeishuCardWithinEnvelope(card) {
3292
3350
  function assertFeishuCardWithinEnvelope(card, label = "Feishu card") {
3293
3351
  if (!isFeishuCardWithinEnvelope(card)) throw new Error(`${label} exceeds the 30 KB or 200-element API limit.`);
3294
3352
  }
3353
+ /** Feishu allows at most five table components per static interactive card. */
3354
+ const FEISHU_CARD_TABLE_LIMIT = 5;
3355
+ function countMarkdownTables(text) {
3356
+ return text ? markdownToIRWithMeta(text, { tableMode: "block" }).tables.length : 0;
3357
+ }
3358
+ function withinCardTableLimit(text) {
3359
+ return countMarkdownTables(text) <= FEISHU_CARD_TABLE_LIMIT;
3360
+ }
3361
+ function collectFeishuCardMarkdownTexts(value, output) {
3362
+ if (Array.isArray(value)) {
3363
+ for (const item of value) collectFeishuCardMarkdownTexts(item, output);
3364
+ return;
3365
+ }
3366
+ if (!isRecord(value)) return;
3367
+ if (value.tag === "markdown" && typeof value.content === "string") output.push(value.content);
3368
+ for (const child of Object.values(value)) collectFeishuCardMarkdownTexts(child, output);
3369
+ }
3370
+ function feishuCardWithinTableLimit(card) {
3371
+ const markdownTexts = [];
3372
+ collectFeishuCardMarkdownTexts(card, markdownTexts);
3373
+ return markdownTexts.reduce((total, text) => total + countMarkdownTables(text), 0) <= FEISHU_CARD_TABLE_LIMIT;
3374
+ }
3295
3375
  function resolveFeishuButtonUrl(button) {
3296
3376
  if (button.action?.type === "url" || button.action?.type === "web-app") return button.action.url;
3297
3377
  if (button.action) return;
@@ -3477,8 +3557,11 @@ function buildFeishuPayloadCard(params) {
3477
3557
  }, ...card.body.elements]
3478
3558
  }
3479
3559
  };
3480
- if (isNativeCard) assertFeishuCardWithinEnvelope(card, "Feishu native card");
3481
- return isFeishuCardWithinEnvelope(card) ? markRenderedFeishuCard(card) : void 0;
3560
+ if (isNativeCard) {
3561
+ assertFeishuCardWithinEnvelope(card, "Feishu native card");
3562
+ return markRenderedFeishuCard(card);
3563
+ }
3564
+ return isFeishuCardWithinEnvelope(card) && feishuCardWithinTableLimit(card) ? markRenderedFeishuCard(card) : void 0;
3482
3565
  }
3483
3566
  function renderFeishuPresentationPayload({ payload, presentation, sourcePresentation, ctx }) {
3484
3567
  const card = buildFeishuPayloadCard({
@@ -3549,20 +3632,15 @@ function hasProviderIdentity(result) {
3549
3632
  /** Normalizes every physical Lark send behind one logical reply payload. */
3550
3633
  function createFeishuReplyDeliveryResult(params) {
3551
3634
  const results = params.visibleReplySent ? (params.results ?? []).filter(hasProviderIdentity) : [];
3552
- const receipt = results.length > 0 ? createMessageReceiptFromOutboundResults({
3553
- results,
3554
- ...params.kind ? { kind: params.kind } : {}
3555
- }) : void 0;
3556
- if (!receipt) return {
3557
- visibleReplySent: params.visibleReplySent,
3558
- ...params.content === void 0 ? {} : { content: params.content }
3559
- };
3560
- return {
3561
- messageIds: [...receipt.platformMessageIds],
3562
- receipt,
3635
+ if (results.length === 0) return {
3563
3636
  visibleReplySent: params.visibleReplySent,
3564
3637
  ...params.content === void 0 ? {} : { content: params.content }
3565
3638
  };
3639
+ return createAcceptedChannelDeliveryResult({
3640
+ results,
3641
+ kind: params.kind,
3642
+ content: params.content
3643
+ });
3566
3644
  }
3567
3645
  /** Preserves the first result's provider identity while retaining supplemental ids. */
3568
3646
  function mergeFeishuReplyDeliveryResults(results, content) {
@@ -3583,4 +3661,4 @@ function createFeishuPartialReplyDeliveryError(cause, result) {
3583
3661
  });
3584
3662
  }
3585
3663
  //#endregion
3586
- export { parseCommentContentElements as $, isFeishuBroadcastMention as A, decodeFeishuCardAction as At, deliverCommentThreadText as B, editMessageFeishu as C, resolveFeishuGroupToolPolicy as Ct, sendMessageFeishu as D, FEISHU_CARD_INTERACTION_VERSION as Dt, sendCardFeishu as E, resolveFeishuChatType as Et, createFeishuSendReceipt as F, createFeishuToolClient as G, feishuExternalToolResult as H, assertFeishuApiSuccess as I, cleanupAmbientCommentTypingReaction as J, resolveAnyEnabledFeishuToolsConfig as K, buildFeishuMediaFallbackText as L, isFeishuGroupChatType as M, parseInteractiveCardContent as N, sendStructuredCardFeishu as O, buildFeishuCardActionTextFallback as Ot, parsePostContent as P, formatFeishuApiError as Q, resolveFeishuIdentityEmoji as R, chunkFeishuCardMarkdown as S, resolveFeishuGroupSenderActivationIngressAccess as St, listFeishuThreadMessages as T, normalizeFeishuChatType as Tt, toolExecutionErrorResult as U, registerFeishuDriveTools as V, unknownToolActionResult as W, encodeQuery as X, createCommentTypingReactionLifecycle as Y, extractReplyText as Z, resolveFeishuRichReply as _, hasExplicitFeishuGroupConfig as _t, FEISHU_PRESENTATION_CAPABILITIES as a, resolveFeishuCardTemplate as at, sendStickerFeishu as b, resolveFeishuGroupConfig as bt, buildFeishuPresentationCard as c, materializeFeishuPostMarkdownSoftBreaks as ct, isFeishuCardWithinEnvelope as d, authorizeFeishuChatMemberRead as dt, requestFeishuApi as et, markRenderedFeishuCard as f, canEnumerateAllFeishuGroups as ft, renderFeishuReplyPayload as g, resolveFeishuChatReadPreliminaryAuthorization as gt, renderFeishuPresentationPayload as h, isFeishuGroupReadEnabled as ht, noVisibleFeishuReplyDelivery as i, readNativeFeishuCardJson as it, isMentionForwardRequest as j, extractMentionTargets as k, createFeishuCardInteractionEnvelope as kt, buildFeishuPresentationFallback as l, parseFeishuMarkdown as lt, renderFeishuPresentationFallbackText as m, isFeishuGroupReadAllowed as mt, createFeishuReplyDeliveryResult as n, normalizeCommentFileType as nt, assertFeishuCardWithinEnvelope as o, chunkFeishuMarkdown as ot, readNativeFeishuCard as p, canEnumerateAllFeishuPeers as pt, resolveFeishuToolAccount as q, mergeFeishuReplyDeliveryResults as r, parseFeishuCommentTarget as rt, buildFeishuPayloadCard as s, chunkFeishuPostMarkdown as st, createFeishuPartialReplyDeliveryError as t, buildFeishuCommentTarget as tt, consumeFeishuPresentationFallbackMarker as u, assertFeishuChatReadAllowed as ut, saveMessageResourceFeishu as v, normalizeFeishuAllowEntry as vt, getMessageFeishu as w, resolveFeishuReplyPolicy as wt, shouldSuppressFeishuTextForVoiceMedia as x, resolveFeishuGroupConversationIngressAccess as xt, sendMediaFeishu as y, resolveFeishuDmIngressAccess as yt, resolveFeishuIdentityHeaderTitle as z };
3664
+ export { encodeQuery as $, sendStructuredCardFeishu as A, FEISHU_CARD_INTERACTION_VERSION as At, buildFeishuMediaFallbackText as B, shouldSuppressFeishuTextForVoiceMedia as C, resolveFeishuGroupConfig as Ct, listFeishuThreadMessages as D, resolveFeishuReplyPolicy as Dt, getMessageFeishu as E, resolveFeishuGroupToolPolicy as Et, isFeishuGroupChatType as F, feishuExternalToolResult as G, resolveFeishuIdentityHeaderTitle as H, parseInteractiveCardContent as I, createFeishuToolClient as J, toolExecutionErrorResult as K, parsePostContent as L, extractMentionTargets as M, createFeishuCardInteractionEnvelope as Mt, isFeishuBroadcastMention as N, decodeFeishuCardAction as Nt, sendCardFeishu as O, normalizeFeishuChatType as Ot, isMentionForwardRequest as P, createCommentTypingReactionLifecycle as Q, createFeishuSendReceipt as R, sendStickerFeishu as S, resolveFeishuDmIngressAccess as St, editMessageFeishu as T, resolveFeishuGroupSenderActivationIngressAccess as Tt, deliverCommentThreadText as U, resolveFeishuIdentityEmoji as V, registerFeishuDriveTools as W, resolveFeishuToolAccount as X, resolveAnyEnabledFeishuToolsConfig as Y, cleanupAmbientCommentTypingReaction as Z, renderFeishuReplyPayload as _, isFeishuGroupReadAllowed as _t, FEISHU_PRESENTATION_CAPABILITIES as a, normalizeCommentFileType as at, saveMessageResourceFeishu as b, hasExplicitFeishuGroupConfig as bt, buildFeishuPresentationCard as c, resolveFeishuCardTemplate as ct, feishuCardWithinTableLimit as d, materializeFeishuPostMarkdownSoftBreaks as dt, extractReplyText as et, isFeishuCardWithinEnvelope as f, parseFeishuMarkdown as ft, renderFeishuPresentationPayload as g, canEnumerateAllFeishuPeers as gt, renderFeishuPresentationFallbackText as h, canEnumerateAllFeishuGroups as ht, noVisibleFeishuReplyDelivery as i, buildFeishuCommentTarget as it, formatFeishuMediaContent as j, buildFeishuCardActionTextFallback as jt, sendMessageFeishu as k, resolveFeishuChatType as kt, buildFeishuPresentationFallback as l, chunkFeishuMarkdown as lt, readNativeFeishuCard as m, authorizeFeishuChatMemberRead as mt, createFeishuReplyDeliveryResult as n, parseCommentContentElements as nt, assertFeishuCardWithinEnvelope as o, parseFeishuCommentTarget as ot, markRenderedFeishuCard as p, assertFeishuChatReadAllowed as pt, unknownToolActionResult as q, mergeFeishuReplyDeliveryResults as r, requestFeishuApi as rt, buildFeishuPayloadCard as s, readNativeFeishuCardJson as st, createFeishuPartialReplyDeliveryError as t, formatFeishuApiError as tt, consumeFeishuPresentationFallbackMarker as u, chunkFeishuPostMarkdown as ut, resolveFeishuRichReply as v, isFeishuGroupReadEnabled as vt, chunkFeishuCardMarkdown as w, resolveFeishuGroupConversationIngressAccess as wt, sendMediaFeishu as x, normalizeFeishuAllowEntry as xt, withinCardTableLimit as y, resolveFeishuChatReadPreliminaryAuthorization as yt, assertFeishuApiSuccess as z };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-VytDDz_B.js";
1
+ import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DrAqd5ED.js";
2
2
  export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/feishu",
3
- "version": "2026.9.2",
3
+ "version": "2026.9.3",
4
4
  "description": "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,7 @@
16
16
  "zod": "4.4.3"
17
17
  },
18
18
  "peerDependencies": {
19
- "openclaw": ">=2026.9.2"
19
+ "openclaw": ">=2026.9.3"
20
20
  },
21
21
  "peerDependenciesMeta": {
22
22
  "openclaw": {
@@ -62,11 +62,11 @@
62
62
  "minHostVersion": ">=2026.5.29"
63
63
  },
64
64
  "compat": {
65
- "pluginApi": ">=2026.9.2"
65
+ "pluginApi": ">=2026.9.3"
66
66
  },
67
67
  "build": {
68
68
  "bundledDist": false,
69
- "openclawVersion": "2026.9.2"
69
+ "openclawVersion": "2026.9.3"
70
70
  },
71
71
  "release": {
72
72
  "publishToClawHub": true,