@openclaw/discord 2026.7.2-beta.2 → 2026.7.2-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 (85) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{activities-api-D0D_87n3.js → activities-api-DS1L7KRY.js} +15 -42
  3. package/dist/activities-api.js +1 -1
  4. package/dist/{allow-list-B7qOH5xu.js → allow-list-DzKrtfah.js} +18 -7
  5. package/dist/{api-B88T4rFN.js → api-C43z8vUm.js} +3 -1
  6. package/dist/api.js +25 -16
  7. package/dist/{approval-handler.runtime-B7E1-x5q.js → approval-handler.runtime-BbkfPoGc.js} +5 -6
  8. package/dist/{conversation-identity-BPcbhCAH.js → approval-message-safety-DrLlgIPz.js} +48 -2
  9. package/dist/{audit-CjpnY1yb.js → audit-BC6bgPMX.js} +3 -3
  10. package/dist/{channel-BDfKr14H.js → channel-5MfaC2IE.js} +18 -18
  11. package/dist/{channel-actions-BQct-o4T.js → channel-actions-DicWTbYH.js} +2 -2
  12. package/dist/{channel-actions.runtime-CC4QwYVO.js → channel-actions.runtime-D9Qadn-l.js} +8 -8
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel.setup-BS4gd8sc.js → channel.setup-CJXfFclP.js} +3 -3
  16. package/dist/{chunk-CHpY_lsO.js → chunk-CWzmeWf_.js} +9 -49
  17. package/dist/{config-schema-CoDck1GW.js → config-schema-BxrgfSMD.js} +4 -0
  18. package/dist/{directory-config-G1QwBIdi.js → directory-config-CmukHSms.js} +1 -1
  19. package/dist/directory-contract-api.js +1 -1
  20. package/dist/{directory-live-nfvekMil.js → directory-live-BYlCRO__.js} +3 -3
  21. package/dist/{doctor-Bq4pJAQk.js → doctor-Ci7USQAW.js} +1 -1
  22. package/dist/{handle-action.guild-admin-LYY75_Oa.js → handle-action.guild-admin-Bsk2YBPs.js} +2 -2
  23. package/dist/index.js +2 -2
  24. package/dist/{manager.runtime-w6VVYQMl.js → manager.runtime-Cc8vS-M7.js} +98 -145
  25. package/dist/message-handler-CWJ-mQvH.js +590 -0
  26. package/dist/{message-handler.preflight-DnAH04Ss.js → message-handler.preflight-DK46_qbS.js} +16 -15
  27. package/dist/{message-handler.process-ClgKz8I5.js → message-handler.process-BK0mjFJH.js} +37 -61
  28. package/dist/{message-utils-BU3gc01v.js → message-utils-Cfe6CZ6N.js} +1 -1
  29. package/dist/{outbound-adapter-ByoXsR-V.js → outbound-adapter-B2_oa-zV.js} +36 -5
  30. package/dist/{outbound-components-7wAolLuS.js → outbound-components-BGhk6HI3.js} +3 -3
  31. package/dist/{outbound-session-route-D4QqePZN.js → outbound-session-route-M2bEY9tI.js} +36 -6
  32. package/dist/{pluralkit-r4wiI2yy.js → pluralkit-CT0VRsZd.js} +1 -1
  33. package/dist/{probe-WKeTYyJX.js → probe-C3SCKyYU.js} +1 -1
  34. package/dist/{probe.runtime-CRY-qUke.js → probe.runtime-D55QqC9u.js} +1 -1
  35. package/dist/{provider-PRlF_lZm.js → provider-HW0G-2l6.js} +868 -794
  36. package/dist/{provider-session.runtime-BZPUZRjn.js → provider-session.runtime-cCfGMsF_.js} +3 -3
  37. package/dist/provider.runtime-BWCRsVoT.js +2 -0
  38. package/dist/{resolve-allowlist-common-QN7VMZaC.js → resolve-allowlist-common-CJlqw_nC.js} +2 -2
  39. package/dist/{resolve-channels-BO9NUPlS.js → resolve-channels-9KcG3mLg.js} +4 -4
  40. package/dist/{resolve-users-CU3oG5-m.js → resolve-users-GCGf3oV0.js} +3 -3
  41. package/dist/{runtime-CCf5nkV3.js → runtime-CD5MRZkD.js} +1 -2
  42. package/dist/{runtime-BsefAQ9q.js → runtime-Z2qkKakn.js} +9 -8
  43. package/dist/runtime-api.actions.js +2 -2
  44. package/dist/runtime-api.js +23 -22
  45. package/dist/runtime-api.lookup.js +5 -5
  46. package/dist/runtime-api.monitor-C3EDYJqk.js +6 -0
  47. package/dist/runtime-api.monitor.js +8 -8
  48. package/dist/runtime-api.send.js +6 -5
  49. package/dist/runtime-api.threads.js +3 -3
  50. package/dist/{secret-config-contract-BDNrRWvd.js → secret-config-contract-B1akrlyY.js} +1 -0
  51. package/dist/secret-contract-api.js +1 -1
  52. package/dist/security-audit-contract-api.js +1 -1
  53. package/dist/{security-audit.runtime-K0nkzjzJ.js → security-audit.runtime-DEEywczv.js} +1 -1
  54. package/dist/{send-JJzVo_sK.js → send-CSRxNNgq.js} +7 -86
  55. package/dist/{send.components-CCRW54gK.js → send.components-DLqzOEPJ.js} +5 -4
  56. package/dist/{send.outbound-Cvulb-k_.js → send.outbound-C1SfX20w.js} +4 -3
  57. package/dist/send.reactions-Cj4ugX3F.js +85 -0
  58. package/dist/send.receipt-CuwRrpFz.js +48 -0
  59. package/dist/{send.shared-XuOTO1dW.js → send.shared-Dber6X57.js} +2 -2
  60. package/dist/{sender-identity-C62lBe18.js → sender-identity-mG_zsdox.js} +1 -1
  61. package/dist/session-binding-contract-api.js +1 -1
  62. package/dist/setup-plugin-api.js +1 -1
  63. package/dist/{shared-Bngr0I-v.js → shared-BEhmAdMY.js} +5 -5
  64. package/dist/{subagent-hooks-mSxRv4AE.js → subagent-hooks-Wrk61DwY.js} +2 -2
  65. package/dist/subagent-hooks-api.js +10 -1
  66. package/dist/subagent-progress-B2fpHe3m.js +662 -0
  67. package/dist/{system-events-BANBRAe3.js → system-events-AhfcKRRb.js} +2 -2
  68. package/dist/{target-resolver-BaEmTxeK.js → target-resolver-vqLA450R.js} +2 -2
  69. package/dist/targets-BjEMIe8t.js +3 -0
  70. package/dist/{thread-bindings-4MV-u8vU.js → thread-bindings-CJQoXuWY.js} +4 -4
  71. package/dist/{thread-bindings.discord-api-DXfXsHGZ.js → thread-bindings.discord-api-mNCWlgvM.js} +4 -4
  72. package/dist/{thread-bindings.manager-DJ4cO5Eu.js → thread-bindings.manager-D1z8mK4b.js} +3 -3
  73. package/dist/{timeouts-D5lWili0.js → timeouts-B9cy6RvQ.js} +1 -6
  74. package/dist/{transcripts-source-TDZcMriu.js → transcripts-source-DjDbt14x.js} +1 -1
  75. package/dist/transcripts-source-api.js +1 -1
  76. package/dist/{typing-CKd2NMWG.js → typing-wxtm1NlF.js} +2 -2
  77. package/npm-shrinkwrap.json +3 -3
  78. package/openclaw.plugin.json +10 -0
  79. package/package.json +4 -4
  80. package/dist/approval-message-safety-C-8BL6Kv.js +0 -47
  81. package/dist/message-handler-DExaghWK.js +0 -331
  82. package/dist/provider.runtime-BX8GcCYx.js +0 -2
  83. package/dist/runtime-api.monitor-DCS_ETft.js +0 -6
  84. package/dist/targets-fjJt3PYG.js +0 -3
  85. package/dist/{security-audit-aLbWmUjl.js → security-audit-DtT0SIns.js} +1 -1
