@openclaw/discord 2026.7.2-beta.7 → 2026.8.1-beta.3

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 (114) hide show
  1. package/dist/{account-inspect-SXiCBCgP.js → account-inspect-D5xmUpnj.js} +49 -43
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/accounts-DNl-oCEw.js +371 -0
  4. package/dist/action-runtime-api.js +1 -1
  5. package/dist/{active-turn-thread-route-MlKWp8RD.js → active-turn-thread-route-fmHl1HNd.js} +9 -11
  6. package/dist/{activities-api-DTKsuDxf.js → activities-api-KCBAmWxD.js} +133 -175
  7. package/dist/activities-api.js +1 -1
  8. package/dist/{allow-list-DzKrtfah.js → allow-list-BDMimASD.js} +2 -3
  9. package/dist/{api-DnrK9gML.js → api-DNVbJyEy.js} +7 -2
  10. package/dist/api.js +18 -18
  11. package/dist/{approval-handler.runtime-HymvFTYn.js → approval-handler.runtime-B12RCYr8.js} +6 -6
  12. package/dist/{approval-message-safety-DEyKVsAw.js → approval-message-safety-XGFPbDoI.js} +89 -3
  13. package/dist/{audit-CjsrabP8.js → audit-Yc7eZDd6.js} +5 -5
  14. package/dist/{channel-actions-uXFTJX3o.js → channel-actions-Dr2QI8Bf.js} +11 -7
  15. package/dist/{channel-actions.runtime-CIYU90sl.js → channel-actions.runtime-CODTvo7c.js} +99 -97
  16. package/dist/{channel-vKa-76xY.js → channel-cXYzGc4L.js} +102 -104
  17. package/dist/channel-config-api.js +1 -1
  18. package/dist/channel-plugin-api.js +1 -1
  19. package/dist/{channel.setup-CmcwZ_qd.js → channel.setup-CSiwnMgs.js} +16 -54
  20. package/dist/{outbound-session-route-H-HoyH5U.js → components-ezGKo_Yh.js} +69 -142
  21. package/dist/{config-schema-WHRQcY19.js → config-schema-DC5kDtQ5.js} +42 -11
  22. package/dist/conversation-binding-route-D6rCQ5S0.js +43 -0
  23. package/dist/{directory-config-D_LojtCe.js → directory-config-XyCfoqLg.js} +2 -2
  24. package/dist/directory-contract-api.js +1 -1
  25. package/dist/{directory-live-Bw5Ephzv.js → directory-live-Df0NbFJB.js} +7 -4
  26. package/dist/{doctor-Bc4S1qyd.js → doctor-DYY31GrO.js} +34 -13
  27. package/dist/{doctor-contract-CCFMIDk9.js → doctor-contract-DsWvKIXF.js} +2 -3
  28. package/dist/doctor-contract-api.js +147 -2
  29. package/dist/{handle-action.guild-admin-D6VRRW-C.js → handle-action.guild-admin-6Tvvinsa.js} +4 -7
  30. package/dist/{inbound-event-delivery-CEPlt2uz.js → inbound-event-delivery-De2qxTrP.js} +4 -25
  31. package/dist/index.js +5 -4
  32. package/dist/{message-handler-CeiSulIw.js → message-handler-B61q5MOF.js} +118 -33
  33. package/dist/{message-handler.preflight-DibgQFf2.js → message-handler.preflight-Bzg_Hawz.js} +75 -61
  34. package/dist/{message-handler.process-Dnc_oz9y.js → message-handler.process--4_712Ec.js} +236 -296
  35. package/dist/{message-utils-BgTwT_q7.js → message-utils-BXLLnFl6.js} +34 -35
  36. package/dist/{model-picker-preference-primitives-D25pnCOu.js → model-picker-preference-primitives-BHT1LkR_.js} +3 -3
  37. package/dist/{outbound-adapter-DtE-i2OJ.js → outbound-adapter-DxHTOqiN.js} +157 -68
  38. package/dist/{outbound-components-C7GVyfPh.js → outbound-components-B2PL0ooH.js} +3 -3
  39. package/dist/owner-access-Bg41VzAf.js +107 -0
  40. package/dist/{pluralkit-CD4B11JB.js → pluralkit-dUl4v1X3.js} +1 -1
  41. package/dist/{preflight-audio-BjnQZ5yS.js → preflight-audio-DrnqFA8U.js} +22 -28
  42. package/dist/{probe-BeWKhjqX.js → probe-BTdtVRwU.js} +37 -13
  43. package/dist/probe.runtime-Dq5-PRKT.js +2 -0
  44. package/dist/{provider-DfQJX3z5.js → provider-DlK_ynqt.js} +1131 -2021
  45. package/dist/{provider-session.runtime-Bo88k0pV.js → provider-session.runtime-CvvphHvs.js} +3 -3
  46. package/dist/provider.runtime-CzKGRiAF.js +2 -0
  47. package/dist/realtime-session.runtime-ysbMG8v7.js +1351 -0
  48. package/dist/{resolve-allowlist-common-DHERApxi.js → resolve-allowlist-common-DIVXbD1P.js} +3 -3
  49. package/dist/{resolve-channels-BjepAj8b.js → resolve-channels-CGeUoIqb.js} +4 -4
  50. package/dist/{resolve-users-Bc28htgt.js → resolve-users-BtKxLUZv.js} +3 -3
  51. package/dist/{chunk-DX4jpFQP.js → retry-DNsVTeTo.js} +308 -131
  52. package/dist/{runtime-BmW_j_hl.js → runtime-CQovMsH3.js} +12 -15
  53. package/dist/{runtime-_aRC6V2s.js → runtime-D71_VFLr.js} +49 -58
  54. package/dist/runtime-api.actions.js +2 -2
  55. package/dist/runtime-api.js +23 -23
  56. package/dist/runtime-api.lookup.js +5 -5
  57. package/dist/runtime-api.monitor-CzeTYF32.js +6 -0
  58. package/dist/runtime-api.monitor.js +6 -5
  59. package/dist/runtime-api.send.js +6 -6
  60. package/dist/runtime-api.threads.js +5 -6
  61. package/dist/sdk-runtime-37OU6_Yh.js +369 -0
  62. package/dist/{secret-config-contract-B1akrlyY.js → secret-config-contract-B3i6WxV5.js} +53 -25
  63. package/dist/secret-contract-api.js +1 -1
  64. package/dist/security-audit-contract-api.js +1 -1
  65. package/dist/{security-audit.runtime-CPN0xFYz.js → security-audit.runtime-BVKF9VtY.js} +1 -1
  66. package/dist/{send-CdkZ6zeW.js → send-rQIffSYu.js} +180 -91
  67. package/dist/{send.components-5uqwkjgE.js → send.components-ChK1wS7w.js} +32 -46
  68. package/dist/{send.outbound-D-80Ej-t.js → send.outbound-HIkgji_7.js} +57 -34
  69. package/dist/{send.reactions-CtlmGd_o.js → send.reactions-gmJ6yFPo.js} +14 -21
  70. package/dist/{send.receipt-CuwRrpFz.js → send.receipt-Cnhoautb.js} +29 -1
  71. package/dist/{send.shared-jpvAG_u6.js → send.shared-B3fpaLWJ.js} +112 -181
  72. package/dist/{sender-identity-mG_zsdox.js → sender-identity-BJtfyise.js} +1 -1
  73. package/dist/setup-entry.js +0 -5
  74. package/dist/setup-plugin-api.js +1 -1
  75. package/dist/{shared-CXF1tup1.js → shared-CaAaoksy.js} +27 -23
  76. package/dist/subagent-hooks-CNT2ZJTJ.js +49 -0
  77. package/dist/subagent-hooks-api.js +2 -6
  78. package/dist/subagent-progress-FPLqBY1s.js +110 -0
  79. package/dist/{system-events-DdUqf91B.js → system-events-Nuwoq_au.js} +2 -2
  80. package/dist/{target-resolver-C3s_2m0t.js → target-resolver-DIXShHaZ.js} +3 -3
  81. package/dist/targets-B2pf49zc.js +3 -0
  82. package/dist/{thread-bindings-C-M-ly7P.js → thread-bindings-VlymUnbz.js} +29 -28
  83. package/dist/{thread-bindings.manager-DYvSlrva.js → thread-bindings.manager-CngsgErq.js} +258 -29
  84. package/dist/{thread-bindings.session-updates-DAIlp7Px.js → thread-bindings.session-updates-CJb-BpPL.js} +1 -1
  85. package/dist/{thread-bindings.state-Cgi5uRn-.js → thread-bindings.state-D8OMdk4O.js} +3 -16
  86. package/dist/threading-DU6pgeGm.js +456 -0
  87. package/dist/transcripts-source-DeIMNbRh.js +265 -0
  88. package/dist/transcripts-source-api.js +7 -2
  89. package/dist/{typing--Mzo2kM7.js → typing-oLiS5Z13.js} +1 -1
  90. package/dist/voice-runtime-CjNFaynA.js +2549 -0
  91. package/openclaw.plugin.json +215 -25
  92. package/package.json +15 -11
  93. package/skills/discord/SKILL.md +22 -115
  94. package/dist/accounts-DJTf1LtA.js +0 -219
  95. package/dist/channel-api-CAJ0wMoV.js +0 -21
  96. package/dist/legacy-state-migrations-api.js +0 -138
  97. package/dist/manager.runtime-Bjav3pfC.js +0 -3655
  98. package/dist/preflight-audio.runtime-CoCXMM8r.js +0 -7
  99. package/dist/probe.runtime-DqFGFMbZ.js +0 -2
  100. package/dist/provider.runtime-Cy7XZmyh.js +0 -2
  101. package/dist/runtime-api.monitor-CCRubd3-.js +0 -5
  102. package/dist/session-binding-contract-api.js +0 -2
  103. package/dist/subagent-hooks-Dxy7Fmnq.js +0 -120
  104. package/dist/subagent-progress-DI3sYLLo.js +0 -664
  105. package/dist/targets-Dv_RN7vT.js +0 -3
  106. package/dist/thread-bindings.discord-api-BoDNqamb.js +0 -246
  107. package/dist/transcripts-source-DIKGsTGT.js +0 -123
  108. package/node_modules/p-map/index.d.ts +0 -155
  109. package/node_modules/p-map/index.js +0 -285
  110. package/node_modules/p-map/license +0 -9
  111. package/node_modules/p-map/package.json +0 -57
  112. package/node_modules/p-map/readme.md +0 -190
  113. package/dist/{audit-core-xwjIczO0.js → audit-core-BCX8UeFH.js} +1 -1
  114. package/dist/{security-audit-jGnJF__m.js → security-audit-BfJhAXZp.js} +1 -1
