@openclaw/feishu 2026.5.9-beta.1 → 2026.5.10-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -2,7 +2,7 @@ import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as bu
2
2
  import { n as createFeishuThreadBindingManager, r as getFeishuThreadBindingManager, t as __testing } from "./thread-bindings-BmS6TLes.js";
3
3
  import { n as handleFeishuSubagentEnded, r as handleFeishuSubagentSpawning, t as handleFeishuSubagentDeliveryTarget } from "./subagent-hooks-C3UhPVLV.js";
4
4
  import { r as listEnabledFeishuAccounts } from "./accounts-Ba3-WP1z.js";
5
- import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-C5kY2KFE.js";
5
+ import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-DCSECj4a.js";
6
6
  import { t as getFeishuRuntime } from "./runtime-CG0DuRCy.js";
7
7
  import { a as jsonToolResult, d as registerFeishuChatTools, f as createFeishuToolClient, m as resolveFeishuToolAccount, n as registerFeishuDriveTools, o as toolExecutionErrorResult, p as resolveAnyEnabledFeishuToolsConfig, s as unknownToolActionResult } from "./drive-C5eJLJr7.js";
8
8
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/text-runtime";
@@ -4,7 +4,7 @@ import { a as resolveDefaultFeishuAccountId, f as isRecord$1, i as listFeishuAcc
4
4
  import { n as createFeishuSendReceipt } from "./send-result-BVFTskB_.js";
5
5
  import { n as listFeishuDirectoryPeers, t as listFeishuDirectoryGroups } from "./directory.static-f3EeoRJd.js";
6
6
  import { t as messageActionTargetAliases } from "./security-audit-DqJdocrN.js";
7
- import { a as resolveFeishuGroupToolPolicy } from "./policy-D6c-wMPl.js";
7
+ import { o as resolveFeishuGroupToolPolicy } from "./policy-DrLgxWF_.js";
8
8
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Dm4Z_zQN.js";
9
9
  import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-ByuMx9cJ.js";
10
10
  import { t as resolveFeishuSessionConversation } from "./session-conversation-B4nrW-vo.js";
@@ -811,7 +811,7 @@ const meta = {
811
811
  aliases: ["lark"],
812
812
  order: 70
813
813
  };
814
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BK6_CcZk.js"), "feishuChannelRuntime");
814
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BlDkd6xR.js"), "feishuChannelRuntime");
815
815
  function toFeishuMessageSendResult(result, kind) {
816
816
  const receipt = result.receipt ?? createFeishuSendReceipt({
817
817
  messageId: result.messageId,
@@ -957,6 +957,28 @@ function areAnyFeishuReactionActionsEnabled(cfg) {
957
957
  })) return true;
958
958
  return false;
959
959
  }
960
+ function isFeishuGroupTopicSessionKey(sessionKey) {
961
+ if (typeof sessionKey !== "string" || !sessionKey) return false;
962
+ const parsed = parseFeishuConversationId({ conversationId: sessionKey });
963
+ return parsed?.scope === "group_topic" || parsed?.scope === "group_topic_sender";
964
+ }
965
+ function resolveFeishuTopicAutoThreadAnchor(ctx) {
966
+ if (ctx.action !== "send") return;
967
+ if (!isFeishuGroupTopicSessionKey(ctx.sessionKey)) return;
968
+ const inbound = ctx.toolContext?.currentMessageId;
969
+ return typeof inbound === "string" && inbound.length > 0 ? inbound : void 0;
970
+ }
971
+ function buildFeishuSendReplyAnchor(ctx) {
972
+ if (ctx.action === "thread-reply") return {
973
+ replyToMessageId: resolveFeishuMessageId(ctx.params),
974
+ replyInThread: true
975
+ };
976
+ const autoThreadId = resolveFeishuTopicAutoThreadAnchor(ctx);
977
+ return {
978
+ replyToMessageId: autoThreadId,
979
+ replyInThread: autoThreadId !== void 0
980
+ };
981
+ }
960
982
  function isSupportedFeishuDirectConversationId(conversationId) {
961
983
  const trimmed = conversationId.trim();
962
984
  if (!trimmed || trimmed.includes(":")) return false;
@@ -1213,7 +1235,7 @@ const feishuPlugin = createChatChannelPlugin({
1213
1235
  if (ctx.action === "send" || ctx.action === "thread-reply") {
1214
1236
  const to = resolveFeishuActionTarget(ctx);
1215
1237
  if (!to) throw new Error(`Feishu ${ctx.action} requires a target (to).`);
1216
- const replyToMessageId = ctx.action === "thread-reply" ? resolveFeishuMessageId(ctx.params) : void 0;
1238
+ const { replyToMessageId, replyInThread } = buildFeishuSendReplyAnchor(ctx);
1217
1239
  if (ctx.action === "thread-reply" && !replyToMessageId) throw new Error("Feishu thread-reply requires messageId.");
1218
1240
  const presentation = normalizeMessagePresentation(ctx.params.presentation);
1219
1241
  const text = readFirstString(ctx.params, ["text", "message"]);
@@ -1238,7 +1260,7 @@ const feishuPlugin = createChatChannelPlugin({
1238
1260
  card,
1239
1261
  accountId: ctx.accountId ?? void 0,
1240
1262
  replyToMessageId,
1241
- replyInThread: ctx.action === "thread-reply"
1263
+ replyInThread
1242
1264
  });
1243
1265
  } else if (mediaUrl) result = await sendMedia({
1244
1266
  cfg: ctx.cfg,
@@ -1247,7 +1269,7 @@ const feishuPlugin = createChatChannelPlugin({
1247
1269
  mediaUrl,
1248
1270
  accountId: ctx.accountId ?? void 0,
1249
1271
  mediaLocalRoots: ctx.mediaLocalRoots,
1250
- replyToId: replyToMessageId,
1272
+ ...replyInThread ? { threadId: replyToMessageId } : { replyToId: replyToMessageId },
1251
1273
  ...audioAsVoice === true ? { audioAsVoice: true } : {}
1252
1274
  });
1253
1275
  else result = await runtime.sendMessageFeishu({
@@ -1256,7 +1278,7 @@ const feishuPlugin = createChatChannelPlugin({
1256
1278
  text,
1257
1279
  accountId: ctx.accountId ?? void 0,
1258
1280
  replyToMessageId,
1259
- replyInThread: ctx.action === "thread-reply"
1281
+ replyInThread
1260
1282
  });
1261
1283
  return jsonActionResult({
1262
1284
  ok: true,
@@ -1632,7 +1654,7 @@ const feishuPlugin = createChatChannelPlugin({
1632
1654
  })
1633
1655
  }),
1634
1656
  gateway: { startAccount: async (ctx) => {
1635
- const { monitorFeishuProvider } = await import("./monitor-YkqtEXDP.js");
1657
+ const { monitorFeishuProvider } = await import("./monitor-BnbCJWIU.js");
1636
1658
  const account = resolveFeishuRuntimeAccount({
1637
1659
  cfg: ctx.cfg,
1638
1660
  accountId: ctx.accountId
@@ -1,2 +1,2 @@
1
- import { t as feishuPlugin } from "./channel-C5kY2KFE.js";
1
+ import { t as feishuPlugin } from "./channel-DCSECj4a.js";
2
2
  export { feishuPlugin };
@@ -313,6 +313,13 @@ function resolveReplyToMessageId(params) {
313
313
  if (params.threadId == null) return;
314
314
  return String(params.threadId).trim() || void 0;
315
315
  }
316
+ function resolveFeishuMediaReplyMode(params) {
317
+ const trimmedReplyToId = params.replyToId?.trim() || void 0;
318
+ return {
319
+ replyToMessageId: resolveReplyToMessageId(params),
320
+ replyInThread: params.threadId != null && !trimmedReplyToId
321
+ };
322
+ }
316
323
  async function sendCommentThreadReply(params) {
317
324
  const target = parseFeishuCommentTarget(params.to);
318
325
  if (!target) return null;
@@ -345,7 +352,7 @@ async function sendCommentThreadReply(params) {
345
352
  }
346
353
  }
347
354
  async function sendOutboundText(params) {
348
- const { cfg, to, text, accountId, replyToMessageId } = params;
355
+ const { cfg, to, text, accountId, replyToMessageId, replyInThread } = params;
349
356
  const commentResult = await sendCommentThreadReply({
350
357
  cfg,
351
358
  to,
@@ -363,14 +370,16 @@ async function sendOutboundText(params) {
363
370
  to,
364
371
  text,
365
372
  accountId,
366
- replyToMessageId
373
+ replyToMessageId,
374
+ replyInThread
367
375
  });
368
376
  return sendMessageFeishu({
369
377
  cfg,
370
378
  to,
371
379
  text,
372
380
  accountId,
373
- replyToMessageId
381
+ replyToMessageId,
382
+ replyInThread
374
383
  });
375
384
  }
376
385
  const feishuOutbound = {
@@ -447,7 +456,7 @@ const feishuOutbound = {
447
456
  ...createAttachedChannelResultAdapter({
448
457
  channel: "feishu",
449
458
  sendText: async ({ cfg, to, text, accountId, replyToId, threadId, mediaLocalRoots, identity }) => {
450
- const replyToMessageId = resolveReplyToMessageId({
459
+ const { replyToMessageId, replyInThread } = resolveFeishuMediaReplyMode({
451
460
  replyToId,
452
461
  threadId
453
462
  });
@@ -459,6 +468,7 @@ const feishuOutbound = {
459
468
  mediaUrl: localImagePath,
460
469
  accountId: accountId ?? void 0,
461
470
  replyToMessageId,
471
+ replyInThread,
462
472
  mediaLocalRoots
463
473
  });
464
474
  } catch (err) {
@@ -469,7 +479,8 @@ const feishuOutbound = {
469
479
  to,
470
480
  text,
471
481
  accountId: accountId ?? void 0,
472
- replyToMessageId
482
+ replyToMessageId,
483
+ replyInThread
473
484
  });
474
485
  const renderMode = resolveFeishuAccount({
475
486
  cfg,
@@ -485,7 +496,7 @@ const feishuOutbound = {
485
496
  to,
486
497
  text,
487
498
  replyToMessageId,
488
- replyInThread: threadId != null && !replyToId,
499
+ replyInThread,
489
500
  accountId: accountId ?? void 0,
490
501
  header: header?.title ? header : void 0
491
502
  });
@@ -495,11 +506,12 @@ const feishuOutbound = {
495
506
  to,
496
507
  text,
497
508
  accountId: accountId ?? void 0,
498
- replyToMessageId
509
+ replyToMessageId,
510
+ replyInThread
499
511
  });
500
512
  },
501
513
  sendMedia: async ({ cfg, to, text, mediaUrl, audioAsVoice, accountId, mediaLocalRoots, replyToId, threadId }) => {
502
- const replyToMessageId = resolveReplyToMessageId({
514
+ const { replyToMessageId, replyInThread } = resolveFeishuMediaReplyMode({
503
515
  replyToId,
504
516
  threadId
505
517
  });
@@ -508,7 +520,8 @@ const feishuOutbound = {
508
520
  to,
509
521
  text: [text?.trim(), mediaUrl?.trim()].filter(Boolean).join("\n\n") || mediaUrl || text || "",
510
522
  accountId: accountId ?? void 0,
511
- replyToMessageId
523
+ replyToMessageId,
524
+ replyInThread
512
525
  });
513
526
  const suppressTextForVoiceMedia = mediaUrl !== void 0 && shouldSuppressFeishuTextForVoiceMedia({
514
527
  mediaUrl,
@@ -519,7 +532,8 @@ const feishuOutbound = {
519
532
  to,
520
533
  text,
521
534
  accountId: accountId ?? void 0,
522
- replyToMessageId
535
+ replyToMessageId,
536
+ replyInThread
523
537
  });
524
538
  if (mediaUrl) try {
525
539
  const result = await sendMediaFeishu({
@@ -529,6 +543,7 @@ const feishuOutbound = {
529
543
  accountId: accountId ?? void 0,
530
544
  mediaLocalRoots,
531
545
  replyToMessageId,
546
+ replyInThread,
532
547
  ...audioAsVoice === true ? { audioAsVoice: true } : {}
533
548
  });
534
549
  if (result.voiceIntentDegradedToFile && text?.trim()) await sendOutboundText({
@@ -536,7 +551,8 @@ const feishuOutbound = {
536
551
  to,
537
552
  text,
538
553
  accountId: accountId ?? void 0,
539
- replyToMessageId
554
+ replyToMessageId,
555
+ replyInThread
540
556
  });
541
557
  return result;
542
558
  } catch (err) {
@@ -546,7 +562,8 @@ const feishuOutbound = {
546
562
  to,
547
563
  text: [text?.trim(), `📎 ${mediaUrl}`].filter(Boolean).join("\n\n"),
548
564
  accountId: accountId ?? void 0,
549
- replyToMessageId
565
+ replyToMessageId,
566
+ replyInThread
550
567
  });
551
568
  }
552
569
  return await sendOutboundText({
@@ -554,7 +571,8 @@ const feishuOutbound = {
554
571
  to,
555
572
  text: text ?? "",
556
573
  accountId: accountId ?? void 0,
557
- replyToMessageId
574
+ replyToMessageId,
575
+ replyInThread
558
576
  });
559
577
  }
560
578
  })
@@ -3,7 +3,7 @@ import { l as fetchBotIdentityForMonitor } from "./monitor.state-DYM02ipp.js";
3
3
  //#region extensions/feishu/src/monitor.ts
4
4
  let monitorAccountRuntimePromise;
5
5
  async function loadMonitorAccountRuntime() {
6
- monitorAccountRuntimePromise ??= import("./monitor.account-BRSlHEbU.js");
6
+ monitorAccountRuntimePromise ??= import("./monitor.account-88yMtLAU.js");
7
7
  return await monitorAccountRuntimePromise;
8
8
  }
9
9
  async function monitorFeishuProvider(opts = {}) {
@@ -3,11 +3,11 @@ import { i as resolveReceiveIdType } from "./targets-JMFJRKSe.js";
3
3
  import { n as createFeishuThreadBindingManager } from "./thread-bindings-BmS6TLes.js";
4
4
  import { _ as buildFeishuCommentTarget, f as isRecord$3, h as readString$2, l as encodeQuery, m as parseCommentContentElements, p as normalizeString, s as resolveFeishuRuntimeAccount, u as extractReplyText, v as normalizeCommentFileType } from "./accounts-Ba3-WP1z.js";
5
5
  import { c as decodeFeishuCardAction, o as buildFeishuCardActionTextFallback, s as createFeishuCardInteractionEnvelope } from "./send-result-BVFTskB_.js";
6
- import { i as resolveFeishuGroupConfig, n as isFeishuGroupAllowed, o as resolveFeishuReplyPolicy, r as resolveFeishuAllowlistMatch, t as hasExplicitFeishuGroupConfig } from "./policy-D6c-wMPl.js";
6
+ import { a as resolveFeishuGroupSenderActivationIngressAccess, i as resolveFeishuGroupConversationIngressAccess, n as resolveFeishuDmIngressAccess, r as resolveFeishuGroupConfig, s as resolveFeishuReplyPolicy, t as hasExplicitFeishuGroupConfig } from "./policy-DrLgxWF_.js";
7
7
  import { t as getFeishuRuntime } from "./runtime-CG0DuRCy.js";
8
8
  import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient } from "./client-DBVoQL5w.js";
9
9
  import { c as getChatInfo, i as createCommentTypingReactionLifecycle, t as deliverCommentThreadText } from "./drive-C5eJLJr7.js";
10
- import { buildAgentMediaPayload, createReplyPrefixContext, evaluateSupplementalContextVisibility, filterSupplementalContextItems, loadSessionStore, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode, resolveSessionStoreEntry } from "./runtime-api.js";
10
+ import { buildAgentMediaPayload, createReplyPrefixContext, evaluateSupplementalContextVisibility, loadSessionStore, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode, resolveSessionStoreEntry } from "./runtime-api.js";
11
11
  import { _ as normalizeFeishuExternalKey, a as sendCardFeishu, c as sendStructuredCardFeishu, d as isFeishuBroadcastMention, f as isMentionForwardRequest, g as shouldSuppressFeishuTextForVoiceMedia, h as sendMediaFeishu, i as resolveFeishuCardTemplate, l as parsePostContent, m as downloadMessageResourceFeishu, n as getMessageFeishu, p as isFeishuGroupChatType, r as listFeishuThreadMessages, s as sendMessageFeishu, u as extractMentionTargets } from "./send-DKHEQrzH.js";
12
12
  import { i as waitForAbortableDelay, r as raceWithTimeoutAndAbort } from "./probe-BNzzU_uR.js";
13
13
  import { a as feishuWebhookRateLimiter, c as wsClients, i as botOpenIds, l as fetchBotIdentityForMonitor, n as FEISHU_WEBHOOK_MAX_BODY_BYTES, o as httpServers, r as botNames, s as recordWebhookStatus, t as FEISHU_WEBHOOK_BODY_TIMEOUT_MS } from "./monitor.state-DYM02ipp.js";
@@ -25,7 +25,7 @@ import { createPersistentDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
25
25
  import { applyBasicWebhookRequestGuards } from "openclaw/plugin-sdk/webhook-ingress";
26
26
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
27
27
  import { resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
28
- import { resolveOpenDmAllowlistAccess, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
28
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
29
29
  import * as crypto$1 from "node:crypto";
30
30
  import crypto from "node:crypto";
31
31
  import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
@@ -1746,42 +1746,37 @@ function buildFeishuAgentBody(params) {
1746
1746
  }
1747
1747
  return messageBody;
1748
1748
  }
1749
- function isFetchedGroupContextSenderAllowed(params) {
1750
- if (!params.isGroup || params.allowFrom.length === 0) return true;
1751
- if (params.senderType === "app") return true;
1749
+ async function shouldIncludeFetchedGroupContextMessage(params) {
1750
+ let senderAllowed = !params.isGroup || params.allowFrom.length === 0 || params.senderType === "app";
1752
1751
  const senderId = params.senderId?.trim();
1753
- return !!senderId && isFeishuGroupAllowed({
1754
- groupPolicy: "allowlist",
1755
- allowFrom: params.allowFrom,
1756
- senderId,
1757
- senderName: void 0
1758
- });
1759
- }
1760
- function shouldIncludeFetchedGroupContextMessage(params) {
1761
- const senderAllowed = isFetchedGroupContextSenderAllowed({
1762
- isGroup: params.isGroup,
1752
+ if (!senderAllowed && senderId) senderAllowed = (await resolveFeishuGroupSenderActivationIngressAccess({
1753
+ cfg: params.cfg,
1754
+ accountId: params.accountId,
1755
+ chatId: params.chatId,
1763
1756
  allowFrom: params.allowFrom,
1764
- senderId: params.senderId,
1765
- senderType: params.senderType
1766
- });
1757
+ senderOpenId: senderId,
1758
+ senderUserId: senderId,
1759
+ requireMention: false,
1760
+ mentionedBot: true
1761
+ })).senderAccess.decision === "allow";
1767
1762
  return evaluateSupplementalContextVisibility({
1768
1763
  mode: params.mode,
1769
1764
  kind: params.kind,
1770
1765
  senderAllowed
1771
1766
  }).include;
1772
1767
  }
1773
- function filterFetchedGroupContextMessages(messages, params) {
1774
- return filterSupplementalContextItems({
1775
- items: messages,
1768
+ async function filterFetchedGroupContextMessages(messages, params) {
1769
+ return (await Promise.all(messages.map(async (message) => await shouldIncludeFetchedGroupContextMessage({
1770
+ cfg: params.cfg,
1771
+ accountId: params.accountId,
1772
+ chatId: params.chatId,
1773
+ isGroup: params.isGroup,
1774
+ allowFrom: params.allowFrom,
1776
1775
  mode: params.mode,
1777
1776
  kind: params.kind,
1778
- isSenderAllowed: (message) => isFetchedGroupContextSenderAllowed({
1779
- isGroup: params.isGroup,
1780
- allowFrom: params.allowFrom,
1781
- senderId: message.senderId,
1782
- senderType: message.senderType
1783
- })
1784
- }).items;
1777
+ senderId: message.senderId,
1778
+ senderType: message.senderType
1779
+ }) ? message : void 0))).filter((message) => message !== void 0);
1785
1780
  }
1786
1781
  async function handleFeishuMessage(params) {
1787
1782
  const { cfg, event, botOpenId, botName, runtime, chatHistories, accountId, processingClaimHeld = false } = params;
@@ -1902,7 +1897,6 @@ async function handleFeishuMessage(params) {
1902
1897
  const groupHistoryKey = isGroup ? groupSession?.peerId ?? ctx.chatId : void 0;
1903
1898
  const dmPolicy = feishuCfg?.dmPolicy ?? "pairing";
1904
1899
  const configAllowFrom = feishuCfg?.allowFrom ?? [];
1905
- const useAccessGroups = cfg.commands?.useAccessGroups !== false;
1906
1900
  const rawBroadcastAgents = isGroup ? resolveBroadcastAgents(cfg, ctx.chatId) : null;
1907
1901
  const broadcastAgents = rawBroadcastAgents ? [...new Set(rawBroadcastAgents.map((id) => normalizeAgentId$1(id)))] : null;
1908
1902
  const messageCreateTimeMs = event.message.create_time ? Number.parseInt(event.message.create_time, 10) : Date.now();
@@ -1929,27 +1923,17 @@ async function handleFeishuMessage(params) {
1929
1923
  cfg: feishuCfg,
1930
1924
  groupId: ctx.chatId
1931
1925
  });
1932
- if (!(groupPolicy !== "disabled" && (groupExplicitlyConfigured || isFeishuGroupAllowed({
1926
+ if ((await resolveFeishuGroupConversationIngressAccess({
1927
+ cfg,
1928
+ accountId: account.accountId,
1929
+ chatId: ctx.chatId,
1933
1930
  groupPolicy,
1934
- allowFrom: groupAllowFrom,
1935
- senderId: ctx.chatId,
1936
- senderName: void 0
1937
- })))) {
1931
+ groupAllowFrom,
1932
+ groupExplicitlyConfigured
1933
+ })).ingress.admission !== "dispatch") {
1938
1934
  log(`feishu[${account.accountId}]: group ${ctx.chatId} not in groupAllowFrom (groupPolicy=${groupPolicy})`);
1939
1935
  return;
1940
1936
  }
1941
- if (effectiveGroupSenderAllowFrom.length > 0) {
1942
- if (!isFeishuGroupAllowed({
1943
- groupPolicy: "allowlist",
1944
- allowFrom: effectiveGroupSenderAllowFrom,
1945
- senderId: ctx.senderOpenId,
1946
- senderIds: [senderUserId],
1947
- senderName: ctx.senderName
1948
- })) {
1949
- log(`feishu: sender ${ctx.senderOpenId} not in group ${ctx.chatId} sender allowlist`);
1950
- return;
1951
- }
1952
- }
1953
1937
  ({requireMention} = resolveFeishuReplyPolicy({
1954
1938
  isDirectMessage: false,
1955
1939
  cfg,
@@ -1957,7 +1941,21 @@ async function handleFeishuMessage(params) {
1957
1941
  groupId: ctx.chatId,
1958
1942
  groupPolicy
1959
1943
  }));
1960
- if (requireMention && !ctx.mentionedBot) {
1944
+ const groupSenderActivationIngress = await resolveFeishuGroupSenderActivationIngressAccess({
1945
+ cfg,
1946
+ accountId: account.accountId,
1947
+ chatId: ctx.chatId,
1948
+ allowFrom: effectiveGroupSenderAllowFrom,
1949
+ senderOpenId: ctx.senderOpenId,
1950
+ senderUserId,
1951
+ requireMention,
1952
+ mentionedBot: ctx.mentionedBot
1953
+ });
1954
+ if (groupSenderActivationIngress.senderAccess.decision !== "allow") {
1955
+ log(`feishu: sender ${ctx.senderOpenId} not in group ${ctx.chatId} sender allowlist`);
1956
+ return;
1957
+ }
1958
+ if (groupSenderActivationIngress.ingress.admission !== "dispatch") {
1961
1959
  log(`feishu[${account.accountId}]: message in group ${ctx.chatId} did not mention bot`);
1962
1960
  if (!broadcastAgents && chatHistories && groupHistoryKey) recordPendingHistoryEntryIfEnabled({
1963
1961
  historyMap: chatHistories,
@@ -1982,25 +1980,20 @@ async function handleFeishuMessage(params) {
1982
1980
  });
1983
1981
  const commandProbeBody = isGroup ? normalizeFeishuCommandProbeBody(ctx.content) : ctx.content;
1984
1982
  const shouldComputeCommandAuthorized = core.channel.commands.shouldComputeCommandAuthorized(commandProbeBody, cfg);
1985
- const storeAllowFrom = !isGroup && dmPolicy !== "allowlist" && dmPolicy !== "open" ? await pairing.readAllowFromStore().catch(() => []) : [];
1986
- const effectiveDmAllowFrom = [...configAllowFrom, ...storeAllowFrom];
1987
- const dmAllowed = resolveFeishuAllowlistMatch({
1988
- allowFrom: effectiveDmAllowFrom,
1989
- senderId: ctx.senderOpenId,
1990
- senderIds: [senderUserId],
1991
- senderName: ctx.senderName
1992
- }).allowed;
1993
- const dmAccessAllowed = dmPolicy === "open" ? resolveOpenDmAllowlistAccess({
1994
- effectiveAllowFrom: effectiveDmAllowFrom,
1995
- isSenderAllowed: (allowFrom) => resolveFeishuAllowlistMatch({
1996
- allowFrom,
1997
- senderId: ctx.senderOpenId,
1998
- senderIds: [senderUserId],
1999
- senderName: ctx.senderName
2000
- }).allowed
2001
- }).decision === "allow" : dmAllowed;
2002
- if (isDirect && !dmAccessAllowed) {
2003
- if (dmPolicy === "pairing") await pairing.issueChallenge({
1983
+ const dmIngress = isDirect ? await resolveFeishuDmIngressAccess({
1984
+ cfg,
1985
+ accountId: account.accountId,
1986
+ dmPolicy,
1987
+ allowFrom: configAllowFrom,
1988
+ readAllowFromStore: pairing.readAllowFromStore,
1989
+ senderOpenId: ctx.senderOpenId,
1990
+ senderUserId,
1991
+ conversationId: ctx.senderOpenId,
1992
+ mayPair: true,
1993
+ ...shouldComputeCommandAuthorized ? { command: { hasControlCommand: true } } : {}
1994
+ }) : null;
1995
+ if (isDirect && dmIngress?.ingress.admission !== "dispatch") {
1996
+ if (dmIngress?.ingress.admission === "pairing-required") await pairing.issueChallenge({
2004
1997
  senderId: ctx.senderOpenId,
2005
1998
  senderIdLine: `Your Feishu user id: ${ctx.senderOpenId}`,
2006
1999
  meta: { name: ctx.senderName },
@@ -2022,13 +2015,7 @@ async function handleFeishuMessage(params) {
2022
2015
  else log(`feishu[${account.accountId}]: blocked unauthorized sender ${ctx.senderOpenId} (dmPolicy=${dmPolicy})`);
2023
2016
  return;
2024
2017
  }
2025
- const commandAllowFrom = isGroup ? groupConfig?.allowFrom ?? configAllowFrom : effectiveDmAllowFrom;
2026
- const senderAllowedForCommands = resolveFeishuAllowlistMatch({
2027
- allowFrom: commandAllowFrom,
2028
- senderId: ctx.senderOpenId,
2029
- senderIds: [senderUserId],
2030
- senderName: ctx.senderName
2031
- }).allowed;
2018
+ const commandAllowFrom = isGroup ? groupConfig?.allowFrom ?? configAllowFrom : dmIngress?.senderAccess.effectiveAllowFrom ?? configAllowFrom;
2032
2019
  const feishuFrom = `feishu:${ctx.senderOpenId}`;
2033
2020
  const feishuTo = isGroup ? `chat:${ctx.chatId}` : `user:${ctx.senderOpenId}`;
2034
2021
  const peerId = isGroup ? groupSession?.peerId ?? ctx.chatId : ctx.senderOpenId;
@@ -2165,13 +2152,28 @@ async function handleFeishuMessage(params) {
2165
2152
  content: audioTranscript
2166
2153
  };
2167
2154
  const effectiveCommandProbeBody = audioTranscript === void 0 ? commandProbeBody : isGroup ? normalizeFeishuCommandProbeBody(audioTranscript) : audioTranscript;
2168
- const commandAuthorized = (audioTranscript === void 0 ? shouldComputeCommandAuthorized : core.channel.commands.shouldComputeCommandAuthorized(effectiveCommandProbeBody, cfg)) ? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
2169
- useAccessGroups,
2170
- authorizers: [{
2171
- configured: commandAllowFrom.length > 0,
2172
- allowed: senderAllowedForCommands
2173
- }]
2174
- }) : void 0;
2155
+ const commandAuthorized = (audioTranscript === void 0 ? shouldComputeCommandAuthorized : core.channel.commands.shouldComputeCommandAuthorized(effectiveCommandProbeBody, cfg)) ? isDirect && audioTranscript === void 0 && dmIngress ? dmIngress.commandAccess.authorized : isGroup ? (await resolveFeishuGroupSenderActivationIngressAccess({
2156
+ cfg,
2157
+ accountId: account.accountId,
2158
+ chatId: ctx.chatId,
2159
+ allowFrom: commandAllowFrom,
2160
+ senderOpenId: ctx.senderOpenId,
2161
+ senderUserId,
2162
+ requireMention: false,
2163
+ mentionedBot: true,
2164
+ command: { hasControlCommand: true }
2165
+ })).commandAccess.authorized : (await resolveFeishuDmIngressAccess({
2166
+ cfg,
2167
+ accountId: account.accountId,
2168
+ dmPolicy,
2169
+ allowFrom: configAllowFrom,
2170
+ readAllowFromStore: pairing.readAllowFromStore,
2171
+ senderOpenId: ctx.senderOpenId,
2172
+ senderUserId,
2173
+ conversationId: ctx.senderOpenId,
2174
+ mayPair: false,
2175
+ command: { hasControlCommand: true }
2176
+ })).commandAccess.authorized : void 0;
2175
2177
  let quotedMessageInfo = null;
2176
2178
  let quotedContent;
2177
2179
  if (ctx.parentId) try {
@@ -2180,7 +2182,10 @@ async function handleFeishuMessage(params) {
2180
2182
  messageId: ctx.parentId,
2181
2183
  accountId: account.accountId
2182
2184
  });
2183
- if (quotedMessageInfo && shouldIncludeFetchedGroupContextMessage({
2185
+ if (quotedMessageInfo && await shouldIncludeFetchedGroupContextMessage({
2186
+ cfg,
2187
+ accountId: account.accountId,
2188
+ chatId: ctx.chatId,
2184
2189
  isGroup,
2185
2190
  allowFrom: effectiveGroupSenderAllowFrom,
2186
2191
  mode: contextVisibilityMode,
@@ -2250,7 +2255,10 @@ async function handleFeishuMessage(params) {
2250
2255
  rootMessageInfo = null;
2251
2256
  }
2252
2257
  rootMessageThreadId = rootMessageInfo?.threadId;
2253
- if (rootMessageInfo && !shouldIncludeFetchedGroupContextMessage({
2258
+ if (rootMessageInfo && !await shouldIncludeFetchedGroupContextMessage({
2259
+ cfg,
2260
+ accountId: account.accountId,
2261
+ chatId: ctx.chatId,
2254
2262
  isGroup,
2255
2263
  allowFrom: effectiveGroupSenderAllowFrom,
2256
2264
  mode: contextVisibilityMode,
@@ -2310,7 +2318,10 @@ async function handleFeishuMessage(params) {
2310
2318
  });
2311
2319
  const senderScoped = groupSession?.groupSessionScope === "group_topic_sender";
2312
2320
  const senderIds = new Set([ctx.senderOpenId, senderUserId].map((id) => id?.trim()).filter((id) => id !== void 0 && id.length > 0));
2313
- const allowlistedMessages = filterFetchedGroupContextMessages(threadMessages, {
2321
+ const allowlistedMessages = await filterFetchedGroupContextMessages(threadMessages, {
2322
+ cfg,
2323
+ accountId: account.accountId,
2324
+ chatId: ctx.chatId,
2314
2325
  isGroup,
2315
2326
  allowFrom: effectiveGroupSenderAllowFrom,
2316
2327
  mode: contextVisibilityMode,
@@ -3977,22 +3988,19 @@ async function handleFeishuCommentEvent(params) {
3977
3988
  channel: "feishu",
3978
3989
  accountId: account.accountId
3979
3990
  });
3980
- const storeAllowFrom = dmPolicy !== "allowlist" && dmPolicy !== "open" ? await pairing.readAllowFromStore().catch(() => []) : [];
3981
- const effectiveDmAllowFrom = [...configAllowFrom, ...storeAllowFrom];
3982
- const senderAllowed = resolveFeishuAllowlistMatch({
3983
- allowFrom: effectiveDmAllowFrom,
3984
- senderId: turn.senderId,
3985
- senderIds: [turn.senderUserId]
3986
- }).allowed;
3987
- if (!(dmPolicy === "open" ? resolveOpenDmAllowlistAccess({
3988
- effectiveAllowFrom: effectiveDmAllowFrom,
3989
- isSenderAllowed: (allowFrom) => resolveFeishuAllowlistMatch({
3990
- allowFrom,
3991
- senderId: turn.senderId,
3992
- senderIds: [turn.senderUserId]
3993
- }).allowed
3994
- }).decision === "allow" : senderAllowed)) {
3995
- if (dmPolicy === "pairing") {
3991
+ const dmIngress = await resolveFeishuDmIngressAccess({
3992
+ cfg: params.cfg,
3993
+ accountId: account.accountId,
3994
+ dmPolicy,
3995
+ allowFrom: configAllowFrom,
3996
+ readAllowFromStore: pairing.readAllowFromStore,
3997
+ senderOpenId: turn.senderId,
3998
+ senderUserId: turn.senderUserId,
3999
+ conversationId: turn.senderId,
4000
+ mayPair: true
4001
+ });
4002
+ if (dmIngress.ingress.admission !== "dispatch") {
4003
+ if (dmIngress.ingress.admission === "pairing-required") {
3996
4004
  const client = createFeishuClient(account);
3997
4005
  await pairing.issueChallenge({
3998
4006
  senderId: turn.senderId,
@@ -0,0 +1,181 @@
1
+ import { t as detectIdType } from "./targets-JMFJRKSe.js";
2
+ import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
3
+ import { normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
4
+ import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
5
+ //#region extensions/feishu/src/policy.ts
6
+ const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
7
+ const FEISHU_TYPED_PREFIX_RE = /^(chat|group|channel|user|dm|open_id):/i;
8
+ const FEISHU_ID_KIND = "plugin:feishu-id";
9
+ const feishuIngressIdentity = defineStableChannelIngressIdentity({
10
+ key: "feishu-id",
11
+ kind: FEISHU_ID_KIND,
12
+ normalize: normalizeFeishuAllowEntry,
13
+ sensitivity: "pii",
14
+ aliases: [{
15
+ key: "feishu-alt-id",
16
+ kind: FEISHU_ID_KIND,
17
+ normalizeEntry: () => null,
18
+ normalizeSubject: normalizeFeishuAllowEntry,
19
+ sensitivity: "pii"
20
+ }],
21
+ isWildcardEntry: (entry) => normalizeFeishuAllowEntry(entry) === "*",
22
+ resolveEntryId: ({ entryIndex }) => `feishu-entry-${entryIndex + 1}`
23
+ });
24
+ function normalizeFeishuAllowEntry(raw) {
25
+ const trimmed = raw.trim();
26
+ if (!trimmed) return "";
27
+ if (trimmed === "*") return "*";
28
+ let withoutProviderPrefix = trimmed;
29
+ while (FEISHU_PROVIDER_PREFIX_RE.test(withoutProviderPrefix)) withoutProviderPrefix = withoutProviderPrefix.replace(FEISHU_PROVIDER_PREFIX_RE, "").trim();
30
+ if (withoutProviderPrefix === "*") return "*";
31
+ const lowered = normalizeOptionalLowercaseString(withoutProviderPrefix) ?? "";
32
+ if (!lowered) return "";
33
+ const prefixed = lowered.match(FEISHU_TYPED_PREFIX_RE);
34
+ if (prefixed?.[1]) {
35
+ const kind = [
36
+ "chat",
37
+ "group",
38
+ "channel"
39
+ ].includes(prefixed[1]) ? "chat" : "user";
40
+ const value = withoutProviderPrefix.slice(prefixed[0].length).trim();
41
+ return value === "*" ? "*" : value ? `${kind}:${value}` : "";
42
+ }
43
+ const detectedType = detectIdType(withoutProviderPrefix);
44
+ if (detectedType === "chat_id") return `chat:${withoutProviderPrefix}`;
45
+ if (detectedType === "open_id" || detectedType === "user_id") return `user:${withoutProviderPrefix}`;
46
+ return "";
47
+ }
48
+ function normalizeFeishuDmPolicy(policy) {
49
+ return policy === "open" || policy === "pairing" || policy === "allowlist" || policy === "disabled" ? policy : "pairing";
50
+ }
51
+ function normalizeFeishuGroupPolicy(policy) {
52
+ return policy === "allowall" ? "open" : policy;
53
+ }
54
+ function createFeishuIngressSubject(params) {
55
+ const ids = [params.primaryId, ...params.alternateIds ?? []].map((value) => value?.trim()).filter((value) => Boolean(value));
56
+ return {
57
+ stableId: ids[0],
58
+ aliases: { "feishu-alt-id": ids[1] }
59
+ };
60
+ }
61
+ function createFeishuIngressResolver(params) {
62
+ return createChannelIngressResolver({
63
+ channelId: "feishu",
64
+ accountId: normalizeAccountId(params.accountId) ?? "default",
65
+ identity: feishuIngressIdentity,
66
+ cfg: params.cfg,
67
+ ...params.readAllowFromStore ? { readStoreAllowFrom: params.readAllowFromStore } : {}
68
+ });
69
+ }
70
+ async function resolveFeishuDmIngressAccess(params) {
71
+ return await createFeishuIngressResolver({
72
+ cfg: params.cfg,
73
+ accountId: params.accountId,
74
+ readAllowFromStore: params.readAllowFromStore
75
+ }).message({
76
+ subject: createFeishuIngressSubject({
77
+ primaryId: params.senderOpenId,
78
+ alternateIds: [params.senderUserId]
79
+ }),
80
+ conversation: {
81
+ kind: "direct",
82
+ id: params.conversationId
83
+ },
84
+ event: { mayPair: params.mayPair },
85
+ dmPolicy: normalizeFeishuDmPolicy(params.dmPolicy),
86
+ groupPolicy: "disabled",
87
+ allowFrom: params.allowFrom ?? [],
88
+ ...params.command ? { command: params.command } : {}
89
+ });
90
+ }
91
+ async function resolveFeishuGroupConversationIngressAccess(params) {
92
+ const groupPolicy = normalizeFeishuGroupPolicy(params.groupPolicy);
93
+ const groupAllowFrom = groupPolicy === "allowlist" && params.groupExplicitlyConfigured ? [...params.groupAllowFrom ?? [], params.chatId] : params.groupAllowFrom ?? [];
94
+ return await createFeishuIngressResolver({
95
+ cfg: params.cfg,
96
+ accountId: params.accountId
97
+ }).message({
98
+ subject: createFeishuIngressSubject({ primaryId: params.chatId }),
99
+ conversation: {
100
+ kind: "group",
101
+ id: params.chatId
102
+ },
103
+ dmPolicy: "disabled",
104
+ groupPolicy,
105
+ groupAllowFrom
106
+ });
107
+ }
108
+ async function resolveFeishuGroupSenderActivationIngressAccess(params) {
109
+ const groupAllowFrom = params.allowFrom ?? [];
110
+ return await createFeishuIngressResolver({
111
+ cfg: params.cfg,
112
+ accountId: params.accountId
113
+ }).message({
114
+ subject: createFeishuIngressSubject({
115
+ primaryId: params.senderOpenId,
116
+ alternateIds: [params.senderUserId]
117
+ }),
118
+ conversation: {
119
+ kind: "group",
120
+ id: params.chatId
121
+ },
122
+ dmPolicy: "disabled",
123
+ groupPolicy: groupAllowFrom.length > 0 ? "allowlist" : "open",
124
+ groupAllowFrom,
125
+ mentionFacts: {
126
+ canDetectMention: true,
127
+ wasMentioned: params.mentionedBot
128
+ },
129
+ policy: { activation: {
130
+ requireMention: params.requireMention,
131
+ allowTextCommands: false
132
+ } },
133
+ ...params.command ? { command: params.command } : {}
134
+ });
135
+ }
136
+ function resolveFeishuGroupConfig(params) {
137
+ const groups = params.cfg?.groups ?? {};
138
+ const wildcard = groups["*"];
139
+ const groupId = params.groupId?.trim();
140
+ if (!groupId) return;
141
+ const direct = groups[groupId];
142
+ if (direct) return direct;
143
+ const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
144
+ const matchKey = Object.keys(groups).find((key) => normalizeOptionalLowercaseString(key) === lowered);
145
+ if (matchKey) return groups[matchKey];
146
+ return wildcard;
147
+ }
148
+ function hasExplicitFeishuGroupConfig(params) {
149
+ const groups = params.cfg?.groups ?? {};
150
+ const groupId = params.groupId?.trim();
151
+ if (!groupId) return false;
152
+ if (Object.prototype.hasOwnProperty.call(groups, groupId) && groupId !== "*") return true;
153
+ const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
154
+ return Object.keys(groups).some((key) => key !== "*" && normalizeOptionalLowercaseString(key) === lowered);
155
+ }
156
+ function resolveFeishuGroupToolPolicy(params) {
157
+ const cfg = params.cfg.channels?.feishu;
158
+ if (!cfg) return;
159
+ return resolveFeishuGroupConfig({
160
+ cfg,
161
+ groupId: params.groupId
162
+ })?.tools;
163
+ }
164
+ function resolveFeishuReplyPolicy(params) {
165
+ if (params.isDirectMessage) return { requireMention: false };
166
+ const feishuCfg = params.cfg.channels?.feishu;
167
+ const resolvedCfg = resolveMergedAccountConfig({
168
+ channelConfig: feishuCfg,
169
+ accounts: feishuCfg?.accounts,
170
+ accountId: normalizeAccountId(params.accountId),
171
+ normalizeAccountId,
172
+ omitKeys: ["defaultAccount"]
173
+ });
174
+ const groupRequireMention = resolveFeishuGroupConfig({
175
+ cfg: resolvedCfg,
176
+ groupId: params.groupId
177
+ })?.requireMention;
178
+ return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
179
+ }
180
+ //#endregion
181
+ export { resolveFeishuGroupSenderActivationIngressAccess as a, resolveFeishuGroupConversationIngressAccess as i, resolveFeishuDmIngressAccess as n, resolveFeishuGroupToolPolicy as o, resolveFeishuGroupConfig as r, resolveFeishuReplyPolicy as s, hasExplicitFeishuGroupConfig as t };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-C5kY2KFE.js";
1
+ import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DCSECj4a.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.5.9-beta.1",
3
+ "version": "2026.5.10-beta.2",
4
4
  "description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@larksuiteoapi/node-sdk": "^1.62.1",
11
+ "@larksuiteoapi/node-sdk": "^1.63.1",
12
12
  "typebox": "1.1.38"
13
13
  },
14
14
  "devDependencies": {
@@ -16,7 +16,7 @@
16
16
  "openclaw": "workspace:*"
17
17
  },
18
18
  "peerDependencies": {
19
- "openclaw": ">=2026.5.9-beta.1"
19
+ "openclaw": ">=2026.5.10-beta.2"
20
20
  },
21
21
  "peerDependenciesMeta": {
22
22
  "openclaw": {
@@ -47,10 +47,10 @@
47
47
  "minHostVersion": ">=2026.4.25"
48
48
  },
49
49
  "compat": {
50
- "pluginApi": ">=2026.5.9-beta.1"
50
+ "pluginApi": ">=2026.5.10-beta.2"
51
51
  },
52
52
  "build": {
53
- "openclawVersion": "2026.5.9-beta.1"
53
+ "openclawVersion": "2026.5.10-beta.2"
54
54
  },
55
55
  "release": {
56
56
  "publishToClawHub": true,
@@ -1,118 +0,0 @@
1
- import { t as detectIdType } from "./targets-JMFJRKSe.js";
2
- import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
3
- import { normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
4
- import { evaluateSenderGroupAccessForPolicy } from "openclaw/plugin-sdk/group-access";
5
- //#region extensions/feishu/src/policy.ts
6
- const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
7
- function stripRepeatedFeishuProviderPrefixes(raw) {
8
- let normalized = raw.trim();
9
- while (FEISHU_PROVIDER_PREFIX_RE.test(normalized)) normalized = normalized.replace(FEISHU_PROVIDER_PREFIX_RE, "").trim();
10
- return normalized;
11
- }
12
- function canonicalizeFeishuAllowlistKey(params) {
13
- const value = params.value.trim();
14
- if (!value) return "";
15
- if (value === "*") return "*";
16
- return `${params.kind}:${value}`;
17
- }
18
- function normalizeFeishuAllowEntry(raw) {
19
- const trimmed = raw.trim();
20
- if (!trimmed) return "";
21
- if (trimmed === "*") return "*";
22
- const withoutProviderPrefix = stripRepeatedFeishuProviderPrefixes(trimmed);
23
- if (withoutProviderPrefix === "*") return "*";
24
- const lowered = normalizeOptionalLowercaseString(withoutProviderPrefix) ?? "";
25
- if (!lowered) return "";
26
- if (lowered.startsWith("chat:") || lowered.startsWith("group:") || lowered.startsWith("channel:")) return canonicalizeFeishuAllowlistKey({
27
- kind: "chat",
28
- value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
29
- });
30
- if (lowered.startsWith("user:") || lowered.startsWith("dm:")) return canonicalizeFeishuAllowlistKey({
31
- kind: "user",
32
- value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
33
- });
34
- if (lowered.startsWith("open_id:")) return canonicalizeFeishuAllowlistKey({
35
- kind: "user",
36
- value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
37
- });
38
- const detectedType = detectIdType(withoutProviderPrefix);
39
- if (detectedType === "chat_id") return canonicalizeFeishuAllowlistKey({
40
- kind: "chat",
41
- value: withoutProviderPrefix
42
- });
43
- if (detectedType === "open_id" || detectedType === "user_id") return canonicalizeFeishuAllowlistKey({
44
- kind: "user",
45
- value: withoutProviderPrefix
46
- });
47
- return "";
48
- }
49
- function resolveFeishuAllowlistMatch(params) {
50
- const allowFrom = params.allowFrom.map((entry) => normalizeFeishuAllowEntry(String(entry))).filter(Boolean);
51
- if (allowFrom.length === 0) return { allowed: false };
52
- if (allowFrom.includes("*")) return {
53
- allowed: true,
54
- matchKey: "*",
55
- matchSource: "wildcard"
56
- };
57
- const senderCandidates = [params.senderId, ...params.senderIds ?? []].map((entry) => normalizeFeishuAllowEntry(entry ?? "")).filter(Boolean);
58
- for (const senderId of senderCandidates) if (allowFrom.includes(senderId)) return {
59
- allowed: true,
60
- matchKey: senderId,
61
- matchSource: "id"
62
- };
63
- return { allowed: false };
64
- }
65
- function resolveFeishuGroupConfig(params) {
66
- const groups = params.cfg?.groups ?? {};
67
- const wildcard = groups["*"];
68
- const groupId = params.groupId?.trim();
69
- if (!groupId) return;
70
- const direct = groups[groupId];
71
- if (direct) return direct;
72
- const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
73
- const matchKey = Object.keys(groups).find((key) => normalizeOptionalLowercaseString(key) === lowered);
74
- if (matchKey) return groups[matchKey];
75
- return wildcard;
76
- }
77
- function hasExplicitFeishuGroupConfig(params) {
78
- const groups = params.cfg?.groups ?? {};
79
- const groupId = params.groupId?.trim();
80
- if (!groupId) return false;
81
- if (Object.prototype.hasOwnProperty.call(groups, groupId) && groupId !== "*") return true;
82
- const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
83
- return Object.keys(groups).some((key) => key !== "*" && normalizeOptionalLowercaseString(key) === lowered);
84
- }
85
- function resolveFeishuGroupToolPolicy(params) {
86
- const cfg = params.cfg.channels?.feishu;
87
- if (!cfg) return;
88
- return resolveFeishuGroupConfig({
89
- cfg,
90
- groupId: params.groupId
91
- })?.tools;
92
- }
93
- function isFeishuGroupAllowed(params) {
94
- return evaluateSenderGroupAccessForPolicy({
95
- groupPolicy: params.groupPolicy === "allowall" ? "open" : params.groupPolicy,
96
- groupAllowFrom: params.allowFrom.map((entry) => String(entry)),
97
- senderId: params.senderId,
98
- isSenderAllowed: () => resolveFeishuAllowlistMatch(params).allowed
99
- }).allowed;
100
- }
101
- function resolveFeishuReplyPolicy(params) {
102
- if (params.isDirectMessage) return { requireMention: false };
103
- const feishuCfg = params.cfg.channels?.feishu;
104
- const resolvedCfg = resolveMergedAccountConfig({
105
- channelConfig: feishuCfg,
106
- accounts: feishuCfg?.accounts,
107
- accountId: normalizeAccountId(params.accountId),
108
- normalizeAccountId,
109
- omitKeys: ["defaultAccount"]
110
- });
111
- const groupRequireMention = resolveFeishuGroupConfig({
112
- cfg: resolvedCfg,
113
- groupId: params.groupId
114
- })?.requireMention;
115
- return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
116
- }
117
- //#endregion
118
- export { resolveFeishuGroupToolPolicy as a, resolveFeishuGroupConfig as i, isFeishuGroupAllowed as n, resolveFeishuReplyPolicy as o, resolveFeishuAllowlistMatch as r, hasExplicitFeishuGroupConfig as t };