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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/accounts-CCCdMen2.js +203 -0
  2. package/dist/api.js +52 -51
  3. package/dist/{channel-CScE82zY.js → channel-Dzb5jv3i.js} +53 -30
  4. package/dist/channel-plugin-api.js +1 -1
  5. package/dist/{channel.runtime-B21e1E0r.js → channel.runtime-CoPaHQQ6.js} +181 -142
  6. package/dist/{client-Dcbs6vml.js → client-Hp7uo_cl.js} +27 -16
  7. package/dist/contract-api.js +2 -2
  8. package/dist/{conversation-id-BeJL-wq7.js → conversation-id-VYgGQ-GX.js} +11 -15
  9. package/dist/doctor-contract-api.js +1 -1
  10. package/dist/doctor-contract-bEQXIXyP.js +158 -0
  11. package/dist/{drive-hSi_Utp0.js → drive-B0JkoiRj.js} +318 -41
  12. package/dist/{media-CEUraFOR.js → media-BjSy8fiy.js} +298 -182
  13. package/dist/{monitor-DngbaA6a.js → monitor-CeHCfROt.js} +3 -3
  14. package/dist/{monitor.account-BnTWCw55.js → monitor.account-DwdqexLU.js} +237 -158
  15. package/dist/{monitor.startup-CqH2tiJq.js → monitor.startup-BGErejNH.js} +1 -1
  16. package/dist/{probe-p3POS2RN.js → probe-DVpy58s0.js} +2 -2
  17. package/dist/security-audit-D6Fz2h6p.js +23 -0
  18. package/dist/{send-result-B9_BpUPx.js → send-result-DEAycmOk.js} +18 -9
  19. package/dist/session-binding-contract-api.js +1 -1
  20. package/dist/{session-conversation-BksWrfzm.js → session-conversation-DFCIvQK-.js} +1 -1
  21. package/dist/session-key-api.js +1 -1
  22. package/dist/setup-api.js +1 -1
  23. package/dist/{subagent-hooks-Cx1cX7rW.js → subagent-hooks-B867acTt.js} +2 -2
  24. package/dist/subagent-hooks-api.js +1 -1
  25. package/dist/{thread-bindings-N3wkgkIN.js → thread-bindings-Itvfmx6_.js} +6 -3
  26. package/openclaw.plugin.json +143 -1
  27. package/package.json +4 -4
  28. package/skills/feishu-doc/SKILL.md +20 -195
  29. package/skills/feishu-doc/references/block-types.md +9 -14
  30. package/skills/feishu-drive/SKILL.md +16 -102
  31. package/skills/feishu-perm/SKILL.md +10 -110
  32. package/skills/feishu-wiki/SKILL.md +13 -109
  33. package/dist/accounts-u9X5Wsan.js +0 -469
  34. package/dist/doctor-contract-BiD9tyIv.js +0 -102
  35. package/dist/security-audit-D7WK_BHh.js +0 -11
@@ -1,2 +1,2 @@
1
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BiD9tyIv.js";
1
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-bEQXIXyP.js";
2
2
  export { legacyConfigRules, normalizeCompatibilityConfig };
