@openclaw/feishu 2026.9.1 → 2026.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accounts-RwSyseKr.js → accounts-cCMNFBKg.js} +1 -1
- package/dist/api.js +24 -24
- package/dist/{channel-DSYCNah8.js → channel-DrAqd5ED.js} +24 -39
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-EfDl2Q8q.js → channel.runtime-BZzuDlOS.js} +4 -4
- package/dist/{chat-CxXaPTuO.js → chat-BZSnEtgb.js} +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{doctor-contract-CQAuBotF.js → doctor-contract-w6FpMypv.js} +1 -1
- package/dist/{monitor-DoMmOfaD.js → monitor-DSGtOPio.js} +2 -2
- package/dist/{monitor.account-WFdlCDhR.js → monitor.account-BRDUsS_q.js} +69 -125
- package/dist/{reply-delivery-result-CEFTDA3f.js → reply-delivery-result-BXEmtKpf.js} +98 -20
- package/dist/setup-api.js +1 -1
- package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
- package/node_modules/typebox/build/system/memory/create.mjs +3 -4
- package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
- package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
- package/node_modules/typebox/build/system/memory/update.mjs +3 -2
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +5 -5
|
@@ -66,7 +66,7 @@ function resolveFeishuBaseCredentials(cfg, mode, rootConfig) {
|
|
|
66
66
|
return {
|
|
67
67
|
appId,
|
|
68
68
|
appSecret,
|
|
69
|
-
domain: cfg?.domain ?? "feishu"
|
|
69
|
+
domain: cfg?.domain?.replace(/^https:/i, "https:") ?? "feishu"
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
function resolveFeishuEventSecrets(cfg, mode, rootConfig) {
|
package/dist/api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-
|
|
2
|
-
import { G as
|
|
1
|
+
import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-DrAqd5ED.js";
|
|
2
|
+
import { G as feishuExternalToolResult, J as createFeishuToolClient, K as toolExecutionErrorResult, W as registerFeishuDriveTools, X as resolveFeishuToolAccount, Y as resolveAnyEnabledFeishuToolsConfig, ft as parseFeishuMarkdown, lt as chunkFeishuMarkdown, q as unknownToolActionResult } from "./reply-delivery-result-BXEmtKpf.js";
|
|
3
3
|
import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
|
|
4
4
|
import { s as resolveConfiguredHttpTimeoutMs } from "./client-DMbIL3UH.js";
|
|
5
5
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
6
|
-
import { o as registerFeishuChatTools } from "./chat-
|
|
6
|
+
import { o as registerFeishuChatTools } from "./chat-BZSnEtgb.js";
|
|
7
7
|
import { n as getFeishuThreadBindingManager, t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
|
|
8
8
|
import { n as handleFeishuSubagentEnded, t as handleFeishuSubagentDeliveryTarget } from "./subagent-hooks-Buadpie_.js";
|
|
9
9
|
import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
@@ -553,19 +553,13 @@ function resolveRemoteImageUrl(value) {
|
|
|
553
553
|
}
|
|
554
554
|
function collectMarkdownImages(root) {
|
|
555
555
|
const definitions = /* @__PURE__ */ new Map();
|
|
556
|
+
const imageNodes = [];
|
|
556
557
|
visitMarkdown(root, (node) => {
|
|
557
|
-
if (node.type
|
|
558
|
-
|
|
558
|
+
if (node.type === "definition" && node.identifier && node.url) {
|
|
559
|
+
if (!definitions.has(node.identifier)) definitions.set(node.identifier, node.url);
|
|
560
|
+
} else if (node.type === "image" || node.type === "imageReference") imageNodes.push(node);
|
|
559
561
|
});
|
|
560
|
-
|
|
561
|
-
visitMarkdown(root, (node) => {
|
|
562
|
-
if (node.type === "image") {
|
|
563
|
-
images.push({ url: resolveRemoteImageUrl(node.url) });
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
if (node.type === "imageReference") images.push({ url: resolveRemoteImageUrl(node.identifier ? definitions.get(node.identifier) : void 0) });
|
|
567
|
-
});
|
|
568
|
-
return images;
|
|
562
|
+
return imageNodes.map((node) => ({ url: resolveRemoteImageUrl(node.type === "image" ? node.url : node.identifier ? definitions.get(node.identifier) : void 0) }));
|
|
569
563
|
}
|
|
570
564
|
function splitSourceAtOffsets(source, offsets) {
|
|
571
565
|
const chunks = [];
|
|
@@ -658,7 +652,11 @@ function createDocxMarkdownChunk(markdown) {
|
|
|
658
652
|
};
|
|
659
653
|
}
|
|
660
654
|
function createDocxMarkdownPlan(markdown) {
|
|
661
|
-
|
|
655
|
+
const root = parseFeishuMarkdown(markdown);
|
|
656
|
+
return { chunks: splitSourceAtOffsets(markdown, headingOffsets(markdown, root)).map((chunk) => chunk === markdown ? {
|
|
657
|
+
markdown,
|
|
658
|
+
images: collectMarkdownImages(root)
|
|
659
|
+
} : createDocxMarkdownChunk(chunk)) };
|
|
662
660
|
}
|
|
663
661
|
function splitDocxMarkdownBySize(markdown, maxChars) {
|
|
664
662
|
if (markdown.length <= maxChars) return [markdown];
|
|
@@ -2277,7 +2275,7 @@ async function createApp(client, name, folderToken, logger) {
|
|
|
2277
2275
|
url: res.data?.app?.url,
|
|
2278
2276
|
cleaned_placeholder_rows: cleanedRows,
|
|
2279
2277
|
cleaned_default_fields: cleanedFields,
|
|
2280
|
-
hint: tableId ? `Table created. Use app_token="${appToken}" and table_id="${tableId}" for other bitable tools.` : "
|
|
2278
|
+
hint: tableId ? `Table created. Use app_token="${appToken}" and table_id="${tableId}" for other bitable tools.` : "Application created, but table metadata was not retrieved. Inspect the existing application using the returned app_token or URL; do not create it again."
|
|
2281
2279
|
};
|
|
2282
2280
|
}
|
|
2283
2281
|
async function createField(client, appToken, tableId, fieldName, fieldType, property) {
|
|
@@ -2321,13 +2319,15 @@ async function updateRecord(client, appToken, tableId, recordId, fields) {
|
|
|
2321
2319
|
});
|
|
2322
2320
|
return { record: res.data?.record };
|
|
2323
2321
|
}
|
|
2322
|
+
const BITABLE_APP_TOKEN_DESCRIPTION = "Bitable application token (the /base/ URL identifier, or app_token from metadata). Not the node token in a /wiki/ URL.";
|
|
2323
|
+
const BITABLE_RECORD_FIELDS_DESCRIPTION = "Field values keyed by field name. Format by type: Text='string', Number=123, SingleSelect='Option', MultiSelect=['A','B'], DateTime=timestamp_ms, User=[{id:'ou_xxx'}], URL={text:'Display',link:'https://...'}";
|
|
2324
2324
|
const GetMetaSchema = Type.Object({ url: Type.String({ description: "Bitable URL. Supports both formats: /base/XXX?table=YYY or /wiki/XXX?table=YYY" }) });
|
|
2325
2325
|
const ListFieldsSchema = Type.Object({
|
|
2326
|
-
app_token: Type.String({ description:
|
|
2326
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2327
2327
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" })
|
|
2328
2328
|
});
|
|
2329
2329
|
const ListRecordsSchema = Type.Object({
|
|
2330
|
-
app_token: Type.String({ description:
|
|
2330
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2331
2331
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }),
|
|
2332
2332
|
page_size: optionalPositiveIntegerSchema({
|
|
2333
2333
|
description: "Number of records per page (1-500, default 100)",
|
|
@@ -2336,7 +2336,7 @@ const ListRecordsSchema = Type.Object({
|
|
|
2336
2336
|
page_token: Type.Optional(Type.String({ description: "Pagination token from previous response" }))
|
|
2337
2337
|
});
|
|
2338
2338
|
const GetRecordSchema = Type.Object({
|
|
2339
|
-
app_token: Type.String({ description:
|
|
2339
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2340
2340
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }),
|
|
2341
2341
|
record_id: Type.String({ description: "Record ID to retrieve" })
|
|
2342
2342
|
});
|
|
@@ -2349,16 +2349,16 @@ const BitableFieldValueSchema = Type.Unsafe({ type: [
|
|
|
2349
2349
|
"null"
|
|
2350
2350
|
] });
|
|
2351
2351
|
const CreateRecordSchema = Type.Object({
|
|
2352
|
-
app_token: Type.String({ description:
|
|
2352
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2353
2353
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }),
|
|
2354
|
-
fields: Type.Record(Type.String(), BitableFieldValueSchema, { description:
|
|
2354
|
+
fields: Type.Record(Type.String(), BitableFieldValueSchema, { description: BITABLE_RECORD_FIELDS_DESCRIPTION })
|
|
2355
2355
|
});
|
|
2356
2356
|
const CreateAppSchema = Type.Object({
|
|
2357
2357
|
name: Type.String({ description: "Name for the new Bitable application" }),
|
|
2358
2358
|
folder_token: Type.Optional(Type.String({ description: "Optional folder token to place the Bitable in a specific folder" }))
|
|
2359
2359
|
});
|
|
2360
2360
|
const CreateFieldSchema = Type.Object({
|
|
2361
|
-
app_token: Type.String({ description:
|
|
2361
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2362
2362
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }),
|
|
2363
2363
|
field_name: Type.String({ description: "Name for the new field" }),
|
|
2364
2364
|
field_type: Type.Number({
|
|
@@ -2368,10 +2368,10 @@ const CreateFieldSchema = Type.Object({
|
|
|
2368
2368
|
property: Type.Optional(Type.Record(Type.String(), BitableFieldValueSchema, { description: "Field-specific properties (e.g., options for SingleSelect, format for Number)" }))
|
|
2369
2369
|
});
|
|
2370
2370
|
const UpdateRecordSchema = Type.Object({
|
|
2371
|
-
app_token: Type.String({ description:
|
|
2371
|
+
app_token: Type.String({ description: BITABLE_APP_TOKEN_DESCRIPTION }),
|
|
2372
2372
|
table_id: Type.String({ description: "Table ID (from URL: ?table=YYY)" }),
|
|
2373
2373
|
record_id: Type.String({ description: "Record ID to update" }),
|
|
2374
|
-
fields: Type.Record(Type.String(), BitableFieldValueSchema, { description:
|
|
2374
|
+
fields: Type.Record(Type.String(), BitableFieldValueSchema, { description: BITABLE_RECORD_FIELDS_DESCRIPTION })
|
|
2375
2375
|
});
|
|
2376
2376
|
function registerFeishuBitableTools(api) {
|
|
2377
2377
|
if (!api.config) return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
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
2
|
import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BTQCYRZQ.js";
|
|
3
|
-
import {
|
|
4
|
-
import { n as normalizeCompatibilityConfig, o as normalizeFeishuExternalKey, r as FeishuChannelConfigSchema, t as legacyConfigRules } from "./doctor-contract-
|
|
3
|
+
import { A as sendStructuredCardFeishu, B as buildFeishuMediaFallbackText, C as shouldSuppressFeishuTextForVoiceMedia, Ct as resolveFeishuGroupConfig, Et as resolveFeishuGroupToolPolicy, H as resolveFeishuIdentityHeaderTitle, O as sendCardFeishu, Ot as normalizeFeishuChatType, R as createFeishuSendReceipt, U as deliverCommentThreadText, Z as cleanupAmbientCommentTypingReaction, _t as isFeishuGroupReadAllowed, a as FEISHU_PRESENTATION_CAPABILITIES, c as buildFeishuPresentationCard, d as feishuCardWithinTableLimit, dt as materializeFeishuPostMarkdownSoftBreaks, f as isFeishuCardWithinEnvelope, g as renderFeishuPresentationPayload, gt as canEnumerateAllFeishuPeers, h as renderFeishuPresentationFallbackText, ht as canEnumerateAllFeishuGroups, k as sendMessageFeishu, kt as resolveFeishuChatType, l as buildFeishuPresentationFallback, lt as chunkFeishuMarkdown, m as readNativeFeishuCard, mt as authorizeFeishuChatMemberRead, n as createFeishuReplyDeliveryResult, o as assertFeishuCardWithinEnvelope, ot as parseFeishuCommentTarget, p as markRenderedFeishuCard, pt as assertFeishuChatReadAllowed, s as buildFeishuPayloadCard, st as readNativeFeishuCardJson, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as chunkFeishuPostMarkdown, v as resolveFeishuRichReply, vt as isFeishuGroupReadEnabled, w as chunkFeishuCardMarkdown, x as sendMediaFeishu, y as withinCardTableLimit, yt as resolveFeishuChatReadPreliminaryAuthorization } from "./reply-delivery-result-BXEmtKpf.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
6
|
import { t as messageActionTargetAliases } from "./security-audit-DKfR4Cv3.js";
|
|
7
7
|
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
@@ -44,7 +44,7 @@ import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMedia
|
|
|
44
44
|
import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
45
45
|
import { readStringParam } from "openclaw/plugin-sdk/param-readers";
|
|
46
46
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
47
|
-
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput, 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, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
48
48
|
import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
|
|
49
49
|
//#region extensions/feishu/src/approval-auth.ts
|
|
50
50
|
function normalizeFeishuApproverId(value) {
|
|
@@ -799,10 +799,6 @@ function partialFeishuSendError(error, results) {
|
|
|
799
799
|
function readFeishuPropagateMediaUploadFailure(payload) {
|
|
800
800
|
return (isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.[FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER] === true;
|
|
801
801
|
}
|
|
802
|
-
function buildFeishuPropagationOnlyChannelData(payload) {
|
|
803
|
-
if ((isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.["__openclawPropagateMediaUploadFailure"] !== true) return;
|
|
804
|
-
return { feishu: { [FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER]: true } };
|
|
805
|
-
}
|
|
806
802
|
function resolveFeishuReplyMode(params) {
|
|
807
803
|
const replyToMessageId = params.replyToId?.trim();
|
|
808
804
|
if (replyToMessageId) return {
|
|
@@ -867,7 +863,7 @@ async function sendOutboundText(params) {
|
|
|
867
863
|
cfg,
|
|
868
864
|
accountId
|
|
869
865
|
}).config?.renderMode ?? "auto";
|
|
870
|
-
const useCard = renderMode === "card" || renderMode === "auto" && shouldUseCard(text);
|
|
866
|
+
const useCard = (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) && withinCardTableLimit(text);
|
|
871
867
|
const tableMode = resolveMarkdownTableMode({
|
|
872
868
|
cfg,
|
|
873
869
|
channel: "feishu"
|
|
@@ -1023,7 +1019,7 @@ const feishuOutbound = {
|
|
|
1023
1019
|
text,
|
|
1024
1020
|
interactive: void 0,
|
|
1025
1021
|
presentation: void 0,
|
|
1026
|
-
channelData:
|
|
1022
|
+
channelData: void 0
|
|
1027
1023
|
},
|
|
1028
1024
|
separateMediaAndText: true
|
|
1029
1025
|
});
|
|
@@ -1355,36 +1351,22 @@ function resolveFeishuOutboundSessionRoute(params) {
|
|
|
1355
1351
|
//#region extensions/feishu/src/setup-core.ts
|
|
1356
1352
|
function setFeishuNamedAccountEnabled(cfg, accountId, enabled) {
|
|
1357
1353
|
const feishuCfg = cfg.channels?.feishu;
|
|
1358
|
-
return {
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
accounts
|
|
1365
|
-
|
|
1366
|
-
[accountId]: {
|
|
1367
|
-
...feishuCfg?.accounts?.[accountId],
|
|
1368
|
-
enabled
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1354
|
+
return patchTopLevelChannelConfigSection({
|
|
1355
|
+
cfg,
|
|
1356
|
+
channel: "feishu",
|
|
1357
|
+
patch: { accounts: {
|
|
1358
|
+
...feishuCfg?.accounts,
|
|
1359
|
+
[accountId]: {
|
|
1360
|
+
...feishuCfg?.accounts?.[accountId],
|
|
1361
|
+
enabled
|
|
1371
1362
|
}
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1363
|
+
} }
|
|
1364
|
+
});
|
|
1374
1365
|
}
|
|
1375
1366
|
const feishuSetupAdapter = {
|
|
1376
1367
|
resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
|
|
1377
1368
|
applyAccountConfig: ({ cfg, accountId }) => {
|
|
1378
|
-
if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return
|
|
1379
|
-
...cfg,
|
|
1380
|
-
channels: {
|
|
1381
|
-
...cfg.channels,
|
|
1382
|
-
feishu: {
|
|
1383
|
-
...cfg.channels?.feishu,
|
|
1384
|
-
enabled: true
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
};
|
|
1369
|
+
if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return setSetupChannelEnabled(cfg, "feishu", true);
|
|
1388
1370
|
return setFeishuNamedAccountEnabled(cfg, accountId, true);
|
|
1389
1371
|
}
|
|
1390
1372
|
};
|
|
@@ -1900,7 +1882,7 @@ const meta = {
|
|
|
1900
1882
|
order: 70,
|
|
1901
1883
|
preferSessionLookupForAnnounceTarget: true
|
|
1902
1884
|
};
|
|
1903
|
-
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
1885
|
+
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-BZzuDlOS.js"), "feishuChannelRuntime");
|
|
1904
1886
|
async function resolveFeishuMessageSender(params) {
|
|
1905
1887
|
try {
|
|
1906
1888
|
const sender = params.resolve(await loadFeishuChannelRuntime());
|
|
@@ -2390,7 +2372,10 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2390
2372
|
buildModelOverrideParentCandidates: ({ parentConversationId }) => buildFeishuModelOverrideParentCandidates(parentConversationId)
|
|
2391
2373
|
},
|
|
2392
2374
|
mentions: { stripPatterns: () => ["<at user_id=\"[^\"]*\">[^<]*</at>"] },
|
|
2393
|
-
reload: {
|
|
2375
|
+
reload: {
|
|
2376
|
+
configPrefixes: ["channels.feishu"],
|
|
2377
|
+
noopPrefixes: ["messages.inbound"]
|
|
2378
|
+
},
|
|
2394
2379
|
doctor: feishuDoctor,
|
|
2395
2380
|
configSchema: FeishuChannelConfigSchema,
|
|
2396
2381
|
config: {
|
|
@@ -2487,7 +2472,7 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2487
2472
|
interactive
|
|
2488
2473
|
})
|
|
2489
2474
|
}) : void 0;
|
|
2490
|
-
const presentationCard = generatedCard && isFeishuCardWithinEnvelope(generatedCard) ? generatedCard : void 0;
|
|
2475
|
+
const presentationCard = generatedCard && feishuCardWithinTableLimit(generatedCard) && isFeishuCardWithinEnvelope(generatedCard) ? generatedCard : void 0;
|
|
2491
2476
|
const presentationFellBack = Boolean(generatedCard && !presentationCard);
|
|
2492
2477
|
const card = presentation ? presentationCard : textCard;
|
|
2493
2478
|
if (card && mediaUrl) throw new Error(`Feishu ${ctx.action} does not support card with media.`);
|
|
@@ -3024,7 +3009,7 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
3024
3009
|
})
|
|
3025
3010
|
}),
|
|
3026
3011
|
gateway: { startAccount: async (ctx) => {
|
|
3027
|
-
const { monitorFeishuProvider } = await import("./monitor-
|
|
3012
|
+
const { monitorFeishuProvider } = await import("./monitor-DSGtOPio.js");
|
|
3028
3013
|
const account = resolveFeishuRuntimeAccount({
|
|
3029
3014
|
cfg: ctx.cfg,
|
|
3030
3015
|
accountId: ctx.accountId
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as feishuPlugin } from "./channel-
|
|
1
|
+
import { t as feishuPlugin } from "./channel-DrAqd5ED.js";
|
|
2
2
|
export { feishuPlugin };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
2
|
-
import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-
|
|
3
|
-
import {
|
|
1
|
+
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
|
+
import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-DrAqd5ED.js";
|
|
3
|
+
import { E as getMessageFeishu, O as sendCardFeishu, S as sendStickerFeishu, T as editMessageFeishu, k as sendMessageFeishu, z as assertFeishuApiSuccess } from "./reply-delivery-result-BXEmtKpf.js";
|
|
4
4
|
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
5
|
-
import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-
|
|
5
|
+
import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-BZSnEtgb.js";
|
|
6
6
|
import { t as probeFeishu } from "./probe-D_SatdZl.js";
|
|
7
7
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
//#region extensions/feishu/src/directory.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { G as feishuExternalToolResult, X as resolveFeishuToolAccount, Y as resolveAnyEnabledFeishuToolsConfig, kt as resolveFeishuChatType, mt as authorizeFeishuChatMemberRead, pt as assertFeishuChatReadAllowed, tt as formatFeishuApiError, yt as resolveFeishuChatReadPreliminaryAuthorization } from "./reply-delivery-result-BXEmtKpf.js";
|
|
2
2
|
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
3
3
|
import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
4
4
|
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-w6FpMypv.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -40,7 +40,7 @@ function canonicalTextPattern(maxLength) {
|
|
|
40
40
|
const FeishuStickerSetSchema = z.record(z.string().regex(FEISHU_EXTERNAL_KEY_PATTERN), z.array(z.string().regex(canonicalTextPattern(64))).min(1).max(8)).refine((set) => Object.keys(set).length <= MAX_STICKERS_PER_SET, { message: `At most ${MAX_STICKERS_PER_SET} stickers per bot set are allowed` }).meta({ maxProperties: MAX_STICKERS_PER_SET });
|
|
41
41
|
const FeishuStickerSetsSchema = z.record(z.string().regex(canonicalTextPattern(128)), FeishuStickerSetSchema).refine((sets) => Object.keys(sets).length <= MAX_STICKER_SETS, { message: `At most ${MAX_STICKER_SETS} bot sticker sets are allowed` }).meta({ maxProperties: MAX_STICKER_SETS });
|
|
42
42
|
const FeishuGroupPolicySchema = z.union([GroupPolicySchema, z.literal("allowall").transform(() => "open")]);
|
|
43
|
-
const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().
|
|
43
|
+
const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().regex(/^[Hh][Tt][Tt][Pp][Ss]:\/\//).url()]);
|
|
44
44
|
const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
|
|
45
45
|
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" });
|
|
46
46
|
const TtsOverrideSchema = z.object({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
1
|
+
import { r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
2
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
3
3
|
import { r as registerFeishuAiAgent, t as probeFeishu } from "./probe-D_SatdZl.js";
|
|
4
4
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
@@ -143,7 +143,7 @@ async function fetchBotIdentityForMonitor(account, options = {}) {
|
|
|
143
143
|
}
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region extensions/feishu/src/monitor.ts
|
|
146
|
-
const loadMonitorAccountRuntime = createLazyRuntimeModule(() => import("./monitor.account-
|
|
146
|
+
const loadMonitorAccountRuntime = createLazyRuntimeModule(() => import("./monitor.account-BRDUsS_q.js"));
|
|
147
147
|
async function monitorFeishuProvider(opts = {}) {
|
|
148
148
|
const cfg = opts.config;
|
|
149
149
|
if (!cfg) throw new Error("Config is required for Feishu monitor");
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
1
|
+
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
2
|
import { i as resolveReceiveIdType } from "./targets-BTQCYRZQ.js";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as normalizeFeishuWebhookPath, o as normalizeFeishuExternalKey } from "./doctor-contract-
|
|
3
|
+
import { $ as encodeQuery, A as sendStructuredCardFeishu, B as buildFeishuMediaFallbackText, C as shouldSuppressFeishuTextForVoiceMedia, Ct as resolveFeishuGroupConfig, D as listFeishuThreadMessages, Dt as resolveFeishuReplyPolicy, E as getMessageFeishu, F as isFeishuGroupChatType, I as parseInteractiveCardContent, L as parsePostContent, M as extractMentionTargets, Mt as createFeishuCardInteractionEnvelope, N as isFeishuBroadcastMention, Nt as decodeFeishuCardAction, O as sendCardFeishu, Ot as normalizeFeishuChatType$2, P as isMentionForwardRequest, Q as createCommentTypingReactionLifecycle, St as resolveFeishuDmIngressAccess, Tt as resolveFeishuGroupSenderActivationIngressAccess, U as deliverCommentThreadText, V as resolveFeishuIdentityEmoji, _ as renderFeishuReplyPayload, at as normalizeCommentFileType, b as saveMessageResourceFeishu, bt as hasExplicitFeishuGroupConfig, ct as resolveFeishuCardTemplate, dt as materializeFeishuPostMarkdownSoftBreaks, et as extractReplyText, i as noVisibleFeishuReplyDelivery, it as buildFeishuCommentTarget, j as formatFeishuMediaContent, jt as buildFeishuCardActionTextFallback, k as sendMessageFeishu, kt as resolveFeishuChatType, l as buildFeishuPresentationFallback, n as createFeishuReplyDeliveryResult, nt as parseCommentContentElements, r as mergeFeishuReplyDeliveryResults, rt as requestFeishuApi, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as chunkFeishuPostMarkdown, v as resolveFeishuRichReply, w as chunkFeishuCardMarkdown, wt as resolveFeishuGroupConversationIngressAccess, x as sendMediaFeishu, xt as normalizeFeishuAllowEntry, y as withinCardTableLimit } from "./reply-delivery-result-BXEmtKpf.js";
|
|
4
|
+
import { a as normalizeFeishuWebhookPath, o as normalizeFeishuExternalKey } from "./doctor-contract-w6FpMypv.js";
|
|
5
5
|
import { o as resolveConfiguredFeishuGroupSessionScope, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
|
|
6
6
|
import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient, s as resolveConfiguredHttpTimeoutMs } from "./client-DMbIL3UH.js";
|
|
7
7
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
8
|
-
import { r as getChatInfo } from "./chat-
|
|
8
|
+
import { r as getChatInfo } from "./chat-BZSnEtgb.js";
|
|
9
9
|
import { t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
|
|
10
10
|
import { evaluateSupplementalContextVisibility, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode } from "./runtime-api.js";
|
|
11
11
|
import { t as readFeishuJsonResponse } from "./json-response-DXajw_FU.js";
|
|
12
12
|
import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-D_SatdZl.js";
|
|
13
|
-
import { t as fetchBotIdentityForMonitor } from "./monitor-
|
|
13
|
+
import { t as fetchBotIdentityForMonitor } from "./monitor-DSGtOPio.js";
|
|
14
14
|
import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
15
15
|
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createReplyPrefixContext, formatChannelProgressDraftLineForEntry, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
16
16
|
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
|
@@ -32,11 +32,11 @@ import { resolvePinnedMainDmOwnerFromAllowlist, safeEqualSecret } from "openclaw
|
|
|
32
32
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
33
33
|
import { isRecord as isRecord$1 } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
34
34
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
35
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
35
36
|
import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
|
|
36
37
|
import * as crypto$1 from "node:crypto";
|
|
37
38
|
import crypto, { createHash } from "node:crypto";
|
|
38
39
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
39
|
-
import { escapeHtml, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
40
40
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
41
41
|
import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
42
42
|
import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
|
|
@@ -44,6 +44,7 @@ import { formatReasoningMessage, resolveHumanDelayConfig } from "openclaw/plugin
|
|
|
44
44
|
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
|
45
45
|
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
46
46
|
import * as http from "node:http";
|
|
47
|
+
import { createRuntimeConfigReader } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
47
48
|
import { channelBlockedPatch, channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
48
49
|
import { installRequestBodyLimitGuard, readWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
49
50
|
import { isAbortRequestText, isBtwRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
|
|
@@ -336,56 +337,6 @@ const FEISHU_MEDIA_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
|
336
337
|
"media",
|
|
337
338
|
"sticker"
|
|
338
339
|
]);
|
|
339
|
-
function formatFeishuMediaContent(parsed, messageType) {
|
|
340
|
-
if (messageType === "sticker") {
|
|
341
|
-
const fileKey = normalizeFeishuExternalKey(parsed?.file_key);
|
|
342
|
-
return fileKey ? `<sticker key="${escapeHtml(fileKey)}"/>` : "[Sticker]";
|
|
343
|
-
}
|
|
344
|
-
const speechToText = messageType === "audio" && typeof parsed.speech_to_text === "string" ? parsed.speech_to_text.trim() : "";
|
|
345
|
-
if (speechToText) return speechToText;
|
|
346
|
-
return "";
|
|
347
|
-
}
|
|
348
|
-
function formatSubMessageContent(content, contentType) {
|
|
349
|
-
try {
|
|
350
|
-
const parsed = JSON.parse(content);
|
|
351
|
-
switch (contentType) {
|
|
352
|
-
case "text": return parsed.text || content;
|
|
353
|
-
case "post": return parsePostContent(content).textContent;
|
|
354
|
-
case "interactive": return parseInteractiveCardContent(parsed);
|
|
355
|
-
case "image": return "[Image]";
|
|
356
|
-
case "file": return `[File: ${parsed.file_name || "unknown"}]`;
|
|
357
|
-
case "audio": return "[Audio]";
|
|
358
|
-
case "video": return "[Video]";
|
|
359
|
-
case "sticker": return formatFeishuMediaContent(parsed, contentType);
|
|
360
|
-
case "merge_forward": return "[Nested Merged Forward]";
|
|
361
|
-
default: return `[${contentType}]`;
|
|
362
|
-
}
|
|
363
|
-
} catch {
|
|
364
|
-
return content;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
function parseMergeForwardContent(params) {
|
|
368
|
-
const { content, log } = params;
|
|
369
|
-
const maxMessages = 50;
|
|
370
|
-
log?.("feishu: parsing merge_forward sub-messages from API response");
|
|
371
|
-
let items;
|
|
372
|
-
try {
|
|
373
|
-
items = JSON.parse(content);
|
|
374
|
-
} catch {
|
|
375
|
-
log?.("feishu: merge_forward items parse failed");
|
|
376
|
-
return "[Merged and Forwarded Message - parse error]";
|
|
377
|
-
}
|
|
378
|
-
if (!Array.isArray(items) || items.length === 0) return "[Merged and Forwarded Message - no sub-messages]";
|
|
379
|
-
const container = items.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id);
|
|
380
|
-
const subMessages = container ? items.filter((item) => item !== container) : items.filter((item) => item.upper_message_id);
|
|
381
|
-
if (subMessages.length === 0) return "[Merged and Forwarded Message - no sub-messages found]";
|
|
382
|
-
log?.(`feishu: merge_forward contains ${subMessages.length} sub-messages`);
|
|
383
|
-
subMessages.sort((a, b) => (parseStrictNonNegativeInteger(a.create_time) ?? 0) - (parseStrictNonNegativeInteger(b.create_time) ?? 0));
|
|
384
|
-
const lines = ["[Merged and Forwarded Messages]"];
|
|
385
|
-
for (const item of subMessages.slice(0, maxMessages)) lines.push(`- ${formatSubMessageContent(item.body?.content || "", item.msg_type || "text")}`);
|
|
386
|
-
if (subMessages.length > maxMessages) lines.push(`... and ${subMessages.length - maxMessages} more messages`);
|
|
387
|
-
return lines.join("\n");
|
|
388
|
-
}
|
|
389
340
|
function checkBotMentioned(event, botOpenId) {
|
|
390
341
|
if (!botOpenId) return false;
|
|
391
342
|
const mentions = event.message.mentions ?? [];
|
|
@@ -397,13 +348,14 @@ function normalizeMentions(text, mentions, botStripId) {
|
|
|
397
348
|
if (!mentions || mentions.length === 0) return text;
|
|
398
349
|
const escaped = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
399
350
|
const escapeName = (value) => value.replace(/</g, "<").replace(/>/g, ">");
|
|
400
|
-
|
|
351
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
401
352
|
for (const mention of mentions) {
|
|
402
353
|
const mentionId = mention.id.open_id;
|
|
403
354
|
const replacement = botStripId && mentionId === botStripId ? "" : mentionId ? `<at user_id="${mentionId}">${escapeName(mention.name)}</at>` : `@${mention.name}`;
|
|
404
|
-
|
|
355
|
+
replacements.set(mention.key, replacement);
|
|
405
356
|
}
|
|
406
|
-
|
|
357
|
+
const keys = [...replacements.keys()].toSorted((a, b) => b.length - a.length).map(escaped);
|
|
358
|
+
return text.replace(new RegExp(keys.join("|"), "g"), (key) => replacements.get(key)).trim();
|
|
407
359
|
}
|
|
408
360
|
function normalizeFeishuCommandProbeBody(text) {
|
|
409
361
|
if (!text) return "";
|
|
@@ -439,12 +391,6 @@ function parseMediaKeys(content, messageType) {
|
|
|
439
391
|
function toMessageResourceType(messageType) {
|
|
440
392
|
return messageType === "image" ? "image" : "file";
|
|
441
393
|
}
|
|
442
|
-
async function resolveSavedFeishuMedia(params) {
|
|
443
|
-
if ("saved" in params.result) return params.result.saved;
|
|
444
|
-
const core = getFeishuRuntime();
|
|
445
|
-
const contentType = params.result.contentType ?? await core.media.detectMime({ buffer: params.result.buffer });
|
|
446
|
-
return await core.channel.media.saveMediaBuffer(params.result.buffer, contentType, "inbound", params.maxBytes, params.result.fileName ?? params.originalFilename);
|
|
447
|
-
}
|
|
448
394
|
function resolveFeishuMediaKind(messageType) {
|
|
449
395
|
switch (messageType) {
|
|
450
396
|
case "image": return "image";
|
|
@@ -478,16 +424,12 @@ async function resolveFeishuMediaList(params) {
|
|
|
478
424
|
const fileName = attachment.kind === "file" ? attachment.fileName : void 0;
|
|
479
425
|
const mediaKind = attachment.kind === "image" ? "image" : "video";
|
|
480
426
|
try {
|
|
481
|
-
const saved = await
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
accountId,
|
|
488
|
-
maxBytes,
|
|
489
|
-
...fileName ? { originalFilename: fileName } : {}
|
|
490
|
-
}),
|
|
427
|
+
const { saved } = await saveMessageResourceFeishu({
|
|
428
|
+
cfg,
|
|
429
|
+
messageId,
|
|
430
|
+
fileKey: attachment.key,
|
|
431
|
+
type: attachment.kind,
|
|
432
|
+
accountId,
|
|
491
433
|
maxBytes,
|
|
492
434
|
...fileName ? { originalFilename: fileName } : {}
|
|
493
435
|
});
|
|
@@ -509,16 +451,12 @@ async function resolveFeishuMediaList(params) {
|
|
|
509
451
|
try {
|
|
510
452
|
const fileKey = mediaKeys.fileKey || mediaKeys.imageKey;
|
|
511
453
|
if (!fileKey) return [{ kind: resolveFeishuMediaKind(messageType) }];
|
|
512
|
-
const saved = await
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
accountId,
|
|
519
|
-
maxBytes,
|
|
520
|
-
originalFilename: mediaKeys.fileName
|
|
521
|
-
}),
|
|
454
|
+
const { saved } = await saveMessageResourceFeishu({
|
|
455
|
+
cfg,
|
|
456
|
+
messageId,
|
|
457
|
+
fileKey,
|
|
458
|
+
type: toMessageResourceType(messageType),
|
|
459
|
+
accountId,
|
|
522
460
|
maxBytes,
|
|
523
461
|
originalFilename: mediaKeys.fileName
|
|
524
462
|
});
|
|
@@ -2287,14 +2225,15 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2287
2225
|
if (result?.visibleReplySent === true || !content?.trim()) return result;
|
|
2288
2226
|
const cardHeader = resolveCardHeader(agentId, identity);
|
|
2289
2227
|
const cardNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
|
|
2228
|
+
const useRecoveryCard = withinCardTableLimit(content);
|
|
2290
2229
|
return await sendChunkedTextReply({
|
|
2291
2230
|
text: content,
|
|
2292
|
-
useCard:
|
|
2231
|
+
useCard: useRecoveryCard,
|
|
2293
2232
|
infoKind,
|
|
2294
2233
|
header: cardHeader,
|
|
2295
2234
|
note: cardNote,
|
|
2296
2235
|
chunkMentions: requiredMentionTargets,
|
|
2297
|
-
sendChunk: async ({ chunk, mentions }) => await sendStructuredCardFeishu({
|
|
2236
|
+
sendChunk: async ({ chunk, mentions }) => useRecoveryCard ? await sendStructuredCardFeishu({
|
|
2298
2237
|
cfg,
|
|
2299
2238
|
to: sendTarget,
|
|
2300
2239
|
text: chunk,
|
|
@@ -2305,6 +2244,16 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2305
2244
|
header: cardHeader,
|
|
2306
2245
|
note: cardNote,
|
|
2307
2246
|
...mentions ? { mentions } : {}
|
|
2247
|
+
}) : await sendMessageFeishu({
|
|
2248
|
+
cfg,
|
|
2249
|
+
to: sendTarget,
|
|
2250
|
+
text: chunk,
|
|
2251
|
+
preparedPostText: true,
|
|
2252
|
+
replyToMessageId: sendReplyToMessageId,
|
|
2253
|
+
replyInThread: effectiveReplyInThread,
|
|
2254
|
+
allowTopLevelReplyFallback,
|
|
2255
|
+
accountId,
|
|
2256
|
+
...mentions ? { mentions } : {}
|
|
2308
2257
|
})
|
|
2309
2258
|
});
|
|
2310
2259
|
};
|
|
@@ -2482,9 +2431,9 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2482
2431
|
ttsSupplement
|
|
2483
2432
|
}));
|
|
2484
2433
|
const finalTextExceedsStreamingLimit = info?.kind === "final" && hasText && text.length > textChunkLimit;
|
|
2485
|
-
const
|
|
2486
|
-
const
|
|
2487
|
-
const
|
|
2434
|
+
const cardRenderingRequested = renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text);
|
|
2435
|
+
const useStaticCard = hasText && cardRenderingRequested && withinCardTableLimit(text);
|
|
2436
|
+
const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || cardRenderingRequested);
|
|
2488
2437
|
const skipTextForDuplicateFinal = !hasIndependentPresentation && info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
|
|
2489
2438
|
const shouldDeliverText = hasText && (!hasVoiceMedia || hasPresentationFallback) && !skipTextForDuplicateFinal;
|
|
2490
2439
|
const shouldDiscardStreamingPreview = info?.kind === "final" && !(hasIndependentPresentation && payload.isError === true && hasStreamingFinalText) && (hasIndependentPresentation || finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
|
|
@@ -2576,7 +2525,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2576
2525
|
}
|
|
2577
2526
|
return deferStreamingDelivery(mergeFeishuReplyDeliveryResults(deliveredResults, text), info?.kind, ownerGeneration);
|
|
2578
2527
|
}
|
|
2579
|
-
if (
|
|
2528
|
+
if (useStaticCard || useStreamingCard && !isStreamingStartBackedOff(account.accountId) && withinCardTableLimit(text)) {
|
|
2580
2529
|
const cardHeader = resolveCardHeader(agentId, identity);
|
|
2581
2530
|
const cardNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
|
|
2582
2531
|
deliveredResults.push(await sendChunkedTextReply({
|
|
@@ -2703,11 +2652,10 @@ async function resolveFeishuAudioTranscript(params) {
|
|
|
2703
2652
|
/**
|
|
2704
2653
|
* Parse an inbound Feishu event into its caption and routing metadata.
|
|
2705
2654
|
*/
|
|
2706
|
-
function parseFeishuMessageEvent(event, botOpenId, _botName) {
|
|
2707
|
-
const rawContent = parseMessageContent(event.message.content, event.message.message_type);
|
|
2655
|
+
function parseFeishuMessageEvent(event, botOpenId, _botName, preparedContent) {
|
|
2708
2656
|
const mentionedBot = checkBotMentioned(event, botOpenId);
|
|
2709
2657
|
const hasAnyMention = (event.message.mentions?.length ?? 0) > 0;
|
|
2710
|
-
const content = normalizeMentions(
|
|
2658
|
+
const content = preparedContent ?? normalizeMentions(parseMessageContent(event.message.content, event.message.message_type), event.message.mentions, botOpenId);
|
|
2711
2659
|
const senderOpenId = event.sender.sender_id.open_id?.trim();
|
|
2712
2660
|
const senderUserId = event.sender.sender_id.user_id?.trim();
|
|
2713
2661
|
const senderFallbackId = senderOpenId || senderUserId || "";
|
|
@@ -2769,7 +2717,8 @@ async function filterFetchedGroupContextMessages(messages, params) {
|
|
|
2769
2717
|
}) ? message : void 0))).filter((message) => message !== void 0);
|
|
2770
2718
|
}
|
|
2771
2719
|
async function handleFeishuMessage(params) {
|
|
2772
|
-
const {
|
|
2720
|
+
const { event, preparedContent, botOpenId, botName, runtime, channelRuntime, chatHistories, accountId, processingClaim, messageDedupeKey: messageDedupeKeyOverride, turnAdoptionLifecycle } = params;
|
|
2721
|
+
const cfg = getFeishuRuntime().config.current();
|
|
2773
2722
|
const account = resolveFeishuRuntimeAccount({
|
|
2774
2723
|
cfg,
|
|
2775
2724
|
accountId
|
|
@@ -2788,7 +2737,7 @@ async function handleFeishuMessage(params) {
|
|
|
2788
2737
|
log(`feishu: skipping duplicate message ${messageId}`);
|
|
2789
2738
|
return;
|
|
2790
2739
|
}
|
|
2791
|
-
let ctx = parseFeishuMessageEvent(event, botOpenId, botName);
|
|
2740
|
+
let ctx = parseFeishuMessageEvent(event, botOpenId, botName, preparedContent);
|
|
2792
2741
|
const isGroup = isFeishuGroupChatType(ctx.chatType);
|
|
2793
2742
|
const isDirect = !isGroup;
|
|
2794
2743
|
const directPreDispatchTarget = isDirect ? getFeishuSyntheticDirectPreDispatchTarget(event) : void 0;
|
|
@@ -2838,7 +2787,7 @@ async function handleFeishuMessage(params) {
|
|
|
2838
2787
|
log(`feishu[${account.accountId}]: dropping bot message ${ctx.messageId} (local mention not verifiable)`);
|
|
2839
2788
|
return;
|
|
2840
2789
|
}
|
|
2841
|
-
const deliveredCtx = parseFeishuMessageEvent(verifiedEvent, localBotOpenId, botName);
|
|
2790
|
+
const deliveredCtx = parseFeishuMessageEvent(verifiedEvent, localBotOpenId, botName, preparedContent);
|
|
2842
2791
|
ctx = {
|
|
2843
2792
|
...deliveredCtx,
|
|
2844
2793
|
mentionedBot: true,
|
|
@@ -2850,22 +2799,17 @@ async function handleFeishuMessage(params) {
|
|
|
2850
2799
|
if (event.message.message_type === "merge_forward") {
|
|
2851
2800
|
log(`feishu[${account.accountId}]: processing merge_forward message, fetching full content via API`);
|
|
2852
2801
|
try {
|
|
2853
|
-
const
|
|
2854
|
-
|
|
2855
|
-
|
|
2802
|
+
const messageInfo = await getMessageFeishu({
|
|
2803
|
+
cfg,
|
|
2804
|
+
messageId: event.message.message_id,
|
|
2805
|
+
accountId: account.accountId
|
|
2856
2806
|
});
|
|
2857
|
-
if (
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
});
|
|
2863
|
-
ctx = {
|
|
2864
|
-
...ctx,
|
|
2865
|
-
content: expandedContent
|
|
2866
|
-
};
|
|
2867
|
-
} else {
|
|
2868
|
-
log(`feishu[${account.accountId}]: merge_forward API returned no items`);
|
|
2807
|
+
if (messageInfo) ctx = {
|
|
2808
|
+
...ctx,
|
|
2809
|
+
content: messageInfo.content
|
|
2810
|
+
};
|
|
2811
|
+
else {
|
|
2812
|
+
log(`feishu[${account.accountId}]: merge_forward message retrieval returned no result`);
|
|
2869
2813
|
ctx = {
|
|
2870
2814
|
...ctx,
|
|
2871
2815
|
content: "[Merged and Forwarded Message - could not fetch]"
|
|
@@ -5597,7 +5541,7 @@ async function resolveDriveCommentEventCore(params) {
|
|
|
5597
5541
|
return null;
|
|
5598
5542
|
}
|
|
5599
5543
|
const context = await fetchDriveCommentContext({
|
|
5600
|
-
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-
|
|
5544
|
+
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-cCMNFBKg.js").then((n) => n.t)).resolveFeishuAccount({
|
|
5601
5545
|
cfg,
|
|
5602
5546
|
accountId
|
|
5603
5547
|
})),
|
|
@@ -6129,8 +6073,9 @@ function resolveFeishuDebounceMentions(params) {
|
|
|
6129
6073
|
return botMentions.length > 0 ? botMentions : void 0;
|
|
6130
6074
|
}
|
|
6131
6075
|
function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, runtime, chatHistories, fireAndForget, handleMessage, resolveDebounceText: resolveText, hasProcessedMessage, getBotOpenId = () => void 0, getBotName = () => void 0, resolveSequentialKey = ({ accountId: accountIdLocal, event }) => `feishu:${accountIdLocal}:${event.message.chat_id?.trim() || "unknown"}`, statusSink, resolveIngressLifecycle }) {
|
|
6132
|
-
const
|
|
6133
|
-
|
|
6076
|
+
const readConfig = createRuntimeConfigReader(cfg);
|
|
6077
|
+
const resolveDebounceMs = () => channelRuntime.debounce.resolveInboundDebounceMs({
|
|
6078
|
+
cfg: readConfig(),
|
|
6134
6079
|
channel: "feishu"
|
|
6135
6080
|
});
|
|
6136
6081
|
const log = runtime?.log ?? console.log;
|
|
@@ -6138,10 +6083,11 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
|
|
|
6138
6083
|
const enqueue = createSequentialQueue({ onTaskTimeout: (key, timeoutMs) => {
|
|
6139
6084
|
log(`feishu[${accountId}]: per-chat task exceeded ${timeoutMs}ms cap (key=${key}); evicting from queue so later same-key messages can proceed (#70133)`);
|
|
6140
6085
|
} });
|
|
6141
|
-
const dispatchFeishuMessage = async (event, messageDedupeKey, processingClaim, turnAdoptionLifecycle) => {
|
|
6086
|
+
const dispatchFeishuMessage = async (event, messageDedupeKey, processingClaim, turnAdoptionLifecycle, preparedContent) => {
|
|
6142
6087
|
const sequentialKey = resolveSequentialKey({
|
|
6143
6088
|
accountId,
|
|
6144
6089
|
event,
|
|
6090
|
+
preparedContent,
|
|
6145
6091
|
botOpenId: getBotOpenId(accountId),
|
|
6146
6092
|
botName: getBotName(accountId)
|
|
6147
6093
|
});
|
|
@@ -6153,6 +6099,7 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
|
|
|
6153
6099
|
await handleMessage({
|
|
6154
6100
|
cfg,
|
|
6155
6101
|
event,
|
|
6102
|
+
preparedContent,
|
|
6156
6103
|
botOpenId: getBotOpenId(accountId),
|
|
6157
6104
|
botName: getBotName(accountId),
|
|
6158
6105
|
runtime,
|
|
@@ -6189,7 +6136,8 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
|
|
|
6189
6136
|
}
|
|
6190
6137
|
};
|
|
6191
6138
|
const inboundDebouncer = channelRuntime.debounce.createInboundDebouncer({
|
|
6192
|
-
debounceMs:
|
|
6139
|
+
debounceMs: resolveDebounceMs(),
|
|
6140
|
+
resolveDebounceMs,
|
|
6193
6141
|
buildKey: ({ event }) => {
|
|
6194
6142
|
const chatId = event.message.chat_id?.trim();
|
|
6195
6143
|
const senderId = resolveSenderDebounceId(event);
|
|
@@ -6242,13 +6190,9 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
|
|
|
6242
6190
|
...dispatchEntry.event,
|
|
6243
6191
|
message: {
|
|
6244
6192
|
...dispatchEntry.event.message,
|
|
6245
|
-
...combinedText.trim() ? {
|
|
6246
|
-
message_type: "text",
|
|
6247
|
-
content: JSON.stringify({ text: combinedText })
|
|
6248
|
-
} : {},
|
|
6249
6193
|
mentions: mergedMentions ?? dispatchEntry.event.message.mentions
|
|
6250
6194
|
}
|
|
6251
|
-
}, dispatchDedupeKey, dispatchEntry.processingClaim, admissionLifecycle);
|
|
6195
|
+
}, dispatchDedupeKey, dispatchEntry.processingClaim, admissionLifecycle, combinedText);
|
|
6252
6196
|
await settle();
|
|
6253
6197
|
} catch (err) {
|
|
6254
6198
|
await admissionLifecycle.onAbandoned();
|
|
@@ -6819,9 +6763,9 @@ function createFeishuVcMeetingInvitedHandler(params) {
|
|
|
6819
6763
|
//#endregion
|
|
6820
6764
|
//#region extensions/feishu/src/sequential-key.ts
|
|
6821
6765
|
function getFeishuSequentialKey(params) {
|
|
6822
|
-
const { accountId, event, botOpenId, botName } = params;
|
|
6766
|
+
const { accountId, event, botOpenId, botName, preparedContent } = params;
|
|
6823
6767
|
const baseKey = `feishu:${accountId}:${event.message.chat_id?.trim() || "unknown"}`;
|
|
6824
|
-
const text = parseFeishuMessageEvent(event, botOpenId, botName).content.trim();
|
|
6768
|
+
const text = (preparedContent ?? parseFeishuMessageEvent(event, botOpenId, botName).content).trim();
|
|
6825
6769
|
if (isAbortRequestText(text)) return `${baseKey}:control`;
|
|
6826
6770
|
if (isBtwRequestText(text)) return `${baseKey}:btw`;
|
|
6827
6771
|
return baseKey;
|
|
@@ -6837,7 +6781,7 @@ async function resolveReactionSyntheticEvent(params) {
|
|
|
6837
6781
|
const senderUserId = event.user_id?.user_id?.trim();
|
|
6838
6782
|
const senderId = senderOpenId || senderUserId;
|
|
6839
6783
|
if (!emoji || !messageId || !senderId) return null;
|
|
6840
|
-
const { resolveFeishuAccount } = await import("./accounts-
|
|
6784
|
+
const { resolveFeishuAccount } = await import("./accounts-cCMNFBKg.js").then((n) => n.t);
|
|
6841
6785
|
const reactionNotifications = resolveFeishuAccount({
|
|
6842
6786
|
cfg,
|
|
6843
6787
|
accountId
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
1
|
+
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
2
|
import { i as resolveReceiveIdType, r as normalizeFeishuTarget, t as detectIdType } from "./targets-BTQCYRZQ.js";
|
|
3
|
-
import { o as normalizeFeishuExternalKey } from "./doctor-contract-
|
|
3
|
+
import { o as normalizeFeishuExternalKey } from "./doctor-contract-w6FpMypv.js";
|
|
4
4
|
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
5
5
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
6
6
|
import { normalizeAccountId, normalizeOptionalAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
@@ -11,7 +11,7 @@ import { PlatformMessageNotDispatchedError, formatErrorMessage } from "openclaw/
|
|
|
11
11
|
import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, renderPresentationForDelivery, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
|
|
12
12
|
import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
13
13
|
import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
14
|
-
import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
|
|
14
|
+
import { convertMarkdownTables, markdownToIRWithMeta } from "openclaw/plugin-sdk/text-chunking";
|
|
15
15
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
16
16
|
import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
17
17
|
import fs from "node:fs";
|
|
@@ -20,7 +20,7 @@ import { fromMarkdown } from "mdast-util-from-markdown";
|
|
|
20
20
|
import { gfmTableFromMarkdown } from "mdast-util-gfm-table";
|
|
21
21
|
import { gfmTable } from "micromark-extension-gfm-table";
|
|
22
22
|
import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
|
|
23
|
-
import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
|
|
23
|
+
import { createAcceptedChannelDeliveryResult, createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
|
|
24
24
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
25
25
|
import { readRegularFile, wrapExternalContent, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
26
26
|
import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
|
|
@@ -34,6 +34,7 @@ import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, buildOutboundMediaLoadOptions, ru
|
|
|
34
34
|
import { saveMediaBuffer, saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
35
35
|
import { resolvePreferredOpenClawTmpDir, withTempDownloadPath, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
36
36
|
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
37
|
+
import { escapeHtml, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
37
38
|
//#region extensions/feishu/src/card-interaction.ts
|
|
38
39
|
const FEISHU_CARD_INTERACTION_VERSION = "ocf1";
|
|
39
40
|
function isInteractionKind(value) {
|
|
@@ -2282,6 +2283,57 @@ function buildMentionedCardContent(targets, message) {
|
|
|
2282
2283
|
return `${targets.map((t) => formatMentionForCard(t)).join(" ")} ${message}`;
|
|
2283
2284
|
}
|
|
2284
2285
|
//#endregion
|
|
2286
|
+
//#region extensions/feishu/src/message-content.ts
|
|
2287
|
+
function formatFeishuMediaContent(parsed, messageType) {
|
|
2288
|
+
if (messageType === "sticker") {
|
|
2289
|
+
const fileKey = normalizeFeishuExternalKey(parsed?.file_key);
|
|
2290
|
+
return fileKey ? `<sticker key="${escapeHtml(fileKey)}"/>` : "[Sticker]";
|
|
2291
|
+
}
|
|
2292
|
+
const speechToText = messageType === "audio" && typeof parsed.speech_to_text === "string" ? parsed.speech_to_text.trim() : "";
|
|
2293
|
+
if (speechToText) return speechToText;
|
|
2294
|
+
return "";
|
|
2295
|
+
}
|
|
2296
|
+
function formatSubMessageContent(content, contentType) {
|
|
2297
|
+
try {
|
|
2298
|
+
const parsed = JSON.parse(content);
|
|
2299
|
+
switch (contentType) {
|
|
2300
|
+
case "text": return parsed.text || content;
|
|
2301
|
+
case "post": return parsePostContent(content).textContent;
|
|
2302
|
+
case "interactive": return parseInteractiveCardContent(parsed);
|
|
2303
|
+
case "image": return "[Image]";
|
|
2304
|
+
case "file": return `[File: ${parsed.file_name || "unknown"}]`;
|
|
2305
|
+
case "audio": return "[Audio]";
|
|
2306
|
+
case "video": return "[Video]";
|
|
2307
|
+
case "sticker": return formatFeishuMediaContent(parsed, contentType);
|
|
2308
|
+
case "merge_forward": return "[Nested Merged Forward]";
|
|
2309
|
+
default: return `[${contentType}]`;
|
|
2310
|
+
}
|
|
2311
|
+
} catch {
|
|
2312
|
+
return content;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
function parseMergeForwardContent(params) {
|
|
2316
|
+
const { content } = params;
|
|
2317
|
+
const maxMessages = 50;
|
|
2318
|
+
let items;
|
|
2319
|
+
try {
|
|
2320
|
+
items = JSON.parse(content);
|
|
2321
|
+
} catch {
|
|
2322
|
+
return "[Merged and Forwarded Message - parse error]";
|
|
2323
|
+
}
|
|
2324
|
+
if (!Array.isArray(items) || items.length === 0) return "[Merged and Forwarded Message - no sub-messages]";
|
|
2325
|
+
const container = items.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id);
|
|
2326
|
+
const subMessages = container ? items.filter((item) => item !== container) : items.filter((item) => item.upper_message_id);
|
|
2327
|
+
if (subMessages.length === 0) return "[Merged and Forwarded Message - no sub-messages found]";
|
|
2328
|
+
subMessages.sort((a, b) => (parseStrictNonNegativeInteger(a.create_time) ?? 0) - (parseStrictNonNegativeInteger(b.create_time) ?? 0));
|
|
2329
|
+
const lines = ["[Merged and Forwarded Messages]"];
|
|
2330
|
+
for (const item of subMessages.slice(0, maxMessages)) lines.push(`- ${formatSubMessageContent(item.body?.content || "", item.msg_type || "text")}`);
|
|
2331
|
+
if (subMessages.length > maxMessages) lines.push(`... and ${subMessages.length - maxMessages} more messages`);
|
|
2332
|
+
const rendered = lines.join("\n");
|
|
2333
|
+
return rendered.length <= 2e4 ? rendered : `${truncateUtf16Safe(rendered, 19961).trimEnd()}
|
|
2334
|
+
... [Merged-forward content truncated]`;
|
|
2335
|
+
}
|
|
2336
|
+
//#endregion
|
|
2285
2337
|
//#region extensions/feishu/src/send.ts
|
|
2286
2338
|
const WITHDRAWN_REPLY_ERROR_CODES = /* @__PURE__ */ new Set([230011, 231003]);
|
|
2287
2339
|
function shouldFallbackFromReplyTarget(response) {
|
|
@@ -2396,10 +2448,16 @@ async function getMessageFeishu(params) {
|
|
|
2396
2448
|
path: { message_id: messageId }
|
|
2397
2449
|
});
|
|
2398
2450
|
if (response.code !== 0) return null;
|
|
2399
|
-
const
|
|
2451
|
+
const responseItems = response.data?.items;
|
|
2452
|
+
const rawItem = responseItems?.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id) ?? responseItems?.[0] ?? response.data;
|
|
2400
2453
|
const item = rawItem && (rawItem.body !== void 0 || rawItem.message_id !== void 0) ? rawItem : null;
|
|
2401
2454
|
if (!item) return null;
|
|
2402
|
-
|
|
2455
|
+
const parsedItem = parseFeishuMessageItem(item, messageId);
|
|
2456
|
+
if (parsedItem.contentType === "merge_forward" && responseItems) return {
|
|
2457
|
+
...parsedItem,
|
|
2458
|
+
content: parseMergeForwardContent({ content: JSON.stringify(responseItems) })
|
|
2459
|
+
};
|
|
2460
|
+
return parsedItem;
|
|
2403
2461
|
} catch {
|
|
2404
2462
|
return null;
|
|
2405
2463
|
}
|
|
@@ -3292,6 +3350,28 @@ function isFeishuCardWithinEnvelope(card) {
|
|
|
3292
3350
|
function assertFeishuCardWithinEnvelope(card, label = "Feishu card") {
|
|
3293
3351
|
if (!isFeishuCardWithinEnvelope(card)) throw new Error(`${label} exceeds the 30 KB or 200-element API limit.`);
|
|
3294
3352
|
}
|
|
3353
|
+
/** Feishu allows at most five table components per static interactive card. */
|
|
3354
|
+
const FEISHU_CARD_TABLE_LIMIT = 5;
|
|
3355
|
+
function countMarkdownTables(text) {
|
|
3356
|
+
return text ? markdownToIRWithMeta(text, { tableMode: "block" }).tables.length : 0;
|
|
3357
|
+
}
|
|
3358
|
+
function withinCardTableLimit(text) {
|
|
3359
|
+
return countMarkdownTables(text) <= FEISHU_CARD_TABLE_LIMIT;
|
|
3360
|
+
}
|
|
3361
|
+
function collectFeishuCardMarkdownTexts(value, output) {
|
|
3362
|
+
if (Array.isArray(value)) {
|
|
3363
|
+
for (const item of value) collectFeishuCardMarkdownTexts(item, output);
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
if (!isRecord(value)) return;
|
|
3367
|
+
if (value.tag === "markdown" && typeof value.content === "string") output.push(value.content);
|
|
3368
|
+
for (const child of Object.values(value)) collectFeishuCardMarkdownTexts(child, output);
|
|
3369
|
+
}
|
|
3370
|
+
function feishuCardWithinTableLimit(card) {
|
|
3371
|
+
const markdownTexts = [];
|
|
3372
|
+
collectFeishuCardMarkdownTexts(card, markdownTexts);
|
|
3373
|
+
return markdownTexts.reduce((total, text) => total + countMarkdownTables(text), 0) <= FEISHU_CARD_TABLE_LIMIT;
|
|
3374
|
+
}
|
|
3295
3375
|
function resolveFeishuButtonUrl(button) {
|
|
3296
3376
|
if (button.action?.type === "url" || button.action?.type === "web-app") return button.action.url;
|
|
3297
3377
|
if (button.action) return;
|
|
@@ -3477,8 +3557,11 @@ function buildFeishuPayloadCard(params) {
|
|
|
3477
3557
|
}, ...card.body.elements]
|
|
3478
3558
|
}
|
|
3479
3559
|
};
|
|
3480
|
-
if (isNativeCard)
|
|
3481
|
-
|
|
3560
|
+
if (isNativeCard) {
|
|
3561
|
+
assertFeishuCardWithinEnvelope(card, "Feishu native card");
|
|
3562
|
+
return markRenderedFeishuCard(card);
|
|
3563
|
+
}
|
|
3564
|
+
return isFeishuCardWithinEnvelope(card) && feishuCardWithinTableLimit(card) ? markRenderedFeishuCard(card) : void 0;
|
|
3482
3565
|
}
|
|
3483
3566
|
function renderFeishuPresentationPayload({ payload, presentation, sourcePresentation, ctx }) {
|
|
3484
3567
|
const card = buildFeishuPayloadCard({
|
|
@@ -3549,20 +3632,15 @@ function hasProviderIdentity(result) {
|
|
|
3549
3632
|
/** Normalizes every physical Lark send behind one logical reply payload. */
|
|
3550
3633
|
function createFeishuReplyDeliveryResult(params) {
|
|
3551
3634
|
const results = params.visibleReplySent ? (params.results ?? []).filter(hasProviderIdentity) : [];
|
|
3552
|
-
|
|
3553
|
-
results,
|
|
3554
|
-
...params.kind ? { kind: params.kind } : {}
|
|
3555
|
-
}) : void 0;
|
|
3556
|
-
if (!receipt) return {
|
|
3557
|
-
visibleReplySent: params.visibleReplySent,
|
|
3558
|
-
...params.content === void 0 ? {} : { content: params.content }
|
|
3559
|
-
};
|
|
3560
|
-
return {
|
|
3561
|
-
messageIds: [...receipt.platformMessageIds],
|
|
3562
|
-
receipt,
|
|
3635
|
+
if (results.length === 0) return {
|
|
3563
3636
|
visibleReplySent: params.visibleReplySent,
|
|
3564
3637
|
...params.content === void 0 ? {} : { content: params.content }
|
|
3565
3638
|
};
|
|
3639
|
+
return createAcceptedChannelDeliveryResult({
|
|
3640
|
+
results,
|
|
3641
|
+
kind: params.kind,
|
|
3642
|
+
content: params.content
|
|
3643
|
+
});
|
|
3566
3644
|
}
|
|
3567
3645
|
/** Preserves the first result's provider identity while retaining supplemental ids. */
|
|
3568
3646
|
function mergeFeishuReplyDeliveryResults(results, content) {
|
|
@@ -3583,4 +3661,4 @@ function createFeishuPartialReplyDeliveryError(cause, result) {
|
|
|
3583
3661
|
});
|
|
3584
3662
|
}
|
|
3585
3663
|
//#endregion
|
|
3586
|
-
export {
|
|
3664
|
+
export { encodeQuery as $, sendStructuredCardFeishu as A, FEISHU_CARD_INTERACTION_VERSION as At, buildFeishuMediaFallbackText as B, shouldSuppressFeishuTextForVoiceMedia as C, resolveFeishuGroupConfig as Ct, listFeishuThreadMessages as D, resolveFeishuReplyPolicy as Dt, getMessageFeishu as E, resolveFeishuGroupToolPolicy as Et, isFeishuGroupChatType as F, feishuExternalToolResult as G, resolveFeishuIdentityHeaderTitle as H, parseInteractiveCardContent as I, createFeishuToolClient as J, toolExecutionErrorResult as K, parsePostContent as L, extractMentionTargets as M, createFeishuCardInteractionEnvelope as Mt, isFeishuBroadcastMention as N, decodeFeishuCardAction as Nt, sendCardFeishu as O, normalizeFeishuChatType as Ot, isMentionForwardRequest as P, createCommentTypingReactionLifecycle as Q, createFeishuSendReceipt as R, sendStickerFeishu as S, resolveFeishuDmIngressAccess as St, editMessageFeishu as T, resolveFeishuGroupSenderActivationIngressAccess as Tt, deliverCommentThreadText as U, resolveFeishuIdentityEmoji as V, registerFeishuDriveTools as W, resolveFeishuToolAccount as X, resolveAnyEnabledFeishuToolsConfig as Y, cleanupAmbientCommentTypingReaction as Z, renderFeishuReplyPayload as _, isFeishuGroupReadAllowed as _t, FEISHU_PRESENTATION_CAPABILITIES as a, normalizeCommentFileType as at, saveMessageResourceFeishu as b, hasExplicitFeishuGroupConfig as bt, buildFeishuPresentationCard as c, resolveFeishuCardTemplate as ct, feishuCardWithinTableLimit as d, materializeFeishuPostMarkdownSoftBreaks as dt, extractReplyText as et, isFeishuCardWithinEnvelope as f, parseFeishuMarkdown as ft, renderFeishuPresentationPayload as g, canEnumerateAllFeishuPeers as gt, renderFeishuPresentationFallbackText as h, canEnumerateAllFeishuGroups as ht, noVisibleFeishuReplyDelivery as i, buildFeishuCommentTarget as it, formatFeishuMediaContent as j, buildFeishuCardActionTextFallback as jt, sendMessageFeishu as k, resolveFeishuChatType as kt, buildFeishuPresentationFallback as l, chunkFeishuMarkdown as lt, readNativeFeishuCard as m, authorizeFeishuChatMemberRead as mt, createFeishuReplyDeliveryResult as n, parseCommentContentElements as nt, assertFeishuCardWithinEnvelope as o, parseFeishuCommentTarget as ot, markRenderedFeishuCard as p, assertFeishuChatReadAllowed as pt, unknownToolActionResult as q, mergeFeishuReplyDeliveryResults as r, requestFeishuApi as rt, buildFeishuPayloadCard as s, readNativeFeishuCardJson as st, createFeishuPartialReplyDeliveryError as t, formatFeishuApiError as tt, consumeFeishuPresentationFallbackMarker as u, chunkFeishuPostMarkdown as ut, resolveFeishuRichReply as v, isFeishuGroupReadEnabled as vt, chunkFeishuCardMarkdown as w, resolveFeishuGroupConversationIngressAccess as wt, sendMediaFeishu as x, normalizeFeishuAllowEntry as xt, withinCardTableLimit as y, resolveFeishuChatReadPreliminaryAuthorization as yt, assertFeishuApiSuccess as z };
|
package/dist/setup-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-
|
|
1
|
+
import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DrAqd5ED.js";
|
|
2
2
|
export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a json pointer
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsJsonPointer(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const JsonPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a json pointer
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsJsonPointer(value) {
|
|
8
8
|
return JsonPointer.test(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a json pointer uri fragment
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsJsonPointerUriFragment(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const JsonPointerUriFragment = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a json pointer uri fragment
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsJsonPointerUriFragment(value) {
|
|
8
8
|
return JsonPointerUriFragment.test(value);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a relative json pointer
|
|
3
|
-
* @specification
|
|
4
|
-
* @source ajv-formats
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
4
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
5
5
|
*/
|
|
6
6
|
export declare function IsRelativeJsonPointer(value: string): boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const RelativeJsonPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a relative json pointer
|
|
4
|
-
* @specification
|
|
5
|
-
* @source ajv-formats
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc6901
|
|
5
|
+
* @source https://github.com/ajv-validator/ajv-formats
|
|
6
6
|
*/
|
|
7
7
|
export function IsRelativeJsonPointer(value) {
|
|
8
8
|
return RelativeJsonPointer.test(value);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// deno-lint-ignore-file ban-types no-explicit-any
|
|
2
2
|
// deno-fmt-ignore-file
|
|
3
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
4
5
|
/**
|
|
5
6
|
* Performs an Object assign using the Left and Right object types. We track this operation as it
|
|
6
7
|
* creates a new GC handle per assignment.
|
|
7
8
|
*/
|
|
8
9
|
export function Assign(left, right) {
|
|
9
10
|
Metrics.assign += 1;
|
|
10
|
-
return { ...left, ...right };
|
|
11
|
+
return Freeze({ ...left, ...right });
|
|
11
12
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
|
-
// deno-fmt-ignore-file
|
|
3
2
|
import { Settings } from '../settings/index.mjs';
|
|
4
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
5
5
|
function MergeHidden(left, right) {
|
|
6
6
|
for (const key of Object.keys(right)) {
|
|
7
7
|
Object.defineProperty(left, key, {
|
|
@@ -23,8 +23,7 @@ function Merge(left, right) {
|
|
|
23
23
|
*/
|
|
24
24
|
export function Create(hidden, enumerable, options = {}) {
|
|
25
25
|
Metrics.create += 1;
|
|
26
|
-
const settings = Settings.Get();
|
|
27
26
|
const withOptions = Merge(enumerable, options);
|
|
28
|
-
const withHidden =
|
|
29
|
-
return
|
|
27
|
+
const withHidden = Settings.Get().enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
28
|
+
return Freeze(withHidden);
|
|
30
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
|
-
// deno-fmt-ignore-file
|
|
3
2
|
import { Guard } from '../../guard/index.mjs';
|
|
4
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
5
5
|
import { Clone } from './clone.mjs';
|
|
6
6
|
/** Discards multiple property keys from the given object value */
|
|
7
7
|
export function Discard(value, propertyKeys) {
|
|
@@ -14,5 +14,5 @@ export function Discard(value, propertyKeys) {
|
|
|
14
14
|
descriptor.value = Clone(descriptor.value);
|
|
15
15
|
Object.defineProperty(result, key, descriptor);
|
|
16
16
|
}
|
|
17
|
-
return result;
|
|
17
|
+
return Freeze(result);
|
|
18
18
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
|
+
import { Settings } from '../settings/index.mjs';
|
|
3
|
+
/** Conditionally freezes the value if `immutableTypes` is true, otherwise no action. */
|
|
4
|
+
export function Freeze(value) {
|
|
5
|
+
return Settings.Get().immutableTypes ? Object.freeze(value) : value;
|
|
6
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// deno-
|
|
1
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2
2
|
import { Settings } from '../settings/index.mjs';
|
|
3
3
|
import { Metrics } from './metrics.mjs';
|
|
4
|
+
import { Freeze } from './freeze.mjs';
|
|
4
5
|
import { Clone } from './clone.mjs';
|
|
5
6
|
/**
|
|
6
7
|
* Updates a value with new properties while preserving property enumerability. Use this function to modify
|
|
@@ -28,5 +29,5 @@ export function Update(current, hidden, enumerable) {
|
|
|
28
29
|
value: enumerable[key]
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
|
-
return result;
|
|
32
|
+
return Freeze(result);
|
|
32
33
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -661,7 +661,7 @@
|
|
|
661
661
|
{
|
|
662
662
|
"type": "string",
|
|
663
663
|
"format": "uri",
|
|
664
|
-
"pattern": "^
|
|
664
|
+
"pattern": "^[Hh][Tt][Tt][Pp][Ss]:\\/\\/"
|
|
665
665
|
}
|
|
666
666
|
]
|
|
667
667
|
},
|
|
@@ -1531,7 +1531,7 @@
|
|
|
1531
1531
|
{
|
|
1532
1532
|
"type": "string",
|
|
1533
1533
|
"format": "uri",
|
|
1534
|
-
"pattern": "^
|
|
1534
|
+
"pattern": "^[Hh][Tt][Tt][Pp][Ss]:\\/\\/"
|
|
1535
1535
|
}
|
|
1536
1536
|
]
|
|
1537
1537
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/feishu",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.3",
|
|
4
4
|
"description": "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"mdast-util-from-markdown": "2.0.3",
|
|
13
13
|
"mdast-util-gfm-table": "2.0.0",
|
|
14
14
|
"micromark-extension-gfm-table": "2.1.1",
|
|
15
|
-
"typebox": "1.3.
|
|
15
|
+
"typebox": "1.3.18",
|
|
16
16
|
"zod": "4.4.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.9.
|
|
19
|
+
"openclaw": ">=2026.9.3"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -62,11 +62,11 @@
|
|
|
62
62
|
"minHostVersion": ">=2026.5.29"
|
|
63
63
|
},
|
|
64
64
|
"compat": {
|
|
65
|
-
"pluginApi": ">=2026.9.
|
|
65
|
+
"pluginApi": ">=2026.9.3"
|
|
66
66
|
},
|
|
67
67
|
"build": {
|
|
68
68
|
"bundledDist": false,
|
|
69
|
-
"openclawVersion": "2026.9.
|
|
69
|
+
"openclawVersion": "2026.9.3"
|
|
70
70
|
},
|
|
71
71
|
"release": {
|
|
72
72
|
"publishToClawHub": true,
|