@openclaw/feishu 2026.8.2 → 2026.9.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.
@@ -1,16 +1,16 @@
1
1
  import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-RwSyseKr.js";
2
- import { i as resolveReceiveIdType } from "./targets-BUjQ1TcA.js";
3
- import { A as buildFeishuMediaFallbackText, C as extractMentionTargets, Ct as createFeishuCardInteractionEnvelope, D as parseInteractiveCardContent, E as isFeishuGroupChatType, H as createCommentTypingReactionLifecycle, J as buildFeishuCommentTarget, K as parseCommentContentElements, N as deliverCommentThreadText, O as parsePostContent, Q as resolveFeishuCardTemplate, S as sendStructuredCardFeishu, St as buildFeishuCardActionTextFallback, T as isMentionForwardRequest, U as encodeQuery, W as extractReplyText, Y as normalizeCommentFileType, b as sendCardFeishu, bt as resolveFeishuChatType, c as buildFeishuPresentationFallback, d as resolveFeishuRichReply, dt as hasExplicitFeishuGroupConfig, f as saveMessageResourceFeishu, ft as normalizeFeishuAllowEntry, g as chunkFeishuCardMarkdown, gt as resolveFeishuGroupSenderActivationIngressAccess, h as shouldSuppressFeishuTextForVoiceMedia, ht as resolveFeishuGroupConversationIngressAccess, i as noVisibleFeishuReplyDelivery, j as resolveFeishuIdentityEmoji, mt as resolveFeishuGroupConfig, n as createFeishuReplyDeliveryResult, nt as materializeFeishuPostMarkdownSoftBreaks, p as sendMediaFeishu, pt as resolveFeishuDmIngressAccess, q as requestFeishuApi, r as mergeFeishuReplyDeliveryResults, t as createFeishuPartialReplyDeliveryError, tt as chunkFeishuPostMarkdown, v as getMessageFeishu, vt as resolveFeishuReplyPolicy, w as isFeishuBroadcastMention, wt as decodeFeishuCardAction, x as sendMessageFeishu, y as listFeishuThreadMessages, yt as normalizeFeishuChatType$2 } from "./reply-delivery-result-BIIeISPG.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-CEFTDA3f.js";
4
4
  import { a as normalizeFeishuWebhookPath, o as normalizeFeishuExternalKey } from "./doctor-contract-CQAuBotF.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-vJJyfkxI.js";
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-Cbr7P_JH.js";
8
+ import { r as getChatInfo } from "./chat-CxXaPTuO.js";
9
9
  import { t as createFeishuThreadBindingManager } from "./thread-bindings-BiL1wGqK.js";
10
10
  import { evaluateSupplementalContextVisibility, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode } from "./runtime-api.js";
11
11
  import { t as readFeishuJsonResponse } from "./json-response-DXajw_FU.js";
12
- import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-Cs1s-qSS.js";
13
- import { t as fetchBotIdentityForMonitor } from "./monitor-BVYpA9SO.js";
12
+ import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-D_SatdZl.js";
13
+ import { t as fetchBotIdentityForMonitor } from "./monitor-DoMmOfaD.js";
14
14
  import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
15
15
  import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createReplyPrefixContext, formatChannelProgressDraftLineForEntry, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
16
16
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
@@ -1620,21 +1620,10 @@ var FeishuStreamingSession = class {
1620
1620
  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);
1621
1621
  return result;
1622
1622
  }
1623
- async close(finalText, options) {
1624
- try {
1625
- return (await this.closeWithResult(finalText, options)).visibleReplySent;
1626
- } catch (error) {
1627
- if (error instanceof FeishuStreamingFinalizationError) return error.result.visibleReplySent;
1628
- throw error;
1629
- }
1630
- }
1631
1623
  async discard() {
1632
- if (!this.state || this.closed) return;
1624
+ if (!this.state || this.closed) return { visibleReplySent: false };
1633
1625
  const { cardId, messageId } = this.state;
1634
- if (!messageId) {
1635
- await this.close("");
1636
- return;
1637
- }
1626
+ if (!messageId) return this.closeWithResult("");
1638
1627
  this.closed = true;
1639
1628
  this.clearFlushTimer();
1640
1629
  await this.queue;
@@ -1644,10 +1633,11 @@ var FeishuStreamingSession = class {
1644
1633
  this.state = null;
1645
1634
  this.pendingText = null;
1646
1635
  this.log?.(`Discarded streaming card: cardId=${cardId}`);
1636
+ return { visibleReplySent: false };
1647
1637
  } catch (error) {
1648
1638
  this.log?.(`Discard failed: ${String(error)}`);
1649
1639
  this.closed = false;
1650
- await this.close("");
1640
+ return this.closeWithResult("");
1651
1641
  }
1652
1642
  }
