@openclaw/feishu 2026.5.14-beta.2 → 2026.5.16-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-D5kDxq_j.js";
3
3
  import { n as handleFeishuSubagentEnded, r as handleFeishuSubagentSpawning, t as handleFeishuSubagentDeliveryTarget } from "./subagent-hooks-BUPKo9Al.js";
4
4
  import { r as listEnabledFeishuAccounts } from "./accounts-CP4tDW-z.js";
5
- import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-1pldyM9r.js";
5
+ import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-DLhJC9t0.js";
6
6
  import { t as getFeishuRuntime } from "./runtime-Cc16UY23.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-Av1h_Lwd.js";
8
8
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -29,7 +29,7 @@ import { normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/
29
29
  import { z } from "zod";
30
30
  import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$2 } from "openclaw/plugin-sdk/secret-input";
31
31
  import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
32
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, splitSetupEntries } from "openclaw/plugin-sdk/setup";
32
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, splitSetupEntries } from "openclaw/plugin-sdk/setup";
33
33
  //#region extensions/feishu/src/approval-auth.ts
34
34
  function normalizeFeishuApproverId(value) {
35
35
  const trimmed = normalizeOptionalLowercaseString(normalizeFeishuTarget(String(value)));
@@ -752,6 +752,7 @@ const feishuSetupAdapter = {
752
752
  };
753
753
  //#endregion
754
754
  //#region extensions/feishu/src/setup-surface.ts
755
+ const t = createSetupTranslator();
755
756
  const channel = "feishu";
756
757
  const SCAN_TO_CREATE_TP = "ob_cli_app";
757
758
  function normalizeString(value) {
@@ -780,6 +781,10 @@ function isFeishuConfigured(cfg) {
780
781
  });
781
782
  return topLevelConfigured || accountConfigured;
782
783
  }
