@openclaw/feishu 2026.9.1-beta.1 → 2026.9.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 (47) hide show
  1. package/dist/{accounts-RwSyseKr.js → accounts-cCMNFBKg.js} +1 -1
  2. package/dist/api.js +222 -284
  3. package/dist/{app-registration-BpfUzS9m.js → app-registration-BI99e_JI.js} +12 -9
  4. package/dist/{channel-Dpgf8wLD.js → channel-VytDDz_B.js} +244 -582
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/{channel.runtime-BIEERwaT.js → channel.runtime-CAD74kBO.js} +15 -19
  7. package/dist/{chat-C1nuW5UY.js → chat-DDhIcPQy.js} +13 -13
  8. package/dist/{client-DalLmpMf.js → client-DMbIL3UH.js} +3 -6
  9. package/dist/contract-api.js +2 -2
  10. package/dist/doctor-contract-api.js +1 -1
  11. package/dist/doctor-contract-w6FpMypv.js +465 -0
  12. package/dist/index.js +11 -42
  13. package/dist/{json-response-CheljwGr.js → json-response-DXajw_FU.js} +1 -1
  14. package/dist/{monitor.startup-D-gL82OO.js → monitor-rPJ_Mz4M.js} +62 -2
  15. package/dist/{monitor.account-DhhotI_Q.js → monitor.account-v0XIDyfs.js} +270 -299
  16. package/dist/{probe-CfhhR_0a.js → probe-D_SatdZl.js} +3 -3
  17. package/dist/{presentation-card-C8Z7Tgru.js → reply-delivery-result-CYaKkbUN.js} +334 -240
  18. package/dist/{security-audit-D6Fz2h6p.js → security-audit-DKfR4Cv3.js} +1 -1
  19. package/dist/{security-audit-shared-BgpY7AiJ.js → security-audit-shared-CK5rVR-1.js} +2 -1
  20. package/dist/security-contract-api.js +1 -1
  21. package/dist/session-binding-contract-api.js +1 -1
  22. package/dist/setup-api.js +1 -1
  23. package/dist/{subagent-hooks-KUi-7vUy.js → subagent-hooks-Buadpie_.js} +3 -6
  24. package/dist/subagent-hooks-api.js +1 -1
  25. package/dist/{targets-BUjQ1TcA.js → targets-BTQCYRZQ.js} +4 -4
  26. package/dist/{thread-bindings-CptM_YU6.js → thread-bindings-BiL1wGqK.js} +20 -9
  27. package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/lib/permessage-deflate.js +3 -1
  28. package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/package.json +1 -1
  29. package/node_modules/typebox/build/format/email.mjs +1 -1
  30. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
  31. package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
  32. package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
  33. package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
  34. package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
  35. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  36. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  37. package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
  38. package/node_modules/typebox/build/system/memory/create.mjs +3 -4
  39. package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
  40. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  41. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  42. package/node_modules/typebox/build/system/memory/update.mjs +3 -2
  43. package/node_modules/typebox/package.json +1 -1
  44. package/openclaw.plugin.json +45 -238
  45. package/package.json +6 -5
  46. package/dist/doctor-contract-bEQXIXyP.js +0 -158
  47. package/dist/monitor-BRg3sHH7.js +0 -62
@@ -1,12 +1,12 @@
1
- import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-RwSyseKr.js";
2
- import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BUjQ1TcA.js";
3
- import { $ as normalizeFeishuExternalKey, D as resolveFeishuIdentityHeaderTitle, G as parseFeishuCommentTarget, I as cleanupAmbientCommentTypingReaction, J as sanitizeNativeFeishuCard, K as readNativeFeishuCardJson, O as deliverCommentThreadText, T as buildFeishuMediaFallbackText, X as chunkFeishuPostMarkdown, Y as chunkFeishuMarkdown, Z as materializeFeishuPostMarkdownSoftBreaks, _ as sendStructuredCardFeishu, a as isFeishuCardWithinEnvelope, at as isFeishuGroupReadEnabled, c as sendMediaFeishu, et as assertFeishuChatReadAllowed, g as sendMessageFeishu, gt as resolveFeishuChatType, h as sendMarkdownCardFeishu, ht as normalizeFeishuChatType, i as buildFeishuPresentationCardElements, it as isFeishuGroupReadAllowed, m as sendCardFeishu, n as buildFeishuCommentPresentationFallback, nt as canEnumerateAllFeishuGroups, o as resolveFeishuRichReply, ot as resolveFeishuChatReadPreliminaryAuthorization, pt as resolveFeishuGroupToolPolicy, q as resolveFeishuCardTemplate, r as buildFeishuPresentationCard, rt as canEnumerateAllFeishuPeers, t as assertFeishuCardWithinEnvelope, tt as authorizeFeishuChatMemberRead, u as shouldSuppressFeishuTextForVoiceMedia, ut as resolveFeishuGroupConfig, w as createFeishuSendReceipt } from "./presentation-card-C8Z7Tgru.js";
4
- import { i as normalizeFeishuWebhookPath, n as normalizeCompatibilityConfig, r as DEFAULT_FEISHU_WEBHOOK_PATH, t as legacyConfigRules } from "./doctor-contract-bEQXIXyP.js";
1
+ import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
2
+ import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BTQCYRZQ.js";
3
+ import { B as deliverCommentThreadText, Ct as resolveFeishuGroupToolPolicy, D as sendMessageFeishu, E as sendCardFeishu, Et as resolveFeishuChatType, F as createFeishuSendReceipt, J as cleanupAmbientCommentTypingReaction, L as buildFeishuMediaFallbackText, O as sendStructuredCardFeishu, S as chunkFeishuCardMarkdown, Tt as normalizeFeishuChatType, _ as resolveFeishuRichReply, a as FEISHU_PRESENTATION_CAPABILITIES, bt as resolveFeishuGroupConfig, c as buildFeishuPresentationCard, ct as materializeFeishuPostMarkdownSoftBreaks, d as isFeishuCardWithinEnvelope, dt as authorizeFeishuChatMemberRead, f as markRenderedFeishuCard, ft as canEnumerateAllFeishuGroups, gt as resolveFeishuChatReadPreliminaryAuthorization, h as renderFeishuPresentationPayload, ht as isFeishuGroupReadEnabled, it as readNativeFeishuCardJson, l as buildFeishuPresentationFallback, m as renderFeishuPresentationFallbackText, mt as isFeishuGroupReadAllowed, n as createFeishuReplyDeliveryResult, o as assertFeishuCardWithinEnvelope, ot as chunkFeishuMarkdown, p as readNativeFeishuCard, pt as canEnumerateAllFeishuPeers, rt as parseFeishuCommentTarget, s as buildFeishuPayloadCard, st as chunkFeishuPostMarkdown, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as assertFeishuChatReadAllowed, x as shouldSuppressFeishuTextForVoiceMedia, y as sendMediaFeishu, z as resolveFeishuIdentityHeaderTitle } from "./reply-delivery-result-CYaKkbUN.js";
4
+ import { n as normalizeCompatibilityConfig, o as normalizeFeishuExternalKey, r as FeishuChannelConfigSchema, t as legacyConfigRules } from "./doctor-contract-w6FpMypv.js";
5
5
  import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, o as resolveConfiguredFeishuGroupSessionScope, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
6
- import { t as messageActionTargetAliases } from "./security-audit-D6Fz2h6p.js";
7
- import { r as createFeishuClient } from "./client-DalLmpMf.js";
6
+ import { t as messageActionTargetAliases } from "./security-audit-DKfR4Cv3.js";
7
+ import { r as createFeishuClient } from "./client-DMbIL3UH.js";
8
8
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-BCpDLdg9.js";
9
- import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-BgpY7AiJ.js";
9
+ import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-CK5rVR-1.js";
10
10
  import { t as resolveFeishuSessionConversation } from "./session-conversation-DFCIvQK-.js";
11
11
  import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
12
12
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
@@ -16,23 +16,19 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
16
16
  import { ToolAuthorizationError, createActionGate } from "openclaw/plugin-sdk/channel-actions";
