@openclaw/discord 2026.5.14-beta.2 → 2026.5.16-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/api.js +6 -6
  3. package/dist/{approval-handler.runtime-Uco62pII.js → approval-handler.runtime-CS97g4S1.js} +2 -2
  4. package/dist/{audit-eSlKbMHw.js → audit-BZOw16KT.js} +2 -2
  5. package/dist/{channel-actions-DANVAtKL.js → channel-actions-0dOFg3Mu.js} +1 -1
  6. package/dist/{channel-actions.runtime-CpIslHS9.js → channel-actions.runtime-BB30vneH.js} +6 -2
  7. package/dist/channel-config-api.js +1 -1
  8. package/dist/{channel-BjBa8nYY.js → channel-ngFtP-WD.js} +8 -8
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel.setup-DBFcGRFD.js → channel.setup-BwmvzHMR.js} +32 -30
  11. package/dist/{config-schema-D7AtCpJa.js → config-schema-D0eb2vPJ.js} +4 -0
  12. package/dist/contract-api.js +1 -1
  13. package/dist/{conversation-identity-C3AI-1tz.js → conversation-identity-Dh8wIQ_K.js} +1 -1
  14. package/dist/{doctor-mn2XyjuF.js → doctor-CWTWiBru.js} +1 -1
  15. package/dist/{handle-action.guild-admin-DriifPwO.js → handle-action.guild-admin-DOW3XfEG.js} +1 -1
  16. package/dist/{inbound-context-DD7n3Q6U.js → inbound-context-BdfOEkhj.js} +7 -10
  17. package/dist/{manager.runtime-DauS2xA3.js → manager.runtime-D8sDxzBv.js} +3 -3
  18. package/dist/{message-handler-CMHwRlxG.js → message-handler-D-Nboii4.js} +5 -5
  19. package/dist/{message-handler.preflight-C0sT-ewp.js → message-handler.preflight-BMgBVbmR.js} +98 -17
  20. package/dist/{message-handler.process-Cy7_-0H0.js → message-handler.process-Dfu_aSv1.js} +240 -199
  21. package/dist/{message-utils-N5UTOXQ2.js → message-utils-CY91O2k2.js} +35 -2
  22. package/dist/{outbound-adapter-Cw9JsRTY.js → outbound-adapter-Fe4Ee_GO.js} +3 -3
  23. package/dist/{provider-nzJg2k5t.js → provider-B81nXBRc.js} +15 -12
  24. package/dist/{provider-session.runtime-DMxaLPB3.js → provider-session.runtime-C7p_WbqZ.js} +3 -3
  25. package/dist/provider.runtime-B_3TYTb7.js +2 -0
  26. package/dist/{runtime-DL82ijB1.js → runtime-ToiiUiXe.js} +24 -8
  27. package/dist/runtime-api.actions.js +2 -2
  28. package/dist/runtime-api.js +14 -14
  29. package/dist/runtime-api.lookup.js +1 -1
  30. package/dist/runtime-api.monitor-DJQdXyzE.js +5 -0
  31. package/dist/runtime-api.monitor.js +4 -4
  32. package/dist/runtime-api.send.js +4 -4
  33. package/dist/runtime-api.threads.js +3 -3
  34. package/dist/{send-BqzTEkt9.js → send-CvXckvRn.js} +14 -3
  35. package/dist/{send.components-D6pXHVrU.js → send.components-BlNbbG3H.js} +4 -3
  36. package/dist/{send.outbound-D8o8BW6q.js → send.outbound-BKh71kjw.js} +45 -22
  37. package/dist/{send.shared-DSpva7uA.js → send.shared-Bdj-DP6-.js} +41 -26
  38. package/dist/setup-plugin-api.js +1 -1
  39. package/dist/{shared-0kdaIxXP.js → shared-LuaeDRhK.js} +2 -2
  40. package/dist/subagent-hooks-api.js +1 -1
  41. package/dist/{subagent-hooks-DhuBhwRw.js → subagent-hooks-vwV-pjIA.js} +1 -1
  42. package/dist/test-api.js +5 -5
  43. package/dist/{thread-bindings-C1f7Iim4.js → thread-bindings-BwcE40jS.js} +2 -2
  44. package/dist/{thread-bindings.discord-api-DJACBZJ1.js → thread-bindings.discord-api-BtXi8-Fz.js} +3 -3
  45. package/dist/{thread-bindings.manager-DN_q0IW7.js → thread-bindings.manager-BL5QlX3G.js} +2 -2
  46. package/openclaw.plugin.json +10 -0
  47. package/package.json +4 -4
  48. package/dist/provider.runtime-S-wZdzK5.js +0 -2
  49. package/dist/runtime-api.monitor-DbLcHuhE.js +0 -5
@@ -1,18 +1,19 @@
1
1
  import { Ut as resolveDiscordChannelId, c as discord_exports, ft as editChannelMessage, s as chunkDiscordTextWithMode, st as createChannelMessage, ut as deleteChannelMessage } from "./send.receipt-nKLxvA1s.js";
