@openclaw/discord 2026.7.1-beta.6 → 2026.7.2-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.
Files changed (92) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{allow-list-C-MqM-B_.js → allow-list-B7qOH5xu.js} +22 -1
  3. package/dist/{api-DPRqYi4Z.js → api--f62GFaS.js} +38 -23
  4. package/dist/api.js +15 -15
  5. package/dist/approval-custom-id-NYPYj9Po.js +58 -0
  6. package/dist/{approval-handler.runtime-Bw2pZNk1.js → approval-handler.runtime-DeyIiw5I.js} +28 -23
  7. package/dist/approval-message-safety-C-8BL6Kv.js +47 -0
  8. package/dist/{audit-MLpDpN3Z.js → audit-Df2w046a.js} +3 -3
  9. package/dist/{channel-actions-D_fjVUZ_.js → channel-actions-ChpBSRcu.js} +6 -4
  10. package/dist/{channel-actions.runtime-CVHlFqQg.js → channel-actions.runtime-D8V8tyCq.js} +41 -12
  11. package/dist/channel-config-api.js +1 -1
  12. package/dist/{channel-DGWPH5u3.js → channel-nYmJ7FKQ.js} +275 -62
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel.setup-tZcpIn4e.js → channel.setup-DyihrqHH.js} +3 -3
  15. package/dist/{components-BIBizbtZ.js → components-BVrfRS-F.js} +78 -52
  16. package/dist/config-api-B18fR8M5.js +3 -0
  17. package/dist/{config-schema-BgIhO3ko.js → config-schema-_bUKcWRm.js} +42 -82
  18. package/dist/conversation-identity-BpkAzklg.js +94 -0
  19. package/dist/{directory-config-ByaUKL1f.js → directory-config-BUe-qq_C.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-BX79ImQA.js → directory-live-DZByMdPO.js} +6 -6
  22. package/dist/{doctor-Bgoxm5AN.js → doctor-bMaWXFKu.js} +5 -4
  23. package/dist/doctor-contract-api.js +1 -1
  24. package/dist/{doctor-contract-CU7lnZTF.js → doctor-contract-wId6pV41.js} +41 -37
  25. package/dist/{handle-action.guild-admin-CgJSNVui.js → handle-action.guild-admin-C_PQcBEV.js} +12 -12
  26. package/dist/index.js +1 -1
  27. package/dist/legacy-state-migrations-api.js +24 -2
  28. package/dist/{manager.runtime-DaB-CTrt.js → manager.runtime-3RVMVkyx.js} +546 -154
  29. package/dist/{message-handler-DcgvugZt.js → message-handler-CIxWq5Yh.js} +8 -9
  30. package/dist/{message-handler.preflight-DvLXiYVH.js → message-handler.preflight-BI86IbHz.js} +46 -22
  31. package/dist/{message-handler.process-Cjf30P_B.js → message-handler.process-bPS_9_tb.js} +241 -107
  32. package/dist/{message-utils-B_3yA5bN.js → message-utils-D4j07taS.js} +2 -3
  33. package/dist/{outbound-adapter-82RWi_SR.js → outbound-adapter-Czny0Fae.js} +16 -68
  34. package/dist/outbound-components-oJKLj62G.js +99 -0
  35. package/dist/pluralkit-UXq1OyUI.js +40 -0
  36. package/dist/{probe-D1ImJEo2.js → probe-DxjMBeVG.js} +1 -1
  37. package/dist/{probe.runtime-B1dsEeBD.js → probe.runtime-Bc8Oo6e8.js} +1 -1
  38. package/dist/{provider-BkfbC9FQ.js → provider-DWvDNb4J.js} +702 -236
  39. package/dist/{provider-session.runtime-BDjyFw6Y.js → provider-session.runtime-D5pCGibh.js} +3 -3
  40. package/dist/provider.runtime-C2iIfbTs.js +2 -0
  41. package/dist/{resolve-allowlist-common-CWbE8XlH.js → resolve-allowlist-common-B9S8LLBd.js} +5 -5
  42. package/dist/{resolve-channels-CcpvWoRa.js → resolve-channels-BrkWOKdG.js} +7 -7
  43. package/dist/{resolve-users-DJamun9s.js → resolve-users-OjrXXvJP.js} +5 -5
  44. package/dist/{runtime-C6rRmQ6l.js → runtime-C618lis9.js} +223 -33
  45. package/dist/runtime-api.actions.js +2 -2
  46. package/dist/runtime-api.js +23 -23
  47. package/dist/runtime-api.lookup.js +5 -5
  48. package/dist/runtime-api.monitor-DSbfGnzS.js +6 -0
  49. package/dist/runtime-api.monitor.js +7 -7
  50. package/dist/runtime-api.send.js +5 -5
  51. package/dist/runtime-api.threads.js +5 -5
  52. package/dist/{send-iJ0B4bAX.js → send-BFpa_DHn.js} +21 -12
  53. package/dist/{send.components-D0I39utR.js → send.components-BaXQJKWT.js} +15 -10
  54. package/dist/{send.outbound-Bj61Q3a0.js → send.outbound-BlA6VzSE.js} +22 -12
  55. package/dist/{send.receipt-BpBQED_5.js → send.receipt-aegk69an.js} +59 -75
  56. package/dist/{send.shared-wAwMhHDN.js → send.shared-Xz7IMVpu.js} +44 -20
  57. package/dist/{sender-identity-CEsfDF0L.js → sender-identity-C62lBe18.js} +1 -1
  58. package/dist/session-binding-contract-api.js +1 -1
  59. package/dist/setup-plugin-api.js +1 -1
  60. package/dist/{shared-BQVKfnev.js → shared-BLYBt5SA.js} +3 -3
  61. package/dist/{subagent-hooks-B-bJxxGg.js → subagent-hooks-DKJ4p-Oy.js} +2 -2
  62. package/dist/subagent-hooks-api.js +1 -1
  63. package/dist/{system-events-DVnqSyTt.js → system-events-DKwfE-2z.js} +2 -2
  64. package/dist/{target-resolver-CwHaSLt3.js → target-resolver-BQyEHyeS.js} +2 -2
  65. package/dist/targets-DvItNIvP.js +3 -0
  66. package/dist/thread-binding-api-BDZJD4na.js +4 -0
  67. package/dist/thread-binding-api.js +1 -3
  68. package/dist/{thread-bindings-DsTxqu3E.js → thread-bindings-BbzjUuNB.js} +26 -43
  69. package/dist/{thread-bindings.discord-api-BjIf9lGs.js → thread-bindings.discord-api-DJD3qwIN.js} +8 -10
  70. package/dist/{thread-bindings.manager-Bwmtqc3E.js → thread-bindings.manager-BIBm6bQd.js} +4 -4
  71. package/dist/{thread-bindings.session-updates-Chs3HgGa.js → thread-bindings.session-updates-eRcJlzIU.js} +1 -1
  72. package/dist/{thread-bindings.state-iSWjCYjB.js → thread-bindings.state-9VAxhVf6.js} +3 -14
  73. package/dist/{timeouts-C5TBc_9x.js → timeouts-ZRc9Lw4y.js} +3 -15
  74. package/dist/timeouts.js +1 -1
  75. package/dist/{transcripts-source-BvvM5Ns9.js → transcripts-source-CLZ5X8fh.js} +1 -1
  76. package/dist/transcripts-source-api.js +1 -1
  77. package/dist/{typing-BVcJgILg.js → typing-BnYT7Szq.js} +2 -2
  78. package/node_modules/p-map/index.d.ts +155 -0
  79. package/node_modules/p-map/index.js +284 -0
  80. package/node_modules/p-map/license +9 -0
  81. package/node_modules/p-map/package.json +57 -0
  82. package/node_modules/p-map/readme.md +190 -0
  83. package/npm-shrinkwrap.json +16 -3
  84. package/openclaw.plugin.json +70 -2
  85. package/package.json +6 -4
  86. package/dist/config-api-JiPdJeb0.js +0 -2
  87. package/dist/conversation-identity-DWC5d8Sy.js +0 -272
  88. package/dist/pluralkit-DcD-3EcL.js +0 -24
  89. package/dist/preview-streaming-DXT8oJdo.js +0 -8
  90. package/dist/provider.runtime-WrJXikSv.js +0 -2
  91. package/dist/runtime-api.monitor-C2_WVtJf.js +0 -6
  92. package/dist/targets-69w7c3vU.js +0 -3
