@openclaw/discord 2026.10.1-beta.2 → 2026.10.5-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 (166) hide show
  1. package/dist/.setup/account-inspect-8cCu6uPG.mjs +31 -0
  2. package/dist/.setup/{accounts-DXHCL8Zm.mjs → accounts-ljrzXBJi.mjs} +49 -47
  3. package/dist/.setup/{channel-type-D1_b5UhU.mjs → allow-list-B_ToXtew.mjs} +40 -107
  4. package/dist/.setup/{api-_oznjffB.mjs → api-CtmE1OUN.mjs} +27 -33
  5. package/dist/.setup/{approval-handler.runtime-BilYSrRF.mjs → approval-handler.runtime-D7FoZSn0.mjs} +31 -54
  6. package/dist/.setup/{approval-message-updates-CgRwc2_Q.mjs → approval-message-updates-CIGByUcE.mjs} +2 -2
  7. package/dist/.setup/{audit-DtvzxONM.mjs → audit-zSawfbsN.mjs} +5 -8
  8. package/dist/.setup/{channel-z5lQMS_9.mjs → channel-DI7DMzu0.mjs} +138 -217
  9. package/dist/.setup/{channel-access-BRp3Y-rF.mjs → channel-access-DasWY_OV.mjs} +6 -7
  10. package/dist/.setup/{channel.setup-DBJtugJO.mjs → channel.setup-DdFVN1mr.mjs} +36 -65
  11. package/dist/.setup/{components-CzXLR3QH.mjs → components-HN9052L1.mjs} +124 -147
  12. package/dist/.setup/{config-schema-CsemBDpS.mjs → config-schema-_uBILpQf.mjs} +8 -4
  13. package/dist/.setup/{conversation-binding-route-Bi62ApSt.mjs → conversation-binding-route-DTjCBx-O.mjs} +1 -1
  14. package/dist/.setup/{directory-config-BH1LA-gi.mjs → directory-config-3iVub-3m.mjs} +1 -1
  15. package/dist/.setup/{directory-live-C9xz7IPm.mjs → directory-live-DLygQVtW.mjs} +18 -22
  16. package/dist/.setup/{discord-J7aRzq2Q.mjs → discord-BGqgx-yd.mjs} +686 -854
  17. package/dist/.setup/{doctor-DxSAngJq.mjs → doctor-DSrNJ7m2.mjs} +36 -47
  18. package/dist/.setup/{doctor-contract-Bg3NPAMw.mjs → doctor-contract-Bwv8EuQ_.mjs} +7 -16
  19. package/dist/.setup/{handle-action-DlmFX-_G.mjs → handle-action-CXH1h1tq.mjs} +26 -39
  20. package/dist/.setup/{handle-action.guild-admin-BnyqTczR.mjs → handle-action.guild-admin-CHKW6PIs.mjs} +25 -31
  21. package/dist/.setup/{live-policy-config-emmPNyh4.mjs → live-policy-config-BPoB3eQi.mjs} +1 -1
  22. package/dist/.setup/{message-handler-BNToPZrx.mjs → message-handler-Ii_etru1.mjs} +17 -22
  23. package/dist/.setup/{message-handler.preflight-D0BcCyI7.mjs → message-handler.preflight-C7YuSRl_.mjs} +85 -134
  24. package/dist/.setup/{message-handler.preflight-pluralkit-D1g3eq0B.mjs → message-handler.preflight-pluralkit-C3iAG--b.mjs} +9 -9
  25. package/dist/.setup/{message-handler.process-CThRmCWK.mjs → message-handler.process-ClgWYH6o.mjs} +239 -211
  26. package/dist/.setup/{outbound-adapter-BPq0c2wY.mjs → outbound-adapter-D8OOIuKn.mjs} +76 -115
  27. package/dist/.setup/{outbound-session-route-DGPPKA0z.mjs → outbound-session-route-B0Hpwn3Q.mjs} +61 -119
  28. package/dist/.setup/output-activity-uBpMSX75.mjs +11 -0
  29. package/dist/.setup/{pluralkit-a6_NLfH9.mjs → pluralkit-B64dfq_j.mjs} +3 -1
  30. package/dist/.setup/{preflight-audio-DHGIHEpv.mjs → preflight-audio-wWgsZG5F.mjs} +1 -5
  31. package/dist/.setup/{presence-cache-Dg3_dADn.mjs → presence-cache-DCeTsL73.mjs} +3 -6
  32. package/dist/.setup/{probe-DIRY8vh7.mjs → probe-D3P1x4aN.mjs} +11 -19
  33. package/dist/.setup/{provider-D2OKMo0f.mjs → provider-D9EzOfG8.mjs} +4003 -4916
  34. package/dist/.setup/{provider-session.runtime-BupCTZQv.mjs → provider-session.runtime-CsnRvddI.mjs} +5 -5
  35. package/dist/.setup/provider.runtime-BQVcM7Pg.mjs +2 -0
  36. package/dist/.setup/{realtime-session.runtime-D5RSC8Mm.mjs → realtime-session.runtime-CxGBk8H-.mjs} +84 -113
  37. package/dist/.setup/{receive-recovery-GqF2PUN_.mjs → receive-recovery-eVmK2_hl.mjs} +4 -14
  38. package/dist/.setup/resolve-allowlist-common-DNSsUQRF.mjs +66 -0
  39. package/dist/.setup/resolve-channels-UdT38DzU.mjs +173 -0
  40. package/dist/.setup/{resolve-users-CxLcDmM0.mjs → resolve-users-DnCsrXZ_.mjs} +12 -41
  41. package/dist/.setup/{runtime-CwUA0RLr.mjs → runtime-CRTkKU1G.mjs} +205 -316
  42. package/dist/.setup/{runtime-CerRBfaa.mjs → runtime-DPm3gZXk.mjs} +2 -2
  43. package/dist/.setup/{secret-config-contract-uOE2J7yM.mjs → secret-config-contract-CIsH7R3B.mjs} +9 -18
  44. package/dist/.setup/{security-audit-D42cp5N7.mjs → security-audit-BFT84Uny.mjs} +1 -2
  45. package/dist/.setup/security-contract-DNyyjTZZ.mjs +19 -0
  46. package/dist/.setup/{send-2X7m2Zmw.mjs → send-DbILKgcm.mjs} +194 -251
  47. package/dist/.setup/{send.components-RqvgmCs3.mjs → send.components-FxOS3CeP.mjs} +49 -65
  48. package/dist/.setup/{send.outbound-B4QHSEzm.mjs → send.outbound-_jx4Xpd3.mjs} +182 -206
  49. package/dist/.setup/{retry-BKzO25k4.mjs → send.receipt-BAlzXd8e.mjs} +155 -231
  50. package/dist/.setup/{send.shared-BQIUh6Vn.mjs → send.shared-CjQ4jbMA.mjs} +129 -185
  51. package/dist/.setup/{sender-identity-CooeYfAn.mjs → sender-identity-BNBvGt1l.mjs} +1 -1
  52. package/dist/.setup/setup-plugin-api-xfOFmxHP.mjs +3 -0
  53. package/dist/.setup/{shared-CIJn6AEC.mjs → shared-CestGDpq.mjs} +32 -22
  54. package/dist/.setup/{subagent-hooks-BYlVnxzy.mjs → subagent-hooks-CME7dSGf.mjs} +5 -10
  55. package/dist/.setup/{system-events-CQ3if_pS.mjs → system-events-BwwSDCw4.mjs} +2 -2
  56. package/dist/.setup/target-parsing-C-HyNxcE.mjs +48 -0
  57. package/dist/.setup/{target-resolver-Dzxf80Y3.mjs → target-resolver-Bse8fuTi.mjs} +11 -27
  58. package/dist/.setup/targets-DZJjkzwv.mjs +3 -0
  59. package/dist/.setup/{thread-bindings-yNNlcUWE.mjs → thread-bindings-0hPTSK14.mjs} +61 -52
  60. package/dist/.setup/{thread-bindings.manager-plk0zJAW.mjs → thread-bindings.manager-CdNa6UIs.mjs} +141 -214
  61. package/dist/.setup/{thread-bindings.session-updates-C9aKtVs6.mjs → thread-bindings.session-updates-DGSljVf6.mjs} +62 -101
  62. package/dist/.setup/threading-BKe3E3tp.mjs +246 -0
  63. package/dist/.setup/{timeouts-pun7VJ02.mjs → timeouts-CuNARfyw.mjs} +10 -18
  64. package/dist/.setup/{transcripts-source-CNWswry-.mjs → transcripts-source-0qQYVu2H.mjs} +62 -380
  65. package/dist/.setup/{voice-runtime-Bc75quiH.mjs → voice-runtime-QlZJ4sHv.mjs} +197 -353
  66. package/dist/account-inspect-api.js +1 -1
  67. package/dist/action-runtime-api.js +1 -1
  68. package/dist/activities-api.js +745 -1
  69. package/dist/api.js +19 -18
  70. package/dist/channel-config-api.js +1 -1
  71. package/dist/channel-plugin-api.js +1 -1
  72. package/dist/directory-contract-api.js +1 -1
  73. package/dist/doctor-contract-api.js +1 -1
  74. package/dist/index.js +9 -5
  75. package/dist/runtime-api.js +21 -20
  76. package/dist/secret-contract-api.js +1 -1
  77. package/dist/security-audit-contract-api.js +1 -1
  78. package/dist/security-contract-api.js +1 -1
  79. package/dist/setup-entry.js +1 -1
  80. package/dist/setup-plugin-api.js +1 -1
  81. package/dist/src/voice/audio-worker.runtime.js +40 -59
  82. package/dist/subagent-hooks-api.js +1 -1
  83. package/dist/transcripts-source-api.js +1 -1
  84. package/node_modules/discord-api-types/_generated_/rest/v10/interfaces.d.ts +21 -0
  85. package/node_modules/discord-api-types/_generated_/rest/v10/interfaces.d.ts.map +1 -1
  86. package/node_modules/discord-api-types/_generated_/rest/v9/interfaces.d.ts +21 -0
  87. package/node_modules/discord-api-types/_generated_/rest/v9/interfaces.d.ts.map +1 -1
  88. package/node_modules/discord-api-types/globals.d.ts +6 -0
  89. package/node_modules/discord-api-types/globals.d.ts.map +1 -1
  90. package/node_modules/discord-api-types/globals.js +6 -0
  91. package/node_modules/discord-api-types/globals.js.map +1 -1
  92. package/node_modules/discord-api-types/package.json +3 -3
  93. package/node_modules/discord-api-types/payloads/index.mjs +1 -0
  94. package/node_modules/discord-api-types/payloads/v10/applicationIdentityProfile.d.ts +207 -0
  95. package/node_modules/discord-api-types/payloads/v10/applicationIdentityProfile.d.ts.map +1 -0
  96. package/node_modules/discord-api-types/payloads/v10/applicationIdentityProfile.js +25 -0
  97. package/node_modules/discord-api-types/payloads/v10/applicationIdentityProfile.js.map +1 -0
  98. package/node_modules/discord-api-types/payloads/v10/autoModeration.d.ts +1 -1
  99. package/node_modules/discord-api-types/payloads/v10/autoModeration.d.ts.map +1 -1
  100. package/node_modules/discord-api-types/payloads/v10/guild.d.ts +27 -23
  101. package/node_modules/discord-api-types/payloads/v10/guild.d.ts.map +1 -1
  102. package/node_modules/discord-api-types/payloads/v10/guild.js +26 -22
  103. package/node_modules/discord-api-types/payloads/v10/guild.js.map +1 -1
  104. package/node_modules/discord-api-types/payloads/v10/index.d.ts +1 -0
  105. package/node_modules/discord-api-types/payloads/v10/index.d.ts.map +1 -1
  106. package/node_modules/discord-api-types/payloads/v10/index.js +1 -0
  107. package/node_modules/discord-api-types/payloads/v10/index.js.map +1 -1
  108. package/node_modules/discord-api-types/payloads/v10/index.mjs +1 -0
  109. package/node_modules/discord-api-types/payloads/v9/applicationIdentityProfile.d.ts +207 -0
  110. package/node_modules/discord-api-types/payloads/v9/applicationIdentityProfile.d.ts.map +1 -0
  111. package/node_modules/discord-api-types/payloads/v9/applicationIdentityProfile.js +25 -0
  112. package/node_modules/discord-api-types/payloads/v9/applicationIdentityProfile.js.map +1 -0
  113. package/node_modules/discord-api-types/payloads/v9/autoModeration.d.ts +1 -1
  114. package/node_modules/discord-api-types/payloads/v9/autoModeration.d.ts.map +1 -1
  115. package/node_modules/discord-api-types/payloads/v9/guild.d.ts +27 -23
  116. package/node_modules/discord-api-types/payloads/v9/guild.d.ts.map +1 -1
  117. package/node_modules/discord-api-types/payloads/v9/guild.js +26 -22
  118. package/node_modules/discord-api-types/payloads/v9/guild.js.map +1 -1
  119. package/node_modules/discord-api-types/payloads/v9/index.d.ts +1 -0
  120. package/node_modules/discord-api-types/payloads/v9/index.d.ts.map +1 -1
  121. package/node_modules/discord-api-types/payloads/v9/index.js +1 -0
  122. package/node_modules/discord-api-types/payloads/v9/index.js.map +1 -1
  123. package/node_modules/discord-api-types/payloads/v9/index.mjs +1 -0
  124. package/node_modules/discord-api-types/rest/common.d.ts +2 -0
  125. package/node_modules/discord-api-types/rest/common.d.ts.map +1 -1
  126. package/node_modules/discord-api-types/rest/common.js +2 -0
  127. package/node_modules/discord-api-types/rest/common.js.map +1 -1
  128. package/node_modules/discord-api-types/rest/v10/applicationIdentityProfile.d.ts +70 -0
  129. package/node_modules/discord-api-types/rest/v10/applicationIdentityProfile.d.ts.map +1 -0
  130. package/node_modules/discord-api-types/rest/v10/applicationIdentityProfile.js +3 -0
  131. package/node_modules/discord-api-types/rest/v10/applicationIdentityProfile.js.map +1 -0
  132. package/node_modules/discord-api-types/rest/v10/index.d.ts +1 -0
  133. package/node_modules/discord-api-types/rest/v10/index.d.ts.map +1 -1
  134. package/node_modules/discord-api-types/rest/v10/index.js +29 -0
  135. package/node_modules/discord-api-types/rest/v10/index.js.map +1 -1
  136. package/node_modules/discord-api-types/rest/v10/interactions.d.ts +4 -2
  137. package/node_modules/discord-api-types/rest/v10/interactions.d.ts.map +1 -1
  138. package/node_modules/discord-api-types/rest/v10/user.d.ts +2 -2
  139. package/node_modules/discord-api-types/rest/v10/user.d.ts.map +1 -1
  140. package/node_modules/discord-api-types/rest/v9/applicationIdentityProfile.d.ts +70 -0
  141. package/node_modules/discord-api-types/rest/v9/applicationIdentityProfile.d.ts.map +1 -0
  142. package/node_modules/discord-api-types/rest/v9/applicationIdentityProfile.js +3 -0
  143. package/node_modules/discord-api-types/rest/v9/applicationIdentityProfile.js.map +1 -0
  144. package/node_modules/discord-api-types/rest/v9/index.d.ts +1 -0
  145. package/node_modules/discord-api-types/rest/v9/index.d.ts.map +1 -1
  146. package/node_modules/discord-api-types/rest/v9/index.js +29 -0
  147. package/node_modules/discord-api-types/rest/v9/index.js.map +1 -1
  148. package/node_modules/discord-api-types/rest/v9/interactions.d.ts +4 -2
  149. package/node_modules/discord-api-types/rest/v9/interactions.d.ts.map +1 -1
  150. package/node_modules/discord-api-types/rest/v9/user.d.ts +2 -2
  151. package/node_modules/discord-api-types/rest/v9/user.d.ts.map +1 -1
  152. package/node_modules/discord-api-types/v10.mjs +1 -0
  153. package/node_modules/discord-api-types/v9.mjs +1 -0
  154. package/node_modules/ws/lib/websocket.js +17 -3
  155. package/node_modules/ws/package.json +1 -1
  156. package/openclaw.plugin.json +3 -6
  157. package/package.json +7 -12
  158. package/dist/.setup/account-inspect-CjKqkDnH.mjs +0 -45
  159. package/dist/.setup/activities-api-BL_hJ0JQ.mjs +0 -789
  160. package/dist/.setup/provider.runtime-BaW_JnfU.mjs +0 -2
  161. package/dist/.setup/resolve-allowlist-common-DUYRrrVB.mjs +0 -27
  162. package/dist/.setup/resolve-channels--MUatisx.mjs +0 -246
  163. package/dist/.setup/security-contract-DSHk7I2w.mjs +0 -26
  164. package/dist/.setup/setup-plugin-api-nn_giXvZ.mjs +0 -3
  165. package/dist/.setup/targets-Lf8D55w-.mjs +0 -3
  166. package/dist/control-ui/2afbdab1d3aa65a789d8a3eba666f4b1ec5df07073538fe30d6cf32093a1fb2d/index.js +0 -1
