@openclaw/discord 2026.5.19 → 2026.5.20-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/{account-inspect-DCXwNu_u.js → account-inspect-CcJ-bl80.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/api.js +19 -19
  5. package/dist/{approval-handler.runtime-CRpNixJy.js → approval-handler.runtime-hi6B0JCi.js} +4 -4
  6. package/dist/{audit-DEF10R9j.js → audit-DJIvRRzY.js} +5 -5
  7. package/dist/{channel-CR9HckAC.js → channel-V98WbX-k.js} +22 -22
  8. package/dist/{channel-actions-CpmPx9PW.js → channel-actions-CSepY2Kq.js} +5 -5
  9. package/dist/{channel-actions.runtime-Be5OvB1r.js → channel-actions.runtime-CSjcHZNT.js} +6 -6
  10. package/dist/channel-config-api.js +1 -1
  11. package/dist/channel-plugin-api.js +1 -1
  12. package/dist/{channel.setup-DR3-U0wF.js → channel.setup-C0rcLtB9.js} +4 -4
  13. package/dist/{components-q41bHoCx.js → components-DDPGekgY.js} +1 -1
  14. package/dist/{config-schema-jz2FX2x5.js → config-schema-BGgg8U6A.js} +13 -1
  15. package/dist/contract-api.js +7 -7
  16. package/dist/{conversation-identity-BY7sjUQh.js → conversation-identity-CAyt5wzy.js} +3 -3
  17. package/dist/{directory-config-x3YzNdoX.js → directory-config-BcWtq4k8.js} +2 -2
  18. package/dist/directory-contract-api.js +1 -1
  19. package/dist/{directory-live-CJiEWQwK.js → directory-live-5HHCCe5Q.js} +4 -4
  20. package/dist/{doctor-CzcS8Yt1.js → doctor-BLcVWv42.js} +5 -5
  21. package/dist/{doctor-contract-AkgUZQHW.js → doctor-contract-Bia3BmP_.js} +1 -1
  22. package/dist/doctor-contract-api.js +1 -1
  23. package/dist/{handle-action.guild-admin-kAzbjGMw.js → handle-action.guild-admin-DX2D9qkG.js} +1 -1
  24. package/dist/{inbound-context-CzEAvKgx.js → inbound-context-B5EsqsSr.js} +1 -1
  25. package/dist/{manager.runtime-CGcJ0yjq.js → manager.runtime-BbDV8jY7.js} +364 -21
  26. package/dist/{message-handler-1NV0WiHC.js → message-handler-DGEpF6pb.js} +7 -7
  27. package/dist/{message-handler.preflight-DQ-oPY7m.js → message-handler.preflight-DsNzvkrd.js} +15 -15
  28. package/dist/{message-handler.process-PpgZ0qC4.js → message-handler.process-AA6doeek.js} +27 -16
  29. package/dist/{message-utils-BKrlzFtM.js → message-utils-Bw-wDr8G.js} +2 -2
  30. package/dist/{outbound-adapter-CaZzpMnE.js → outbound-adapter-TGbHliO_.js} +8 -8
  31. package/dist/{pluralkit-BnCH6cHK.js → pluralkit-sQLfeyIZ.js} +1 -1
  32. package/dist/{preflight-audio-B0keJeQe.js → preflight-audio-ClWbnQto.js} +1 -1
  33. package/dist/{probe-BLDNbk41.js → probe-CRWV5COl.js} +2 -2
  34. package/dist/{probe.runtime-DKB38YvU.js → probe.runtime-h9mEzmMs.js} +1 -1
  35. package/dist/{provider-BZtjA1y_.js → provider-CfxRE5oz.js} +42 -55
  36. package/dist/{provider-session.runtime-DVfzQxFH.js → provider-session.runtime-DxOvTd-z.js} +3 -3
  37. package/dist/provider.runtime-ChDLWrdw.js +2 -0
  38. package/dist/{resolve-allowlist-common-C8L5MYFl.js → resolve-allowlist-common-Bec0FONA.js} +3 -3
  39. package/dist/{resolve-channels-Czrd7fOv.js → resolve-channels-DcWaMURg.js} +4 -4
  40. package/dist/{resolve-users-D7hQMaaq.js → resolve-users-TQPLKDeV.js} +3 -3
  41. package/dist/{runtime-BJUWt4mA.js → runtime-DP_0_5a0.js} +9 -9
  42. package/dist/runtime-api.actions.js +2 -2
  43. package/dist/runtime-api.js +25 -25
  44. package/dist/runtime-api.lookup.js +6 -6
  45. package/dist/runtime-api.monitor-BL4xboSm.js +5 -0
  46. package/dist/runtime-api.monitor.js +7 -7
  47. package/dist/runtime-api.send.js +5 -5
  48. package/dist/runtime-api.threads.js +5 -5
  49. package/dist/runtime-setter-api.js +1 -1
  50. package/dist/secret-contract-api.js +1 -1
  51. package/dist/{security-audit-Dx3j4a0l.js → security-audit-C_fzlczo.js} +1 -1
  52. package/dist/security-audit-contract-api.js +1 -1
  53. package/dist/{security-audit.runtime-C5nnf0k8.js → security-audit.runtime-Dt4zoGpL.js} +1 -1
  54. package/dist/security-contract-api.js +1 -1
  55. package/dist/{send-BsBy21Te.js → send-CEAtV0qd.js} +5 -6
  56. package/dist/{send.components-BuZBJysI.js → send.components-sY6aQV0K.js} +8 -8
  57. package/dist/{send.outbound-C0OzZAv_.js → send.outbound-1vG9pOh2.js} +4 -4
  58. package/dist/{send.shared-C6JLgsbp.js → send.shared-D4clao4K.js} +3 -3
  59. package/dist/{sender-identity-DC0FdEcU.js → sender-identity-BFp5w0F8.js} +1 -1
  60. package/dist/session-key-api.js +1 -1
  61. package/dist/setup-plugin-api.js +1 -1
  62. package/dist/{shared-CYe5A_Bs.js → shared-mo-r7V5W.js} +10 -10
  63. package/dist/{subagent-hooks-DCIt8Gt7.js → subagent-hooks-D65kqOor.js} +3 -3
  64. package/dist/subagent-hooks-api.js +1 -1
  65. package/dist/{system-events-DEuiLTl9.js → system-events-HgHeiMHg.js} +2 -2
  66. package/dist/{target-resolver-C5tK3vit.js → target-resolver-BpGdnBIZ.js} +3 -3
  67. package/dist/targets-DMdVjHOU.js +3 -0
  68. package/dist/test-api.js +4 -4
  69. package/dist/{thread-bindings-BcXgbZ3-.js → thread-bindings-BQz8z6MR.js} +6 -6
  70. package/dist/{thread-bindings.discord-api-BzelVdsY.js → thread-bindings.discord-api-Ddalj9wo.js} +5 -5
  71. package/dist/{thread-bindings.manager-CouT_qjE.js → thread-bindings.manager-I5f-Oykv.js} +4 -4
  72. package/dist/{thread-bindings.session-updates-CgOqEOPe.js → thread-bindings.session-updates-D5gY2ZTE.js} +1 -1
  73. package/dist/timeouts.js +1 -1
  74. package/dist/{typing-DtdZgo-g.js → typing-DUUjLsPr.js} +2 -2
  75. package/openclaw.plugin.json +72 -2
  76. package/package.json +4 -4
  77. package/dist/provider.runtime-DlegJeN5.js +0 -2
  78. package/dist/runtime-api.monitor-BlxEnLN_.js +0 -5
  79. package/dist/targets-JvlTzyfK.js +0 -3
  80. /package/dist/{accounts-ltxKLzxN.js → accounts-C2TOAmpo.js} +0 -0
  81. /package/dist/{agent-components.runtime-BIemD2Iz.js → agent-components.runtime-CEPrf2SY.js} +0 -0
  82. /package/dist/{allow-list-CBI-M84K.js → allow-list-BnkWtVpA.js} +0 -0
  83. /package/dist/{api-DgQLz1wq.js → api-Kq7vtaSO.js} +0 -0
  84. /package/dist/{audit-core-DRyoXREU.js → audit-core-xwjIczO0.js} +0 -0
  85. /package/dist/{channel-api-JudoSiJv.js → channel-api-CAJ0wMoV.js} +0 -0
  86. /package/dist/{config-api-oLS_52S7.js → config-api-JiPdJeb0.js} +0 -0
  87. /package/dist/{gateway-registry-BKSpa4GB.js → gateway-registry-DPxmW0Db.js} +0 -0
  88. /package/dist/{inbound-event-delivery-D8zHG9Lz.js → inbound-event-delivery-DTGIjZVJ.js} +0 -0
  89. /package/dist/{preflight-audio.runtime-DT1Hmhsq.js → preflight-audio.runtime-CoCXMM8r.js} +0 -0
  90. /package/dist/{preview-streaming-nClS_TQx.js → preview-streaming-CQ7PsV9J.js} +0 -0
  91. /package/dist/{runtime-Tqtvj5GX.js → runtime-DgnVQ7zW.js} +0 -0
  92. /package/dist/{secret-config-contract-57_WV6qt.js → secret-config-contract-BjM-1hr9.js} +0 -0
  93. /package/dist/{security-contract-BWDASKVo.js → security-contract-DSHk7I2w.js} +0 -0
  94. /package/dist/{security-doctor-DepqtNCI.js → security-doctor-uUo8hTD5.js} +0 -0
  95. /package/dist/{send.receipt-Dhym-qOF.js → send.receipt-spSPAC77.js} +0 -0
  96. /package/dist/{session-contract-Dwhw3RTY.js → session-contract-BO5tlIdl.js} +0 -0
  97. /package/dist/{session-key-normalization-DnCXUKGA.js → session-key-normalization-wJgsKPNF.js} +0 -0
  98. /package/dist/{thread-bindings.state-CSphZOiL.js → thread-bindings.state-BsOnj5NX.js} +0 -0
  99. /package/dist/{timeouts-CEwuGaWT.js → timeouts-l_PsHQvX.js} +0 -0
@@ -1,10 +1,10 @@
1
- import { Ht as parseDiscordTarget, _ as VoiceStateUpdateListener, c as discord_exports, h as ResumedListener, m as ReadyListener } from "./send.receipt-Dhym-qOF.js";
2
- import { c as resolveDiscordAccountAllowFrom } from "./accounts-ltxKLzxN.js";
3
- import { a as normalizeDiscordSlug, b as formatDiscordUserTag, m as resolveDiscordOwnerAccess } from "./allow-list-CBI-M84K.js";
4
- import { i as formatMention } from "./send.outbound-C0OzZAv_.js";
5
- import { t as getDiscordRuntime } from "./runtime-Tqtvj5GX.js";
6
- import { o as authorizeDiscordVoiceIngress, u as resolveDiscordVoiceEnabled } from "./provider-BZtjA1y_.js";
7
- import { t as buildDiscordGroupSystemPrompt } from "./inbound-context-CzEAvKgx.js";
1
+ import { Ht as parseDiscordTarget, _ as VoiceStateUpdateListener, c as discord_exports, h as ResumedListener, jt as getGuildVoiceState, m as ReadyListener } from "./send.receipt-spSPAC77.js";
2
+ import { c as resolveDiscordAccountAllowFrom } from "./accounts-C2TOAmpo.js";
3
+ import { a as normalizeDiscordSlug, b as formatDiscordUserTag, m as resolveDiscordOwnerAccess } from "./allow-list-BnkWtVpA.js";
4
+ import { i as formatMention } from "./send.outbound-1vG9pOh2.js";
5
+ import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
6
+ import { o as authorizeDiscordVoiceIngress, u as resolveDiscordVoiceEnabled } from "./provider-CfxRE5oz.js";
7
+ import { t as buildDiscordGroupSystemPrompt } from "./inbound-context-B5EsqsSr.js";
8
8
  import { createRequire } from "node:module";
9
9
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
10
10
  import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
@@ -17,6 +17,7 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
17
17
  import { agentCommandFromIngress, getTtsProvider, resolveAgentDir, resolveTtsConfig, resolveTtsPrefsPath } from "openclaw/plugin-sdk/agent-runtime";
18
18
  import { escapeRegExp } from "openclaw/plugin-sdk/text-utility-runtime";
19
19
  import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ, buildRealtimeVoiceAgentConsultChatMessage, buildRealtimeVoiceAgentConsultPolicyInstructions, buildRealtimeVoiceAgentConsultWorkingResponse, createRealtimeVoiceAgentTalkbackQueue, createRealtimeVoiceBridgeSession, resamplePcm, resolveConfiguredRealtimeVoiceProvider, resolveRealtimeVoiceAgentConsultToolPolicy, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow } from "openclaw/plugin-sdk/realtime-voice";