2
2
  import { f as resolveDiscordMaxLinesPerMessage } from "./accounts-DnNVBDfc.js";
3
- import { M as createDiscordRestClient, N as createDiscordRuntimeAccountContext, d as resolveDiscordTargetChannelId } from "./send.shared-DSpva7uA.js";
3
+ import { N as createDiscordRestClient, P as createDiscordRuntimeAccountContext, _ as resolveDiscordMessageFlags, d as resolveDiscordTargetChannelId } from "./send.shared-Bdj-DP6-.js";
4
4
  import { S as resolveTimestampMs, a as normalizeDiscordSlug, r as normalizeDiscordAllowList } from "./allow-list-CBI-M84K.js";
5
- import { a as removeReactionDiscord, f as editMessageDiscord, r as reactMessageDiscord } from "./send-BqzTEkt9.js";
5
+ import { a as removeReactionDiscord, f as editMessageDiscord, r as reactMessageDiscord } from "./send-CvXckvRn.js";
6
6
  import "./targets-DwW6OieO.js";
7
- import { t as resolveDiscordConversationIdentity } from "./conversation-identity-C3AI-1tz.js";
8
- import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-Cw9JsRTY.js";
7
+ import { t as resolveDiscordConversationIdentity } from "./conversation-identity-Dh8wIQ_K.js";
8
+ import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-Fe4Ee_GO.js";
9
9
  import { t as resolveDiscordPreviewStreamMode } from "./preview-streaming-CXTZydhx.js";
10
10
  import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-snXNwR4m.js";
11
- import { a as resolveReplyContext, i as buildGuildLabel, n as deliverDiscordReply, r as buildDirectLabel, x as resolveDiscordThreadStarter, y as resolveDiscordAutoThreadReplyPlan } from "./provider-nzJg2k5t.js";
12
- import { a as resolveForwardedMediaList, i as buildDiscordMediaPayload, o as resolveMediaList, r as resolveDiscordMessageText } from "./message-utils-N5UTOXQ2.js";
11
+ import { a as resolveReplyContext, i as buildGuildLabel, n as deliverDiscordReply, r as buildDirectLabel, x as resolveDiscordThreadStarter, y as resolveDiscordAutoThreadReplyPlan } from "./provider-B81nXBRc.js";
12
+ import { a as resolveForwardedMediaList, o as resolveMediaList, r as resolveDiscordMessageText, s as resolveReferencedReplyMediaList } from "./message-utils-CY91O2k2.js";
13
13
  import { t as sendTyping } from "./typing-C_8U8J7E.js";
