@openclaw/feishu 2026.7.2-beta.7 → 2026.8.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/accounts-CCCdMen2.js +203 -0
  2. package/dist/api.js +52 -51
  3. package/dist/{channel-CScE82zY.js → channel-Dzb5jv3i.js} +53 -30
  4. package/dist/channel-plugin-api.js +1 -1
  5. package/dist/{channel.runtime-B21e1E0r.js → channel.runtime-CoPaHQQ6.js} +181 -142
  6. package/dist/{client-Dcbs6vml.js → client-Hp7uo_cl.js} +27 -16
  7. package/dist/contract-api.js +2 -2
  8. package/dist/{conversation-id-BeJL-wq7.js → conversation-id-VYgGQ-GX.js} +11 -15
  9. package/dist/doctor-contract-api.js +1 -1
  10. package/dist/doctor-contract-bEQXIXyP.js +158 -0
  11. package/dist/{drive-hSi_Utp0.js → drive-B0JkoiRj.js} +318 -41
  12. package/dist/{media-CEUraFOR.js → media-BjSy8fiy.js} +298 -182
  13. package/dist/{monitor-DngbaA6a.js → monitor-CeHCfROt.js} +3 -3
  14. package/dist/{monitor.account-BnTWCw55.js → monitor.account-DwdqexLU.js} +237 -158
  15. package/dist/{monitor.startup-CqH2tiJq.js → monitor.startup-BGErejNH.js} +1 -1
  16. package/dist/{probe-p3POS2RN.js → probe-DVpy58s0.js} +2 -2
  17. package/dist/security-audit-D6Fz2h6p.js +23 -0
  18. package/dist/{send-result-B9_BpUPx.js → send-result-DEAycmOk.js} +18 -9
  19. package/dist/session-binding-contract-api.js +1 -1
  20. package/dist/{session-conversation-BksWrfzm.js → session-conversation-DFCIvQK-.js} +1 -1
  21. package/dist/session-key-api.js +1 -1
  22. package/dist/setup-api.js +1 -1
  23. package/dist/{subagent-hooks-Cx1cX7rW.js → subagent-hooks-B867acTt.js} +2 -2
  24. package/dist/subagent-hooks-api.js +1 -1
  25. package/dist/{thread-bindings-N3wkgkIN.js → thread-bindings-Itvfmx6_.js} +6 -3
  26. package/openclaw.plugin.json +143 -1
  27. package/package.json +4 -4
  28. package/skills/feishu-doc/SKILL.md +20 -195
  29. package/skills/feishu-doc/references/block-types.md +9 -14
  30. package/skills/feishu-drive/SKILL.md +16 -102
  31. package/skills/feishu-perm/SKILL.md +10 -110
  32. package/skills/feishu-wiki/SKILL.md +13 -109
  33. package/dist/accounts-u9X5Wsan.js +0 -469
  34. package/dist/doctor-contract-BiD9tyIv.js +0 -102
  35. package/dist/security-audit-D7WK_BHh.js +0 -11
@@ -1,20 +1,21 @@
1
- import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount, v as parseFeishuCommentTarget } from "./accounts-u9X5Wsan.js";
2
- import { c as isFeishuCardWithinEnvelope, l as listFeishuDirectoryGroups, o as assertFeishuCardWithinEnvelope, s as buildFeishuPresentationCardElements, u as listFeishuDirectoryPeers } from "./channel-CScE82zY.js";
3
- import { a as readNativeFeishuCardJson, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, o as resolveFeishuCardTemplate, s as sanitizeNativeFeishuCard, u as chunkFeishuMarkdown } from "./send-result-B9_BpUPx.js";
4
- import { r as createFeishuClient } from "./client-Dcbs6vml.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-hSi_Utp0.js";
6
- import { _ as buildFeishuMediaFallbackText, a as getMessageFeishu, c as sendMarkdownCardFeishu, i as editMessageFeishu, l as sendMessageFeishu, n as sendMediaFeishu, r as shouldSuppressFeishuTextForVoiceMedia, s as sendCardFeishu, u as sendStructuredCardFeishu, y as resolveFeishuIdentityHeaderTitle } from "./media-CEUraFOR.js";
7
- import { t as probeFeishu } from "./probe-p3POS2RN.js";
1
+ import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-CCCdMen2.js";
2
+ import { c as isFeishuCardWithinEnvelope, l as listFeishuDirectoryGroups, o as assertFeishuCardWithinEnvelope, s as buildFeishuPresentationCardElements, u as listFeishuDirectoryPeers } from "./channel-Dzb5jv3i.js";
3
+ import { a as readNativeFeishuCardJson, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, o as resolveFeishuCardTemplate, s as sanitizeNativeFeishuCard, u as chunkFeishuMarkdown } from "./send-result-DEAycmOk.js";
4
+ import { r as createFeishuClient } from "./client-Hp7uo_cl.js";
5
+ import { _ as parseFeishuCommentTarget, a as assertFeishuChatMember, c as getChatMembers, l as getFeishuMemberInfo, o as buildFeishuDirectChatMembers, r as cleanupAmbientCommentTypingReaction, s as getChatInfo, t as deliverCommentThreadText } from "./drive-B0JkoiRj.js";
6
+ import { a as getMessageFeishu, b as resolveFeishuIdentityHeaderTitle, c as sendMarkdownCardFeishu, i as editMessageFeishu, l as sendMessageFeishu, n as sendMediaFeishu, r as shouldSuppressFeishuTextForVoiceMedia, s as sendCardFeishu, u as sendStructuredCardFeishu, v as buildFeishuMediaFallbackText } from "./media-BjSy8fiy.js";
7
+ import { t as probeFeishu } from "./probe-DVpy58s0.js";
8
8
  import { createReplyToFanout } from "openclaw/plugin-sdk/channel-outbound";