17
17
  import { adaptScopedAccountAccessor, createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
18
18
  import { buildChannelOutboundSessionRoute, createChatChannelPlugin, stripChannelTargetPrefix } from "openclaw/plugin-sdk/channel-core";
19
- import { createAccountStatusSink, createReplyToFanout, createRuntimeOutboundDelegates, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
19
+ import { createAccountStatusSink, createMessageReceiptFromOutboundResults, createReplyToFanout, createRuntimeOutboundDelegates, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
20
20
  import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
21
21
  import { createAllowlistProviderGroupPolicyWarningCollector, createConditionalWarningCollector } from "openclaw/plugin-sdk/channel-policy";
22
22
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
23
23
  import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
24
24
  import { applyDirectoryQueryAndLimit, createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter, listDirectoryGroupEntriesFromMapKeysAndAllowFrom, listDirectoryUserEntriesFromAllowFrom, listDirectoryUserEntriesFromAllowFromAndMapKeys } from "openclaw/plugin-sdk/directory-runtime";
25
25
  import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
26
- import { renderMessagePresentationFallbackText, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
26
+ import { resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
27
27
  import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
28
28
  import { buildProbeChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
29
29
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
30
30
  import { convertMarkdownTables, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
31
31
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
32
- import { normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/account-id";
33
- import { DmPolicySchema, GroupPolicySchema, ReplyToModeSchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
34
- import { z } from "zod";
35
- import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$2 } from "openclaw/plugin-sdk/secret-input";
36
32
  import fs from "node:fs";
37
33
  import os from "node:os";
38
34
  import path from "node:path";
@@ -41,17 +37,19 @@ import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
41
37
  import { deleteSessionEntry, isValidAgentHarnessSessionStoreEntry, listSessionEntries, loadTranscriptEventsSync, resolveSessionStoreBackupPaths, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
42
38
  import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
43
39
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
44
- import { createChannelPartialDeliveryError, isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
40
+ import { isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
45
41
  import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
46
42
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
47
43
  import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
48
44
  import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
45
+ import { readStringParam } from "openclaw/plugin-sdk/param-readers";
49
46
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
50
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchScopedAccountConfig, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
47
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput, mergeAllowFromEntries, patchScopedAccountConfig, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
51
48
  import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
52
49
  //#region extensions/feishu/src/approval-auth.ts
53
50
  function normalizeFeishuApproverId(value) {
54
- const trimmed = normalizeOptionalLowercaseString(normalizeFeishuTarget(String(value)));
51
+ const normalized = normalizeFeishuTarget(String(value));
52
+ const trimmed = normalizeOptionalLowercaseString(normalized);
55
53
  return trimmed?.startsWith("ou_") ? trimmed : void 0;
56
54
  }
57
55
  const feishuApprovalAuth = createChannelApprovalAuth({
@@ -65,282 +63,6 @@ const feishuApprovalAuth = createChannelApprovalAuth({
65
63
  normalizeApprover: normalizeFeishuApproverId
66
64
  }).approvalAuth;
67
65
  //#endregion
68
- //#region extensions/feishu/src/config-schema.ts
69
- const ChannelActionsSchema = z.object({
70
- reactions: z.boolean().optional(),
71
- sticker: z.boolean().optional()
72
- }).strict().optional();
73
- const FeishuGroupPolicySchema = z.union([GroupPolicySchema, z.literal("allowall").transform(() => "open")]);
74
- const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().url().startsWith("https://")]);
75
- const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
76
- const FeishuWebhookPathSchema = z.string().refine((value) => normalizeFeishuWebhookPath(value) === value, { message: "webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\" to repair it" });
77
- const TtsOverrideSchema = z.object({
78
- auto: z.enum([
79
- "off",
80
- "always",
81
- "inbound",
82
- "tagged"
83
- ]).optional(),
84
- enabled: z.boolean().optional(),
85
- mode: z.enum(["final", "all"]).optional(),
86
- provider: z.string().optional(),
87
- persona: z.string().optional(),
88
- personas: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
89
- summaryModel: z.string().optional(),
90
- modelOverrides: z.record(z.string(), z.unknown()).optional(),
91
- providers: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
92
- prefsPath: z.string().optional(),
93
- maxTextLength: z.number().int().min(1).optional(),
94
- timeoutMs: z.number().int().min(1e3).max(12e4).optional()
95
- }).strict().optional();
96
- const ToolPolicySchema = z.object({
97
- allow: z.array(z.string()).optional(),
98
- deny: z.array(z.string()).optional()
99
- }).strict().optional();
100
- const DmConfigSchema = z.object({
101
- enabled: z.boolean().optional(),
102
- systemPrompt: z.string().optional()
103
- }).strict().optional();
104
- const MarkdownConfigSchema = z.object({
105
- mode: z.enum([
106
- "native",
107
- "escape",
108
- "strip"
109
- ]).optional(),
110
- tableMode: z.enum([
111
- "native",
112
- "ascii",
113
- "simple"
114
- ]).optional()
115
- }).strict().optional();
116
- const RenderModeSchema = z.enum([
117
- "auto",
118
- "raw",
119
- "card"
120
- ]).optional();
121
- const BlockStreamingCoalesceSchema = z.object({
122
- minChars: z.number().int().positive().optional(),
123
- maxChars: z.number().int().positive().optional(),
124
- idleMs: z.number().int().nonnegative().optional()
125
- }).strict().optional();
126
- const FeishuStreamingSchema = z.object({
127
- mode: z.enum(["off", "partial"]).optional(),
128
- chunkMode: z.enum(["length", "newline"]).optional(),
129
- block: z.object({
130
- enabled: z.boolean().optional(),
131
- coalesce: BlockStreamingCoalesceSchema
132
- }).strict().optional()
133
- }).strict().optional();
134
- const ChannelHeartbeatVisibilitySchema = z.object({
135
- visibility: z.enum(["visible", "hidden"]).optional(),
136
- intervalMs: z.number().int().positive().optional()
137
- }).strict().optional();
138
- /**
139
- * Dynamic agent creation configuration.
140
- * When enabled, a new agent is created for each unique DM user.
141
- */
142
- const DynamicAgentCreationSchema = z.object({
143
- enabled: z.boolean().optional(),
144
- workspaceTemplate: z.string().optional(),
145
- agentDirTemplate: z.string().optional(),
146
- maxAgents: z.number().int().positive().optional()
147
- }).strict().optional();
148
- /**
149
- * Feishu tools configuration.
150
- * Controls which tool categories are enabled.
151
- *
152
- * Dependencies:
153
- * - wiki requires doc (wiki content is edited via doc tools)
154
- * - perm can work independently but is typically used with drive
155
- */
156
- const FeishuToolsConfigSchema = z.object({
157
- doc: z.boolean().optional(),
158
- chat: z.boolean().optional(),
159
- wiki: z.boolean().optional(),
160
- drive: z.boolean().optional(),
161
- perm: z.boolean().optional(),
162
- scopes: z.boolean().optional(),
163
- bitable: z.boolean().optional()
164
- }).strict().optional();
165
- /**
166
- * Group session scope for routing Feishu group messages.
167
- * - "group" (default): one session per group chat
168
- * - "group_sender": one session per (group + sender)
169
- * - "group_topic": one session per group topic thread (falls back to group if no topic)
170
- * - "group_topic_sender": one session per (group + topic thread + sender),
171
- * falls back to (group + sender) if no topic
172
- */
173
- const GroupSessionScopeSchema = z.enum([
174
- "group",
175
- "group_sender",
176
- "group_topic",
177
- "group_topic_sender"
178
- ]).optional();
179
- /**
180
- * @deprecated Use groupSessionScope instead.
181
- *
182
- * Topic session isolation mode for group chats.
183
- * - "disabled" (default): All messages in a group share one session
184
- * - "enabled": Messages in different topics get separate sessions
185
- *
186
- * Topic routing uses Feishu topic-group `thread_id` when the event identifies a
187
- * native topic group, and keeps `root_id` precedence for normal groups so
188
- * reply-created threads stay on the initiating message session.
189
- */
190
- const TopicSessionModeSchema = z.enum(["disabled", "enabled"]).optional();
191
- const ReactionNotificationModeSchema = z.enum([
192
- "off",
193
- "own",
194
- "all"
195
- ]).optional();
196
- /**
197
- * Reply-in-thread mode for group chats.
198
- * - "disabled" (default): Bot replies are normal inline replies
199
- * - "enabled": Bot replies create or continue a Feishu topic thread
200
- *
201
- * When enabled, the Feishu reply API is called with `reply_in_thread: true`,
202
- * causing the reply to appear as a topic (话题) under the original message.
203
- */
204
- const ReplyInThreadSchema = z.enum(["disabled", "enabled"]).optional();
205
- const FeishuGroupSchema = buildGroupEntrySchema({
206
- tools: ToolPolicySchema,
207
- groupSessionScope: GroupSessionScopeSchema,
208
- topicSessionMode: TopicSessionModeSchema,
209
- replyInThread: ReplyInThreadSchema
210
- }).omit({ toolsBySender: true });
211
- const FeishuSharedConfigShape = {
212
- webhookHost: z.string().optional(),
213
- webhookPort: z.number().int().positive().optional(),
214
- capabilities: z.array(z.string()).optional(),
215
- markdown: MarkdownConfigSchema,
216
- configWrites: z.boolean().optional(),
217
- replyToMode: ReplyToModeSchema.optional(),
218
- responsePrefix: z.string().optional(),
219
- dmPolicy: DmPolicySchema.optional(),
220
- allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
221
- groupPolicy: FeishuGroupPolicySchema.optional(),
222
- groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
223
- groupSenderAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
224
- requireMention: z.boolean().optional(),
225
- groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
226
- historyLimit: z.number().int().min(0).optional(),
227
- dmHistoryLimit: z.number().int().min(0).optional(),
228
- dms: z.record(z.string(), DmConfigSchema).optional(),
229
- textChunkLimit: z.number().int().positive().optional(),
230
- mediaMaxMb: z.number().positive().optional(),
231
- httpTimeoutMs: z.number().int().positive().max(3e5).optional(),
232
- heartbeatVisibility: ChannelHeartbeatVisibilitySchema,
233
- renderMode: RenderModeSchema,
234
- streaming: FeishuStreamingSchema,
235
- tools: FeishuToolsConfigSchema,
236
- actions: ChannelActionsSchema,
237
- replyInThread: ReplyInThreadSchema,
238
- reactionNotifications: ReactionNotificationModeSchema,
239
- typingIndicator: z.boolean().optional(),
240
- resolveSenderNames: z.boolean().optional(),
241
- allowBots: z.boolean().optional(),
242
- vcAutoJoin: z.boolean().optional(),
243
- tts: TtsOverrideSchema
244
- };
245
- /**
246
- * Per-account configuration.
247
- * All fields are optional - missing fields inherit from top-level config.
248
- */
249
- const FeishuAccountConfigSchema = z.object({
250
- enabled: z.boolean().optional(),
251
- name: z.string().optional(),
252
- appId: z.string().optional(),
253
- appSecret: buildSecretInputSchema().optional(),
254
- encryptKey: buildSecretInputSchema().optional(),
255
- verificationToken: buildSecretInputSchema().optional(),
256
- domain: FeishuDomainSchema.optional(),
257
- connectionMode: FeishuConnectionModeSchema.optional(),
258
- webhookPath: FeishuWebhookPathSchema.optional(),
259
- ...FeishuSharedConfigShape,
260
- groupSessionScope: GroupSessionScopeSchema,
261
- topicSessionMode: TopicSessionModeSchema
262
- }).strict();
263
- const FeishuChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(z.object({
264
- enabled: z.boolean().optional(),
265
- defaultAccount: z.string().optional(),
266
- appId: z.string().optional(),
267
- appSecret: buildSecretInputSchema().optional(),
268
- encryptKey: buildSecretInputSchema().optional(),
269
- verificationToken: buildSecretInputSchema().optional(),
270
- domain: FeishuDomainSchema.optional().default("feishu"),
271
- connectionMode: FeishuConnectionModeSchema.optional().default("websocket"),
272
- webhookPath: FeishuWebhookPathSchema.optional().default(DEFAULT_FEISHU_WEBHOOK_PATH),
273
- ...FeishuSharedConfigShape,
274
- dmPolicy: DmPolicySchema.optional().default("pairing"),
275
- reactionNotifications: ReactionNotificationModeSchema.optional().default("own"),
276
- groupPolicy: FeishuGroupPolicySchema.optional().default("allowlist"),
277
- requireMention: z.boolean().optional(),
278
- groupSessionScope: GroupSessionScopeSchema,
279
- topicSessionMode: TopicSessionModeSchema,
280
- dynamicAgentCreation: DynamicAgentCreationSchema,
281
- typingIndicator: z.boolean().optional().default(true),
282
- resolveSenderNames: z.boolean().optional().default(true)
283
- }).strict(), {
284
- accountSchema: FeishuAccountConfigSchema,
285
- optionalAccount: true
286
- }).superRefine((value, ctx) => {
287
- const defaultAccount = value.defaultAccount?.trim();
288
- if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
289
- const normalizedDefaultAccount = normalizeAccountId$1(defaultAccount);
290
- if (!Object.hasOwn(value.accounts, normalizedDefaultAccount)) ctx.addIssue({
291
- code: z.ZodIssueCode.custom,
292
- path: ["defaultAccount"],
293
- message: `channels.feishu.defaultAccount="${defaultAccount}" does not match a configured account key`
294
- });
295
- }
296
- const defaultConnectionMode = value.connectionMode ?? "websocket";
297
- const defaultVerificationTokenConfigured = hasConfiguredSecretInput$2(value.verificationToken);
298
- const defaultEncryptKeyConfigured = hasConfiguredSecretInput$2(value.encryptKey);
299
- if (defaultConnectionMode === "webhook") {
300
- if (!defaultVerificationTokenConfigured) ctx.addIssue({
301
- code: z.ZodIssueCode.custom,
302
- path: ["verificationToken"],
303
- message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.verificationToken"
304
- });
305
- if (!defaultEncryptKeyConfigured) ctx.addIssue({
306
- code: z.ZodIssueCode.custom,
307
- path: ["encryptKey"],
308
- message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.encryptKey"
309
- });
310
- }
311
- for (const [accountId, account] of Object.entries(value.accounts ?? {})) {
312
- if (!account) continue;
313
- if ((account.connectionMode ?? defaultConnectionMode) !== "webhook") continue;
314
- const accountVerificationTokenConfigured = hasConfiguredSecretInput$2(account.verificationToken) || defaultVerificationTokenConfigured;
315
- const accountEncryptKeyConfigured = hasConfiguredSecretInput$2(account.encryptKey) || defaultEncryptKeyConfigured;
316
- if (!accountVerificationTokenConfigured) ctx.addIssue({
317
- code: z.ZodIssueCode.custom,
318
- path: [
319
- "accounts",
320
- accountId,
321
- "verificationToken"
322
- ],
323
- message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires a verificationToken (account-level or top-level)`
324
- });
325
- if (!accountEncryptKeyConfigured) ctx.addIssue({
326
- code: z.ZodIssueCode.custom,
327
- path: [
328
- "accounts",
329
- accountId,
330
- "encryptKey"
331
- ],
332
- message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires an encryptKey (account-level or top-level)`
333
- });
334
- }
335
- if (value.dmPolicy === "open") {
336
- if (!(value.allowFrom ?? []).some((entry) => String(entry).trim() === "*")) ctx.addIssue({
337
- code: z.ZodIssueCode.custom,
338
- path: ["allowFrom"],
339
- message: "channels.feishu.dmPolicy=\"open\" requires channels.feishu.allowFrom to include \"*\""
340
- });
341
- }
342
- }), { jsonSchemaMode: "input" });
343
- //#endregion
344
66
  //#region extensions/feishu/src/directory.static.ts
345
67
  function toFeishuDirectoryPeers(ids) {
346
68
  return ids.map((id) => ({
@@ -381,11 +103,12 @@ async function listFeishuDirectoryGroups(params) {
381
103
  }).map((entry) => entry.id));
382
104
  }
383
105
  async function listAuthorizedFeishuDirectoryPeers(params) {
106
+ const account = resolveFeishuAccount({
107
+ cfg: params.cfg,
108
+ accountId: params.accountId
109
+ });
384
110
  return toFeishuDirectoryPeers(listDirectoryUserEntriesFromAllowFrom({
385
- allowFrom: resolveFeishuAccount({
386
- cfg: params.cfg,
387
- accountId: params.accountId
388
- }).config.allowFrom,
111
+ allowFrom: account.config.allowFrom,
389
112
  query: params.query,
390
113
  limit: params.limit,
391
114
  normalizeId: (entry) => normalizeFeishuTarget(entry) ?? entry
@@ -396,17 +119,19 @@ async function listAuthorizedFeishuDirectoryGroups(params) {
396
119
  cfg: params.cfg,
397
120
  accountId: params.accountId
398
121
  });
399
- return toFeishuDirectoryGroups(applyDirectoryQueryAndLimit(listDirectoryGroupEntriesFromMapKeysAndAllowFrom({
400
- groups: Object.fromEntries(Object.entries(account.config.groups ?? {}).filter(([, group]) => group?.enabled !== false)),
122
+ const enabledGroups = Object.fromEntries(Object.entries(account.config.groups ?? {}).filter(([, group]) => group?.enabled !== false));
123
+ const authorizedEntries = listDirectoryGroupEntriesFromMapKeysAndAllowFrom({
124
+ groups: enabledGroups,
401
125
  allowFrom: account.config.groupAllowFrom
402
- }).filter((entry) => isFeishuGroupReadAllowed(params.cfg, account, entry.id, false)).map((entry) => entry.id), params));
126
+ }).filter((entry) => isFeishuGroupReadAllowed(params.cfg, account, entry.id, false));
127
+ return toFeishuDirectoryGroups(applyDirectoryQueryAndLimit(authorizedEntries.map((entry) => entry.id), params));
403
128
  }
404
129
  //#endregion
405
130
  //#region extensions/feishu/src/doctor.ts
406
131
  const FEISHU_STATE_DIR = "feishu";
407
132
  const BACKUP_PREFIX = "feishu-state-repair";
408
133
  const BLANK_USER_MESSAGE_REPAIR_THRESHOLD = 3;
409
- const SESSION_FILE_INSPECTION_MAX_BYTES = 16 * 1024 * 1024;
134
+ const SESSION_FILE_INSPECTION_MAX_BYTES = 16777216;
410
135
  function timestampForPath(now = /* @__PURE__ */ new Date()) {
411
136
  return now.toISOString().replaceAll(":", "-");
412
137
  }
@@ -470,9 +195,12 @@ function isFeishuSessionEntry(key, value) {
470
195
  if (isFeishuSessionStoreKey(key)) return true;
471
196
  if (!isRecord(value)) return false;
472
197
  if (normalizeLowercaseStringOrEmpty(value.channel) === "feishu" || normalizeLowercaseStringOrEmpty(value.lastChannel) === "feishu") return true;
473
- if (normalizeLowercaseStringOrEmpty((isRecord(value.route) ? value.route : null)?.channel) === "feishu") return true;
474
- if (normalizeLowercaseStringOrEmpty((isRecord(value.deliveryContext) ? value.deliveryContext : null)?.channel) === "feishu") return true;
475
- if (normalizeLowercaseStringOrEmpty((isRecord(value.pendingFinalDeliveryContext) ? value.pendingFinalDeliveryContext : null)?.channel) === "feishu") return true;
198
+ const route = isRecord(value.route) ? value.route : null;
199
+ if (normalizeLowercaseStringOrEmpty(route?.channel) === "feishu") return true;
200
+ const deliveryContext = isRecord(value.deliveryContext) ? value.deliveryContext : null;
201
+ if (normalizeLowercaseStringOrEmpty(deliveryContext?.channel) === "feishu") return true;
202
+ const pendingDeliveryContext = isRecord(value.pendingFinalDeliveryContext) ? value.pendingFinalDeliveryContext : null;
203
+ if (normalizeLowercaseStringOrEmpty(pendingDeliveryContext?.channel) === "feishu") return true;
476
204
  const origin = isRecord(value.origin) ? value.origin : null;
477
205
  const originProvider = normalizeLowercaseStringOrEmpty(origin?.provider);
478
206
  const originSurface = normalizeLowercaseStringOrEmpty(origin?.surface);
@@ -1003,8 +731,6 @@ const feishuDoctor = {
1003
731
  };
1004
732
  //#endregion
1005
733
  //#region extensions/feishu/src/outbound.ts
1006
- const RENDERED_FEISHU_CARD = Symbol("openclaw.renderedFeishuCard");
1007
- const FEISHU_PRESENTATION_FALLBACK_MARKER = "__openclawPresentationFallback";
1008
734
  const FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER = "__openclawPropagateMediaUploadFailure";
1009
735
  const FEISHU_TEXT_CHUNK_LIMIT = 4e3;
1010
736
  function normalizePossibleLocalImagePath(text) {
@@ -1036,22 +762,6 @@ function normalizePossibleLocalImagePath(text) {
1036
762
  function shouldUseCard(text) {
1037
763
  return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text);
1038
764
  }
1039
- function markRenderedFeishuCard(card) {
1040
- Object.defineProperty(card, RENDERED_FEISHU_CARD, {
1041
- value: true,
1042
- enumerable: false
1043
- });
1044
- return card;
1045
- }
1046
- function readNativeFeishuCard(payload) {
1047
- const feishuData = payload.channelData?.feishu;
1048
- if (!isRecord(feishuData)) return;
1049
- const card = feishuData.card ?? feishuData.interactiveCard;
1050
- if (!isRecord(card)) return;
1051
- if (card[RENDERED_FEISHU_CARD] === true) return card;
1052
- const sanitizedCard = sanitizeNativeFeishuCard(card);
1053
- return sanitizedCard ? markRenderedFeishuCard(sanitizedCard) : void 0;
1054
- }
1055
765
  function toFeishuOutboundResult(result) {
1056
766
  const { chatId, ...delivery } = result;
1057
767
  return {
@@ -1066,25 +776,26 @@ async function reportFeishuOutboundDelivery(result, onDeliveryResult) {
1066
776
  await onDeliveryResult?.(attachChannelToResult("feishu", toFeishuOutboundResult(result)));
1067
777
  return result;
1068
778
  }
1069
- function consumeFeishuPresentationFallbackMarker(payload) {
1070
- const feishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
1071
- if (feishuData?.[FEISHU_PRESENTATION_FALLBACK_MARKER] !== true) return {
1072
- payload,
1073
- presentationFallback: false
1074
- };
1075
- const nextFeishuData = { ...feishuData };
1076
- delete nextFeishuData[FEISHU_PRESENTATION_FALLBACK_MARKER];
1077
- const nextChannelData = { ...payload.channelData };
1078
- if (Object.keys(nextFeishuData).length > 0) nextChannelData.feishu = nextFeishuData;
1079
- else delete nextChannelData.feishu;
779
+ function aggregateFeishuSendResult(result, results) {
1080
780
  return {
1081
- payload: {
1082
- ...payload,
1083
- channelData: Object.keys(nextChannelData).length > 0 ? nextChannelData : void 0
1084
- },
1085
- presentationFallback: true
781
+ ...result,
782
+ receipt: {
783
+ ...createMessageReceiptFromOutboundResults({ results }),
784
+ primaryPlatformMessageId: result.messageId
785
+ }
1086
786
  };
1087
787
  }
788
+ function partialFeishuSendError(error, results) {
789
+ if (results.length === 0 && error instanceof Error) return error;
790
+ const accepted = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
791
+ return createFeishuPartialReplyDeliveryError(error, {
792
+ ...accepted,
793
+ ...createFeishuReplyDeliveryResult({
794
+ results: [...results, accepted],
795
+ visibleReplySent: results.length > 0 || accepted !== void 0
796
+ })
797
+ });
798
+ }
1088
799
  function readFeishuPropagateMediaUploadFailure(payload) {
1089
800
  return (isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.[FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER] === true;
1090
801
  }
@@ -1092,87 +803,6 @@ function buildFeishuPropagationOnlyChannelData(payload) {
1092
803
  if ((isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.["__openclawPropagateMediaUploadFailure"] !== true) return;
1093
804
  return { feishu: { [FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER]: true } };
1094
805
  }
1095
- function buildFeishuPayloadCard(params) {
1096
- const nativeCard = readNativeFeishuCard(params.payload);
1097
- if (nativeCard) {
1098
- assertFeishuCardWithinEnvelope(nativeCard, "Feishu native card");
1099
- return nativeCard;
1100
- }
1101
- const rawText = params.text ?? params.payload.text;
1102
- const textCard = readNativeFeishuCardJson(rawText);
1103
- const { interactive, presentation } = resolveFeishuRichReply(params.payload);
1104
- if (!presentation && !interactive) {
1105
- if (!textCard) return;
1106
- assertFeishuCardWithinEnvelope(textCard, "Feishu native card");
1107
- return markRenderedFeishuCard(textCard);
1108
- }
1109
- const text = textCard ? void 0 : resolveLegacyInteractiveTextFallback({
1110
- text: rawText,
1111
- interactive
1112
- });
1113
- const elements = presentation ? buildFeishuPresentationCardElements({
1114
- presentation,
1115
- fallbackText: text
1116
- }) : [{
1117
- tag: "markdown",
1118
- content: renderMessagePresentationFallbackText({
1119
- text,
1120
- presentation
1121
- })
1122
- }];
1123
- const identityTitle = resolveFeishuIdentityHeaderTitle(params.identity);
1124
- const title = presentation?.title ?? identityTitle;
1125
- const template = resolveFeishuCardTemplate(presentation?.tone === "danger" ? "red" : presentation?.tone === "warning" ? "orange" : presentation?.tone === "success" ? "green" : "blue");
1126
- const card = markRenderedFeishuCard({
1127
- schema: "2.0",
1128
- config: { width_mode: "fill" },
1129
- ...title ? { header: {
1130
- title: {
1131
- tag: "plain_text",
1132
- content: title
1133
- },
1134
- template: template ?? "blue"
1135
- } } : {},
1136
- body: { elements }
1137
- });
1138
- return isFeishuCardWithinEnvelope(card) ? card : void 0;
1139
- }
1140
- function renderFeishuPresentationPayload({ payload, presentation, ctx }) {
1141
- const { fallbackText, fallbackHasCommand } = buildFeishuCommentPresentationFallback({
1142
- text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
1143
- presentation
1144
- });
1145
- const card = buildFeishuPayloadCard({
1146
- payload,
1147
- text: payload.text,
1148
- identity: ctx.identity
1149
- });
1150
- const existingFeishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
1151
- if (!card) return {
1152
- ...payload,
1153
- text: fallbackText,
1154
- channelData: {
1155
- ...payload.channelData,
1156
- feishu: {
1157
- ...existingFeishuData,
1158
- [FEISHU_PRESENTATION_FALLBACK_MARKER]: true,
1159
- ...fallbackHasCommand ? { fallbackHasCommand: true } : {}
1160
- }
1161
- }
1162
- };
1163
- return {
1164
- ...payload,
1165
- text: fallbackText,
1166
- channelData: {
1167
- ...payload.channelData,
1168
- feishu: {
1169
- ...existingFeishuData,
1170
- card,
1171
- ...fallbackHasCommand ? { fallbackHasCommand: true } : {}
1172
- }
1173
- }
1174
- };
1175
- }
1176
806
  function resolveFeishuReplyMode(params) {
1177
807
  const replyToMessageId = params.replyToId?.trim();
1178
808
  if (replyToMessageId) return {
@@ -1194,10 +824,11 @@ function resolveFeishuReplyMode(params) {
1194
824
  async function sendCommentThreadReply(params) {
1195
825
  const target = parseFeishuCommentTarget(params.to);
1196
826
  if (!target) return null;
1197
- const client = createFeishuClient(resolveFeishuAccount({
827
+ const account = resolveFeishuAccount({
1198
828
  cfg: params.cfg,
1199
829
  accountId: params.accountId
1200
- }));
830
+ });
831
+ const client = createFeishuClient(account);
1201
832
  const replyId = params.replyId?.trim();
1202
833
  try {
1203
834
  const result = await deliverCommentThreadText(client, {
@@ -1236,40 +867,50 @@ async function sendOutboundText(params) {
1236
867
  cfg,
1237
868
  accountId
1238
869
  }).config?.renderMode ?? "auto";
1239
- if (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) return await reportFeishuOutboundDelivery(await sendMarkdownCardFeishu({
1240
- cfg,
1241
- to,
1242
- text,
1243
- accountId,
1244
- replyToMessageId,
1245
- replyInThread
1246
- }), onDeliveryResult);
1247
- const normalizedText = materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, resolveMarkdownTableMode({
870
+ const useCard = renderMode === "card" || renderMode === "auto" && shouldUseCard(text);
871
+ const tableMode = resolveMarkdownTableMode({
1248
872
  cfg,
1249
873
  channel: "feishu"
1250
- })));
1251
- const subChunks = chunkFeishuPostMarkdown({
874
+ });
875
+ const normalizedText = useCard ? text : materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, tableMode));
876
+ const chunkOptions = {
1252
877
  text: normalizedText,
1253
878
  limit: resolveTextChunkLimit(cfg, "feishu", accountId, { fallbackLimit: FEISHU_TEXT_CHUNK_LIMIT }),
1254
879
  mode: resolveChunkMode(cfg, "feishu", accountId)
1255
- });
1256
- let lastResult;
880
+ };
881
+ const subChunks = useCard ? chunkFeishuCardMarkdown({
882
+ ...chunkOptions,
883
+ header: params.header
884
+ }) : chunkFeishuPostMarkdown(chunkOptions);
885
+ const results = [];
1257
886
  const preserveThread = replyInThread === true;
1258
887
  const nextReplyToMessageId = createReplyToFanout({
1259
888
  replyToId: replyToMessageId,
1260
889
  replyToIdSource: params.replyToIdSource,
1261
890
  replyToMode: params.replyToMode ?? "first"
1262
891
  });
1263
- for (const [i, chunk] of (subChunks.length ? subChunks : [normalizedText]).entries()) lastResult = await reportFeishuOutboundDelivery(await sendMessageFeishu({
1264
- cfg,
1265
- to,
1266
- text: chunk,
1267
- preparedPostText: true,
1268
- accountId,
1269
- replyToMessageId: preserveThread ? replyToMessageId : nextReplyToMessageId(),
1270
- replyInThread: preserveThread ? true : i === 0 ? replyInThread : void 0
1271
- }), onDeliveryResult);
1272
- return lastResult;
892
+ for (const [i, chunk] of (subChunks.length ? subChunks : [normalizedText]).entries()) try {
893
+ const sendParams = {
894
+ cfg,
895
+ to,
896
+ text: chunk,
897
+ accountId,
898
+ replyToMessageId: preserveThread ? replyToMessageId : nextReplyToMessageId(),
899
+ replyInThread: preserveThread ? true : i === 0 ? replyInThread : void 0
900
+ };
901
+ const result = useCard ? await sendStructuredCardFeishu({
902
+ ...sendParams,
903
+ header: params.header
904
+ }) : await sendMessageFeishu({
905
+ ...sendParams,
906
+ preparedPostText: true
907
+ });
908
+ results.push(result);
909
+ await reportFeishuOutboundDelivery(result, onDeliveryResult);
910
+ } catch (error) {
911
+ throw partialFeishuSendError(error, results);
912
+ }
913
+ return aggregateFeishuSendResult(results.at(-1), results);
1273
914
  }
1274
915
  async function sendFeishuFallbackPayload(params) {
1275
916
  const propagateMediaUploadFailure = readFeishuPropagateMediaUploadFailure(params.payload);
@@ -1334,7 +975,7 @@ async function sendFeishuTtsSupplementPayload(params) {
1334
975
  if (params.sendVisiblePayload) {
1335
976
  lastResult = await params.sendVisiblePayload(nextReplyToId());
1336
977
  await ctx.onDeliveryResult?.(lastResult);
1337
- } else if (params.supplement.visibleTextAlreadyDelivered !== true) {
978
+ } else if (params.hasVisiblePresentationFallback || params.supplement.visibleTextAlreadyDelivered !== true) {
1338
979
  const text = params.payload.text?.trim() ? params.payload.text : params.supplement.spokenText;
1339
980
  for (const chunk of chunkFeishuMarkdown(text, FEISHU_TEXT_CHUNK_LIMIT)) lastResult = await sendText({
1340
981
  ...ctx,
@@ -1359,26 +1000,7 @@ const feishuOutbound = {
1359
1000
  chunker: chunkFeishuMarkdown,
1360
1001
  chunkerMode: "markdown",
1361
1002
  textChunkLimit: FEISHU_TEXT_CHUNK_LIMIT,
1362
- presentationCapabilities: {
1363
- supported: true,
1364
- buttons: true,
1365
- selects: false,
1366
- context: true,
1367
- divider: true,
1368
- limits: {
1369
- actions: {
1370
- maxActions: 20,
1371
- maxActionsPerRow: 5,
1372
- maxLabelLength: 40,
1373
- maxValueBytes: 1024
1374
- },
1375
- text: {
1376
- maxLength: FEISHU_TEXT_CHUNK_LIMIT,
1377
- encoding: "characters",
1378
- markdownDialect: "markdown"
1379
- }
1380
- }
1381
- },
1003
+ presentationCapabilities: FEISHU_PRESENTATION_CAPABILITIES,
1382
1004
  renderPresentation: renderFeishuPresentationPayload,
1383
1005
  sendPayload: async (ctx) => {
1384
1006
  const { payload, presentationFallback } = consumeFeishuPresentationFallbackMarker(ctx.payload);
@@ -1386,8 +1008,9 @@ const feishuOutbound = {
1386
1008
  if (parseFeishuCommentTarget(ctx.to)) {
1387
1009
  const { presentation } = resolveFeishuRichReply(payload);
1388
1010
  const textCard = readNativeFeishuCardJson(payload.text);
1389
- const { text, fallbackText } = buildFeishuCommentPresentationFallback({
1390
- text: textCard ? void 0 : payload.text,
1011
+ const fallbackSourceText = textCard ? void 0 : payload.text;
1012
+ const { commentText: text, fallbackText } = buildFeishuPresentationFallback({
1013
+ text: fallbackSourceText,
1391
1014
  presentation,
1392
1015
  fallbackHasCommand: isRecord(payload.channelData?.feishu) && payload.channelData.feishu.fallbackHasCommand === true
1393
1016
  });
@@ -1411,24 +1034,26 @@ const feishuOutbound = {
1411
1034
  identity: ctx.identity
1412
1035
  });
1413
1036
  if (!card) {
1037
+ const { presentation } = resolveFeishuRichReply(payload);
1038
+ const fallbackPayload = presentation ? {
1039
+ ...payload,
1040
+ text: renderFeishuPresentationFallbackText({
1041
+ text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
1042
+ presentation
1043
+ }, "markdown"),
1044
+ presentation: void 0,
1045
+ interactive: void 0
1046
+ } : payload;
1414
1047
  if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
1415
1048
  ctx,
1416
- payload,
1417
- supplement: ttsSupplement
1049
+ payload: fallbackPayload,
1050
+ supplement: ttsSupplement,
1051
+ hasVisiblePresentationFallback: presentationFallback?.hasVisibleContent ?? Boolean(renderFeishuPresentationFallbackText({ presentation }).trim())
1418
1052
  });
1419
- const { presentation } = resolveFeishuRichReply(payload);
1420
1053
  return await sendFeishuFallbackPayload({
1421
1054
  ctx,
1422
- payload: presentation ? {
1423
- ...payload,
1424
- text: renderMessagePresentationFallbackText({
1425
- text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
1426
- presentation
1427
- }),
1428
- presentation: void 0,
1429
- interactive: void 0
1430
- } : payload,
1431
- separateMediaAndText: presentationFallback || presentation !== void 0
1055
+ payload: fallbackPayload,
1056
+ separateMediaAndText: presentationFallback !== void 0 || presentation !== void 0
1432
1057
  });
1433
1058
  }
1434
1059
  if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
@@ -1561,25 +1186,7 @@ const feishuOutbound = {
1561
1186
  replyInThread
1562
1187
  }), onDeliveryResult));
1563
1188
  }
1564
- const renderMode = resolveFeishuAccount({
1565
- cfg,
1566
- accountId: accountId ?? void 0
1567
- }).config?.renderMode ?? "auto";
1568
- if (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) {
1569
- const header = identity ? {
1570
- title: resolveFeishuIdentityHeaderTitle(identity),
1571
- template: "blue"
1572
- } : void 0;
1573
- return toFeishuOutboundResult(await reportFeishuOutboundDelivery(await sendStructuredCardFeishu({
1574
- cfg,
1575
- to,
1576
- text,
1577
- replyToMessageId,
1578
- replyInThread,
1579
- accountId: accountId ?? void 0,
1580
- header: header?.title ? header : void 0
1581
- }), onDeliveryResult));
1582
- }
1189
+ const title = identity ? resolveFeishuIdentityHeaderTitle(identity) : void 0;
1583
1190
  return toFeishuOutboundResult(await sendOutboundText({
1584
1191
  cfg,
1585
1192
  to,
@@ -1587,6 +1194,10 @@ const feishuOutbound = {
1587
1194
  accountId: accountId ?? void 0,
1588
1195
  replyToMessageId,
1589
1196
  replyInThread,
1197
+ header: title ? {
1198
+ title,
1199
+ template: "blue"
1200
+ } : void 0,
1590
1201
  ...deliveryOptions
1591
1202
  }));
1592
1203
  },
@@ -1639,19 +1250,16 @@ const feishuOutbound = {
1639
1250
  mediaUrl,
1640
1251
  audioAsVoice
1641
1252
  });
1642
- let textSent = false;
1643
1253
  let captionResult;
1644
- if (text?.trim() && !suppressTextForVoiceMedia) {
1645
- captionResult = await sendOutboundText({
1646
- cfg,
1647
- to,
1648
- text,
1649
- accountId: accountId ?? void 0,
1650
- ...nextReplyMode(),
1651
- ...deliveryOptions
1652
- });
1653
- textSent = true;
1654
- }
1254
+ if (text?.trim() && !suppressTextForVoiceMedia) captionResult = await sendOutboundText({
1255
+ cfg,
1256
+ to,
1257
+ text,
1258
+ accountId: accountId ?? void 0,
1259
+ ...nextReplyMode(),
1260
+ ...deliveryOptions
1261
+ });
1262
+ const results = captionResult ? [captionResult] : [];
1655
1263
  let mediaResult;
1656
1264
  const mediaReplyMode = nextReplyMode();
1657
1265
  try {
@@ -1667,37 +1275,45 @@ const feishuOutbound = {
1667
1275
  ...audioAsVoice === true ? { audioAsVoice: true } : {}
1668
1276
  });
1669
1277
  } catch (err) {
1670
- if (isChannelPartialDeliveryError(err)) throw err;
1278
+ if (isChannelPartialDeliveryError(err)) throw partialFeishuSendError(err, results);
1671
1279
  if (propagateMediaUploadFailure) {
1672
- if (textSent && captionResult) throw createChannelPartialDeliveryError(err, {
1673
- messageIds: [captionResult.messageId],
1674
- visibleReplySent: true
1675
- });
1280
+ if (captionResult) throw partialFeishuSendError(err, results);
1676
1281
  throw new Error(`Feishu send could not deliver the requested media attachment: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
1677
1282
  }
1678
1283
  console.error(`[feishu] sendMediaFeishu failed:`, err);
1679
- return toFeishuOutboundResult(await sendOutboundText({
1284
+ const fallbackText = await buildFeishuMediaFallbackText({
1285
+ text: captionResult ? void 0 : text,
1286
+ mediaUrl
1287
+ });
1288
+ try {
1289
+ const fallbackResult = await sendOutboundText({
1290
+ cfg,
1291
+ to,
1292
+ text: fallbackText,
1293
+ accountId: accountId ?? void 0,
1294
+ ...captionResult ? nextReplyMode() : mediaReplyMode,
1295
+ ...deliveryOptions
1296
+ });
1297
+ return toFeishuOutboundResult(aggregateFeishuSendResult(fallbackResult, [...results, fallbackResult]));
1298
+ } catch (error) {
1299
+ throw partialFeishuSendError(error, results);
1300
+ }
1301
+ }
1302
+ results.push(mediaResult);
1303
+ try {
1304
+ await reportFeishuOutboundDelivery(mediaResult, onDeliveryResult);
1305
+ if (mediaResult.voiceIntentDegradedToFile && text?.trim()) results.push(await sendOutboundText({
1680
1306
  cfg,
1681
1307
  to,
1682
- text: await buildFeishuMediaFallbackText({
1683
- text: textSent ? void 0 : text,
1684
- mediaUrl
1685
- }),
1308
+ text,
1686
1309
  accountId: accountId ?? void 0,
1687
- ...textSent ? nextReplyMode() : mediaReplyMode,
1310
+ ...nextReplyMode(),
1688
1311
  ...deliveryOptions
1689
1312
  }));
1313
+ } catch (error) {
1314
+ throw partialFeishuSendError(error, results);
1690
1315
  }
1691
- await reportFeishuOutboundDelivery(mediaResult, onDeliveryResult);
1692
- if (mediaResult.voiceIntentDegradedToFile && text?.trim()) await sendOutboundText({
1693
- cfg,
1694
- to,
1695
- text,
1696
- accountId: accountId ?? void 0,
1697
- ...nextReplyMode(),
1698
- ...deliveryOptions
1699
- });
1700
- return toFeishuOutboundResult(mediaResult);
1316
+ return toFeishuOutboundResult(aggregateFeishuSendResult(mediaResult, results));
1701
1317
  }
1702
1318
  })
1703
1319
  };
@@ -1759,7 +1375,7 @@ function setFeishuNamedAccountEnabled(cfg, accountId, enabled) {
1759
1375
  const feishuSetupAdapter = {
1760
1376
  resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
1761
1377
  applyAccountConfig: ({ cfg, accountId }) => {
1762
- if (!accountId || accountId === DEFAULT_ACCOUNT_ID$2) return {
1378
+ if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return {
1763
1379
  ...cfg,
1764
1380
  channels: {
1765
1381
  ...cfg.channels,
@@ -1791,15 +1407,15 @@ function isFeishuConfigured(cfg) {
1791
1407
  const source = normalizeOptionalString(rec.source)?.toLowerCase();
1792
1408
  const id = normalizeOptionalString(rec.id);
1793
1409
  if (source === "env" && id) return Boolean(normalizeOptionalString(process.env[id]));
1794
- return hasConfiguredSecretInput$1(value);
1410
+ return hasConfiguredSecretInput(value);
1795
1411
  };
1796
- const topLevelConfigured = isAppIdConfigured(feishuCfg?.appId) && hasConfiguredSecretInput$1(feishuCfg?.appSecret);
1412
+ const topLevelConfigured = isAppIdConfigured(feishuCfg?.appId) && hasConfiguredSecretInput(feishuCfg?.appSecret);
1797
1413
  const accountConfigured = Object.values(feishuCfg?.accounts ?? {}).some((account) => {
1798
1414
  if (!account || typeof account !== "object") return false;
1799
1415
  const hasOwnAppId = Object.hasOwn(account, "appId");
1800
1416
  const hasOwnAppSecret = Object.hasOwn(account, "appSecret");
1801
1417
  const accountAppIdConfigured = hasOwnAppId ? isAppIdConfigured(account.appId) : isAppIdConfigured(feishuCfg?.appId);
1802
- const accountSecretConfigured = hasOwnAppSecret ? hasConfiguredSecretInput$1(account.appSecret) : hasConfiguredSecretInput$1(feishuCfg?.appSecret);
1418
+ const accountSecretConfigured = hasOwnAppSecret ? hasConfiguredSecretInput(account.appSecret) : hasConfiguredSecretInput(feishuCfg?.appSecret);
1803
1419
  return accountAppIdConfigured && accountSecretConfigured;
1804
1420
  });
1805
1421
  return topLevelConfigured || accountConfigured;
@@ -1827,7 +1443,7 @@ function patchFeishuConfig(cfg, accountId, patch) {
1827
1443
  async function promptFeishuAllowFrom(params) {
1828
1444
  const feishuCfg = params.cfg.channels?.feishu;
1829
1445
  const resolvedAccountId = params.accountId ?? resolveDefaultFeishuAccountId(params.cfg);
1830
- const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$2 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
1446
+ const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$1 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
1831
1447
  await params.prompter.note([
1832
1448
  t("wizard.feishu.allowlistIntro"),
1833
1449
  t("wizard.feishu.allowlistFindUser"),
@@ -1835,11 +1451,12 @@ async function promptFeishuAllowFrom(params) {
1835
1451
  "- ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
1836
1452
  "- on_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1837
1453
  ].join("\n"), t("wizard.feishu.allowlistTitle"));
1838
- const mergedAllowFrom = mergeAllowFromEntries(existingAllowFrom, splitSetupEntries(await params.prompter.text({
1454
+ const entry = await params.prompter.text({
1839
1455
  message: t("wizard.feishu.allowFromPrompt"),
1840
1456
  placeholder: "ou_xxxxx, ou_yyyyy",
1841
1457
  initialValue: existingAllowFrom.length > 0 ? existingAllowFrom.map(String).join(", ") : void 0
1842
- })));
1458
+ });
1459
+ const mergedAllowFrom = mergeAllowFromEntries(existingAllowFrom, splitSetupEntries(entry));
1843
1460
  return patchFeishuConfig(params.cfg, resolvedAccountId, { allowFrom: mergedAllowFrom });
1844
1461
  }
1845
1462
  async function noteFeishuCredentialHelp(prompter) {
@@ -1866,7 +1483,7 @@ const feishuDmPolicy = createChannelDmPolicy({
1866
1483
  resolveAccount: (cfg, accountId) => {
1867
1484
  const feishuCfg = cfg.channels?.feishu;
1868
1485
  const resolvedAccountId = accountId ?? resolveDefaultFeishuAccountId(cfg);
1869
- const account = resolvedAccountId === DEFAULT_ACCOUNT_ID$2 ? void 0 : feishuCfg?.accounts?.[resolvedAccountId];
1486
+ const account = resolvedAccountId === DEFAULT_ACCOUNT_ID$1 ? void 0 : feishuCfg?.accounts?.[resolvedAccountId];
1870
1487
  return {
1871
1488
  accountId: resolvedAccountId,
1872
1489
  config: {
@@ -1890,7 +1507,7 @@ function applyNewAppSecurityPolicy(cfg, accountId, openId, groupPolicy) {
1890
1507
  next = patchFeishuConfig(next, accountId, groupPatch);
1891
1508
  return next;
1892
1509
  }
1893
- const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-BpfUzS9m.js"));
1510
+ const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-BI99e_JI.js"));
1894
1511
  async function promptFeishuDomain(params) {
1895
1512
  return await params.prompter.select({
1896
1513
  message: t("wizard.feishu.domainPrompt"),
@@ -2054,7 +1671,7 @@ async function runEditFlow(params) {
2054
1671
  if (value && typeof value === "object") {
2055
1672
  const rec = value;
2056
1673
  if (normalizeOptionalString(rec.source) && normalizeOptionalString(rec.id)) return normalizeOptionalString(process.env[rec.id]) ?? `env:${String(rec.id)}`;
2057
- if (hasConfiguredSecretInput$1(value)) return "(configured)";
1674
+ if (hasConfiguredSecretInput(value)) return "(configured)";
2058
1675
  }
2059
1676
  };
2060
1677
  const existingAppId = resolveAppIdLabel(feishuCfg?.appId) ?? Object.values(feishuCfg?.accounts ?? {}).reduce((found, account) => {
@@ -2115,7 +1732,7 @@ const feishuSetupWizard = {
2115
1732
  });
2116
1733
  let probeResult = null;
2117
1734
  if (configured && account.configured) try {
2118
- const { probeFeishu } = await import("./probe-CfhhR_0a.js").then((n) => n.n);
1735
+ const { probeFeishu } = await import("./probe-D_SatdZl.js").then((n) => n.n);
2119
1736
  probeResult = await probeFeishu(account);
2120
1737
  } catch {}
2121
1738
  if (!configured) return [formatFeishuStatusLine("needs-credentials")];
@@ -2154,6 +1771,45 @@ const feishuSetupWizard = {
2154
1771
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
2155
1772
  };
2156
1773
  //#endregion
1774
+ //#region extensions/feishu/src/sticker-catalog.ts
1775
+ const STICKER_QUERY_PATTERN = /^[^\p{Cs}]{1,128}$/u;
1776
+ function fitsStickerSearchResult(stickers) {
1777
+ return Buffer.byteLength(JSON.stringify({
1778
+ stickers,
1779
+ truncated: false
1780
+ }), "utf8") <= 3072;
1781
+ }
1782
+ function resolveFeishuStickerSet(cfg, account) {
1783
+ const sets = (cfg.channels?.feishu)?.stickerSets;
1784
+ const set = sets && account.appId && Object.hasOwn(sets, account.appId) ? sets[account.appId] : void 0;
1785
+ return set ? Object.entries(set) : [];
1786
+ }
1787
+ function searchFeishuStickerSet(entries, params) {
1788
+ const query = readStringParam(params, "query", { required: true });
1789
+ if (!STICKER_QUERY_PATTERN.test(query)) throw new Error("Feishu sticker-search query must contain 1–128 Unicode characters.");
1790
+ const limit = params.limit === void 0 ? 5 : params.limit;
1791
+ if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 10) throw new Error("Feishu sticker-search limit must be an integer from 1 through 10.");
1792
+ const needle = query.toLowerCase();
1793
+ const stickers = [];
1794
+ for (const [fileId, keywords] of entries) {
1795
+ const keyword = keywords.find((label) => label.toLowerCase().includes(needle));
1796
+ if (keyword === void 0) continue;
1797
+ const match = {
1798
+ fileId,
1799
+ keyword
1800
+ };
1801
+ if (stickers.length === limit || !fitsStickerSearchResult([...stickers, match])) return {
1802
+ stickers,
1803
+ truncated: true
1804
+ };
1805
+ stickers.push(match);
1806
+ }
1807
+ return {
1808
+ stickers,
1809
+ truncated: false
1810
+ };
1811
+ }
1812
+ //#endregion
2157
1813
  //#region extensions/feishu/src/channel.ts
2158
1814
  function resolveFeishuSendAttachmentMedia(params) {
2159
1815
  const sourceKeys = [
@@ -2244,7 +1900,7 @@ const meta = {
2244
1900
  order: 70,
2245
1901
  preferSessionLookupForAnnounceTarget: true
2246
1902
  };
2247
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BIEERwaT.js"), "feishuChannelRuntime");
1903
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CAD74kBO.js"), "feishuChannelRuntime");
2248
1904
  async function resolveFeishuMessageSender(params) {
2249
1905
  try {
2250
1906
  const sender = params.resolve(await loadFeishuChannelRuntime());
@@ -2308,12 +1964,13 @@ const feishuMessageAdapter = defineChannelMessageAdapter({
2308
1964
  }
2309
1965
  });
2310
1966
  async function createFeishuActionClient(account) {
2311
- const { createFeishuClient } = await import("./client-DalLmpMf.js").then((n) => n.t);
1967
+ const { createFeishuClient } = await import("./client-DMbIL3UH.js").then((n) => n.t);
2312
1968
  return createFeishuClient(account);
2313
1969
  }
2314
1970
  async function resolveFeishuChatTypeById(params) {
2315
1971
  const client = await createFeishuActionClient(params.account);
2316
- return resolveFeishuChatType(await params.runtime.getChatInfo(client, params.chatId));
1972
+ const chat = await params.runtime.getChatInfo(client, params.chatId);
1973
+ return resolveFeishuChatType(chat);
2317
1974
  }
2318
1975
  async function resolveFeishuMessageChatType(params) {
2319
1976
  const knownChatType = normalizeFeishuChatType(params.message.chatType);
@@ -2371,6 +2028,11 @@ function describeFeishuMessageTool({ cfg, accountId }) {
2371
2028
  actions.add("reactions");
2372
2029
  }
2373
2030
  if (enabledAccounts.some((account) => isFeishuActionEnabled(account, "sticker"))) actions.add("sticker");
2031
+ const selectedAccount = resolveFeishuAccount({
2032
+ cfg,
2033
+ accountId
2034
+ });
2035
+ if (isFeishuActionEnabled(selectedAccount, "sticker") && resolveFeishuStickerSet(cfg, selectedAccount).length > 0) actions.add("sticker-search");
2374
2036
  return {
2375
2037
  actions: Array.from(actions),
2376
2038
  capabilities: enabled ? ["presentation"] : []
@@ -2708,15 +2370,19 @@ const feishuPlugin = createChatChannelPlugin({
2708
2370
  edit: true,
2709
2371
  reply: true
2710
2372
  },
2711
- agentPrompt: { messageToolHints: ({ cfg, accountId }) => [
2712
- "- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
2713
- "- Feishu supports interactive cards plus native image, file, audio, and video/media delivery.",
2714
- "- Feishu supports `send`, `read`, `edit`, `thread-reply`, pins, and channel/member lookup, plus reactions when enabled.",
2715
- ...describeFeishuMessageTool({
2373
+ agentPrompt: { messageToolHints: ({ cfg, accountId }) => {
2374
+ const actions = describeFeishuMessageTool({
2716
2375
  cfg,
2717
2376
  accountId: accountId ?? void 0
2718
- }).actions?.includes("sticker") ? ["- Feishu stickers: use `action=sticker` with `fileId` (or the first `stickerId`) from a sticker this bot previously received. Sticker search and upload are not supported."] : []
2719
- ] },
2377
+ }).actions;
2378
+ return [
2379
+ "- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
2380
+ "- Feishu supports interactive cards plus native image, file, audio, and video/media delivery.",
2381
+ "- Feishu supports `send`, `read`, `edit`, `thread-reply`, pins, and channel/member lookup, plus reactions when enabled.",
2382
+ ...actions?.includes("sticker") ? ["- Feishu stickers: use `action=sticker` with `fileId` (or the first `stickerId`) from a sticker this bot previously received. Sticker upload is not supported."] : [],
2383
+ ...actions?.includes("sticker-search") ? ["- Feishu `action=sticker-search`: configured keyword lookup only, not store search or learned/visual matching. Supply `query` (1–128 characters) and optional `limit` (1–10, default 5); send a returned `fileId` with `action=sticker` on the same account. If `truncated` is true, narrow the query."] : []
2384
+ ];
2385
+ } },
2720
2386
  groups: { resolveToolPolicy: resolveFeishuGroupToolPolicy },
2721
2387
  conversationBindings: {
2722
2388
  bindingStore: "adapter",
@@ -2724,7 +2390,10 @@ const feishuPlugin = createChatChannelPlugin({
2724
2390
  buildModelOverrideParentCandidates: ({ parentConversationId }) => buildFeishuModelOverrideParentCandidates(parentConversationId)
2725
2391
  },
2726
2392
  mentions: { stripPatterns: () => ["<at user_id=\"[^\"]*\">[^<]*</at>"] },
2727
- reload: { configPrefixes: ["channels.feishu"] },
2393
+ reload: {
2394
+ configPrefixes: ["channels.feishu"],
2395
+ noopPrefixes: ["messages.inbound"]
2396
+ },
2728
2397
  doctor: feishuDoctor,
2729
2398
  configSchema: FeishuChannelConfigSchema,
2730
2399
  config: {
@@ -2777,8 +2446,13 @@ const feishuPlugin = createChatChannelPlugin({
2777
2446
  accountId: ctx.accountId ?? void 0
2778
2447
  });
2779
2448
  if ((ctx.action === "react" || ctx.action === "reactions") && !isFeishuActionEnabled(account, "reactions")) throw new Error("Feishu reactions are disabled via actions.reactions.");
2780
- if (ctx.action === "sticker") {
2449
+ if (ctx.action === "sticker" || ctx.action === "sticker-search") {
2781
2450
  if (!isFeishuActionEnabled(account, "sticker")) throw new Error("Feishu stickers are disabled; enable actions.sticker for a configured account.");
2451
+ if (ctx.action === "sticker-search") {
2452
+ const stickers = resolveFeishuStickerSet(ctx.cfg, account);
2453
+ if (stickers.length === 0) throw new Error("Feishu sticker-search requires a nonempty channels.feishu.stickerSets entry for this account's appId.");
2454
+ return jsonActionResult(searchFeishuStickerSet(stickers, ctx.params));
2455
+ }
2782
2456
  const to = resolveFeishuActionTarget(ctx);
2783
2457
  if (!to) throw new Error("Feishu sticker requires a target (to).");
2784
2458
  const fileKey = normalizeFeishuExternalKey(readFirstString(ctx.params, ["fileId"]) ?? (Array.isArray(ctx.params.stickerId) ? ctx.params.stickerId[0] : void 0));
@@ -3353,7 +3027,7 @@ const feishuPlugin = createChatChannelPlugin({
3353
3027
  })
3354
3028
  }),
3355
3029
  gateway: { startAccount: async (ctx) => {
3356
- const { monitorFeishuProvider } = await import("./monitor-BRg3sHH7.js");
3030
+ const { monitorFeishuProvider } = await import("./monitor-rPJ_Mz4M.js");
3357
3031
  const account = resolveFeishuRuntimeAccount({
3358
3032
  cfg: ctx.cfg,
3359
3033
  accountId: ctx.accountId
@@ -3400,39 +3074,27 @@ const feishuPlugin = createChatChannelPlugin({
3400
3074
  });
3401
3075
  }
3402
3076
  } },
3403
- threading: { buildToolContext: ({ context, hasRepliedRef }) => ({
3404
- currentChannelId: normalizeOptionalString(context.NativeChannelId) ?? normalizeOptionalString(context.To),
3405
- currentChatType: context.ChatType === "direct" || context.ChatType === "group" || context.ChatType === "channel" ? context.ChatType : void 0,
3406
- currentMessagingTarget: normalizeOptionalString(context.To),
3407
- currentThreadTs: context.MessageThreadId != null ? String(context.MessageThreadId) : void 0,
3408
- hasRepliedRef
3409
- }) },
3077
+ threading: {
3078
+ matchesToolContextTarget: ({ target, toolContext }) => {
3079
+ const normalizedTarget = normalizeFeishuTarget(target);
3080
+ if (!normalizedTarget) return false;
3081
+ return [toolContext.currentChannelId, toolContext.currentMessagingTarget].some((currentTarget) => currentTarget !== void 0 && normalizeFeishuTarget(currentTarget) === normalizedTarget);
3082
+ },
3083
+ buildToolContext: ({ context, hasRepliedRef }) => ({
3084
+ currentChannelId: normalizeOptionalString(context.NativeChannelId) ?? normalizeOptionalString(context.To),
3085
+ currentChatType: context.ChatType === "direct" || context.ChatType === "group" || context.ChatType === "channel" ? context.ChatType : void 0,
3086
+ currentMessagingTarget: normalizeOptionalString(context.To),
3087
+ currentThreadTs: context.MessageThreadId != null ? String(context.MessageThreadId) : void 0,
3088
+ hasRepliedRef
3089
+ })
3090
+ },
3410
3091
  outbound: {
3411
3092
  deliveryMode: "direct",
3412
3093
  chunker: chunkFeishuMarkdown,
3413
3094
  chunkerMode: "markdown",
3414
3095
  textChunkLimit: 4e3,
3415
3096
  sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text),
3416
- presentationCapabilities: {
3417
- supported: true,
3418
- buttons: true,
3419
- selects: false,
3420
- context: true,
3421
- divider: true,
3422
- limits: {
3423
- actions: {
3424
- maxActions: 20,
3425
- maxActionsPerRow: 5,
3426
- maxLabelLength: 40,
3427
- maxValueBytes: 1024
3428
- },
3429
- text: {
3430
- maxLength: 4e3,
3431
- encoding: "characters",
3432
- markdownDialect: "markdown"
3433
- }
3434
- }
3435
- },
3097
+ presentationCapabilities: FEISHU_PRESENTATION_CAPABILITIES,
3436
3098
  ...createRuntimeOutboundDelegates({
3437
3099
  getRuntime: loadFeishuChannelRuntime,
3438
3100
  renderPresentation: { resolve: (runtime) => runtime.feishuOutbound.renderPresentation },