@@ -0,0 +1,158 @@
1
+ import { asObjectRecord, defineChannelAliasMigration, defineKeyMoveMigration, hasLegacyAccountStreamingAliases, normalizeChannelConfigEntries } from "openclaw/plugin-sdk/runtime-doctor-migrations";
2
+ //#region extensions/feishu/src/webhook-path.ts
3
+ const DEFAULT_FEISHU_WEBHOOK_PATH = "/feishu/events";
4
+ /** Normalize trusted configuration only; incoming request targets must remain unmodified. */
5
+ function normalizeFeishuWebhookPath(value) {
6
+ const configured = value?.trim();
7
+ if (!configured) return DEFAULT_FEISHU_WEBHOOK_PATH;
8
+ try {
9
+ const parsed = new URL(configured, "http://localhost");
10
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
11
+ const emptyQuery = !parsed.search && parsed.href.endsWith("?") && !configured.includes("#") ? "?" : "";
12
+ return `${parsed.pathname}${parsed.search}${emptyQuery}`;
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+ //#endregion
18
+ //#region extensions/feishu/src/doctor-contract.ts
19
+ const streamingAliasMigration = defineChannelAliasMigration({
20
+ channelId: "feishu",
21
+ streaming: { defaultMode: "partial" },
22
+ accountStreamingReplacesRoot: true
23
+ });
24
+ const LEGACY_COALESCE_FIELDS = [
25
+ "enabled",
26
+ "minDelayMs",
27
+ "maxDelayMs"
28
+ ];
29
+ const LEGACY_HEARTBEAT_FIELDS = ["visibility", "intervalMs"];
30
+ const toolsBaseMigration = defineKeyMoveMigration({
31
+ from: ["tools", "base"],
32
+ to: ["tools", "bitable"],
33
+ match: (value) => typeof value === "boolean",
34
+ sourceOwn: false
35
+ });
36
+ function sanitizeLegacyHeartbeatFields(params) {
37
+ const heartbeat = asObjectRecord(params.entry.heartbeat);
38
+ if (!heartbeat || Object.keys(heartbeat).length > 0 && !LEGACY_HEARTBEAT_FIELDS.some((field) => Object.hasOwn(heartbeat, field))) return {
39
+ entry: params.entry,
40
+ changed: false
41
+ };
42
+ const next = { ...params.entry };
43
+ delete next.heartbeat;
44
+ params.changes.push(`Removed ${params.pathPrefix}.heartbeat (legacy Feishu fields were never read by runtime).`);
45
+ return {
46
+ entry: next,
47
+ changed: true
48
+ };
49
+ }
50
+ function sanitizeLegacyCoalesceFields(params) {
51
+ const streaming = asObjectRecord(params.entry.streaming);
52
+ const block = asObjectRecord(streaming?.block);
53
+ const coalesce = asObjectRecord(block?.coalesce);
54
+ if (!streaming || !block || !coalesce) return {
55
+ entry: params.entry,
56
+ changed: false
57
+ };
58
+ const removed = LEGACY_COALESCE_FIELDS.filter((field) => coalesce[field] !== void 0);
59
+ if (removed.length === 0) return {
60
+ entry: params.entry,
61
+ changed: false
62
+ };
63
+ const nextCoalesce = { ...coalesce };
64
+ for (const field of removed) delete nextCoalesce[field];
65
+ params.changes.push(`Removed ${params.pathPrefix}.streaming.block.coalesce.{${removed.join(",")}} (legacy Feishu-only fields; block delivery reads minChars/maxChars/idleMs).`);
66
+ return {
67
+ entry: {
68
+ ...params.entry,
69
+ streaming: {
70
+ ...streaming,
71
+ block: {
72
+ ...block,
73
+ coalesce: nextCoalesce
74
+ }
75
+ }
76
+ },
77
+ changed: true
78
+ };
79
+ }
80
+ function hasLegacyWebhookPath(value) {
81
+ const path = asObjectRecord(value)?.webhookPath;
82
+ return typeof path === "string" && normalizeFeishuWebhookPath(path) !== path;
83
+ }
84
+ function normalizeLegacyWebhookPath(params) {
85
+ const path = params.entry.webhookPath;
86
+ if (typeof path !== "string") return {
87
+ entry: params.entry,
88
+ changed: false
89
+ };
90
+ const normalized = normalizeFeishuWebhookPath(path);
91
+ const canonical = normalized ?? "/feishu/events";
92
+ if (canonical === path) return {
93
+ entry: params.entry,
94
+ changed: false
95
+ };
96
+ params.changes.push(normalized === null ? `Reset invalid ${params.pathPrefix}.webhookPath to ${DEFAULT_FEISHU_WEBHOOK_PATH}.` : `Normalized ${params.pathPrefix}.webhookPath to its HTTP request path.`);
97
+ return {
98
+ entry: {
99
+ ...params.entry,
100
+ webhookPath: canonical
101
+ },
102
+ changed: true
103
+ };
104
+ }
105
+ function normalizeFeishuLegacyConfigEntries(cfg, changes) {
106
+ return normalizeChannelConfigEntries({
107
+ cfg,
108
+ channelId: "feishu",
109
+ changes,
110
+ normalizeEntry: (params) => {
111
+ const tools = toolsBaseMigration.normalize(params);
112
+ const coalesce = sanitizeLegacyCoalesceFields({
113
+ ...params,
114
+ entry: tools.entry
115
+ });
116
+ const heartbeat = sanitizeLegacyHeartbeatFields({
117
+ ...params,
118
+ entry: coalesce.entry
119
+ });
120
+ const webhook = normalizeLegacyWebhookPath({
121
+ ...params,
122
+ entry: heartbeat.entry
123
+ });
124
+ return {
125
+ entry: webhook.entry,
126
+ changed: tools.changed || coalesce.changed || heartbeat.changed || webhook.changed
127
+ };
128
+ }
129
+ }).config;
130
+ }
131
+ const legacyConfigRules = [
132
+ ...streamingAliasMigration.legacyConfigRules,
133
+ {
134
+ path: ["channels", "feishu"],
135
+ message: "channels.feishu[.accounts.<id>].webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\".",
136
+ match: (value) => {
137
+ const entry = asObjectRecord(value);
138
+ return hasLegacyWebhookPath(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, hasLegacyWebhookPath);
139
+ }
140
+ },
141
+ {
142
+ path: ["channels", "feishu"],
143
+ message: "channels.feishu[.accounts.<id>].tools.base is legacy; use tools.bitable. Run \"openclaw doctor --fix\".",
144
+ match: (value) => {
145
+ const entry = asObjectRecord(value);
146
+ return toolsBaseMigration.hasLegacy(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, toolsBaseMigration.hasLegacy);
147
+ }
148
+ }
149
+ ];
150
+ function normalizeCompatibilityConfig({ cfg }) {
151
+ const aliases = streamingAliasMigration.normalizeChannelConfig({ cfg });
152
+ return {
153
+ config: normalizeFeishuLegacyConfigEntries(aliases.config, aliases.changes),
154
+ changes: aliases.changes
155
+ };
156
+ }
157
+ //#endregion
158
+ export { normalizeFeishuWebhookPath as i, normalizeCompatibilityConfig as n, DEFAULT_FEISHU_WEBHOOK_PATH as r, legacyConfigRules as t };
@@ -1,13 +1,15 @@
1
- import { a as resolveDefaultFeishuAccountId, c as encodeQuery, d as isRecord$1, i as listFeishuAccountIds, l as extractReplyText, m as readString, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount, u as formatFeishuApiError, v as parseFeishuCommentTarget } from "./accounts-u9X5Wsan.js";
2
- import { A as resolveFeishuChatType, b as resolveFeishuChatReadPreliminaryAuthorization, h as authorizeFeishuChatMemberRead, m as assertFeishuChatReadAllowed } from "./send-result-B9_BpUPx.js";
3
- import { r as createFeishuClient } from "./client-Dcbs6vml.js";
1
+ import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-CCCdMen2.js";
2
+ import { A as resolveFeishuChatType, b as resolveFeishuChatReadPreliminaryAuthorization, h as authorizeFeishuChatMemberRead, m as assertFeishuChatReadAllowed } from "./send-result-DEAycmOk.js";
3
+ import { r as createFeishuClient } from "./client-Hp7uo_cl.js";
4
4
  import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
5
- import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
- import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resolution";
7
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
8
- import { jsonResult } from "openclaw/plugin-sdk/tool-results";
6
+ import { isRecord, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resolution";
9
8
  import { Type } from "typebox";
9
+ import { wrapExternalContent } from "openclaw/plugin-sdk/security-runtime";
10
+ import { jsonResult } from "openclaw/plugin-sdk/tool-results";
10
11
  import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
12
+ import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
11
13
  //#region extensions/feishu/src/tools-config.ts
12
14
  /**
13
15
  * Default tool configuration.
@@ -99,6 +101,26 @@ function resolveAnyEnabledFeishuToolsConfig(accounts) {
99
101
  }
100
102
  return merged;
101
103
  }
104
+ //#endregion
105
+ //#region extensions/feishu/src/tool-result.ts
106
+ function feishuExternalToolResult(details) {
107
+ return {
108
+ content: [{
109
+ type: "text",
110
+ text: wrapExternalContent(JSON.stringify(details, null, 2), {
111
+ source: "api",
112
+ includeWarning: false
113
+ })
114
+ }],
115
+ details
116
+ };
117
+ }
118
+ function unknownToolActionResult(action) {
119
+ return jsonResult({ error: `Unknown action: ${String(action)}` });
120
+ }
121
+ function toolExecutionErrorResult(error) {
122
+ return feishuExternalToolResult({ error: formatErrorMessage(error) });
123
+ }
102
124
  const FeishuChatSchema = Type.Object({
103
125
  action: Type.Enum([
104
126
  "members",
@@ -125,6 +147,267 @@ const FeishuChatSchema = Type.Object({
125
147
  }))
126
148
  });
127
149
  //#endregion
150
+ //#region extensions/feishu/src/comment-target.ts
151
+ const FEISHU_COMMENT_FILE_TYPES = [
152
+ "doc",
153
+ "docx",
154
+ "file",
155
+ "sheet",
156
+ "slides"
157
+ ];
158
+ function normalizeCommentFileType(value) {
159
+ return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value) ? value : void 0;
160
+ }
161
+ function buildFeishuCommentTarget(params) {
162
+ return `comment:${params.fileType}:${params.fileToken}:${params.commentId}`;
163
+ }
164
+ function parseFeishuCommentTarget(raw) {
165
+ const trimmed = raw?.trim();
166
+ if (!trimmed?.startsWith("comment:")) return null;
167
+ const parts = trimmed.split(":");
168
+ if (parts.length !== 4) return null;
169
+ const fileType = normalizeCommentFileType(parts[1]);
170
+ const fileToken = parts[2]?.trim();
171
+ const commentId = parts[3]?.trim();
172
+ if (!fileType || !fileToken || !commentId) return null;
173
+ return {
174
+ fileType,
175
+ fileToken,
176
+ commentId
177
+ };
178
+ }
179
+ //#endregion
180
+ //#region extensions/feishu/src/send-rate-limit.ts
181
+ const FEISHU_SEND_RATE_LIMIT_CODES = /* @__PURE__ */ new Set([230020, 11232]);
182
+ function getFeishuSendRateLimitCode(error) {
183
+ if (!isRecord(error)) return;
184
+ const response = isRecord(error.response) ? error.response : void 0;
185
+ if (response?.status === 429) return 429;
186
+ const code = (isRecord(response?.data) ? response.data : void 0)?.code;
187
+ return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
188
+ }
189
+ function getFeishuSendRateLimitCodeFromResponse(response) {
190
+ if (!isRecord(response)) return;
191
+ const code = response.code;
192
+ return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
193
+ }
194
+ //#endregion
195
+ //#region extensions/feishu/src/comment-shared.ts
196
+ function encodeQuery(params) {
197
+ const query = new URLSearchParams();
198
+ for (const [key, value] of Object.entries(params)) {
199
+ const trimmed = value?.trim();
200
+ if (trimmed) query.set(key, trimmed);
201
+ }
202
+ const queryString = query.toString();
203
+ return queryString ? `?${queryString}` : "";
204
+ }
205
+ function formatFeishuApiError(error, options = {}) {
206
+ if (!isRecord(error)) return typeof error === "string" ? error : JSON.stringify(error);
207
+ const config = isRecord(error.config) ? error.config : void 0;
208
+ const response = isRecord(error.response) ? error.response : void 0;
209
+ const responseData = isRecord(response?.data) ? response?.data : void 0;
210
+ const feishuLogId = readStringValue(responseData?.log_id) || (options.includeNestedErrorLogId ? readStringValue(isRecord(responseData?.error) ? responseData.error.log_id : void 0) : void 0);
211
+ const nestedError = isRecord(responseData?.error) ? responseData.error : void 0;
212
+ return JSON.stringify({
213
+ message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
214
+ code: readStringValue(error.code),
215
+ method: readStringValue(config?.method),
216
+ url: readStringValue(config?.url),
217
+ ...options.includeConfigParams ? { params: config?.params } : {},
218
+ http_status: typeof response?.status === "number" ? response.status : void 0,
219
+ feishu_code: typeof responseData?.code === "number" ? responseData.code : readStringValue(responseData?.code),
220
+ feishu_msg: readStringValue(responseData?.msg),
221
+ feishu_log_id: feishuLogId,
222
+ feishu_troubleshooter: readStringValue(responseData?.troubleshooter) || readStringValue(nestedError?.troubleshooter)
223
+ });
224
+ }
225
+ function formatFeishuApiFailure(error, errorPrefix, options = {}) {
226
+ return `${errorPrefix}: ${formatFeishuApiError(error, options) || "unknown error"}`;
227
+ }
228
+ function createFeishuApiError(error, errorPrefix, options = {}) {
229
+ return new Error(formatFeishuApiFailure(error, errorPrefix, options), { cause: error });
230
+ }
231
+ const FEISHU_SEND_RETRY_BASE_MS = 500;
232
+ async function requestFeishuApi(request, errorPrefix, options = {}) {
233
+ try {
234
+ return await retryAsync(async () => {
235
+ const result = await request();
236
+ const fulfilledRateLimit = getFeishuSendRateLimitCodeFromResponse(result);
237
+ if (fulfilledRateLimit !== void 0) throw Object.assign(/* @__PURE__ */ new Error(`Request fulfilled with rate-limit code ${fulfilledRateLimit}`), { response: {
238
+ status: 200,
239
+ data: result
240
+ } });
241
+ return result;
242
+ }, {
243
+ attempts: 3,
244
+ minDelayMs: options.retryDelayMs ?? FEISHU_SEND_RETRY_BASE_MS,
245
+ shouldRetry: (error) => getFeishuSendRateLimitCode(error) !== void 0
246
+ });
247
+ } catch (error) {
248
+ throw createFeishuApiError(error, errorPrefix, options);
249
+ }
250
+ }
251
+ function readDocsLinkUrl(element) {
252
+ const docsLink = isRecord(element.docs_link) ? element.docs_link : void 0;
253
+ return normalizeOptionalString(docsLink?.url) || normalizeOptionalString(docsLink?.link) || normalizeOptionalString(element.url) || normalizeOptionalString(element.link) || void 0;
254
+ }
255
+ function readMentionUserId(element) {
256
+ const mention = isRecord(element.mention) ? element.mention : void 0;
257
+ return normalizeOptionalString((isRecord(element.person) ? element.person : void 0)?.user_id) || normalizeOptionalString(mention?.user_id) || normalizeOptionalString(mention?.open_id) || normalizeOptionalString(element.mention_user) || normalizeOptionalString(element.user_id) || void 0;
258
+ }
259
+ function readMentionDisplayText(element, userId) {
260
+ const mention = isRecord(element.mention) ? element.mention : void 0;
261
+ const mentionName = normalizeOptionalString(mention?.name) || normalizeOptionalString(mention?.display_name) || normalizeOptionalString(element.name);
262
+ return mentionName ? `@${mentionName}` : `@${userId}`;
263
+ }
264
+ function normalizeCommentText(parts) {
265
+ return parts.join("").trim() || void 0;
266
+ }
267
+ function normalizeCommentSemanticText(parts) {
268
+ return parts.join("").replace(/\s+/g, " ").trim() || void 0;
269
+ }
270
+ function readElementTextPreservingWhitespace(element) {
271
+ return (isRecord(element.text_run) ? readStringValue(element.text_run.content) || readStringValue(element.text_run.text) : void 0) || readStringValue(element.text) || readStringValue(element.content) || readStringValue(element.name) || void 0;
272
+ }
273
+ const FEISHU_LINK_TOKEN_MIN_LENGTH = 22;
274
+ const FEISHU_LINK_TOKEN_MAX_LENGTH = 28;
275
+ const COMMENT_LINK_KIND_ALIASES = /* @__PURE__ */ new Map([
276
+ ["doc", "doc"],
277
+ ["docs", "doc"],
278
+ ["docx", "docx"],
279
+ ["sheet", "sheet"],
280
+ ["sheets", "sheet"],
281
+ ["slide", "slides"],
282
+ ["slides", "slides"],
283
+ ["file", "file"],
284
+ ["files", "file"],
285
+ ["wiki", "wiki"],
286
+ ["mindnote", "mindnote"],
287
+ ["mindnotes", "mindnote"],
288
+ ["bitable", "bitable"],
289
+ ["base", "base"]
290
+ ]);
291
+ function isCommentFileType(value) {
292
+ return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value);
293
+ }
294
+ function isReasonableFeishuLinkToken(token) {
295
+ return typeof token === "string" && token.length >= FEISHU_LINK_TOKEN_MIN_LENGTH && token.length <= FEISHU_LINK_TOKEN_MAX_LENGTH;
296
+ }
297
+ function parseCommentLinkedDocumentPath(pathname) {
298
+ const segments = normalizeStringEntries(pathname.split("/"));
299
+ const offset = segments[0]?.toLowerCase() === "space" ? 1 : 0;
300
+ const kind = COMMENT_LINK_KIND_ALIASES.get(segments[offset]?.toLowerCase() ?? "");
301
+ const token = normalizeOptionalString(segments[offset + 1]);
302
+ if (!kind || !isReasonableFeishuLinkToken(token)) return null;
303
+ return {
304
+ urlKind: kind,
305
+ token
306
+ };
307
+ }
308
+ function hasResolvedLinkedDocumentReference(link) {
309
+ return link.urlKind !== "unknown" && (Boolean(link.resolvedObjToken) || Boolean(link.wikiNodeToken));
310
+ }
311
+ function resolveCommentLinkedDocumentFromUrl(params) {
312
+ const link = {
313
+ rawUrl: params.rawUrl,
314
+ urlKind: "unknown"
315
+ };
316
+ try {
317
+ const parsedPath = parseCommentLinkedDocumentPath(new URL(params.rawUrl).pathname);
318
+ if (!parsedPath) return link;
319
+ const { urlKind, token } = parsedPath;
320
+ link.urlKind = urlKind;
321
+ if (urlKind === "wiki") {
322
+ link.urlKind = "wiki";
323
+ link.wikiNodeToken = token;
324
+ } else {
325
+ link.resolvedObjType = urlKind;
326
+ link.resolvedObjToken = token;
327
+ }
328
+ if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType) && params.currentDocument?.fileType === link.resolvedObjType && params.currentDocument.fileToken === link.resolvedObjToken) link.isCurrentDocument = true;
329
+ else if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType)) link.isCurrentDocument = false;
330
+ } catch {
331
+ return link;
332
+ }
333
+ return link;
334
+ }
335
+ function parseCommentContentElements(params) {
336
+ const elements = Array.isArray(params.elements) ? params.elements : [];
337
+ const plainTextParts = [];
338
+ const semanticTextParts = [];
339
+ const mentions = [];
340
+ const linkedDocuments = [];
341
+ const botIds = new Set(Array.from(params.botOpenIds ?? []).map((value) => normalizeOptionalString(value)).filter((value) => Boolean(value)));
342
+ const linkedDocumentKeys = /* @__PURE__ */ new Set();
343
+ let botMentioned = false;
344
+ for (const rawElement of elements) {
345
+ if (!isRecord(rawElement)) continue;
346
+ const element = rawElement;
347
+ const type = normalizeOptionalString(element.type);
348
+ const text = (type === "text_run" ? readElementTextPreservingWhitespace(element) : void 0) || (type === "text" ? readElementTextPreservingWhitespace(element) : void 0) || (type === "docs_link" || type === "link" ? readDocsLinkUrl(element) : void 0) || (type === "mention" || type === "mention_user" || type === "person" ? (() => {
349
+ const userId = readMentionUserId(element);
350
+ return userId ? readMentionDisplayText(element, userId) : void 0;
351
+ })() : void 0) || readElementTextPreservingWhitespace(element) || void 0;
352
+ if (type === "mention" || type === "mention_user" || type === "person") {
353
+ const userId = readMentionUserId(element);
354
+ if (userId) {
355
+ const displayText = readMentionDisplayText(element, userId);
356
+ const isBotMention = botIds.has(userId);
357
+ mentions.push({
358
+ userId,
359
+ displayText,
360
+ isBotMention
361
+ });
362
+ plainTextParts.push(displayText);
363
+ if (!isBotMention) semanticTextParts.push(displayText);
364
+ else botMentioned = true;
365
+ continue;
366
+ }
367
+ }
368
+ if (type === "docs_link" || type === "link") {
369
+ const rawUrl = readDocsLinkUrl(element);
370
+ if (rawUrl) {
371
+ plainTextParts.push(rawUrl);
372
+ semanticTextParts.push(rawUrl);
373
+ const linkedDocument = resolveCommentLinkedDocumentFromUrl({
374
+ rawUrl,
375
+ currentDocument: params.currentDocument
376
+ });
377
+ if (hasResolvedLinkedDocumentReference(linkedDocument)) {
378
+ const key = [
379
+ linkedDocument.rawUrl,
380
+ linkedDocument.urlKind,
381
+ linkedDocument.resolvedObjType,
382
+ linkedDocument.resolvedObjToken,
383
+ linkedDocument.wikiNodeToken
384
+ ].join(":");
385
+ if (!linkedDocumentKeys.has(key)) {
386
+ linkedDocumentKeys.add(key);
387
+ linkedDocuments.push(linkedDocument);
388
+ }
389
+ }
390
+ continue;
391
+ }
392
+ }
393
+ if (text) {
394
+ plainTextParts.push(text);
395
+ semanticTextParts.push(text);
396
+ }
397
+ }
398
+ return {
399
+ plainText: normalizeCommentText(plainTextParts),
400
+ semanticText: normalizeCommentSemanticText(semanticTextParts),
401
+ mentions,
402
+ linkedDocuments,
403
+ botMentioned
404
+ };
405
+ }
406
+ function extractReplyText(reply) {
407
+ if (!reply || !isRecord(reply.content)) return;
408
+ return parseCommentContentElements({ elements: Array.isArray(reply.content.elements) ? reply.content.elements : [] }).plainText;
409
+ }
410
+ //#endregion
128
411
  //#region extensions/feishu/src/chat.ts