9
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
+ import { isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
15
+ import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
14
16
  import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
15
17
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
16
18
  import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
17
- import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
18
19
  //#region extensions/feishu/src/directory.ts
19
20
  const MAX_FEISHU_DIRECTORY_PAGES = 100;
20
21
  async function listFeishuDirectoryPeersLive(params) {
@@ -111,6 +112,8 @@ function normalizePossibleLocalImagePath(text) {
111
112
  ".webp",
112
113
  ".bmp",
113
114
  ".ico",
115
+ ".heic",
116
+ ".tif",
114
117
  ".tiff"
115
118
  ].includes(ext)) return null;
116
119
  if (!path.isAbsolute(raw)) return null;
@@ -140,6 +143,10 @@ function readNativeFeishuCard(payload) {
140
143
  const sanitizedCard = sanitizeNativeFeishuCard(card);
141
144
  return sanitizedCard ? markRenderedFeishuCard(sanitizedCard) : void 0;
142
145
  }
146
+ async function reportFeishuOutboundDelivery(result, onDeliveryResult) {
147
+ await onDeliveryResult?.(attachChannelToResult("feishu", result));
148
+ return result;
149
+ }
143
150
  function consumeFeishuPresentationFallbackMarker(payload) {
144
151
  const feishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
145
152
  if (feishuData?.[FEISHU_PRESENTATION_FALLBACK_MARKER] !== true) return {
@@ -295,7 +302,7 @@ async function sendCommentThreadReply(params) {
295
302
  }
296
303
  }
297
304
  async function sendOutboundText(params) {
298
- const { cfg, to, text, accountId, replyToMessageId, replyInThread } = params;
305
+ const { cfg, to, text, accountId, replyToMessageId, replyInThread, onDeliveryResult } = params;
299
306
  const commentResult = await sendCommentThreadReply({
300
307
  cfg,
301
308
  to,
@@ -303,19 +310,19 @@ async function sendOutboundText(params) {
303
310
  replyId: replyToMessageId,
304
311
  accountId
305
312
  });
306
- if (commentResult) return commentResult;
313
+ if (commentResult) return await reportFeishuOutboundDelivery(commentResult, onDeliveryResult);
307
314
  const renderMode = resolveFeishuAccount({
308
315
  cfg,
309
316
  accountId
310
317
  }).config?.renderMode ?? "auto";
311
- if (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) return sendMarkdownCardFeishu({
318
+ if (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) return await reportFeishuOutboundDelivery(await sendMarkdownCardFeishu({
312
319
  cfg,
313
320
  to,
314
321
  text,
315
322
  accountId,
316
323
  replyToMessageId,
317
324
  replyInThread
318
- });
325
+ }), onDeliveryResult);
319
326
  const normalizedText = materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, resolveMarkdownTableMode({
320
327
  cfg,
321
328
  channel: "feishu"
@@ -325,24 +332,21 @@ async function sendOutboundText(params) {
325
332
  limit: resolveTextChunkLimit(cfg, "feishu", accountId, { fallbackLimit: FEISHU_TEXT_CHUNK_LIMIT }),
326
333
  mode: resolveChunkMode(cfg, "feishu", accountId)
327
334
  });
328
- if (subChunks.length <= 1) return sendMessageFeishu({
329
- cfg,
330
- to,
331
- text: subChunks[0] ?? normalizedText,
332
- accountId,
333
- replyToMessageId,
334
- replyInThread
335
- });
336
335
  let lastResult;
337
336
  const preserveThread = replyInThread === true;
338
- for (const [i, chunk] of subChunks.entries()) lastResult = await sendMessageFeishu({
337
+ const nextReplyToMessageId = createReplyToFanout({
338
+ replyToId: replyToMessageId,
339
+ replyToIdSource: params.replyToIdSource,
340
+ replyToMode: params.replyToMode ?? "first"
341
+ });
342
+ for (const [i, chunk] of (subChunks.length ? subChunks : [normalizedText]).entries()) lastResult = await reportFeishuOutboundDelivery(await sendMessageFeishu({
339
343
  cfg,
340
344
  to,
341
345
  text: chunk,
342
346
  accountId,
343
- replyToMessageId: preserveThread || i === 0 ? replyToMessageId : void 0,
347
+ replyToMessageId: preserveThread ? replyToMessageId : nextReplyToMessageId(),
344
348
  replyInThread: preserveThread ? true : i === 0 ? replyInThread : void 0
345
- });
349
+ }), onDeliveryResult);
346
350
  return lastResult;
347
351
  }
348
352
  async function sendFeishuFallbackPayload(params) {
@@ -371,26 +375,18 @@ async function sendFeishuFallbackPayload(params) {
371
375
  const sendText = feishuOutbound.sendText;
372
376
  if (!sendMedia || !sendText) throw new Error("Feishu fallback delivery is not available.");
373
377
  let lastResult;
374
- for (const mediaUrl of mediaUrls) {
375
- lastResult = await sendMedia({
376
- ...ctx,
377
- text: "",
378
- mediaUrl,
379
- replyToId: nextReplyToId(),
380
- audioAsVoice: params.payload.audioAsVoice ?? ctx.audioAsVoice,
381
- onDeliveryResult: void 0
382
- });
383
- await ctx.onDeliveryResult?.(lastResult);
384
- }
385
- for (const chunk of textChunks) {
386
- lastResult = await sendText({
387
- ...ctx,
388
- text: chunk,
389
- replyToId: nextReplyToId(),
390
- onDeliveryResult: void 0
391
- });
392
- await ctx.onDeliveryResult?.(lastResult);
393
- }
378
+ for (const mediaUrl of mediaUrls) lastResult = await sendMedia({
379
+ ...ctx,
380
+ text: "",
381
+ mediaUrl,
382
+ replyToId: nextReplyToId(),
383
+ audioAsVoice: params.payload.audioAsVoice ?? ctx.audioAsVoice
384
+ });
385
+ for (const chunk of textChunks) lastResult = await sendText({
386
+ ...ctx,
387
+ text: chunk,
388
+ replyToId: nextReplyToId()
389
+ });
394
390
  return lastResult;
395
391
  }
396
392
  async function sendFeishuTtsSupplementPayload(params) {
@@ -416,15 +412,11 @@ async function sendFeishuTtsSupplementPayload(params) {
416
412
  await ctx.onDeliveryResult?.(lastResult);
417
413
  } else if (params.supplement.visibleTextAlreadyDelivered !== true) {
418
414
  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
- }
415
+ for (const chunk of chunkFeishuMarkdown(text, FEISHU_TEXT_CHUNK_LIMIT)) lastResult = await sendText({
416
+ ...ctx,
417
+ text: chunk,
418
+ replyToId: nextReplyToId()
419
+ });
428
420
  }
429
421
  for (const mediaUrl of normalizeStringEntries(resolvePayloadMediaUrls(params.payload))) lastResult = await sendMedia({
430
422
  ...ctx,
@@ -563,7 +555,9 @@ const feishuOutbound = {
563
555
  to: ctx.to,
564
556
  mediaUrl,
565
557
  accountId: ctx.accountId ?? void 0,
558
+ mediaAccess: ctx.mediaAccess,
566
559
  mediaLocalRoots: ctx.mediaLocalRoots,
560
+ mediaReadFile: ctx.mediaReadFile,
567
561
  replyToMessageId,
568
562
  replyInThread,
569
563
  ...payload.audioAsVoice === true || ctx.audioAsVoice === true ? { audioAsVoice: true } : {}
@@ -584,32 +578,45 @@ const feishuOutbound = {
584
578
  },
585
579
  ...createAttachedChannelResultAdapter({
586
580
  channel: "feishu",
587
- sendText: async ({ cfg, to, text, accountId, replyToId, threadId, mediaLocalRoots, identity }) => {
581
+ sendText: async ({ cfg, to, text, accountId, replyToId, replyToIdSource, replyToMode, threadId, mediaAccess, mediaLocalRoots, mediaReadFile, identity, onDeliveryResult }) => {
588
582
  const { replyToMessageId, replyInThread } = resolveFeishuReplyMode({
589
583
  replyToId,
590
584
  threadId
591
585
  });
586
+ const deliveryOptions = {
587
+ replyToIdSource,
588
+ replyToMode,
589
+ onDeliveryResult
590
+ };
592
591
  const localImagePath = normalizePossibleLocalImagePath(text);
593
- if (localImagePath) try {
594
- return await sendMediaFeishu({
595
- cfg,
596
- to,
597
- mediaUrl: localImagePath,
598
- accountId: accountId ?? void 0,
599
- replyToMessageId,
600
- replyInThread,
601
- mediaLocalRoots
602
- });
603
- } catch (err) {
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
- });
592
+ if (localImagePath) {
593
+ let mediaResult;
594
+ try {
595
+ mediaResult = await sendMediaFeishu({
596
+ cfg,
597
+ to,
598
+ mediaUrl: localImagePath,
599
+ accountId: accountId ?? void 0,
600
+ replyToMessageId,
601
+ replyInThread,
602
+ mediaAccess,
603
+ mediaLocalRoots,
604
+ mediaReadFile
605
+ });
606
+ } catch (err) {
607
+ if (isChannelPartialDeliveryError(err)) throw err;
608
+ console.error(`[feishu] local image path auto-send failed:`, err);
609
+ return await sendOutboundText({
610
+ cfg,
611
+ to,
612
+ text: await buildFeishuMediaFallbackText({}),
613
+ accountId: accountId ?? void 0,
614
+ replyToMessageId,
615
+ replyInThread,
616
+ ...deliveryOptions
617
+ });
618
+ }
619
+ return await reportFeishuOutboundDelivery(mediaResult, onDeliveryResult);
613
620
  }
614
621
  if (parseFeishuCommentTarget(to)) return await sendOutboundText({
615
622
  cfg,
@@ -617,19 +624,20 @@ const feishuOutbound = {
617
624
  text,
618
625
  accountId: accountId ?? void 0,
619
626
  replyToMessageId,
620
- replyInThread
627
+ replyInThread,
628
+ ...deliveryOptions
621
629
  });
622
630
  const card = readNativeFeishuCardJson(text);
623
631
  if (card) {
624
632
  assertFeishuCardWithinEnvelope(card, "Feishu native card");
625
- return await sendCardFeishu({
633
+ return await reportFeishuOutboundDelivery(await sendCardFeishu({
626
634
  cfg,
627
635
  to,
628
636
  card: markRenderedFeishuCard(card),
629
637
  accountId: accountId ?? void 0,
630
638
  replyToMessageId,
631
639
  replyInThread
632
- });
640
+ }), onDeliveryResult);
633
641
  }
634
642
  const renderMode = resolveFeishuAccount({
635
643
  cfg,
@@ -640,7 +648,7 @@ const feishuOutbound = {
640
648
  title: resolveFeishuIdentityHeaderTitle(identity),
641
649
  template: "blue"
642
650
  } : void 0;
643
- return await sendStructuredCardFeishu({
651
+ return await reportFeishuOutboundDelivery(await sendStructuredCardFeishu({
644
652
  cfg,
645
653
  to,
646
654
  text,
@@ -648,7 +656,7 @@ const feishuOutbound = {
648
656
  replyInThread,
649
657
  accountId: accountId ?? void 0,
650
658
  header: header?.title ? header : void 0
651
- });
659
+ }), onDeliveryResult);
652
660
  }
653
661
  return await sendOutboundText({
654
662
  cfg,
@@ -656,14 +664,35 @@ const feishuOutbound = {
656
664
  text,
657
665
  accountId: accountId ?? void 0,
658
666
  replyToMessageId,
659
- replyInThread
667
+ replyInThread,
668
+ ...deliveryOptions
660
669
  });
661
670
  },
662
- sendMedia: async ({ cfg, to, text, mediaUrl, audioAsVoice, accountId, mediaLocalRoots, replyToId, threadId, onDeliveryResult }) => {
663
- const { replyToMessageId, replyInThread } = resolveFeishuReplyMode({
671
+ sendMedia: async ({ cfg, to, text, mediaUrl, audioAsVoice, accountId, mediaAccess, mediaLocalRoots, mediaReadFile, replyToId, replyToIdSource, replyToMode, threadId, onDeliveryResult }) => {
672
+ const { normalizedReplyToId } = resolveFeishuReplyMode({
664
673
  replyToId,
665
674
  threadId
666
675
  });
676
+ const nextReplyToId = createReplyToFanout({
677
+ replyToId: normalizedReplyToId,
678
+ replyToIdSource,
679
+ replyToMode
680
+ });
681
+ const nextReplyMode = () => {
682
+ const { replyToMessageId, replyInThread } = resolveFeishuReplyMode({
683
+ replyToId: nextReplyToId(),
684
+ threadId
685
+ });
686
+ return {
687
+ replyToMessageId,
688
+ replyInThread
689
+ };
690
+ };
691
+ const deliveryOptions = {
692
+ replyToIdSource,
693
+ replyToMode,
694
+ onDeliveryResult
695
+ };
667
696
  if (parseFeishuCommentTarget(to)) return await sendOutboundText({
668
697
  cfg,
669
698
  to,
@@ -673,77 +702,72 @@ const feishuOutbound = {
673
702
  mediaLinkStyle: "plain"
674
703
  }) : text?.trim() ?? "",
675
704
  accountId: accountId ?? void 0,
676
- replyToMessageId,
677
- replyInThread
705
+ ...nextReplyMode(),
706
+ ...deliveryOptions
678
707
  });
679
- const suppressTextForVoiceMedia = mediaUrl !== void 0 && shouldSuppressFeishuTextForVoiceMedia({
708
+ if (!mediaUrl) return await sendOutboundText({
709
+ cfg,
710
+ to,
711
+ text: text ?? "",
712
+ accountId: accountId ?? void 0,
713
+ ...nextReplyMode(),
714
+ ...deliveryOptions
715
+ });
716
+ const suppressTextForVoiceMedia = shouldSuppressFeishuTextForVoiceMedia({
680
717
  mediaUrl,
681
718
  audioAsVoice
682
719
  });
683
- const reportDelivery = async (result) => {
684
- await onDeliveryResult?.(attachChannelToResult("feishu", result));
685
- };
686
720
  let textSent = false;
687
721
  if (text?.trim() && !suppressTextForVoiceMedia) {
688
- const textResult = await sendOutboundText({
722
+ await sendOutboundText({
689
723
  cfg,
690
724
  to,
691
725
  text,
692
726
  accountId: accountId ?? void 0,
693
- replyToMessageId,
694
- replyInThread
727
+ ...nextReplyMode(),
728
+ ...deliveryOptions
695
729
  });
696
730
  textSent = true;
697
- await reportDelivery(textResult);
698
731
  }
699
- if (mediaUrl) {
700
- let mediaResult;
701
- try {
702
- mediaResult = await sendMediaFeishu({
703
- cfg,
704
- to,
705
- mediaUrl,
706
- accountId: accountId ?? void 0,
707
- mediaLocalRoots,
708
- replyToMessageId,
709
- replyInThread,
710
- ...audioAsVoice === true ? { audioAsVoice: true } : {}
711
- });
712
- } catch (err) {
713
- console.error(`[feishu] sendMediaFeishu failed:`, err);
714
- const fallbackResult = await sendOutboundText({
715
- cfg,
716
- to,
717
- text: await buildFeishuMediaFallbackText({
718
- text: textSent ? void 0 : text,
719
- mediaUrl
720
- }),
721
- accountId: accountId ?? void 0,
722
- replyToMessageId,
723
- replyInThread
724
- });
725
- await reportDelivery(fallbackResult);
726
- return fallbackResult;
727
- }
728
- await onDeliveryResult?.(attachChannelToResult("feishu", mediaResult));
729
- if (mediaResult.voiceIntentDegradedToFile && text?.trim()) await reportDelivery(await sendOutboundText({
732
+ let mediaResult;
733
+ const mediaReplyMode = nextReplyMode();
734
+ try {
735
+ mediaResult = await sendMediaFeishu({
730
736
  cfg,
731
737
  to,
732
- text,
738
+ mediaUrl,
733
739
  accountId: accountId ?? void 0,
734
- replyToMessageId,
735
- replyInThread
736
- }));
737
- return mediaResult;
740
+ mediaAccess,
741
+ mediaLocalRoots,
742
+ mediaReadFile,
743
+ ...mediaReplyMode,
744
+ ...audioAsVoice === true ? { audioAsVoice: true } : {}
745
+ });
746
+ } catch (err) {
747
+ if (isChannelPartialDeliveryError(err)) throw err;
748
+ console.error(`[feishu] sendMediaFeishu failed:`, err);
749
+ return await sendOutboundText({
750
+ cfg,
751
+ to,
752
+ text: await buildFeishuMediaFallbackText({
753
+ text: textSent ? void 0 : text,
754
+ mediaUrl
755
+ }),
756
+ accountId: accountId ?? void 0,
757
+ ...textSent ? nextReplyMode() : mediaReplyMode,
758
+ ...deliveryOptions
759
+ });
738
760
  }
739
- return await sendOutboundText({
761
+ await reportFeishuOutboundDelivery(mediaResult, onDeliveryResult);
762
+ if (mediaResult.voiceIntentDegradedToFile && text?.trim()) await sendOutboundText({
740
763
  cfg,
741
764
  to,
742
- text: text ?? "",
765
+ text,
743
766
  accountId: accountId ?? void 0,
744
- replyToMessageId,
745
- replyInThread
767
+ ...nextReplyMode(),
768
+ ...deliveryOptions
746
769
  });
770
+ return mediaResult;
747
771
  }
748
772
  })
749
773
  };
@@ -847,20 +871,35 @@ async function removeReactionFeishu(params) {
847
871
  */
848
872
  async function listReactionsFeishu(params) {
849
873
  const { cfg, messageId, emojiType, accountId } = params;
850
- const response = await resolveConfiguredFeishuClient({
874
+ const client = resolveConfiguredFeishuClient({
851
875
  cfg,
852
876
  accountId
853
- }).im.messageReaction.list({
854
- path: { message_id: messageId },
855
- params: emojiType ? { reaction_type: emojiType } : void 0
856
877
  });
857
- assertFeishuReactionApiSuccess(response, "list reactions");
858
- return (response.data?.items ?? []).map((item) => ({
859
- reactionId: item.reaction_id ?? "",
860
- emojiType: item.reaction_type?.emoji_type ?? "",
861
- operatorType: item.operator?.operator_type === "app" ? "app" : item.operator?.operator_type === "user" ? "user" : "unknown",
862
- operatorId: item.operator?.operator_id ?? ""
863
- }));
878
+ const reactions = [];
879
+ const seenPageTokens = /* @__PURE__ */ new Set();
880
+ let pageToken;
881
+ while (true) {
882
+ const response = await client.im.messageReaction.list({
883
+ path: { message_id: messageId },
884
+ params: emojiType || pageToken ? {
885
+ ...emojiType ? { reaction_type: emojiType } : {},
886
+ ...pageToken ? { page_token: pageToken } : {}
887
+ } : void 0
888
+ });
889
+ assertFeishuReactionApiSuccess(response, "list reactions");
890
+ for (const item of response.data?.items ?? []) reactions.push({
891
+ reactionId: item.reaction_id ?? "",
892
+ emojiType: item.reaction_type?.emoji_type ?? "",
893
+ operatorType: item.operator?.operator_type === "app" ? "app" : item.operator?.operator_type === "user" ? "user" : "unknown",
894
+ operatorId: item.operator?.operator_id ?? ""
895
+ });
896
+ if (response.data?.has_more !== true) return reactions;
897
+ const nextPageToken = response.data.page_token?.trim();
898
+ if (!nextPageToken) throw new Error("Feishu reaction pagination is missing its next page token");
899
+ if (seenPageTokens.has(nextPageToken)) throw new Error("Feishu reaction pagination returned a repeated page token");
900
+ seenPageTokens.add(nextPageToken);
901
+ pageToken = nextPageToken;
902
+ }
864
903
  }
865
904
  //#endregion
866
905
  //#region extensions/feishu/src/channel.runtime.ts
@@ -33,6 +33,7 @@ var client_exports = /* @__PURE__ */ __exportAll({
33
33
  getFeishuUserAgent: () => getFeishuUserAgent
34
34
  });
35
35
  const FEISHU_USER_AGENT = `openclaw-feishu-builtin/${readPluginPackageVersion({ require: createRequire(import.meta.url) })}/${process.platform}`;
36
+ const FEISHU_SDK_ORIGIN = "https://open.feishu.cn";
36
37
  const FEISHU_WS_CONFIG = { pingTimeout: 3 };
37
38
  /** User-Agent header value for all Feishu API requests. */
38
39
  function getFeishuUserAgent() {
@@ -145,10 +146,8 @@ async function getFeishuProxyAgent() {
145
146
  }
146
147
  }
147
148
  const clientCache = /* @__PURE__ */ new Map();
148
- function resolveDomain(domain) {
149
- if (domain === "lark") return feishuClientSdk.Domain.Lark;
150
- if (domain === "feishu" || !domain) return feishuClientSdk.Domain.Feishu;
151
- return domain.replace(/\/+$/, "");
149
+ function resolveSdkDomain(domain) {
150
+ return domain === "lark" ? feishuClientSdk.Domain.Lark : feishuClientSdk.Domain.Feishu;
152
151
  }
153
152
  /**
154
153
  * Create an HTTP instance that delegates to the Lark SDK's default instance
@@ -156,13 +155,25 @@ function resolveDomain(domain) {
156
155
  * indefinite hangs, set a standardized User-Agent per OAPI best practices, and
157
156
  * keep axios from taking a separate ambient proxy path for HTTPS requests.
158
157
  */
159
- function createFeishuHttpInstance(defaultTimeoutMs) {
158
+ function createFeishuHttpInstance(defaultTimeoutMs, configuredDomain) {
160
159
  const base = feishuClientSdk.defaultHttpInstance;
160
+ const customDomain = configuredDomain && configuredDomain !== "feishu" && configuredDomain !== "lark" ? new URL(configuredDomain) : void 0;
161
+ function resolveRequestUrl(url) {
162
+ if (!customDomain) return url;
163
+ const requestUrl = new URL(url);
164
+ if (requestUrl.origin !== FEISHU_SDK_ORIGIN) return url;
165
+ const destination = new URL(customDomain);
166
+ destination.pathname = `${destination.pathname.replace(/\/+$/, "")}${requestUrl.pathname}`;
167
+ destination.search = requestUrl.search;
168
+ destination.hash = requestUrl.hash;
169
+ return destination.toString();
170
+ }
161
171
  async function injectRequestOptions(opts) {
162
172
  const next = {
163
173
  timeout: defaultTimeoutMs,
164
174
  ...opts
165
175
  };
176
+ if (typeof next.url === "string") next.url = resolveRequestUrl(next.url);
166
177
  const agent = await getFeishuProxyAgent();
167
178
  if (agent) {
168
179
  if (isManagedProxyActive()) {
@@ -178,13 +189,13 @@ function createFeishuHttpInstance(defaultTimeoutMs) {
178
189
  }
179
190
  return {
180
191
  request: async (opts) => base.request(await injectRequestOptions(normalizeMultipartUploadData(opts))),
181
- get: async (url, opts) => base.get(url, await injectRequestOptions(opts)),
182
- post: async (url, data, opts) => base.post(url, data, await injectRequestOptions(opts)),
183
- put: async (url, data, opts) => base.put(url, data, await injectRequestOptions(opts)),
184
- patch: async (url, data, opts) => base.patch(url, data, await injectRequestOptions(opts)),
185
- delete: async (url, opts) => base.delete(url, await injectRequestOptions(opts)),
186
- head: async (url, opts) => base.head(url, await injectRequestOptions(opts)),
187
- options: async (url, opts) => base.options(url, await injectRequestOptions(opts))
192
+ get: async (url, opts) => base.get(resolveRequestUrl(url), await injectRequestOptions(opts)),
193
+ post: async (url, data, opts) => base.post(resolveRequestUrl(url), data, await injectRequestOptions(opts)),
194
+ put: async (url, data, opts) => base.put(resolveRequestUrl(url), data, await injectRequestOptions(opts)),
195
+ patch: async (url, data, opts) => base.patch(resolveRequestUrl(url), data, await injectRequestOptions(opts)),
196
+ delete: async (url, opts) => base.delete(resolveRequestUrl(url), await injectRequestOptions(opts)),
197
+ head: async (url, opts) => base.head(resolveRequestUrl(url), await injectRequestOptions(opts)),
198
+ options: async (url, opts) => base.options(resolveRequestUrl(url), await injectRequestOptions(opts))
188
199
  };
189
200
  }
190
201
  /**
@@ -201,8 +212,8 @@ function createFeishuClient(creds) {
201
212
  appId,
202
213
  appSecret,
203
214
  appType: feishuClientSdk.AppType.SelfBuild,
204
- domain: resolveDomain(domain),
205
- httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs)
215
+ domain: resolveSdkDomain(domain),
216
+ httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs, domain)
206
217
  });
207
218
  clientCache.set(accountId, {
208
219
  client,
@@ -227,8 +238,8 @@ async function createFeishuWSClient(account, callbacks = {}) {
227
238
  return new feishuClientSdk.WSClient({
228
239
  appId,
229
240
  appSecret,
230
- domain: resolveDomain(domain),
231
- httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs),
241
+ domain: resolveSdkDomain(domain),
242
+ httpInstance: createFeishuHttpInstance(defaultHttpTimeoutMs, domain),
232
243
  ...callbacks,
233
244
  loggerLevel: feishuClientSdk.LoggerLevel.info,
234
245
  wsConfig: FEISHU_WS_CONFIG,
@@ -1,4 +1,4 @@
1
- import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-BeJL-wq7.js";
2
- import { t as messageActionTargetAliases } from "./security-audit-D7WK_BHh.js";
1
+ import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
2
+ import { t as messageActionTargetAliases } from "./security-audit-D6Fz2h6p.js";
3
3
  import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-BgpY7AiJ.js";
4
4
  export { buildFeishuConversationId, collectFeishuSecurityAuditFindings, messageActionTargetAliases, parseFeishuConversationId, parseFeishuDirectConversationId, parseFeishuTargetId };
@@ -1,17 +1,13 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  //#region extensions/feishu/src/conversation-id.ts
3
3
  function resolveConfiguredFeishuGroupSessionScope(params) {
4
4
  const legacyTopicSessionMode = params.groupConfig?.topicSessionMode ?? params.feishuCfg?.topicSessionMode ?? "disabled";
5
5
  return params.groupConfig?.groupSessionScope ?? params.feishuCfg?.groupSessionScope ?? (legacyTopicSessionMode === "enabled" ? "group_topic" : "group");
6
6
  }
7
- function normalizeText(value) {
8
- if (typeof value !== "string") return;
9
- return value.trim() || void 0;
10
- }
11
7
  function buildFeishuConversationId(params) {
12
- const chatId = normalizeText(params.chatId) ?? "unknown";
13
- const senderOpenId = normalizeText(params.senderOpenId);
14
- const topicId = normalizeText(params.topicId);
8
+ const chatId = normalizeOptionalString(params.chatId) ?? "unknown";
9
+ const senderOpenId = normalizeOptionalString(params.senderOpenId);
10
+ const topicId = normalizeOptionalString(params.topicId);
15
11
  switch (params.scope) {
16
12
  case "group_sender": return senderOpenId ? `${chatId}:sender:${senderOpenId}` : chatId;
17
13
  case "group_topic": return topicId ? `${chatId}:topic:${topicId}` : chatId;
@@ -23,7 +19,7 @@ function buildFeishuConversationId(params) {
23
19
  }
24
20
  }
25
21
  function parseFeishuTargetId(raw) {
26
- const target = normalizeText(raw);
22
+ const target = normalizeOptionalString(raw);
27
23
  if (!target) return;
28
24
  const withoutProvider = target.replace(/^(feishu|lark):/i, "").trim();
29
25
  if (!withoutProvider) return;
@@ -35,11 +31,11 @@ function parseFeishuTargetId(raw) {
35
31
  "user:",
36
32
  "dm:",
37
33
  "open_id:"
38
- ]) if (lowered.startsWith(prefix)) return normalizeText(withoutProvider.slice(prefix.length));
34
+ ]) if (lowered.startsWith(prefix)) return normalizeOptionalString(withoutProvider.slice(prefix.length));
39
35
  return withoutProvider;
40
36
  }
41
37
  function parseFeishuDirectConversationId(raw) {
42
- const target = normalizeText(raw);
38
+ const target = normalizeOptionalString(raw);
43
39
  if (!target) return;
44
40
  const withoutProvider = target.replace(/^(feishu|lark):/i, "").trim();
45
41
  if (!withoutProvider) return;
@@ -48,14 +44,14 @@ function parseFeishuDirectConversationId(raw) {
48
44
  "user:",
49
45
  "dm:",
50
46
  "open_id:"
51
- ]) if (lowered.startsWith(prefix)) return normalizeText(withoutProvider.slice(prefix.length));
47
+ ]) if (lowered.startsWith(prefix)) return normalizeOptionalString(withoutProvider.slice(prefix.length));
52
48
  const id = parseFeishuTargetId(target);
53
49
  if (!id) return;
54
50
  if (id.startsWith("ou_") || id.startsWith("on_")) return id;
55
51
  }
56
52
  function parseFeishuConversationId(params) {
57
- const conversationId = normalizeText(params.conversationId);
58
- const parentConversationId = normalizeText(params.parentConversationId);
53
+ const conversationId = normalizeOptionalString(params.conversationId);
54
+ const parentConversationId = normalizeOptionalString(params.parentConversationId);
59
55
  if (!conversationId) return null;
60
56
  const topicSenderMatch = conversationId.match(/^(.+):topic:([^:]+):sender:([^:]+)$/i);
61
57
  if (topicSenderMatch) {
@@ -121,7 +117,7 @@ function parseFeishuConversationId(params) {
121
117
  };
122
118
  }
123
119
  function buildFeishuModelOverrideParentCandidates(parentConversationId) {
124
- const rawId = normalizeText(parentConversationId);
120
+ const rawId = normalizeOptionalString(parentConversationId);
125
121
  if (!rawId) return [];
126
122
  const topicSenderMatch = rawId.match(/^(.+):topic:([^:]+):sender:([^:]+)$/i);
127
123
  if (topicSenderMatch) {