784
+ function formatFeishuStatusLine(status) {
785
+ if (status === "needs-credentials") return `Feishu: ${t("wizard.channels.statusNeedsAppCredentials")}`;
786
+ return `Feishu: ${t("wizard.channels.statusConfiguredConnectionNotVerified")}`;
787
+ }
783
788
  /**
784
789
  * Patch feishu config at the correct location based on accountId.
785
790
  * - DEFAULT_ACCOUNT_ID → writes to top-level channels.feishu
@@ -813,14 +818,14 @@ async function promptFeishuAllowFrom(params) {
813
818
  const resolvedAccountId = params.accountId ?? resolveDefaultFeishuAccountId(params.cfg);
814
819
  const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$1 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
815
820
  await params.prompter.note([
816
- "Allowlist Feishu DMs by open_id or user_id.",
817
- "You can find user open_id in Feishu admin console or via API.",
818
- "Examples:",
821
+ t("wizard.feishu.allowlistIntro"),
822
+ t("wizard.feishu.allowlistFindUser"),
823
+ t("wizard.feishu.examples"),
819
824
  "- ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
820
825
  "- on_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
821
- ].join("\n"), "Feishu allowlist");
826
+ ].join("\n"), t("wizard.feishu.allowlistTitle"));
822
827
  const mergedAllowFrom = mergeAllowFromEntries(existingAllowFrom, splitSetupEntries(await params.prompter.text({
823
- message: "Feishu allowFrom (user open_ids)",
828
+ message: t("wizard.feishu.allowFromPrompt"),
824
829
  placeholder: "ou_xxxxx, ou_yyyyy",
825
830
  initialValue: existingAllowFrom.length > 0 ? existingAllowFrom.map(String).join(", ") : void 0
826
831
  })));
@@ -828,20 +833,20 @@ async function promptFeishuAllowFrom(params) {
828
833
  }
829
834
  async function noteFeishuCredentialHelp(prompter) {
830
835
  await prompter.note([
831
- "1) Go to Feishu Open Platform (open.feishu.cn)",
832
- "2) Create a self-built app",
833
- "3) Get App ID and App Secret from Credentials page",
834
- "4) Enable required permissions: im:message, im:chat, contact:user.base:readonly",
835
- "5) Publish the app or add it to a test group",
836
- "Tip: you can also set FEISHU_APP_ID / FEISHU_APP_SECRET env vars.",
837
- `Docs: ${formatDocsLink("/channels/feishu", "feishu")}`
838
- ].join("\n"), "Feishu credentials");
836
+ t("wizard.feishu.credentialsStepOpenPlatform"),
837
+ t("wizard.feishu.credentialsStepCreateApp"),
838
+ t("wizard.feishu.credentialsStepGetCredentials"),
839
+ t("wizard.feishu.credentialsStepPermissions"),
840
+ t("wizard.feishu.credentialsStepPublish"),
841
+ t("wizard.feishu.credentialsEnvTip"),
842
+ t("wizard.channels.docs", { link: formatDocsLink("/channels/feishu", "feishu") })
843
+ ].join("\n"), t("wizard.feishu.credentialsTitle"));
839
844
  }
840
845
  async function promptFeishuAppId(params) {
841
846
  return (await params.prompter.text({
842
- message: "Enter Feishu App ID",
847
+ message: t("wizard.feishu.appIdPrompt"),
843
848
  initialValue: params.initialValue,
844
- validate: (value) => value?.trim() ? void 0 : "Required"
849
+ validate: (value) => value?.trim() ? void 0 : t("common.required")
845
850
  })).trim();
846
851
  }
847
852
  const feishuDmPolicy = {
@@ -889,26 +894,26 @@ function applyNewAppSecurityPolicy(cfg, accountId, openId, groupPolicy) {
889
894
  }
890
895
  async function promptFeishuDomain(params) {
891
896
  return await params.prompter.select({
892
- message: "Which Feishu domain?",
897
+ message: t("wizard.feishu.domainPrompt"),
893
898
  options: [{
894
899
  value: "feishu",
895
- label: "Feishu (feishu.cn) - China"
900
+ label: t("wizard.feishu.domainFeishu")
896
901
  }, {
897
902
  value: "lark",
898
- label: "Lark (larksuite.com) - International"
903
+ label: t("wizard.feishu.domainLark")
899
904
  }],
900
905
  initialValue: params.initialValue ?? "feishu"
901
906
  });
902
907
  }
903
908
  async function promptFeishuSetupMethod(prompter) {
904
909
  return await prompter.select({
905
- message: "How do you want to connect Feishu?",
910
+ message: t("wizard.feishu.setupMethodPrompt"),
906
911
  options: [{
907
912
  value: "manual",
908
- label: "Enter App ID and App Secret manually"
913
+ label: t("wizard.feishu.setupMethodManual")
909
914
  }, {
910
915
  value: "scan",
911
- label: "Scan a QR code to create a bot automatically"
916
+ label: t("wizard.feishu.setupMethodScan")
912
917
  }],
913
918
  initialValue: "manual"
914
919
  });
@@ -918,13 +923,13 @@ async function runScanToCreate(prompter, domain) {
918
923
  try {
919
924
  await initAppRegistration(domain);
920
925
  } catch {
921
- await prompter.note("Scan-to-create is not available in this environment. Falling back to manual input.", "Feishu setup");
926
+ await prompter.note(t("wizard.feishu.scanUnavailable"), t("wizard.feishu.setupTitle"));
922
927
  return null;
923
928
  }
924
929
  const begin = await beginAppRegistration(domain);
925
- await prompter.note("Scan the QR with Lark/Feishu on your phone. If the mobile app does not react, rerun setup and choose manual input.", "Feishu scan-to-create");
930
+ await prompter.note(t("wizard.feishu.scanQr"), t("wizard.feishu.scanTitle"));
926
931
  await printQrCode(begin.qrUrl);
927
- const progress = prompter.progress("Fetching configuration results...");
932
+ const progress = prompter.progress(t("wizard.feishu.fetchingConfig"));
928
933
  const outcome = await pollAppRegistration({
929
934
  deviceCode: begin.deviceCode,
930
935
  interval: begin.interval,
@@ -934,19 +939,19 @@ async function runScanToCreate(prompter, domain) {
934
939
  });
935
940
  switch (outcome.status) {
936
941
  case "success":
937
- progress.stop("Scan completed.");
942
+ progress.stop(t("wizard.feishu.scanCompleted"));
938
943
  return outcome.result;
939
944
  case "access_denied":
940
- progress.stop("User denied authorization. Falling back to manual input.");
945
+ progress.stop(t("wizard.feishu.scanDenied"));
941
946
  return null;
942
947
  case "expired":
943
- progress.stop("Session expired. Falling back to manual input.");
948
+ progress.stop(t("wizard.feishu.scanExpired"));
944
949
  return null;
945
950
  case "timeout":
946
- progress.stop("Scan timed out. Falling back to manual input.");
951
+ progress.stop(t("wizard.feishu.scanTimedOut"));
947
952
  return null;
948
953
  case "error":
949
- progress.stop(`Registration error: ${outcome.message}. Falling back to manual input.`);
954
+ progress.stop(t("wizard.feishu.scanError", { error: outcome.message }));
950
955
  return null;
951
956
  }
952
957
  return null;
@@ -990,8 +995,8 @@ async function runNewAppFlow(params) {
990
995
  canUseEnv: false,
991
996
  hasConfigToken: false,
992
997
  envPrompt: "",
993
- keepPrompt: "Feishu App Secret already configured. Keep it?",
994
- inputPrompt: "Enter Feishu App Secret",
998
+ keepPrompt: t("wizard.feishu.appSecretKeep"),
999
+ inputPrompt: t("wizard.feishu.appSecretPrompt"),
995
1000
  preferredEnvVar: "FEISHU_APP_SECRET"
996
1001
  });
997
1002
  if (appSecretResult.action === "set") {
@@ -1008,24 +1013,24 @@ async function runNewAppFlow(params) {
1008
1013
  }
1009
1014
  }
1010
1015
  const groupPolicy = await prompter.select({
1011
- message: "Group chat policy",
1016
+ message: t("wizard.feishu.groupPolicyPrompt"),
1012
1017
  options: [
1013
1018
  {
1014
1019
  value: "allowlist",
1015
- label: "Allowlist - only respond in specific groups"
1020
+ label: t("wizard.feishu.groupPolicyAllowlist")
1016
1021
  },
1017
1022
  {
1018
1023
  value: "open",
1019
- label: "Open - respond in all groups (requires mention)"
1024
+ label: t("wizard.feishu.groupPolicyOpen")
1020
1025
  },
1021
1026
  {
1022
1027
  value: "disabled",
1023
- label: "Disabled - don't respond in groups"
1028
+ label: t("wizard.feishu.groupPolicyDisabled")
1024
1029
  }
1025
1030
  ],
1026
1031
  initialValue: "allowlist"
1027
1032
  });
1028
- const configProgress = prompter.progress("Configuring...");
1033
+ const configProgress = prompter.progress(t("wizard.feishu.configuring"));
1029
1034
  await new Promise((resolve) => setTimeout(resolve, 50));
1030
1035
  if (appId && appSecret) next = patchFeishuConfig(next, targetAccountId, {
1031
1036
  appId,
@@ -1035,7 +1040,7 @@ async function runNewAppFlow(params) {
1035
1040
  });
1036
1041
  else if (scanDomain) next = patchFeishuConfig(next, targetAccountId, { domain: scanDomain });
1037
1042
  next = applyNewAppSecurityPolicy(next, targetAccountId, scanOpenId, groupPolicy);
1038
- configProgress.stop("Bot configured.");
1043
+ configProgress.stop(t("wizard.feishu.botConfigured"));
1039
1044
  return { cfg: next };
1040
1045
  }
1041
1046
  async function runEditFlow(params) {
@@ -1057,7 +1062,7 @@ async function runEditFlow(params) {
1057
1062
  }, void 0);
1058
1063
  if (existingAppId) {
1059
1064
  if (!await prompter.confirm({
1060
- message: `We found an existing bot (App ID: ${existingAppId}). Use it for this setup?`,
1065
+ message: t("wizard.feishu.existingBotPrompt", { appId: existingAppId }),
1061
1066
  initialValue: true
1062
1067
  })) return runNewAppFlow({
1063
1068
  cfg: next,
@@ -1069,7 +1074,7 @@ async function runEditFlow(params) {
1069
1074
  prompter,
1070
1075
  options
1071
1076
  });
1072
- await prompter.note("Bot configured.", "");
1077
+ await prompter.note(t("wizard.feishu.botConfigured"), "");
1073
1078
  return { cfg: next };
1074
1079
  }
1075
1080
  async function runFeishuLogin(params) {
@@ -1095,10 +1100,10 @@ const feishuSetupWizard = {
1095
1100
  resolveAccountIdForConfigure: ({ accountOverride, defaultAccountId, cfg }) => (typeof accountOverride === "string" && accountOverride.trim() ? accountOverride.trim() : void 0) ?? resolveDefaultFeishuAccountId(cfg) ?? defaultAccountId,
1096
1101
  resolveShouldPromptAccountIds: () => false,
1097
1102
  status: {
1098
- configuredLabel: "configured",
1099
- unconfiguredLabel: "needs app credentials",
1100
- configuredHint: "configured",
1101
- unconfiguredHint: "needs app creds",
1103
+ configuredLabel: t("wizard.channels.statusConfigured"),
1104
+ unconfiguredLabel: t("wizard.channels.statusNeedsAppCredentials"),
1105
+ configuredHint: t("wizard.channels.statusConfigured"),
1106
+ unconfiguredHint: t("wizard.channels.statusNeedsAppCreds"),
1102
1107
  configuredScore: 2,
1103
1108
  unconfiguredScore: 0,
1104
1109
  resolveConfigured: ({ cfg }) => isFeishuConfigured(cfg),
@@ -1112,9 +1117,9 @@ const feishuSetupWizard = {
1112
1117
  const { probeFeishu } = await import("./probe-DpPNslkb.js").then((n) => n.n);
1113
1118
  probeResult = await probeFeishu(account);
1114
1119
  } catch {}
1115
- if (!configured) return ["Feishu: needs app credentials"];
1116
- if (probeResult?.ok) return [`Feishu: connected as ${probeResult.botName ?? probeResult.botOpenId ?? "bot"}`];
1117
- return ["Feishu: configured (connection not verified)"];
1120
+ if (!configured) return [formatFeishuStatusLine("needs-credentials")];
1121
+ if (probeResult?.ok) return [`Feishu: ${t("wizard.channels.statusConnectedAs", { name: probeResult.botName ?? probeResult.botOpenId ?? "bot" })}`];
1122
+ return [formatFeishuStatusLine("configured-unverified")];
1118
1123
  }
1119
1124
  },
1120
1125
  prepare: async ({ cfg, credentialValues }) => {
@@ -1183,7 +1188,7 @@ const meta = {
1183
1188
  aliases: ["lark"],
1184
1189
  order: 70
1185
1190
  };
1186
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BlJ_XJTS.js"), "feishuChannelRuntime");
1191
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-6Ww4ftMP.js"), "feishuChannelRuntime");
1187
1192
  function toFeishuMessageSendResult(result, kind) {
1188
1193
  const receipt = result.receipt ?? createFeishuSendReceipt({
1189
1194
  messageId: result.messageId,
@@ -2026,7 +2031,7 @@ const feishuPlugin = createChatChannelPlugin({
2026
2031
  })
2027
2032
  }),
2028
2033
  gateway: { startAccount: async (ctx) => {
2029
- const { monitorFeishuProvider } = await import("./monitor-Dfy3D2pM.js");
2034
+ const { monitorFeishuProvider } = await import("./monitor-BGTm4U2e.js");
2030
2035
  const account = resolveFeishuRuntimeAccount({
2031
2036
  cfg: ctx.cfg,
2032
2037
  accountId: ctx.accountId
@@ -1,2 +1,2 @@
1
- import { t as feishuPlugin } from "./channel-1pldyM9r.js";
1
+ import { t as feishuPlugin } from "./channel-DLhJC9t0.js";
2
2
  export { feishuPlugin };
@@ -1,9 +1,9 @@
1
1
  import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount, y as parseFeishuCommentTarget } from "./accounts-CP4tDW-z.js";
2
- import { g as listFeishuDirectoryPeers, h as listFeishuDirectoryGroups, v as createFeishuCardInteractionEnvelope } from "./channel-1pldyM9r.js";
2
+ import { g as listFeishuDirectoryPeers, h as listFeishuDirectoryGroups, v as createFeishuCardInteractionEnvelope } from "./channel-DLhJC9t0.js";
3
3
  import { r as createFeishuClient } from "./client-B18oTGHf.js";
4
4
  import { c as getChatInfo, l as getChatMembers, r as cleanupAmbientCommentTypingReaction, t as deliverCommentThreadText, u as getFeishuMemberInfo } from "./drive-Av1h_Lwd.js";
5
5
  import { chunkTextForOutbound } from "./runtime-api.js";
6
- import { a as sendCardFeishu, c as sendStructuredCardFeishu, g as shouldSuppressFeishuTextForVoiceMedia, h as sendMediaFeishu, i as resolveFeishuCardTemplate, n as getMessageFeishu, o as sendMarkdownCardFeishu, s as sendMessageFeishu, t as editMessageFeishu } from "./send-DsCQVFOE.js";
6
+ import { a as sendCardFeishu, c as sendStructuredCardFeishu, g as shouldSuppressFeishuTextForVoiceMedia, h as sendMediaFeishu, i as resolveFeishuCardTemplate, n as getMessageFeishu, o as sendMarkdownCardFeishu, s as sendMessageFeishu, t as editMessageFeishu } from "./send-Csj0slBG.js";
7
7
  import { t as probeFeishu } from "./probe-DpPNslkb.js";
8
8
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
9
9
  import { interactiveReplyToPresentation, normalizeInteractiveReply, normalizeMessagePresentation, renderMessagePresentationFallbackText, resolveInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
@@ -3,7 +3,7 @@ import { l as fetchBotIdentityForMonitor } from "./monitor.state-D6ByOM5W.js";
3
3
  //#region extensions/feishu/src/monitor.ts
4
4
  let monitorAccountRuntimePromise;
5
5
  async function loadMonitorAccountRuntime() {
6
- monitorAccountRuntimePromise ??= import("./monitor.account-CT3Skl8y.js");
6
+ monitorAccountRuntimePromise ??= import("./monitor.account-Cn20lZwr.js");
7
7
  return await monitorAccountRuntimePromise;
8
8
  }
9
9
  async function monitorFeishuProvider(opts = {}) {
@@ -2,12 +2,12 @@ import { t as buildFeishuConversationId } from "./conversation-id-Byq1c20x.js";
2
2
  import { i as resolveReceiveIdType } from "./targets-Bb05cFr4.js";
3
3
  import { n as createFeishuThreadBindingManager } from "./thread-bindings-D5kDxq_j.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-CP4tDW-z.js";
5
- import { _ as buildFeishuCardActionTextFallback, d as resolveFeishuGroupConfig, f as resolveFeishuGroupConversationIngressAccess, l as hasExplicitFeishuGroupConfig, m as resolveFeishuReplyPolicy, p as resolveFeishuGroupSenderActivationIngressAccess, u as resolveFeishuDmIngressAccess, v as createFeishuCardInteractionEnvelope, y as decodeFeishuCardAction } from "./channel-1pldyM9r.js";
5
+ import { _ as buildFeishuCardActionTextFallback, d as resolveFeishuGroupConfig, f as resolveFeishuGroupConversationIngressAccess, l as hasExplicitFeishuGroupConfig, m as resolveFeishuReplyPolicy, p as resolveFeishuGroupSenderActivationIngressAccess, u as resolveFeishuDmIngressAccess, v as createFeishuCardInteractionEnvelope, y as decodeFeishuCardAction } from "./channel-DLhJC9t0.js";
6
6
  import { t as getFeishuRuntime } from "./runtime-Cc16UY23.js";
7
7
  import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient } from "./client-B18oTGHf.js";
8
8
  import { c as getChatInfo, i as createCommentTypingReactionLifecycle, t as deliverCommentThreadText } from "./drive-Av1h_Lwd.js";
9
9
  import { buildAgentMediaPayload, createReplyPrefixContext, evaluateSupplementalContextVisibility, loadSessionStore, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode, resolveSessionStoreEntry } from "./runtime-api.js";
10
- 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 saveMessageResourceFeishu, n as getMessageFeishu, p as isFeishuGroupChatType, r as listFeishuThreadMessages, s as sendMessageFeishu, u as extractMentionTargets } from "./send-DsCQVFOE.js";
10
+ 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 saveMessageResourceFeishu, n as getMessageFeishu, p as isFeishuGroupChatType, r as listFeishuThreadMessages, s as sendMessageFeishu, u as extractMentionTargets } from "./send-Csj0slBG.js";
11
11
  import { i as waitForAbortableDelay, r as raceWithTimeoutAndAbort } from "./probe-DpPNslkb.js";
12
12
  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-D6ByOM5W.js";
13
13
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -29,7 +29,7 @@ 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";
32
- import { DEFAULT_GROUP_HISTORY_LIMIT, buildPendingHistoryContextFromMap, clearHistoryEntriesIfEnabled, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
32
+ import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
33
33
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
34
34
  import { formatReasoningMessage } from "openclaw/plugin-sdk/agent-runtime";
35
35
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
@@ -753,6 +753,11 @@ function shouldPushStreamingUpdate(previousText, nextText) {
753
753
  if (hasNaturalStreamingBoundary(nextText)) return true;
754
754
  return nextText.length - previousText.length >= STREAMING_SIGNIFICANT_DELTA_CHARS;
755
755
  }
756
+ function resolveStreamingCardAppendContent(previousText, nextText) {
757
+ if (!nextText || nextText === previousText) return "";
758
+ if (!previousText) return nextText;
759
+ return nextText.startsWith(previousText) ? nextText.slice(previousText.length) : nextText;
760
+ }
756
761
  function mergeStreamingText(previousText, nextText) {
757
762
  const previous = typeof previousText === "string" ? previousText : "";
758
763
  const next = typeof nextText === "string" ? nextText : "";
@@ -790,7 +795,7 @@ var FeishuStreamingSession = class {
790
795
  const apiBase = resolveApiBase(this.creds.domain);
791
796
  const elements = [{
792
797
  tag: "markdown",
793
- content: "⏳ Thinking...",
798
+ content: "",
794
799
  element_id: "content"
795
800
  }];
796
801
  if (options?.note) {
@@ -882,34 +887,82 @@ var FeishuStreamingSession = class {
882
887
  messageId: sendRes.data.message_id,
883
888
  sequence: 1,
884
889
  currentText: "",
890
+ sentText: "",
885
891
  hasNote: !!options?.note
886
892
  };
887
893
  this.log?.(`Started streaming: cardId=${cardId}, messageId=${sendRes.data.message_id}`);
888
894
  }
889
895
  async updateCardContent(text, onError) {
890
- if (!this.state) return;
896
+ if (!this.state) return false;
891
897
  const apiBase = resolveApiBase(this.creds.domain);
892
898
  this.state.sequence += 1;
893
- await fetchWithSsrFGuard({
894
- url: `${apiBase}/cardkit/v1/cards/${this.state.cardId}/elements/content/content`,
895
- init: {
896
- method: "PUT",
897
- headers: {
898
- Authorization: `Bearer ${await getToken(this.creds)}`,
899
- "Content-Type": "application/json",
900
- "User-Agent": getFeishuUserAgent()
899
+ try {
900
+ const { response, release } = await fetchWithSsrFGuard({
901
+ url: `${apiBase}/cardkit/v1/cards/${this.state.cardId}/elements/content/content`,
902
+ init: {
903
+ method: "PUT",
904
+ headers: {
905
+ Authorization: `Bearer ${await getToken(this.creds)}`,
906
+ "Content-Type": "application/json",
907
+ "User-Agent": getFeishuUserAgent()
908
+ },
909
+ body: JSON.stringify({
910
+ content: text,
911
+ sequence: this.state.sequence,
912
+ uuid: `s_${this.state.cardId}_${this.state.sequence}`
913
+ })
901
914
  },
902
- body: JSON.stringify({
903
- content: text,
904
- sequence: this.state.sequence,
905
- uuid: `s_${this.state.cardId}_${this.state.sequence}`
906
- })
907
- },
908
- policy: { allowedHostnames: resolveAllowedHostnames(this.creds.domain) },
909
- auditContext: "feishu.streaming-card.update"
910
- }).then(async ({ release }) => {
915
+ policy: { allowedHostnames: resolveAllowedHostnames(this.creds.domain) },
916
+ auditContext: "feishu.streaming-card.update"
917
+ });
911
918
  await release();
912
- }).catch((error) => onError?.(error));
919
+ if (!response.ok) {
920
+ onError?.(/* @__PURE__ */ new Error(`Update card content failed with HTTP ${response.status}`));
921
+ return false;
922
+ }
923
+ return true;
924
+ } catch (error) {
925
+ onError?.(error);
926
+ return false;
927
+ }
928
+ }
929
+ async replaceCardContent(text, onError) {
930
+ if (!this.state) return false;
931
+ const apiBase = resolveApiBase(this.creds.domain);
932
+ this.state.sequence += 1;
933
+ try {
934
+ const { response, release } = await fetchWithSsrFGuard({
935
+ url: `${apiBase}/cardkit/v1/cards/${this.state.cardId}/elements/content`,
936
+ init: {
937
+ method: "PUT",
938
+ headers: {
939
+ Authorization: `Bearer ${await getToken(this.creds)}`,
940
+ "Content-Type": "application/json",
941
+ "User-Agent": getFeishuUserAgent()
942
+ },
943
+ body: JSON.stringify({
944
+ element: JSON.stringify({
945
+ tag: "markdown",
946
+ content: text,
947
+ element_id: "content"
948
+ }),
949
+ sequence: this.state.sequence,
950
+ uuid: `r_${this.state.cardId}_${this.state.sequence}`
951
+ })
952
+ },
953
+ policy: { allowedHostnames: resolveAllowedHostnames(this.creds.domain) },
954
+ auditContext: "feishu.streaming-card.replace"
955
+ });
956
+ await release();
957
+ if (!response.ok) {
958
+ onError?.(/* @__PURE__ */ new Error(`Replace card content failed with HTTP ${response.status}`));
959
+ return false;
960
+ }
961
+ return true;
962
+ } catch (error) {
963
+ onError?.(error);
964
+ return false;
965
+ }
913
966
  }
