@openclaw/feishu 2026.9.1-beta.1 → 2026.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accounts-RwSyseKr.js → accounts-cCMNFBKg.js} +1 -1
- package/dist/api.js +222 -284
- package/dist/{app-registration-BpfUzS9m.js → app-registration-BI99e_JI.js} +12 -9
- package/dist/{channel-Dpgf8wLD.js → channel-VytDDz_B.js} +244 -582
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BIEERwaT.js → channel.runtime-CAD74kBO.js} +15 -19
- package/dist/{chat-C1nuW5UY.js → chat-DDhIcPQy.js} +13 -13
- package/dist/{client-DalLmpMf.js → client-DMbIL3UH.js} +3 -6
- package/dist/contract-api.js +2 -2
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-w6FpMypv.js +465 -0
- package/dist/index.js +11 -42
- package/dist/{json-response-CheljwGr.js → json-response-DXajw_FU.js} +1 -1
- package/dist/{monitor.startup-D-gL82OO.js → monitor-rPJ_Mz4M.js} +62 -2
- package/dist/{monitor.account-DhhotI_Q.js → monitor.account-v0XIDyfs.js} +270 -299
- package/dist/{probe-CfhhR_0a.js → probe-D_SatdZl.js} +3 -3
- package/dist/{presentation-card-C8Z7Tgru.js → reply-delivery-result-CYaKkbUN.js} +334 -240
- package/dist/{security-audit-D6Fz2h6p.js → security-audit-DKfR4Cv3.js} +1 -1
- package/dist/{security-audit-shared-BgpY7AiJ.js → security-audit-shared-CK5rVR-1.js} +2 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{subagent-hooks-KUi-7vUy.js → subagent-hooks-Buadpie_.js} +3 -6
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{targets-BUjQ1TcA.js → targets-BTQCYRZQ.js} +4 -4
- package/dist/{thread-bindings-CptM_YU6.js → thread-bindings-BiL1wGqK.js} +20 -9
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/package.json +1 -1
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -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 +45 -238
- package/package.json +6 -5
- package/dist/doctor-contract-bEQXIXyP.js +0 -158
- package/dist/monitor-BRg3sHH7.js +0 -62
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
2
|
-
import { i as resolveReceiveIdType } from "./targets-
|
|
3
|
-
import { $ as
|
|
4
|
-
import {
|
|
1
|
+
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
|
+
import { i as resolveReceiveIdType } from "./targets-BTQCYRZQ.js";
|
|
3
|
+
import { $ as parseCommentContentElements, A as isFeishuBroadcastMention, At as decodeFeishuCardAction, B as deliverCommentThreadText, D as sendMessageFeishu, E as sendCardFeishu, Et as resolveFeishuChatType, L as buildFeishuMediaFallbackText, M as isFeishuGroupChatType, N as parseInteractiveCardContent, O as sendStructuredCardFeishu, Ot as buildFeishuCardActionTextFallback, P as parsePostContent, R as resolveFeishuIdentityEmoji, S as chunkFeishuCardMarkdown, St as resolveFeishuGroupSenderActivationIngressAccess, T as listFeishuThreadMessages, Tt as normalizeFeishuChatType$2, X as encodeQuery, Y as createCommentTypingReactionLifecycle, Z as extractReplyText, _ as resolveFeishuRichReply, _t as hasExplicitFeishuGroupConfig, at as resolveFeishuCardTemplate, bt as resolveFeishuGroupConfig, ct as materializeFeishuPostMarkdownSoftBreaks, et as requestFeishuApi, g as renderFeishuReplyPayload, i as noVisibleFeishuReplyDelivery, j as isMentionForwardRequest, k as extractMentionTargets, kt as createFeishuCardInteractionEnvelope, l as buildFeishuPresentationFallback, n as createFeishuReplyDeliveryResult, nt as normalizeCommentFileType, r as mergeFeishuReplyDeliveryResults, st as chunkFeishuPostMarkdown, t as createFeishuPartialReplyDeliveryError, tt as buildFeishuCommentTarget, u as consumeFeishuPresentationFallbackMarker, v as saveMessageResourceFeishu, vt as normalizeFeishuAllowEntry, w as getMessageFeishu, wt as resolveFeishuReplyPolicy, x as shouldSuppressFeishuTextForVoiceMedia, xt as resolveFeishuGroupConversationIngressAccess, y as sendMediaFeishu, yt as resolveFeishuDmIngressAccess } from "./reply-delivery-result-CYaKkbUN.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
|
-
import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient, s as resolveConfiguredHttpTimeoutMs } from "./client-
|
|
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-
|
|
9
|
-
import { t as createFeishuThreadBindingManager } from "./thread-bindings-
|
|
8
|
+
import { r as getChatInfo } from "./chat-DDhIcPQy.js";
|
|
9
|
+
import { t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
|
|
10
10
|
import { evaluateSupplementalContextVisibility, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode } from "./runtime-api.js";
|
|
11
|
-
import { t as readFeishuJsonResponse } from "./json-response-
|
|
12
|
-
import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-
|
|
13
|
-
import { t as fetchBotIdentityForMonitor } from "./monitor
|
|
11
|
+
import { t as readFeishuJsonResponse } from "./json-response-DXajw_FU.js";
|
|
12
|
+
import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-D_SatdZl.js";
|
|
13
|
+
import { t as fetchBotIdentityForMonitor } from "./monitor-rPJ_Mz4M.js";
|
|
14
14
|
import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
15
|
-
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline,
|
|
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";
|
|
17
17
|
import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
|
|
18
18
|
import { collectErrorGraphCandidates, formatErrorMessage, toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
|
@@ -26,7 +26,7 @@ import os from "node:os";
|
|
|
26
26
|
import path from "node:path";
|
|
27
27
|
import { normalizeAccountId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
28
28
|
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
29
|
-
import {
|
|
29
|
+
import { formatAgentEnvelope, formatInboundMediaUnavailableText, isChannelPartialDeliveryError, recordChannelBotPairLoopAndCheckSuppression, resolveEnvelopeFormatOptions, resolveInboundReplyDispatchCounts, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
|
|
30
30
|
import { getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
31
31
|
import { resolvePinnedMainDmOwnerFromAllowlist, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
32
32
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
@@ -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";
|
|
@@ -53,7 +54,7 @@ function formatMentionNameForAgentContext(name) {
|
|
|
53
54
|
const normalized = Array.from(name, (char) => {
|
|
54
55
|
return char.charCodeAt(0) < 32 || char === "[" || char === "]" ? " " : char;
|
|
55
56
|
}).join("").replace(/\s+/g, " ").trim();
|
|
56
|
-
const bounded = normalized.length > MAX_MENTION_CONTEXT_NAME_LENGTH ? `${truncateUtf16Safe(normalized,
|
|
57
|
+
const bounded = normalized.length > MAX_MENTION_CONTEXT_NAME_LENGTH ? `${truncateUtf16Safe(normalized, 77)}...` : normalized;
|
|
57
58
|
return JSON.stringify(bounded || "unknown");
|
|
58
59
|
}
|
|
59
60
|
function buildFeishuAgentBody(params) {
|
|
@@ -196,6 +197,7 @@ function createFeishuBroadcastIngressSettlement(params) {
|
|
|
196
197
|
lane.status = "deferred";
|
|
197
198
|
defer();
|
|
198
199
|
},
|
|
200
|
+
onDeferredHeartbeat: () => params.lifecycle?.onDeferredHeartbeat?.(),
|
|
199
201
|
onAdoptionFinalizing: beginFinalizing,
|
|
200
202
|
onAbandoned: async () => {
|
|
201
203
|
if (lane.status === "completed" || lane.status === "failed" || lane.status === "abandoned") return;
|
|
@@ -289,9 +291,7 @@ function resolveFeishuGroupSession(params) {
|
|
|
289
291
|
senderOpenId
|
|
290
292
|
});
|
|
291
293
|
break;
|
|
292
|
-
default:
|
|
293
|
-
peerId = chatId;
|
|
294
|
-
break;
|
|
294
|
+
default: peerId = chatId;
|
|
295
295
|
}
|
|
296
296
|
return {
|
|
297
297
|
peerId,
|
|
@@ -540,7 +540,7 @@ async function resolveFeishuMediaList(params) {
|
|
|
540
540
|
const feishuGroupNameCache = /* @__PURE__ */ new Map();
|
|
541
541
|
//#endregion
|
|
542
542
|
//#region extensions/feishu/src/bot-group-name.ts
|
|
543
|
-
const GROUP_NAME_CACHE_TTL_MS =
|
|
543
|
+
const GROUP_NAME_CACHE_TTL_MS = 18e5;
|
|
544
544
|
const GROUP_NAME_CACHE_MAX_SIZE = 500;
|
|
545
545
|
function evictGroupNameCache() {
|
|
546
546
|
const now = asDateTimestampMs(Date.now());
|
|
@@ -582,7 +582,8 @@ async function resolveGroupName(params) {
|
|
|
582
582
|
}
|
|
583
583
|
let resolvedName;
|
|
584
584
|
try {
|
|
585
|
-
const
|
|
585
|
+
const client = createFeishuClient(account);
|
|
586
|
+
const name = (await getChatInfo(client, chatId))?.name?.trim();
|
|
586
587
|
if (name) {
|
|
587
588
|
setCacheEntry(cacheKey, name);
|
|
588
589
|
resolvedName = name;
|
|
@@ -596,11 +597,11 @@ async function resolveGroupName(params) {
|
|
|
596
597
|
}
|
|
597
598
|
//#endregion
|
|
598
599
|
//#region extensions/feishu/src/bot-name.ts
|
|
599
|
-
const POSITIVE_TTL_MS =
|
|
600
|
+
const POSITIVE_TTL_MS = 6e5;
|
|
600
601
|
const NEGATIVE_TTL_MS = 6e4;
|
|
601
602
|
const MAX_CACHE_ENTRIES = 5e3;
|
|
602
603
|
const BREAKER_FAILURE_THRESHOLD = 10;
|
|
603
|
-
const BREAKER_OPEN_MS =
|
|
604
|
+
const BREAKER_OPEN_MS = 36e5;
|
|
604
605
|
const PERMISSION_BACKOFF_MS = 6e4;
|
|
605
606
|
const REQUEST_TIMEOUT_MS = 1500;
|
|
606
607
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -738,8 +739,8 @@ async function resolveFeishuBotName(params) {
|
|
|
738
739
|
const IGNORED_PERMISSION_SCOPE_TOKENS = ["contact:contact.base:readonly"];
|
|
739
740
|
const FEISHU_SCOPE_CORRECTIONS = { "contact:contact.base:readonly": "contact:user.base:readonly" };
|
|
740
741
|
const FEISHU_USER_LOOKUP_UNAUTHORIZED_CODE = 41050;
|
|
741
|
-
const SENDER_NAME_TTL_MS =
|
|
742
|
-
const SENDER_NAME_NEGATIVE_TTL_MS =
|
|
742
|
+
const SENDER_NAME_TTL_MS = 6e5;
|
|
743
|
+
const SENDER_NAME_NEGATIVE_TTL_MS = 18e5;
|
|
743
744
|
const SENDER_NAME_CACHE_MAX_SIZE = 500;
|
|
744
745
|
const senderNameCache = /* @__PURE__ */ new Map();
|
|
745
746
|
function correctFeishuScopeInUrl(url) {
|
|
@@ -840,7 +841,7 @@ async function resolveFeishuSenderName(params) {
|
|
|
840
841
|
//#endregion
|
|
841
842
|
//#region extensions/feishu/src/dedup-state.ts
|
|
842
843
|
const DEDUPE_NAMESPACE_PREFIX = "feishu.dedup";
|
|
843
|
-
const DEDUP_TTL_MS =
|
|
844
|
+
const DEDUP_TTL_MS = 864e5;
|
|
844
845
|
const MEMORY_MAX_SIZE = 1e3;
|
|
845
846
|
const STORE_MAX_ENTRIES = 1e4;
|
|
846
847
|
function createFeishuDedupeGuard() {
|
|
@@ -1145,48 +1146,6 @@ function resolveFeishuReasoningPreviewEnabled(params) {
|
|
|
1145
1146
|
return configDefault === "stream";
|
|
1146
1147
|
}
|
|
1147
1148
|
//#endregion
|
|
1148
|
-
//#region extensions/feishu/src/reply-delivery-result.ts
|
|
1149
|
-
const noVisibleFeishuReplyDelivery = { visibleReplySent: false };
|
|
1150
|
-
function hasProviderIdentity(result) {
|
|
1151
|
-
return Boolean(result && (result.messageId?.trim() || result.receipt?.primaryPlatformMessageId?.trim() || result.receipt?.platformMessageIds.length));
|
|
1152
|
-
}
|
|
1153
|
-
/** Normalizes every physical Lark send behind one logical reply payload. */
|
|
1154
|
-
function createFeishuReplyDeliveryResult(params) {
|
|
1155
|
-
const results = params.visibleReplySent ? (params.results ?? []).filter(hasProviderIdentity) : [];
|
|
1156
|
-
const receipt = results.length > 0 ? createMessageReceiptFromOutboundResults({
|
|
1157
|
-
results,
|
|
1158
|
-
...params.kind ? { kind: params.kind } : {}
|
|
1159
|
-
}) : void 0;
|
|
1160
|
-
if (!receipt) return {
|
|
1161
|
-
visibleReplySent: params.visibleReplySent,
|
|
1162
|
-
...params.content === void 0 ? {} : { content: params.content }
|
|
1163
|
-
};
|
|
1164
|
-
return {
|
|
1165
|
-
messageIds: [...receipt.platformMessageIds],
|
|
1166
|
-
receipt,
|
|
1167
|
-
visibleReplySent: params.visibleReplySent,
|
|
1168
|
-
...params.content === void 0 ? {} : { content: params.content }
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
/** Preserves the first result's provider identity while retaining supplemental ids. */
|
|
1172
|
-
function mergeFeishuReplyDeliveryResults(results, content) {
|
|
1173
|
-
const visible = results.filter((result) => result.visibleReplySent === true);
|
|
1174
|
-
const acceptedContent = visible.flatMap((result) => result.content === void 0 ? [] : [result.content]);
|
|
1175
|
-
return createFeishuReplyDeliveryResult({
|
|
1176
|
-
results: visible,
|
|
1177
|
-
visibleReplySent: visible.length > 0,
|
|
1178
|
-
content: content === void 0 ? acceptedContent.length > 0 ? acceptedContent.join("\n\n") : void 0 : content
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
/** Keeps a failed outcome while exposing the provider-visible subset to core lifecycle hooks. */
|
|
1182
|
-
function createFeishuPartialReplyDeliveryError(cause, result) {
|
|
1183
|
-
if (result.visibleReplySent !== true) return cause instanceof Error ? cause : new Error(formatErrorMessage(cause), { cause });
|
|
1184
|
-
return createChannelPartialDeliveryError(cause, {
|
|
1185
|
-
...result,
|
|
1186
|
-
visibleReplySent: true
|
|
1187
|
-
});
|
|
1188
|
-
}
|
|
1189
|
-
//#endregion
|
|
1190
1149
|
//#region extensions/feishu/src/reply-dispatcher-state.ts
|
|
1191
1150
|
const streamingStartBackoffUntilByAccount = /* @__PURE__ */ new Map();
|
|
1192
1151
|
//#endregion
|
|
@@ -1662,21 +1621,10 @@ var FeishuStreamingSession = class {
|
|
|
1662
1621
|
if (finalWriteError !== void 0 || closeError !== void 0) throw new FeishuStreamingFinalizationError(finalWriteError !== void 0 && closeError !== void 0 ? new AggregateError([finalWriteError, closeError], "Feishu streaming card finalization failed") : finalWriteError ?? closeError, result);
|
|
1663
1622
|
return result;
|
|
1664
1623
|
}
|
|
1665
|
-
async close(finalText, options) {
|
|
1666
|
-
try {
|
|
1667
|
-
return (await this.closeWithResult(finalText, options)).visibleReplySent;
|
|
1668
|
-
} catch (error) {
|
|
1669
|
-
if (error instanceof FeishuStreamingFinalizationError) return error.result.visibleReplySent;
|
|
1670
|
-
throw error;
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
1624
|
async discard() {
|
|
1674
|
-
if (!this.state || this.closed) return;
|
|
1625
|
+
if (!this.state || this.closed) return { visibleReplySent: false };
|
|
1675
1626
|
const { cardId, messageId } = this.state;
|
|
1676
|
-
if (!messageId)
|
|
1677
|
-
await this.close("");
|
|
1678
|
-
return;
|
|
1679
|
-
}
|
|
1627
|
+
if (!messageId) return this.closeWithResult("");
|
|
1680
1628
|
this.closed = true;
|
|
1681
1629
|
this.clearFlushTimer();
|
|
1682
1630
|
await this.queue;
|
|
@@ -1686,10 +1634,11 @@ var FeishuStreamingSession = class {
|
|
|
1686
1634
|
this.state = null;
|
|
1687
1635
|
this.pendingText = null;
|
|
1688
1636
|
this.log?.(`Discarded streaming card: cardId=${cardId}`);
|
|
1637
|
+
return { visibleReplySent: false };
|
|
1689
1638
|
} catch (error) {
|
|
1690
1639
|
this.log?.(`Discard failed: ${String(error)}`);
|
|
1691
1640
|
this.closed = false;
|
|
1692
|
-
|
|
1641
|
+
return this.closeWithResult("");
|
|
1693
1642
|
}
|
|
1694
1643
|
}
|
|
1695
1644
|
isActive() {
|
|
@@ -1820,7 +1769,7 @@ function mergeStreamingFinalText(previousText, nextText, appendError) {
|
|
|
1820
1769
|
}
|
|
1821
1770
|
/** Maximum age (ms) for a message to receive a typing indicator reaction.
|
|
1822
1771
|
* Messages older than this are likely replays after context compaction (#30418). */
|
|
1823
|
-
const TYPING_INDICATOR_MAX_AGE_MS =
|
|
1772
|
+
const TYPING_INDICATOR_MAX_AGE_MS = 12e4;
|
|
1824
1773
|
const MS_EPOCH_MIN = 0xe8d4a51000;
|
|
1825
1774
|
const STREAMING_START_FAILURE_BACKOFF_MS = 6e4;
|
|
1826
1775
|
const NO_VISIBLE_REPLY_FALLBACK_TEXT = "⚠️ This reply completed without visible content. The turn may have been interrupted; please retry or ask me to recover from recent context.";
|
|
@@ -1947,9 +1896,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1947
1896
|
let activeStreamingGeneration;
|
|
1948
1897
|
let inFlightStreamingClose;
|
|
1949
1898
|
let visibleReplySent = false;
|
|
1950
|
-
let
|
|
1951
|
-
let skippedFinalAssistantMessageIndex;
|
|
1952
|
-
let preparedDeliveryAssistantMessageIndex;
|
|
1899
|
+
let replyOutcome;
|
|
1953
1900
|
let idleSideEffectsPromise = Promise.resolve();
|
|
1954
1901
|
let activeIdleSideEffectsPromise = null;
|
|
1955
1902
|
let idleRequestedForReply = false;
|
|
@@ -2049,97 +1996,86 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2049
1996
|
lastSnapshotTextLength = 0;
|
|
2050
1997
|
hasStreamingFinalText = false;
|
|
2051
1998
|
};
|
|
2052
|
-
const rememberClosedStreamingSettlement = (generation, content, result, error) => {
|
|
2053
|
-
if (generation === void 0 || !content) return;
|
|
1999
|
+
const rememberClosedStreamingSettlement = (generation, content, result, error, disposition = "closed") => {
|
|
2000
|
+
if (generation === void 0 || !content && disposition === "closed") return;
|
|
2054
2001
|
closedStreamingSettlements.set(generation, {
|
|
2002
|
+
disposition,
|
|
2055
2003
|
result,
|
|
2056
2004
|
content,
|
|
2057
2005
|
...error === void 0 ? {} : { error }
|
|
2058
2006
|
});
|
|
2059
2007
|
};
|
|
2060
|
-
const performStreamingClose = async () => {
|
|
2008
|
+
const performStreamingClose = async (disposition) => {
|
|
2061
2009
|
const streamingToClose = streaming;
|
|
2062
2010
|
const generationToClose = activeStreamingGeneration;
|
|
2063
2011
|
const startPromiseToClose = streamingStartPromise;
|
|
2064
2012
|
const updateQueueToClose = partialUpdateQueue;
|
|
2065
2013
|
const finalizedAnswerText = streamText;
|
|
2066
2014
|
const finalizedReasoningText = reasoningText;
|
|
2015
|
+
const outcome = {
|
|
2016
|
+
disposition,
|
|
2017
|
+
...generationToClose === void 0 ? {} : { generation: generationToClose }
|
|
2018
|
+
};
|
|
2067
2019
|
if (generationToClose !== void 0 && activeStreamingGeneration === generationToClose) activeStreamingGeneration = void 0;
|
|
2068
2020
|
try {
|
|
2069
2021
|
if (startPromiseToClose) await startPromiseToClose;
|
|
2070
2022
|
await updateQueueToClose;
|
|
2023
|
+
let result = noVisibleFeishuReplyDelivery;
|
|
2024
|
+
let finalizationError;
|
|
2071
2025
|
if (streamingToClose?.isActive()) {
|
|
2072
2026
|
statusLine = "";
|
|
2073
2027
|
const text = buildCombinedStreamText(finalizedReasoningText, finalizedAnswerText);
|
|
2074
|
-
const finalNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
|
|
2075
2028
|
let closed;
|
|
2076
2029
|
try {
|
|
2077
|
-
closed = await streamingToClose.
|
|
2030
|
+
if (disposition === "discarded") closed = await streamingToClose.discard();
|
|
2031
|
+
else {
|
|
2032
|
+
const finalNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
|
|
2033
|
+
closed = await streamingToClose.closeWithResult(text, { note: finalNote });
|
|
2034
|
+
}
|
|
2078
2035
|
} catch (error) {
|
|
2079
2036
|
if (!(error instanceof FeishuStreamingFinalizationError)) throw error;
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
visibleReplySent: error.result.visibleReplySent,
|
|
2083
|
-
content: error.result.content,
|
|
2084
|
-
kind: "card"
|
|
2085
|
-
});
|
|
2086
|
-
if (failedResult.visibleReplySent) markVisibleReplySent();
|
|
2087
|
-
if (failedResult.visibleReplySent && finalizedAnswerText) {
|
|
2088
|
-
const partialError = createFeishuPartialReplyDeliveryError(error.cause ?? error, failedResult);
|
|
2089
|
-
if (failedResult.content === text) deliveredFinalTexts.add(finalizedAnswerText);
|
|
2090
|
-
rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, failedResult, partialError);
|
|
2091
|
-
return {
|
|
2092
|
-
result: failedResult,
|
|
2093
|
-
...generationToClose === void 0 ? {} : { generation: generationToClose },
|
|
2094
|
-
error: partialError
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, failedResult, error);
|
|
2098
|
-
return {
|
|
2099
|
-
result: failedResult,
|
|
2100
|
-
...generationToClose === void 0 ? {} : { generation: generationToClose },
|
|
2101
|
-
error
|
|
2102
|
-
};
|
|
2037
|
+
closed = error.result;
|
|
2038
|
+
finalizationError = error;
|
|
2103
2039
|
}
|
|
2104
|
-
|
|
2040
|
+
result = createFeishuReplyDeliveryResult({
|
|
2105
2041
|
results: [closed],
|
|
2106
2042
|
visibleReplySent: closed.visibleReplySent,
|
|
2107
2043
|
content: closed.content,
|
|
2108
2044
|
kind: "card"
|
|
2109
2045
|
});
|
|
2110
2046
|
if (result.visibleReplySent) markVisibleReplySent();
|
|
2111
|
-
if (result.visibleReplySent && finalizedAnswerText)
|
|
2112
|
-
|
|
2113
|
-
rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, result);
|
|
2114
|
-
}
|
|
2115
|
-
return {
|
|
2116
|
-
result,
|
|
2117
|
-
...generationToClose === void 0 ? {} : { generation: generationToClose }
|
|
2118
|
-
};
|
|
2047
|
+
if (disposition === "closed" && result.visibleReplySent && finalizedAnswerText && (finalizationError === void 0 || result.content === text)) deliveredFinalTexts.add(finalizedAnswerText);
|
|
2048
|
+
if (finalizationError instanceof FeishuStreamingFinalizationError && result.visibleReplySent) finalizationError = createFeishuPartialReplyDeliveryError(finalizationError.cause ?? finalizationError, result);
|
|
2119
2049
|
}
|
|
2050
|
+
if (disposition === "discarded" || finalizationError !== void 0 || result.visibleReplySent && finalizedAnswerText) rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, result, finalizationError, disposition);
|
|
2120
2051
|
return {
|
|
2121
|
-
|
|
2122
|
-
|
|
2052
|
+
...outcome,
|
|
2053
|
+
result,
|
|
2054
|
+
...finalizationError === void 0 ? {} : { error: finalizationError }
|
|
2123
2055
|
};
|
|
2124
2056
|
} catch (error) {
|
|
2057
|
+
if (disposition === "discarded") rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, noVisibleFeishuReplyDelivery, error, disposition);
|
|
2125
2058
|
return {
|
|
2059
|
+
...outcome,
|
|
2126
2060
|
result: noVisibleFeishuReplyDelivery,
|
|
2127
|
-
...generationToClose === void 0 ? {} : { generation: generationToClose },
|
|
2128
2061
|
error
|
|
2129
2062
|
};
|
|
2130
2063
|
} finally {
|
|
2131
2064
|
if (streaming === streamingToClose) resetStreamingState();
|
|
2132
2065
|
}
|
|
2133
2066
|
};
|
|
2134
|
-
const closeStreaming = () => {
|
|
2067
|
+
const closeStreaming = (disposition = "closed") => {
|
|
2068
|
+
const session = streaming;
|
|
2135
2069
|
const generation = activeStreamingGeneration;
|
|
2136
|
-
if (
|
|
2070
|
+
if (session && inFlightStreamingClose?.session === session) return inFlightStreamingClose.promise;
|
|
2137
2071
|
const content = streamText;
|
|
2138
|
-
const closePromise = performStreamingClose();
|
|
2139
|
-
if (generation !== void 0) {
|
|
2072
|
+
const closePromise = performStreamingClose(disposition);
|
|
2073
|
+
if (session && generation !== void 0) {
|
|
2140
2074
|
const closing = {
|
|
2075
|
+
session,
|
|
2141
2076
|
generation,
|
|
2142
2077
|
content,
|
|
2078
|
+
disposition,
|
|
2143
2079
|
promise: closePromise
|
|
2144
2080
|
};
|
|
2145
2081
|
inFlightStreamingClose = closing;
|
|
@@ -2171,13 +2107,12 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2171
2107
|
};
|
|
2172
2108
|
};
|
|
2173
2109
|
const discardStreamingPreview = async () => {
|
|
2174
|
-
|
|
2175
|
-
if (streamingStartPromise) await streamingStartPromise;
|
|
2176
|
-
await partialUpdateQueue;
|
|
2177
|
-
if (streaming?.isActive()) await streaming.discard();
|
|
2178
|
-
} finally {
|
|
2110
|
+
if (streaming && inFlightStreamingClose?.session === streaming && inFlightStreamingClose.disposition === "closed") {
|
|
2179
2111
|
resetStreamingState();
|
|
2112
|
+
return;
|
|
2180
2113
|
}
|
|
2114
|
+
const outcome = await closeStreaming("discarded");
|
|
2115
|
+
if (outcome.error !== void 0) throw toStringifiedError(outcome.error);
|
|
2181
2116
|
};
|
|
2182
2117
|
const updateStreamingStatusLine = (nextStatusLine, options) => {
|
|
2183
2118
|
statusLine = nextStatusLine;
|
|
@@ -2189,14 +2124,19 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2189
2124
|
const sendChunkedTextReply = async (paramsLocal) => {
|
|
2190
2125
|
const chunkSource = paramsLocal.useCard ? paramsLocal.text : materializeFeishuPostMarkdownSoftBreaks(core.channel.text.convertMarkdownTables(paramsLocal.text, tableMode));
|
|
2191
2126
|
const initialChunks = core.channel.text.chunkMarkdownTextWithMode(chunkSource, textChunkLimit, chunkMode);
|
|
2192
|
-
const
|
|
2127
|
+
const chunkOptions = {
|
|
2193
2128
|
text: chunkSource,
|
|
2194
2129
|
limit: textChunkLimit,
|
|
2195
2130
|
mode: chunkMode,
|
|
2196
2131
|
firstChunkMentions: paramsLocal.firstChunkMentions,
|
|
2197
2132
|
chunkMentions: paramsLocal.chunkMentions,
|
|
2198
2133
|
initialChunks
|
|
2199
|
-
}
|
|
2134
|
+
};
|
|
2135
|
+
const chunks = resolveTextChunksWithFallback(chunkSource, paramsLocal.useCard ? chunkFeishuCardMarkdown({
|
|
2136
|
+
...chunkOptions,
|
|
2137
|
+
header: paramsLocal.header,
|
|
2138
|
+
note: paramsLocal.note
|
|
2139
|
+
}) : chunkFeishuPostMarkdown(chunkOptions));
|
|
2200
2140
|
const results = [];
|
|
2201
2141
|
const acceptedChunks = [];
|
|
2202
2142
|
for (const [index, chunk] of chunks.entries()) {
|
|
@@ -2235,27 +2175,28 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2235
2175
|
kind: paramsLocal.useCard ? "card" : "text"
|
|
2236
2176
|
});
|
|
2237
2177
|
};
|
|
2178
|
+
const sendPostReply = (text, infoKind, firstChunkMentions) => sendChunkedTextReply({
|
|
2179
|
+
text,
|
|
2180
|
+
useCard: false,
|
|
2181
|
+
infoKind,
|
|
2182
|
+
firstChunkMentions,
|
|
2183
|
+
chunkMentions: requiredMentionTargets,
|
|
2184
|
+
sendChunk: ({ chunk, mentions }) => sendMessageFeishu({
|
|
2185
|
+
cfg,
|
|
2186
|
+
to: sendTarget,
|
|
2187
|
+
text: chunk,
|
|
2188
|
+
replyToMessageId: sendReplyToMessageId,
|
|
2189
|
+
replyInThread: effectiveReplyInThread,
|
|
2190
|
+
allowTopLevelReplyFallback,
|
|
2191
|
+
accountId,
|
|
2192
|
+
...mentions ? { mentions } : {}
|
|
2193
|
+
})
|
|
2194
|
+
});
|
|
2238
2195
|
const sendMediaReplies = async (payload, options) => {
|
|
2239
2196
|
const mediaUrls = resolveSendableOutboundReplyParts(payload).mediaUrls;
|
|
2240
2197
|
let sentFallbackText = false;
|
|
2241
2198
|
let degradedVoiceFallbackText;
|
|
2242
2199
|
const results = [];
|
|
2243
|
-
const sendFallbackText = async (text) => await sendChunkedTextReply({
|
|
2244
|
-
text,
|
|
2245
|
-
useCard: false,
|
|
2246
|
-
infoKind: "final",
|
|
2247
|
-
chunkMentions: requiredMentionTargets,
|
|
2248
|
-
sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
|
|
2249
|
-
cfg,
|
|
2250
|
-
to: sendTarget,
|
|
2251
|
-
text: chunk,
|
|
2252
|
-
replyToMessageId: sendReplyToMessageId,
|
|
2253
|
-
replyInThread: effectiveReplyInThread,
|
|
2254
|
-
allowTopLevelReplyFallback,
|
|
2255
|
-
accountId,
|
|
2256
|
-
...mentions ? { mentions } : {}
|
|
2257
|
-
})
|
|
2258
|
-
});
|
|
2259
2200
|
try {
|
|
2260
2201
|
await sendMediaWithLeadingCaption({
|
|
2261
2202
|
mediaUrls,
|
|
@@ -2289,12 +2230,12 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2289
2230
|
mediaUrl
|
|
2290
2231
|
});
|
|
2291
2232
|
sentFallbackText = true;
|
|
2292
|
-
results.push(await
|
|
2233
|
+
results.push(await sendPostReply(fallbackText, "final"));
|
|
2293
2234
|
}
|
|
2294
2235
|
});
|
|
2295
2236
|
if (degradedVoiceFallbackText && !sentFallbackText) {
|
|
2296
2237
|
sentFallbackText = true;
|
|
2297
|
-
results.push(await
|
|
2238
|
+
results.push(await sendPostReply(degradedVoiceFallbackText, "final"));
|
|
2298
2239
|
}
|
|
2299
2240
|
} catch (error) {
|
|
2300
2241
|
const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
|
|
@@ -2306,8 +2247,8 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2306
2247
|
const ensureNoVisibleReplyFallback = async (reason) => {
|
|
2307
2248
|
await idleSideEffectsPromise;
|
|
2308
2249
|
if (visibleReplySent) return false;
|
|
2309
|
-
if (
|
|
2310
|
-
params.runtime.log?.(`feishu[${account.accountId}]: no-visible-reply fallback skipped for
|
|
2250
|
+
if (replyOutcome?.kind === "suppressed" || replyOutcome?.kind === "skipped" && replyOutcome.reason === "silent") {
|
|
2251
|
+
params.runtime.log?.(`feishu[${account.accountId}]: no-visible-reply fallback skipped for ${replyOutcome.reason} (${reason})`);
|
|
2311
2252
|
return false;
|
|
2312
2253
|
}
|
|
2313
2254
|
await sendMessageFeishu({
|
|
@@ -2351,6 +2292,8 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2351
2292
|
text: content,
|
|
2352
2293
|
useCard: true,
|
|
2353
2294
|
infoKind,
|
|
2295
|
+
header: cardHeader,
|
|
2296
|
+
note: cardNote,
|
|
2354
2297
|
chunkMentions: requiredMentionTargets,
|
|
2355
2298
|
sendChunk: async ({ chunk, mentions }) => await sendStructuredCardFeishu({
|
|
2356
2299
|
cfg,
|
|
@@ -2379,10 +2322,17 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2379
2322
|
if (completions.some((completion) => ownsCurrentClose(completion))) markClosedStreamingContentClaimed(closeOutcome.generation);
|
|
2380
2323
|
for (const completion of completions) {
|
|
2381
2324
|
const claimedSettlement = ownsCurrentClose(completion) ? {
|
|
2325
|
+
disposition: closeOutcome.disposition,
|
|
2382
2326
|
result: finalized,
|
|
2383
2327
|
...closeOutcome.error === void 0 ? {} : { error: closeOutcome.error }
|
|
2384
2328
|
} : claimClosedStreamingResult(completion.streamingGeneration, completion.result.content);
|
|
2385
2329
|
const deliveryError = claimedSettlement?.error;
|
|
2330
|
+
if (claimedSettlement?.disposition === "discarded") {
|
|
2331
|
+
const retained = mergeFeishuReplyDeliveryResults([claimedSettlement.result, completion.result], claimedSettlement.result.content ?? "");
|
|
2332
|
+
if (deliveryError !== void 0) completion.reject(createFeishuPartialReplyDeliveryError(deliveryError, retained));
|
|
2333
|
+
else completion.resolve(retained);
|
|
2334
|
+
continue;
|
|
2335
|
+
}
|
|
2386
2336
|
let providerFinalized = claimedSettlement?.result;
|
|
2387
2337
|
try {
|
|
2388
2338
|
providerFinalized = await ensureVisibleStreamingDelivery(providerFinalized, completion.result.content, completion.infoKind);
|
|
@@ -2419,22 +2369,16 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2419
2369
|
let finalized = noVisibleFeishuReplyDelivery;
|
|
2420
2370
|
let finalizationError;
|
|
2421
2371
|
let fallbackPartial;
|
|
2422
|
-
|
|
2423
|
-
if (
|
|
2424
|
-
finalized = claimedSettlement.result;
|
|
2425
|
-
finalizationError = claimedSettlement.error;
|
|
2426
|
-
} else if (paramsLocal.ownerGeneration !== void 0 && inFlightStreamingClose?.generation === paramsLocal.ownerGeneration) {
|
|
2372
|
+
let settlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content);
|
|
2373
|
+
if (!settlement && paramsLocal.ownerGeneration !== void 0 && inFlightStreamingClose?.generation === paramsLocal.ownerGeneration) {
|
|
2427
2374
|
const closeOutcome = await inFlightStreamingClose.promise;
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
finalized = closeOutcome.result;
|
|
2434
|
-
finalizationError = closeOutcome.error;
|
|
2435
|
-
}
|
|
2375
|
+
settlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content) ?? closeOutcome;
|
|
2376
|
+
} else if (!settlement && paramsLocal.ownerGeneration !== void 0 && activeStreamingGeneration === paramsLocal.ownerGeneration) settlement = await closeStreaming();
|
|
2377
|
+
finalized = settlement?.result ?? finalized;
|
|
2378
|
+
finalizationError = settlement?.error;
|
|
2379
|
+
const discarded = settlement?.disposition === "discarded";
|
|
2436
2380
|
try {
|
|
2437
|
-
finalized = await ensureVisibleStreamingDelivery(finalized, paramsLocal.content, paramsLocal.infoKind) ?? finalized;
|
|
2381
|
+
if (!discarded) finalized = await ensureVisibleStreamingDelivery(finalized, paramsLocal.content, paramsLocal.infoKind) ?? finalized;
|
|
2438
2382
|
} catch (fallbackError) {
|
|
2439
2383
|
fallbackPartial = isChannelPartialDeliveryError(fallbackError) ? fallbackError.deliveryResult : void 0;
|
|
2440
2384
|
const fallbackCause = fallbackPartial && fallbackError instanceof Error ? fallbackError.cause ?? fallbackError : fallbackError;
|
|
@@ -2445,9 +2389,10 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2445
2389
|
finalized,
|
|
2446
2390
|
...fallbackPartial ? [fallbackPartial] : [],
|
|
2447
2391
|
...mediaPartial ? [mediaPartial] : []
|
|
2448
|
-
], fallbackPartial?.visibleReplySent === true ? fallbackPartial.content : finalized.visibleReplySent === true ? finalized.content : paramsLocal.content);
|
|
2392
|
+
], discarded ? finalized.content ?? "" : fallbackPartial?.visibleReplySent === true ? fallbackPartial.content : finalized.visibleReplySent === true ? finalized.content : paramsLocal.content);
|
|
2449
2393
|
const mediaCause = mediaPartial && paramsLocal.error instanceof Error ? paramsLocal.error.cause ?? paramsLocal.error : paramsLocal.error;
|
|
2450
|
-
|
|
2394
|
+
const cause = finalizationError ? new AggregateError([mediaCause, finalizationError instanceof Error ? finalizationError.cause ?? finalizationError : finalizationError], "Feishu reply delivery and streaming finalization failed") : mediaCause;
|
|
2395
|
+
throw createFeishuPartialReplyDeliveryError(cause, accepted);
|
|
2451
2396
|
};
|
|
2452
2397
|
const dispatcherOptions = {
|
|
2453
2398
|
responsePrefix,
|
|
@@ -2460,13 +2405,14 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2460
2405
|
conversationType: chatId.startsWith("oc_") ? "group" : "direct"
|
|
2461
2406
|
},
|
|
2462
2407
|
onSkip: (_payload, info) => {
|
|
2463
|
-
if (info.kind === "final" || info.kind === "block" && info.reason === "silent") {
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2408
|
+
if (replyOutcome?.kind !== "failed" && (info.kind === "final" || info.kind === "block" && info.reason === "silent")) replyOutcome = {
|
|
2409
|
+
kind: "skipped",
|
|
2410
|
+
reason: info.reason,
|
|
2411
|
+
assistantMessageIndex: info.assistantMessageIndex
|
|
2412
|
+
};
|
|
2467
2413
|
},
|
|
2468
2414
|
beforeDeliver: (payload, info) => {
|
|
2469
|
-
|
|
2415
|
+
if (!(replyOutcome?.kind === "skipped" && replyOutcome.reason === "silent" && info.kind !== "final" && replyOutcome.assistantMessageIndex !== void 0 && info.assistantMessageIndex !== void 0 && info.assistantMessageIndex < replyOutcome.assistantMessageIndex) && replyOutcome?.kind !== "failed") replyOutcome = void 0;
|
|
2470
2416
|
return payload;
|
|
2471
2417
|
},
|
|
2472
2418
|
onReplyStart: async () => {
|
|
@@ -2477,9 +2423,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2477
2423
|
sentIndependentBlockText = false;
|
|
2478
2424
|
idleRequestedForReply = false;
|
|
2479
2425
|
visibleReplySent = false;
|
|
2480
|
-
|
|
2481
|
-
skippedFinalAssistantMessageIndex = void 0;
|
|
2482
|
-
preparedDeliveryAssistantMessageIndex = void 0;
|
|
2426
|
+
replyOutcome = void 0;
|
|
2483
2427
|
}
|
|
2484
2428
|
if (previewStreamingEnabled && renderMode === "card") startStreaming();
|
|
2485
2429
|
await Promise.resolve(typingCallbacks?.onReplyStart?.());
|
|
@@ -2490,6 +2434,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2490
2434
|
}
|
|
2491
2435
|
};
|
|
2492
2436
|
const handleDeliveryError = async (error, info) => {
|
|
2437
|
+
if (info.kind === "final") replyOutcome = { kind: "failed" };
|
|
2493
2438
|
if (isChannelPartialDeliveryError(error)) markVisibleReplySent();
|
|
2494
2439
|
params.runtime.error?.(`feishu[${account.accountId}] ${info.kind} reply failed: ${String(error)}`);
|
|
2495
2440
|
await queueIdleSideEffects().catch((cleanupError) => params.runtime.error?.(`feishu[${account.accountId}] reply error cleanup failed: ${String(cleanupError)}`));
|
|
@@ -2498,19 +2443,36 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2498
2443
|
dispatcherOptions,
|
|
2499
2444
|
delivery: {
|
|
2500
2445
|
observeMessageSent: true,
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
skippedFinalAssistantMessageIndex = void 0;
|
|
2446
|
+
onDelivered: (_payload, info, result) => {
|
|
2447
|
+
if (result?.visibleReplySent) {
|
|
2448
|
+
markVisibleReplySent();
|
|
2449
|
+
if (info.kind === "final") replyOutcome = void 0;
|
|
2450
|
+
return;
|
|
2507
2451
|
}
|
|
2508
|
-
const
|
|
2452
|
+
const reason = result?.suppression?.reason;
|
|
2453
|
+
if (info.kind === "final" && replyOutcome?.kind !== "failed" && (reason === "cancelled_by_reply_payload_sending_hook" || reason === "empty_after_reply_payload_sending_hook" || reason === "cancelled_by_message_sending_hook" || reason === "empty_after_message_sending_hook" || reason === "channel_transform")) replyOutcome = {
|
|
2454
|
+
kind: "suppressed",
|
|
2455
|
+
reason
|
|
2456
|
+
};
|
|
2457
|
+
},
|
|
2458
|
+
deliver: async (inputPayload, info) => {
|
|
2459
|
+
const prepared = await renderFeishuReplyPayload(inputPayload, {
|
|
2460
|
+
to: sendTarget,
|
|
2461
|
+
identity,
|
|
2462
|
+
mentions: requiredMentionTargets
|
|
2463
|
+
});
|
|
2464
|
+
const rendered = consumeFeishuPresentationFallbackMarker(prepared.payload);
|
|
2465
|
+
const payload = rendered.payload;
|
|
2466
|
+
const presentationCard = prepared.card;
|
|
2467
|
+
const hasPresentationFallback = rendered.presentationFallback?.hasVisibleContent === true;
|
|
2468
|
+
const hasIndependentPresentation = presentationCard !== void 0 || hasPresentationFallback;
|
|
2469
|
+
const resolvedText = payload.text;
|
|
2470
|
+
const payloadText = payload.isReasoning && resolvedText ? formatReasoningMessage(resolvedText) : resolvedText;
|
|
2509
2471
|
const reply = resolveSendableOutboundReplyParts({
|
|
2510
2472
|
...payload,
|
|
2511
2473
|
text: payloadText
|
|
2512
2474
|
});
|
|
2513
|
-
const text = info?.kind === "final" ? mergeStreamingFinalText(streamText, reply.text, payload.isError === true && hasStreamingFinalText) : reply.text;
|
|
2475
|
+
const text = info?.kind === "final" && !hasIndependentPresentation ? mergeStreamingFinalText(streamText, reply.text, payload.isError === true && hasStreamingFinalText) : reply.text;
|
|
2514
2476
|
const hasText = reply.hasText;
|
|
2515
2477
|
const hasMedia = reply.hasMedia;
|
|
2516
2478
|
const ttsSupplement = getReplyPayloadTtsSupplement(payload);
|
|
@@ -2524,52 +2486,61 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2524
2486
|
const useStaticCard = hasText && (renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text));
|
|
2525
2487
|
const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || useStaticCard);
|
|
2526
2488
|
const useCard = useStaticCard || useStreamingCard;
|
|
2527
|
-
const skipTextForDuplicateFinal = info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
|
|
2528
|
-
const shouldDeliverText = hasText && !hasVoiceMedia && !skipTextForDuplicateFinal;
|
|
2529
|
-
const shouldDiscardStreamingPreview = info?.kind === "final" && (finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
|
|
2489
|
+
const skipTextForDuplicateFinal = !hasIndependentPresentation && info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
|
|
2490
|
+
const shouldDeliverText = hasText && (!hasVoiceMedia || hasPresentationFallback) && !skipTextForDuplicateFinal;
|
|
2491
|
+
const shouldDiscardStreamingPreview = info?.kind === "final" && !(hasIndependentPresentation && payload.isError === true && hasStreamingFinalText) && (hasIndependentPresentation || finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
|
|
2530
2492
|
const priorClosedStreamingSettlement = info?.kind === "final" && hasText && skipTextForDuplicateFinal ? claimClosedStreamingResult(void 0, text) : void 0;
|
|
2531
|
-
if (!shouldDeliverText && !hasMedia) {
|
|
2493
|
+
if (!shouldDeliverText && !hasMedia && !presentationCard) {
|
|
2532
2494
|
if (priorClosedStreamingSettlement?.error !== void 0) throw toStringifiedError(priorClosedStreamingSettlement.error);
|
|
2533
2495
|
return priorClosedStreamingSettlement?.result ?? noVisibleFeishuReplyDelivery;
|
|
2534
2496
|
}
|
|
2535
2497
|
const deliveredResults = priorClosedStreamingSettlement ? [priorClosedStreamingSettlement.result] : [];
|
|
2536
|
-
const
|
|
2498
|
+
const collectDelivery = async (pending, acceptedContent) => {
|
|
2537
2499
|
try {
|
|
2538
|
-
deliveredResults.push(await
|
|
2500
|
+
deliveredResults.push(await pending);
|
|
2539
2501
|
} catch (error) {
|
|
2540
2502
|
const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
|
|
2541
|
-
const accumulated = mergeFeishuReplyDeliveryResults([...deliveredResults, ...partial ? [
|
|
2542
|
-
|
|
2503
|
+
const accumulated = mergeFeishuReplyDeliveryResults([...deliveredResults, ...partial ? [{
|
|
2504
|
+
...partial,
|
|
2505
|
+
content: partial.content ?? acceptedContent
|
|
2506
|
+
}] : []]);
|
|
2507
|
+
throw createFeishuPartialReplyDeliveryError(partial && error instanceof Error ? error.cause ?? error : error, {
|
|
2508
|
+
...accumulated,
|
|
2509
|
+
content: accumulated.content ?? ""
|
|
2510
|
+
});
|
|
2543
2511
|
}
|
|
2544
2512
|
};
|
|
2545
2513
|
if (shouldDiscardStreamingPreview) await discardStreamingPreview();
|
|
2514
|
+
if (presentationCard) {
|
|
2515
|
+
if (hasMedia) await collectDelivery(sendMediaReplies(payload));
|
|
2516
|
+
await collectDelivery(sendCardFeishu({
|
|
2517
|
+
cfg,
|
|
2518
|
+
to: sendTarget,
|
|
2519
|
+
card: presentationCard,
|
|
2520
|
+
replyToMessageId: sendReplyToMessageId,
|
|
2521
|
+
replyInThread: effectiveReplyInThread,
|
|
2522
|
+
allowTopLevelReplyFallback,
|
|
2523
|
+
accountId
|
|
2524
|
+
}).then((result) => createFeishuReplyDeliveryResult({
|
|
2525
|
+
results: [result],
|
|
2526
|
+
visibleReplySent: true,
|
|
2527
|
+
content: resolvedText,
|
|
2528
|
+
kind: "card"
|
|
2529
|
+
})), resolvedText);
|
|
2530
|
+
markVisibleReplySent();
|
|
2531
|
+
return mergeFeishuReplyDeliveryResults(deliveredResults, resolvedText);
|
|
2532
|
+
}
|
|
2546
2533
|
if (shouldDeliverText) {
|
|
2547
|
-
if (info?.kind === "block") {
|
|
2548
|
-
if (
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
useCard: false,
|
|
2554
|
-
infoKind: "block",
|
|
2555
|
-
firstChunkMentions,
|
|
2556
|
-
chunkMentions: requiredMentionTargets,
|
|
2557
|
-
sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
|
|
2558
|
-
cfg,
|
|
2559
|
-
to: sendTarget,
|
|
2560
|
-
text: chunk,
|
|
2561
|
-
replyToMessageId: sendReplyToMessageId,
|
|
2562
|
-
replyInThread: effectiveReplyInThread,
|
|
2563
|
-
allowTopLevelReplyFallback,
|
|
2564
|
-
accountId,
|
|
2565
|
-
...mentions ? { mentions } : {}
|
|
2566
|
-
})
|
|
2567
|
-
}));
|
|
2568
|
-
sentIndependentBlockText = true;
|
|
2569
|
-
if (hasMedia) await collectMediaDelivery(payload);
|
|
2570
|
-
}
|
|
2571
|
-
return mergeFeishuReplyDeliveryResults(deliveredResults, text);
|
|
2534
|
+
if (hasPresentationFallback || info?.kind === "block" && !useStreamingCard) {
|
|
2535
|
+
if (hasPresentationFallback || coreBlockStreamingEnabled) {
|
|
2536
|
+
const firstChunkMentions = info?.kind === "final" || info?.kind === "block" && !sentIndependentBlockText ? mentionTargets : void 0;
|
|
2537
|
+
await collectDelivery(sendPostReply(text, info?.kind, firstChunkMentions));
|
|
2538
|
+
if (info?.kind === "block") sentIndependentBlockText = true;
|
|
2539
|
+
if (hasMedia) await collectDelivery(sendMediaReplies(payload));
|
|
2572
2540
|
}
|
|
2541
|
+
return mergeFeishuReplyDeliveryResults(deliveredResults, text);
|
|
2542
|
+
}
|
|
2543
|
+
if (info?.kind === "block") {
|
|
2573
2544
|
startStreaming();
|
|
2574
2545
|
if (streamingStartPromise) await streamingStartPromise;
|
|
2575
2546
|
}
|
|
@@ -2578,7 +2549,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2578
2549
|
if (streamingStartPromise) await streamingStartPromise;
|
|
2579
2550
|
}
|
|
2580
2551
|
const shouldStreamText = info?.kind === "block" || info?.kind === "final";
|
|
2581
|
-
const matchingInFlightClose = info?.kind === "final" && inFlightStreamingClose?.content === text ? inFlightStreamingClose : void 0;
|
|
2552
|
+
const matchingInFlightClose = info?.kind === "final" && inFlightStreamingClose?.disposition === "closed" && inFlightStreamingClose.content === text ? inFlightStreamingClose : void 0;
|
|
2582
2553
|
const ownerGeneration = activeStreamingGeneration ?? matchingInFlightClose?.generation;
|
|
2583
2554
|
if (shouldStreamText && ownerGeneration !== void 0 && (streaming?.isActive() || matchingInFlightClose !== void 0)) {
|
|
2584
2555
|
if (activeStreamingGeneration !== void 0) {
|
|
@@ -2595,7 +2566,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2595
2566
|
}
|
|
2596
2567
|
}
|
|
2597
2568
|
if (hasMedia) try {
|
|
2598
|
-
await
|
|
2569
|
+
await collectDelivery(sendMediaReplies(payload));
|
|
2599
2570
|
} catch (error) {
|
|
2600
2571
|
await throwStreamingDeliveryFailure({
|
|
2601
2572
|
error,
|
|
@@ -2613,6 +2584,8 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2613
2584
|
text,
|
|
2614
2585
|
useCard: true,
|
|
2615
2586
|
infoKind: info?.kind,
|
|
2587
|
+
header: cardHeader,
|
|
2588
|
+
note: cardNote,
|
|
2616
2589
|
chunkMentions: requiredMentionTargets,
|
|
2617
2590
|
sendChunk: async ({ chunk, mentions }) => await sendStructuredCardFeishu({
|
|
2618
2591
|
cfg,
|
|
@@ -2629,27 +2602,12 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2629
2602
|
}));
|
|
2630
2603
|
} else {
|
|
2631
2604
|
const firstChunkMentions = info?.kind === "final" && mentionTargets?.length ? mentionTargets : void 0;
|
|
2632
|
-
deliveredResults.push(await
|
|
2633
|
-
text,
|
|
2634
|
-
useCard: false,
|
|
2635
|
-
infoKind: info?.kind,
|
|
2636
|
-
firstChunkMentions,
|
|
2637
|
-
chunkMentions: requiredMentionTargets,
|
|
2638
|
-
sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
|
|
2639
|
-
cfg,
|
|
2640
|
-
to: sendTarget,
|
|
2641
|
-
text: chunk,
|
|
2642
|
-
replyToMessageId: sendReplyToMessageId,
|
|
2643
|
-
replyInThread: effectiveReplyInThread,
|
|
2644
|
-
allowTopLevelReplyFallback,
|
|
2645
|
-
accountId,
|
|
2646
|
-
...mentions ? { mentions } : {}
|
|
2647
|
-
})
|
|
2648
|
-
}));
|
|
2605
|
+
deliveredResults.push(await sendPostReply(text, info?.kind, firstChunkMentions));
|
|
2649
2606
|
}
|
|
2650
2607
|
}
|
|
2651
|
-
if (hasMedia) await
|
|
2652
|
-
const
|
|
2608
|
+
if (hasMedia) await collectDelivery(sendMediaReplies(payload, !shouldDeliverText && !ttsTextAlreadyVisible && hasVoiceMedia && hasText ? { fallbackText: text } : void 0));
|
|
2609
|
+
const deliveredContent = hasVoiceMedia ? deliveredResults.at(-1)?.content ?? text : text;
|
|
2610
|
+
const result = mergeFeishuReplyDeliveryResults(deliveredResults, deliveredContent);
|
|
2653
2611
|
if (priorClosedStreamingSettlement?.error !== void 0) throw createFeishuPartialReplyDeliveryError(isChannelPartialDeliveryError(priorClosedStreamingSettlement.error) && priorClosedStreamingSettlement.error instanceof Error ? priorClosedStreamingSettlement.error.cause ?? priorClosedStreamingSettlement.error : priorClosedStreamingSettlement.error, result);
|
|
2654
2612
|
return result;
|
|
2655
2613
|
},
|
|
@@ -2697,7 +2655,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2697
2655
|
ensureNoVisibleReplyFallback,
|
|
2698
2656
|
getVisibleReplyState: () => ({
|
|
2699
2657
|
visibleReplySent,
|
|
2700
|
-
skippedFinalReason
|
|
2658
|
+
skippedFinalReason: replyOutcome?.kind === "skipped" || replyOutcome?.kind === "suppressed" ? replyOutcome.reason : null
|
|
2701
2659
|
})
|
|
2702
2660
|
};
|
|
2703
2661
|
}
|
|
@@ -2715,7 +2673,7 @@ function getFeishuSyntheticDirectPreDispatchTarget(event) {
|
|
|
2715
2673
|
//#endregion
|
|
2716
2674
|
//#region extensions/feishu/src/bot.ts
|
|
2717
2675
|
const permissionErrorNotifiedAt = /* @__PURE__ */ new Map();
|
|
2718
|
-
const PERMISSION_ERROR_COOLDOWN_MS =
|
|
2676
|
+
const PERMISSION_ERROR_COOLDOWN_MS = 3e5;
|
|
2719
2677
|
function shouldSendNoVisibleReplyFallback(dispatchResult) {
|
|
2720
2678
|
const emptyEligibleDispatch = dispatchResult.noVisibleReplyFallbackEligible === true && dispatchResult.settledReceipt?.anyVisibleDelivered !== true;
|
|
2721
2679
|
const finalFailedBeforeSend = (dispatchResult.settledReceipt?.counts.final.failedBeforeSend ?? 0) > 0;
|
|
@@ -2724,10 +2682,11 @@ function shouldSendNoVisibleReplyFallback(dispatchResult) {
|
|
|
2724
2682
|
function isFeishuTopicSessionScope(scope) {
|
|
2725
2683
|
return scope === "group_topic" || scope === "group_topic_sender";
|
|
2726
2684
|
}
|
|
2727
|
-
async function
|
|
2728
|
-
if (params.messageType !== "audio"
|
|
2685
|
+
async function resolveFeishuAudioTranscript(params) {
|
|
2686
|
+
if (params.messageType !== "audio") return;
|
|
2729
2687
|
const audioMedia = params.mediaList.filter((media) => Boolean(media.path) && (media.kind === "audio" || media.contentType?.startsWith("audio/")));
|
|
2730
2688
|
if (audioMedia.length === 0) return;
|
|
2689
|
+
if (params.content.trim()) return params.content;
|
|
2731
2690
|
try {
|
|
2732
2691
|
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-BBfC9elv.js");
|
|
2733
2692
|
return await transcribeFirstAudio({
|
|
@@ -2922,32 +2881,34 @@ async function handleFeishuMessage(params) {
|
|
|
2922
2881
|
}
|
|
2923
2882
|
}
|
|
2924
2883
|
let permissionErrorForAgent;
|
|
2925
|
-
if (feishuCfg?.resolveSenderNames ?? true)
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
permissionErrorNotifiedAt.
|
|
2950
|
-
|
|
2884
|
+
if (feishuCfg?.resolveSenderNames ?? true) {
|
|
2885
|
+
if (ctx.senderType === "bot") {
|
|
2886
|
+
const senderName = await resolveFeishuBotName({
|
|
2887
|
+
account,
|
|
2888
|
+
openId: ctx.senderOpenId,
|
|
2889
|
+
log
|
|
2890
|
+
});
|
|
2891
|
+
if (senderName) ctx = {
|
|
2892
|
+
...ctx,
|
|
2893
|
+
senderName
|
|
2894
|
+
};
|
|
2895
|
+
} else {
|
|
2896
|
+
const senderResult = await resolveFeishuSenderName({
|
|
2897
|
+
account,
|
|
2898
|
+
senderId: ctx.senderOpenId,
|
|
2899
|
+
log
|
|
2900
|
+
});
|
|
2901
|
+
if (senderResult.name) ctx = {
|
|
2902
|
+
...ctx,
|
|
2903
|
+
senderName: senderResult.name
|
|
2904
|
+
};
|
|
2905
|
+
if (senderResult.permissionError) {
|
|
2906
|
+
const appKey = account.appId ?? "default";
|
|
2907
|
+
const now = Date.now();
|
|
2908
|
+
if (now - (permissionErrorNotifiedAt.get(appKey) ?? 0) > PERMISSION_ERROR_COOLDOWN_MS) {
|
|
2909
|
+
permissionErrorNotifiedAt.set(appKey, now);
|
|
2910
|
+
permissionErrorForAgent = senderResult.permissionError;
|
|
2911
|
+
}
|
|
2951
2912
|
}
|
|
2952
2913
|
}
|
|
2953
2914
|
}
|
|
@@ -3335,7 +3296,7 @@ async function handleFeishuMessage(params) {
|
|
|
3335
3296
|
log(`feishu[${account.accountId}]: skipping empty message (no text, no media, no quoted) from ${ctx.senderOpenId}`);
|
|
3336
3297
|
return;
|
|
3337
3298
|
}
|
|
3338
|
-
const audioTranscript = await
|
|
3299
|
+
const audioTranscript = await resolveFeishuAudioTranscript({
|
|
3339
3300
|
cfg: effectiveCfg,
|
|
3340
3301
|
mediaList,
|
|
3341
3302
|
content: ctx.content,
|
|
@@ -3343,8 +3304,8 @@ async function handleFeishuMessage(params) {
|
|
|
3343
3304
|
chatType: isGroup ? "group" : "direct",
|
|
3344
3305
|
log
|
|
3345
3306
|
});
|
|
3346
|
-
const
|
|
3347
|
-
const inboundMedia = await toInboundMediaFactsWithMetadata(mediaList, { transcribed: (_media, index) => index ===
|
|
3307
|
+
const transcribedAudioIndex = audioTranscript === void 0 ? -1 : mediaList.findIndex((media) => media.kind === "audio" || media.contentType?.startsWith("audio/"));
|
|
3308
|
+
const inboundMedia = await toInboundMediaFactsWithMetadata(mediaList, { transcribed: (_media, index) => index === transcribedAudioIndex });
|
|
3348
3309
|
const requiredMentionTargets = isGroup && ctx.senderType === "bot" && ctx.senderOpenId ? [{
|
|
3349
3310
|
openId: ctx.senderOpenId,
|
|
3350
3311
|
name: ctx.senderName ?? ctx.senderOpenId,
|
|
@@ -4018,8 +3979,8 @@ function createApprovalCard(params) {
|
|
|
4018
3979
|
}
|
|
4019
3980
|
//#endregion
|
|
4020
3981
|
//#region extensions/feishu/src/card-action.ts
|
|
4021
|
-
const FEISHU_APPROVAL_CARD_TTL_MS =
|
|
4022
|
-
const FEISHU_CARD_ACTION_TOKEN_TTL_MS =
|
|
3982
|
+
const FEISHU_APPROVAL_CARD_TTL_MS = 3e5;
|
|
3983
|
+
const FEISHU_CARD_ACTION_TOKEN_TTL_MS = 9e5;
|
|
4023
3984
|
function pruneProcessedCardActionTokens(now) {
|
|
4024
3985
|
const validNow = asDateTimestampMs(now);
|
|
4025
3986
|
if (validNow === void 0) {
|
|
@@ -4111,7 +4072,7 @@ async function dispatchSyntheticCommand(params) {
|
|
|
4111
4072
|
});
|
|
4112
4073
|
}
|
|
4113
4074
|
const resolvedChatTypeCache = resolvedCardActionChatTypes;
|
|
4114
|
-
const CHAT_TYPE_CACHE_TTL_MS =
|
|
4075
|
+
const CHAT_TYPE_CACHE_TTL_MS = 18e5;
|
|
4115
4076
|
const CHAT_TYPE_CACHE_MAX_SIZE = 5e3;
|
|
4116
4077
|
function pruneChatTypeCache(now) {
|
|
4117
4078
|
const validNow = asDateTimestampMs(now);
|
|
@@ -4366,7 +4327,8 @@ function inspectFeishuIngressEnvelope(rawEnvelope, encryptKey, allowInvalidLane
|
|
|
4366
4327
|
if (!eventId) throw new FeishuIngressPermanentError("invalid-event", `Feishu ${eventType} envelope is missing header.event_id.`);
|
|
4367
4328
|
const event = nestedEvent ?? envelope;
|
|
4368
4329
|
if (eventType === "im.message.receive_v1") {
|
|
4369
|
-
const
|
|
4330
|
+
const message = isRecord$1(event.message) ? event.message : null;
|
|
4331
|
+
const chatId = normalizeNullableString(message?.chat_id);
|
|
4370
4332
|
if (!chatId) {
|
|
4371
4333
|
if (allowInvalidLane) return {
|
|
4372
4334
|
eventId,
|
|
@@ -4490,6 +4452,7 @@ function buildFeishuFlushIngressLifecycle(sources, options) {
|
|
|
4490
4452
|
handedOff = true;
|
|
4491
4453
|
transportLifecycle.onDeferred();
|
|
4492
4454
|
},
|
|
4455
|
+
onDeferredHeartbeat: () => transportLifecycle.onDeferredHeartbeat?.(),
|
|
4493
4456
|
onAdoptionFinalizing: () => {
|
|
4494
4457
|
transportLifecycle.onAdoptionFinalizing();
|
|
4495
4458
|
},
|
|
@@ -4642,7 +4605,7 @@ const FEISHU_WEBHOOK_RATE_LIMIT_FALLBACK_DEFAULTS = {
|
|
|
4642
4605
|
};
|
|
4643
4606
|
const FEISHU_WEBHOOK_ANOMALY_FALLBACK_DEFAULTS = {
|
|
4644
4607
|
maxTrackedKeys: 4096,
|
|
4645
|
-
ttlMs:
|
|
4608
|
+
ttlMs: 216e5,
|
|
4646
4609
|
logEvery: 25
|
|
4647
4610
|
};
|
|
4648
4611
|
function coercePositiveInt(value, fallback) {
|
|
@@ -4673,7 +4636,7 @@ const httpServers = /* @__PURE__ */ new Map();
|
|
|
4673
4636
|
const botOpenIds = /* @__PURE__ */ new Map();
|
|
4674
4637
|
const botNames = /* @__PURE__ */ new Map();
|
|
4675
4638
|
const botIdentityRevisions = /* @__PURE__ */ new Map();
|
|
4676
|
-
const FEISHU_WEBHOOK_MAX_BODY_BYTES =
|
|
4639
|
+
const FEISHU_WEBHOOK_MAX_BODY_BYTES = 65536;
|
|
4677
4640
|
const FEISHU_WEBHOOK_BODY_TIMEOUT_MS = 5e3;
|
|
4678
4641
|
const FEISHU_HTTP_SERVER_CLOSE_TIMEOUT_MS = 5e3;
|
|
4679
4642
|
const feishuWebhookRateLimitDefaults = resolveFeishuWebhookRateLimitDefaults(WEBHOOK_RATE_LIMIT_DEFAULTS);
|
|
@@ -4821,7 +4784,7 @@ function startBotIdentityRecovery(params) {
|
|
|
4821
4784
|
}
|
|
4822
4785
|
//#endregion
|
|
4823
4786
|
//#region extensions/feishu/src/card-ux-launcher.ts
|
|
4824
|
-
const FEISHU_QUICK_ACTION_CARD_TTL_MS =
|
|
4787
|
+
const FEISHU_QUICK_ACTION_CARD_TTL_MS = 6e5;
|
|
4825
4788
|
const QUICK_ACTION_MENU_KEYS = /* @__PURE__ */ new Set([
|
|
4826
4789
|
"quick-actions",
|
|
4827
4790
|
"quick_actions",
|
|
@@ -5043,10 +5006,11 @@ function createFeishuCommentReplyDispatcher(params) {
|
|
|
5043
5006
|
channel: "feishu",
|
|
5044
5007
|
accountId: params.accountId
|
|
5045
5008
|
});
|
|
5046
|
-
const
|
|
5009
|
+
const account = resolveFeishuRuntimeAccount({
|
|
5047
5010
|
cfg: params.cfg,
|
|
5048
5011
|
accountId: params.accountId
|
|
5049
|
-
})
|
|
5012
|
+
});
|
|
5013
|
+
const client = createFeishuClient(account);
|
|
5050
5014
|
const textChunkLimit = core.channel.text.resolveTextChunkLimit(params.cfg, "feishu", params.accountId, { fallbackLimit: 4e3 });
|
|
5051
5015
|
const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "feishu", params.accountId);
|
|
5052
5016
|
const typingReaction = createCommentTypingReactionLifecycle({
|
|
@@ -5075,7 +5039,7 @@ function createFeishuCommentReplyDispatcher(params) {
|
|
|
5075
5039
|
if (info.kind !== "final") return noVisibleFeishuReplyDelivery;
|
|
5076
5040
|
const reply = resolveSendableOutboundReplyParts(payload);
|
|
5077
5041
|
const { presentation } = resolveFeishuRichReply(payload);
|
|
5078
|
-
let { text } =
|
|
5042
|
+
let { commentText: text } = buildFeishuPresentationFallback({
|
|
5079
5043
|
text: reply.text,
|
|
5080
5044
|
presentation
|
|
5081
5045
|
});
|
|
@@ -5634,7 +5598,7 @@ async function resolveDriveCommentEventCore(params) {
|
|
|
5634
5598
|
return null;
|
|
5635
5599
|
}
|
|
5636
5600
|
const context = await fetchDriveCommentContext({
|
|
5637
|
-
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-
|
|
5601
|
+
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-cCMNFBKg.js").then((n) => n.t)).resolveFeishuAccount({
|
|
5638
5602
|
cfg,
|
|
5639
5603
|
accountId
|
|
5640
5604
|
})),
|
|
@@ -5994,7 +5958,8 @@ async function handleFeishuCommentEvent(params) {
|
|
|
5994
5958
|
})
|
|
5995
5959
|
}
|
|
5996
5960
|
});
|
|
5997
|
-
const
|
|
5961
|
+
const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
|
|
5962
|
+
const counts = resolveInboundReplyDispatchCounts(dispatchResult);
|
|
5998
5963
|
log(`feishu[${account.accountId}]: drive comment dispatch complete (replies=${counts.final}, session=${commentSessionKey})`);
|
|
5999
5964
|
} finally {
|
|
6000
5965
|
cleanupTypingReaction();
|
|
@@ -6019,7 +5984,7 @@ async function handleFeishuCommentEvent(params) {
|
|
|
6019
5984
|
* resolves) keeps later same-chat messages in `queued` state until the
|
|
6020
5985
|
* gateway is restarted. See #70133.
|
|
6021
5986
|
*/
|
|
6022
|
-
const DEFAULT_TASK_TIMEOUT_MS =
|
|
5987
|
+
const DEFAULT_TASK_TIMEOUT_MS = 3e5;
|
|
6023
5988
|
function createSequentialQueue(options = {}) {
|
|
6024
5989
|
const queues = /* @__PURE__ */ new Map();
|
|
6025
5990
|
const taskTimeoutMs = options.taskTimeoutMs ?? DEFAULT_TASK_TIMEOUT_MS;
|
|
@@ -6165,8 +6130,9 @@ function resolveFeishuDebounceMentions(params) {
|
|
|
6165
6130
|
return botMentions.length > 0 ? botMentions : void 0;
|
|
6166
6131
|
}
|
|
6167
6132
|
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 }) {
|
|
6168
|
-
const
|
|
6169
|
-
|
|
6133
|
+
const readConfig = createRuntimeConfigReader(cfg);
|
|
6134
|
+
const resolveDebounceMs = () => channelRuntime.debounce.resolveInboundDebounceMs({
|
|
6135
|
+
cfg: readConfig(),
|
|
6170
6136
|
channel: "feishu"
|
|
6171
6137
|
});
|
|
6172
6138
|
const log = runtime?.log ?? console.log;
|
|
@@ -6225,13 +6191,14 @@ function createFeishuMessageReceiveHandler({ cfg, channelRuntime, accountId, run
|
|
|
6225
6191
|
}
|
|
6226
6192
|
};
|
|
6227
6193
|
const inboundDebouncer = channelRuntime.debounce.createInboundDebouncer({
|
|
6228
|
-
debounceMs:
|
|
6194
|
+
debounceMs: resolveDebounceMs(),
|
|
6195
|
+
resolveDebounceMs,
|
|
6229
6196
|
buildKey: ({ event }) => {
|
|
6230
6197
|
const chatId = event.message.chat_id?.trim();
|
|
6231
6198
|
const senderId = resolveSenderDebounceId(event);
|
|
6232
6199
|
if (!chatId || !senderId) return null;
|
|
6233
|
-
const
|
|
6234
|
-
return `feishu:${accountId}:${chatId}:${
|
|
6200
|
+
const threadId = event.message.root_id?.trim() || event.message.thread_id?.trim();
|
|
6201
|
+
return `feishu:${accountId}:${chatId}:${threadId ? `thread:${threadId}` : "chat"}:${senderId}`;
|
|
6235
6202
|
},
|
|
6236
6203
|
shouldDebounce: ({ event }) => {
|
|
6237
6204
|
if (event.message.message_type !== "text") return false;
|
|
@@ -6427,7 +6394,8 @@ function isFeishuWebhookSignatureValid(params) {
|
|
|
6427
6394
|
const nonce = Array.isArray(nonceHeader) ? nonceHeader[0] : nonceHeader;
|
|
6428
6395
|
const signature = Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader;
|
|
6429
6396
|
if (!timestamp || !nonce || !signature) return false;
|
|
6430
|
-
|
|
6397
|
+
const computedSignature = crypto.createHash("sha256").update(timestamp + nonce + encryptKey + params.rawBody).digest("hex");
|
|
6398
|
+
return safeEqualSecret(computedSignature, signature);
|
|
6431
6399
|
}
|
|
6432
6400
|
function respondText(res, statusCode, body) {
|
|
6433
6401
|
res.statusCode = statusCode;
|
|
@@ -6617,8 +6585,10 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
|
|
|
6617
6585
|
respondText(res, 404, "Not Found");
|
|
6618
6586
|
return;
|
|
6619
6587
|
}
|
|
6620
|
-
res.
|
|
6588
|
+
res.once("close", () => {
|
|
6621
6589
|
recordWebhookStatus(runtime, accountId, path, res.statusCode);
|
|
6590
|
+
});
|
|
6591
|
+
res.once("finish", () => {
|
|
6622
6592
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
6623
6593
|
const inboundAt = Date.now();
|
|
6624
6594
|
statusSink?.({
|
|
@@ -6627,16 +6597,17 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
|
|
|
6627
6597
|
});
|
|
6628
6598
|
}
|
|
6629
6599
|
});
|
|
6600
|
+
const rateLimitKey = buildFeishuWebhookRateLimitKey({
|
|
6601
|
+
accountId,
|
|
6602
|
+
path,
|
|
6603
|
+
clientIp: resolveRequestClientIp(req)
|
|
6604
|
+
});
|
|
6630
6605
|
if (!applyBasicWebhookRequestGuards({
|
|
6631
6606
|
req,
|
|
6632
6607
|
res,
|
|
6633
6608
|
allowMethods: ["POST"],
|
|
6634
6609
|
rateLimiter: feishuWebhookRateLimiter,
|
|
6635
|
-
rateLimitKey
|
|
6636
|
-
accountId,
|
|
6637
|
-
path,
|
|
6638
|
-
clientIp: resolveRequestClientIp(req)
|
|
6639
|
-
}),
|
|
6610
|
+
rateLimitKey,
|
|
6640
6611
|
nowMs: Date.now(),
|
|
6641
6612
|
requireJsonContentType: true
|
|
6642
6613
|
})) return;
|
|
@@ -6869,7 +6840,7 @@ async function resolveReactionSyntheticEvent(params) {
|
|
|
6869
6840
|
const senderUserId = event.user_id?.user_id?.trim();
|
|
6870
6841
|
const senderId = senderOpenId || senderUserId;
|
|
6871
6842
|
if (!emoji || !messageId || !senderId) return null;
|
|
6872
|
-
const { resolveFeishuAccount } = await import("./accounts-
|
|
6843
|
+
const { resolveFeishuAccount } = await import("./accounts-cCMNFBKg.js").then((n) => n.t);
|
|
6873
6844
|
const reactionNotifications = resolveFeishuAccount({
|
|
6874
6845
|
cfg,
|
|
6875
6846
|
accountId
|