@@ -1,31 +1,30 @@
1
- import { d as discord_exports, dt as createChannelMessage, gt as editChannelMessage, mt as deleteChannelMessage, qt as resolveDiscordChannelId, u as chunkDiscordTextWithMode } from "./send.receipt-BpBQED_5.js";
1
+ import { Yt as resolveDiscordChannelId, d as discord_exports, gt as deleteChannelMessage, pt as createChannelMessage, u as chunkDiscordTextWithMode, vt as editChannelMessage } from "./send.receipt-aegk69an.js";
2
2
  import { f as resolveDiscordMaxLinesPerMessage } from "./accounts-P19jm1lf.js";
3
- import { I as createDiscordRestClient, L as createDiscordRuntimeAccountContext, _ as resolveDiscordMessageFlags, d as resolveDiscordTargetChannelId } from "./send.shared-wAwMhHDN.js";
4
- import { S as resolveTimestampMs, a as normalizeDiscordSlug, r as normalizeDiscordAllowList } from "./allow-list-C-MqM-B_.js";
5
- import { a as removeReactionDiscord, f as editMessageDiscord, r as reactMessageDiscord } from "./send-iJ0B4bAX.js";
6
- import { i as discordTextHasBroadcastMention } from "./send.outbound-Bj61Q3a0.js";
7
- import "./targets-69w7c3vU.js";
8
- import { t as resolveDiscordConversationIdentity } from "./conversation-identity-DWC5d8Sy.js";
3
+ import { I as createDiscordRestClient, L as createDiscordRuntimeAccountContext, _ as resolveDiscordMessageFlags, d as resolveDiscordTargetChannelId } from "./send.shared-Xz7IMVpu.js";
4
+ import { C as resolveTimestampMs, a as normalizeDiscordSlug, r as normalizeDiscordAllowList } from "./allow-list-B7qOH5xu.js";
5
+ import { a as removeReactionDiscord, f as editMessageDiscord, r as reactMessageDiscord } from "./send-BFpa_DHn.js";
6
+ import { i as discordTextHasBroadcastMention } from "./send.outbound-BlA6VzSE.js";
7
+ import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-ZRc9Lw4y.js";
8
+ import "./targets-DvItNIvP.js";
9
+ import { t as resolveDiscordConversationIdentity } from "./conversation-identity-BpkAzklg.js";
9
10
  import { t as beginDiscordInboundEventDeliveryCorrelation } from "./inbound-event-delivery-CEPlt2uz.js";