@@ -1,41 +1,44 @@
1
- import { d as discord_exports, gt as deleteChannelMessage, pt as createChannelMessage, t as chunkDiscordTextWithMode, u as resolveDiscordChannelId, vt as editChannelMessage } from "./chunk-DX4jpFQP.js";
2
- import { f as resolveDiscordMaxLinesPerMessage } from "./accounts-DJTf1LtA.js";
3
- import { F as createDiscordRestClient, I as createDiscordRuntimeAccountContext, _ as resolveDiscordMessageFlags, d as resolveDiscordTargetChannelId } from "./send.shared-jpvAG_u6.js";
4
- import { C as resolveTimestampMs, a as normalizeDiscordSlug, r as normalizeDiscordAllowList } from "./allow-list-DzKrtfah.js";
5
- import { i as discordTextHasBroadcastMention } from "./send.outbound-D-80Ej-t.js";
6
- import { s as editMessageDiscord } from "./send-CdkZ6zeW.js";
1
+ import { Ct as editChannelMessage, a as chunkDiscordTextWithMode, p as discord_exports, vt as createChannelMessage, xt as deleteChannelMessage } from "./retry-DNsVTeTo.js";
2
+ import { A as resolveDiscordChannelId, p as resolveDiscordMaxLinesPerMessage } from "./accounts-DNl-oCEw.js";
3
+ import { C as resolveTimestampMs, a as normalizeDiscordSlug, r as normalizeDiscordAllowList } from "./allow-list-BDMimASD.js";
4
+ import { A as resolveDiscordMessageFlags, F as createDiscordRestClient, I as createDiscordRuntimeAccountContext, d as resolveDiscordTargetChannelId } from "./send.shared-B3fpaLWJ.js";
5
+ import { i as discordTextHasBroadcastMention } from "./send.outbound-HIkgji_7.js";
6
+ import { c as editMessageDiscord } from "./send-rQIffSYu.js";
7
7
  import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-B9cy6RvQ.js";
8
- import { i as removeReactionDiscord, n as reactMessageDiscord } from "./send.reactions-CtlmGd_o.js";
9
- import "./targets-Dv_RN7vT.js";
10
- import { r as resolveDiscordConversationIdentity } from "./approval-message-safety-DEyKVsAw.js";
11
- import { t as beginDiscordInboundEventDeliveryCorrelation } from "./inbound-event-delivery-CEPlt2uz.js";
12
- import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-DtE-i2OJ.js";
13
- import { D as resolveDiscordAutoThreadReplyPlan, _ as buildDiscordInboundAccessContext, a as sanitizeDiscordFrontChannelReplyPayloads, c as resolveReplyContext, i as formatDiscordReplySkip, k as resolveDiscordThreadStarter, n as deliverDiscordReply, o as buildDirectLabel, r as formatDiscordReplyDeliveryFailure, s as buildGuildLabel, v as createDiscordSupplementalContextAccessChecker } from "./provider-DfQJX3z5.js";
14
- import { a as formatDiscordMediaText, c as resolveReferencedReplyMediaList, i as resolveDiscordMessageText, u as resolveDiscordMessageStickers } from "./message-utils-BgTwT_q7.js";
15
- import { n as resolveDiscordWebhookId } from "./sender-identity-mG_zsdox.js";
16
- import { t as beginDiscordActiveTurnThreadRoute } from "./active-turn-thread-route-MlKWp8RD.js";
8
+ import { i as removeReactionDiscord, n as reactMessageDiscord } from "./send.reactions-gmJ6yFPo.js";
9
+ import "./targets-B2pf49zc.js";
10
+ import { i as buildDiscordRoutePeer, l as resolveDiscordConversationIdentity } from "./approval-message-safety-XGFPbDoI.js";
11
+ import { t as discordInboundEventDelivery } from "./inbound-event-delivery-De2qxTrP.js";
12
+ import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-DxHTOqiN.js";
13
+ import { a as sanitizeDiscordFrontChannelReplyPayloads, c as resolveReplyContext, h as createDiscordSupplementalContextAccessChecker, i as formatDiscordReplySkip, m as buildDiscordInboundAccessContext, n as deliverDiscordReply, o as buildDirectLabel, r as formatDiscordReplyDeliveryFailure, s as buildGuildLabel } from "./provider-DlK_ynqt.js";
14
+ import { a as formatDiscordMediaText, c as resolveReferencedReplyMediaList, d as resolveDiscordMessageStickers, i as resolveDiscordMessageText } from "./message-utils-BXLLnFl6.js";
15
+ import { a as resolveDiscordThreadStarter, n as resolveDiscordAutoThreadReplyPlan } from "./threading-DU6pgeGm.js";
16
+ import { n as resolveDiscordWebhookId } from "./sender-identity-BJtfyise.js";
17
+ import { t as beginDiscordActiveTurnThreadRoute } from "./active-turn-thread-route-fmHl1HNd.js";
17
18
  import { n as filterDiscordHistoryEntriesForContext, t as createDiscordHistorySenderProvenance } from "./message-handler.history-CJOsN9w5.js";