@@ -0,0 +1,590 @@
1
+ import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
2
+ import { d as resolveDiscordChannelNameSafe, l as resolveDiscordChannelIdSafe, p as resolveDiscordChannelParentSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-mNCWlgvM.js";
3
+ import { C as mapGatewayDispatchData } from "./provider-HW0G-2l6.js";
4
+ import { c as hasDiscordMessageStickers, d as resolveDiscordMessageChannelId, r as resolveDiscordMessageText } from "./message-utils-Cfe6CZ6N.js";
5
+ import { GatewayDispatchEvents } from "discord-api-types/v10";
6
+ import { danger } from "openclaw/plugin-sdk/runtime-env";
7
+ import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
8
+ import { DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, createChannelIngressDrain, createChannelRunQueue } from "openclaw/plugin-sdk/channel-outbound";
9
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
10
+ import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
11
+ import { createChannelInboundDebouncer, shouldDebounceTextInbound } from "openclaw/plugin-sdk/channel-inbound";
12
+ //#region extensions/discord/src/monitor/ingress.ts
13
+ const DISCORD_INGRESS_PAYLOAD_VERSION = 1;
14
+ const DISCORD_INGRESS_DRAIN_INTERVAL_MS = 1e3;
15
+ const DISCORD_INGRESS_COMPLETED_TTL_MS = 720 * 60 * 6e4;
16
+ const DISCORD_INGRESS_COMPLETED_MAX_ENTRIES = 5e3;
17
+ const DISCORD_INGRESS_FAILED_TTL_MS = 720 * 60 * 6e4;
18
+ const DISCORD_INGRESS_FAILED_MAX_ENTRIES = 5e3;
19
+ var DiscordIngressPayloadError = class extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "DiscordIngressPayloadError";
23
+ }
24
+ };
25
+ function nonEmptyString(value) {
26
+ return typeof value === "string" && value.trim() ? value.trim() : null;
27
+ }
28
+ function inspectDiscordMessage(rawMessage) {
29
+ if (!rawMessage || typeof rawMessage !== "object" || Array.isArray(rawMessage)) throw new DiscordIngressPayloadError("Discord MESSAGE_CREATE payload must be an object");
30
+ const candidate = rawMessage;
31
+ const eventId = nonEmptyString(candidate.id);
32
+ if (!eventId) throw new DiscordIngressPayloadError("Discord MESSAGE_CREATE payload is missing its snowflake");
33
+ const channelId = nonEmptyString(candidate.channel_id);
34
+ if (!channelId) throw new DiscordIngressPayloadError("Discord MESSAGE_CREATE payload is missing channel_id");
35
+ return {
36
+ eventId,
37
+ laneKey: `channel:${channelId}`
38
+ };
39
+ }
40
+ function parseClaimedMessage(payload, claimedId) {
41
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new DiscordIngressPayloadError("Discord ingress payload must be an object");
42
+ const candidate = payload;
43
+ if (candidate.version !== DISCORD_INGRESS_PAYLOAD_VERSION) throw new DiscordIngressPayloadError("Discord ingress payload version is unsupported");
44
+ if (inspectDiscordMessage(candidate.rawMessage).eventId !== claimedId) throw new DiscordIngressPayloadError("Discord message id changed after durable admission");
45
+ return candidate.rawMessage;
46
+ }
47
+ function errorText(error) {
48
+ return error instanceof Error ? error.message : String(error);
49
+ }
50
+ function isDiscordAuthenticationFailure(error) {
51
+ let current = error;
52
+ const seen = /* @__PURE__ */ new Set();
53
+ while (current && typeof current === "object" && !seen.has(current)) {
54
+ seen.add(current);
55
+ const candidate = current;
56
+ if (candidate.status === 401 || candidate.statusCode === 401) return true;
57
+ current = candidate.cause;
58
+ }
59
+ return false;
60
+ }
61
+ function createDiscordIngressMonitor(params) {
62
+ const queue = params.queue ?? getDiscordRuntime().state.openChannelIngressQueue({ accountId: params.accountId });
63
+ const shutdown = new AbortController();
64
+ const drain = createChannelIngressDrain({
65
+ queue,
66
+ abortSignal: shutdown.signal,
67
+ retryPolicy: {
68
+ maxAttempts: DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS,
69
+ deadLetterMinAgeMs: DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS
70
+ },
71
+ resolveNonRetryableFailure: (error) => {
72
+ if (error instanceof DiscordIngressPayloadError) return {
73
+ reason: "invalid-event",
74
+ message: error.message
75
+ };
76
+ if (isDiscordAuthenticationFailure(error)) return {
77
+ reason: "authentication-failed",
78
+ message: errorText(error)
79
+ };
80
+ return null;
81
+ },
82
+ onLog: (message) => params.runtime.error?.(danger(`discord ingress: ${message}`)),
83
+ dispatchClaimedEvent: async (claimed, lifecycle) => {
84
+ const rawMessage = parseClaimedMessage(claimed.payload, claimed.id);
85
+ const event = mapGatewayDispatchData(params.client, GatewayDispatchEvents.MessageCreate, rawMessage);
86
+ return await params.dispatch(event, lifecycle);
87
+ }
88
+ });
89
+ let running = false;
90
+ let drainRequested = false;
91
+ let drainTask;
92
+ let drainTimer;
93
+ const requestDrain = () => {
94
+ if (!running || shutdown.signal.aborted) return;
95
+ drainRequested = true;
96
+ if (drainTask) return;
97
+ drainTask = (async () => {
98
+ while (drainRequested && !shutdown.signal.aborted) {
99
+ if (!running) break;
100
+ drainRequested = false;
101
+ await drain.drainOnce();
102
+ }
103
+ })().catch((error) => {
104
+ params.runtime.error?.(danger(`discord ingress drain failed: ${errorText(error)}`));
105
+ }).finally(() => {
106
+ drainTask = void 0;
107
+ if (running && drainRequested && !shutdown.signal.aborted) requestDrain();
108
+ });
109
+ };
110
+ return {
111
+ accept: async (rawMessage) => {
112
+ const facts = inspectDiscordMessage(rawMessage);
113
+ await queue.prune({
114
+ completedTtlMs: DISCORD_INGRESS_COMPLETED_TTL_MS,
115
+ completedMaxEntries: DISCORD_INGRESS_COMPLETED_MAX_ENTRIES,
116
+ failedTtlMs: DISCORD_INGRESS_FAILED_TTL_MS,
117
+ failedMaxEntries: DISCORD_INGRESS_FAILED_MAX_ENTRIES
118
+ });
119
+ const receivedAt = Date.now();
120
+ await queue.enqueue(facts.eventId, {
121
+ version: DISCORD_INGRESS_PAYLOAD_VERSION,
122
+ receivedAt,
123
+ rawMessage
124
+ }, {
125
+ receivedAt,
126
+ laneKey: facts.laneKey
127
+ });
128
+ requestDrain();
129
+ },
130
+ start: () => {
131
+ if (running) return;
132
+ running = true;
133
+ requestDrain();
134
+ drainTimer = setInterval(requestDrain, DISCORD_INGRESS_DRAIN_INTERVAL_MS);
135
+ drainTimer.unref?.();
136
+ },
137
+ stop: async () => {
138
+ if (!running && shutdown.signal.aborted) return;
139
+ running = false;
140
+ if (drainTimer) {
141
+ clearInterval(drainTimer);
142
+ drainTimer = void 0;
143
+ }
144
+ shutdown.abort();
145
+ await drainTask;
146
+ await drain.waitForIdle();
147
+ drain.dispose();
148
+ }
149
+ };
150
+ }
151
+ //#endregion
152
+ //#region extensions/discord/src/monitor/inbound-job.ts
153
+ function resolveDiscordInboundJobQueueKey(ctx) {
154
+ const sessionKey = ctx.route.sessionKey?.trim();
155
+ if (sessionKey) return sessionKey;
156
+ const baseSessionKey = ctx.baseSessionKey?.trim();
157
+ if (baseSessionKey) return baseSessionKey;
158
+ return ctx.messageChannelId;
159
+ }
160
+ function buildDiscordInboundJob(ctx, options) {
161
+ const { runtime, abortSignal, guildHistories, client, turnAdoptionLifecycle, threadBindings, discordRestFetch, message, data, threadChannel, ...payload } = ctx;
162
+ const sanitizedMessage = sanitizeDiscordInboundMessage(message);
163
+ return {
164
+ queueKey: resolveDiscordInboundJobQueueKey(ctx),
165
+ payload: {
166
+ ...payload,
167
+ message: sanitizedMessage,
168
+ data: {
169
+ ...data,
170
+ message: sanitizedMessage
171
+ },
172
+ threadChannel: normalizeDiscordThreadChannel(threadChannel)
173
+ },
174
+ runtime: {
175
+ runtime,
176
+ abortSignal,
177
+ guildHistories,
178
+ client,
179
+ turnAdoptionLifecycle,
180
+ threadBindings,
181
+ discordRestFetch
182
+ },
183
+ ingressSettlement: options?.ingressSettlement
184
+ };
185
+ }
186
+ function materializeDiscordInboundJob(job, abortSignal) {
187
+ return {
188
+ ...job.payload,
189
+ ...job.runtime,
190
+ abortSignal: abortSignal ?? job.runtime.abortSignal
191
+ };
192
+ }
193
+ function sanitizeDiscordInboundMessage(message) {
194
+ const descriptors = Object.getOwnPropertyDescriptors(message);
195
+ delete descriptors.channel;
196
+ return Object.create(Object.getPrototypeOf(message), descriptors);
197
+ }
198
+ function normalizeDiscordThreadChannel(threadChannel) {
199
+ if (!threadChannel) return null;
200
+ const channelInfo = resolveDiscordChannelInfoSafe(threadChannel);
201
+ const parent = resolveDiscordChannelParentSafe(threadChannel);
202
+ return {
203
+ id: threadChannel.id,
204
+ name: channelInfo.name,
205
+ parentId: channelInfo.parentId,
206
+ parent: parent ? {
207
+ id: resolveDiscordChannelIdSafe(parent),
208
+ name: resolveDiscordChannelNameSafe(parent)
209
+ } : void 0,
210
+ ownerId: channelInfo.ownerId
211
+ };
212
+ }
213
+ //#endregion
214
+ //#region extensions/discord/src/monitor/message-handler.batch-gate.ts
215
+ function applyImplicitReplyBatchGate(ctx, replyToMode, isBatched) {
216
+ const replyThreading = resolveBatchedReplyThreadingPolicy(replyToMode, isBatched);
217
+ if (!replyThreading) return;
218
+ ctx.ReplyThreading = replyThreading;
219
+ }
220
+ //#endregion
221
+ //#region extensions/discord/src/monitor/message-run-queue.ts
222
+ const loadMessageProcessRuntime = createLazyRuntimeModule(() => import("./message-handler.process-BK0mjFJH.js"));
223
+ async function processDiscordQueuedMessage(params) {
224
+ const abortSignal = params.job.runtime.abortSignal && params.lifecycleSignal ? AbortSignal.any([params.job.runtime.abortSignal, params.lifecycleSignal]) : params.job.runtime.abortSignal ?? params.lifecycleSignal;
225
+ try {
226
+ await (params.testing?.processDiscordMessage ?? (await loadMessageProcessRuntime()).processDiscordMessage)(materializeDiscordInboundJob(params.job, abortSignal));
227
+ if (abortSignal?.aborted) await params.job.ingressSettlement?.abandon(abortSignal.reason);
228
+ else await params.job.ingressSettlement?.settle();
229
+ } catch (error) {
230
+ await params.job.ingressSettlement?.abandon(error);
231
+ throw error;
232
+ }
233
+ }
234
+ async function cleanupSkippedDiscordQueuedMessage(params) {
235
+ await params.job.ingressSettlement?.abandon(/* @__PURE__ */ new Error("discord queued run skipped before processing"));
236
+ }
237
+ function createDiscordMessageRunQueue(params) {
238
+ const skippedCleanup = /* @__PURE__ */ new Set();
239
+ const runQueue = createChannelRunQueue({
240
+ setStatus: params.setStatus,
241
+ abortSignal: params.abortSignal,
242
+ onError: (error) => {
243
+ params.runtime.error(danger(`discord message run failed: ${String(error)}`));
244
+ }
245
+ });
246
+ let lifecycleActive = !params.abortSignal?.aborted;
247
+ const pendingTasks = /* @__PURE__ */ new Set();
248
+ const onAbort = () => void cleanupSkippedQueuedMessages();
249
+ async function cleanupSkippedQueuedMessages() {
250
+ params.abortSignal?.removeEventListener("abort", onAbort);
251
+ if (!lifecycleActive && skippedCleanup.size === 0) return;
252
+ lifecycleActive = false;
253
+ const cleanups = [...skippedCleanup];
254
+ skippedCleanup.clear();
255
+ for (const cleanup of cleanups) await cleanup();
256
+ }
257
+ if (params.abortSignal?.aborted) cleanupSkippedQueuedMessages();
258
+ else params.abortSignal?.addEventListener("abort", onAbort, { once: true });
259
+ return {
260
+ enqueue(job) {
261
+ let resolvePending;
262
+ const pending = new Promise((resolve) => {
263
+ resolvePending = resolve;
264
+ });
265
+ pendingTasks.add(pending);
266
+ const settlePending = () => {
267
+ pendingTasks.delete(pending);
268
+ resolvePending();
269
+ };
270
+ const cleanupSkipped = async () => {
271
+ try {
272
+ await cleanupSkippedDiscordQueuedMessage({ job });
273
+ } finally {
274
+ settlePending();
275
+ }
276
+ };
277
+ if (!lifecycleActive) {
278
+ cleanupSkipped();
279
+ return;
280
+ }
281
+ skippedCleanup.add(cleanupSkipped);
282
+ runQueue.enqueue(job.queueKey, async ({ lifecycleSignal }) => {
283
+ skippedCleanup.delete(cleanupSkipped);
284
+ try {
285
+ await processDiscordQueuedMessage({
286
+ job,
287
+ lifecycleSignal,
288
+ testing: params.testing
289
+ });
290
+ } finally {
291
+ settlePending();
292
+ }
293
+ });
294
+ },
295
+ async deactivate() {
296
+ runQueue.deactivate();
297
+ await cleanupSkippedQueuedMessages();
298
+ await Promise.allSettled(pendingTasks);
299
+ }
300
+ };
301
+ }
302
+ //#endregion
303
+ //#region extensions/discord/src/monitor/message-dispatcher.ts
304
+ const loadMessagePreflightRuntime = createLazyRuntimeModule(() => import("./message-handler.preflight-DK46_qbS.js"));
305
+ function isNonEmptyString(value) {
306
+ return typeof value === "string" && value.length > 0;
307
+ }
308
+ function buildFlushIngressLifecycle(entries) {
309
+ const lifecycles = entries.map((entry) => entry.turnAdoptionLifecycle).filter((lifecycle) => lifecycle !== void 0);
310
+ const [firstLifecycle] = lifecycles;
311
+ if (!firstLifecycle) return {
312
+ lifecycle: void 0,
313
+ settle: async () => {},
314
+ abandon: async () => {}
315
+ };
316
+ let handedOff = false;
317
+ const adoptAll = async () => {
318
+ for (const lifecycle of lifecycles) await lifecycle.onAdopted();
319
+ };
320
+ return {
321
+ lifecycle: {
322
+ abortSignal: lifecycles.length === 1 ? firstLifecycle.abortSignal : AbortSignal.any(lifecycles.map((lifecycle) => lifecycle.abortSignal)),
323
+ onAdopted: async () => {
324
+ handedOff = true;
325
+ await adoptAll();
326
+ },
327
+ onDeferred: () => {
328
+ handedOff = true;
329
+ for (const lifecycle of lifecycles) lifecycle.onDeferred();
330
+ },
331
+ onAdoptionFinalizing: () => {
332
+ for (const lifecycle of lifecycles) lifecycle.onAdoptionFinalizing();
333
+ },
334
+ onAbandoned: async () => {
335
+ handedOff = true;
336
+ for (const lifecycle of lifecycles) await lifecycle.onAbandoned();
337
+ }
338
+ },
339
+ settle: async () => {
340
+ if (!handedOff) await adoptAll();
341
+ },
342
+ abandon: async () => {
343
+ if (handedOff) return;
344
+ handedOff = true;
345
+ for (const lifecycle of lifecycles) await lifecycle.onAbandoned();
346
+ }
347
+ };
348
+ }
349
+ function createDiscordMessageDispatcher(params) {
350
+ const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
351
+ providerConfigPresent: params.cfg.channels?.discord !== void 0,
352
+ groupPolicy: params.discordConfig?.groupPolicy,
353
+ defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy
354
+ });
355
+ const ackReactionScope = params.discordConfig?.ackReactionScope ?? params.cfg.messages?.ackReactionScope ?? "group-mentions";
356
+ const preflightDiscordMessageImpl = params.testing?.preflightDiscordMessage;
357
+ const messageRunQueue = createDiscordMessageRunQueue({
358
+ runtime: params.runtime,
359
+ setStatus: params.setStatus,
360
+ abortSignal: params.abortSignal,
361
+ testing: params.testing
362
+ });
363
+ const dispatcherShutdown = new AbortController();
364
+ const pendingDebounceEntries = /* @__PURE__ */ new Set();
365
+ const pendingCancellationSettlements = /* @__PURE__ */ new Set();
366
+ const activeDebounceFlushes = /* @__PURE__ */ new Set();
367
+ const resolveDebounceKey = (entry) => {
368
+ const message = entry.data.message;
369
+ const authorId = entry.data.author?.id;
370
+ if (!message || !authorId) return null;
371
+ const channelId = resolveDiscordMessageChannelId({
372
+ message,
373
+ eventChannelId: entry.data.channel_id
374
+ });
375
+ return channelId ? `discord:${params.accountId}:${channelId}:${authorId}` : null;
376
+ };
377
+ const { debouncer } = createChannelInboundDebouncer({
378
+ cfg: params.cfg,
379
+ channel: "discord",
380
+ buildKey: resolveDebounceKey,
381
+ shouldDebounce: (entry) => {
382
+ const message = entry.data.message;
383
+ if (!message) return false;
384
+ return shouldDebounceTextInbound({
385
+ text: resolveDiscordMessageText(message, { includeForwarded: false }),
386
+ cfg: params.cfg,
387
+ hasMedia: message.attachments && message.attachments.length > 0 || hasDiscordMessageStickers(message)
388
+ });
389
+ },
390
+ onFlush: async (entries) => {
391
+ let resolveTrackedFlush;
392
+ const trackedFlush = new Promise((resolve) => {
393
+ resolveTrackedFlush = resolve;
394
+ });
395
+ activeDebounceFlushes.add(trackedFlush);
396
+ try {
397
+ for (const entry of entries) pendingDebounceEntries.delete(entry);
398
+ const last = entries.at(-1);
399
+ if (!last) return;
400
+ const ingress = buildFlushIngressLifecycle(entries);
401
+ const abortSignal = last.abortSignal;
402
+ if (abortSignal?.aborted) {
403
+ await ingress.abandon(abortSignal.reason);
404
+ return;
405
+ }
406
+ try {
407
+ if (entries.length === 1) {
408
+ const ctx = await (preflightDiscordMessageImpl ?? (await loadMessagePreflightRuntime()).preflightDiscordMessage)({
409
+ ...params,
410
+ ackReactionScope,
411
+ groupPolicy,
412
+ abortSignal,
413
+ data: last.data,
414
+ client: last.client,
415
+ turnAdoptionLifecycle: ingress.lifecycle
416
+ });
417
+ if (abortSignal?.aborted) {
418
+ await ingress.abandon(abortSignal.reason);
419
+ return;
420
+ }
421
+ if (!ctx) {
422
+ await ingress.settle();
423
+ return;
424
+ }
425
+ applyImplicitReplyBatchGate(ctx, params.replyToMode, false);
426
+ messageRunQueue.enqueue(buildDiscordInboundJob(ctx, { ingressSettlement: ingress }));
427
+ return;
428
+ }
429
+ const combinedBaseText = entries.map((entry) => resolveDiscordMessageText(entry.data.message, { includeForwarded: false })).filter(Boolean).join("\n");
430
+ const syntheticMessage = Object.create(Object.getPrototypeOf(last.data.message), {
431
+ ...Object.getOwnPropertyDescriptors(last.data.message),
432
+ content: {
433
+ value: combinedBaseText,
434
+ enumerable: true,
435
+ configurable: true
436
+ },
437
+ attachments: {
438
+ value: [],
439
+ enumerable: true,
440
+ configurable: true
441
+ },
442
+ message_snapshots: {
443
+ value: last.data.message.message_snapshots,
444
+ enumerable: true,
445
+ configurable: true
446
+ },
447
+ messageSnapshots: {
448
+ value: last.data.message.messageSnapshots,
449
+ enumerable: true,
450
+ configurable: true
451
+ },
452
+ rawData: {
453
+ value: { ...last.data.message.rawData },
454
+ enumerable: true,
455
+ configurable: true
456
+ }
457
+ });
458
+ const syntheticData = {
459
+ ...last.data,
460
+ message: syntheticMessage
461
+ };
462
+ const ctx = await (preflightDiscordMessageImpl ?? (await loadMessagePreflightRuntime()).preflightDiscordMessage)({
463
+ ...params,
464
+ ackReactionScope,
465
+ groupPolicy,
466
+ abortSignal,
467
+ data: syntheticData,
468
+ client: last.client,
469
+ turnAdoptionLifecycle: ingress.lifecycle
470
+ });
471
+ if (abortSignal?.aborted) {
472
+ await ingress.abandon(abortSignal.reason);
473
+ return;
474
+ }
475
+ if (!ctx) {
476
+ await ingress.settle();
477
+ return;
478
+ }
479
+ applyImplicitReplyBatchGate(ctx, params.replyToMode, true);
480
+ const ids = entries.map((entry) => entry.data.message?.id).filter(isNonEmptyString);
481
+ if (ids.length > 0) {
482
+ const ctxBatch = ctx;
483
+ ctxBatch.MessageSids = ids;
484
+ ctxBatch.MessageSidFirst = ids[0];
485
+ ctxBatch.MessageSidLast = ids[ids.length - 1];
486
+ }
487
+ messageRunQueue.enqueue(buildDiscordInboundJob(ctx, { ingressSettlement: ingress }));
488
+ } catch (error) {
489
+ await ingress.abandon(error);
490
+ throw error;
491
+ }
492
+ } finally {
493
+ activeDebounceFlushes.delete(trackedFlush);
494
+ resolveTrackedFlush();
495
+ }
496
+ },
497
+ onError: (err) => {
498
+ params.runtime.error(danger(`discord debounce flush failed: ${String(err)}`));
499
+ },
500
+ onCancel: (entries) => {
501
+ for (const entry of entries) {
502
+ pendingDebounceEntries.delete(entry);
503
+ const settlement = Promise.resolve(entry.turnAdoptionLifecycle?.onAbandoned()).catch((error) => {
504
+ params.runtime.error(danger(`discord ingress cancellation settlement failed: ${String(error)}`));
505
+ }).finally(() => {
506
+ pendingCancellationSettlements.delete(settlement);
507
+ });
508
+ pendingCancellationSettlements.add(settlement);
509
+ }
510
+ }
511
+ });
512
+ const dispatchMessage = async (data, client, options) => {
513
+ try {
514
+ if (dispatcherShutdown.signal.aborted || options?.abortSignal?.aborted) return {
515
+ kind: "failed-retryable",
516
+ error: dispatcherShutdown.signal.aborted ? dispatcherShutdown.signal.reason ?? /* @__PURE__ */ new Error("discord dispatcher shut down") : options?.abortSignal?.reason ?? /* @__PURE__ */ new Error("discord dispatch aborted")
517
+ };
518
+ const msgAuthorId = data.message?.author?.id ?? data.author?.id;
519
+ if (params.botUserId && msgAuthorId === params.botUserId) return { kind: "completed" };
520
+ const entry = {
521
+ data,
522
+ client,
523
+ abortSignal: options?.abortSignal ? AbortSignal.any([options.abortSignal, dispatcherShutdown.signal]) : dispatcherShutdown.signal,
524
+ turnAdoptionLifecycle: options?.turnAdoptionLifecycle
525
+ };
526
+ const debounceKey = resolveDebounceKey(entry);
527
+ if (debounceKey) {
528
+ entry.debounceKey = debounceKey;
529
+ pendingDebounceEntries.add(entry);
530
+ }
531
+ await debouncer.enqueue(entry);
532
+ if (options?.turnAdoptionLifecycle) return { kind: "deferred" };
533
+ return { kind: "completed" };
534
+ } catch (err) {
535
+ params.runtime.error(danger(`handler failed: ${String(err)}`));
536
+ if (options?.turnAdoptionLifecycle) throw err;
537
+ return { kind: "completed" };
538
+ }
539
+ };
540
+ const handler = (data, client, options) => {
541
+ const result = dispatchMessage(data, client, options);
542
+ return options?.turnAdoptionLifecycle ? result : result.then(() => void 0);
543
+ };
544
+ handler.deactivate = async () => {
545
+ dispatcherShutdown.abort(/* @__PURE__ */ new Error("discord-message-handler-deactivated"));
546
+ const pendingKeys = new Set([...pendingDebounceEntries].map((entry) => entry.debounceKey).filter((key) => key !== void 0));
547
+ for (const key of pendingKeys) debouncer.cancelKey(key);
548
+ pendingDebounceEntries.clear();
549
+ await Promise.allSettled(pendingCancellationSettlements);
550
+ await Promise.allSettled(activeDebounceFlushes);
551
+ await messageRunQueue.deactivate();
552
+ };
553
+ return handler;
554
+ }
555
+ //#endregion
556
+ //#region extensions/discord/src/monitor/message-handler.ts
557
+ function createDiscordMessageHandler(params) {
558
+ const dispatcher = createDiscordMessageDispatcher(params);
559
+ const ingress = (params.testing?.createIngressMonitor ?? createDiscordIngressMonitor)({
560
+ accountId: params.accountId,
561
+ client: params.client,
562
+ runtime: params.runtime,
563
+ dispatch: (event, lifecycle) => dispatcher(event, params.client, {
564
+ abortSignal: lifecycle.abortSignal,
565
+ turnAdoptionLifecycle: lifecycle
566
+ })
567
+ });
568
+ ingress.start();
569
+ const activeAdmissions = /* @__PURE__ */ new Set();
570
+ let accepting = true;
571
+ const handler = async (rawMessage) => {
572
+ if (!accepting) return;
573
+ const admission = ingress.accept(rawMessage);
574
+ activeAdmissions.add(admission);
575
+ try {
576
+ await admission;
577
+ } finally {
578
+ activeAdmissions.delete(admission);
579
+ }
580
+ };
581
+ handler.deactivate = async () => {
582
+ accepting = false;
583
+ await Promise.allSettled(activeAdmissions);
584
+ await dispatcher.deactivate();
585
+ await ingress.stop();
586
+ };
587
+ return handler;
588
+ }
589
+ //#endregion
590
+ export { createDiscordMessageHandler as t };
@@ -1,22 +1,22 @@
1
- import { L as Message, St as getChannelMessage, p as discord_exports } from "./chunk-CHpY_lsO.js";
1
+ import { F as Message, bt as getChannelMessage, d as discord_exports } from "./chunk-CWzmeWf_.js";
2
2
  import { o as resolveDefaultDiscordAccountId } from "./accounts-CMd4nzLB.js";
