@openclaw/feishu 2026.5.31-beta.3 → 2026.6.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +1 -1
- package/dist/{app-registration-DCy5-X_C.js → app-registration-tgQJUcv3.js} +3 -1
- package/dist/{channel-YU5bdC_o.js → channel-B3ngoThy.js} +8 -7
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BBY1Yd01.js → channel.runtime-D_5rduJm.js} +1 -1
- package/dist/legacy-state-migrations-api.js +1 -1
- package/dist/{monitor-CMwQEm1t.js → monitor-CKQ1C1aY.js} +1 -1
- package/dist/{monitor.account-DrLtrcbg.js → monitor.account-CZN6TDMA.js} +122 -37
- package/dist/setup-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as listEnabledFeishuAccounts } from "./accounts-Bpe6CjpS.js";
|
|
2
|
-
import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-
|
|
2
|
+
import { a as setFeishuNamedAccountEnabled, i as feishuSetupAdapter, n as feishuSetupWizard, r as runFeishuLogin, t as feishuPlugin } from "./channel-B3ngoThy.js";
|
|
3
3
|
import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-DuL575sn.js";
|
|
4
4
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
5
5
|
import { r as createFeishuClient } from "./client-BhMNZBJD.js";
|
|
@@ -182,7 +182,9 @@ async function getAppOwnerOpenId(params) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
function sleep(ms) {
|
|
185
|
-
return new Promise((resolve) =>
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
setTimeout(resolve, ms);
|
|
187
|
+
});
|
|
186
188
|
}
|
|
187
189
|
function sleepRegistrationPollInterval(intervalSeconds) {
|
|
188
190
|
return sleep(finiteSecondsToTimerSafeMilliseconds(intervalSeconds) ?? finiteSecondsToTimerSafeMilliseconds(DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS) ?? REQUEST_TIMEOUT_MS);
|
|
@@ -574,7 +574,7 @@ function inspectSessionTranscript(params) {
|
|
|
574
574
|
reason: "not a file"
|
|
575
575
|
};
|
|
576
576
|
if (stat.size > SESSION_FILE_INSPECTION_MAX_BYTES) return null;
|
|
577
|
-
let raw
|
|
577
|
+
let raw;
|
|
578
578
|
try {
|
|
579
579
|
raw = fs.readFileSync(params.transcriptPath, "utf-8");
|
|
580
580
|
} catch {
|
|
@@ -1433,7 +1433,7 @@ function applyNewAppSecurityPolicy(cfg, accountId, openId, groupPolicy) {
|
|
|
1433
1433
|
}
|
|
1434
1434
|
let appRegistrationModulePromise = null;
|
|
1435
1435
|
const loadAppRegistrationModule = async () => {
|
|
1436
|
-
appRegistrationModulePromise ??= import("./app-registration-
|
|
1436
|
+
appRegistrationModulePromise ??= import("./app-registration-tgQJUcv3.js");
|
|
1437
1437
|
return await appRegistrationModulePromise;
|
|
1438
1438
|
};
|
|
1439
1439
|
async function promptFeishuDomain(params) {
|
|
@@ -1504,7 +1504,7 @@ async function runNewAppFlow(params) {
|
|
|
1504
1504
|
const { prompter, options } = params;
|
|
1505
1505
|
let next = params.cfg;
|
|
1506
1506
|
const targetAccountId = resolveDefaultFeishuAccountId(next);
|
|
1507
|
-
let appId
|
|
1507
|
+
let appId;
|
|
1508
1508
|
let appSecret = null;
|
|
1509
1509
|
let appSecretProbeValue = null;
|
|
1510
1510
|
let scanDomain;
|
|
@@ -1520,7 +1520,6 @@ async function runNewAppFlow(params) {
|
|
|
1520
1520
|
if (scanResult) {
|
|
1521
1521
|
appId = scanResult.appId;
|
|
1522
1522
|
appSecret = scanResult.appSecret;
|
|
1523
|
-
appSecretProbeValue = scanResult.appSecret;
|
|
1524
1523
|
scanDomain = scanResult.domain;
|
|
1525
1524
|
scanOpenId = scanResult.openId;
|
|
1526
1525
|
} else {
|
|
@@ -1575,7 +1574,9 @@ async function runNewAppFlow(params) {
|
|
|
1575
1574
|
initialValue: "allowlist"
|
|
1576
1575
|
});
|
|
1577
1576
|
const configProgress = prompter.progress(t("wizard.feishu.configuring"));
|
|
1578
|
-
await new Promise((resolve) =>
|
|
1577
|
+
await new Promise((resolve) => {
|
|
1578
|
+
setTimeout(resolve, 50);
|
|
1579
|
+
});
|
|
1579
1580
|
if (appId && appSecret) next = patchFeishuConfig(next, targetAccountId, {
|
|
1580
1581
|
appId,
|
|
1581
1582
|
appSecret,
|
|
@@ -1734,7 +1735,7 @@ const meta = {
|
|
|
1734
1735
|
order: 70,
|
|
1735
1736
|
preferSessionLookupForAnnounceTarget: true
|
|
1736
1737
|
};
|
|
1737
|
-
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
1738
|
+
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-D_5rduJm.js"), "feishuChannelRuntime");
|
|
1738
1739
|
function toFeishuMessageSendResult(result, kind) {
|
|
1739
1740
|
const receipt = result.receipt ?? createFeishuSendReceipt({
|
|
1740
1741
|
messageId: result.messageId,
|
|
@@ -2550,7 +2551,7 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2550
2551
|
})
|
|
2551
2552
|
}),
|
|
2552
2553
|
gateway: { startAccount: async (ctx) => {
|
|
2553
|
-
const { monitorFeishuProvider } = await import("./monitor-
|
|
2554
|
+
const { monitorFeishuProvider } = await import("./monitor-CKQ1C1aY.js");
|
|
2554
2555
|
const account = resolveFeishuRuntimeAccount({
|
|
2555
2556
|
cfg: ctx.cfg,
|
|
2556
2557
|
accountId: ctx.accountId
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as feishuPlugin } from "./channel-
|
|
1
|
+
import { t as feishuPlugin } from "./channel-B3ngoThy.js";
|
|
2
2
|
export { feishuPlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount, y as parseFeishuCommentTarget } from "./accounts-Bpe6CjpS.js";
|
|
2
|
-
import { h as listFeishuDirectoryPeers, m as listFeishuDirectoryGroups, o as buildFeishuPresentationCardElements } from "./channel-
|
|
2
|
+
import { h as listFeishuDirectoryPeers, m as listFeishuDirectoryGroups, o as buildFeishuPresentationCardElements } from "./channel-B3ngoThy.js";
|
|
3
3
|
import { r as createFeishuClient } from "./client-BhMNZBJD.js";
|
|
4
4
|
import { c as getChatInfo, l as getChatMembers, r as cleanupAmbientCommentTypingReaction, t as deliverCommentThreadText, u as getFeishuMemberInfo } from "./drive-BIrffRwc.js";
|
|
5
5
|
import { chunkTextForOutbound } from "./runtime-api.js";
|
|
@@ -42,7 +42,7 @@ function buildMigrationEntries(namespace, sourcePath, now) {
|
|
|
42
42
|
}
|
|
43
43
|
const detectFeishuLegacyStateMigrations = ({ stateDir }) => {
|
|
44
44
|
const dedupDir = path.join(stateDir, "feishu", "dedup");
|
|
45
|
-
let entries
|
|
45
|
+
let entries;
|
|
46
46
|
try {
|
|
47
47
|
entries = fs.readdirSync(dedupDir, { withFileTypes: true });
|
|
48
48
|
} catch {
|
|
@@ -3,7 +3,7 @@ import { l as fetchBotIdentityForMonitor } from "./monitor.state-r4OLFBfg.js";
|
|
|
3
3
|
//#region extensions/feishu/src/monitor.ts
|
|
4
4
|
let monitorAccountRuntimePromise;
|
|
5
5
|
async function loadMonitorAccountRuntime() {
|
|
6
|
-
monitorAccountRuntimePromise ??= import("./monitor.account-
|
|
6
|
+
monitorAccountRuntimePromise ??= import("./monitor.account-CZN6TDMA.js");
|
|
7
7
|
return await monitorAccountRuntimePromise;
|
|
8
8
|
}
|
|
9
9
|
async function monitorFeishuProvider(opts = {}) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as buildFeishuCommentTarget, f as isRecord$1, h as readString, l as encodeQuery, m as parseCommentContentElements, p as normalizeString, s as resolveFeishuRuntimeAccount, u as extractReplyText, v as normalizeCommentFileType } from "./accounts-Bpe6CjpS.js";
|
|
2
2
|
import { i as resolveReceiveIdType } from "./targets-BUjQ1TcA.js";
|
|
3
|
-
import { c as normalizeFeishuAllowEntry, d as resolveFeishuGroupConversationIngressAccess, f as resolveFeishuGroupSenderActivationIngressAccess, l as resolveFeishuDmIngressAccess, p as resolveFeishuReplyPolicy, s as hasExplicitFeishuGroupConfig, u as resolveFeishuGroupConfig } from "./channel-
|
|
3
|
+
import { c as normalizeFeishuAllowEntry, d as resolveFeishuGroupConversationIngressAccess, f as resolveFeishuGroupSenderActivationIngressAccess, l as resolveFeishuDmIngressAccess, p as resolveFeishuReplyPolicy, s as hasExplicitFeishuGroupConfig, u as resolveFeishuGroupConfig } from "./channel-B3ngoThy.js";
|
|
4
4
|
import { c as decodeFeishuCardAction, o as buildFeishuCardActionTextFallback, s as createFeishuCardInteractionEnvelope } from "./send-result-D9rgEUlm.js";
|
|
5
5
|
import { t as buildFeishuConversationId } from "./conversation-id-DuL575sn.js";
|
|
6
6
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
@@ -49,7 +49,7 @@ function resolveFeishuGroupSession(params) {
|
|
|
49
49
|
const legacyTopicSessionMode = groupConfig?.topicSessionMode ?? feishuCfg?.topicSessionMode ?? "disabled";
|
|
50
50
|
const groupSessionScope = groupConfig?.groupSessionScope ?? feishuCfg?.groupSessionScope ?? (legacyTopicSessionMode === "enabled" ? "group_topic" : "group");
|
|
51
51
|
const topicScope = groupSessionScope === "group_topic" || groupSessionScope === "group_topic_sender" ? (chatType === "topic_group" ? normalizedThreadId ?? normalizedRootId : void 0) ?? normalizedRootId ?? normalizedThreadId ?? (replyInThread ? messageId : null) : null;
|
|
52
|
-
let peerId
|
|
52
|
+
let peerId;
|
|
53
53
|
switch (groupSessionScope) {
|
|
54
54
|
case "group_sender":
|
|
55
55
|
peerId = buildFeishuConversationId({
|
|
@@ -1176,17 +1176,21 @@ var FeishuStreamingSession = class {
|
|
|
1176
1176
|
}).catch((e) => this.log?.(`Note update failed: ${String(e)}`));
|
|
1177
1177
|
}
|
|
1178
1178
|
async close(finalText, options) {
|
|
1179
|
-
if (!this.state || this.closed) return;
|
|
1179
|
+
if (!this.state || this.closed) return false;
|
|
1180
1180
|
this.closed = true;
|
|
1181
1181
|
this.clearFlushTimer();
|
|
1182
1182
|
await this.queue;
|
|
1183
1183
|
const pendingMerged = mergeStreamingText(this.state.currentText, this.pendingText ?? void 0);
|
|
1184
1184
|
const text = finalText ?? pendingMerged;
|
|
1185
1185
|
const apiBase = resolveApiBase(this.creds.domain);
|
|
1186
|
+
let visibleContentSent = Boolean(this.state.sentText.trim());
|
|
1186
1187
|
if ((text || finalText !== void 0) && text !== this.state.sentText) {
|
|
1187
1188
|
const sent = text.startsWith(this.state.sentText) ? await this.updateCardContent(resolveStreamingCardAppendContent(this.state.sentText, text), (e) => this.log?.(`Final update failed: ${String(e)}`)) : await this.replaceCardContent(text, (e) => this.log?.(`Final replace failed: ${String(e)}`));
|
|
1188
1189
|
this.state.currentText = text;
|
|
1189
|
-
if (sent)
|
|
1190
|
+
if (sent) {
|
|
1191
|
+
this.state.sentText = text;
|
|
1192
|
+
visibleContentSent = Boolean(text.trim());
|
|
1193
|
+
}
|
|
1190
1194
|
}
|
|
1191
1195
|
if (options?.note) await this.updateNoteContent(options.note);
|
|
1192
1196
|
this.state.sequence += 1;
|
|
@@ -1217,6 +1221,7 @@ var FeishuStreamingSession = class {
|
|
|
1217
1221
|
this.state = null;
|
|
1218
1222
|
this.pendingText = null;
|
|
1219
1223
|
this.log?.(`Closed streaming: cardId=${finalState.cardId}`);
|
|
1224
|
+
return visibleContentSent;
|
|
1220
1225
|
}
|
|
1221
1226
|
async discard() {
|
|
1222
1227
|
if (!this.state || this.closed) return;
|
|
@@ -1390,6 +1395,7 @@ function shouldUseCard(text) {
|
|
|
1390
1395
|
const TYPING_INDICATOR_MAX_AGE_MS = 2 * 6e4;
|
|
1391
1396
|
const MS_EPOCH_MIN = 0xe8d4a51000;
|
|
1392
1397
|
const STREAMING_START_FAILURE_BACKOFF_MS = 6e4;
|
|
1398
|
+
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.";
|
|
1393
1399
|
const streamingStartBackoffUntilByAccount = /* @__PURE__ */ new Map();
|
|
1394
1400
|
function isStreamingStartBackedOff(accountId, now = Date.now()) {
|
|
1395
1401
|
const backoffUntil = streamingStartBackoffUntilByAccount.get(accountId);
|
|
@@ -1513,6 +1519,13 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1513
1519
|
let streamingStartPromise = null;
|
|
1514
1520
|
let streamingClosedForReply = false;
|
|
1515
1521
|
let streamingCloseErroredForReply = false;
|
|
1522
|
+
let visibleReplySent = false;
|
|
1523
|
+
let skippedFinalReason = null;
|
|
1524
|
+
let idleSideEffectsPromise = Promise.resolve();
|
|
1525
|
+
let replyLifecycleStateInitialized = false;
|
|
1526
|
+
const markVisibleReplySent = () => {
|
|
1527
|
+
visibleReplySent = true;
|
|
1528
|
+
};
|
|
1516
1529
|
const formatReasoningPrefix = (thinking) => {
|
|
1517
1530
|
if (!thinking) return "";
|
|
1518
1531
|
return `> 💭 **Thinking**\n${thinking.replace(/^(?:Reasoning:|Thinking\.{0,3})\s*/u, "").replace(/^_(.*)_$/gm, "$1").split("\n").map((line) => `> ${line}`).join("\n")}`;
|
|
@@ -1599,8 +1612,9 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1599
1612
|
statusLine = "";
|
|
1600
1613
|
const text = buildCombinedStreamText(reasoningText, streamText);
|
|
1601
1614
|
const finalNote = resolveCardNote(agentId, identity, prefixContext.prefixContext);
|
|
1602
|
-
await streaming.close(text, { note: finalNote });
|
|
1603
|
-
if (
|
|
1615
|
+
const contentVisible = await streaming.close(text, { note: finalNote });
|
|
1616
|
+
if (contentVisible) markVisibleReplySent();
|
|
1617
|
+
if (contentVisible && streamText) {
|
|
1604
1618
|
deliveredFinalTexts.add(streamText);
|
|
1605
1619
|
if (options?.markClosedForReply !== false && !streamingCloseErroredForReply) streamingClosedForReply = true;
|
|
1606
1620
|
}
|
|
@@ -1626,11 +1640,14 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1626
1640
|
};
|
|
1627
1641
|
const sendChunkedTextReply = async (paramsLocal) => {
|
|
1628
1642
|
const chunkSource = paramsLocal.useCard ? paramsLocal.text : core.channel.text.convertMarkdownTables(paramsLocal.text, tableMode);
|
|
1629
|
-
const chunks = resolveTextChunksWithFallback(chunkSource, core.channel.text.chunkTextWithMode(chunkSource, textChunkLimit, chunkMode));
|
|
1630
|
-
for (const [index, chunk] of chunks.entries())
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1643
|
+
const chunks = resolveTextChunksWithFallback(chunkSource, (paramsLocal.useCard ? core.channel.text.chunkMarkdownTextWithMode : core.channel.text.chunkTextWithMode)(chunkSource, textChunkLimit, chunkMode));
|
|
1644
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
1645
|
+
await paramsLocal.sendChunk({
|
|
1646
|
+
chunk,
|
|
1647
|
+
isFirst: index === 0
|
|
1648
|
+
});
|
|
1649
|
+
markVisibleReplySent();
|
|
1650
|
+
}
|
|
1634
1651
|
if (paramsLocal.infoKind === "final") deliveredFinalTexts.add(paramsLocal.text);
|
|
1635
1652
|
};
|
|
1636
1653
|
const sendMediaReplies = async (payload, options) => {
|
|
@@ -1640,7 +1657,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1640
1657
|
mediaUrls,
|
|
1641
1658
|
caption: "",
|
|
1642
1659
|
send: async ({ mediaUrl }) => {
|
|
1643
|
-
|
|
1660
|
+
const result = await sendMediaFeishu({
|
|
1644
1661
|
cfg,
|
|
1645
1662
|
to: chatId,
|
|
1646
1663
|
mediaUrl,
|
|
@@ -1648,7 +1665,9 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1648
1665
|
replyInThread: effectiveReplyInThread,
|
|
1649
1666
|
accountId,
|
|
1650
1667
|
...payload.audioAsVoice === true ? { audioAsVoice: true } : {}
|
|
1651
|
-
})
|
|
1668
|
+
});
|
|
1669
|
+
markVisibleReplySent();
|
|
1670
|
+
if (result?.voiceIntentDegradedToFile && options?.fallbackText && !sentFallbackText) {
|
|
1652
1671
|
sentFallbackText = true;
|
|
1653
1672
|
await sendChunkedTextReply({
|
|
1654
1673
|
text: options.fallbackText,
|
|
@@ -1690,18 +1709,61 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1690
1709
|
}
|
|
1691
1710
|
});
|
|
1692
1711
|
};
|
|
1712
|
+
const ensureNoVisibleReplyFallback = async (reason) => {
|
|
1713
|
+
await idleSideEffectsPromise;
|
|
1714
|
+
if (visibleReplySent) return false;
|
|
1715
|
+
if (skippedFinalReason === "silent") {
|
|
1716
|
+
params.runtime.log?.(`feishu[${account.accountId}]: no-visible-reply fallback skipped for intentional silence (${reason})`);
|
|
1717
|
+
return false;
|
|
1718
|
+
}
|
|
1719
|
+
await sendMessageFeishu({
|
|
1720
|
+
cfg,
|
|
1721
|
+
to: chatId,
|
|
1722
|
+
text: NO_VISIBLE_REPLY_FALLBACK_TEXT,
|
|
1723
|
+
replyToMessageId: sendReplyToMessageId,
|
|
1724
|
+
replyInThread: effectiveReplyInThread,
|
|
1725
|
+
allowTopLevelReplyFallback,
|
|
1726
|
+
accountId
|
|
1727
|
+
});
|
|
1728
|
+
markVisibleReplySent();
|
|
1729
|
+
params.runtime.error?.(`feishu[${account.accountId}]: sent no-visible-reply fallback (${reason})`);
|
|
1730
|
+
return true;
|
|
1731
|
+
};
|
|
1732
|
+
const queueIdleSideEffects = (options) => {
|
|
1733
|
+
const nextIdleSideEffects = idleSideEffectsPromise.then(async () => {
|
|
1734
|
+
await closeStreaming(options);
|
|
1735
|
+
await Promise.resolve(typingCallbacks?.onIdle?.());
|
|
1736
|
+
});
|
|
1737
|
+
idleSideEffectsPromise = nextIdleSideEffects.catch(() => {});
|
|
1738
|
+
return nextIdleSideEffects;
|
|
1739
|
+
};
|
|
1693
1740
|
const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
1694
1741
|
responsePrefix: prefixContext.responsePrefix,
|
|
1695
1742
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
1696
1743
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, agentId),
|
|
1744
|
+
silentReplyContext: {
|
|
1745
|
+
cfg,
|
|
1746
|
+
sessionKey: params.sessionKey,
|
|
1747
|
+
surface: "feishu",
|
|
1748
|
+
conversationType: chatId.startsWith("oc_") ? "group" : "direct"
|
|
1749
|
+
},
|
|
1750
|
+
onSkip: (_payload, info) => {
|
|
1751
|
+
if (info.kind === "final") skippedFinalReason = info.reason;
|
|
1752
|
+
},
|
|
1697
1753
|
onReplyStart: async () => {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1754
|
+
if (!replyLifecycleStateInitialized) {
|
|
1755
|
+
replyLifecycleStateInitialized = true;
|
|
1756
|
+
deliveredFinalTexts.clear();
|
|
1757
|
+
streamingClosedForReply = false;
|
|
1758
|
+
streamingCloseErroredForReply = false;
|
|
1759
|
+
visibleReplySent = false;
|
|
1760
|
+
skippedFinalReason = null;
|
|
1761
|
+
}
|
|
1701
1762
|
if (streamingEnabled && renderMode === "card") startStreaming();
|
|
1702
|
-
await typingCallbacks?.onReplyStart?.();
|
|
1763
|
+
await Promise.resolve(typingCallbacks?.onReplyStart?.());
|
|
1703
1764
|
},
|
|
1704
1765
|
deliver: async (payload, info) => {
|
|
1766
|
+
if (info?.kind === "final") skippedFinalReason = null;
|
|
1705
1767
|
const payloadText = payload.isReasoning && payload.text ? formatReasoningMessage(payload.text) : payload.text;
|
|
1706
1768
|
const reply = resolveSendableOutboundReplyParts({
|
|
1707
1769
|
...payload,
|
|
@@ -1714,21 +1776,24 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1714
1776
|
mediaUrl,
|
|
1715
1777
|
...payload.audioAsVoice === true ? { audioAsVoice: true } : {}
|
|
1716
1778
|
}));
|
|
1717
|
-
const
|
|
1718
|
-
const
|
|
1779
|
+
const finalTextExceedsStreamingLimit = info?.kind === "final" && hasText && text.length > textChunkLimit;
|
|
1780
|
+
const useStaticCard = hasText && (renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text));
|
|
1781
|
+
const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || useStaticCard);
|
|
1782
|
+
const finalTextWouldUseStreamingCard = info?.kind === "final" && hasText && streamingEnabled;
|
|
1783
|
+
const useCard = useStaticCard || useStreamingCard;
|
|
1719
1784
|
const skipTextForDuplicateFinal = info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
|
|
1720
|
-
const skipTextForClosedStreamingFinal = info?.kind === "final" && hasText && streamingClosedForReply && !streamingCloseErroredForReply &&
|
|
1785
|
+
const skipTextForClosedStreamingFinal = info?.kind === "final" && hasText && streamingClosedForReply && !streamingCloseErroredForReply && finalTextWouldUseStreamingCard;
|
|
1721
1786
|
const shouldDeliverText = hasText && !hasVoiceMedia && !skipTextForDuplicateFinal && !skipTextForClosedStreamingFinal;
|
|
1722
|
-
const shouldDiscardStreamingPreview = info?.kind === "final" && hasMedia && (hasVoiceMedia && !shouldDeliverText || skipTextForDuplicateFinal);
|
|
1787
|
+
const shouldDiscardStreamingPreview = info?.kind === "final" && (finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText || skipTextForDuplicateFinal));
|
|
1723
1788
|
if (!shouldDeliverText && !hasMedia) return;
|
|
1724
1789
|
if (shouldDiscardStreamingPreview) await discardStreamingPreview();
|
|
1725
1790
|
if (shouldDeliverText) {
|
|
1726
1791
|
if (info?.kind === "block") {
|
|
1727
|
-
if (!
|
|
1792
|
+
if (!useStreamingCard) return;
|
|
1728
1793
|
startStreaming();
|
|
1729
1794
|
if (streamingStartPromise) await streamingStartPromise;
|
|
1730
1795
|
}
|
|
1731
|
-
if (info?.kind === "final" &&
|
|
1796
|
+
if (info?.kind === "final" && useStreamingCard) {
|
|
1732
1797
|
startStreaming();
|
|
1733
1798
|
if (streamingStartPromise) await streamingStartPromise;
|
|
1734
1799
|
}
|
|
@@ -1791,13 +1856,9 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1791
1856
|
streamingCloseErroredForReply = true;
|
|
1792
1857
|
streamingClosedForReply = false;
|
|
1793
1858
|
params.runtime.error?.(`feishu[${account.accountId}] ${info.kind} reply failed: ${String(error)}`);
|
|
1794
|
-
await
|
|
1795
|
-
typingCallbacks?.onIdle?.();
|
|
1796
|
-
},
|
|
1797
|
-
onIdle: async () => {
|
|
1798
|
-
await closeStreaming();
|
|
1799
|
-
typingCallbacks?.onIdle?.();
|
|
1859
|
+
await queueIdleSideEffects({ markClosedForReply: false });
|
|
1800
1860
|
},
|
|
1861
|
+
onIdle: () => queueIdleSideEffects(),
|
|
1801
1862
|
onCleanup: () => {
|
|
1802
1863
|
typingCallbacks?.onCleanup?.();
|
|
1803
1864
|
}
|
|
@@ -1847,7 +1908,12 @@ function createFeishuReplyDispatcher(params) {
|
|
|
1847
1908
|
updateStreamingStatusLine("");
|
|
1848
1909
|
} : void 0
|
|
1849
1910
|
},
|
|
1850
|
-
markDispatchIdle
|
|
1911
|
+
markDispatchIdle,
|
|
1912
|
+
ensureNoVisibleReplyFallback,
|
|
1913
|
+
getVisibleReplyState: () => ({
|
|
1914
|
+
visibleReplySent,
|
|
1915
|
+
skippedFinalReason
|
|
1916
|
+
})
|
|
1851
1917
|
};
|
|
1852
1918
|
}
|
|
1853
1919
|
//#endregion
|
|
@@ -1857,6 +1923,13 @@ const PERMISSION_ERROR_COOLDOWN_MS = 300 * 1e3;
|
|
|
1857
1923
|
const groupNameCache = /* @__PURE__ */ new Map();
|
|
1858
1924
|
const GROUP_NAME_CACHE_TTL_MS = 1800 * 1e3;
|
|
1859
1925
|
const GROUP_NAME_CACHE_MAX_SIZE = 500;
|
|
1926
|
+
function shouldSendNoVisibleReplyFallback(dispatchResult) {
|
|
1927
|
+
const finalCount = dispatchResult.counts.final ?? 0;
|
|
1928
|
+
const failedFinalCount = dispatchResult.failedCounts?.final ?? 0;
|
|
1929
|
+
const emptyEligibleDispatch = dispatchResult.noVisibleReplyFallbackEligible === true && dispatchResult.queuedFinal !== true && finalCount === 0;
|
|
1930
|
+
const queuedFinalFailed = dispatchResult.queuedFinal === true && failedFinalCount > 0;
|
|
1931
|
+
return dispatchResult.sendPolicyDenied !== true && dispatchResult.sourceReplyDeliveryMode !== "message_tool_only" && (emptyEligibleDispatch || queuedFinalFailed);
|
|
1932
|
+
}
|
|
1860
1933
|
function resolveConfiguredFeishuGroupSessionScope(params) {
|
|
1861
1934
|
const legacyTopicSessionMode = params.groupConfig?.topicSessionMode ?? params.feishuCfg?.topicSessionMode ?? "disabled";
|
|
1862
1935
|
return params.groupConfig?.groupSessionScope ?? params.feishuCfg?.groupSessionScope ?? (legacyTopicSessionMode === "enabled" ? "group_topic" : "group");
|
|
@@ -2740,7 +2813,7 @@ async function handleFeishuMessage(params) {
|
|
|
2740
2813
|
const agentCtx = await buildCtxPayloadForAgent(agentId, agentSessionKey, route.accountId, ctx.mentionedBot && agentId === activeAgentId);
|
|
2741
2814
|
if (agentId === activeAgentId) {
|
|
2742
2815
|
const identity = resolveAgentOutboundIdentity(cfg, agentId);
|
|
2743
|
-
const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({
|
|
2816
|
+
const { dispatcher, replyOptions, markDispatchIdle, ensureNoVisibleReplyFallback } = createFeishuReplyDispatcher({
|
|
2744
2817
|
cfg,
|
|
2745
2818
|
agentId,
|
|
2746
2819
|
runtime,
|
|
@@ -2753,10 +2826,11 @@ async function handleFeishuMessage(params) {
|
|
|
2753
2826
|
threadReply,
|
|
2754
2827
|
accountId: account.accountId,
|
|
2755
2828
|
identity,
|
|
2756
|
-
messageCreateTimeMs
|
|
2829
|
+
messageCreateTimeMs,
|
|
2830
|
+
sessionKey: agentSessionKey
|
|
2757
2831
|
});
|
|
2758
2832
|
log(`feishu[${account.accountId}]: broadcast active dispatch agent=${agentId} (session=${agentSessionKey})`);
|
|
2759
|
-
await core.channel.inbound.run({
|
|
2833
|
+
const turnResult = await core.channel.inbound.run({
|
|
2760
2834
|
channel: "feishu",
|
|
2761
2835
|
accountId: route.accountId,
|
|
2762
2836
|
raw: ctx,
|
|
@@ -2794,6 +2868,10 @@ async function handleFeishuMessage(params) {
|
|
|
2794
2868
|
})
|
|
2795
2869
|
}
|
|
2796
2870
|
});
|
|
2871
|
+
if (turnResult.dispatched && shouldSendNoVisibleReplyFallback({
|
|
2872
|
+
...turnResult.dispatchResult,
|
|
2873
|
+
failedCounts: dispatcher.getFailedCounts()
|
|
2874
|
+
})) await ensureNoVisibleReplyFallback("broadcast-dispatch-complete-no-visible-reply");
|
|
2797
2875
|
} else {
|
|
2798
2876
|
delete agentCtx.CommandAuthorized;
|
|
2799
2877
|
const noopDispatcher = {
|
|
@@ -2872,7 +2950,7 @@ async function handleFeishuMessage(params) {
|
|
|
2872
2950
|
storePath,
|
|
2873
2951
|
sessionKey: route.sessionKey
|
|
2874
2952
|
});
|
|
2875
|
-
const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({
|
|
2953
|
+
const { dispatcher, replyOptions, markDispatchIdle, ensureNoVisibleReplyFallback } = createFeishuReplyDispatcher({
|
|
2876
2954
|
cfg,
|
|
2877
2955
|
agentId: route.agentId,
|
|
2878
2956
|
runtime,
|
|
@@ -2885,7 +2963,8 @@ async function handleFeishuMessage(params) {
|
|
|
2885
2963
|
threadReply,
|
|
2886
2964
|
accountId: account.accountId,
|
|
2887
2965
|
identity,
|
|
2888
|
-
messageCreateTimeMs
|
|
2966
|
+
messageCreateTimeMs,
|
|
2967
|
+
sessionKey: route.sessionKey
|
|
2889
2968
|
});
|
|
2890
2969
|
log(`feishu[${account.accountId}]: dispatching to agent (session=${route.sessionKey})`);
|
|
2891
2970
|
const turnResult = await core.channel.inbound.run({
|
|
@@ -2945,6 +3024,10 @@ async function handleFeishuMessage(params) {
|
|
|
2945
3024
|
if (!turnResult.dispatched) return;
|
|
2946
3025
|
const { dispatchResult } = turnResult;
|
|
2947
3026
|
const { queuedFinal, counts } = dispatchResult;
|
|
3027
|
+
if (shouldSendNoVisibleReplyFallback({
|
|
3028
|
+
...dispatchResult,
|
|
3029
|
+
failedCounts: dispatcher.getFailedCounts()
|
|
3030
|
+
})) await ensureNoVisibleReplyFallback("dispatch-complete-no-visible-reply");
|
|
2948
3031
|
log(`feishu[${account.accountId}]: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`);
|
|
2949
3032
|
}
|
|
2950
3033
|
} catch (err) {
|
|
@@ -3805,7 +3888,9 @@ async function resolveParsedCommentContent(params) {
|
|
|
3805
3888
|
};
|
|
3806
3889
|
}
|
|
3807
3890
|
async function delayMs(ms) {
|
|
3808
|
-
await new Promise((resolve) =>
|
|
3891
|
+
await new Promise((resolve) => {
|
|
3892
|
+
setTimeout(resolve, ms);
|
|
3893
|
+
});
|
|
3809
3894
|
}
|
|
3810
3895
|
function buildDriveCommentTargetUrl(params) {
|
|
3811
3896
|
return `/open-apis/drive/v1/files/${encodeURIComponent(params.fileToken)}/comments/batch_query` + encodeQuery({
|
|
@@ -5429,7 +5514,7 @@ async function monitorSingleAccount(params) {
|
|
|
5429
5514
|
if (connectionMode === "webhook" && !account.encryptKey?.trim()) throw new Error(`Feishu account "${accountId}" webhook mode requires encryptKey`);
|
|
5430
5515
|
const warmupCount = await warmupDedupFromDisk(accountId, log);
|
|
5431
5516
|
if (warmupCount > 0) log(`feishu[${accountId}]: dedup warmup loaded ${warmupCount} entries from disk`);
|
|
5432
|
-
let threadBindingManager
|
|
5517
|
+
let threadBindingManager;
|
|
5433
5518
|
try {
|
|
5434
5519
|
const eventDispatcher = createEventDispatcher(account);
|
|
5435
5520
|
const chatHistories = /* @__PURE__ */ new Map();
|
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-B3ngoThy.js";
|
|
2
2
|
export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/feishu",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.6.1-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/feishu",
|
|
9
|
-
"version": "2026.
|
|
9
|
+
"version": "2026.6.1-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@larksuiteoapi/node-sdk": "1.66.0",
|
|
12
12
|
"typebox": "1.1.39",
|
|
13
13
|
"zod": "4.4.3"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.
|
|
16
|
+
"openclaw": ">=2026.6.1-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/feishu",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.6.1-beta.1",
|
|
4
4
|
"description": "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"zod": "4.4.3"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.
|
|
16
|
+
"openclaw": ">=2026.6.1-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"minHostVersion": ">=2026.5.29"
|
|
48
48
|
},
|
|
49
49
|
"compat": {
|
|
50
|
-
"pluginApi": ">=2026.
|
|
50
|
+
"pluginApi": ">=2026.6.1-beta.1"
|
|
51
51
|
},
|
|
52
52
|
"build": {
|
|
53
|
-
"openclawVersion": "2026.
|
|
53
|
+
"openclawVersion": "2026.6.1-beta.1"
|
|
54
54
|
},
|
|
55
55
|
"release": {
|
|
56
56
|
"publishToClawHub": true,
|