18
- import { t as sendTyping } from "./typing--Mzo2kM7.js";
19
- import { buildAgentSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
19
+ import { t as sendTyping } from "./typing-oLiS5Z13.js";
20
20
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
21
21
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
22
+ import { buildAgentSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
22
23
  import { danger, logVerbose, shouldLogVerbose, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
23
24
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
24
25
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
25
26
  import { resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
26
27
  import { convertMarkdownTables, stripInlineDirectiveTagsForDelivery, stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
27
- import { bindIngressLifecycleToReplyOptions, buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, createTypingCallbacks, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, isChannelProgressDraftWorkToolName, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewCommandText, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingProgressNarration, resolveChannelStreamingSuppressDefaultToolProgressMessages, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
28
+ import { bindIngressLifecycleToReplyOptions, buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, createTypingCallbacks, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewCommandText, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingProgressNarration, resolveChannelStreamingSuppressDefaultToolProgressMessages, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
28
29
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
29
30
  import { getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
30
31
  import { evaluateSupplementalContextVisibility } from "openclaw/plugin-sdk/security-runtime";
31
32
  import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/conversation-runtime";
32
- import { EmbeddedBlockChunker, resolveAckReaction, resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
33
+ import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
34
+ import { EmbeddedBlockChunker, resolveAckReaction, resolveAgentConfig as resolveAgentConfig$1, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
33
35
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
34
- import { buildChannelInboundEventContext, dispatchChannelInboundTurn, formatInboundEnvelope, hasFinalInboundReplyDispatch, resolveEnvelopeFormatOptions, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
35
36
  import { getSessionEntry, readSessionUpdatedAt, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
37
+ import { buildChannelInboundEventContext, createCommandTurnContext, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasFinalInboundReplyDispatch, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
36
38
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
37
39
  import { buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
38
40
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
41
+ import { formatAudioTranscriptForAgent } from "openclaw/plugin-sdk/media-understanding-runtime";
39
42
  import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
40
43
  import { readLatestAssistantTextByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime";
41
44
  //#region extensions/discord/src/monitor/message-handler.retry.ts
@@ -78,7 +81,7 @@ function isContextAborted(abortSignal) {
78
81
  }
79
82
  async function buildDiscordMessageProcessContext(params) {
80
83
  const { ctx, text, mediaList } = params;
81
- const { cfg, discordConfig, accountId, runtime, botUserId, 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;
84
+ const { cfg, discordConfig, accountId, runtime, botUserId, mediaMaxBytes, discordRestFetch, abortSignal, guildHistories, historyLimit, replyToMode, message, author, sender, canonicalMessageId, data, client, channelInfo, channelName, messageChannelId, isGuildMessage, isDirectMessage, isGroupDm, baseText, preflightAudioTranscript, threadChannel, threadParentId, threadParentName, threadParentType, threadName, displayChannelSlug, guildInfo, guildSlug, memberRoleIds, channelConfig, baseSessionKey, boundSessionKey, route, commandAuthorized, hasControlCommand, resolveChannelIngress } = ctx;
82
85
  const fromLabel = isDirectMessage ? buildDirectLabel(author) : buildGuildLabel({
83
86
  guild: data.guild ?? void 0,
84
87
  channelName: channelName ?? messageChannelId,
@@ -129,11 +132,18 @@ async function buildDiscordMessageProcessContext(params) {
129
132
  });
130
133
  const channelHistory = createChannelHistoryWindow({ historyMap: guildHistories });
131
134
  let visibleChannelHistory;
135
+ const unavailableMediaCount = mediaList.filter((media) => !media.path).length;
136
+ const appendMediaUnavailableNotice = (body) => unavailableMediaCount > 0 ? formatInboundMediaUnavailableText({
137
+ body,
138
+ notice: `[discord ${unavailableMediaCount > 1 ? `${unavailableMediaCount} attachments` : "attachment"} unavailable]`
139
+ }) : body;
140
+ const bodyWithMediaNotice = appendMediaUnavailableNotice(text) ?? text;
141
+ const agentFacingBody = preflightAudioTranscript !== void 0 ? formatAudioTranscriptForAgent(preflightAudioTranscript) : baseText ?? text;
132
142
  let combinedBody = formatInboundEnvelope({
133
143
  channel: "Discord",
134
144
  from: fromLabel,
135
145
  timestamp: resolveTimestampMs(message.timestamp),
136
- body: text,
146
+ body: bodyWithMediaNotice,
137
147
  chatType: isDirectMessage ? "direct" : "channel",
138
148
  senderLabel,
139
149
  previousTimestamp,
@@ -210,15 +220,17 @@ async function buildDiscordMessageProcessContext(params) {
210
220
  if (threadParentId) {
211
221
  parentSessionKey = buildAgentSessionKey({
212
222
  agentId: route.agentId,
223
+ mainKey: cfg.session?.mainKey,
213
224
  channel: route.channel,
214
225
  peer: {
215
226
  kind: "channel",
216
227
  id: threadParentId
217
- }
228
+ },
229
+ groupScope: route.groupScope
218
230
  });
219
- modelParentSessionKey = parentSessionKey;
231
+ modelParentSessionKey = parentSessionKey === baseSessionKey ? void 0 : parentSessionKey;
220
232
  }
221
- if (!threadParentInheritanceEnabled) parentSessionKey = void 0;
233
+ parentSessionKey = threadParentInheritanceEnabled ? modelParentSessionKey : void 0;
222
234
  }
223
235
  const preflightAudioIndex = preflightAudioTranscript === void 0 ? -1 : mediaList.findIndex((media) => media.contentType?.startsWith("audio/"));
224
236
  const threadKeys = resolveThreadSessionKeys({
@@ -243,12 +255,15 @@ async function buildDiscordMessageProcessContext(params) {
243
255
  agentId: route.agentId,
244
256
  channel: route.channel,
245
257
  cfg,
258
+ parentSessionKey: route.sessionKey,
259
+ groupScope: route.groupScope,
246
260
  threadParentInheritanceEnabled
247
261
  });
248
262
  const deliverTarget = replyPlan.deliverTarget;
249
263
  const replyTarget = replyPlan.replyTarget;
250
264
  const replyReference = replyPlan.replyReference;
251
265
  const autoThreadContext = replyPlan.autoThreadContext;
266
+ const conversationParentId = threadChannel ? threadParentId : autoThreadContext ? messageChannelId : void 0;
252
267
  const effectiveFrom = isDirectMessage ? `discord:${author.id}` : autoThreadContext?.From ?? `discord:channel:${messageChannelId}`;
253
268
  const dmConversationTarget = isDirectMessage ? resolveDiscordConversationIdentity({
254
269
  isDirectMessage,
@@ -270,7 +285,17 @@ async function buildDiscordMessageProcessContext(params) {
270
285
  storePath,
271
286
  sessionKey: effectiveSessionKey
272
287
  });
273
- const ctxPayload = await buildChannelInboundEventContext({
288
+ const channelIngress = await resolveChannelIngress({
289
+ agentId: route.agentId,
290
+ sessionKey: effectiveSessionKey,
291
+ messageId: canonicalMessageId ?? message.id,
292
+ inboundEventKind: ctx.inboundEventKind
293
+ }, {
294
+ parentId: conversationParentId,
295
+ threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0
296
+ });
297
+ const ctxPayload = await (ctx.buildContext ?? buildChannelInboundEventContext)({
298
+ channelIngress,
274
299
  channel: "discord",
275
300
  resolveSupplementalMedia: true,
276
301
  contextVisibility: contextVisibilityMode,
@@ -289,12 +314,19 @@ async function buildDiscordMessageProcessContext(params) {
289
314
  isBot: author.bot && !sender.isPluralKit ? true : void 0
290
315
  },
291
316
  conversation: {
292
- kind: isDirectMessage ? "direct" : "channel",
317
+ kind: isGroupDm ? "group" : isDirectMessage ? "direct" : "channel",
293
318
  id: messageChannelId,
319
+ routePeer: buildDiscordRoutePeer({
320
+ isDirectMessage,
321
+ isGroupDm,
322
+ directUserId: author.id,
323
+ conversationId: messageChannelId
324
+ }),
294
325
  nativeChannelId: messageChannelId,
326
+ avatar: ctx.conversationAvatar,
295
327
  label: fromLabel,
296
- spaceId: isGuildMessage ? (guildInfo?.id ?? guildSlug) || void 0 : void 0,
297
- parentId: threadChannel ? threadParentId : void 0,
328
+ spaceId: isGuildMessage ? (guildInfo?.id ?? data.guild?.id ?? data.guild_id ?? guildSlug) || void 0 : void 0,
329
+ parentId: conversationParentId,
298
330
  threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0
299
331
  },
300
332
  route: {
@@ -313,9 +345,9 @@ async function buildDiscordMessageProcessContext(params) {
313
345
  message: {
314
346
  inboundEventKind: ctx.inboundEventKind,
315
347
  body: combinedBody,
316
- rawBody: preflightAudioTranscript ?? baseText,
317
- bodyForAgent: preflightAudioTranscript ?? baseText ?? text,
318
- commandBody: preflightAudioTranscript ?? baseText,
348
+ rawBody: baseText,
349
+ bodyForAgent: appendMediaUnavailableNotice(agentFacingBody),
350
+ commandBody: baseText,
319
351
  inboundHistory
320
352
  },
321
353
  sessionTranscript: { historyLimit: shouldIncludeChannelHistory ? historyLimit : 0 },
@@ -329,12 +361,10 @@ async function buildDiscordMessageProcessContext(params) {
329
361
  },
330
362
  commands: { authorized: commandAuthorized }
331
363
  },
332
- commandTurn: {
333
- kind: "text-slash",
334
- source: "text",
364
+ commandTurn: createCommandTurnContext(hasControlCommand ? "text" : "message", {
335
365
  authorized: commandAuthorized,
336
- body: preflightAudioTranscript ?? baseText
337
- },
366
+ body: baseText
367
+ }),
338
368
  media: await toInboundMediaFactsWithMetadata(mediaList, { transcribed: (_media, index) => index === preflightAudioIndex }),
339
369
  supplemental: {
340
370
  quote: replyContext && replyVisible ? {
@@ -351,7 +381,7 @@ async function buildDiscordMessageProcessContext(params) {
351
381
  totalTimeoutMs: DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS,
352
382
  abortSignal
353
383
  });
354
- return isContextAborted(abortSignal) ? [] : await toInboundMediaFactsWithMetadata(referencedReplyMediaList);
384
+ return isContextAborted(abortSignal) ? [] : await toInboundMediaFactsWithMetadata(referencedReplyMediaList, { messageId: replyContext.id });
355
385
  }
356
386
  } : void 0,
357
387
  thread: {
@@ -437,10 +467,9 @@ function isFailedProgress(payload) {
437
467
  }
438
468
  function createDiscordMessageProgressRuntime(params) {
439
469
  const { ctx, draftPreview } = params;
440
- const { cfg, discordConfig, route, abortSignal } = ctx;
470
+ const { cfg, route, abortSignal } = ctx;
441
471
  const reasoningLevel = (() => {
442
- const normalizedAgentId = (route.agentId ?? "").trim().toLowerCase() || "main";
443
- const cfgDefault = cfg.agents?.list?.find((entry) => ((entry?.id ?? "").trim().toLowerCase() || "main") === normalizedAgentId)?.reasoningDefault ?? cfg.agents?.defaults?.reasoningDefault;
472
+ const cfgDefault = resolveAgentConfig(cfg, route.agentId ?? "main")?.reasoningDefault ?? cfg.agents?.defaults?.reasoningDefault;
444
473
  const configDefault = cfgDefault === "on" || cfgDefault === "stream" ? cfgDefault : "off";
445
474
  if (!params.sessionKey) return configDefault;
446
475
  try {
@@ -459,177 +488,84 @@ function createDiscordMessageProgressRuntime(params) {
459
488
  const reasoningDurableEnabled = reasoningLevel === "on";
460
489
  const reasoningWindowEnabled = reasoningLevel === "stream";
461
490
  let shouldYieldDraftCommentary = () => false;
462
- let progressTurnStartedAt = Date.now();
463
- let progressReasoningSteps = 0;
464
- let progressToolCalls = 0;
465
- let progressCommentaryNotes = 0;
466
- const seenCommentaryIds = /* @__PURE__ */ new Set();
467
- let lastCommentaryNoteText = "";
468
- const noteWindowCommentary = (itemId, noteText) => {
469
- const trimmed = noteText?.trim();
470
- if (!trimmed) return;
471
- if (itemId) {
472
- if (seenCommentaryIds.has(itemId)) return;
473
- seenCommentaryIds.add(itemId);
474
- progressCommentaryNotes += 1;
475
- return;
476
- }
477
- if (trimmed !== lastCommentaryNoteText) {
478
- lastCommentaryNoteText = trimmed;
479
- progressCommentaryNotes += 1;
480
- }
481
- };
482
- let windowReasoningOpen = false;
483
- const closePendingWindowThought = () => {
484
- if (windowReasoningOpen) {
485
- windowReasoningOpen = false;
486
- progressReasoningSteps += 1;
487
- }
488
- };
489
- const resetTurnState = () => {
490
- progressTurnStartedAt = Date.now();
491
- progressReasoningSteps = 0;
492
- progressToolCalls = 0;
493
- progressCommentaryNotes = 0;
494
- seenCommentaryIds.clear();
495
- lastCommentaryNoteText = "";
496
- windowReasoningOpen = false;
497
- };
498
491
  const handleAssistantMessageBoundary = () => {
499
- if (draftPreview.handleAssistantMessageBoundary()) {
500
- resetTurnState();
501
- params.onTurnReset();
502
- }
492
+ if (draftPreview.handleAssistantMessageBoundary()) params.onTurnReset();
503
493
  };
504
- const buildProgressSummaryLine = () => {
505
- closePendingWindowThought();
506
- const seconds = Math.max(1, Math.round((Date.now() - progressTurnStartedAt) / 1e3));
507
- return `-# ${[
508
- ...progressReasoningSteps > 0 ? [`🧠 ${progressReasoningSteps} thought${progressReasoningSteps === 1 ? "" : "s"}`] : [],
509
- ...progressCommentaryNotes > 0 ? [`💬 ${progressCommentaryNotes} note${progressCommentaryNotes === 1 ? "" : "s"}`] : [],
510
- ...progressToolCalls > 0 ? [`🛠️ ${progressToolCalls} tool call${progressToolCalls === 1 ? "" : "s"}`] : [],
511
- `⏱️ ${seconds}s`
512
- ].join(" · ")}`;
513
- };
514
- return {
515
- replyOptions: {
516
- onAssistantMessageStart: draftPreview.draftStream ? handleAssistantMessageBoundary : void 0,
517
- onReasoningEnd: draftPreview.draftStream ? () => {
518
- closePendingWindowThought();
519
- handleAssistantMessageBoundary();
520
- } : void 0,
521
- suppressDefaultToolProgressMessages: params.sourceRepliesAreToolOnly && params.reactions.statusReactionsExplicitlyEnabled || draftPreview.suppressDefaultToolProgressMessages ? true : void 0,
522
- allowToolLifecycleWhenProgressHidden: params.reactions.statusReactionsEnabled ? true : void 0,
523
- commentaryProgressEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
524
- progressPreambleEnabled: draftPreview.draftStream && draftPreview.isProgressMode ? true : void 0,
525
- commentaryPayloadsEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
526
- reasoningPayloadsEnabled: reasoningDurableEnabled,
527
- onVerboseProgressVisibility: (isActive) => {
528
- shouldYieldDraftCommentary = isActive;
529
- },
530
- onNarrationUpdate: draftPreview.narrationProgressEnabled ? async (payload) => {
531
- if (isProcessAborted$1(abortSignal) || shouldYieldDraftCommentary()) return;
532
- await draftPreview.pushNarrationProgress(payload.text);
533
- } : void 0,
534
- onProgressNarratorLifecycle: draftPreview.narrationProgressEnabled ? (lifecycle) => draftPreview.setProgressNarratorLifecycle(lifecycle) : void 0,
535
- isProgressDraftVisible: draftPreview.narrationProgressEnabled ? () => draftPreview.isProgressDraftVisible : void 0,
536
- narrationHideCommandText: draftPreview.narrationHideCommandText ? true : void 0,
537
- onReasoningStream: async (payload) => {
538
- if (payload?.requiresReasoningProgressOptIn === true && !reasoningWindowEnabled) return;
539
- if (payload?.text) windowReasoningOpen = true;
540
- await params.reactions.controller.setThinking();
541
- await draftPreview.pushReasoningProgress(payload?.text, { snapshot: payload?.isReasoningSnapshot === true });
542
- },
543
- streamReasoningInNonStreamModes: reasoningWindowEnabled,
544
- onToolStart: async (payload) => {
545
- if (isProcessAborted$1(abortSignal)) return;
546
- await params.reactions.maybeBindToToolReaction(payload);
547
- await params.reactions.controller.setTool(payload.name);
548
- if (payload.phase === "start") closePendingWindowThought();
549
- if (payload.phase === "start" && isChannelProgressDraftWorkToolName(payload.name)) progressToolCalls += 1;
550
- await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
551
- event: "tool",
552
- itemId: payload.itemId,
553
- toolCallId: payload.toolCallId,
554
- name: payload.name,
555
- phase: payload.phase,
556
- args: payload.args
557
- }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
558
- },
559
- onItemEvent: async (payload) => {
560
- if (isFailedProgress(payload)) return false;
561
- if (payload.kind === "preamble") {
562
- if (shouldYieldDraftCommentary()) return;
563
- return await draftPreview.pushPreambleItemEvent(payload, noteWindowCommentary);
564
- }
565
- await draftPreview.pushToolProgress(buildChannelProgressDraftLineForEntry(discordConfig, {
566
- event: "item",
567
- itemId: payload.itemId,
568
- toolCallId: payload.toolCallId,
569
- itemKind: payload.kind,
570
- title: payload.title,
571
- name: payload.name,
572
- phase: payload.phase,
573
- status: payload.status,
574
- summary: payload.summary,
575
- progressText: payload.progressText,
576
- meta: payload.meta
577
- }));
578
- },
579
- onPlanUpdate: async (payload) => {
580
- if (payload.phase === "update") await draftPreview.pushPlanProgress(payload.steps, { explanation: payload.explanation });
581
- },
582
- onApprovalEvent: async (payload) => {
583
- if (payload.phase === "requested") await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
584
- event: "approval",
585
- phase: payload.phase,
586
- title: payload.title,
587
- command: payload.command,
588
- reason: payload.reason,
589
- message: payload.message
590
- }));
591
- },
592
- onCommandOutput: async (payload) => {
593
- if (isFailedProgress(payload)) return false;
594
- if (payload.phase !== "end") return;
595
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
596
- event: "command-output",
597
- itemId: payload.itemId,
598
- toolCallId: payload.toolCallId,
599
- phase: payload.phase,
600
- title: payload.title,
601
- name: payload.name,
602
- status: payload.status,
603
- exitCode: payload.exitCode
604
- }));
605
- },
606
- onPatchSummary: async (payload) => {
607
- if (payload.phase !== "end") return;
608
- await draftPreview.pushToolProgress(buildChannelProgressDraftLine({
609
- event: "patch",
610
- itemId: payload.itemId,
611
- toolCallId: payload.toolCallId,
612
- phase: payload.phase,
613
- title: payload.title,
614
- name: payload.name,
615
- added: payload.added,
616
- modified: payload.modified,
617
- deleted: payload.deleted,
618
- summary: payload.summary
619
- }));
620
- },
621
- onCompactionStart: async () => {
622
- if (!isProcessAborted$1(abortSignal)) await params.reactions.controller.setCompacting();
623
- },
624
- onCompactionEnd: async () => {
625
- if (!isProcessAborted$1(abortSignal)) {
626
- params.reactions.controller.cancelPending();
627
- await params.reactions.controller.setThinking();
628
- }
494
+ return { replyOptions: {
495
+ onAssistantMessageStart: draftPreview.draftStream ? () => {
496
+ handleAssistantMessageBoundary();
497
+ return false;
498
+ } : void 0,
499
+ onReasoningEnd: draftPreview.draftStream ? () => {
500
+ handleAssistantMessageBoundary();
501
+ return false;
502
+ } : void 0,
503
+ onQueuedFollowupAdmitted: draftPreview.draftStream ? () => {
504
+ if (draftPreview.handleQueuedFollowupAdmitted()) params.onTurnReset();
505
+ } : void 0,
506
+ suppressDefaultToolProgressMessages: params.sourceRepliesAreToolOnly && params.reactions.statusReactionsExplicitlyEnabled || draftPreview.suppressDefaultToolProgressMessages ? true : void 0,
507
+ allowToolLifecycleWhenProgressHidden: params.reactions.statusReactionsEnabled ? true : void 0,
508
+ commentaryProgressEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
509
+ progressPreambleEnabled: draftPreview.draftStream && draftPreview.isProgressMode ? true : void 0,
510
+ commentaryPayloadsEnabled: draftPreview.isProgressMode ? draftPreview.commentaryProgressEnabled : void 0,
511
+ shouldDeliverCommentaryPayloads: draftPreview.isProgressMode && draftPreview.commentaryProgressEnabled ? () => shouldYieldDraftCommentary() : void 0,
512
+ reasoningPayloadsEnabled: reasoningDurableEnabled,
513
+ onVerboseProgressVisibility: (isActive) => {
514
+ shouldYieldDraftCommentary = isActive;
515
+ },
516
+ onNarrationUpdate: draftPreview.narrationProgressEnabled ? async (payload) => {
517
+ if (isProcessAborted$1(abortSignal) || shouldYieldDraftCommentary()) return;
518
+ await draftPreview.pushNarrationProgress(payload.text);
519
+ } : void 0,
520
+ onProgressNarratorLifecycle: draftPreview.narrationProgressEnabled ? (lifecycle) => draftPreview.setProgressNarratorLifecycle(lifecycle) : void 0,
521
+ isProgressDraftVisible: draftPreview.narrationProgressEnabled ? () => draftPreview.isProgressDraftVisible : void 0,
522
+ narrationHideCommandText: draftPreview.narrationHideCommandText ? true : void 0,
523
+ onReasoningStream: async (payload) => {
524
+ if (payload?.requiresReasoningProgressOptIn === true && !reasoningWindowEnabled) return false;
525
+ await params.reactions.controller.setThinking();
526
+ return await draftPreview.pushReasoningProgress(payload?.text, { snapshot: payload?.isReasoningSnapshot === true });
527
+ },
528
+ streamReasoningInNonStreamModes: reasoningWindowEnabled,
529
+ onToolStart: async (payload) => {
530
+ if (isProcessAborted$1(abortSignal)) return false;
531
+ await params.reactions.maybeBindToToolReaction(payload);
532
+ await params.reactions.controller.setTool(payload.name);
533
+ return await draftPreview.pushToolEvent(payload);
534
+ },
535
+ onItemEvent: async (payload) => {
536
+ if (isFailedProgress(payload)) return false;
537
+ if (payload.kind === "preamble") {
538
+ if (shouldYieldDraftCommentary()) return;
539
+ return await draftPreview.pushPreambleItemEvent(payload);
629
540
  }
541
+ return await draftPreview.pushItemEvent(payload);
630
542
  },
631
- buildProgressSummaryLine
632
- };
543
+ onPlanUpdate: async (payload) => {
544
+ if (payload.phase === "update") return await draftPreview.pushPlanProgress(payload.steps, { explanation: payload.explanation });
545
+ return false;
546
+ },
547
+ onApprovalEvent: async (payload) => {
548
+ return await draftPreview.pushApprovalEvent(payload);
549
+ },
550
+ onCommandOutput: async (payload) => {
551
+ if (isFailedProgress(payload)) return false;
552
+ return await draftPreview.pushCommandOutputEvent(payload);
553
+ },
554
+ onPatchSummary: async (payload) => {
555
+ return await draftPreview.pushPatchEvent(payload);
556
+ },
557
+ onCompactionStart: async () => {
558
+ if (!isProcessAborted$1(abortSignal)) await params.reactions.controller.setCompacting();
559
+ return false;
560
+ },
561
+ onCompactionEnd: async () => {
562
+ if (!isProcessAborted$1(abortSignal)) {
563
+ params.reactions.controller.cancelPending();
564
+ await params.reactions.controller.setThinking();
565
+ }
566
+ return false;
567
+ }
568
+ } };
633
569
  }
634
570
  //#endregion
635
571
  //#region extensions/discord/src/monitor/ack-reactions.ts
@@ -1029,7 +965,6 @@ function createDiscordDraftStream(params) {
1029
965
  //#endregion
1030
966
  //#region extensions/discord/src/preview-streaming.ts
1031
967
  function resolveDiscordPreviewStreamMode(params = {}) {
1032
- if (params.streaming === void 0) return "progress";
1033
968
  return resolveChannelPreviewStreamMode(params, "off");
1034
969
  }
1035
970
  //#endregion
@@ -1039,9 +974,13 @@ function createDiscordDraftPreviewController(params) {
1039
974
  const hookRunner = getGlobalHookRunner();
1040
975
  const allowProviderPreview = !((hookRunner?.hasHooks("reply_payload_sending") ?? false) || (hookRunner?.hasHooks("message_sending") ?? false));
1041
976
  const draftMaxChars = Math.min(params.textLimit, 2e3);
1042
- const accountBlockStreamingEnabled = resolveChannelStreamingBlockEnabled(params.discordConfig) ?? params.cfg.agents?.defaults?.blockStreamingDefault === "on";
1043
977
  const canStreamProgressDraftForToolOnlySource = params.sourceRepliesAreToolOnly && discordStreamMode === "progress";
1044
- const draftStream = allowProviderPreview && (!params.sourceRepliesAreToolOnly || canStreamProgressDraftForToolOnlySource) && discordStreamMode !== "off" && !accountBlockStreamingEnabled ? createDiscordDraftStream({
978
+ const previewAvailable = allowProviderPreview && (!params.sourceRepliesAreToolOnly || canStreamProgressDraftForToolOnlySource) && discordStreamMode !== "off";
979
+ const accountBlockStreamingEnabled = resolveChannelStreamingBlockEnabled(params.discordConfig, {
980
+ previewAvailable,
981
+ blockStreamingDefault: params.cfg.agents?.defaults?.blockStreamingDefault
982
+ });
983
+ const draftStream = previewAvailable && !accountBlockStreamingEnabled ? createDiscordDraftStream({
1045
984
  rest: params.deliveryRest,
1046
985
  channelId: params.deliverChannelId,
1047
986
  maxChars: draftMaxChars,
@@ -1059,7 +998,7 @@ function createDiscordDraftPreviewController(params) {
1059
998
  let draftText = "";
1060
999
  let hasStreamedMessage = false;
1061
1000
  let finalizedViaPreviewMessage = false;
1062
- let finalReplyDelivered = false;
1001
+ let finalReplyError;
1063
1002
  let progressDraftStartedBeforeFinal = false;
1064
1003
  let progressDraftCollapsed = false;
1065
1004
  let progressNarratorLifecycle;
@@ -1080,13 +1019,16 @@ function createDiscordDraftPreviewController(params) {
1080
1019
  commentaryLinePrefix: "💬 ",
1081
1020
  reasoningGate: previewToolProgressEnabled,
1082
1021
  commentaryItalics: false,
1022
+ buildProgressEventLine: (input, options) => input.event === "tool" || input.event === "item" || input.event === "command-output" ? buildChannelProgressDraftLineForEntry(params.discordConfig, input, options) : buildChannelProgressDraftLine(input, options),
1083
1023
  update: async (previewText, options) => {
1024
+ if (!draftStream) return false;
1084
1025
  lastPartialText = previewText;
1085
1026
  draftText = previewText;
1086
1027
  hasStreamedMessage = true;
1087
1028
  draftChunker?.reset();
1088
- draftStream?.update(previewText);
1089
- if (options?.flush) await draftStream?.flush();
1029
+ draftStream.update(previewText);
1030
+ if (options?.flush) await draftStream.flush();
1031
+ return Boolean(draftStream.messageId());
1090
1032
  },
1091
1033
  deleteCurrent: async () => {
1092
1034
  lastPartialText = "";
@@ -1111,7 +1053,22 @@ function createDiscordDraftPreviewController(params) {
1111
1053
  resetProgressState();
1112
1054
  };
1113
1055
  const pushPreambleHeadline = async (text, options) => {
1114
- if (discordStreamMode === "progress") await progressDraft.pushPreambleHeadline(text, options);
1056
+ if (discordStreamMode === "progress") return await progressDraft.pushPreambleHeadline(text, options);
1057
+ return false;
1058
+ };
1059
+ const beginNewProgressTurn = (options) => {
1060
+ const beganNewTurn = progressDraft.beginNewTurn(options);
1061
+ if (beganNewTurn) {
1062
+ progressDraftCollapsed = false;
1063
+ progressDraftStartedBeforeFinal = false;
1064
+ finalReplyError = void 0;
1065
+ finalizedViaPreviewMessage = false;
1066
+ progressNarratorLifecycle?.beginTurn();
1067
+ }
1068
+ if (discordStreamMode === "progress") {
1069
+ if (beganNewTurn) draftStream?.forceNewMessage("discard");
1070
+ } else forceNewMessageIfNeeded();
1071
+ return beganNewTurn;
1115
1072
  };
1116
1073
  return {
1117
1074
  draftStream,
@@ -1147,8 +1104,8 @@ function createDiscordDraftPreviewController(params) {
1147
1104
  progressDraft.markFinalReplyStarted();
1148
1105
  progressNarratorLifecycle?.stopTurn();
1149
1106
  },
1150
- markFinalReplyDelivered() {
1151
- finalReplyDelivered = true;
1107
+ markFinalReplyDelivered(isError = false) {
1108
+ finalReplyError = isError;
1152
1109
  progressDraft.markFinalReplyDelivered();
1153
1110
  },
1154
1111
  markPreviewFinalized() {
@@ -1157,40 +1114,43 @@ function createDiscordDraftPreviewController(params) {
1157
1114
  async retarget(channelId) {
1158
1115
  await draftStream?.retarget(channelId);
1159
1116
  },
1160
- async finalizeProgressReceipt(receiptLine) {
1117
+ async finalizeProgressDraft() {
1161
1118
  if (!draftStream || discordStreamMode !== "progress") return false;
1162
- const receipt = receiptLine.trim();
1163
- if (!receipt) return false;
1164
1119
  const progressText = lastPartialText.trimEnd();
1165
- const maxProgressChars = Math.max(0, draftMaxChars - receipt.length - 1);
1166
- const fittedProgressText = progressText.length > maxProgressChars ? progressText.slice(progressText.length - maxProgressChars).trimStart() : progressText;
1167
- draftStream.update(fittedProgressText ? `${fittedProgressText}\n${receipt}` : receipt);
1120
+ if (!progressText) return false;
1121
+ draftStream.update(progressText);
1168
1122
  await draftStream.stop();
1169
1123
  if (!draftStream.messageId()) return false;
1170
1124
  finalizedViaPreviewMessage = true;
1171
1125
  return true;
1172
1126
  },
1173
1127
  disableBlockStreamingForDraft: draftStream ? true : void 0,
1128
+ pushToolEvent: progressDraft.pushToolEvent,
1129
+ pushItemEvent: progressDraft.pushItemEvent,
1130
+ pushApprovalEvent: progressDraft.pushApprovalEvent,
1131
+ pushCommandOutputEvent: progressDraft.pushCommandOutputEvent,
1132
+ pushPatchEvent: progressDraft.pushPatchEvent,
1174
1133
  async pushToolProgress(line, options) {
1175
- await progressDraft.pushToolProgress(line, options);
1134
+ return await progressDraft.pushToolProgress(line, options);
1176
1135
  },
1177
1136
  async pushPlanProgress(steps, options) {
1178
- await progressDraft.pushPlanProgress(steps, options);
1137
+ return await progressDraft.pushPlanProgress(steps, options);
1179
1138
  },
1180
1139
  async pushReasoningProgress(text, options) {
1181
- await progressDraft.pushReasoningProgress(text, options);
1140
+ return await progressDraft.pushReasoningProgress(text, options);
1182
1141
  },
1183
1142
  async pushNarrationProgress(text) {
1184
- await progressDraft.pushNarrationProgress(text);
1143
+ return await progressDraft.pushNarrationProgress(text);
1185
1144
  },
1186
1145
  pushPreambleHeadline,
1187
- async pushPreambleItemEvent(payload, noteCommentary) {
1188
- await pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
1189
- if (!progressDraft.commentaryProgressEnabled) return;
1190
- if (await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) noteCommentary(payload.itemId, payload.progressText);
1146
+ async pushPreambleItemEvent(payload) {
1147
+ const headlineAccepted = await pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
1148
+ if (!progressDraft.commentaryProgressEnabled) return headlineAccepted;
1149
+ const commentaryAccepted = await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId });
1150
+ return headlineAccepted || commentaryAccepted;
1191
1151
  },
1192
1152
  async pushCommentaryProgress(text, options) {
1193
- await progressDraft.pushCommentaryProgress(text, options);
1153
+ return await progressDraft.pushCommentaryProgress(text, options);
1194
1154
  },
1195
1155
  resolvePreviewFinalText(text) {
1196
1156
  if (typeof text !== "string") return;
@@ -1248,20 +1208,10 @@ function createDiscordDraftPreviewController(params) {
1248
1208
  });
1249
1209
  },
1250
1210
  handleAssistantMessageBoundary() {
1251
- const beganNewTurn = progressDraft.beginNewTurn();
1252
- if (beganNewTurn) {
1253
- progressDraftCollapsed = false;
1254
- progressDraftStartedBeforeFinal = false;
1255
- finalReplyDelivered = false;
1256
- finalizedViaPreviewMessage = false;
1257
- progressNarratorLifecycle?.beginTurn();
1258
- }
1259
- if (discordStreamMode === "progress") {
1260
- if (beganNewTurn) draftStream?.forceNewMessage("discard");
1261
- return beganNewTurn;
1262
- }
1263
- forceNewMessageIfNeeded();
1264
- return beganNewTurn;
1211
+ return beginNewProgressTurn();
1212
+ },
1213
+ handleQueuedFollowupAdmitted() {
1214
+ return beginNewProgressTurn({ force: true });
1265
1215
  },
1266
1216
  async flush() {
1267
1217
  if (!draftStream) return;
@@ -1280,8 +1230,8 @@ function createDiscordDraftPreviewController(params) {
1280
1230
  async cleanup() {
1281
1231
  try {
1282
1232
  progressDraft.cancel();
1283
- if (!finalReplyDelivered) await draftStream?.discardPending();
1284
- if (!finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();
1233
+ if (finalReplyError !== false) await draftStream?.discardPending();
1234
+ if (finalReplyError !== true && !finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();
1285
1235
  await draftStream?.cleanupRetargeted();
1286
1236
  } catch (err) {
1287
1237
  params.log(`discord: draft cleanup failed: ${String(err)}`);
@@ -1391,7 +1341,7 @@ function createDiscordMessageReplyRuntime(params) {
1391
1341
  limit: historyLimit
1392
1342
  });
1393
1343
  };
1394
- const beginDeliveryCorrelation = () => params.isRoomEvent ? beginDiscordInboundEventDeliveryCorrelation(ctxPayload.SessionKey, {
1344
+ const beginDeliveryCorrelation = () => params.isRoomEvent ? discordInboundEventDelivery.begin(ctxPayload.SessionKey, {
1395
1345
  outboundTo: messageChannelId,
1396
1346
  outboundAccountId: route.accountId,
1397
1347
  markInboundEventDelivered: clearGroupHistory
@@ -1451,16 +1401,6 @@ function createDiscordMessageReplyRuntime(params) {
1451
1401
  }
1452
1402
  //#endregion
1453
1403
  //#region extensions/discord/src/monitor/message-handler.process-thread-route.ts
1454
- async function finalizeDiscordAdoptedThreadProgressReceipt(hasProgressDraft, receipt, finalizeDraft, deliverReceipt, onFinalizeError) {
1455
- const receiptLine = receipt.trim();
1456
- if (!hasProgressDraft || !receiptLine) return;
1457
- try {
1458
- if (await finalizeDraft(receiptLine)) return;
1459
- } catch (error) {
1460
- onFinalizeError?.(error);
1461
- }
1462
- await deliverReceipt(receiptLine);
1463
- }
1464
1404
  function createDiscordMessageActiveThreadRoute(params) {
1465
1405
  let adoptedThreadId;
1466
1406
  let threadReplyDelivered = false;
@@ -1510,7 +1450,7 @@ async function processDiscordMessage(ctx, observer) {
1510
1450
  }
1511
1451
  async function processDiscordMessageInner(ctx, observer) {
1512
1452
  const dispatchStartedAt = Date.now();
1513
- const { cfg, accountId, token, runtime, guildHistories, historyLimit, textLimit, replyToMode, message, messageChannelId, canonicalMessageId, isGuildMessage, isDirectMessage, isGroupDm, messageText, channelConfig, threadBindings, route, abortSignal, turnAdoptionLifecycle, preparedMedia: mediaList } = ctx;
1453
+ const { cfg, accountId, token, runtime, textLimit, replyToMode, message, messageChannelId, isGuildMessage, isDirectMessage, isGroupDm, messageText, threadBindings, route, abortSignal, turnAdoptionLifecycle, preparedMedia: mediaList } = ctx;
1514
1454
  if (isProcessAborted(abortSignal)) return;
1515
1455
  const text = messageText;
1516
1456
  if (!text && mediaList.length === 0) {
@@ -1527,7 +1467,7 @@ async function processDiscordMessageInner(ctx, observer) {
1527
1467
  }
1528
1468
  });
1529
1469
  const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
1530
- const configuredTypingMode = resolveAgentConfig(cfg, route.agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
1470
+ const configuredTypingMode = resolveAgentConfig$1(cfg, route.agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
1531
1471
  const configuredTypingInterval = cfg.agents?.defaults?.typingIntervalSeconds;
1532
1472
  const shouldDisableCoreTypingKeepalive = sourceRepliesAreToolOnly && configuredTypingMode === void 0 && configuredTypingInterval === void 0;
1533
1473
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
@@ -1549,7 +1489,7 @@ async function processDiscordMessageInner(ctx, observer) {
1549
1489
  sessionKey: ctxPayload.SessionKey,
1550
1490
  accountId,
1551
1491
  sourceChannelId: messageChannelId,
1552
- sourceMessageId: canonicalMessageId ?? message.id,
1492
+ sourceMessageId: ctx.canonicalMessageId ?? message.id,
1553
1493
  sourceReplyReference,
1554
1494
  log: logVerbose
1555
1495
  });
@@ -1587,21 +1527,21 @@ async function processDiscordMessageInner(ctx, observer) {
1587
1527
  let userFacingFinalDelivered = false;
1588
1528
  let userFacingFinalDeliveryFailed = false;
1589
1529
  let pendingToolWarningFinal;
1590
- const markUserFacingFinalDelivered = () => {
1591
- userFacingFinalDelivered = true;
1592
- userFacingFinalDeliveryFailed = false;
1593
- pendingToolWarningFinal = void 0;
1594
- draftPreview.markFinalReplyDelivered();
1595
- observer?.onFinalReplyDelivered?.();
1530
+ const markFinalReplyDelivered = (isError = false) => {
1531
+ draftPreview.markFinalReplyDelivered(isError);
1532
+ if (!isError) {
1533
+ userFacingFinalDelivered = true;
1534
+ userFacingFinalDeliveryFailed = false;
1535
+ pendingToolWarningFinal = void 0;
1536
+ observer?.onFinalReplyDelivered?.();
1537
+ }
1596
1538
  };
1597
- let progressReceiptLine;
1598
1539
  let clearProgressDraftAfterFinalDelivery = false;
1599
1540
  const resetDeliveryState = () => {
1600
1541
  finalReplyStartNotified = false;
1601
1542
  userFacingFinalDelivered = false;
1602
1543
  userFacingFinalDeliveryFailed = false;
1603
1544
  pendingToolWarningFinal = void 0;
1604
- progressReceiptLine = void 0;
1605
1545
  clearProgressDraftAfterFinalDelivery = false;
1606
1546
  };
1607
1547
  const progress = createDiscordMessageProgressRuntime({
@@ -1668,7 +1608,8 @@ async function processDiscordMessageInner(ctx, observer) {
1668
1608
  sessionKey: ctxPayload.SessionKey,
1669
1609
  threadBindings,
1670
1610
  mediaLocalRoots,
1671
- kind: "block"
1611
+ kind: "block",
1612
+ bindPendingFinalDelivery: info.bindPendingFinalDelivery
1672
1613
  });
1673
1614
  if (result.visibleReplySent) replyReference.markSent();
1674
1615
  return result;
@@ -1705,7 +1646,6 @@ async function processDiscordMessageInner(ctx, observer) {
1705
1646
  }
1706
1647
  if (draftStream && isFinal && draftPreview.isProgressMode && !deliverablePayload.isError && draftPreview.hasProgressDraftToCollapse && draftStream) {
1707
1648
  await draftPreview.flush();
1708
- progressReceiptLine = progress.buildProgressSummaryLine();
1709
1649
  clearProgressDraftAfterFinalDelivery = true;
1710
1650
  }
1711
1651
  if (draftStream && isFinal && !draftPreview.isProgressMode && !deliverablePayload.isError) {
@@ -1732,7 +1672,7 @@ async function processDiscordMessageInner(ctx, observer) {
1732
1672
  });
1733
1673
  },
1734
1674
  onPreviewFinalized: () => {
1735
- markUserFacingFinalDelivered();
1675
+ markFinalReplyDelivered();
1736
1676
  draftPreview.markPreviewFinalized();
1737
1677
  replyReference.markSent();
1738
1678
  },
@@ -1767,11 +1707,12 @@ async function processDiscordMessageInner(ctx, observer) {
1767
1707
  threadBindings,
1768
1708
  mediaLocalRoots,
1769
1709
  allowedMentions,
1770
- kind: info.kind
1710
+ kind: info.kind,
1711
+ bindPendingFinalDelivery: info.bindPendingFinalDelivery
1771
1712
  })).visibleReplySent;
1772
1713
  },
1773
1714
  onNormalDelivered: () => {
1774
- markUserFacingFinalDelivered();
1715
+ markFinalReplyDelivered();
1775
1716
  replyReference.markSent();
1776
1717
  }
1777
1718
  })).kind !== "normal-skipped") return { visibleReplySent: true };
@@ -1787,14 +1728,9 @@ async function processDiscordMessageInner(ctx, observer) {
1787
1728
  }
1788
1729
  const replyToId = replyReference.use();
1789
1730
  if (isFinal) notifyFinalReplyStart();
1790
- const receiptLine = isFinal && deliverablePayload.isError !== true ? progressReceiptLine : void 0;
1791
- const payloadForDelivery = receiptLine ? {
1792
- ...deliverablePayload,
1793
- text: deliverablePayload.text?.trim() ? `${deliverablePayload.text.trimEnd()}\n${receiptLine}` : receiptLine
1794
- } : deliverablePayload;
1795
1731
  const result = await deliverDiscordReply({
1796
1732
  cfg,
1797
- replies: [payloadForDelivery],
1733
+ replies: [deliverablePayload],
1798
1734
  target: deliverTarget,
1799
1735
  token,
1800
1736
  accountId,
@@ -1809,18 +1745,16 @@ async function processDiscordMessageInner(ctx, observer) {
1809
1745
  sessionKey: ctxPayload.SessionKey,
1810
1746
  threadBindings,
1811
1747
  mediaLocalRoots,
1812
- kind: info.kind
1748
+ kind: info.kind,
1749
+ bindPendingFinalDelivery: info.bindPendingFinalDelivery
1813
1750
  });
1814
1751
  if (!result.visibleReplySent) return result;
1815
1752
  replyReference.markSent();
1816
- if (isFinal && deliverablePayload.isError !== true) {
1817
- if (receiptLine) {
1818
- progressReceiptLine = void 0;
1819
- draftPreview.markProgressDraftCollapsed();
1820
- }
1821
- markUserFacingFinalDelivered();
1822
- if (clearProgressDraftAfterFinalDelivery) {
1753
+ if (isFinal) {
1754
+ markFinalReplyDelivered(deliverablePayload.isError === true);
1755
+ if (deliverablePayload.isError !== true && clearProgressDraftAfterFinalDelivery) {
1823
1756
  clearProgressDraftAfterFinalDelivery = false;
1757
+ draftPreview.markProgressDraftCollapsed();
1824
1758
  await draftStream?.discardPending();
1825
1759
  await draftStream?.clear();
1826
1760
  }
@@ -1891,13 +1825,13 @@ async function processDiscordMessageInner(ctx, observer) {
1891
1825
  history: isRoomEvent ? void 0 : {
1892
1826
  isGroup: isGuildMessage,
1893
1827
  historyKey: messageChannelId,
1894
- historyMap: guildHistories,
1895
- limit: historyLimit
1828
+ historyMap: ctx.guildHistories,
1829
+ limit: ctx.historyLimit
1896
1830
  },
1897
1831
  replyOptions: {
1898
1832
  ...turnAdoptionLifecycle ? bindIngressLifecycleToReplyOptions(turnAdoptionLifecycle) : {},
1899
1833
  abortSignal,
1900
- skillFilter: channelConfig?.skills,
1834
+ skillFilter: ctx.channelConfig?.skills,
1901
1835
  sourceReplyDeliveryMode,
1902
1836
  typingKeepalive: shouldDisableCoreTypingKeepalive ? false : void 0,
1903
1837
  queuedDeliveryCorrelations: isRoomEvent ? [{ begin: beginQueuedDeliveryCorrelation }] : void 0,
@@ -1917,8 +1851,12 @@ async function processDiscordMessageInner(ctx, observer) {
1917
1851
  }
1918
1852
  if (activeThreadRoute.threadReplyDelivered && !userFacingFinalDelivered) {
1919
1853
  draftPreview.markFinalReplyStarted();
1920
- await finalizeDiscordAdoptedThreadProgressReceipt(draftPreview.hasProgressDraftToCollapse, progress.buildProgressSummaryLine(), (receiptLine) => draftPreview.finalizeProgressReceipt(receiptLine), (receiptText) => deliverDiscordPayload({ text: receiptText }, { kind: "block" }, { allowProgressBlock: true }), (error) => logVerbose(`discord: failed to finalize adopted thread progress (${String(error)})`));
1921
- markUserFacingFinalDelivered();
1854
+ if (draftPreview.hasProgressDraftToCollapse) try {
1855
+ await draftPreview.finalizeProgressDraft();
1856
+ } catch (error) {
1857
+ logVerbose(`discord: failed to finalize adopted thread progress (${String(error)})`);
1858
+ }
1859
+ markFinalReplyDelivered();
1922
1860
  }
1923
1861
  } catch (err) {
1924
1862
  if (isProcessAborted(abortSignal)) {
@@ -1932,7 +1870,9 @@ async function processDiscordMessageInner(ctx, observer) {
1932
1870
  activeThreadRoute.end();
1933
1871
  endDeliveryCorrelation();
1934
1872
  await draftPreview.cleanup();
1935
- const finalDeliveryFailed = (dispatchResult?.failedCounts?.final ?? 0) > 0;
1873
+ dispatchError ||= readAgentRunTerminalOutcome(dispatchResult) === "failed";
1874
+ const finalReceipt = dispatchResult?.settledReceipt?.counts.final;
1875
+ const finalDeliveryFailed = (finalReceipt?.failedBeforeSend ?? 0) + (finalReceipt?.failedAfterSend ?? 0) > 0;
1936
1876
  await reactions.finish({
1937
1877
  dispatchAborted,
1938
1878
  dispatchError,
@@ -1943,7 +1883,7 @@ async function processDiscordMessageInner(ctx, observer) {
1943
1883
  const finalDispatchResult = dispatchResult;
1944
1884
  if (!finalDispatchResult || !hasFinalInboundReplyDispatch(finalDispatchResult)) return;
1945
1885
  if (shouldLogVerbose()) {
1946
- const finalCount = finalDispatchResult.counts.final;
1886
+ const finalCount = finalDispatchResult.settledReceipt?.counts.final.delivered ?? 0;
1947
1887
  logVerbose(`discord: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`);
1948
1888
  }
1949
1889
  }