914
967
  clearFlushTimer() {
915
968
  if (this.flushTimer) {
@@ -945,9 +998,11 @@ var FeishuStreamingSession = class {
945
998
  const nextText = this.pendingText ?? mergedInput;
946
999
  const mergedText = mergeStreamingText(this.state.currentText, nextText);
947
1000
  if (!mergedText || mergedText === this.state.currentText) return;
1001
+ const appendContent = resolveStreamingCardAppendContent(this.state.sentText, mergedText);
1002
+ if (!appendContent) return;
948
1003
  this.pendingText = null;
949
1004
  this.state.currentText = mergedText;
950
- await this.updateCardContent(mergedText, (e) => this.log?.(`Update failed: ${String(e)}`));
1005
+ if (await this.updateCardContent(appendContent, (e) => this.log?.(`Update failed: ${String(e)}`)) && this.state) this.state.sentText = mergedText;
951
1006
  });
952
1007
  await this.queue;
953
1008
  }
@@ -982,11 +1037,12 @@ var FeishuStreamingSession = class {
982
1037
  this.clearFlushTimer();
983
1038
  await this.queue;
984
1039
  const pendingMerged = mergeStreamingText(this.state.currentText, this.pendingText ?? void 0);
985
- const text = finalText ? mergeStreamingText(pendingMerged, finalText) : pendingMerged;
1040
+ const text = finalText ?? pendingMerged;
986
1041
  const apiBase = resolveApiBase(this.creds.domain);
987
- if (text && text !== this.state.currentText) {
988
- await this.updateCardContent(text);
1042
+ if (text && text !== this.state.sentText) {
1043
+ const sent = text.startsWith(this.state.sentText) ? await this.updateCardContent(resolveStreamingCardAppendContent(this.state.sentText, text), (e) => this.log?.(`Final update failed: ${String(e)}`)) : await this.replaceCardContent(text, (e) => this.log?.(`Final replace failed: ${String(e)}`));
989
1044
  this.state.currentText = text;
1045
+ if (sent) this.state.sentText = text;
990
1046
  }
991
1047
  if (options?.note) await this.updateNoteContent(options.note);
992
1048
  this.state.sequence += 1;
@@ -1978,8 +2034,7 @@ async function handleFeishuMessage(params) {
1978
2034
  }
1979
2035
  if (groupSenderActivationIngress.ingress.admission !== "dispatch") {
1980
2036
  log(`feishu[${account.accountId}]: message in group ${ctx.chatId} did not mention bot`);
1981
- if (!broadcastAgents && chatHistories && groupHistoryKey) recordPendingHistoryEntryIfEnabled({
1982
- historyMap: chatHistories,
2037
+ if (!broadcastAgents && chatHistories && groupHistoryKey) createChannelHistoryWindow({ historyMap: chatHistories }).record({
1983
2038
  historyKey: groupHistoryKey,
1984
2039
  limit: historyLimit,
1985
2040
  entry: {
@@ -2238,8 +2293,7 @@ async function handleFeishuMessage(params) {
2238
2293
  body: messageBody
2239
2294
  });
2240
2295
  const historyKey = groupHistoryKey;
2241
- if (isGroup && historyKey && chatHistories) combinedBody = buildPendingHistoryContextFromMap({
2242
- historyMap: chatHistories,
2296
+ if (isGroup && historyKey && chatHistories) combinedBody = createChannelHistoryWindow({ historyMap: chatHistories }).buildPendingContext({
2243
2297
  historyKey,
2244
2298
  limit: historyLimit,
2245
2299
  currentMessage: combinedBody,
@@ -2251,11 +2305,10 @@ async function handleFeishuMessage(params) {
2251
2305
  envelope: envelopeOptions
2252
2306
  })
2253
2307
  });
2254
- const inboundHistory = isGroup && historyKey && historyLimit > 0 && chatHistories ? (chatHistories.get(historyKey) ?? []).map((entry) => ({
2255
- sender: entry.sender,
2256
- body: entry.body,
2257
- timestamp: entry.timestamp
2258
- })) : void 0;
2308
+ const inboundHistory = isGroup && historyKey && historyLimit > 0 && chatHistories ? createChannelHistoryWindow({ historyMap: chatHistories }).buildInboundHistory({
2309
+ historyKey,
2310
+ limit: historyLimit
2311
+ }) : void 0;
2259
2312
  const threadContextBySessionKey = /* @__PURE__ */ new Map();
2260
2313
  let rootMessageInfo;
2261
2314
  let rootMessageThreadId;
@@ -2559,8 +2612,7 @@ async function handleFeishuMessage(params) {
2559
2612
  const results = await Promise.allSettled(broadcastAgents.map(dispatchForAgent));
2560
2613
  for (let i = 0; i < results.length; i++) if (results[i].status === "rejected") log(`feishu[${account.accountId}]: broadcast dispatch failed for agent=${broadcastAgents[i]}: ${String(results[i].reason)}`);
2561
2614
  }
2562
- if (isGroup && historyKey && chatHistories) clearHistoryEntriesIfEnabled({
2563
- historyMap: chatHistories,
2615
+ if (isGroup && historyKey && chatHistories) createChannelHistoryWindow({ historyMap: chatHistories }).clear({
2564
2616
  historyKey,
2565
2617
  limit: historyLimit
2566
2618
  });
@@ -1,6 +1,6 @@
1
1
  import { i as resolveReceiveIdType, r as normalizeFeishuTarget } from "./targets-Bb05cFr4.js";
2
2
  import { c as createFeishuApiError, f as isRecord$2, g as requestFeishuApi, s as resolveFeishuRuntimeAccount } from "./accounts-CP4tDW-z.js";
3
- import { c as toFeishuSendResult, o as assertFeishuMessageApiSuccess, s as resolveFeishuReceiptKind } from "./channel-1pldyM9r.js";
3
+ import { c as toFeishuSendResult, o as assertFeishuMessageApiSuccess, s as resolveFeishuReceiptKind } from "./channel-DLhJC9t0.js";
4
4
  import { t as getFeishuRuntime } from "./runtime-Cc16UY23.js";
5
5
  import { r as createFeishuClient } from "./client-B18oTGHf.js";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -455,6 +455,8 @@ async function transcodeToFeishuVoiceOpus(params) {
455
455
  "libopus",
456
456
  "-b:a",
457
457
  FEISHU_VOICE_BITRATE,
458
+ "-f",
459
+ "ogg",
458
460
  outputPath
459
461
  ]);
460
462
  }
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-1pldyM9r.js";
1
+ import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DLhJC9t0.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.14-beta.2",
3
+ "version": "2026.5.16-beta.2",
4
4
  "description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "openclaw": "workspace:*"
18
18
  },
19
19
  "peerDependencies": {
20
- "openclaw": ">=2026.5.14-beta.2"
20
+ "openclaw": ">=2026.5.16-beta.2"
21
21
  },
22
22
  "peerDependenciesMeta": {
23
23
  "openclaw": {
@@ -48,10 +48,10 @@
48
48
  "minHostVersion": ">=2026.4.25"
49
49
  },
50
50
  "compat": {
51
- "pluginApi": ">=2026.5.14-beta.2"
51
+ "pluginApi": ">=2026.5.16-beta.2"
52
52
  },
53
53
  "build": {
54
- "openclawVersion": "2026.5.14-beta.2"
54
+ "openclawVersion": "2026.5.16-beta.2"
55
55
  },
56
56
  "release": {
57
57
  "publishToClawHub": true,