129
412
  function readChatPageSize(params) {
130
413
  return readPositiveIntegerParam(params, "page_size", {
@@ -292,6 +575,7 @@ function registerFeishuChatTools(api) {
292
575
  if (!resolveAnyEnabledFeishuToolsConfig(accounts).chat) return;
293
576
  api.registerTool((toolContext) => ({
294
577
  name: "feishu_chat",
578
+ resultContentSource: "network",
295
579
  label: "Feishu Chat",
296
580
  description: "Feishu chat operations. Actions: members, info, member_info",
297
581
  parameters: FeishuChatSchema,
@@ -310,7 +594,7 @@ function registerFeishuChatTools(api) {
310
594
  const client = createFeishuClient(account);
311
595
  switch (p.action) {
312
596
  case "members":
313
- if (!p.chat_id) return jsonResult({ error: "chat_id is required for action members" });
597
+ if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action members" });
314
598
  {
315
599
  const chat = await getAuthorizedFeishuChatInfo({
316
600
  client,
@@ -327,12 +611,12 @@ function registerFeishuChatTools(api) {
327
611
  ctx: toolContext,
328
612
  memberIdType: p.member_id_type
329
613
  });
330
- if (authorization.kind === "direct") return jsonResult(buildFeishuDirectChatMembers(authorization));
614
+ if (authorization.kind === "direct") return feishuExternalToolResult(buildFeishuDirectChatMembers(authorization));
331
615
  }
332
- return jsonResult(await getChatMembers(client, p.chat_id, readChatPageSize(rawParams), p.page_token, p.member_id_type));
616
+ return feishuExternalToolResult(await getChatMembers(client, p.chat_id, readChatPageSize(rawParams), p.page_token, p.member_id_type));
333
617
  case "info":
334
- if (!p.chat_id) return jsonResult({ error: "chat_id is required for action info" });
335
- return jsonResult(await getAuthorizedFeishuChatInfo({
618
+ if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action info" });
619
+ return feishuExternalToolResult(await getAuthorizedFeishuChatInfo({
336
620
  client,
337
621
  cfg,
338
622
  account,
@@ -340,8 +624,8 @@ function registerFeishuChatTools(api) {
340
624
  ctx: toolContext
341
625
  }));
342
626
  case "member_info":
343
- if (!p.member_id) return jsonResult({ error: "member_id is required for action member_info" });
344
- if (!p.chat_id) return jsonResult({ error: "chat_id is required for action member_info" });
627
+ if (!p.member_id) return feishuExternalToolResult({ error: "member_id is required for action member_info" });
628
+ if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action member_info" });
345
629
  {
346
630
  const chat = await getAuthorizedFeishuChatInfo({
347
631
  client,
@@ -362,27 +646,19 @@ function registerFeishuChatTools(api) {
362
646
  if (authorization.kind === "group") {
363
647
  const memberIdType = p.member_id_type ?? "open_id";
364
648
  await assertFeishuChatMember(client, p.chat_id, p.member_id, memberIdType);
365
- return jsonResult(await getFeishuMemberInfo(client, p.member_id, memberIdType));
649
+ return feishuExternalToolResult(await getFeishuMemberInfo(client, p.member_id, memberIdType));
366
650
  }
367
- return jsonResult(await getFeishuMemberInfo(client, authorization.memberId, authorization.memberIdType));
651
+ return feishuExternalToolResult(await getFeishuMemberInfo(client, authorization.memberId, authorization.memberIdType));
368
652
  }
369
- default: return jsonResult({ error: `Unknown action: ${String(p.action)}` });
653
+ default: return feishuExternalToolResult({ error: `Unknown action: ${String(p.action)}` });
370
654
  }
371
655
  } catch (err) {
372
- return jsonResult({ error: formatFeishuApiError(err, { includeNestedErrorLogId: true }) });
656
+ return feishuExternalToolResult({ error: formatFeishuApiError(err, { includeNestedErrorLogId: true }) });
373
657
  }
374
658
  }
375
659
  }), { name: "feishu_chat" });
376
660
  }
377
661
  //#endregion
378
- //#region extensions/feishu/src/tool-result.ts
379
- function unknownToolActionResult(action) {
380
- return jsonResult({ error: `Unknown action: ${String(action)}` });
381
- }
382
- function toolExecutionErrorResult(error) {
383
- return jsonResult({ error: formatErrorMessage(error) });
384
- }
385
- //#endregion
386
662
  //#region extensions/feishu/src/comment-reaction.ts
387
663
  const COMMENT_TYPING_REACTION_TYPE = "Typing";
388
664
  const COMMENT_REACTION_TIMEOUT_MS = 3e4;
@@ -726,15 +1002,15 @@ function formatDriveApiError(error) {
726
1002
  return formatFeishuApiError(error, { includeConfigParams: true });
727
1003
  }
728
1004
  function extractDriveApiErrorMeta(error) {
729
- if (!isRecord$1(error)) return { message: typeof error === "string" ? error : JSON.stringify(error) };
730
- const response = isRecord$1(error.response) ? error.response : void 0;
731
- const responseData = isRecord$1(response?.data) ? response?.data : void 0;
1005
+ if (!isRecord(error)) return { message: typeof error === "string" ? error : JSON.stringify(error) };
1006
+ const response = isRecord(error.response) ? error.response : void 0;
1007
+ const responseData = isRecord(response?.data) ? response?.data : void 0;
732
1008
  return {
733
1009
  message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
734
1010
  httpStatus: typeof response?.status === "number" ? response.status : void 0,
735
- feishuCode: typeof responseData?.code === "number" ? responseData.code : readString(responseData?.code),
736
- feishuMsg: readString(responseData?.msg),
737
- feishuLogId: readString(responseData?.log_id)
1011
+ feishuCode: typeof responseData?.code === "number" ? responseData.code : readStringValue(responseData?.code),
1012
+ feishuMsg: readStringValue(responseData?.msg),
1013
+ feishuLogId: readStringValue(responseData?.log_id)
738
1014
  };
739
1015
  }
740
1016
  function isReplyNotAllowedError(error) {
@@ -1013,6 +1289,7 @@ function registerFeishuDriveTools(api) {
1013
1289
  const defaultAccountId = ctx.agentAccountId;
1014
1290
  return {
1015
1291
  name: "feishu_drive",
1292
+ resultContentSource: "network",
1016
1293
  label: "Feishu Drive",
1017
1294
  description: "Feishu cloud storage operations. Actions: list, info, create_folder, move, delete, list_comments, list_comment_replies, add_comment, reply_comment",
1018
1295
  parameters: FeishuDriveSchema,
@@ -1029,21 +1306,21 @@ function registerFeishuDriveTools(api) {
1029
1306
  }
1030
1307
  });
1031
1308
  switch (p.action) {
1032
- case "list": return jsonResult(await listFolder(client, {
1309
+ case "list": return feishuExternalToolResult(await listFolder(client, {
1033
1310
  folder_token: p.folder_token,
1034
1311
  page_size: p.page_size,
1035
1312
  page_token: p.page_token
1036
1313
  }));
1037
- case "info": return jsonResult(await getFileInfo(client, p.file_token, p.type));
1038
- case "create_folder": return jsonResult(await createFolder(client, p.name, p.folder_token));
1039
- case "move": return jsonResult(await moveFile(client, p.file_token, p.type, p.folder_token));
1040
- case "delete": return jsonResult(await deleteFile(client, p.file_token, p.type));
1041
- case "list_comments": return jsonResult(await listComments(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comments")));
1042
- case "list_comment_replies": return jsonResult(await listCommentReplies(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comment_replies")));
1314
+ case "info": return feishuExternalToolResult(await getFileInfo(client, p.file_token, p.type));
1315
+ case "create_folder": return feishuExternalToolResult(await createFolder(client, p.name, p.folder_token));
1316
+ case "move": return feishuExternalToolResult(await moveFile(client, p.file_token, p.type, p.folder_token));
1317
+ case "delete": return feishuExternalToolResult(await deleteFile(client, p.file_token, p.type));
1318
+ case "list_comments": return feishuExternalToolResult(await listComments(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comments")));
1319
+ case "list_comment_replies": return feishuExternalToolResult(await listCommentReplies(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comment_replies")));
1043
1320
  case "add_comment": {
1044
1321
  const resolved = applyAddCommentDefaults(applyAddCommentAmbientDefaults(p, ctx));
1045
1322
  try {
1046
- return jsonResult(await addComment(client, resolved));
1323
+ return feishuExternalToolResult(await addComment(client, resolved));
1047
1324
  } finally {
1048
1325
  cleanupAmbientCommentTypingReaction({
1049
1326
  client: getDriveInternalClient(client),
@@ -1054,7 +1331,7 @@ function registerFeishuDriveTools(api) {
1054
1331
  case "reply_comment": {
1055
1332
  const resolved = applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "reply_comment");
1056
1333
  try {
1057
- return jsonResult(await deliverCommentThreadText(client, resolved));
1334
+ return feishuExternalToolResult(await deliverCommentThreadText(client, resolved));
1058
1335
  } finally {
1059
1336
  cleanupAmbientCommentTypingReaction({
1060
1337
  client: getDriveInternalClient(client),
@@ -1072,4 +1349,4 @@ function registerFeishuDriveTools(api) {
1072
1349
  }, { name: "feishu_drive" });
1073
1350
  }
1074
1351
  //#endregion
1075
- export { toolExecutionErrorResult as a, buildFeishuDirectChatMembers as c, getFeishuMemberInfo as d, registerFeishuChatTools as f, resolveToolsConfig as g, resolveFeishuToolAccount as h, createCommentTypingReactionLifecycle as i, getChatInfo as l, resolveAnyEnabledFeishuToolsConfig as m, registerFeishuDriveTools as n, unknownToolActionResult as o, createFeishuToolClient as p, cleanupAmbientCommentTypingReaction as r, assertFeishuChatMember as s, deliverCommentThreadText as t, getChatMembers as u };
1352
+ export { resolveFeishuToolAccount as C, resolveAnyEnabledFeishuToolsConfig as S, parseFeishuCommentTarget as _, assertFeishuChatMember as a, unknownToolActionResult as b, getChatMembers as c, encodeQuery as d, extractReplyText as f, normalizeCommentFileType as g, buildFeishuCommentTarget as h, createCommentTypingReactionLifecycle as i, getFeishuMemberInfo as l, requestFeishuApi as m, registerFeishuDriveTools as n, buildFeishuDirectChatMembers as o, parseCommentContentElements as p, cleanupAmbientCommentTypingReaction as r, getChatInfo as s, deliverCommentThreadText as t, registerFeishuChatTools as u, feishuExternalToolResult as v, resolveToolsConfig as w, createFeishuToolClient as x, toolExecutionErrorResult as y };