3
- import { C as resolveTimestampMs, S as resolveDiscordSystemLocation, _ as resolveDiscordShouldRequireMention, a as normalizeDiscordSlug, c as resolveDiscordChannelConfigWithFallback, f as resolveDiscordGuildEntry, i as normalizeDiscordDisplaySlug, n as isDiscordGroupAllowedByPolicy, p as resolveDiscordMemberAccessState, v as resolveGroupDmAllow, x as formatDiscordUserTag } from "./allow-list-B7qOH5xu.js";
4
- import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-D5lWili0.js";
5
- import { t as resolveDiscordConversationIdentity } from "./conversation-identity-BPcbhCAH.js";
3
+ import { C as resolveTimestampMs, S as resolveDiscordSystemLocation, _ as resolveDiscordShouldRequireMention, a as normalizeDiscordSlug, c as resolveDiscordChannelConfigWithFallback, f as resolveDiscordGuildEntry, i as normalizeDiscordDisplaySlug, n as isDiscordGroupAllowedByPolicy, p as resolveDiscordMemberAccessState, v as resolveGroupDmAllow, x as formatDiscordUserTag } from "./allow-list-DzKrtfah.js";
4
+ import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-B9cy6RvQ.js";
5
+ import { r as resolveDiscordConversationIdentity } from "./approval-message-safety-DrLlgIPz.js";
6
6
  import { d as isRecentlyUnboundThreadWebhookMessage } from "./thread-bindings.state-C5afR9TD.js";