14
- import { n as buildDiscordInboundAccessContext, r as createDiscordSupplementalContextAccessChecker } from "./inbound-context-DD7n3Q6U.js";
14
+ import { n as buildDiscordInboundAccessContext, r as createDiscordSupplementalContextAccessChecker } from "./inbound-context-BdfOEkhj.js";
15
15
  import { buildAgentSessionKey, normalizeAccountId, resolveAccountEntry, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
16
+ import { MessageFlags } from "discord-api-types/v10";
16
17
  import { evaluateSupplementalContextVisibility } from "openclaw/plugin-sdk/security-runtime";
17
18
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
18
19
  import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
@@ -28,13 +29,12 @@ import { EmbeddedBlockChunker, formatReasoningMessage, resolveAckReaction, resol
28
29
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
29
30
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
30
31
  import { readSessionUpdatedAt, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
31
- import { formatInboundEnvelope, resolveEnvelopeFormatOptions } from "openclaw/plugin-sdk/channel-inbound";
32
+ import { buildChannelTurnContext, formatInboundEnvelope, resolveEnvelopeFormatOptions, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
32
33
  import { createFinalizableDraftLifecycle } from "openclaw/plugin-sdk/channel-lifecycle";
33
- import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
34
- import { hasFinalInboundReplyDispatch, recordChannelBotPairLoopAndCheckSuppression, runInboundReplyTurn } from "openclaw/plugin-sdk/inbound-reply-dispatch";
35
- import { buildPendingHistoryContextFromMap } from "openclaw/plugin-sdk/reply-history";
34
+ import { hasFinalInboundReplyDispatch, recordChannelBotPairLoopAndCheckSuppression, runPreparedInboundReplyTurn } from "openclaw/plugin-sdk/inbound-reply-dispatch";
36
35
  import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
37
36
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
37
+ import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
38
38
  //#region extensions/discord/src/monitor/ack-reactions.ts
39
39
  function createDiscordAckReactionContext(params) {
40
40
  return {
@@ -80,9 +80,12 @@ function normalizeDiscordDmOwnerEntry(entry) {
80
80
  ])?.ids.values().next().value;
81
81
  return typeof candidate === "string" && /^\d+$/.test(candidate) ? candidate : void 0;
82
82
  }
83
+ function isContextAborted(abortSignal) {
84
+ return Boolean(abortSignal?.aborted);
85
+ }
83
86
  async function buildDiscordMessageProcessContext(params) {
84
87
  const { ctx, text, mediaList } = params;
85
- const { cfg, discordConfig, accountId, runtime, guildHistories, historyLimit, replyToMode, message, author, sender, canonicalMessageId, data, client, channelInfo, channelName, messageChannelId, isGuildMessage, isDirectMessage, baseText, preflightAudioTranscript, threadChannel, threadParentId, threadParentName, threadParentType, threadName, displayChannelSlug, guildInfo, guildSlug, memberRoleIds, channelConfig, baseSessionKey, boundSessionKey, route, commandAuthorized } = ctx;
88
+ const { cfg, discordConfig, accountId, runtime, mediaMaxBytes, discordRestFetch, abortSignal, guildHistories, historyLimit, replyToMode, message, author, sender, canonicalMessageId, data, client, channelInfo, channelName, messageChannelId, isGuildMessage, isDirectMessage, baseText, preflightAudioTranscript, threadChannel, threadParentId, threadParentName, threadParentType, threadName, displayChannelSlug, guildInfo, guildSlug, memberRoleIds, channelConfig, baseSessionKey, boundSessionKey, route, commandAuthorized } = ctx;
86
89
  const fromLabel = isDirectMessage ? buildDirectLabel(author) : buildGuildLabel({
87
90
  guild: data.guild ?? void 0,
88
91
  channelName: channelName ?? messageChannelId,
@@ -108,8 +111,7 @@ async function buildDiscordMessageProcessContext(params) {
108
111
  },
109
112
  allowNameMatching: isDangerousNameMatchingEnabled(discordConfig),
110
113
  isGuild: isGuildMessage,
111
- channelTopic: channelInfo?.topic,
112
- messageBody: text
114
+ channelTopic: channelInfo?.topic
113
115
  });
114
116
  const pinnedMainDmOwner = isDirectMessage ? resolvePinnedMainDmOwnerFromAllowlist({
115
117
  dmScope: cfg.session?.dmScope,
@@ -133,6 +135,7 @@ async function buildDiscordMessageProcessContext(params) {
133
135
  storePath,
134
136
  sessionKey: route.sessionKey
135
137
  });
138
+ const channelHistory = createChannelHistoryWindow({ historyMap: guildHistories });
136
139
  let combinedBody = formatInboundEnvelope({
137
140
  channel: "Discord",
138
141
  from: fromLabel,
@@ -144,8 +147,7 @@ async function buildDiscordMessageProcessContext(params) {
144
147
  envelope: envelopeOptions
145
148
  });
146
149
  const shouldIncludeChannelHistory = !isDirectMessage && !(isGuildMessage && channelConfig?.autoThread && !threadChannel);
147
- if (shouldIncludeChannelHistory) combinedBody = buildPendingHistoryContextFromMap({
148
- historyMap: guildHistories,
150
+ if (shouldIncludeChannelHistory) combinedBody = channelHistory.buildPendingContext({
149
151
  historyKey: messageChannelId,
150
152
  limit: historyLimit,
151
153
  currentMessage: combinedBody,
@@ -172,6 +174,17 @@ async function buildDiscordMessageProcessContext(params) {
172
174
  }) : null;
173
175
  const filteredReplyContext = replyContext && replyVisibility?.include ? replyContext : null;
174
176
  if (replyContext && !filteredReplyContext && isGuildMessage) logVerbose(`discord: drop reply context (mode=${contextVisibilityMode})`);
177
+ const mediaListForContext = [...mediaList];
178
+ if (filteredReplyContext) {
179
+ const referencedReplyMediaList = await resolveReferencedReplyMediaList(message, mediaMaxBytes, {
180
+ fetchImpl: discordRestFetch,
181
+ ssrfPolicy: cfg.browser?.ssrfPolicy,
182
+ readIdleTimeoutMs: DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS,
183
+ totalTimeoutMs: DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS,
184
+ abortSignal
185
+ });
186
+ if (!isContextAborted(abortSignal)) mediaListForContext.push(...referencedReplyMediaList);
187
+ }
175
188
  if (forumContextLine) combinedBody = `${combinedBody}\n${forumContextLine}`;
176
189
  let threadStarterBody;
177
190
  let threadLabel;
@@ -213,8 +226,7 @@ async function buildDiscordMessageProcessContext(params) {
213
226
  }
214
227
  if (!threadParentInheritanceEnabled) parentSessionKey = void 0;
215
228
  }
216
- const mediaPayload = buildDiscordMediaPayload(mediaList);
217
- const preflightAudioIndex = preflightAudioTranscript === void 0 ? -1 : mediaList.findIndex((media) => media.contentType?.startsWith("audio/"));
229
+ const preflightAudioIndex = preflightAudioTranscript === void 0 ? -1 : mediaListForContext.findIndex((media) => media.contentType?.startsWith("audio/"));
218
230
  const threadKeys = resolveThreadSessionKeys({
219
231
  baseSessionKey,
220
232
  threadId: threadChannel ? messageChannelId : void 0,
@@ -254,67 +266,105 @@ async function buildDiscordMessageProcessContext(params) {
254
266
  return null;
255
267
  }
256
268
  const lastRouteTo = dmConversationTarget ?? effectiveTo;
257
- const inboundHistory = shouldIncludeChannelHistory && historyLimit > 0 ? (guildHistories.get(messageChannelId) ?? []).map((entry) => ({
258
- sender: entry.sender,
259
- body: entry.body,
260
- timestamp: entry.timestamp
261
- })) : void 0;
269
+ const inboundHistory = shouldIncludeChannelHistory ? channelHistory.buildInboundHistory({
270
+ historyKey: messageChannelId,
271
+ limit: historyLimit
272
+ }) : void 0;
262
273
  const originatingTo = autoThreadContext?.OriginatingTo ?? dmConversationTarget ?? replyTarget;
263
274
  const effectiveSessionKey = boundSessionKey ?? autoThreadContext?.SessionKey ?? threadKeys.sessionKey;
264
275
  const effectivePreviousTimestamp = effectiveSessionKey === route.sessionKey ? previousTimestamp : readSessionUpdatedAt({
265
276
  storePath,
266
277
  sessionKey: effectiveSessionKey
267
278
  });
268
- const ctxPayload = finalizeInboundContext({
269
- Body: combinedBody,
270
- BodyForAgent: preflightAudioTranscript ?? baseText ?? text,
271
- InboundHistory: inboundHistory,
272
- RawBody: preflightAudioTranscript ?? baseText,
273
- CommandBody: preflightAudioTranscript ?? baseText,
274
- ...preflightAudioTranscript !== void 0 ? { Transcript: preflightAudioTranscript } : {},
275
- From: effectiveFrom,
276
- To: effectiveTo,
277
- SessionKey: effectiveSessionKey,
278
- AccountId: route.accountId,
279
- ChatType: isDirectMessage ? "direct" : "channel",
280
- ConversationLabel: fromLabel,
281
- SenderName: senderName,
282
- SenderId: sender.id,
283
- SenderUsername: senderUsername,
284
- SenderTag: sender.tag,
285
- GroupSubject: groupSubject,
286
- GroupChannel: groupChannel,
287
- MemberRoleIds: memberRoleIds,
288
- UntrustedContext: untrustedContext,
289
- GroupSystemPrompt: isGuildMessage ? groupSystemPrompt : void 0,
290
- GroupSpace: isGuildMessage ? (guildInfo?.id ?? guildSlug) || void 0 : void 0,
291
- OwnerAllowFrom: ownerAllowFrom,
292
- Provider: "discord",
293
- Surface: "discord",
294
- WasMentioned: ctx.effectiveWasMentioned,
295
- MessageSid: canonicalMessageId ?? message.id,
296
- ...canonicalMessageId && canonicalMessageId !== message.id ? { MessageSidFull: message.id } : {},
297
- ReplyToId: filteredReplyContext?.id,
298
- ReplyToBody: filteredReplyContext?.body,
299
- ReplyToSender: filteredReplyContext?.sender,
300
- ParentSessionKey: autoThreadContext?.ParentSessionKey ?? threadKeys.parentSessionKey,
301
- ModelParentSessionKey: autoThreadContext?.ModelParentSessionKey ?? modelParentSessionKey ?? void 0,
302
- MessageThreadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0,
303
- ThreadStarterBody: !effectivePreviousTimestamp ? threadStarterBody : void 0,
304
- ThreadLabel: threadLabel,
305
- Timestamp: resolveTimestampMs(message.timestamp),
306
- ...mediaPayload,
307
- ...preflightAudioIndex >= 0 ? { MediaTranscribedIndexes: [preflightAudioIndex] } : {},
308
- CommandAuthorized: commandAuthorized,
309
- CommandTurn: {
279
+ const ctxPayload = buildChannelTurnContext({
280
+ channel: "discord",
281
+ provider: "discord",
282
+ surface: "discord",
283
+ accountId: route.accountId,
284
+ messageId: canonicalMessageId ?? message.id,
285
+ messageIdFull: canonicalMessageId && canonicalMessageId !== message.id ? message.id : void 0,
286
+ timestamp: resolveTimestampMs(message.timestamp),
287
+ from: effectiveFrom,
288
+ sender: {
289
+ id: sender.id,
290
+ name: senderName,
291
+ username: senderUsername,
292
+ tag: sender.tag,
293
+ roles: memberRoleIds,
294
+ displayLabel: senderLabel
295
+ },
296
+ conversation: {
297
+ kind: isDirectMessage ? "direct" : "channel",
298
+ id: messageChannelId,
299
+ label: fromLabel,
300
+ spaceId: isGuildMessage ? (guildInfo?.id ?? guildSlug) || void 0 : void 0,
301
+ threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0,
302
+ routePeer: {
303
+ kind: isDirectMessage ? "direct" : "channel",
304
+ id: isDirectMessage ? author.id : messageChannelId
305
+ }
306
+ },
307
+ route: {
308
+ agentId: route.agentId,
309
+ accountId: route.accountId,
310
+ routeSessionKey: route.sessionKey,
311
+ dispatchSessionKey: effectiveSessionKey,
312
+ parentSessionKey: autoThreadContext?.ParentSessionKey ?? threadKeys.parentSessionKey,
313
+ modelParentSessionKey: autoThreadContext?.ModelParentSessionKey ?? modelParentSessionKey ?? void 0
314
+ },
315
+ reply: {
316
+ to: effectiveTo,
317
+ originatingTo
318
+ },
319
+ message: {
320
+ body: combinedBody,
321
+ rawBody: preflightAudioTranscript ?? baseText,
322
+ bodyForAgent: preflightAudioTranscript ?? baseText ?? text,
323
+ commandBody: preflightAudioTranscript ?? baseText,
324
+ envelopeFrom: fromLabel,
325
+ inboundHistory
326
+ },
327
+ access: {
328
+ mentions: {
329
+ canDetectMention: ctx.canDetectMention,
330
+ wasMentioned: ctx.effectiveWasMentioned,
331
+ hasAnyMention: ctx.hasAnyMention,
332
+ requireMention: ctx.shouldRequireMention,
333
+ effectiveWasMentioned: ctx.effectiveWasMentioned
334
+ },
335
+ commands: {
336
+ authorized: commandAuthorized,
337
+ allowTextCommands: ctx.allowTextCommands,
338
+ useAccessGroups: false,
339
+ authorizers: []
340
+ }
341
+ },
342
+ commandTurn: {
310
343
  kind: "text-slash",
311
344
  source: "text",
312
345
  authorized: commandAuthorized,
313
346
  body: preflightAudioTranscript ?? baseText
314
347
  },
315
- CommandSource: "text",
316
- OriginatingChannel: "discord",
317
- OriginatingTo: originatingTo
348
+ media: toInboundMediaFacts(mediaListForContext, { transcribed: (_media, index) => index === preflightAudioIndex }),
349
+ supplemental: {
350
+ quote: filteredReplyContext ? {
351
+ id: filteredReplyContext.id,
352
+ body: filteredReplyContext.body,
353
+ sender: filteredReplyContext.sender
354
+ } : void 0,
355
+ thread: {
356
+ starterBody: !effectivePreviousTimestamp ? threadStarterBody : void 0,
357
+ label: threadLabel
358
+ },
359
+ groupSystemPrompt: isGuildMessage ? groupSystemPrompt : void 0
360
+ },
361
+ extra: {
362
+ ...preflightAudioTranscript !== void 0 ? { Transcript: preflightAudioTranscript } : {},
363
+ GroupSubject: groupSubject,
364
+ GroupChannel: groupChannel,
365
+ UntrustedStructuredContext: untrustedContext,
366
+ OwnerAllowFrom: ownerAllowFrom
367
+ }
318
368
  });
319
369
  const persistedSessionKey = ctxPayload.SessionKey ?? route.sessionKey;
320
370
  if (shouldLogVerbose()) {
@@ -380,6 +430,7 @@ function createDiscordDraftStream(params) {
380
430
  const minInitialChars = params.minInitialChars;
381
431
  const channelId = params.channelId;
382
432
  const rest = params.rest;
433
+ const flags = resolveDiscordMessageFlags({ suppressEmbeds: params.suppressEmbeds });
383
434
  const resolveReplyToMessageId = () => typeof params.replyToMessageId === "function" ? params.replyToMessageId() : params.replyToMessageId;
384
435
  const streamState = {
385
436
  stopped: false,
@@ -405,7 +456,8 @@ function createDiscordDraftStream(params) {
405
456
  if (streamMessageId !== void 0) {
406
457
  await editChannelMessage(rest, channelId, streamMessageId, { body: {
407
458
  content: trimmed,
408
- allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS
459
+ allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
460
+ ...flags ? { flags } : {}
409
461
  } });
410
462
  return true;
411
463
  }
@@ -417,6 +469,7 @@ function createDiscordDraftStream(params) {
417
469
  const sentMessageId = (await createChannelMessage(rest, channelId, { body: {
418
470
  content: trimmed,
419
471
  allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
472
+ ...flags ? { flags } : {},
420
473
  ...messageReference ? { message_reference: messageReference } : {}
421
474
  } }))?.id;
422
475
  if (typeof sentMessageId !== "string" || !sentMessageId) {
@@ -480,6 +533,7 @@ function createDiscordDraftPreviewController(params) {
480
533
  maxChars: draftMaxChars,
481
534
  replyToMessageId: () => params.replyReference.peek(),
482
535
  minInitialChars: discordStreamMode === "progress" ? 0 : 30,
536
+ suppressEmbeds: params.discordConfig?.suppressEmbeds ?? true,
483
537
  throttleMs: 1200,
484
538
  log: params.log,
485
539
  warn: params.log
@@ -954,6 +1008,7 @@ async function processDiscordMessage(ctx, observer) {
954
1008
  chunkMode,
955
1009
  log: logVerbose
956
1010
  });
1011
+ const finalPreviewFlags = discordConfig?.suppressEmbeds ?? true ? MessageFlags.SuppressEmbeds : void 0;
957
1012
  let finalReplyStartNotified = false;
958
1013
  const notifyFinalReplyStart = () => {
959
1014
  if (finalReplyStartNotified) return;
@@ -989,7 +1044,10 @@ async function processDiscordMessage(ctx, observer) {
989
1044
  },
990
1045
  buildFinalEdit: () => {
991
1046
  if (draftPreview.finalizedViaPreviewMessage || hasMedia || typeof previewFinalText !== "string" || hasExplicitReplyDirective || payload.isError) return;
992
- return { content: previewFinalText };
1047
+ return {
1048
+ content: previewFinalText,
1049
+ ...finalPreviewFlags ? { flags: finalPreviewFlags } : {}
1050
+ };
993
1051
  },
994
1052
  editFinal: async (previewMessageId, edit) => {
995
1053
  if (isProcessAborted(abortSignal)) throw new Error("process aborted");
@@ -1100,139 +1158,122 @@ async function processDiscordMessage(ctx, observer) {
1100
1158
  await settleDispatchBeforeStart();
1101
1159
  return;
1102
1160
  }
1103
- const preparedResult = await runInboundReplyTurn({
1161
+ const preparedResult = await runPreparedInboundReplyTurn({
1104
1162
  channel: "discord",
1105
1163
  accountId: route.accountId,
1106
- raw: ctx,
1107
- adapter: {
1108
- ingest: () => ({
1109
- id: message.id,
1110
- timestamp: message.timestamp ? Date.parse(message.timestamp) : void 0,
1111
- rawText: text,
1112
- textForAgent: ctxPayload.BodyForAgent,
1113
- textForCommands: ctxPayload.CommandBody,
1114
- raw: message
1115
- }),
1116
- resolveTurn: () => ({
1117
- channel: "discord",
1118
- accountId: route.accountId,
1119
- routeSessionKey: persistedSessionKey,
1120
- storePath: turn.storePath,
1121
- ctxPayload,
1122
- recordInboundSession,
1123
- record: turn.record,
1124
- history: {
1125
- isGroup: isGuildMessage,
1126
- historyKey: messageChannelId,
1127
- historyMap: guildHistories,
1128
- limit: historyLimit
1164
+ routeSessionKey: persistedSessionKey,
1165
+ storePath: turn.storePath,
1166
+ ctxPayload,
1167
+ recordInboundSession,
1168
+ record: turn.record,
1169
+ history: {
1170
+ isGroup: isGuildMessage,
1171
+ historyKey: messageChannelId,
1172
+ historyMap: guildHistories,
1173
+ limit: historyLimit
1174
+ },
1175
+ onPreDispatchFailure: settleDispatchBeforeStart,
1176
+ runDispatch: async () => await dispatchInboundMessage({
1177
+ ctx: ctxPayload,
1178
+ cfg,
1179
+ dispatcher,
1180
+ replyOptions: {
1181
+ ...replyOptions,
1182
+ abortSignal,
1183
+ skillFilter: channelConfig?.skills,
1184
+ sourceReplyDeliveryMode,
1185
+ disableBlockStreaming: sourceRepliesAreToolOnly ? true : draftPreview.disableBlockStreamingForDraft ?? (typeof resolvedBlockStreamingEnabled === "boolean" ? !resolvedBlockStreamingEnabled : void 0),
1186
+ onPartialReply: draftPreview.draftStream ? (payload) => draftPreview.updateFromPartial(payload.text) : void 0,
1187
+ onAssistantMessageStart: draftPreview.draftStream ? () => draftPreview.handleAssistantMessageBoundary() : void 0,
1188
+ onReasoningEnd: draftPreview.draftStream ? () => draftPreview.handleAssistantMessageBoundary() : void 0,
1189
+ onModelSelected,
1190
+ suppressDefaultToolProgressMessages: draftPreview.suppressDefaultToolProgressMessages ? true : void 0,
1191
+ onReasoningStream: async (payload) => {
1192
+ await statusReactions.setThinking();
1193
+ const formattedText = payload?.text ? formatReasoningMessage(payload.text) : void 0;
1194
+ await draftPreview.pushReasoningProgress(formattedText);
1129
1195
  },
1130
- onPreDispatchFailure: settleDispatchBeforeStart,
1131
- runDispatch: async () => {
1132
- return await dispatchInboundMessage({
1133
- ctx: ctxPayload,
1134
- cfg,
1135
- dispatcher,
1136
- replyOptions: {
1137
- ...replyOptions,
1138
- abortSignal,
1139
- skillFilter: channelConfig?.skills,
1140
- sourceReplyDeliveryMode,
1141
- disableBlockStreaming: sourceRepliesAreToolOnly ? true : draftPreview.disableBlockStreamingForDraft ?? (typeof resolvedBlockStreamingEnabled === "boolean" ? !resolvedBlockStreamingEnabled : void 0),
1142
- onPartialReply: draftPreview.draftStream ? (payload) => draftPreview.updateFromPartial(payload.text) : void 0,
1143
- onAssistantMessageStart: draftPreview.draftStream ? () => draftPreview.handleAssistantMessageBoundary() : void 0,
1144
- onReasoningEnd: draftPreview.draftStream ? () => draftPreview.handleAssistantMessageBoundary() : void 0,
1145
- onModelSelected,
1146
- suppressDefaultToolProgressMessages: draftPreview.suppressDefaultToolProgressMessages ? true : void 0,
1147
- onReasoningStream: async (payload) => {
1148
- await statusReactions.setThinking();
1149
- const formattedText = payload?.text ? formatReasoningMessage(payload.text) : void 0;
1150
- await draftPreview.pushReasoningProgress(formattedText);
1151
- },
1152
- onToolStart: async (payload) => {
1153
- if (isProcessAborted(abortSignal)) return;
1154
- await maybeBindStatusReactionsToToolReaction(payload);
1155
- await statusReactions.setTool(payload.name);
1156
- await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
1157
- event: "tool",
1158
- name: payload.name,
1159
- phase: payload.phase,
1160
- args: payload.args
1161
- }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1162
- },
1163
- onItemEvent: async (payload) => {
1164
- await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
1165
- event: "item",
1166
- itemId: payload.itemId,
1167
- itemKind: payload.kind,
1168
- title: payload.title,
1169
- name: payload.name,
1170
- phase: payload.phase,
1171
- status: payload.status,
1172
- summary: payload.summary,
1173
- progressText: payload.progressText,
1174
- meta: payload.meta
1175
- }));
1176
- },
1177
- onPlanUpdate: async (payload) => {
1178
- if (payload.phase !== "update") return;
1179
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1180
- event: "plan",
1181
- phase: payload.phase,
1182
- title: payload.title,
1183
- explanation: payload.explanation,
1184
- steps: payload.steps
1185
- }));
1186
- },
1187
- onApprovalEvent: async (payload) => {
1188
- if (payload.phase !== "requested") return;
1189
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1190
- event: "approval",
1191
- phase: payload.phase,
1192
- title: payload.title,
1193
- command: payload.command,
1194
- reason: payload.reason,
1195
- message: payload.message
1196
- }));
1197
- },
1198
- onCommandOutput: async (payload) => {
1199
- if (payload.phase !== "end") return;
1200
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1201
- event: "command-output",
1202
- phase: payload.phase,
1203
- title: payload.title,
1204
- name: payload.name,
1205
- status: payload.status,
1206
- exitCode: payload.exitCode
1207
- }));
1208
- },
1209
- onPatchSummary: async (payload) => {
1210
- if (payload.phase !== "end") return;
1211
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1212
- event: "patch",
1213
- phase: payload.phase,
1214
- title: payload.title,
1215
- name: payload.name,
1216
- added: payload.added,
1217
- modified: payload.modified,
1218
- deleted: payload.deleted,
1219
- summary: payload.summary
1220
- }));
1221
- },
1222
- onCompactionStart: async () => {
1223
- if (isProcessAborted(abortSignal)) return;
1224
- await statusReactions.setCompacting();
1225
- },
1226
- onCompactionEnd: async () => {
1227
- if (isProcessAborted(abortSignal)) return;
1228
- statusReactions.cancelPending();
1229
- await statusReactions.setThinking();
1230
- }
1231
- }
1232
- });
1196
+ onToolStart: async (payload) => {
1197
+ if (isProcessAborted(abortSignal)) return;
1198
+ await maybeBindStatusReactionsToToolReaction(payload);
1199
+ await statusReactions.setTool(payload.name);
1200
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
1201
+ event: "tool",
1202
+ name: payload.name,
1203
+ phase: payload.phase,
1204
+ args: payload.args
1205
+ }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1206
+ },
1207
+ onItemEvent: async (payload) => {
1208
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
1209
+ event: "item",
1210
+ itemId: payload.itemId,
1211
+ itemKind: payload.kind,
1212
+ title: payload.title,
1213
+ name: payload.name,
1214
+ phase: payload.phase,
1215
+ status: payload.status,
1216
+ summary: payload.summary,
1217
+ progressText: payload.progressText,
1218
+ meta: payload.meta
1219
+ }));
1220
+ },
1221
+ onPlanUpdate: async (payload) => {
1222
+ if (payload.phase !== "update") return;
1223
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1224
+ event: "plan",
1225
+ phase: payload.phase,
1226
+ title: payload.title,
1227
+ explanation: payload.explanation,
1228
+ steps: payload.steps
1229
+ }));
1230
+ },
1231
+ onApprovalEvent: async (payload) => {
1232
+ if (payload.phase !== "requested") return;
1233
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1234
+ event: "approval",
1235
+ phase: payload.phase,
1236
+ title: payload.title,
1237
+ command: payload.command,
1238
+ reason: payload.reason,
1239
+ message: payload.message
1240
+ }));
1241
+ },
1242
+ onCommandOutput: async (payload) => {
1243
+ if (payload.phase !== "end") return;
1244
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1245
+ event: "command-output",
1246
+ phase: payload.phase,
1247
+ title: payload.title,
1248
+ name: payload.name,
1249
+ status: payload.status,
1250
+ exitCode: payload.exitCode
1251
+ }));
1252
+ },
1253
+ onPatchSummary: async (payload) => {
1254
+ if (payload.phase !== "end") return;
1255
+ await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
1256
+ event: "patch",
1257
+ phase: payload.phase,
1258
+ title: payload.title,
1259
+ name: payload.name,
1260
+ added: payload.added,
1261
+ modified: payload.modified,
1262
+ deleted: payload.deleted,
1263
+ summary: payload.summary
1264
+ }));
1265
+ },
1266
+ onCompactionStart: async () => {
1267
+ if (isProcessAborted(abortSignal)) return;
1268
+ await statusReactions.setCompacting();
1269
+ },
1270
+ onCompactionEnd: async () => {
1271
+ if (isProcessAborted(abortSignal)) return;
1272
+ statusReactions.cancelPending();
1273
+ await statusReactions.setThinking();
1233
1274
  }
1234
- })
1235
- }
1275
+ }
1276
+ })
1236
1277
  });