@@ -0,0 +1,31 @@
1
+ import { i as listDiscordAccountIds, n as inspectDiscordAccountConfig, v as resolveDiscordAccountAvailability } from "./accounts-ljrzXBJi.mjs";
2
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
3
+ import { normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
4
+ //#region extensions/discord/src/account-inspect.ts
5
+ function inspectDiscordAccountPrimary(params) {
6
+ return inspectDiscordAccountConfig(params, {
7
+ includeName: true,
8
+ resolveFallbackToken: (accountId) => {
9
+ const envToken = accountId === DEFAULT_ACCOUNT_ID ? normalizeSecretInputString(params.envToken ?? process.env.DISCORD_BOT_TOKEN) : void 0;
10
+ return {
11
+ token: envToken?.replace(/^Bot\s+/i, "") ?? "",
12
+ source: envToken ? "env" : "none"
13
+ };
14
+ }
15
+ });
16
+ }
17
+ function inspectDiscordAccount(params) {
18
+ const account = inspectDiscordAccountPrimary(params);
19
+ return {
20
+ ...account,
21
+ ...resolveDiscordAccountAvailability({
22
+ account,
23
+ resolveAccounts: () => listDiscordAccountIds(params.cfg).map((accountId) => inspectDiscordAccountPrimary({
24
+ ...params,
25
+ accountId
26
+ }))
27
+ })
28
+ };
29
+ }
30
+ //#endregion
31
+ export { inspectDiscordAccount as t };
@@ -1,23 +1,18 @@
1
- import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- import { createAccountActionGate, createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
3
1
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
2
+ import { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
3
+ import { createAccountActionGate, createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
4
4
  import { mapAllowFromEntries, normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
5
5
  import { resolveConfiguredFromCredentialStatuses } from "openclaw/plugin-sdk/channel-status";
6
6
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId as normalizeAccountId$1, resolveAccountEntry } from "openclaw/plugin-sdk/routing";
7
- import { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
7
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
8
8
  import { getRuntimeConfigSnapshot, getRuntimeConfigSourceSnapshot, selectApplicableRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
9
9
  //#region extensions/discord/src/account-token-inspect.ts
10
10
  function inspectDiscordConfiguredToken(value) {
11
11
  const normalized = normalizeSecretInputString(value);
12
- if (normalized) return {
13
- token: normalized.replace(/^Bot\s+/i, ""),
12
+ if (normalized || hasConfiguredSecretInput(value)) return {
13
+ token: normalized ? normalized.replace(/^Bot\s+/i, "") : "",
14
14
  tokenSource: "config",
15
- tokenStatus: "available"
16
- };
17
- if (hasConfiguredSecretInput(value)) return {
18
- token: "",
19
- tokenSource: "config",
20
- tokenStatus: "configured_unavailable"
15
+ tokenStatus: normalized ? "available" : "configured_unavailable"
21
16
  };
22
17
  return null;
23
18
  }
@@ -106,8 +101,7 @@ function normalizeDiscordToken(raw, path) {
106
101
  value: raw,
107
102
  path
108
103
  });
109
- if (!trimmed) return;
110
- return stripDiscordBotPrefix(trimmed);
104
+ return trimmed ? stripDiscordBotPrefix(trimmed) : void 0;
111
105
  }
112
106
  function resolveDiscordTokenValue(params) {
113
107
  const resolved = resolveSecretInputString({
@@ -116,12 +110,19 @@ function resolveDiscordTokenValue(params) {
116
110
  defaults: params.cfg.secrets?.defaults,
117
111
  mode: "inspect"
118
112
  });
119
- if (resolved.status === "available") return {
120
- status: "available",
121
- value: stripDiscordBotPrefix(resolved.value)
113
+ if (resolved.status === "available") {
114
+ const token = stripDiscordBotPrefix(resolved.value);
115
+ return token ? {
116
+ token,
117
+ source: "config",
118
+ tokenStatus: "available"
119
+ } : void 0;
120
+ }
121
+ if (resolved.status === "configured_unavailable") return {
122
+ token: "",
123
+ source: "config",
124
+ tokenStatus: "configured_unavailable"
122
125
  };
123
- if (resolved.status === "configured_unavailable") return { status: "configured_unavailable" };
124
- return { status: "missing" };
125
126
  }
126
127
  function resolveDiscordToken(cfg, opts = {}) {
127
128
  const selectedCfg = selectDiscordRuntimeConfig(cfg);
@@ -134,16 +135,7 @@ function resolveDiscordToken(cfg, opts = {}) {
134
135
  value: accountCfg?.token,
135
136
  path: `channels.discord.accounts.${accountId}.token`
136
137
  });
137
- if (accountToken.status === "available" && accountToken.value) return {
138
- token: accountToken.value,
139
- source: "config",
140
- tokenStatus: "available"
141
- };
142
- if (accountToken.status === "configured_unavailable") return {
143
- token: "",
144
- source: "config",
145
- tokenStatus: "configured_unavailable"
146
- };
138
+ if (accountToken) return accountToken;
147
139
  if (hasAccountToken) return {
148
140
  token: "",
149
141
  source: "none",
@@ -154,16 +146,7 @@ function resolveDiscordToken(cfg, opts = {}) {
154
146
  value: discordCfg?.token,
155
147
  path: "channels.discord.token"
156
148
  });
157
- if (configToken.status === "available" && configToken.value) return {
158
- token: configToken.value,
159
- source: "config",
160
- tokenStatus: "available"
161
- };
162
- if (configToken.status === "configured_unavailable") return {
163
- token: "",
164
- source: "config",
165
- tokenStatus: "configured_unavailable"
166
- };
149
+ if (configToken) return configToken;
167
150
  const envToken = accountId === DEFAULT_ACCOUNT_ID$1 ? normalizeDiscordToken(opts.envToken ?? process.env.DISCORD_BOT_TOKEN, "DISCORD_BOT_TOKEN") : void 0;
168
151
  if (envToken) return {
169
152
  token: envToken,
@@ -178,7 +161,7 @@ function resolveDiscordToken(cfg, opts = {}) {
178
161
  }
179
162
  //#endregion
180
163
  //#region extensions/discord/src/accounts.ts
181
- const { listAccountIds, resolveDefaultAccountId, resolveAccountConfig: mergeDiscordAccountConfig } = createAccountListHelpers("discord", {
164
+ const { listAccountIds: listDiscordAccountIds, resolveDefaultAccountId: resolveDefaultDiscordAccountId, resolveAccountConfig: mergeDiscordAccountConfig } = createAccountListHelpers("discord", {
182
165
  implicitDefaultAccount: {
183
166
  channelKeys: ["token"],
184
167
  envVars: ["DISCORD_BOT_TOKEN"]
@@ -189,29 +172,48 @@ const { listAccountIds, resolveDefaultAccountId, resolveAccountConfig: mergeDisc
189
172
  "botLoopProtection"
190
173
  ]
191
174
  });
192
- const listDiscordAccountIds = listAccountIds;
193
- const resolveDefaultDiscordAccountId = resolveDefaultAccountId;
194
175
  function resolveDiscordAccountConfig(cfg, accountId) {
195
176
  return resolveAccountEntry(cfg.channels?.discord?.accounts, accountId);
196
177
  }
197
- function resolveDiscordAccountAllowFrom(params) {
178
+ function inspectDiscordAccountConfig(params, options) {
198
179
  const accountId = normalizeAccountId(params.accountId ?? resolveDefaultDiscordAccountId(params.cfg));
180
+ const config = mergeDiscordAccountConfig(params.cfg, accountId);
181
+ const enabled = params.cfg.channels?.discord?.enabled !== false && config.enabled !== false;
199
182
  const accountConfig = resolveDiscordAccountConfig(params.cfg, accountId);
183
+ const hasAccountToken = Boolean(accountConfig && Object.hasOwn(accountConfig, "token"));
184
+ return inspectDiscordAccountTokenState({
185
+ base: {
186
+ accountId,
187
+ enabled,
188
+ ...options.includeName ? { name: normalizeOptionalString(config.name) } : {}
189
+ },
190
+ config,
191
+ accountToken: accountConfig?.token,
192
+ hasAccountToken,
193
+ channelToken: params.cfg.channels?.discord?.token,
194
+ resolveFallbackToken: () => options.resolveFallbackToken(accountId)
195
+ });
196
+ }
197
+ function readConfiguredAccount(params) {
198
+ const accountId = normalizeAccountId(params.accountId ?? resolveDefaultDiscordAccountId(params.cfg));
199
+ return resolveDiscordAccountConfig(params.cfg, accountId);
200
+ }
201
+ function resolveDiscordAccountAllowFrom(params) {
202
+ const accountConfig = readConfiguredAccount(params);
200
203
  const rootConfig = params.cfg.channels?.discord;
201
204
  const allowFrom = accountConfig?.allowFrom ?? rootConfig?.allowFrom;
202
205
  return allowFrom ? mapAllowFromEntries(allowFrom) : void 0;
203
206
  }
204
207
  function resolveDiscordAccountDmPolicy(params) {
205
- const accountId = normalizeAccountId(params.accountId ?? resolveDefaultDiscordAccountId(params.cfg));
206
- const accountConfig = resolveDiscordAccountConfig(params.cfg, accountId);
208
+ const accountConfig = readConfiguredAccount(params);
207
209
  const rootConfig = params.cfg.channels?.discord;
208
210
  return normalizeChannelDmPolicy(accountConfig?.dmPolicy ?? rootConfig?.dmPolicy ?? "pairing");
209
211
  }
210
212
  function createDiscordActionGate(params) {
211
- const accountId = normalizeAccountId(params.accountId ?? resolveDefaultDiscordAccountId(params.cfg));
213
+ const accountConfig = readConfiguredAccount(params);
212
214
  return createAccountActionGate({
213
215
  baseActions: params.cfg.channels?.discord?.actions,
214
- accountActions: resolveDiscordAccountConfig(params.cfg, accountId)?.actions
216
+ accountActions: accountConfig?.actions
215
217
  });
216
218
  }
217
219
  function resolveDiscordAccount(params) {
@@ -267,4 +269,4 @@ function listDiscordStartupAccountIds(cfg) {
267
269
  return [defaultAccountId, ...startupAccountIds.filter((candidateId) => candidateId !== defaultAccountId)];
268
270
  }
269
271
  //#endregion
270
- export { inspectDiscordAccountTokenState as _, listEnabledDiscordAccounts as a, resolveDiscordAccount as c, resolveDiscordAccountDisabledReason as d, resolveDiscordAccountDmPolicy as f, selectDiscordActivitiesRuntimeConfig as g, resolveDiscordToken as h, listDiscordStartupAccountIds as i, resolveDiscordAccountAllowFrom as l, normalizeDiscordToken as m, isDiscordAccountEnabledForRuntime as n, mergeDiscordAccountConfig as o, resolveDiscordMaxLinesPerMessage as p, listDiscordAccountIds as r, resolveDefaultDiscordAccountId as s, createDiscordActionGate as t, resolveDiscordAccountConfig as u, resolveDiscordAccountAvailability as v };
272
+ export { selectDiscordActivitiesRuntimeConfig as _, listDiscordStartupAccountIds as a, resolveDefaultDiscordAccountId as c, resolveDiscordAccountConfig as d, resolveDiscordAccountDisabledReason as f, resolveDiscordToken as g, normalizeDiscordToken as h, listDiscordAccountIds as i, resolveDiscordAccount as l, resolveDiscordMaxLinesPerMessage as m, inspectDiscordAccountConfig as n, listEnabledDiscordAccounts as o, resolveDiscordAccountDmPolicy as p, isDiscordAccountEnabledForRuntime as r, mergeDiscordAccountConfig as s, createDiscordActionGate as t, resolveDiscordAccountAllowFrom as u, resolveDiscordAccountAvailability as v };
@@ -5,6 +5,11 @@ import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtim
5
5
  import { fetchWithSsrFGuard, isBlockedHostnameOrIp, isLoopbackHost } from "openclaw/plugin-sdk/ssrf-runtime";
6
6
  import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatchWithFallback, resolveChannelMatchConfig } from "openclaw/plugin-sdk/channel-targets";
7
7
  import { resolveAllowlistMatchByCandidates } from "openclaw/plugin-sdk/allow-from";
8
+ //#region extensions/discord/src/channel-type.ts
9
+ function isDiscordThreadChannelType(channelType) {
10
+ return channelType === ChannelType.AnnouncementThread || channelType === ChannelType.PublicThread || channelType === ChannelType.PrivateThread;
11
+ }
12
+ //#endregion
8
13
  //#region extensions/discord/src/endpoint-runtime.ts
9
14
  const DISCORD_ENDPOINT_RESPONSE_MAX_BYTES = 8388608;
10
15
  const DISCORD_API_URL_ENV = "DISCORD_API_URL";
@@ -25,27 +30,16 @@ function normalizeRestApiBaseUrl(value) {
25
30
  url.pathname = url.pathname.replace(/\/+$/u, "") || "/";
26
31
  return url;
27
32
  }
28
- function normalizeGatewayOrigin(value) {
29
- let url;
30
- try {
31
- url = new URL(value);
32
- } catch {
33
- throw new Error("Discord endpoint Gateway origin must be a valid URL");
34
- }
35
- if (url.protocol !== "wss:" && !(url.protocol === "ws:" && isLoopbackHost(url.hostname))) throw new Error("Discord endpoint Gateway origin must use WSS or loopback WS");
36
- if (url.protocol === "wss:" && isBlockedHostnameOrIp(url.hostname)) throw new Error("Discord endpoint Gateway origin must not target a private/internal/special-use hostname or IP address");
37
- if (url.username || url.password || url.hash || url.pathname !== "/" || url.search) throw new Error("Discord endpoint Gateway origin must be an origin without credentials, path, query, or fragment");
38
- return url.origin;
39
- }
40
33
  function resolveDescriptor(apiUrl) {
41
34
  const restApiBaseUrl = normalizeRestApiBaseUrl(apiUrl);
42
35
  const gatewayBotUrl = new URL(`${restApiBaseUrl.pathname.replace(/\/+$/u, "")}/gateway/bot`, restApiBaseUrl.origin);
43
36
  const gatewayOriginUrl = new URL(restApiBaseUrl.origin);
44
37
  gatewayOriginUrl.protocol = gatewayOriginUrl.protocol === "https:" ? "wss:" : "ws:";
38
+ if (gatewayOriginUrl.protocol === "wss:" && isBlockedHostnameOrIp(gatewayOriginUrl.hostname)) throw new Error("Discord endpoint Gateway origin must not target a private/internal/special-use hostname or IP address");
45
39
  return Object.freeze({
46
40
  restApiBaseUrl: restApiBaseUrl.toString().replace(/\/$/u, ""),
47
41
  gatewayBotUrl: gatewayBotUrl.toString(),
48
- gatewayOrigin: normalizeGatewayOrigin(gatewayOriginUrl.origin)
42
+ gatewayOrigin: gatewayOriginUrl.origin
49
43
  });
50
44
  }
51
45
  function isWithinRestApiBase(target, restApiBaseUrl) {
@@ -116,27 +110,22 @@ function getDiscordEndpointRuntime(env = process.env) {
116
110
  });
117
111
  }
118
112
  function resolveDiscordEndpointMediaGuard(url, retainedRuntime) {
119
- if (retainedRuntime === null) return;
120
- const runtime = retainedRuntime ?? getDiscordEndpointRuntime();
121
- if (!runtime) return;
122
- const target = parseHttpAnchor(url, "Discord endpoint media URL");
123
- const restOrigin = new URL(runtime.descriptor.restApiBaseUrl).origin;
124
- if (target.origin !== restOrigin) throw new Error("Discord endpoint media URL is outside the configured REST origin");
125
- return {
126
- maxRedirects: 0,
127
- ssrfPolicy: {
128
- allowedOrigins: [restOrigin],
129
- hostnameAllowlist: [target.hostname]
130
- }
131
- };
113
+ const guard = resolveEndpointResourceGuard(url, "media", retainedRuntime);
114
+ return guard ? {
115
+ maxRedirects: guard.maxRedirects,
116
+ ssrfPolicy: guard.policy
117
+ } : void 0;
132
118
  }
133
119
  function resolveDiscordEndpointAttachmentGuard(url, retainedRuntime) {
120
+ return resolveEndpointResourceGuard(url, "attachment upload", retainedRuntime);
121
+ }
122
+ function resolveEndpointResourceGuard(url, kind, retainedRuntime) {
134
123
  if (retainedRuntime === null) return;
135
124
  const runtime = retainedRuntime ?? getDiscordEndpointRuntime();
136
125
  if (!runtime) return;
137
- const target = parseHttpAnchor(url, "Discord endpoint attachment upload URL");
126
+ const target = parseHttpAnchor(url, `Discord endpoint ${kind} URL`);
138
127
  const restOrigin = new URL(runtime.descriptor.restApiBaseUrl).origin;
139
- if (target.origin !== restOrigin) throw new Error("Discord endpoint attachment upload URL is outside the configured REST origin");
128
+ if (target.origin !== restOrigin) throw new Error(`Discord endpoint ${kind} URL is outside the configured REST origin`);
140
129
  return {
141
130
  maxRedirects: 0,
142
131
  policy: {
@@ -229,19 +218,6 @@ function normalizeDiscordSlug(value) {
229
218
  function normalizeDiscordDisplaySlug(value) {
230
219
  return normalizeLowercaseStringOrEmpty(value).normalize("NFC").replace(/^#/, "").replace(/[\s_]+/g, "-").replace(/[^\p{L}\p{M}\p{N}-]+/gu, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
231
220
  }
232
- function resolveDiscordAllowListNameMatch(list, candidate) {
233
- const nameSlug = candidate.name ? normalizeDiscordSlug(candidate.name) : "";
234
- if (nameSlug && list.names.has(nameSlug)) return {
235
- matchKey: nameSlug,
236
- matchSource: "name"
237
- };
238
- const tagSlug = candidate.tag ? normalizeDiscordSlug(candidate.tag) : "";
239
- if (tagSlug && list.names.has(tagSlug)) return {
240
- matchKey: tagSlug,
241
- matchSource: "tag"
242
- };
243
- return null;
244
- }
245
221
  function allowListMatches(list, candidate, params) {
246
222
  return resolveDiscordAllowListMatch({
247
223
  allowList: list,
@@ -261,11 +237,13 @@ function resolveDiscordAllowListMatch(params) {
261
237
  matchKey: candidate.id,
262
238
  matchSource: "id"
263
239
  };
264
- if (params.allowNameMatching === true) {
265
- const namedMatch = resolveDiscordAllowListNameMatch(allowList, candidate);
266
- if (namedMatch) return {
240
+ if (params.allowNameMatching === true) for (const matchSource of ["name", "tag"]) {
241
+ const value = candidate[matchSource];
242
+ const matchKey = value ? normalizeDiscordSlug(value) : "";
243
+ if (matchKey && allowList.names.has(matchKey)) return {
267
244
  allowed: true,
268
- ...namedMatch
245
+ matchKey,
246
+ matchSource
269
247
  };
270
248
  }
271
249
  return { allowed: false };
@@ -283,27 +261,16 @@ function resolveDiscordUserAllowed(params) {
283
261
  tag: params.userTag
284
262
  }, { allowNameMatching: params.allowNameMatching });
285
263
  }
286
- function resolveDiscordRoleAllowed(params) {
287
- const allowList = normalizeDiscordAllowList(params.allowList, ["role:"]);
288
- if (!allowList) return true;
289
- if (allowList.allowAll) return true;
290
- return params.memberRoleIds.some((roleId) => allowList.ids.has(roleId));
291
- }
292
264
  function resolveDiscordMemberAllowed(params) {
293
265
  const hasUserRestriction = Array.isArray(params.userAllowList) && params.userAllowList.length > 0;
294
266
  const hasRoleRestriction = Array.isArray(params.roleAllowList) && params.roleAllowList.length > 0;
295
267
  if (!hasUserRestriction && !hasRoleRestriction) return true;
296
268
  const userOk = hasUserRestriction ? resolveDiscordUserAllowed({
297
- allowList: params.userAllowList,
298
- userId: params.userId,
299
- userName: params.userName,
300
- userTag: params.userTag,
301
- allowNameMatching: params.allowNameMatching
302
- }) : false;
303
- const roleOk = hasRoleRestriction ? resolveDiscordRoleAllowed({
304
- allowList: params.roleAllowList,
305
- memberRoleIds: params.memberRoleIds
269
+ ...params,
270
+ allowList: params.userAllowList
306
271
  }) : false;
272
+ const roles = hasRoleRestriction ? normalizeDiscordAllowList(params.roleAllowList, ["role:"]) : null;
273
+ const roleOk = Boolean(roles && (roles.allowAll || params.memberRoleIds.some((roleId) => roles.ids.has(roleId))));
307
274
  return userOk || roleOk;
308
275
  }
309
276
  function resolveDiscordMemberAccessState(params) {
@@ -335,11 +302,7 @@ function resolveDiscordOwnerAllowFrom(params) {
335
302
  if (!allowList) return;
336
303
  const match = resolveDiscordAllowListMatch({
337
304
  allowList,
338
- candidate: {
339
- id: params.sender.id,
340
- name: params.sender.name,
341
- tag: params.sender.tag
342
- },
305
+ candidate: params.sender,
343
306
  allowNameMatching: params.allowNameMatching
344
307
  });
345
308
  if (!match.allowed || !match.matchKey || match.matchKey === "*") return;
@@ -374,33 +337,12 @@ function resolveDiscordGuildEntry(params) {
374
337
  };
375
338
  if (!guild) return null;
376
339
  const slug = normalizeDiscordSlug(guild.name ?? "");
377
- const bySlug = entries[slug];
378
- if (bySlug) return {
379
- ...bySlug,
380
- id: guildId ?? guild.id,
381
- slug: slug || bySlug.slug
382
- };
383
- const wildcard = entries["*"];
384
- if (wildcard) return {
385
- ...wildcard,
340
+ const byName = entries[slug] || entries["*"];
341
+ return byName ? {
342
+ ...byName,
386
343
  id: guildId ?? guild.id,
387
- slug: slug || wildcard.slug
388
- };
389
- return null;
390
- }
391
- function buildDiscordChannelKeys(params) {
392
- const allowNameMatch = params.allowNameMatch !== false;
393
- return buildChannelKeyCandidates(params.id, allowNameMatch ? params.slug : void 0, allowNameMatch ? params.name : void 0);
394
- }
395
- function resolveDiscordChannelEntryMatch(channels, params, parentParams) {
396
- const keys = buildDiscordChannelKeys(params);
397
- const parentKeys = parentParams ? buildDiscordChannelKeys(parentParams) : void 0;
398
- return resolveChannelEntryMatchWithFallback({
399
- entries: channels,
400
- keys,
401
- parentKeys,
402
- wildcardKey: "*"
403
- });
344
+ slug: slug || byName.slug
345
+ } : null;
404
346
  }
405
347
  function hasConfiguredDiscordChannels(channels) {
406
348
  return Boolean(channels && Object.keys(channels).length > 0);
@@ -435,16 +377,12 @@ function resolveDiscordChannelConfigWithFallback(params) {
435
377
  const channels = guildInfo?.channels;
436
378
  if (!hasConfiguredDiscordChannels(channels)) return null;
437
379
  const resolvedParentSlug = parentSlug ?? (parentName ? normalizeDiscordSlug(parentName) : "");
438
- const match = resolveDiscordChannelEntryMatch(channels, {
439
- id: channelId,
440
- name: channelName,
441
- slug: channelSlug,
442
- allowNameMatch: scope !== "thread"
443
- }, parentId || parentName || parentSlug ? {
444
- id: parentId ?? "",
445
- name: parentName,
446
- slug: resolvedParentSlug
447
- } : void 0);
380
+ const match = resolveChannelEntryMatchWithFallback({
381
+ entries: channels,
382
+ keys: buildChannelKeyCandidates(channelId, scope === "thread" ? void 0 : channelSlug, scope === "thread" ? void 0 : channelName),
383
+ parentKeys: parentId || parentName || parentSlug ? buildChannelKeyCandidates(parentId ?? "", resolvedParentSlug, parentName) : void 0,
384
+ wildcardKey: "*"
385
+ });
448
386
  return resolveChannelMatchConfig(match, resolveDiscordChannelConfigEntry) ?? { allowed: false };
449
387
  }
450
388
  /** Boolean runtime API retained for plugins built against OpenClaw 2026.9.6. */
@@ -522,9 +460,4 @@ function shouldEmitDiscordReactionNotification(params) {
522
460
  return false;
523
461
  }
524
462
  //#endregion
525
- //#region extensions/discord/src/channel-type.ts
526
- function isDiscordThreadChannelType(channelType) {
527
- return channelType === ChannelType.AnnouncementThread || channelType === ChannelType.PublicThread || channelType === ChannelType.PrivateThread;
528
- }
529
- //#endregion
530
- export { resolveDiscordEndpointMediaGuard as A, formatDiscordUserTag as C, assertDiscordEndpointGatewayUrl as D, parseDiscordRetryAfterBodySeconds as E, getDiscordEndpointRuntime as O, formatDiscordReactionEmoji as S, resolveTimestampMs as T, resolveDiscordOwnerAccess as _, normalizeDiscordAllowList as a, resolveGroupDmAllow as b, resolveDiscordAllowListMatch as c, resolveDiscordChannelPolicyCommandAuthorizer as d, resolveDiscordCommandAuthorized as f, resolveDiscordMentionPolicy as g, resolveDiscordMemberAllowed as h, isDiscordGroupAllowedByPolicy as i, resolveDiscordEndpointAttachmentGuard as k, resolveDiscordChannelConfig as l, resolveDiscordMemberAccessState as m, allowListMatches as n, normalizeDiscordDisplaySlug as o, resolveDiscordGuildEntry as p, hasConfiguredDiscordChannels as r, normalizeDiscordSlug as s, isDiscordThreadChannelType as t, resolveDiscordChannelConfigWithFallback as u, resolveDiscordOwnerAllowFrom as v, resolveDiscordSystemLocation as w, shouldEmitDiscordReactionNotification as x, resolveDiscordShouldRequireMention as y };
463
+ export { isDiscordThreadChannelType as A, resolveDiscordSystemLocation as C, getDiscordEndpointRuntime as D, assertDiscordEndpointGatewayUrl as E, resolveDiscordEndpointAttachmentGuard as O, formatDiscordUserTag as S, parseDiscordRetryAfterBodySeconds as T, resolveDiscordOwnerAllowFrom as _, normalizeDiscordDisplaySlug as a, shouldEmitDiscordReactionNotification as b, resolveDiscordChannelConfig as c, resolveDiscordCommandAuthorized as d, resolveDiscordGuildEntry as f, resolveDiscordOwnerAccess as g, resolveDiscordMentionPolicy as h, normalizeDiscordAllowList as i, resolveDiscordEndpointMediaGuard as k, resolveDiscordChannelConfigWithFallback as l, resolveDiscordMemberAllowed as m, hasConfiguredDiscordChannels as n, normalizeDiscordSlug as o, resolveDiscordMemberAccessState as p, isDiscordGroupAllowedByPolicy as r, resolveDiscordAllowListMatch as s, allowListMatches as t, resolveDiscordChannelPolicyCommandAuthorizer as u, resolveDiscordShouldRequireMention as v, resolveTimestampMs as w, formatDiscordReactionEmoji as x, resolveGroupDmAllow as y };
@@ -1,11 +1,11 @@
1
- import { E as parseDiscordRetryAfterBodySeconds, O as getDiscordEndpointRuntime } from "./channel-type-D1_b5UhU.mjs";
2
- import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
1
+ import { D as getDiscordEndpointRuntime, T as parseDiscordRetryAfterBodySeconds } from "./allow-list-B_ToXtew.mjs";
2
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
3
3
  import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
4
- import { captureChannelReadAuthority, resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
4
+ import { captureChannelReadAuthority, captureEffectAuthority, resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
5
5
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
6
+ import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
6
7
  import { parseRetryAfterHeaderSeconds, resolveRetryConfig, retryAsync } from "openclaw/plugin-sdk/retry-runtime";
7
8
  import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
8
- import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
9
9
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
10
10
  //#region extensions/discord/src/error-body.ts
11
11
  const DISCORD_RESPONSE_BODY_SUMMARY_MAX_CHARS = 240;
@@ -23,11 +23,10 @@ function summarizeDiscordResponseBody(body, opts = {}) {
23
23
  return truncateUtf16Safe(redactToolPayloadText(summary), DISCORD_RESPONSE_BODY_SUMMARY_MAX_CHARS);
24
24
  }
25
25
  function isDiscordHtmlResponseBody(body, contentType) {
26
- return /\bhtml\b/i.test(contentType ?? "") || /^\s*<!doctype\s+html\b/i.test(body) || /^\s*<html\b/i.test(body);
26
+ return /\bhtml\b/i.test(contentType ?? "") || /^\s*(?:<!doctype\s+html\b|<html\b)/i.test(body);
27
27
  }
28
28
  function isDiscordRateLimitResponseBody(body) {
29
- const normalized = body.toLowerCase();
30
- return normalized.includes("error 1015") || normalized.includes("cloudflare") || normalized.includes("rate limit");
29
+ return /error 1015|cloudflare|rate limit/i.test(body);
31
30
  }
32
31
  //#endregion
33
32
  //#region extensions/discord/src/api.ts
@@ -46,23 +45,21 @@ function parseDiscordApiErrorPayload(text) {
46
45
  const trimmed = text.trim();
47
46
  if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
48
47
  try {
49
- const payload = JSON.parse(trimmed);
50
- if (payload && typeof payload === "object") return payload;
48
+ return JSON.parse(trimmed);
51
49
  } catch {
52
50
  return null;
53
51
  }
54
- return null;
55
52
  }
56
53
  function parseRetryAfterSeconds(text, response) {
57
54
  const payload = parseDiscordApiErrorPayload(text);
58
55
  const retryAfter = parseDiscordRetryAfterBodySeconds(payload?.retry_after);
59
56
  if (retryAfter !== void 0) return retryAfter;
60
57
  const header = response.headers.get("Retry-After");
61
- if (!header) return;
62
- return parseRetryAfterHeaderSeconds(header);
58
+ return header ? parseRetryAfterHeaderSeconds(header) : void 0;
63
59
  }
64
- function formatRetryAfterSeconds(value) {
65
- if (value === void 0 || !Number.isFinite(value) || value < 0) return;
60
+ function formatRetryAfterSeconds(raw) {
61
+ const value = parseDiscordRetryAfterBodySeconds(raw);
62
+ if (value === void 0) return;
66
63
  return `${value < 10 ? value.toFixed(1) : Math.round(value).toString()}s`;
67
64
  }
68
65
  function formatDiscordApiErrorTextUntrusted(text, response) {
@@ -79,13 +76,9 @@ function formatDiscordApiErrorTextUntrusted(text, response) {
79
76
  return summary;
80
77
  }
81
78
  const message = typeof payload.message === "string" && payload.message.trim() ? payload.message.trim() : "unknown error";
82
- const retryAfter = formatRetryAfterSeconds(parseDiscordRetryAfterBodySeconds(payload.retry_after));
79
+ const retryAfter = formatRetryAfterSeconds(payload.retry_after);
83
80
  return retryAfter ? `${message} (retry after ${retryAfter})` : message;
84
81
  }
85
- function formatDiscordApiErrorText(text, response) {
86
- const detail = formatDiscordApiErrorTextUntrusted(text, response);
87
- return detail ? redactToolPayloadText(detail) : detail;
88
- }
89
82
  var DiscordApiError = class extends Error {
90
83
  constructor(message, status, retryAfter) {
91
84
  super(message);
@@ -93,13 +86,8 @@ var DiscordApiError = class extends Error {
93
86
  this.retryAfter = retryAfter;
94
87
  }
95
88
  };
96
- function getDiscordApiRetryAfterMs(err, retryConfig) {
97
- if (!(err instanceof DiscordApiError) || typeof err.retryAfter !== "number") return;
98
- return Math.min(Math.max(0, err.retryAfter * 1e3), retryConfig.maxDelayMs);
99
- }
100
89
  function normalizeDiscordRequestBody(body, headers) {
101
- if (body === void 0) return;
102
- if (typeof body === "string" || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ArrayBuffer) return body;
90
+ if (body === void 0 || typeof body === "string" || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ArrayBuffer) return body;
103
91
  headers.set("Content-Type", headers.get("Content-Type") ?? "application/json");
104
92
  return JSON.stringify(body);
105
93
  }
@@ -118,6 +106,7 @@ function createDiscordRequestSignal(options) {
118
106
  }
119
107
  async function requestDiscord(path, token, options) {
120
108
  const assertReadAuthority = captureChannelReadAuthority();
109
+ const effect = captureEffectAuthority();
121
110
  const endpoint = options?.endpointRuntime === void 0 ? getDiscordEndpointRuntime() : options.endpointRuntime;
122
111
  const fetchImpl = resolveFetch(endpoint ? (input, init) => endpoint.fetch(input, init, assertReadAuthority) : options?.fetcher ?? fetch);
123
112
  if (!fetchImpl) throw new Error("fetch is not available");
@@ -129,15 +118,20 @@ async function requestDiscord(path, token, options) {
129
118
  const requestSignal = createDiscordRequestSignal(options ?? {});
130
119
  try {
131
120
  assertReadAuthority?.();
132
- const res = await fetchImpl(`${endpoint?.descriptor.restApiBaseUrl ?? DISCORD_API_BASE}${path}`, {
133
- method: options?.method ?? (body === void 0 ? "GET" : "POST"),
134
- headers,
135
- body,
136
- signal: requestSignal.signal
137
- });
121
+ const request = () => {
122
+ assertReadAuthority?.();
123
+ return fetchImpl(`${endpoint?.descriptor.restApiBaseUrl ?? DISCORD_API_BASE}${path}`, {
124
+ method: options?.method ?? (body === void 0 ? "GET" : "POST"),
125
+ headers,
126
+ body,
127
+ signal: requestSignal.signal
128
+ });
129
+ };
130
+ const res = endpoint ? await effect.run(request) : await effect.initiate(request);
138
131
  if (!res.ok) {
139
132
  const text = await readResponseTextLimited(res, DISCORD_API_ERROR_BODY_LIMIT_BYTES).catch(() => "");
140
- const detail = formatDiscordApiErrorText(text, res);
133
+ const untrustedDetail = formatDiscordApiErrorTextUntrusted(text, res);
134
+ const detail = untrustedDetail ? redactToolPayloadText(untrustedDetail) : untrustedDetail;
141
135
  const suffix = detail ? `: ${detail}` : "";
142
136
  const retryAfter = res.status === 429 ? parseRetryAfterSeconds(text, res) ?? DISCORD_API_429_FALLBACK_RETRY_AFTER_SECONDS : void 0;
143
137
  throw new DiscordApiError(`Discord API ${path} failed (${res.status})${suffix}`, res.status, retryAfter);
@@ -157,7 +151,7 @@ async function requestDiscord(path, token, options) {
157
151
  ...retryConfig,
158
152
  label: options?.label ?? path,
159
153
  shouldRetry: (err) => err instanceof DiscordApiError && err.status === 429,
160
- retryAfterMs: (err) => getDiscordApiRetryAfterMs(err, retryConfig),
154
+ retryAfterMs: (err) => err instanceof DiscordApiError && typeof err.retryAfter === "number" ? Math.min(Math.max(0, err.retryAfter * 1e3), retryConfig.maxDelayMs) : void 0,
161
155
  sleep: (ms) => sleepWithAbort(ms, options?.signal)
162
156
  });
163
157
  }