20
+ import { resolveRealtimeBootstrapContextInstructions } from "openclaw/plugin-sdk/realtime-bootstrap-context";
20
21
  import { PassThrough, Readable } from "node:stream";
21
22
  import { parseTtsDirectives } from "openclaw/plugin-sdk/speech";
22
23
  //#region extensions/discord/src/voice/audio.ts
@@ -148,7 +149,6 @@ function convertRealtimePcm24kMonoToDiscordPcm48kStereo(pcm) {
148
149
  }
149
150
  function estimateDurationSeconds(pcm) {
150
151
  const bytesPerSample = BIT_DEPTH / 8 * CHANNELS;
151
- if (bytesPerSample <= 0) return 0;
152
152
  return pcm.length / (bytesPerSample * SAMPLE_RATE);
153
153
  }
154
154
  async function writeVoiceWavFile(pcm) {
@@ -325,6 +325,22 @@ async function runDiscordVoiceAgentTurn(params) {
325
325
  text
326
326
  };
327
327
  }
328
+ async function resolveDiscordVoiceRealtimeBootstrapContext(params) {
329
+ const files = (params.discordConfig.voice?.realtime)?.bootstrapContextFiles;
330
+ if (files?.length === 0) return;
331
+ try {
332
+ return await resolveRealtimeBootstrapContextInstructions({
333
+ config: params.cfg,
334
+ agentId: params.entry.route.agentId,
335
+ sessionKey: params.entry.route.sessionKey,
336
+ files,
337
+ warn: (message) => logger$3.warn(`discord voice: realtime bootstrap context: ${message}`)
338
+ });
339
+ } catch (error) {
340
+ logger$3.warn(`discord voice: realtime bootstrap context unavailable: ${error instanceof Error ? error.message : String(error)}`);
341
+ return;
342
+ }
343
+ }
328
344
  //#endregion