10
- import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-C5TBc_9x.js";
11
- import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-82RWi_SR.js";
12
- import { t as resolveDiscordPreviewStreamMode } from "./preview-streaming-DXT8oJdo.js";
13
- import { C as resolveDiscordAutoThreadReplyPlan, T as resolveDiscordThreadStarter, a as buildGuildLabel, h as createDiscordSupplementalContextAccessChecker, i as buildDirectLabel, m as buildDiscordInboundAccessContext, n as deliverDiscordReply, o as resolveReplyContext, r as sanitizeDiscordFrontChannelReplyPayloads } from "./provider-BkfbC9FQ.js";
14
- import { r as resolveDiscordMessageText, s as resolveReferencedReplyMediaList } from "./message-utils-B_3yA5bN.js";
15
- import { n as createDiscordReplyTypingFeedback } from "./message-handler-DcgvugZt.js";
11
+ import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-Czny0Fae.js";
12
+ import { D as resolveDiscordThreadStarter, T as resolveDiscordAutoThreadReplyPlan, _ as createDiscordSupplementalContextAccessChecker, a as buildDirectLabel, g as buildDiscordInboundAccessContext, i as sanitizeDiscordFrontChannelReplyPayloads, n as deliverDiscordReply, o as buildGuildLabel, r as formatDiscordReplyDeliveryFailure, s as resolveReplyContext } from "./provider-DWvDNb4J.js";
13
+ import { r as resolveDiscordMessageText, s as resolveReferencedReplyMediaList } from "./message-utils-D4j07taS.js";
14
+ import { n as createDiscordReplyTypingFeedback, r as DiscordRetryableInboundError } from "./message-handler-CIxWq5Yh.js";
16
15
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
17
16
  import { buildAgentSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
18
- import { MessageFlags } from "discord-api-types/v10";
19
17
  import { evaluateSupplementalContextVisibility } from "openclaw/plugin-sdk/security-runtime";
18
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
20
19
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
21
20
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
22
21
  import { getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
23
22
  import { resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
24
- import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
23
+ import { danger, logVerbose, shouldLogVerbose, sleep, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
25
24
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
25
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
27
26
  import { convertMarkdownTables, stripInlineDirectiveTagsForDelivery, stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
28
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, isChannelProgressDraftWorkToolName, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
27
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, isChannelProgressDraftWorkToolName, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelPreviewStreamMode, resolveChannelProgressDraftConfig, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewCommandText, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingProgressNarration, resolveChannelStreamingSuppressDefaultToolProgressMessages, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
29
28
  import { recordInboundSession, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/conversation-runtime";
30
29
  import { EmbeddedBlockChunker, resolveAckReaction, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
31
30
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
@@ -71,6 +70,63 @@ function queueInitialDiscordAckReaction(params) {
71
70
  });
72
71
  }
73
72
  //#endregion
73
+ //#region extensions/discord/src/monitor/message-handler.retry.ts
74
+ const REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE = /^reply session initialization conflicted for \S+$/u;
75
+ const DISCORD_SESSION_INIT_CONFLICT_RETRY_DELAYS_MS = [
76
+ 250,
77
+ 1e3,
78
+ 2500
79
+ ];
80
+ const DISCORD_SESSION_CONFLICT_FAILURE_TEXT = "⚠️ Couldn't process this message because the session stayed busy. Please try again in a moment.";
81
+ function isReplySessionInitConflictError(error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ return REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE.test(message);
84
+ }
85
+ var DiscordReplySessionConflictExhaustedError = class extends DiscordRetryableInboundError {
86
+ constructor(message, options) {
87
+ super(message, options);
88
+ this.name = "DiscordReplySessionConflictExhaustedError";
89
+ }
90
+ };
91
+ async function dispatchDiscordReplyWithSessionConflictRetry(params) {
92
+ for (let retryIndex = 0;; retryIndex += 1) try {
93
+ return await params.dispatch();
94
+ } catch (error) {
95
+ if (!isReplySessionInitConflictError(error)) throw error;
96
+ const delayMs = DISCORD_SESSION_INIT_CONFLICT_RETRY_DELAYS_MS[retryIndex];
97
+ if (delayMs === void 0) throw new DiscordReplySessionConflictExhaustedError(`discord: reply session init conflict persisted after shared and channel retries: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
98
+ params.onRetry?.(retryIndex + 1, delayMs);
99
+ await sleepWithAbort(delayMs, params.abortSignal);
100
+ }
101
+ }
102
+ function withDiscordSessionRetry(dispatch, abortSignal) {
103
+ return (dispatchParams) => dispatchDiscordReplyWithSessionConflictRetry({
104
+ dispatch: () => dispatch(dispatchParams),
105
+ abortSignal,
106
+ onRetry: (attempt, delayMs) => {
107
+ logVerbose(`discord: reply session init conflict; retrying dispatch ${attempt} after ${delayMs}ms`);
108
+ }
109
+ });
110
+ }
111
+ async function completeDiscordSessionConflict(error, deliver, onDeliveryError) {
112
+ if (!(error instanceof DiscordReplySessionConflictExhaustedError)) return false;
113
+ try {
114
+ await deliver({
115
+ text: DISCORD_SESSION_CONFLICT_FAILURE_TEXT,
116
+ isError: true
117
+ }, { kind: "final" });
118
+ return true;
119
+ } catch (deliveryError) {
120
+ onDeliveryError(deliveryError, { kind: "final" });
121
+ return false;
122
+ }
123
+ }
124
+ function removeDiscordReplayHistoryEntry(historyMap, historyKey, messageId) {
125
+ const history = historyMap.get(historyKey);
126
+ if (!history) return;
127
+ for (let index = history.length - 1; index >= 0; index -= 1) if (history[index]?.messageId === messageId) history.splice(index, 1);
128
+ }
129
+ //#endregion
74
130
  //#region extensions/discord/src/monitor/message-handler.context.ts
75
131
  function normalizeDiscordDmOwnerEntry(entry) {
76
132
  const candidate = normalizeDiscordAllowList([entry], [
@@ -98,7 +154,6 @@ async function buildDiscordMessageProcessContext(params) {
98
154
  const threadParentInheritanceEnabled = discordConfig?.thread?.inheritParent ?? false;
99
155
  const forumContextLine = Boolean(threadChannelId && isForumParent && forumParentSlug) && message.id === threadChannelId ? `[Forum parent: #${forumParentSlug}]` : null;
100
156
  const groupChannel = isGuildMessage && displayChannelSlug ? `#${displayChannelSlug}` : void 0;
101
- const groupSubject = isDirectMessage ? void 0 : groupChannel;
102
157
  const senderName = sender.isPluralKit ? sender.name ?? author.username : data.member?.nickname ?? author.globalName ?? author.username;
103
158
  const senderUsername = sender.isPluralKit ? sender.tag ?? sender.name ?? author.username : author.username;
104
159
  const { groupSystemPrompt, ownerAllowFrom, untrustedContext } = buildDiscordInboundAccessContext({
@@ -136,7 +191,6 @@ async function buildDiscordMessageProcessContext(params) {
136
191
  sessionKey: route.sessionKey
137
192
  });
138
193
  const channelHistory = createChannelHistoryWindow({ historyMap: guildHistories });
139
- const isRoomEvent = ctx.inboundEventKind === "room_event";
140
194
  let combinedBody = formatInboundEnvelope({
141
195
  channel: "Discord",
142
196
  from: fromLabel,
@@ -147,21 +201,24 @@ async function buildDiscordMessageProcessContext(params) {
147
201
  previousTimestamp,
148
202
  envelope: envelopeOptions
149
203
  });
150
- const shouldIncludeChannelHistory = !isDirectMessage && (isRoomEvent || !(isGuildMessage && channelConfig?.autoThread && !threadChannel));
151
- if (shouldIncludeChannelHistory) combinedBody = channelHistory.buildPendingContext({
152
- historyKey: messageChannelId,
153
- limit: historyLimit,
154
- currentMessage: combinedBody,
155
- formatEntry: (entry) => formatInboundEnvelope({
156
- channel: "Discord",
157
- from: fromLabel,
158
- timestamp: entry.timestamp,
159
- body: `${entry.body} [id:${entry.messageId ?? "unknown"} channel:${messageChannelId}]`,
160
- chatType: "channel",
161
- senderLabel: entry.sender,
162
- envelope: envelopeOptions
163
- })
164
- });
204
+ const shouldIncludeChannelHistory = !isDirectMessage && (ctx.inboundEventKind === "room_event" || !(isGuildMessage && channelConfig?.autoThread && !threadChannel));
205
+ if (shouldIncludeChannelHistory) {
206
+ removeDiscordReplayHistoryEntry(guildHistories, messageChannelId, message.id);
207
+ combinedBody = channelHistory.buildPendingContext({
208
+ historyKey: messageChannelId,
209
+ limit: historyLimit,
210
+ currentMessage: combinedBody,
211
+ formatEntry: (entry) => formatInboundEnvelope({
212
+ channel: "Discord",
213
+ from: fromLabel,
214
+ timestamp: entry.timestamp,
215
+ body: `${entry.body} [id:${entry.messageId ?? "unknown"} channel:${messageChannelId}]`,
216
+ chatType: "channel",
217
+ senderLabel: entry.sender,
218
+ envelope: envelopeOptions
219
+ })
220
+ });
221
+ }
165
222
  const replyContext = resolveReplyContext(message, resolveDiscordMessageText);
166
223
  const replySenderAllowed = replyContext ? isSupplementalContextSenderAllowed({
167
224
  id: replyContext.senderId,
@@ -228,7 +285,7 @@ async function buildDiscordMessageProcessContext(params) {
228
285
  message,
229
286
  messageChannelId,
230
287
  isGuildMessage,
231
- channelConfig: isRoomEvent ? null : channelConfig,
288
+ channelConfig: ctx.inboundEventKind === "room_event" ? null : channelConfig,
232
289
  threadChannel,
233
290
  channelType: channelInfo?.type,
234
291
  channelName: channelInfo?.name,
@@ -287,6 +344,7 @@ async function buildDiscordMessageProcessContext(params) {
287
344
  conversation: {
288
345
  kind: isDirectMessage ? "direct" : "channel",
289
346
  id: messageChannelId,
347
+ nativeChannelId: messageChannelId,
290
348
  label: fromLabel,
291
349
  spaceId: isGuildMessage ? (guildInfo?.id ?? guildSlug) || void 0 : void 0,
292
350
  parentId: threadChannel ? threadParentId : void 0,
@@ -356,7 +414,7 @@ async function buildDiscordMessageProcessContext(params) {
356
414
  },
357
415
  extra: {
358
416
  ...preflightAudioTranscript !== void 0 ? { Transcript: preflightAudioTranscript } : {},
359
- GroupSubject: groupSubject,
417
+ GroupSubject: isDirectMessage ? void 0 : groupChannel,
360
418
  GroupChannel: groupChannel,
361
419
  ...isGuildMessage ? { GroupRequireMention: ctx.groupRequireMention } : {},
362
420
  UntrustedStructuredContext: untrustedContext,
@@ -364,7 +422,7 @@ async function buildDiscordMessageProcessContext(params) {
364
422
  }
365
423
  });
366
424
  const persistedSessionKey = ctxPayload.SessionKey ?? route.sessionKey;
367
- if (isRoomEvent && shouldIncludeChannelHistory) await channelHistory.recordWithMedia({
425
+ if (ctx.inboundEventKind === "room_event" && shouldIncludeChannelHistory) await channelHistory.recordWithMedia({
368
426
  historyKey: messageChannelId,
369
427
  limit: historyLimit,
370
428
  entry: {
@@ -435,7 +493,11 @@ function createDiscordDraftStream(params) {
435
493
  };
436
494
  let streamMessageId;
437
495
  let lastSentText = "";
496
+ let streamGeneration = 0;
497
+ let activeCreateGeneration;
498
+ let discardActiveCreate = false;
438
499
  const sendOrEditStreamMessage = async (text) => {
500
+ const generation = streamGeneration;
439
501
  if (streamState.stopped && !streamState.final) return false;
440
502
  const trimmed = text.trimEnd();
441
503
  if (!trimmed) return false;
@@ -463,12 +525,24 @@ function createDiscordDraftStream(params) {
463
525
  message_id: replyToMessageId,
464
526
  fail_if_not_exists: false
465
527
  } : void 0;
528
+ activeCreateGeneration = generation;
466
529
  const sentMessageId = (await createChannelMessage(rest, channelId, { body: {
467
530
  content: trimmed,
468
531
  allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
469
532
  ...flags ? { flags } : {},
470
533
  ...messageReference ? { message_reference: messageReference } : {}
471
534
  } }))?.id;
535
+ const shouldDiscardStaleCreate = activeCreateGeneration === generation && discardActiveCreate;
536
+ activeCreateGeneration = void 0;
537
+ discardActiveCreate = false;
538
+ if (generation !== streamGeneration) {
539
+ if (shouldDiscardStaleCreate && typeof sentMessageId === "string" && sentMessageId) try {
540
+ await deleteChannelMessage(rest, channelId, sentMessageId);
541
+ } catch (err) {
542
+ params.warn?.(`discord stale stream preview cleanup failed: ${formatErrorMessage(err)}`);
543
+ }
544
+ return true;
545
+ }
472
546
  if (typeof sentMessageId !== "string" || !sentMessageId) {
473
547
  streamState.stopped = true;
474
548
  params.warn?.("discord stream preview stopped (missing message id from send)");
@@ -477,6 +551,11 @@ function createDiscordDraftStream(params) {
477
551
  streamMessageId = sentMessageId;
478
552
  return true;
479
553
  } catch (err) {
554
+ if (activeCreateGeneration === generation) {
555
+ activeCreateGeneration = void 0;
556
+ discardActiveCreate = false;
557
+ }
558
+ if (generation !== streamGeneration) return true;
480
559
  streamState.stopped = true;
481
560
  params.warn?.(`discord stream preview failed: ${formatErrorMessage(err)}`);
482
561
  return false;
@@ -501,10 +580,15 @@ function createDiscordDraftStream(params) {
501
580
  warn: params.warn,
502
581
  warnPrefix: "discord stream preview cleanup failed"
503
582
  });
504
- const forceNewMessage = () => {
583
+ const forceNewMessage = (mode = "preserve") => {
584
+ if (mode === "discard" && activeCreateGeneration !== void 0) discardActiveCreate = true;
585
+ streamGeneration += 1;
586
+ streamState.stopped = false;
587
+ streamState.final = false;
505
588
  streamMessageId = void 0;
506
589
  lastSentText = "";
507
590
  loop.resetPending();
591
+ loop.resetThrottleWindow();
508
592
  };
509
593
  const deleteCurrentMessage = async () => {
510
594
  loop.resetPending();
@@ -534,6 +618,12 @@ function createDiscordDraftStream(params) {
534
618
  };
535
619
  }
536
620
  //#endregion
621
+ //#region extensions/discord/src/preview-streaming.ts
622
+ function resolveDiscordPreviewStreamMode(params = {}) {
623
+ if (params.streaming === void 0) return "progress";
624
+ return resolveChannelPreviewStreamMode(params, "off");
625
+ }
626
+ //#endregion
537
627
  //#region extensions/discord/src/monitor/message-handler.draft-preview.ts
538
628
  function createDiscordDraftPreviewController(params) {
539
629
  const discordStreamMode = resolveDiscordPreviewStreamMode(params.discordConfig);
@@ -560,7 +650,14 @@ function createDiscordDraftPreviewController(params) {
560
650
  let finalizedViaPreviewMessage = false;
561
651
  let finalReplyDelivered = false;
562
652
  let progressDraftStartedBeforeFinal = false;
563
- const previewToolProgressEnabled = Boolean(draftStream) && resolveChannelStreamingPreviewToolProgress(params.discordConfig);
653
+ let progressDraftCollapsed = false;
654
+ let progressNarratorLifecycle;
655
+ const progressConfig = resolveChannelProgressDraftConfig(params.discordConfig);
656
+ const progressHasExplicitLabel = progressConfig.label !== void 0 || progressConfig.labels !== void 0;
657
+ const progressToolDefault = progressConfig.toolProgress ?? progressHasExplicitLabel;
658
+ const previewToolProgressEnabled = Boolean(draftStream) && resolveChannelStreamingPreviewToolProgress(params.discordConfig, discordStreamMode === "progress" ? progressToolDefault : true);
659
+ const narrationProgressEnabled = Boolean(draftStream) && discordStreamMode === "progress" && resolveChannelStreamingProgressNarration(params.discordConfig);
660
+ const narrationHideCommandText = narrationProgressEnabled && resolveChannelStreamingPreviewCommandText(params.discordConfig) === "status";
564
661
  const suppressDefaultToolProgressMessages = Boolean(draftStream) && resolveChannelStreamingSuppressDefaultToolProgressMessages(params.discordConfig, {
565
662
  draftStreamActive: true,
566
663
  previewToolProgressEnabled
@@ -573,7 +670,7 @@ function createDiscordDraftPreviewController(params) {
573
670
  seed: progressSeed,
574
671
  reasoningLinePrefix: "🧠 ",
575
672
  commentaryLinePrefix: "💬 ",
576
- reasoningGate: true,
673
+ reasoningGate: previewToolProgressEnabled,
577
674
  commentaryItalics: false,
578
675
  update: async (previewText, options) => {
579
676
  lastPartialText = previewText;
@@ -605,23 +702,42 @@ function createDiscordDraftPreviewController(params) {
605
702
  }
606
703
  resetProgressState();
607
704
  };
705
+ const pushPreambleHeadline = async (text, options) => {
706
+ if (discordStreamMode === "progress") await progressDraft.pushPreambleHeadline(text, options);
707
+ };
608
708
  return {
609
709
  draftStream,
610
710
  previewToolProgressEnabled,
711
+ narrationProgressEnabled,
712
+ narrationHideCommandText,
611
713
  commentaryProgressEnabled: progressDraft.commentaryProgressEnabled,
612
714
  suppressDefaultToolProgressMessages,
613
715
  get isProgressMode() {
614
716
  return discordStreamMode === "progress";
615
717
  },
616
718
  get hasProgressDraftStarted() {
617
- return progressDraft.hasStarted || progressDraftStartedBeforeFinal;
719
+ return progressDraft.hasStarted;
720
+ },
721
+ get isProgressDraftVisible() {
722
+ return progressDraft.isVisible;
723
+ },
724
+ get hasProgressDraftToCollapse() {
725
+ return !progressDraftCollapsed && (progressDraft.hasStarted || progressDraftStartedBeforeFinal);
726
+ },
727
+ markProgressDraftCollapsed() {
728
+ progressDraftCollapsed = true;
729
+ progressDraftStartedBeforeFinal = false;
618
730
  },
619
731
  get finalizedViaPreviewMessage() {
620
732
  return finalizedViaPreviewMessage;
621
733
  },
734
+ setProgressNarratorLifecycle(lifecycle) {
735
+ progressNarratorLifecycle = lifecycle;
736
+ },
622
737
  markFinalReplyStarted() {
623
738
  progressDraftStartedBeforeFinal ||= progressDraft.hasStarted;
624
739
  progressDraft.markFinalReplyStarted();
740
+ progressNarratorLifecycle?.stopTurn();
625
741
  },
626
742
  markFinalReplyDelivered() {
627
743
  finalReplyDelivered = true;
@@ -637,6 +753,15 @@ function createDiscordDraftPreviewController(params) {
637
753
  async pushReasoningProgress(text, options) {
638
754
  await progressDraft.pushReasoningProgress(text, options);
639
755
  },
756
+ async pushNarrationProgress(text) {
757
+ await progressDraft.pushNarrationProgress(text);
758
+ },
759
+ pushPreambleHeadline,
760
+ async pushPreambleItemEvent(payload, noteCommentary) {
761
+ await pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
762
+ if (!progressDraft.commentaryProgressEnabled) return;
763
+ if (await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) noteCommentary(payload.itemId, payload.progressText);
764
+ },
640
765
  async pushCommentaryProgress(text, options) {
641
766
  await progressDraft.pushCommentaryProgress(text, options);
642
767
  },
@@ -650,7 +775,7 @@ function createDiscordDraftPreviewController(params) {
650
775
  });
651
776
  if (!chunks.length && formatted) chunks.push(formatted);
652
777
  if (chunks.length !== 1) return;
653
- const trimmed = chunks[0].trim();
778
+ const trimmed = expectDefined(chunks.at(0), "single Discord preview chunk").trim();
654
779
  if (!trimmed) return;
655
780
  const currentPreviewText = discordStreamMode === "block" ? draftText : lastPartialText;
656
781
  if (currentPreviewText && currentPreviewText.startsWith(trimmed) && trimmed.length < currentPreviewText.length) return;
@@ -696,9 +821,20 @@ function createDiscordDraftPreviewController(params) {
696
821
  });
697
822
  },
698
823
  handleAssistantMessageBoundary() {
699
- progressDraft.beginNewTurn();
700
- if (discordStreamMode === "progress") return;
824
+ const beganNewTurn = progressDraft.beginNewTurn();
825
+ if (beganNewTurn) {
826
+ progressDraftCollapsed = false;
827
+ progressDraftStartedBeforeFinal = false;
828
+ finalReplyDelivered = false;
829
+ finalizedViaPreviewMessage = false;
830
+ progressNarratorLifecycle?.beginTurn();
831
+ }
832
+ if (discordStreamMode === "progress") {
833
+ if (beganNewTurn) draftStream?.forceNewMessage("discard");
834
+ return beganNewTurn;
835
+ }
701
836
  forceNewMessageIfNeeded();
837
+ return beganNewTurn;
702
838
  },
703
839
  async flush() {
704
840
  if (!draftStream) return;
@@ -718,7 +854,7 @@ function createDiscordDraftPreviewController(params) {
718
854
  try {
719
855
  progressDraft.cancel();
720
856
  if (!finalReplyDelivered) await draftStream?.discardPending();
721
- if (!finalReplyDelivered && !finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();
857
+ if (!finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();
722
858
  } catch (err) {
723
859
  params.log(`discord: draft cleanup failed: ${String(err)}`);
724
860
  }
@@ -739,14 +875,13 @@ const TARGETED_ONLY_ALLOWED_MENTIONS = { parse: ["users", "roles"] };
739
875
  function isProcessAborted(abortSignal) {
740
876
  return Boolean(abortSignal?.aborted);
741
877
  }
742
- function formatDiscordReplyDeliveryFailure(params) {
743
- const context = [`target=${params.target}`, params.sessionKey ? `session=${params.sessionKey}` : void 0].filter(Boolean).join(" ");
744
- return `discord ${params.kind} reply failed (${context}): ${String(params.err)}`;
745
- }
746
878
  function isFallbackOnlyToolWarningFinal(payload) {
747
879
  if (payload.isError !== true || !isReplyPayloadNonTerminalToolErrorWarning(payload)) return false;
748
880
  return !resolveSendableOutboundReplyParts(payload).hasMedia;
749
881
  }
882
+ function isFailedProgress(payload) {
883
+ return payload.phase === "error" || payload.status === "failed" || payload.status === "error" || typeof payload.exitCode === "number" && payload.exitCode !== 0;
884
+ }
750
885
  function formatDiscordReplySkip(params) {
751
886
  const context = [`target=${params.target}`, params.sessionKey ? `session=${params.sessionKey}` : void 0].filter(Boolean).join(" ");
752
887
  return `discord ${params.kind} reply skipped (${params.reason}): ${context}`;
@@ -776,7 +911,7 @@ async function processDiscordMessageInner(ctx, observer) {
776
911
  }
777
912
  const boundThreadId = ctx.threadBinding?.conversation?.conversationId?.trim();
778
913
  if (boundThreadId && typeof threadBindings.touchThread === "function") threadBindings.touchThread({ threadId: boundThreadId });
779
- const { dispatchReplyWithBufferedBlockDispatcher } = await loadReplyRuntime();
914
+ const { dispatchReplyWithBufferedBlockDispatcher: dispatchReply } = await loadReplyRuntime();
780
915
  const sourceReplyDeliveryMode = resolveChannelMessageSourceReplyDeliveryMode({
781
916
  cfg,
782
917
  ctx: {
@@ -1016,7 +1151,6 @@ async function processDiscordMessageInner(ctx, observer) {
1016
1151
  log: logVerbose
1017
1152
  });
1018
1153
  let shouldYieldDraftProgress = () => false;
1019
- const finalPreviewFlags = discordConfig?.suppressEmbeds ?? true ? MessageFlags.SuppressEmbeds : void 0;
1020
1154
  let finalReplyStartNotified = false;
1021
1155
  const notifyFinalReplyStart = () => {
1022
1156
  if (finalReplyStartNotified) return;
@@ -1061,11 +1195,12 @@ async function processDiscordMessageInner(ctx, observer) {
1061
1195
  })();
1062
1196
  const reasoningDurableEnabled = reasoningLevel === "on";
1063
1197
  const reasoningWindowEnabled = reasoningLevel === "stream";
1064
- const progressTurnStartedAt = Date.now();
1198
+ let progressTurnStartedAt = Date.now();
1065
1199
  let progressReasoningSteps = 0;
1066
1200
  let progressToolCalls = 0;
1067
1201
  let progressCommentaryNotes = 0;
1068
- let persistentReasoningDelivered = false;
1202
+ let progressReceiptLine;
1203
+ let clearProgressDraftAfterFinalDelivery = false;
1069
1204
  const seenCommentaryIds = /* @__PURE__ */ new Set();
1070
1205
  let lastCommentaryNoteText = "";
1071
1206
  const noteWindowCommentary = (itemId, noteText) => {
@@ -1089,6 +1224,24 @@ async function processDiscordMessageInner(ctx, observer) {
1089
1224
  progressReasoningSteps += 1;
1090
1225
  }
1091
1226
  };
1227
+ const resetProgressTurnState = () => {
1228
+ finalReplyStartNotified = false;
1229
+ userFacingFinalDelivered = false;
1230
+ userFacingFinalDeliveryFailed = false;
1231
+ pendingToolWarningFinal = void 0;
1232
+ progressTurnStartedAt = Date.now();
1233
+ progressReasoningSteps = 0;
1234
+ progressToolCalls = 0;
1235
+ progressCommentaryNotes = 0;
1236
+ progressReceiptLine = void 0;
1237
+ clearProgressDraftAfterFinalDelivery = false;
1238
+ seenCommentaryIds.clear();
1239
+ lastCommentaryNoteText = "";
1240
+ windowReasoningOpen = false;
1241
+ };
1242
+ const handleAssistantMessageBoundary = () => {
1243
+ if (draftPreview.handleAssistantMessageBoundary()) resetProgressTurnState();
1244
+ };
1092
1245
  const buildProgressSummaryLine = () => {
1093
1246
  closePendingWindowThought();
1094
1247
  const seconds = Math.max(1, Math.round((Date.now() - progressTurnStartedAt) / 1e3));
@@ -1161,7 +1314,6 @@ async function processDiscordMessageInner(ctx, observer) {
1161
1314
  kind: "block"
1162
1315
  });
1163
1316
  replyReference.markSent();
1164
- persistentReasoningDelivered = true;
1165
1317
  return { visibleReplySent: true };
1166
1318
  }
1167
1319
  if (isFinal && !options?.allowFallbackOnlyToolWarning && isFallbackOnlyToolWarningFinal(payload)) {
@@ -1193,51 +1345,10 @@ async function processDiscordMessageInner(ctx, observer) {
1193
1345
  if (draftStream && draftPreview.isProgressMode && info.kind === "block") {
1194
1346
  if (!resolveSendableOutboundReplyParts(deliverablePayload).hasMedia && !deliverablePayload.isError) return { visibleReplySent: false };
1195
1347
  }
1196
- if (draftStream && isFinal && draftPreview.isProgressMode && draftPreview.hasProgressDraftStarted && !deliverablePayload.isError && draftStream) {
1348
+ if (draftStream && isFinal && draftPreview.isProgressMode && !deliverablePayload.isError && draftPreview.hasProgressDraftToCollapse && draftStream) {
1197
1349
  await draftPreview.flush();
1198
- if (persistentReasoningDelivered) {
1199
- await draftStream.clear();
1200
- await deliverDiscordReply({
1201
- cfg,
1202
- replies: [{ text: buildProgressSummaryLine() }],
1203
- target: deliverTarget,
1204
- token,
1205
- accountId,
1206
- rest: deliveryRest,
1207
- runtime,
1208
- replyToId: replyReference.use(),
1209
- replyToMode,
1210
- textLimit,
1211
- maxLinesPerMessage,
1212
- tableMode,
1213
- chunkMode,
1214
- sessionKey: ctxPayload.SessionKey,
1215
- threadBindings,
1216
- mediaLocalRoots,
1217
- kind: "block"
1218
- });
1219
- replyReference.markSent();
1220
- draftPreview.markPreviewFinalized();
1221
- } else {
1222
- const draftId = draftStream.messageId();
1223
- if (draftId !== void 0) {
1224
- await draftStream.seal();
1225
- try {
1226
- await editMessageDiscord(deliverChannelId, draftId, {
1227
- content: buildProgressSummaryLine(),
1228
- ...finalPreviewFlags ? { flags: finalPreviewFlags } : {}
1229
- }, {
1230
- cfg,
1231
- accountId,
1232
- rest: deliveryRest
1233
- });
1234
- draftPreview.markPreviewFinalized();
1235
- } catch (err) {
1236
- logVerbose(`discord: progress draft summary edit failed; clearing draft (${String(err)})`);
1237
- await draftStream.clear();
1238
- }
1239
- }
1240
- }
1350
+ progressReceiptLine = buildProgressSummaryLine();
1351
+ clearProgressDraftAfterFinalDelivery = true;
1241
1352
  }
1242
1353
  if (draftStream && isFinal && !draftPreview.isProgressMode && !deliverablePayload.isError) {
1243
1354
  const ttsSupplement = getReplyPayloadTtsSupplement(deliverablePayload);
@@ -1319,9 +1430,14 @@ async function processDiscordMessageInner(ctx, observer) {
1319
1430
  }
1320
1431
  const replyToId = replyReference.use();
1321
1432
  if (isFinal) notifyFinalReplyStart();
1433
+ const receiptLine = isFinal && deliverablePayload.isError !== true ? progressReceiptLine : void 0;
1434
+ const payloadForDelivery = receiptLine ? {
1435
+ ...deliverablePayload,
1436
+ text: deliverablePayload.text?.trim() ? `${deliverablePayload.text.trimEnd()}\n${receiptLine}` : receiptLine
1437
+ } : deliverablePayload;
1322
1438
  await deliverDiscordReply({
1323
1439
  cfg,
1324
- replies: [deliverablePayload],
1440
+ replies: [payloadForDelivery],
1325
1441
  target: deliverTarget,
1326
1442
  token,
1327
1443
  accountId,
@@ -1339,7 +1455,18 @@ async function processDiscordMessageInner(ctx, observer) {
1339
1455
  kind: info.kind
1340
1456
  });
1341
1457
  replyReference.markSent();
1342
- if (isFinal && deliverablePayload.isError !== true) markUserFacingFinalDelivered();
1458
+ if (isFinal && deliverablePayload.isError !== true) {
1459
+ if (receiptLine) {
1460
+ progressReceiptLine = void 0;
1461
+ draftPreview.markProgressDraftCollapsed();
1462
+ }
1463
+ markUserFacingFinalDelivered();
1464
+ if (clearProgressDraftAfterFinalDelivery) {
1465
+ clearProgressDraftAfterFinalDelivery = false;
1466
+ await draftStream?.discardPending();
1467
+ await draftStream?.clear();
1468
+ }
1469
+ }
1343
1470
  return { visibleReplySent: true };
1344
1471
  };
1345
1472
  const onDiscordDeliveryError = (err, info) => {
@@ -1387,7 +1514,7 @@ async function processDiscordMessageInner(ctx, observer) {
1387
1514
  ctxPayload,
1388
1515
  recordInboundSession,
1389
1516
  afterRecord: queueInitialAckReactionAfterRecord,
1390
- dispatchReplyWithBufferedBlockDispatcher,
1517
+ dispatchReplyWithBufferedBlockDispatcher: withDiscordSessionRetry(dispatchReply, abortSignal),
1391
1518
  dispatcherOptions: {
1392
1519
  ...replyPipeline,
1393
1520
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
@@ -1416,20 +1543,28 @@ async function processDiscordMessageInner(ctx, observer) {
1416
1543
  allowProgressCallbacksWhenSourceDeliverySuppressed: sourceRepliesAreToolOnly && draftPreview.draftStream && draftPreview.isProgressMode ? true : void 0,
1417
1544
  disableBlockStreaming: sourceRepliesAreToolOnly ? true : draftPreview.disableBlockStreamingForDraft ?? (typeof resolvedBlockStreamingEnabled === "boolean" ? !resolvedBlockStreamingEnabled : void 0),
1418
1545
  onPartialReply: draftPreview.draftStream && !draftPreview.isProgressMode ? (payload) => draftPreview.updateFromPartial(payload.text) : void 0,
1419
- onAssistantMessageStart: draftPreview.draftStream ? () => draftPreview.handleAssistantMessageBoundary() : void 0,
1546
+ onAssistantMessageStart: draftPreview.draftStream ? handleAssistantMessageBoundary : void 0,
1420
1547
  onReasoningEnd: draftPreview.draftStream ? () => {
1421
1548
  closePendingWindowThought();
1422
- return draftPreview.handleAssistantMessageBoundary();
1549
+ handleAssistantMessageBoundary();
1423
1550
  } : void 0,
1424
1551
  onModelSelected,
1425
1552
  suppressDefaultToolProgressMessages: sourceRepliesAreToolOnly && statusReactionsExplicitlyEnabled || draftPreview.suppressDefaultToolProgressMessages ? true : void 0,
1426
1553
  allowToolLifecycleWhenProgressHidden: statusReactionsEnabled ? true : void 0,
1427
1554
  commentaryProgressEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
1555
+ progressPreambleEnabled: draftPreview.draftStream && draftPreview.isProgressMode ? true : void 0,
1428
1556
  commentaryPayloadsEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
1429
1557
  reasoningPayloadsEnabled: reasoningDurableEnabled,
1430
1558
  onVerboseProgressVisibility: (isActive) => {
1431
1559
  shouldYieldDraftProgress = isActive;
1432
1560
  },
1561
+ onNarrationUpdate: draftPreview.narrationProgressEnabled ? async (payload) => {
1562
+ if (isProcessAborted(abortSignal) || shouldYieldDraftProgress()) return;
1563
+ await draftPreview.pushNarrationProgress(payload.text);
1564
+ } : void 0,
1565
+ onProgressNarratorLifecycle: draftPreview.narrationProgressEnabled ? (lifecycle) => draftPreview.setProgressNarratorLifecycle(lifecycle) : void 0,
1566
+ isProgressDraftVisible: draftPreview.narrationProgressEnabled ? () => draftPreview.isProgressDraftVisible : void 0,
1567
+ narrationHideCommandText: draftPreview.narrationHideCommandText ? true : void 0,
1433
1568
  onReasoningStream: async (payload) => {
1434
1569
  if (payload?.requiresReasoningProgressOptIn === true && !reasoningWindowEnabled) return;
1435
1570
  if (payload?.text) windowReasoningOpen = true;
@@ -1454,13 +1589,10 @@ async function processDiscordMessageInner(ctx, observer) {
1454
1589
  }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1455
1590
  },
1456
1591
  onItemEvent: async (payload) => {
1592
+ if (isFailedProgress(payload)) return false;
1457
1593
  if (payload.kind === "preamble") {
1458
1594
  if (shouldYieldDraftProgress()) return;
1459
- if (draftPreview.commentaryProgressEnabled && payload.progressText) {
1460
- noteWindowCommentary(payload.itemId, payload.progressText);
1461
- await draftPreview.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId });
1462
- }
1463
- return;
1595
+ return await draftPreview.pushPreambleItemEvent(payload, noteWindowCommentary);
1464
1596
  }
1465
1597
  if (shouldYieldDraftProgress()) return;
1466
1598
  await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
@@ -1499,6 +1631,7 @@ async function processDiscordMessageInner(ctx, observer) {
1499
1631
  }));
1500
1632
  },
1501
1633
  onCommandOutput: async (payload) => {
1634
+ if (isFailedProgress(payload)) return false;
1502
1635
  if (payload.phase !== "end") return;
1503
1636
  if (shouldYieldDraftProgress()) return;
1504
1637
  await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
@@ -1551,6 +1684,7 @@ async function processDiscordMessageInner(ctx, observer) {
1551
1684
  return;
1552
1685
  }
1553
1686
  dispatchError = true;
1687
+ if (await completeDiscordSessionConflict(err, deliverDiscordPayload, onDiscordDeliveryError)) return;
1554
1688
  throw err;
1555
1689
  } finally {
1556
1690
  endDiscordInboundEventDeliveryCorrelation();
@@ -1,5 +1,4 @@
1
- import { o as mergeAbortSignals } from "./timeouts-C5TBc_9x.js";
2
- import { u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-BjIf9lGs.js";
1
+ import { u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-DJD3qwIN.js";
3
2
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
4
3
  import { ComponentType, StickerFormatType } from "discord-api-types/v10";
5
4
  import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
@@ -275,7 +274,7 @@ async function resolveReferencedReplyMediaList(message, maxBytes, options) {
275
274
  }
276
275
  async function fetchDiscordMedia(params) {
277
276
  const timeoutAbortController = params.totalTimeoutMs ? new AbortController() : void 0;
278
- const signal = mergeAbortSignals([params.abortSignal, timeoutAbortController?.signal]);
277
+ const signal = params.abortSignal && timeoutAbortController ? AbortSignal.any([params.abortSignal, timeoutAbortController.signal]) : params.abortSignal ?? timeoutAbortController?.signal;
279
278
  let timedOut = false;
280
279
  let timeoutHandle = null;
281
280
  const savePromise = saveRemoteMedia({