1237
1278
  if (!preparedResult.dispatched) return;
1238
1279
  dispatchResult = preparedResult.dispatchResult;
@@ -1,4 +1,4 @@
1
- import { u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-DJACBZJ1.js";
1
+ import { u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-BtXi8-Fz.js";
2
2
  import { a as mergeAbortSignals } from "./timeouts-snXNwR4m.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { ComponentType, StickerFormatType } from "discord-api-types/v10";
@@ -89,6 +89,10 @@ function resolveDiscordReferencedForwardMessage(message) {
89
89
  const referenceType = message.messageReference?.type;
90
90
  return Number(referenceType) === FORWARD_MESSAGE_REFERENCE_TYPE ? message.referencedMessage : null;
91
91
  }
92
+ function resolveDiscordReferencedReplyMessage(message) {
93
+ const referenceType = message.messageReference?.type;
94
+ return Number(referenceType) === FORWARD_MESSAGE_REFERENCE_TYPE ? null : message.referencedMessage ?? null;
95
+ }
92
96
  function formatDiscordSnapshotAuthor(author) {
93
97
  if (!author) return;
94
98
  const globalName = normalizeOptionalString(author.global_name) ?? void 0;
@@ -231,6 +235,35 @@ async function resolveForwardedMediaList(message, maxBytes, options) {
231
235
  });
232
236
  return out;
233
237
  }
238
+ async function resolveReferencedReplyMediaList(message, maxBytes, options) {
239
+ const referencedReply = resolveDiscordReferencedReplyMessage(message);
240
+ const out = [];
241
+ if (!referencedReply) return out;
242
+ const resolvedSsrFPolicy = resolveDiscordMediaSsrFPolicy(options?.ssrfPolicy);
243
+ await appendResolvedMediaFromAttachments({
244
+ attachments: referencedReply.attachments,
245
+ maxBytes,
246
+ out,
247
+ errorPrefix: "discord: failed to download referenced reply attachment",
248
+ fetchImpl: options?.fetchImpl,
249
+ ssrfPolicy: resolvedSsrFPolicy,
250
+ readIdleTimeoutMs: options?.readIdleTimeoutMs,
251
+ totalTimeoutMs: options?.totalTimeoutMs,
252
+ abortSignal: options?.abortSignal
253
+ });
254
+ await appendResolvedMediaFromStickers({
255
+ stickers: resolveDiscordMessageStickers(referencedReply),
256
+ maxBytes,
257
+ out,
258
+ errorPrefix: "discord: failed to download referenced reply sticker",
259
+ fetchImpl: options?.fetchImpl,
260
+ ssrfPolicy: resolvedSsrFPolicy,
261
+ readIdleTimeoutMs: options?.readIdleTimeoutMs,
262
+ totalTimeoutMs: options?.totalTimeoutMs,
263
+ abortSignal: options?.abortSignal
264
+ });
265
+ return out;
266
+ }
234
267
  async function fetchDiscordMedia(params) {
235
268
  const timeoutAbortController = params.totalTimeoutMs ? new AbortController() : void 0;
236
269
  const signal = mergeAbortSignals([params.abortSignal, timeoutAbortController?.signal]);
@@ -513,4 +546,4 @@ function resolveDiscordSnapshotMessageText(snapshot) {
513
546
  return content || attachmentText || embedText || componentText || "";
514
547
  }
515
548
  //#endregion
516
- export { resolveForwardedMediaList as a, resolveDiscordChannelInfo as c, buildDiscordMediaPayload as i, resolveDiscordMessageChannelId as l, resolveDiscordForwardedMessagesTextFromSnapshots as n, resolveMediaList as o, resolveDiscordMessageText as r, hasDiscordMessageStickers as s, resolveDiscordEmbedText as t };
549
+ export { resolveForwardedMediaList as a, hasDiscordMessageStickers as c, resolveDiscordMessageChannelId as d, buildDiscordMediaPayload as i, resolveDiscordMessageStickers as l, resolveDiscordForwardedMessagesTextFromSnapshots as n, resolveMediaList as o, resolveDiscordMessageText as r, resolveReferencedReplyMediaList as s, resolveDiscordEmbedText as t, resolveDiscordChannelInfo as u };