329
345
  //#region extensions/discord/src/voice/prompt.ts
330
346
  const DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT = [
@@ -599,6 +615,7 @@ var DiscordRealtimeVoiceSession = class {
599
615
  const instructions = buildDiscordRealtimeInstructions({
600
616
  mode: this.params.mode,
601
617
  instructions: this.realtimeConfig?.instructions,
618
+ bootstrapContextInstructions: this.params.bootstrapContextInstructions,
602
619
  toolPolicy,
603
620
  consultPolicy
604
621
  });
@@ -1197,6 +1214,7 @@ function buildDiscordRealtimeInstructions(params) {
1197
1214
  const base = params.instructions ?? ["You are OpenClaw's Discord voice interface.", "Keep spoken replies concise, natural, and suitable for a live Discord voice channel."].join("\n");
1198
1215
  if (isDiscordAgentProxyVoiceMode(params.mode)) return [
1199
1216
  base,
1217
+ params.bootstrapContextInstructions?.trim(),
1200
1218
  "Mode: OpenClaw agent proxy.",
1201
1219
  "You are the realtime voice surface for the same OpenClaw agent the user can message directly.",
1202
1220
  "Do not mention a backend, supervisor, helper, or separate system. Present the result as your own work.",
@@ -1209,10 +1227,14 @@ function buildDiscordRealtimeInstructions(params) {
1209
1227
  consultPolicy: params.consultPolicy
1210
1228
  })
1211
1229
  ].join("\n\n");
1212
- return [base, buildRealtimeVoiceAgentConsultPolicyInstructions({
1213
- toolPolicy: params.toolPolicy,
1214
- consultPolicy: params.consultPolicy
1215
- })].filter(Boolean).join("\n\n");
1230
+ return [
1231
+ base,
1232
+ params.bootstrapContextInstructions?.trim(),
1233
+ buildRealtimeVoiceAgentConsultPolicyInstructions({
1234
+ toolPolicy: params.toolPolicy,
1235
+ consultPolicy: params.consultPolicy
1236
+ })
1237
+ ].filter(Boolean).join("\n\n");
1216
1238
  }
1217
1239
  //#endregion
1218
1240
  //#region extensions/discord/src/voice/receive-recovery.ts
@@ -1582,6 +1604,9 @@ var DiscordVoiceSpeakerContextResolver = class {
1582
1604
  //#region extensions/discord/src/voice/manager.ts
1583
1605
  const logger = createSubsystemLogger("discord/voice");
1584
1606
  const VOICE_LOG_PREVIEW_CHARS = 500;
1607
+ const FOLLOW_USERS_RECONCILE_INTERVAL_MS = 1e4;
1608
+ const FOLLOW_USERS_RECONCILE_MAX_GUILDS_PER_RUN = 4;
1609
+ const FOLLOW_USERS_RECONCILE_MAX_REST_LOOKUPS_PER_RUN = 32;
1585
1610
  const DISCORD_VOICE_FATAL_AUTOJOIN_ERROR_PATTERNS = [
1586
1611
  "api key missing",
1587
1612
  "incorrect api key",
@@ -1627,6 +1652,22 @@ function normalizeVoiceChannelResidencies(entries) {
1627
1652
  }
1628
1653
  return normalized;
1629
1654
  }
1655
+ function normalizeDiscordUserId(value) {
1656
+ const trimmed = value.trim();
1657
+ const withoutDiscordPrefix = trimmed.startsWith("discord:") ? trimmed.slice(8) : trimmed;
1658
+ return (withoutDiscordPrefix.startsWith("user:") ? withoutDiscordPrefix.slice(5) : withoutDiscordPrefix).trim() || void 0;
1659
+ }
1660
+ function normalizeDiscordUserIds(entries) {
1661
+ const ids = /* @__PURE__ */ new Set();
1662
+ for (const entry of entries ?? []) {
1663
+ const id = normalizeDiscordUserId(entry);
1664
+ if (id) ids.add(id);
1665
+ }
1666
+ return ids;
1667
+ }
1668
+ function resolveFollowUsersEnabled(voiceConfig) {
1669
+ return voiceConfig?.followUsersEnabled !== false;
1670
+ }
1630
1671
  function isVoiceChannelAllowed(params) {
1631
1672
  return params.allowedChannels === null || params.allowedChannels.some((entry) => entry.guildId === params.guildId && entry.channelId === params.channelId);
1632
1673
  }
@@ -1637,6 +1678,9 @@ function isFatalAutoJoinFailure(message) {
1637
1678
  const normalized = message.toLowerCase();
1638
1679
  return DISCORD_VOICE_FATAL_AUTOJOIN_ERROR_PATTERNS.some((pattern) => normalized.includes(pattern));
1639
1680
  }
1681
+ function isUnknownDiscordVoiceStateError(err) {
1682
+ return (err && typeof err === "object" && "status" in err && typeof err.status === "number" ? err.status : void 0) === 404 || /unknown voice state/i.test(formatErrorMessage(err));
1683
+ }
1640
1684
  function startAutoJoin(manager) {
1641
1685
  manager.autoJoin().catch((err) => logger.warn(`discord voice: autoJoin failed: ${formatErrorMessage(err)}`));
1642
1686
  }
@@ -1684,6 +1728,14 @@ var DiscordVoiceManager$1 = class {
1684
1728
  this.sessions = /* @__PURE__ */ new Map();
1685
1729
  this.autoJoinTask = null;
1686
1730
  this.fatalAutoJoinFailures = /* @__PURE__ */ new Map();
1731
+ this.followedUserChannels = /* @__PURE__ */ new Map();
1732
+ this.followedVoiceGuilds = /* @__PURE__ */ new Set();
1733
+ this.followUsersReconcileTimer = null;
1734
+ this.followUsersReconcileTask = null;
1735
+ this.followUsersReconcileGuildCursor = 0;
1736
+ this.followUsersReconcileBotGuildCursor = 0;
1737
+ this.followUsersReconcileUserCursors = /* @__PURE__ */ new Map();
1738
+ this.destroyed = false;
1687
1739
  this.botUserId = params.botUserId;
1688
1740
  this.voiceEnabled = resolveDiscordVoiceEnabled(params.discordConfig.voice);
1689
1741
  this.ownerAllowFrom = resolveDiscordAccountAllowFrom({
@@ -1691,6 +1743,7 @@ var DiscordVoiceManager$1 = class {
1691
1743
  accountId: params.accountId
1692
1744
  }) ?? params.discordConfig.allowFrom ?? params.discordConfig.dm?.allowFrom ?? [];
1693
1745
  this.allowedChannels = params.discordConfig.voice?.allowedChannels === void 0 ? null : normalizeVoiceChannelResidencies(params.discordConfig.voice.allowedChannels);
1746
+ this.followUserIds = resolveFollowUsersEnabled(params.discordConfig.voice) ? normalizeDiscordUserIds(params.discordConfig.voice?.followUsers) : /* @__PURE__ */ new Set();
1694
1747
  this.speakerContext = new DiscordVoiceSpeakerContextResolver({
1695
1748
  client: params.client,
1696
1749
  ownerAllowFrom: this.ownerAllowFrom
@@ -1703,7 +1756,7 @@ var DiscordVoiceManager$1 = class {
1703
1756
  return this.voiceEnabled;
1704
1757
  }
1705
1758
  async autoJoin() {
1706
- if (!this.voiceEnabled) return;
1759
+ if (!this.voiceEnabled || this.destroyed) return;
1707
1760
  if (this.autoJoinTask) return this.autoJoinTask;
1708
1761
  this.autoJoinTask = (async () => {
1709
1762
  const entries = this.params.discordConfig.voice?.autoJoin ?? [];
@@ -1747,6 +1800,8 @@ var DiscordVoiceManager$1 = class {
1747
1800
  });
1748
1801
  }
1749
1802
  }
1803
+ this.ensureFollowUsersReconcileTimer();
1804
+ await this.reconcileFollowedUsers("startup");
1750
1805
  })().finally(() => {
1751
1806
  this.autoJoinTask = null;
1752
1807
  });
@@ -1767,7 +1822,11 @@ var DiscordVoiceManager$1 = class {
1767
1822
  channelId: params.channelId.trim()
1768
1823
  });
1769
1824
  }
1770
- async join(params) {
1825
+ async join(params, options) {
1826
+ if (this.destroyed) return {
1827
+ ok: false,
1828
+ message: "Discord voice manager is stopped."
1829
+ };
1771
1830
  if (!this.voiceEnabled) return {
1772
1831
  ok: false,
1773
1832
  message: "Discord voice is disabled (channels.discord.voice.enabled)."
@@ -1803,7 +1862,7 @@ var DiscordVoiceManager$1 = class {
1803
1862
  }
1804
1863
  if (existing) {
1805
1864
  logVoiceVerbose(`join: replacing existing session for guild ${guildId}`);
1806
- await this.leave({ guildId });
1865
+ await this.leave({ guildId }, { preserveFollowState: options?.preserveFollowState });
1807
1866
  }
1808
1867
  const channelInfo = await this.params.client.fetchChannel(channelId).catch(() => null);
1809
1868
  if (!channelInfo || "type" in channelInfo && !isVoiceChannel(channelInfo.type)) return {
@@ -1942,6 +2001,11 @@ var DiscordVoiceManager$1 = class {
1942
2001
  };
1943
2002
  if (voiceMode !== "stt-tts") {
1944
2003
  entry.realtime = new DiscordRealtimeVoiceSession({
2004
+ bootstrapContextInstructions: await resolveDiscordVoiceRealtimeBootstrapContext({
2005
+ entry,
2006
+ cfg: this.params.cfg,
2007
+ discordConfig: this.params.discordConfig
2008
+ }),
1945
2009
  cfg: this.params.cfg,
1946
2010
  discordConfig: this.params.discordConfig,
1947
2011
  entry,
@@ -2022,7 +2086,7 @@ var DiscordVoiceManager$1 = class {
2022
2086
  channelId
2023
2087
  };
2024
2088
  }
2025
- async leave(params) {
2089
+ async leave(params, options) {
2026
2090
  const guildId = params.guildId.trim();
2027
2091
  logVoiceVerbose(`leave requested: guild ${guildId} channel ${params.channelId ?? "current"}`);
2028
2092
  const entry = this.sessions.get(guildId);
@@ -2036,6 +2100,10 @@ var DiscordVoiceManager$1 = class {
2036
2100
  };
2037
2101
  entry.stop();
2038
2102
  this.sessions.delete(guildId);
2103
+ if (!options?.preserveFollowState) {
2104
+ this.followedVoiceGuilds.delete(guildId);
2105
+ this.deleteFollowedUserChannelsForGuild(guildId);
2106
+ }
2039
2107
  logVoiceVerbose(`leave: disconnected from guild ${guildId} channel ${entry.channelId}`);
2040
2108
  return {
2041
2109
  ok: true,
@@ -2045,10 +2113,26 @@ var DiscordVoiceManager$1 = class {
2045
2113
  };
2046
2114
  }
2047
2115
  async handleVoiceStateUpdate(data) {
2048
- if (!this.botUserId || data.user_id !== this.botUserId) return;
2049
2116
  const guildId = data.guild_id?.trim();
2117
+ const userId = data.user_id?.trim();
2050
2118
  const channelId = data.channel_id?.trim();
2051
- if (!guildId || !channelId) return;
2119
+ if (!guildId || !userId) return;
2120
+ if (this.botUserId && userId === this.botUserId) {
2121
+ await this.handleBotVoiceStateUpdate({
2122
+ guildId,
2123
+ channelId
2124
+ });
2125
+ return;
2126
+ }
2127
+ if (this.followUserIds.has(userId)) await this.handleFollowedUserVoiceStateUpdate({
2128
+ guildId,
2129
+ channelId,
2130
+ userId
2131
+ });
2132
+ }
2133
+ async handleBotVoiceStateUpdate(params) {
2134
+ const { guildId, channelId } = params;
2135
+ if (!channelId) return;
2052
2136
  const existing = this.sessions.get(guildId);
2053
2137
  if (this.isAllowedVoiceChannel({
2054
2138
  guildId,
@@ -2059,7 +2143,7 @@ var DiscordVoiceManager$1 = class {
2059
2143
  await this.join({
2060
2144
  guildId,
2061
2145
  channelId
2062
- });
2146
+ }, { preserveFollowState: this.isFollowOwnedGuild(guildId) });
2063
2147
  }
2064
2148
  return;
2065
2149
  }
@@ -2080,9 +2164,267 @@ var DiscordVoiceManager$1 = class {
2080
2164
  await this.join(target);
2081
2165
  }
2082
2166
  }
2167
+ async handleFollowedUserVoiceStateUpdate(params) {
2168
+ if (!this.voiceEnabled || this.destroyed) return;
2169
+ const { guildId, channelId, userId } = params;
2170
+ const followKey = this.formatFollowedUserKey({
2171
+ guildId,
2172
+ userId
2173
+ });
2174
+ const existing = this.sessions.get(guildId);
2175
+ const wasFollowedVoiceSession = this.followedUserChannels.has(followKey) || this.followedVoiceGuilds.has(guildId);
2176
+ if (!channelId) {
2177
+ this.followedUserChannels.delete(followKey);
2178
+ if (existing && wasFollowedVoiceSession && !this.hasFollowedUserInChannel(existing)) await this.handoffToAnotherFollowedUserOrLeave({
2179
+ guildId,
2180
+ userId,
2181
+ existing,
2182
+ reason: "disconnected"
2183
+ });
2184
+ return;
2185
+ }
2186
+ if (!this.isAllowedVoiceChannel({
2187
+ guildId,
2188
+ channelId
2189
+ })) {
2190
+ this.followedUserChannels.delete(followKey);
2191
+ logger.warn(`discord voice: followed user joined non-allowed channel guild=${guildId} user=${userId} channel=${channelId}; ignoring`);
2192
+ if (existing && wasFollowedVoiceSession && !this.hasFollowedUserInChannel(existing)) await this.handoffToAnotherFollowedUserOrLeave({
2193
+ guildId,
2194
+ userId,
2195
+ existing,
2196
+ reason: "joined non-allowed channel"
2197
+ });
2198
+ return;
2199
+ }
2200
+ this.followedUserChannels.set(followKey, {
2201
+ guildId,
2202
+ channelId
2203
+ });
2204
+ if (existing?.channelId === channelId) {
2205
+ this.followedVoiceGuilds.add(guildId);
2206
+ return;
2207
+ }
2208
+ logger.info(`discord voice: following user guild=${guildId} user=${userId} channel=${channelId}`);
2209
+ const result = await this.join({
2210
+ guildId,
2211
+ channelId
2212
+ }, { preserveFollowState: true });
2213
+ if (!result.ok) {
2214
+ if (this.sessions.get(guildId)?.channelId === channelId) this.followedVoiceGuilds.add(guildId);
2215
+ else this.followedUserChannels.delete(followKey);
2216
+ logger.warn(`discord voice: failed to follow user guild=${guildId} user=${userId} channel=${channelId}: ${result.message}`);
2217
+ return;
2218
+ }
2219
+ this.followedVoiceGuilds.add(guildId);
2220
+ }
2083
2221
  async destroy() {
2222
+ this.destroyed = true;
2223
+ if (this.followUsersReconcileTimer) {
2224
+ clearInterval(this.followUsersReconcileTimer);
2225
+ this.followUsersReconcileTimer = null;
2226
+ }
2084
2227
  for (const entry of this.sessions.values()) entry.stop();
2085
2228
  this.sessions.clear();
2229
+ this.followedUserChannels.clear();
2230
+ this.followedVoiceGuilds.clear();
2231
+ }
2232
+ resolveFollowGuildIds() {
2233
+ const guildIds = /* @__PURE__ */ new Set();
2234
+ for (const guildId of Object.keys(this.params.discordConfig.guilds ?? {})) {
2235
+ const normalized = guildId.trim();
2236
+ if (normalized) guildIds.add(normalized);
2237
+ }
2238
+ for (const entry of normalizeVoiceChannelResidencies(this.params.discordConfig.voice?.autoJoin)) guildIds.add(entry.guildId);
2239
+ for (const entry of this.allowedChannels ?? []) guildIds.add(entry.guildId);
2240
+ for (const entry of this.sessions.values()) guildIds.add(entry.guildId);
2241
+ return Array.from(guildIds);
2242
+ }
2243
+ ensureFollowUsersReconcileTimer() {
2244
+ if (this.followUserIds.size === 0) return;
2245
+ if (this.followUsersReconcileTimer) return;
2246
+ this.followUsersReconcileTimer = setInterval(() => {
2247
+ this.reconcileFollowedUsers("interval").catch((err) => {
2248
+ logger.warn(`discord voice: follow user reconciliation failed: ${formatErrorMessage(err)}`);
2249
+ });
2250
+ }, FOLLOW_USERS_RECONCILE_INTERVAL_MS);
2251
+ this.followUsersReconcileTimer.unref?.();
2252
+ }
2253
+ async reconcileFollowedUsers(reason) {
2254
+ if (this.followUserIds.size === 0 || this.destroyed) return;
2255
+ if (this.followUsersReconcileTask) return this.followUsersReconcileTask;
2256
+ this.followUsersReconcileTask = this.runFollowedUsersReconcile(reason).finally(() => {
2257
+ this.followUsersReconcileTask = null;
2258
+ });
2259
+ return this.followUsersReconcileTask;
2260
+ }
2261
+ async runFollowedUsersReconcile(reason) {
2262
+ if (this.destroyed) return;
2263
+ const guildIds = this.resolveFollowGuildIds();
2264
+ if (guildIds.length === 0) {
2265
+ logVoiceVerbose(`follow user reconcile skipped reason=${reason}: no Discord guild ids are configured`);
2266
+ return;
2267
+ }
2268
+ logVoiceVerbose(`follow user reconcile reason=${reason}: ${this.followUserIds.size} users across ${guildIds.length} guilds`);
2269
+ const plans = this.selectFollowUserReconcilePlans(guildIds, reason);
2270
+ for (const plan of plans) {
2271
+ for (const userId of plan.userIds) {
2272
+ const voiceState = await getGuildVoiceState(this.params.client.rest, plan.guildId, userId).catch((err) => {
2273
+ if (!isUnknownDiscordVoiceStateError(err)) {
2274
+ logger.warn(`discord voice: follow user reconcile skipped transient voice state error guild=${plan.guildId} user=${userId} reason=${reason}: ${formatErrorMessage(err)}`);
2275
+ return "transient-error";
2276
+ }
2277
+ logVoiceVerbose(`follow user reconcile reason=${reason}: no voice state guild ${plan.guildId} user ${userId}: ${formatErrorMessage(err)}`);
2278
+ });
2279
+ if (this.destroyed) return;
2280
+ if (voiceState === "transient-error") continue;
2281
+ const channelId = voiceState?.channel_id?.trim();
2282
+ await this.handleFollowedUserVoiceStateUpdate({
2283
+ guildId: plan.guildId,
2284
+ channelId,
2285
+ userId
2286
+ });
2287
+ }
2288
+ if (plan.checkBotVoiceState) {
2289
+ if (this.destroyed) return;
2290
+ await this.disconnectStaleFollowedBotVoiceState({
2291
+ guildId: plan.guildId,
2292
+ reason
2293
+ });
2294
+ }
2295
+ }
2296
+ }
2297
+ selectFollowUserReconcilePlans(guildIds, reason) {
2298
+ const followedUserIds = Array.from(this.followUserIds);
2299
+ if (followedUserIds.length === 0) return [];
2300
+ let remainingLookups = FOLLOW_USERS_RECONCILE_MAX_REST_LOOKUPS_PER_RUN;
2301
+ const guildLimit = Math.min(guildIds.length, FOLLOW_USERS_RECONCILE_MAX_GUILDS_PER_RUN);
2302
+ const start = this.followUsersReconcileGuildCursor % guildIds.length;
2303
+ const plans = [];
2304
+ for (let offset = 0; offset < guildLimit && remainingLookups > 0; offset += 1) {
2305
+ if (this.botUserId && remainingLookups === 1) break;
2306
+ const guildId = guildIds[(start + offset) % guildIds.length];
2307
+ const userLimit = this.resolveFollowUserReconcileUserLookupLimit(followedUserIds.length, remainingLookups);
2308
+ if (userLimit <= 0) break;
2309
+ const selection = this.selectFollowUserReconcileUserIds(guildId, followedUserIds, userLimit);
2310
+ plans.push({
2311
+ guildId,
2312
+ userIds: selection.userIds,
2313
+ checkedAllUsers: selection.completedCycle,
2314
+ checkBotVoiceState: false
2315
+ });
2316
+ remainingLookups -= selection.userIds.length;
2317
+ }
2318
+ this.followUsersReconcileGuildCursor = (start + plans.length) % guildIds.length;
2319
+ this.assignFollowUserReconcileBotChecks(guildIds, plans, remainingLookups);
2320
+ if (plans.length < guildIds.length || plans.some((plan) => plan.userIds.length < followedUserIds.length)) logVoiceVerbose(`follow user reconcile reason=${reason}: sampling ${plans.length}/${guildIds.length} guilds and up to ${FOLLOW_USERS_RECONCILE_MAX_REST_LOOKUPS_PER_RUN} REST lookups`);
2321
+ return plans;
2322
+ }
2323
+ assignFollowUserReconcileBotChecks(guildIds, plans, remainingLookups) {
2324
+ if (!this.botUserId || remainingLookups <= 0 || plans.length === 0) return;
2325
+ const plansByGuild = new Map(plans.map((plan) => [plan.guildId, plan]));
2326
+ const start = this.followUsersReconcileBotGuildCursor % guildIds.length;
2327
+ let scanned = 0;
2328
+ let assigned = 0;
2329
+ for (; scanned < guildIds.length && assigned < remainingLookups; scanned += 1) {
2330
+ const guildId = guildIds[(start + scanned) % guildIds.length];
2331
+ const plan = plansByGuild.get(guildId);
2332
+ if (!plan?.checkedAllUsers) continue;
2333
+ plan.checkBotVoiceState = true;
2334
+ assigned += 1;
2335
+ }
2336
+ this.followUsersReconcileBotGuildCursor = (start + scanned) % guildIds.length;
2337
+ }
2338
+ resolveFollowUserReconcileUserLookupLimit(followedUserCount, remainingLookups) {
2339
+ const userLimit = Math.min(followedUserCount, remainingLookups);
2340
+ if (this.botUserId && followedUserCount > userLimit && remainingLookups > 1) return remainingLookups - 1;
2341
+ return userLimit;
2342
+ }
2343
+ selectFollowUserReconcileUserIds(guildId, followedUserIds, limit) {
2344
+ if (followedUserIds.length <= limit) {
2345
+ this.followUsersReconcileUserCursors.set(guildId, 0);
2346
+ return {
2347
+ userIds: followedUserIds,
2348
+ completedCycle: true
2349
+ };
2350
+ }
2351
+ const start = this.followUsersReconcileUserCursors.get(guildId) ?? 0;
2352
+ const selected = Array.from({ length: limit }, (_, offset) => followedUserIds[(start + offset) % followedUserIds.length]);
2353
+ const completedCycle = start + selected.length >= followedUserIds.length;
2354
+ this.followUsersReconcileUserCursors.set(guildId, (start + selected.length) % followedUserIds.length);
2355
+ return {
2356
+ userIds: selected,
2357
+ completedCycle
2358
+ };
2359
+ }
2360
+ formatFollowedUserKey(params) {
2361
+ return `${params.guildId}:${params.userId}`;
2362
+ }
2363
+ hasFollowedUserInChannel(entry) {
2364
+ return Array.from(this.followedUserChannels.values()).some((candidate) => candidate.guildId === entry.guildId && candidate.channelId === entry.channelId);
2365
+ }
2366
+ resolveFollowedUserHandoffTarget(guildId, currentChannelId) {
2367
+ for (const entry of this.followedUserChannels.values()) if (entry.guildId === guildId && entry.channelId !== currentChannelId && this.isAllowedVoiceChannel(entry)) return entry;
2368
+ return null;
2369
+ }
2370
+ async handoffToAnotherFollowedUserOrLeave(params) {
2371
+ const target = this.resolveFollowedUserHandoffTarget(params.guildId, params.existing.channelId);
2372
+ if (target) {
2373
+ logger.info(`discord voice: followed user ${params.reason} guild=${params.guildId} user=${params.userId}; moving to remaining followed user channel=${target.channelId}`);
2374
+ const result = await this.join(target, { preserveFollowState: true });
2375
+ if (result.ok) this.followedVoiceGuilds.add(params.guildId);
2376
+ else {
2377
+ logger.warn(`discord voice: failed to hand off followed user session guild=${params.guildId} channel=${target.channelId}: ${result.message}`);
2378
+ this.followedVoiceGuilds.delete(params.guildId);
2379
+ this.deleteFollowedUserChannelsForGuild(params.guildId);
2380
+ await this.leave({ guildId: params.guildId });
2381
+ }
2382
+ return;
2383
+ }
2384
+ logger.info(`discord voice: followed user ${params.reason} guild=${params.guildId} user=${params.userId}; leaving channel=${params.existing.channelId}`);
2385
+ await this.leave({ guildId: params.guildId });
2386
+ }
2387
+ isFollowOwnedGuild(guildId) {
2388
+ return this.followedVoiceGuilds.has(guildId) || Array.from(this.followedUserChannels.values()).some((entry) => entry.guildId === guildId);
2389
+ }
2390
+ deleteFollowedUserChannelsForGuild(guildId) {
2391
+ for (const [key, entry] of this.followedUserChannels.entries()) if (entry.guildId === guildId) this.followedUserChannels.delete(key);
2392
+ }
2393
+ async disconnectStaleFollowedBotVoiceState(params) {
2394
+ if (this.destroyed) return;
2395
+ const { guildId, reason } = params;
2396
+ if (Array.from(this.followedUserChannels.values()).some((entry) => entry.guildId === guildId)) return;
2397
+ const existing = this.sessions.get(guildId);
2398
+ if (existing) {
2399
+ if (this.followedVoiceGuilds.has(guildId)) {
2400
+ logger.info(`discord voice: follow reconcile leaving local session guild=${guildId} channel=${existing.channelId} reason=${reason}`);
2401
+ await this.leave({ guildId });
2402
+ }
2403
+ return;
2404
+ }
2405
+ if (!this.botUserId) return;
2406
+ const botVoiceState = await getGuildVoiceState(this.params.client.rest, guildId, this.botUserId).catch((err) => {
2407
+ if (!isUnknownDiscordVoiceStateError(err)) {
2408
+ logger.warn(`discord voice: follow reconcile skipped transient bot voice state error guild=${guildId} reason=${reason}: ${formatErrorMessage(err)}`);
2409
+ return "transient-error";
2410
+ }
2411
+ logVoiceVerbose(`follow user reconcile reason=${reason}: no bot voice state guild ${guildId}: ${formatErrorMessage(err)}`);
2412
+ });
2413
+ if (this.destroyed || botVoiceState === "transient-error") return;
2414
+ const botChannelId = botVoiceState?.channel_id?.trim();
2415
+ if (!botChannelId) return;
2416
+ const gateway = this.params.client.getPlugin("voice")?.getGateway(guildId);
2417
+ if (!gateway) {
2418
+ logger.warn(`discord voice: follow reconcile cannot disconnect stale bot voice state guild=${guildId} channel=${botChannelId}; gateway unavailable`);
2419
+ return;
2420
+ }
2421
+ logger.info(`discord voice: follow reconcile disconnecting stale bot voice state guild=${guildId} channel=${botChannelId} reason=${reason}`);
2422
+ gateway.updateVoiceState({
2423
+ guild_id: guildId,
2424
+ channel_id: null,
2425
+ self_mute: false,
2426
+ self_deaf: false
2427
+ });
2086
2428
  }
2087
2429
  resolveVoiceResidencyTarget(guildId) {
2088
2430
  const autoJoinTarget = normalizeVoiceChannelResidencies(this.params.discordConfig.voice?.autoJoin).toReversed().find((entry) => entry.guildId === guildId);
@@ -2306,8 +2648,9 @@ var DiscordVoiceManager$1 = class {
2306
2648
  async recoverFromDecryptFailures(entry) {
2307
2649
  const active = this.sessions.get(entry.guildId);
2308
2650
  if (!active || active.connection !== entry.connection) return;
2651
+ const preserveFollowState = this.isFollowOwnedGuild(entry.guildId);
2309
2652
  logger.warn(`discord voice: repeated decrypt failures; attempting rejoin for guild ${entry.guildId} channel ${entry.channelId}`);
2310
- const leaveResult = await this.leave({ guildId: entry.guildId });
2653
+ const leaveResult = await this.leave({ guildId: entry.guildId }, { preserveFollowState });
2311
2654
  if (!leaveResult.ok) {
2312
2655
  logger.warn(`discord voice: decrypt recovery leave failed: ${leaveResult.message}`);
2313
2656
  return;
@@ -2315,7 +2658,7 @@ var DiscordVoiceManager$1 = class {
2315
2658
  const result = await this.join({
2316
2659
  guildId: entry.guildId,
2317
2660
  channelId: entry.channelId
2318
- });
2661
+ }, { preserveFollowState });
2319
2662
  if (!result.ok) logger.warn(`discord voice: rejoin after decrypt failures failed: ${result.message}`);
2320
2663
  }
2321
2664
  };
@@ -1,8 +1,8 @@
1
- import { N as createDiscordRestClient } from "./send.shared-C6JLgsbp.js";
2
- import { d as resolveDiscordChannelNameSafe, l as resolveDiscordChannelIdSafe, p as resolveDiscordChannelParentSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-BzelVdsY.js";
3
- import { a as mergeAbortSignals } from "./timeouts-CEwuGaWT.js";
4
- import { c as hasDiscordMessageStickers, d as resolveDiscordMessageChannelId, r as resolveDiscordMessageText } from "./message-utils-BKrlzFtM.js";
5
- import { t as sendTyping } from "./typing-DtdZgo-g.js";
1
+ import { N as createDiscordRestClient } from "./send.shared-D4clao4K.js";
2
+ import { d as resolveDiscordChannelNameSafe, l as resolveDiscordChannelIdSafe, p as resolveDiscordChannelParentSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-Ddalj9wo.js";
3
+ import { a as mergeAbortSignals } from "./timeouts-l_PsHQvX.js";
4
+ import { c as hasDiscordMessageStickers, d as resolveDiscordMessageChannelId, r as resolveDiscordMessageText } from "./message-utils-Bw-wDr8G.js";
5
+ import { t as sendTyping } from "./typing-DUUjLsPr.js";
6
6
  import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
7
  import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
8
8
  import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
@@ -121,7 +121,7 @@ function applyImplicitReplyBatchGate(ctx, replyToMode, isBatched) {
121
121
  //#region extensions/discord/src/monitor/message-run-queue.ts
122
122
  let messageProcessRuntimePromise;
123
123
  async function loadMessageProcessRuntime() {
124
- messageProcessRuntimePromise ??= import("./message-handler.process-PpgZ0qC4.js");
124
+ messageProcessRuntimePromise ??= import("./message-handler.process-AA6doeek.js");
125
125
  return await messageProcessRuntimePromise;
126
126
  }
127
127
  async function processDiscordQueuedMessage(params) {
@@ -173,7 +173,7 @@ function createDiscordMessageRunQueue(params) {
173
173
  //#region extensions/discord/src/monitor/message-handler.ts
174
174
  let messagePreflightRuntimePromise;
175
175
  async function loadMessagePreflightRuntime() {
176
- messagePreflightRuntimePromise ??= import("./message-handler.preflight-DQ-oPY7m.js");
176
+ messagePreflightRuntimePromise ??= import("./message-handler.preflight-DsNzvkrd.js");
177
177
  return await messagePreflightRuntimePromise;
178
178
  }
179
179
  function isNonEmptyString(value) {
@@ -1,13 +1,13 @@
1
- import { A as Message, c as discord_exports, mt as getChannelMessage } from "./send.receipt-Dhym-qOF.js";
2
- import { o as resolveDefaultDiscordAccountId } from "./accounts-ltxKLzxN.js";
3
- import { S as resolveTimestampMs, _ as resolveGroupDmAllow, a as normalizeDiscordSlug, b as formatDiscordUserTag, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordGuildEntry, f as resolveDiscordMemberAccessState, g as resolveDiscordShouldRequireMention, i as normalizeDiscordDisplaySlug, n as isDiscordGroupAllowedByPolicy, x as resolveDiscordSystemLocation } from "./allow-list-CBI-M84K.js";
4
- import { t as resolveDiscordConversationIdentity } from "./conversation-identity-BY7sjUQh.js";
5
- import { l as isRecentlyUnboundThreadWebhookMessage } from "./thread-bindings.state-CSphZOiL.js";
6
- import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-BzelVdsY.js";
7
- import "./thread-bindings-BcXgbZ3-.js";
8
- import { C as resolveDiscordDmCommandAccess, f as buildDiscordRoutePeer, g as handleDiscordDmCommandDecision, h as shouldIgnoreStaleDiscordRouteBinding, m as resolveDiscordEffectiveRoute, p as resolveDiscordConversationRoute, w as resolveDiscordTextCommandAccess } from "./provider-BZtjA1y_.js";
9
- import { d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-BKrlzFtM.js";
10
- import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-DC0FdEcU.js";
1
+ import { A as Message, c as discord_exports, mt as getChannelMessage } from "./send.receipt-spSPAC77.js";
2
+ import { o as resolveDefaultDiscordAccountId } from "./accounts-C2TOAmpo.js";
3
+ import { S as resolveTimestampMs, _ as resolveGroupDmAllow, a as normalizeDiscordSlug, b as formatDiscordUserTag, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordGuildEntry, f as resolveDiscordMemberAccessState, g as resolveDiscordShouldRequireMention, i as normalizeDiscordDisplaySlug, n as isDiscordGroupAllowedByPolicy, x as resolveDiscordSystemLocation } from "./allow-list-BnkWtVpA.js";
4
+ import { t as resolveDiscordConversationIdentity } from "./conversation-identity-CAyt5wzy.js";
5
+ import { l as isRecentlyUnboundThreadWebhookMessage } from "./thread-bindings.state-BsOnj5NX.js";
6
+ import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-Ddalj9wo.js";
7
+ import "./thread-bindings-BQz8z6MR.js";
8
+ import { C as resolveDiscordDmCommandAccess, f as buildDiscordRoutePeer, g as handleDiscordDmCommandDecision, h as shouldIgnoreStaleDiscordRouteBinding, m as resolveDiscordEffectiveRoute, p as resolveDiscordConversationRoute, w as resolveDiscordTextCommandAccess } from "./provider-CfxRE5oz.js";
9
+ import { d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-Bw-wDr8G.js";
10
+ import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-BFp5w0F8.js";
11
11
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
12
12
  import { getChildLogger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
13
13
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
@@ -29,7 +29,7 @@ async function loadConversationRuntime$1() {
29
29
  return await conversationRuntimePromise$1;
30
30
  }
31
31
  async function loadDiscordSendRuntime() {
32
- discordSendRuntimePromise ??= import("./send-BsBy21Te.js").then((n) => n.t);
32
+ discordSendRuntimePromise ??= import("./send-CEAtV0qd.js").then((n) => n.t);
33
33
  return await discordSendRuntimePromise;
34
34
  }
35
35
  async function resolveDiscordDmPreflightAccess(params) {
@@ -427,19 +427,19 @@ let preflightAudioRuntimePromise;
427
427
  let systemEventsRuntimePromise;
428
428
  let discordThreadingRuntimePromise;
429
429
  async function loadPluralKitRuntime() {
430
- pluralkitRuntimePromise ??= import("./pluralkit-BnCH6cHK.js").then((n) => n.n);
430
+ pluralkitRuntimePromise ??= import("./pluralkit-sQLfeyIZ.js").then((n) => n.n);
431
431
  return await pluralkitRuntimePromise;
432
432
  }
433
433
  async function loadPreflightAudioRuntime() {
434
- preflightAudioRuntimePromise ??= import("./preflight-audio-B0keJeQe.js");
434
+ preflightAudioRuntimePromise ??= import("./preflight-audio-ClWbnQto.js");
435
435
  return await preflightAudioRuntimePromise;
436
436
  }
437
437
  async function loadSystemEventsRuntime() {
438
- systemEventsRuntimePromise ??= import("./system-events-DEuiLTl9.js");
438
+ systemEventsRuntimePromise ??= import("./system-events-HgHeiMHg.js");
439
439
  return await systemEventsRuntimePromise;
440
440
  }
441
441
  async function loadDiscordThreadingRuntime() {
442
- discordThreadingRuntimePromise ??= import("./provider-BZtjA1y_.js").then((n) => n.v);
442
+ discordThreadingRuntimePromise ??= import("./provider-CfxRE5oz.js").then((n) => n.v);
443
443
  return await discordThreadingRuntimePromise;
444
444
  }
445
445
  function isPreflightAborted(abortSignal) {