1653
1643
  isActive() {
@@ -1905,9 +1895,7 @@ function createFeishuReplyDispatcher(params) {
1905
1895
  let activeStreamingGeneration;
1906
1896
  let inFlightStreamingClose;
1907
1897
  let visibleReplySent = false;
1908
- let skippedFinalReason = null;
1909
- let skippedFinalAssistantMessageIndex;
1910
- let preparedDeliveryAssistantMessageIndex;
1898
+ let replyOutcome;
1911
1899
  let idleSideEffectsPromise = Promise.resolve();
1912
1900
  let activeIdleSideEffectsPromise = null;
1913
1901
  let idleRequestedForReply = false;
@@ -2007,97 +1995,86 @@ function createFeishuReplyDispatcher(params) {
2007
1995
  lastSnapshotTextLength = 0;
2008
1996
  hasStreamingFinalText = false;
2009
1997
  };
2010
- const rememberClosedStreamingSettlement = (generation, content, result, error) => {
2011
- if (generation === void 0 || !content) return;
1998
+ const rememberClosedStreamingSettlement = (generation, content, result, error, disposition = "closed") => {
1999
+ if (generation === void 0 || !content && disposition === "closed") return;
2012
2000
  closedStreamingSettlements.set(generation, {
2001
+ disposition,
2013
2002
  result,
2014
2003
  content,
2015
2004
  ...error === void 0 ? {} : { error }
2016
2005
  });
2017
2006
  };
2018
- const performStreamingClose = async () => {
2007
+ const performStreamingClose = async (disposition) => {
2019
2008
  const streamingToClose = streaming;
2020
2009
  const generationToClose = activeStreamingGeneration;
2021
2010
  const startPromiseToClose = streamingStartPromise;
2022
2011
  const updateQueueToClose = partialUpdateQueue;
2023
2012
  const finalizedAnswerText = streamText;
2024
2013
  const finalizedReasoningText = reasoningText;
2014
+ const outcome = {
2015
+ disposition,
2016
+ ...generationToClose === void 0 ? {} : { generation: generationToClose }
2017
+ };
2025
2018
  if (generationToClose !== void 0 && activeStreamingGeneration === generationToClose) activeStreamingGeneration = void 0;
2026
2019
  try {
2027
2020
  if (startPromiseToClose) await startPromiseToClose;
2028
2021
  await updateQueueToClose;
2022
+ let result = noVisibleFeishuReplyDelivery;
2023
+ let finalizationError;
2029
2024
  if (streamingToClose?.isActive()) {
2030
2025
  statusLine = "";
2031
2026
  const text = buildCombinedStreamText(finalizedReasoningText, finalizedAnswerText);
2032
- const finalNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
2033
2027
  let closed;
2034
2028
  try {
2035
- closed = await streamingToClose.closeWithResult(text, { note: finalNote });
2029
+ if (disposition === "discarded") closed = await streamingToClose.discard();
2030
+ else {
2031
+ const finalNote = resolveCardNote(agentId, identity, responsePrefixContextProvider());
2032
+ closed = await streamingToClose.closeWithResult(text, { note: finalNote });
2033
+ }
2036
2034
  } catch (error) {
2037
2035
  if (!(error instanceof FeishuStreamingFinalizationError)) throw error;
2038
- const failedResult = createFeishuReplyDeliveryResult({
2039
- results: [error.result],
2040
- visibleReplySent: error.result.visibleReplySent,
2041
- content: error.result.content,
2042
- kind: "card"
2043
- });
2044
- if (failedResult.visibleReplySent) markVisibleReplySent();
2045
- if (failedResult.visibleReplySent && finalizedAnswerText) {
2046
- const partialError = createFeishuPartialReplyDeliveryError(error.cause ?? error, failedResult);
2047
- if (failedResult.content === text) deliveredFinalTexts.add(finalizedAnswerText);
2048
- rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, failedResult, partialError);
2049
- return {
2050
- result: failedResult,
2051
- ...generationToClose === void 0 ? {} : { generation: generationToClose },
2052
- error: partialError
2053
- };
2054
- }
2055
- rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, failedResult, error);
2056
- return {
2057
- result: failedResult,
2058
- ...generationToClose === void 0 ? {} : { generation: generationToClose },
2059
- error
2060
- };
2036
+ closed = error.result;
2037
+ finalizationError = error;
2061
2038
  }
2062
- const result = createFeishuReplyDeliveryResult({
2039
+ result = createFeishuReplyDeliveryResult({
2063
2040
  results: [closed],
2064
2041
  visibleReplySent: closed.visibleReplySent,
2065
2042
  content: closed.content,
2066
2043
  kind: "card"
2067
2044
  });
2068
2045
  if (result.visibleReplySent) markVisibleReplySent();
2069
- if (result.visibleReplySent && finalizedAnswerText) {
2070
- deliveredFinalTexts.add(finalizedAnswerText);
2071
- rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, result);
2072
- }
2073
- return {
2074
- result,
2075
- ...generationToClose === void 0 ? {} : { generation: generationToClose }
2076
- };
2046
+ if (disposition === "closed" && result.visibleReplySent && finalizedAnswerText && (finalizationError === void 0 || result.content === text)) deliveredFinalTexts.add(finalizedAnswerText);
2047
+ if (finalizationError instanceof FeishuStreamingFinalizationError && result.visibleReplySent) finalizationError = createFeishuPartialReplyDeliveryError(finalizationError.cause ?? finalizationError, result);
2077
2048
  }
2049
+ if (disposition === "discarded" || finalizationError !== void 0 || result.visibleReplySent && finalizedAnswerText) rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, result, finalizationError, disposition);
2078
2050
  return {
2079
- result: noVisibleFeishuReplyDelivery,
2080
- ...generationToClose === void 0 ? {} : { generation: generationToClose }
2051
+ ...outcome,
2052
+ result,
2053
+ ...finalizationError === void 0 ? {} : { error: finalizationError }
2081
2054
  };
2082
2055
  } catch (error) {
2056
+ if (disposition === "discarded") rememberClosedStreamingSettlement(generationToClose, finalizedAnswerText, noVisibleFeishuReplyDelivery, error, disposition);
2083
2057
  return {
2058
+ ...outcome,
2084
2059
  result: noVisibleFeishuReplyDelivery,
2085
- ...generationToClose === void 0 ? {} : { generation: generationToClose },
2086
2060
  error
2087
2061
  };
2088
2062
  } finally {
2089
2063
  if (streaming === streamingToClose) resetStreamingState();
2090
2064
  }
2091
2065
  };
2092
- const closeStreaming = () => {
2066
+ const closeStreaming = (disposition = "closed") => {
2067
+ const session = streaming;
2093
2068
  const generation = activeStreamingGeneration;
2094
- if (generation !== void 0 && inFlightStreamingClose?.generation === generation) return inFlightStreamingClose.promise;
2069
+ if (session && inFlightStreamingClose?.session === session) return inFlightStreamingClose.promise;
2095
2070
  const content = streamText;
2096
- const closePromise = performStreamingClose();
2097
- if (generation !== void 0) {
2071
+ const closePromise = performStreamingClose(disposition);
2072
+ if (session && generation !== void 0) {
2098
2073
  const closing = {
2074
+ session,
2099
2075
  generation,
2100
2076
  content,
2077
+ disposition,
2101
2078
  promise: closePromise
2102
2079
  };
2103
2080
  inFlightStreamingClose = closing;
@@ -2129,13 +2106,12 @@ function createFeishuReplyDispatcher(params) {
2129
2106
  };
2130
2107
  };
2131
2108
  const discardStreamingPreview = async () => {
2132
- try {
2133
- if (streamingStartPromise) await streamingStartPromise;
2134
- await partialUpdateQueue;
2135
- if (streaming?.isActive()) await streaming.discard();
2136
- } finally {
2109
+ if (streaming && inFlightStreamingClose?.session === streaming && inFlightStreamingClose.disposition === "closed") {
2137
2110
  resetStreamingState();
2111
+ return;
2138
2112
  }
2113
+ const outcome = await closeStreaming("discarded");
2114
+ if (outcome.error !== void 0) throw toStringifiedError(outcome.error);
2139
2115
  };
2140
2116
  const updateStreamingStatusLine = (nextStatusLine, options) => {
2141
2117
  statusLine = nextStatusLine;
@@ -2198,27 +2174,28 @@ function createFeishuReplyDispatcher(params) {
2198
2174
  kind: paramsLocal.useCard ? "card" : "text"
2199
2175
  });
2200
2176
  };
2177
+ const sendPostReply = (text, infoKind, firstChunkMentions) => sendChunkedTextReply({
2178
+ text,
2179
+ useCard: false,
2180
+ infoKind,
2181
+ firstChunkMentions,
2182
+ chunkMentions: requiredMentionTargets,
2183
+ sendChunk: ({ chunk, mentions }) => sendMessageFeishu({
2184
+ cfg,
2185
+ to: sendTarget,
2186
+ text: chunk,
2187
+ replyToMessageId: sendReplyToMessageId,
2188
+ replyInThread: effectiveReplyInThread,
2189
+ allowTopLevelReplyFallback,
2190
+ accountId,
2191
+ ...mentions ? { mentions } : {}
2192
+ })
2193
+ });
2201
2194
  const sendMediaReplies = async (payload, options) => {
2202
2195
  const mediaUrls = resolveSendableOutboundReplyParts(payload).mediaUrls;
2203
2196
  let sentFallbackText = false;
2204
2197
  let degradedVoiceFallbackText;
2205
2198
  const results = [];
2206
- const sendFallbackText = async (text) => await sendChunkedTextReply({
2207
- text,
2208
- useCard: false,
2209
- infoKind: "final",
2210
- chunkMentions: requiredMentionTargets,
2211
- sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
2212
- cfg,
2213
- to: sendTarget,
2214
- text: chunk,
2215
- replyToMessageId: sendReplyToMessageId,
2216
- replyInThread: effectiveReplyInThread,
2217
- allowTopLevelReplyFallback,
2218
- accountId,
2219
- ...mentions ? { mentions } : {}
2220
- })
2221
- });
2222
2199
  try {
2223
2200
  await sendMediaWithLeadingCaption({
2224
2201
  mediaUrls,
@@ -2252,12 +2229,12 @@ function createFeishuReplyDispatcher(params) {
2252
2229
  mediaUrl
2253
2230
  });
2254
2231
  sentFallbackText = true;
2255
- results.push(await sendFallbackText(fallbackText));
2232
+ results.push(await sendPostReply(fallbackText, "final"));
2256
2233
  }
2257
2234
  });
2258
2235
  if (degradedVoiceFallbackText && !sentFallbackText) {
2259
2236
  sentFallbackText = true;
2260
- results.push(await sendFallbackText(degradedVoiceFallbackText));
2237
+ results.push(await sendPostReply(degradedVoiceFallbackText, "final"));
2261
2238
  }
2262
2239
  } catch (error) {
2263
2240
  const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
@@ -2269,8 +2246,8 @@ function createFeishuReplyDispatcher(params) {
2269
2246
  const ensureNoVisibleReplyFallback = async (reason) => {
2270
2247
  await idleSideEffectsPromise;
2271
2248
  if (visibleReplySent) return false;
2272
- if (skippedFinalReason === "silent") {
2273
- params.runtime.log?.(`feishu[${account.accountId}]: no-visible-reply fallback skipped for intentional silence (${reason})`);
2249
+ if (replyOutcome?.kind === "suppressed" || replyOutcome?.kind === "skipped" && replyOutcome.reason === "silent") {
2250
+ params.runtime.log?.(`feishu[${account.accountId}]: no-visible-reply fallback skipped for ${replyOutcome.reason} (${reason})`);
2274
2251
  return false;
2275
2252
  }
2276
2253
  await sendMessageFeishu({
@@ -2344,10 +2321,17 @@ function createFeishuReplyDispatcher(params) {
2344
2321
  if (completions.some((completion) => ownsCurrentClose(completion))) markClosedStreamingContentClaimed(closeOutcome.generation);
2345
2322
  for (const completion of completions) {
2346
2323
  const claimedSettlement = ownsCurrentClose(completion) ? {
2324
+ disposition: closeOutcome.disposition,
2347
2325
  result: finalized,
2348
2326
  ...closeOutcome.error === void 0 ? {} : { error: closeOutcome.error }
2349
2327
  } : claimClosedStreamingResult(completion.streamingGeneration, completion.result.content);
2350
2328
  const deliveryError = claimedSettlement?.error;
2329
+ if (claimedSettlement?.disposition === "discarded") {
2330
+ const retained = mergeFeishuReplyDeliveryResults([claimedSettlement.result, completion.result], claimedSettlement.result.content ?? "");
2331
+ if (deliveryError !== void 0) completion.reject(createFeishuPartialReplyDeliveryError(deliveryError, retained));
2332
+ else completion.resolve(retained);
2333
+ continue;
2334
+ }
2351
2335
  let providerFinalized = claimedSettlement?.result;
2352
2336
  try {
2353
2337
  providerFinalized = await ensureVisibleStreamingDelivery(providerFinalized, completion.result.content, completion.infoKind);
@@ -2384,22 +2368,16 @@ function createFeishuReplyDispatcher(params) {
2384
2368
  let finalized = noVisibleFeishuReplyDelivery;
2385
2369
  let finalizationError;
2386
2370
  let fallbackPartial;
2387
- const claimedSettlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content);
2388
- if (claimedSettlement) {
2389
- finalized = claimedSettlement.result;
2390
- finalizationError = claimedSettlement.error;
2391
- } else if (paramsLocal.ownerGeneration !== void 0 && inFlightStreamingClose?.generation === paramsLocal.ownerGeneration) {
2371
+ let settlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content);
2372
+ if (!settlement && paramsLocal.ownerGeneration !== void 0 && inFlightStreamingClose?.generation === paramsLocal.ownerGeneration) {
2392
2373
  const closeOutcome = await inFlightStreamingClose.promise;
2393
- const completedSettlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content);
2394
- finalized = completedSettlement?.result ?? closeOutcome?.result ?? finalized;
2395
- finalizationError = completedSettlement?.error ?? closeOutcome?.error;
2396
- } else if (paramsLocal.ownerGeneration !== void 0 && activeStreamingGeneration === paramsLocal.ownerGeneration) {
2397
- const closeOutcome = await closeStreaming();
2398
- finalized = closeOutcome.result;
2399
- finalizationError = closeOutcome.error;
2400
- }
2374
+ settlement = claimClosedStreamingResult(paramsLocal.ownerGeneration, paramsLocal.content) ?? closeOutcome;
2375
+ } else if (!settlement && paramsLocal.ownerGeneration !== void 0 && activeStreamingGeneration === paramsLocal.ownerGeneration) settlement = await closeStreaming();
2376
+ finalized = settlement?.result ?? finalized;
2377
+ finalizationError = settlement?.error;
2378
+ const discarded = settlement?.disposition === "discarded";
2401
2379
  try {
2402
- finalized = await ensureVisibleStreamingDelivery(finalized, paramsLocal.content, paramsLocal.infoKind) ?? finalized;
2380
+ if (!discarded) finalized = await ensureVisibleStreamingDelivery(finalized, paramsLocal.content, paramsLocal.infoKind) ?? finalized;
2403
2381
  } catch (fallbackError) {
2404
2382
  fallbackPartial = isChannelPartialDeliveryError(fallbackError) ? fallbackError.deliveryResult : void 0;
2405
2383
  const fallbackCause = fallbackPartial && fallbackError instanceof Error ? fallbackError.cause ?? fallbackError : fallbackError;
@@ -2410,7 +2388,7 @@ function createFeishuReplyDispatcher(params) {
2410
2388
  finalized,
2411
2389
  ...fallbackPartial ? [fallbackPartial] : [],
2412
2390
  ...mediaPartial ? [mediaPartial] : []
2413
- ], fallbackPartial?.visibleReplySent === true ? fallbackPartial.content : finalized.visibleReplySent === true ? finalized.content : paramsLocal.content);
2391
+ ], discarded ? finalized.content ?? "" : fallbackPartial?.visibleReplySent === true ? fallbackPartial.content : finalized.visibleReplySent === true ? finalized.content : paramsLocal.content);
2414
2392
  const mediaCause = mediaPartial && paramsLocal.error instanceof Error ? paramsLocal.error.cause ?? paramsLocal.error : paramsLocal.error;
2415
2393
  const cause = finalizationError ? new AggregateError([mediaCause, finalizationError instanceof Error ? finalizationError.cause ?? finalizationError : finalizationError], "Feishu reply delivery and streaming finalization failed") : mediaCause;
2416
2394
  throw createFeishuPartialReplyDeliveryError(cause, accepted);
@@ -2426,13 +2404,14 @@ function createFeishuReplyDispatcher(params) {
2426
2404
  conversationType: chatId.startsWith("oc_") ? "group" : "direct"
2427
2405
  },
2428
2406
  onSkip: (_payload, info) => {
2429
- if (info.kind === "final" || info.kind === "block" && info.reason === "silent") {
2430
- skippedFinalReason = info.reason;
2431
- skippedFinalAssistantMessageIndex = info.assistantMessageIndex;
2432
- }
2407
+ if (replyOutcome?.kind !== "failed" && (info.kind === "final" || info.kind === "block" && info.reason === "silent")) replyOutcome = {
2408
+ kind: "skipped",
2409
+ reason: info.reason,
2410
+ assistantMessageIndex: info.assistantMessageIndex
2411
+ };
2433
2412
  },
2434
2413
  beforeDeliver: (payload, info) => {
2435
- preparedDeliveryAssistantMessageIndex = info.assistantMessageIndex;
2414
+ 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;
2436
2415
  return payload;
2437
2416
  },
2438
2417
  onReplyStart: async () => {
@@ -2443,9 +2422,7 @@ function createFeishuReplyDispatcher(params) {
2443
2422
  sentIndependentBlockText = false;
2444
2423
  idleRequestedForReply = false;
2445
2424
  visibleReplySent = false;
2446
- skippedFinalReason = null;
2447
- skippedFinalAssistantMessageIndex = void 0;
2448
- preparedDeliveryAssistantMessageIndex = void 0;
2425
+ replyOutcome = void 0;
2449
2426
  }
2450
2427
  if (previewStreamingEnabled && renderMode === "card") startStreaming();
2451
2428
  await Promise.resolve(typingCallbacks?.onReplyStart?.());
@@ -2456,6 +2433,7 @@ function createFeishuReplyDispatcher(params) {
2456
2433
  }
2457
2434
  };
2458
2435
  const handleDeliveryError = async (error, info) => {
2436
+ if (info.kind === "final") replyOutcome = { kind: "failed" };
2459
2437
  if (isChannelPartialDeliveryError(error)) markVisibleReplySent();
2460
2438
  params.runtime.error?.(`feishu[${account.accountId}] ${info.kind} reply failed: ${String(error)}`);
2461
2439
  await queueIdleSideEffects().catch((cleanupError) => params.runtime.error?.(`feishu[${account.accountId}] reply error cleanup failed: ${String(cleanupError)}`));
@@ -2464,19 +2442,36 @@ function createFeishuReplyDispatcher(params) {
2464
2442
  dispatcherOptions,
2465
2443
  delivery: {
2466
2444
  observeMessageSent: true,
2467
- deliver: async (payload, info) => {
2468
- const deliveryAssistantMessageIndex = preparedDeliveryAssistantMessageIndex;
2469
- preparedDeliveryAssistantMessageIndex = void 0;
2470
- if (info?.kind === "final" || skippedFinalReason !== "silent" || skippedFinalAssistantMessageIndex === void 0 || deliveryAssistantMessageIndex === void 0 || deliveryAssistantMessageIndex >= skippedFinalAssistantMessageIndex) {
2471
- skippedFinalReason = null;
2472
- skippedFinalAssistantMessageIndex = void 0;
2445
+ onDelivered: (_payload, info, result) => {
2446
+ if (result?.visibleReplySent) {
2447
+ markVisibleReplySent();
2448
+ if (info.kind === "final") replyOutcome = void 0;
2449
+ return;
2473
2450
  }
2474
- const payloadText = payload.isReasoning && payload.text ? formatReasoningMessage(payload.text) : payload.text;
2451
+ const reason = result?.suppression?.reason;
2452
+ 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 = {
2453
+ kind: "suppressed",
2454
+ reason
2455
+ };
2456
+ },
2457
+ deliver: async (inputPayload, info) => {
2458
+ const prepared = await renderFeishuReplyPayload(inputPayload, {
2459
+ to: sendTarget,
2460
+ identity,
2461
+ mentions: requiredMentionTargets
2462
+ });
2463
+ const rendered = consumeFeishuPresentationFallbackMarker(prepared.payload);
2464
+ const payload = rendered.payload;
2465
+ const presentationCard = prepared.card;
2466
+ const hasPresentationFallback = rendered.presentationFallback?.hasVisibleContent === true;
2467
+ const hasIndependentPresentation = presentationCard !== void 0 || hasPresentationFallback;
2468
+ const resolvedText = payload.text;
2469
+ const payloadText = payload.isReasoning && resolvedText ? formatReasoningMessage(resolvedText) : resolvedText;
2475
2470
  const reply = resolveSendableOutboundReplyParts({
2476
2471
  ...payload,
2477
2472
  text: payloadText
2478
2473
  });
2479
- const text = info?.kind === "final" ? mergeStreamingFinalText(streamText, reply.text, payload.isError === true && hasStreamingFinalText) : reply.text;
2474
+ const text = info?.kind === "final" && !hasIndependentPresentation ? mergeStreamingFinalText(streamText, reply.text, payload.isError === true && hasStreamingFinalText) : reply.text;
2480
2475
  const hasText = reply.hasText;
2481
2476
  const hasMedia = reply.hasMedia;
2482
2477
  const ttsSupplement = getReplyPayloadTtsSupplement(payload);
@@ -2490,52 +2485,61 @@ function createFeishuReplyDispatcher(params) {
2490
2485
  const useStaticCard = hasText && (renderMode === "card" || info?.kind === "block" && coreBlockStreamingEnabled && renderMode !== "raw" || renderMode === "auto" && shouldUseCard(text));
2491
2486
  const useStreamingCard = hasText && streamingEnabled && !finalTextExceedsStreamingLimit && (info?.kind === "final" || useStaticCard);
2492
2487
  const useCard = useStaticCard || useStreamingCard;
2493
- const skipTextForDuplicateFinal = info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
2494
- const shouldDeliverText = hasText && !hasVoiceMedia && !skipTextForDuplicateFinal;
2495
- const shouldDiscardStreamingPreview = info?.kind === "final" && (finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
2488
+ const skipTextForDuplicateFinal = !hasIndependentPresentation && info?.kind === "final" && hasText && deliveredFinalTexts.has(text);
2489
+ const shouldDeliverText = hasText && (!hasVoiceMedia || hasPresentationFallback) && !skipTextForDuplicateFinal;
2490
+ const shouldDiscardStreamingPreview = info?.kind === "final" && !(hasIndependentPresentation && payload.isError === true && hasStreamingFinalText) && (hasIndependentPresentation || finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
2496
2491
  const priorClosedStreamingSettlement = info?.kind === "final" && hasText && skipTextForDuplicateFinal ? claimClosedStreamingResult(void 0, text) : void 0;
2497
- if (!shouldDeliverText && !hasMedia) {
2492
+ if (!shouldDeliverText && !hasMedia && !presentationCard) {
2498
2493
  if (priorClosedStreamingSettlement?.error !== void 0) throw toStringifiedError(priorClosedStreamingSettlement.error);
2499
2494
  return priorClosedStreamingSettlement?.result ?? noVisibleFeishuReplyDelivery;
2500
2495
  }
2501
2496
  const deliveredResults = priorClosedStreamingSettlement ? [priorClosedStreamingSettlement.result] : [];
2502
- const collectMediaDelivery = async (mediaPayload, mediaOptions) => {
2497
+ const collectDelivery = async (pending, acceptedContent) => {
2503
2498
  try {
2504
- deliveredResults.push(await sendMediaReplies(mediaPayload, mediaOptions));
2499
+ deliveredResults.push(await pending);
2505
2500
  } catch (error) {
2506
2501
  const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
2507
- const accumulated = mergeFeishuReplyDeliveryResults([...deliveredResults, ...partial ? [partial] : []]);
2508
- throw createFeishuPartialReplyDeliveryError(partial && error instanceof Error ? error.cause ?? error : error, accumulated);
2502
+ const accumulated = mergeFeishuReplyDeliveryResults([...deliveredResults, ...partial ? [{
2503
+ ...partial,
2504
+ content: partial.content ?? acceptedContent
2505
+ }] : []]);
2506
+ throw createFeishuPartialReplyDeliveryError(partial && error instanceof Error ? error.cause ?? error : error, {
2507
+ ...accumulated,
2508
+ content: accumulated.content ?? ""
2509
+ });
2509
2510
  }
2510
2511
  };
2511
2512
  if (shouldDiscardStreamingPreview) await discardStreamingPreview();
2513
+ if (presentationCard) {
2514
+ if (hasMedia) await collectDelivery(sendMediaReplies(payload));
2515
+ await collectDelivery(sendCardFeishu({
2516
+ cfg,
2517
+ to: sendTarget,
2518
+ card: presentationCard,
2519
+ replyToMessageId: sendReplyToMessageId,
2520
+ replyInThread: effectiveReplyInThread,
2521
+ allowTopLevelReplyFallback,
2522
+ accountId
2523
+ }).then((result) => createFeishuReplyDeliveryResult({
2524
+ results: [result],
2525
+ visibleReplySent: true,
2526
+ content: resolvedText,
2527
+ kind: "card"
2528
+ })), resolvedText);
2529
+ markVisibleReplySent();
2530
+ return mergeFeishuReplyDeliveryResults(deliveredResults, resolvedText);
2531
+ }
2512
2532
  if (shouldDeliverText) {
2513
- if (info?.kind === "block") {
2514
- if (!useStreamingCard) {
2515
- if (coreBlockStreamingEnabled) {
2516
- const firstChunkMentions = !sentIndependentBlockText && mentionTargets?.length ? mentionTargets : void 0;
2517
- deliveredResults.push(await sendChunkedTextReply({
2518
- text,
2519
- useCard: false,
2520
- infoKind: "block",
2521
- firstChunkMentions,
2522
- chunkMentions: requiredMentionTargets,
2523
- sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
2524
- cfg,
2525
- to: sendTarget,
2526
- text: chunk,
2527
- replyToMessageId: sendReplyToMessageId,
2528
- replyInThread: effectiveReplyInThread,
2529
- allowTopLevelReplyFallback,
2530
- accountId,
2531
- ...mentions ? { mentions } : {}
2532
- })
2533
- }));
2534
- sentIndependentBlockText = true;
2535
- if (hasMedia) await collectMediaDelivery(payload);
2536
- }
2537
- return mergeFeishuReplyDeliveryResults(deliveredResults, text);
2533
+ if (hasPresentationFallback || info?.kind === "block" && !useStreamingCard) {
2534
+ if (hasPresentationFallback || coreBlockStreamingEnabled) {
2535
+ const firstChunkMentions = info?.kind === "final" || info?.kind === "block" && !sentIndependentBlockText ? mentionTargets : void 0;
2536
+ await collectDelivery(sendPostReply(text, info?.kind, firstChunkMentions));
2537
+ if (info?.kind === "block") sentIndependentBlockText = true;
2538
+ if (hasMedia) await collectDelivery(sendMediaReplies(payload));
2538
2539
  }
2540
+ return mergeFeishuReplyDeliveryResults(deliveredResults, text);
2541
+ }
2542
+ if (info?.kind === "block") {
2539
2543
  startStreaming();
2540
2544
  if (streamingStartPromise) await streamingStartPromise;
2541
2545
  }
@@ -2544,7 +2548,7 @@ function createFeishuReplyDispatcher(params) {
2544
2548
  if (streamingStartPromise) await streamingStartPromise;
2545
2549
  }
2546
2550
  const shouldStreamText = info?.kind === "block" || info?.kind === "final";
2547
- const matchingInFlightClose = info?.kind === "final" && inFlightStreamingClose?.content === text ? inFlightStreamingClose : void 0;
2551
+ const matchingInFlightClose = info?.kind === "final" && inFlightStreamingClose?.disposition === "closed" && inFlightStreamingClose.content === text ? inFlightStreamingClose : void 0;
2548
2552
  const ownerGeneration = activeStreamingGeneration ?? matchingInFlightClose?.generation;
2549
2553
  if (shouldStreamText && ownerGeneration !== void 0 && (streaming?.isActive() || matchingInFlightClose !== void 0)) {
2550
2554
  if (activeStreamingGeneration !== void 0) {
@@ -2561,7 +2565,7 @@ function createFeishuReplyDispatcher(params) {
2561
2565
  }
2562
2566
  }
2563
2567
  if (hasMedia) try {
2564
- await collectMediaDelivery(payload);
2568
+ await collectDelivery(sendMediaReplies(payload));
2565
2569
  } catch (error) {
2566
2570
  await throwStreamingDeliveryFailure({
2567
2571
  error,
@@ -2597,26 +2601,10 @@ function createFeishuReplyDispatcher(params) {
2597
2601
  }));
2598
2602
  } else {
2599
2603
  const firstChunkMentions = info?.kind === "final" && mentionTargets?.length ? mentionTargets : void 0;
2600
- deliveredResults.push(await sendChunkedTextReply({
2601
- text,
2602
- useCard: false,
2603
- infoKind: info?.kind,
2604
- firstChunkMentions,
2605
- chunkMentions: requiredMentionTargets,
2606
- sendChunk: async ({ chunk, mentions }) => await sendMessageFeishu({
2607
- cfg,
2608
- to: sendTarget,
2609
- text: chunk,
2610
- replyToMessageId: sendReplyToMessageId,
2611
- replyInThread: effectiveReplyInThread,
2612
- allowTopLevelReplyFallback,
2613
- accountId,
2614
- ...mentions ? { mentions } : {}
2615
- })
2616
- }));
2604
+ deliveredResults.push(await sendPostReply(text, info?.kind, firstChunkMentions));
2617
2605
  }
2618
2606
  }
2619
- if (hasMedia) await collectMediaDelivery(payload, !ttsTextAlreadyVisible && hasVoiceMedia && hasText ? { fallbackText: text } : void 0);
2607
+ if (hasMedia) await collectDelivery(sendMediaReplies(payload, !shouldDeliverText && !ttsTextAlreadyVisible && hasVoiceMedia && hasText ? { fallbackText: text } : void 0));
2620
2608
  const deliveredContent = hasVoiceMedia ? deliveredResults.at(-1)?.content ?? text : text;
2621
2609
  const result = mergeFeishuReplyDeliveryResults(deliveredResults, deliveredContent);
2622
2610
  if (priorClosedStreamingSettlement?.error !== void 0) throw createFeishuPartialReplyDeliveryError(isChannelPartialDeliveryError(priorClosedStreamingSettlement.error) && priorClosedStreamingSettlement.error instanceof Error ? priorClosedStreamingSettlement.error.cause ?? priorClosedStreamingSettlement.error : priorClosedStreamingSettlement.error, result);
@@ -2666,7 +2654,7 @@ function createFeishuReplyDispatcher(params) {
2666
2654
  ensureNoVisibleReplyFallback,
2667
2655
  getVisibleReplyState: () => ({
2668
2656
  visibleReplySent,
2669
- skippedFinalReason
2657
+ skippedFinalReason: replyOutcome?.kind === "skipped" || replyOutcome?.kind === "suppressed" ? replyOutcome.reason : null
2670
2658
  })
2671
2659
  };
2672
2660
  }
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { r as createFeishuClient } from "./client-vJJyfkxI.js";
2
+ import { r as createFeishuClient } from "./client-DMbIL3UH.js";
3
3
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
4
  import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
5
5
  import { createHash } from "node:crypto";