7
- import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-DXfXsHGZ.js";
8
- import "./thread-bindings-4MV-u8vU.js";
9
- import { A as resolveDiscordTextCommandAccess, S as handleDiscordDmCommandDecision, b as resolveDiscordEffectiveRoute, k as resolveDiscordDmCommandAccess, v as buildDiscordRoutePeer, x as shouldIgnoreStaleDiscordRouteBinding, y as resolveDiscordConversationRoute } from "./provider-PRlF_lZm.js";
10
- import { a as resolveForwardedMediaList, d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-BU3gc01v.js";
11
- import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-C62lBe18.js";
7
+ import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-mNCWlgvM.js";
8
+ import "./thread-bindings-CJQoXuWY.js";
9
+ import { A as resolveDiscordDmCommandAccess, S as handleDiscordDmCommandDecision, b as resolveDiscordEffectiveRoute, j as resolveDiscordTextCommandAccess, v as buildDiscordRoutePeer, x as shouldIgnoreStaleDiscordRouteBinding, y as resolveDiscordConversationRoute } from "./provider-HW0G-2l6.js";
10
+ import { a as resolveForwardedMediaList, d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-Cfe6CZ6N.js";
11
+ import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-mG_zsdox.js";
12
12
  import { normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
13
13
  import { logDebug } from "openclaw/plugin-sdk/logging-core";
14
14
  import { MessageReferenceType, MessageType } from "discord-api-types/v10";
15
- import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
16
15
  import { getChildLogger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
17
16
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
18
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
19
17
  import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
18
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
19
+ import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
20
20
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
21
21
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
22
22
  import { buildMentionRegexes, classifyChannelInboundEvent, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, recordChannelBotPairLoopAndCheckSuppression, recordDroppedChannelInboundHistory, resolveInboundMentionDecision, resolveUnmentionedGroupInboundPolicy, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
@@ -25,7 +25,7 @@ import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runti
25
25
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
26
26
  //#region extensions/discord/src/monitor/message-handler.dm-preflight.ts
27
27
  const loadConversationRuntime$1 = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/conversation-binding-runtime"));
28
- const loadDiscordSendRuntime = createLazyRuntimeModule(() => import("./send-JJzVo_sK.js").then((n) => n.t));
28
+ const loadDiscordSendRuntime = createLazyRuntimeModule(() => import("./send-CSRxNNgq.js").then((n) => n.t));
29
29
  function resolveDiscordDmPairingSenderId(sender) {
30
30
  return sender.isPluralKit ? `pk:${sender.id}` : sender.id;
31
31
  }
@@ -354,6 +354,7 @@ function buildDiscordMessagePreflightContext({ preflightParams, ...fields }) {
354
354
  replyToMode: preflightParams.replyToMode,
355
355
  ackReactionScope: preflightParams.ackReactionScope,
356
356
  groupPolicy: preflightParams.groupPolicy,
357
+ turnAdoptionLifecycle: preflightParams.turnAdoptionLifecycle,
357
358
  ...fields,
358
359
  threadBindings: preflightParams.threadBindings,
359
360
  discordRestFetch: preflightParams.discordRestFetch
@@ -447,10 +448,10 @@ function logDiscordPreflightInboundSummary(params) {
447
448
  }
448
449
  //#endregion
449
450
  //#region extensions/discord/src/monitor/message-handler.preflight-runtime.ts
450
- const loadPluralKitRuntime = createLazyRuntimeModule(() => import("./pluralkit-r4wiI2yy.js").then((n) => n.n));
451
+ const loadPluralKitRuntime = createLazyRuntimeModule(() => import("./pluralkit-CT0VRsZd.js").then((n) => n.n));
451
452
  const loadPreflightAudioRuntime = createLazyRuntimeModule(() => import("./preflight-audio-DDSZna4F.js"));
452
- const loadSystemEventsRuntime = createLazyRuntimeModule(() => import("./system-events-BANBRAe3.js"));
453
- const loadDiscordThreadingRuntime = createLazyRuntimeModule(() => import("./provider-PRlF_lZm.js").then((n) => n.w));
453
+ const loadSystemEventsRuntime = createLazyRuntimeModule(() => import("./system-events-AhfcKRRb.js"));
454
+ const loadDiscordThreadingRuntime = createLazyRuntimeModule(() => import("./provider-HW0G-2l6.js").then((n) => n.T));
454
455
  function isPreflightAborted(abortSignal) {
455
456
  return Boolean(abortSignal?.aborted);
456
457
  }