@openclaw/discord 2026.7.1 → 2026.7.2-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 (92) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{allow-list-C-MqM-B_.js → allow-list-B7qOH5xu.js} +22 -1
  3. package/dist/{api-DPRqYi4Z.js → api--f62GFaS.js} +38 -23
  4. package/dist/api.js +15 -15
  5. package/dist/approval-custom-id-NYPYj9Po.js +58 -0
  6. package/dist/{approval-handler.runtime-Bw2pZNk1.js → approval-handler.runtime-DeyIiw5I.js} +28 -23
  7. package/dist/approval-message-safety-C-8BL6Kv.js +47 -0
  8. package/dist/{audit-MLpDpN3Z.js → audit-Df2w046a.js} +3 -3
  9. package/dist/{channel-actions-D_fjVUZ_.js → channel-actions-ChpBSRcu.js} +6 -4
  10. package/dist/{channel-actions.runtime-CVHlFqQg.js → channel-actions.runtime-D8V8tyCq.js} +41 -12
  11. package/dist/channel-config-api.js +1 -1
  12. package/dist/{channel-DGWPH5u3.js → channel-nYmJ7FKQ.js} +275 -62
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel.setup-tZcpIn4e.js → channel.setup-DyihrqHH.js} +3 -3
  15. package/dist/{components-BIBizbtZ.js → components-BVrfRS-F.js} +78 -52
  16. package/dist/config-api-B18fR8M5.js +3 -0
  17. package/dist/{config-schema-BgIhO3ko.js → config-schema-_bUKcWRm.js} +42 -82
  18. package/dist/conversation-identity-BpkAzklg.js +94 -0
  19. package/dist/{directory-config-ByaUKL1f.js → directory-config-BUe-qq_C.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-BX79ImQA.js → directory-live-DZByMdPO.js} +6 -6
  22. package/dist/{doctor-Bgoxm5AN.js → doctor-bMaWXFKu.js} +5 -4
  23. package/dist/doctor-contract-api.js +1 -1
  24. package/dist/{doctor-contract-CU7lnZTF.js → doctor-contract-wId6pV41.js} +41 -37
  25. package/dist/{handle-action.guild-admin-CgJSNVui.js → handle-action.guild-admin-C_PQcBEV.js} +12 -12
  26. package/dist/index.js +1 -1
  27. package/dist/legacy-state-migrations-api.js +24 -2
  28. package/dist/{manager.runtime-DaB-CTrt.js → manager.runtime-3RVMVkyx.js} +546 -154
  29. package/dist/{message-handler-DcgvugZt.js → message-handler-CIxWq5Yh.js} +8 -9
  30. package/dist/{message-handler.preflight-DvLXiYVH.js → message-handler.preflight-BI86IbHz.js} +46 -22
  31. package/dist/{message-handler.process-Cjf30P_B.js → message-handler.process-bPS_9_tb.js} +241 -107
  32. package/dist/{message-utils-B_3yA5bN.js → message-utils-D4j07taS.js} +2 -3
  33. package/dist/{outbound-adapter-82RWi_SR.js → outbound-adapter-Czny0Fae.js} +16 -68
  34. package/dist/outbound-components-oJKLj62G.js +99 -0
  35. package/dist/pluralkit-UXq1OyUI.js +40 -0
  36. package/dist/{probe-D1ImJEo2.js → probe-DxjMBeVG.js} +1 -1
  37. package/dist/{probe.runtime-B1dsEeBD.js → probe.runtime-Bc8Oo6e8.js} +1 -1
  38. package/dist/{provider-BkfbC9FQ.js → provider-DWvDNb4J.js} +702 -236
  39. package/dist/{provider-session.runtime-BDjyFw6Y.js → provider-session.runtime-D5pCGibh.js} +3 -3
  40. package/dist/provider.runtime-C2iIfbTs.js +2 -0
  41. package/dist/{resolve-allowlist-common-CWbE8XlH.js → resolve-allowlist-common-B9S8LLBd.js} +5 -5
  42. package/dist/{resolve-channels-CcpvWoRa.js → resolve-channels-BrkWOKdG.js} +7 -7
  43. package/dist/{resolve-users-DJamun9s.js → resolve-users-OjrXXvJP.js} +5 -5
  44. package/dist/{runtime-C6rRmQ6l.js → runtime-C618lis9.js} +223 -33
  45. package/dist/runtime-api.actions.js +2 -2
  46. package/dist/runtime-api.js +23 -23
  47. package/dist/runtime-api.lookup.js +5 -5
  48. package/dist/runtime-api.monitor-DSbfGnzS.js +6 -0
  49. package/dist/runtime-api.monitor.js +7 -7
  50. package/dist/runtime-api.send.js +5 -5
  51. package/dist/runtime-api.threads.js +5 -5
  52. package/dist/{send-iJ0B4bAX.js → send-BFpa_DHn.js} +21 -12
  53. package/dist/{send.components-D0I39utR.js → send.components-BaXQJKWT.js} +15 -10
  54. package/dist/{send.outbound-Bj61Q3a0.js → send.outbound-BlA6VzSE.js} +22 -12
  55. package/dist/{send.receipt-BpBQED_5.js → send.receipt-aegk69an.js} +59 -75
  56. package/dist/{send.shared-wAwMhHDN.js → send.shared-Xz7IMVpu.js} +44 -20
  57. package/dist/{sender-identity-CEsfDF0L.js → sender-identity-C62lBe18.js} +1 -1
  58. package/dist/session-binding-contract-api.js +1 -1
  59. package/dist/setup-plugin-api.js +1 -1
  60. package/dist/{shared-BQVKfnev.js → shared-BLYBt5SA.js} +3 -3
  61. package/dist/{subagent-hooks-B-bJxxGg.js → subagent-hooks-DKJ4p-Oy.js} +2 -2
  62. package/dist/subagent-hooks-api.js +1 -1
  63. package/dist/{system-events-DVnqSyTt.js → system-events-DKwfE-2z.js} +2 -2
  64. package/dist/{target-resolver-CwHaSLt3.js → target-resolver-BQyEHyeS.js} +2 -2
  65. package/dist/targets-DvItNIvP.js +3 -0
  66. package/dist/thread-binding-api-BDZJD4na.js +4 -0
  67. package/dist/thread-binding-api.js +1 -3
  68. package/dist/{thread-bindings-DsTxqu3E.js → thread-bindings-BbzjUuNB.js} +26 -43
  69. package/dist/{thread-bindings.discord-api-BjIf9lGs.js → thread-bindings.discord-api-DJD3qwIN.js} +8 -10
  70. package/dist/{thread-bindings.manager-Bwmtqc3E.js → thread-bindings.manager-BIBm6bQd.js} +4 -4
  71. package/dist/{thread-bindings.session-updates-Chs3HgGa.js → thread-bindings.session-updates-eRcJlzIU.js} +1 -1
  72. package/dist/{thread-bindings.state-iSWjCYjB.js → thread-bindings.state-9VAxhVf6.js} +3 -14
  73. package/dist/{timeouts-C5TBc_9x.js → timeouts-ZRc9Lw4y.js} +3 -15
  74. package/dist/timeouts.js +1 -1
  75. package/dist/{transcripts-source-BvvM5Ns9.js → transcripts-source-CLZ5X8fh.js} +1 -1
  76. package/dist/transcripts-source-api.js +1 -1
  77. package/dist/{typing-BVcJgILg.js → typing-BnYT7Szq.js} +2 -2
  78. package/node_modules/p-map/index.d.ts +155 -0
  79. package/node_modules/p-map/index.js +284 -0
  80. package/node_modules/p-map/license +9 -0
  81. package/node_modules/p-map/package.json +57 -0
  82. package/node_modules/p-map/readme.md +190 -0
  83. package/npm-shrinkwrap.json +16 -3
  84. package/openclaw.plugin.json +70 -2
  85. package/package.json +6 -4
  86. package/dist/config-api-JiPdJeb0.js +0 -2
  87. package/dist/conversation-identity-DWC5d8Sy.js +0 -272
  88. package/dist/pluralkit-DcD-3EcL.js +0 -24
  89. package/dist/preview-streaming-DXT8oJdo.js +0 -8
  90. package/dist/provider.runtime-WrJXikSv.js +0 -2
  91. package/dist/runtime-api.monitor-C2_WVtJf.js +0 -6
  92. package/dist/targets-69w7c3vU.js +0 -3
@@ -1,4 +1,5 @@
1
- import { n as buildChannelConfigSchema, t as DiscordConfigSchema } from "./config-api-JiPdJeb0.js";
1
+ import { n as buildChannelConfigSchema, t as DiscordConfigSchema } from "./config-api-B18fR8M5.js";
2
+ import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core";
2
3
  //#endregion
3
4
  //#region extensions/discord/src/config-schema.ts
4
5
  const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema, { uiHints: {
@@ -6,46 +7,28 @@ const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema,
6
7
  label: "Discord",
7
8
  help: "Discord channel provider configuration for bot auth, retry policy, streaming, thread bindings, and optional voice capabilities. Keep privileged intents and advanced features disabled unless needed."
8
9
  },
9
- dmPolicy: {
10
- label: "Discord DM Policy",
11
- help: "Direct message access control (\"pairing\" recommended). \"open\" requires channels.discord.allowFrom=[\"*\"]."
12
- },
13
- "dm.policy": {
14
- label: "Discord DM Policy",
15
- help: "Direct message access control (\"pairing\" recommended). \"open\" requires channels.discord.allowFrom=[\"*\"] (legacy: channels.discord.dm.allowFrom)."
16
- },
17
- configWrites: {
18
- label: "Discord Config Writes",
19
- help: "Allow Discord to write config in response to channel events/commands (default: true)."
20
- },
21
- mentionPatterns: {
22
- label: "Discord Mention Pattern Policy",
23
- help: "Scopes configured groupChat mentionPatterns to selected Discord channel IDs. Native Discord @mentions still trigger even when regex patterns are denied."
24
- },
25
- "mentionPatterns.mode": {
26
- label: "Discord Mention Pattern Mode",
27
- help: "\"allow\" enables configured regex mention patterns unless denyIn matches; \"deny\" disables them unless allowIn matches."
28
- },
29
- "mentionPatterns.allowIn": {
30
- label: "Discord Mention Pattern Allowlist",
31
- help: "Discord channel IDs where configured regex mention patterns are enabled when mode is deny."
32
- },
33
- "mentionPatterns.denyIn": {
34
- label: "Discord Mention Pattern Denylist",
35
- help: "Discord channel IDs where configured regex mention patterns are disabled. Native @mentions still trigger."
36
- },
10
+ ...createChannelConfigUiHints({
11
+ channelLabel: "Discord",
12
+ dmPolicy: {
13
+ channelKey: "discord",
14
+ includeLegacyNestedPolicy: true,
15
+ legacyNestedPolicyOrder: "after"
16
+ },
17
+ configWrites: true,
18
+ mentionPatterns: {
19
+ targetDescription: "Discord channel IDs",
20
+ policyNote: "Native Discord @mentions still trigger even when regex patterns are denied.",
21
+ denyNote: "Native @mentions still trigger."
22
+ }
23
+ }),
37
24
  proxy: {
38
25
  label: "Discord Proxy URL",
39
26
  help: "Proxy URL for Discord gateway + API requests (app-id lookup and allowlist resolution). Set per account via channels.discord.accounts.<id>.proxy."
40
27
  },
41
- "commands.native": {
42
- label: "Discord Native Commands",
43
- help: "Override native commands for Discord (bool or \"auto\")."
44
- },
45
- "commands.nativeSkills": {
46
- label: "Discord Native Skill Commands",
47
- help: "Override native skill commands for Discord (bool or \"auto\")."
48
- },
28
+ ...createChannelConfigUiHints({
29
+ channelLabel: "Discord",
30
+ nativeCommands: true
31
+ }),
49
32
  streaming: {
50
33
  label: "Discord Streaming Mode",
51
34
  help: "Unified Discord stream preview mode: \"off\" | \"partial\" | \"block\" | \"progress\". \"progress\" keeps a single editable progress draft until final delivery. Legacy boolean/streamMode keys are auto-mapped."
@@ -86,50 +69,11 @@ const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema,
86
69
  label: "Discord Draft Command Text",
87
70
  help: "Command/exec detail in preview tool-progress lines: \"raw\" preserves released behavior; \"status\" shows only the tool label."
88
71
  },
89
- "streaming.progress.label": {
90
- label: "Discord Progress Label",
91
- help: "Initial progress draft title. Use \"auto\" for built-in single-word labels, a custom string, or false to hide the title."
92
- },
93
- "streaming.progress.labels": {
94
- label: "Discord Progress Label Pool",
95
- help: "Candidate labels for streaming.progress.label=\"auto\". Leave unset to use OpenClaw built-in progress labels."
96
- },
97
- "streaming.progress.maxLines": {
98
- label: "Discord Progress Max Lines",
99
- help: "Maximum number of compact progress lines to keep below the draft label (default: 8)."
100
- },
101
- "streaming.progress.maxLineChars": {
102
- label: "Discord Progress Max Line Chars",
103
- help: "Maximum characters per compact progress line before truncation (default: 120). Prose cuts at word boundaries; commands and paths keep useful suffixes."
104
- },
105
- "streaming.progress.toolProgress": {
106
- label: "Discord Progress Tool Lines",
107
- help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery."
108
- },
109
- "streaming.progress.commentary": {
110
- label: "Discord Progress Commentary",
111
- help: "Show assistant commentary/preamble text in the temporary progress draft. Final answer delivery is unchanged."
112
- },
113
- "streaming.progress.commandText": {
114
- label: "Discord Progress Command Text",
115
- help: "Command/exec detail in progress draft lines: \"raw\" preserves released behavior; \"status\" shows only the tool label."
116
- },
117
- "retry.attempts": {
118
- label: "Discord Retry Attempts",
119
- help: "Max retry attempts for outbound Discord API calls (default: 3)."
120
- },
121
- "retry.minDelayMs": {
122
- label: "Discord Retry Min Delay (ms)",
123
- help: "Minimum retry delay in ms for Discord outbound calls."
124
- },
125
- "retry.maxDelayMs": {
126
- label: "Discord Retry Max Delay (ms)",
127
- help: "Maximum retry delay cap in ms for Discord outbound calls."
128
- },
129
- "retry.jitter": {
130
- label: "Discord Retry Jitter",
131
- help: "Jitter factor (0-1) applied to Discord retry delays."
132
- },
72
+ ...createChannelConfigUiHints({
73
+ channelLabel: "Discord",
74
+ progress: { includeCommentary: true },
75
+ retry: true
76
+ }),
133
77
  maxLinesPerMessage: {
134
78
  label: "Discord Max Lines Per Message",
135
79
  help: "Soft max line count per Discord message (default: 17)."
@@ -212,7 +156,7 @@ const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema,
212
156
  },
213
157
  "voice.model": {
214
158
  label: "Discord Voice Model",
215
- help: "Optional LLM model override for Discord voice channel responses and realtime agent consults (for example openai/gpt-5.5). Leave unset to inherit the routed agent model."
159
+ help: "Optional LLM model override for Discord voice channel responses and realtime agent consults (for example openai/gpt-5.6-sol). Leave unset to inherit the routed agent model."
216
160
  },
217
161
  "voice.mode": {
218
162
  label: "Discord Voice Mode",
@@ -359,6 +303,22 @@ const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema,
359
303
  label: "Discord Auto Presence Exhausted Text",
360
304
  help: "Optional custom status text while runtime detects exhausted/unavailable model quota (dnd). Supports {reason} template placeholder."
361
305
  },
306
+ "guilds.*.presenceEvents": {
307
+ label: "Discord Online Presence Events",
308
+ help: "Route selected human offline-to-online transitions into the configured guild channel as agent system events. Requires the Guild Presences privileged intent and an enabled agent heartbeat."
309
+ },
310
+ "guilds.*.presenceEvents.enabled": {
311
+ label: "Discord Online Presence Events Enabled",
312
+ help: "Enable online-presence agent wakes for this guild. Defaults to true when presenceEvents is configured."
313
+ },
314
+ "guilds.*.presenceEvents.channelId": {
315
+ label: "Discord Online Presence Target Channel",
316
+ help: "Numeric Discord channel ID whose routed agent session receives online-presence events and greeting delivery."
317
+ },
318
+ "guilds.*.presenceEvents.users": {
319
+ label: "Discord Online Presence User IDs",
320
+ help: "Optional immutable Discord user ID allowlist. Omit to include all human members in the guild."
321
+ },
362
322
  activityType: {
363
323
  label: "Discord Presence Activity Type",
364
324
  help: "Discord presence activity type (0=Playing,1=Streaming,2=Listening,3=Watching,4=Custom,5=Competing)."
@@ -0,0 +1,94 @@
1
+ import { Jt as parseDiscordTarget } from "./send.receipt-aegk69an.js";
2
+ import { s as resolveDiscordAccount } from "./accounts-P19jm1lf.js";
3
+ import { normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { createChannelApproverDmTargetResolver, createChannelNativeOriginTargetResolver } from "openclaw/plugin-sdk/approval-native-runtime";
5
+ import { getExecApprovalReplyMetadata, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
6
+ import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
7
+ import { createApproverRestrictedNativeApprovalCapability } from "openclaw/plugin-sdk/approval-delivery-runtime";
8
+ //#region extensions/discord/src/exec-approvals.ts
9
+ function normalizeDiscordApproverId(value) {
10
+ const trimmed = value.trim();
11
+ if (!trimmed) return;
12
+ if (/^\d+$/.test(trimmed)) return trimmed;
13
+ try {
14
+ const target = parseDiscordTarget(trimmed);
15
+ return target?.kind === "user" ? target.id : void 0;
16
+ } catch {
17
+ return;
18
+ }
19
+ }
20
+ function resolveDiscordOwnerApprovers(cfg) {
21
+ const ownerAllowFrom = cfg.commands?.ownerAllowFrom;
22
+ if (!Array.isArray(ownerAllowFrom) || ownerAllowFrom.length === 0) return [];
23
+ return resolveApprovalApprovers({
24
+ explicit: ownerAllowFrom,
25
+ normalizeApprover: (value) => normalizeDiscordApproverId(String(value))
26
+ });
27
+ }
28
+ function getDiscordExecApprovalApprovers(params) {
29
+ return resolveApprovalApprovers({
30
+ explicit: params.configOverride?.approvers ?? resolveDiscordAccount(params).config.execApprovals?.approvers ?? resolveDiscordOwnerApprovers(params.cfg),
31
+ normalizeApprover: (value) => normalizeDiscordApproverId(String(value))
32
+ });
33
+ }
34
+ function isDiscordExecApprovalClientEnabled(params) {
35
+ return isChannelExecApprovalClientEnabledFromConfig({
36
+ enabled: (params.configOverride ?? resolveDiscordAccount(params).config.execApprovals)?.enabled,
37
+ approverCount: getDiscordExecApprovalApprovers({
38
+ cfg: params.cfg,
39
+ accountId: params.accountId,
40
+ configOverride: params.configOverride
41
+ }).length
42
+ });
43
+ }
44
+ function isDiscordExecApprovalApprover(params) {
45
+ const senderId = params.senderId?.trim();
46
+ if (!senderId) return false;
47
+ return getDiscordExecApprovalApprovers({
48
+ cfg: params.cfg,
49
+ accountId: params.accountId,
50
+ configOverride: params.configOverride
51
+ }).includes(senderId);
52
+ }
53
+ function shouldSuppressLocalDiscordExecApprovalPrompt(params) {
54
+ const metadata = getExecApprovalReplyMetadata(params.payload);
55
+ const config = resolveDiscordAccount(params).config.execApprovals;
56
+ return params.hint?.kind === "approval-pending" && params.hint.nativeRouteActive === true && isDiscordExecApprovalClientEnabled(params) && metadata !== null && matchesApprovalRequestFilters({
57
+ request: {
58
+ agentId: metadata.agentId,
59
+ sessionKey: metadata.sessionKey
60
+ },
61
+ agentFilter: config?.agentFilter,
62
+ sessionFilter: config?.sessionFilter
63
+ });
64
+ }
65
+ //#endregion
66
+ //#region extensions/discord/src/conversation-identity.ts
67
+ function normalizeDiscordTarget(raw, defaultKind) {
68
+ const trimmed = normalizeOptionalString(raw);
69
+ if (!trimmed) return;
70
+ return parseDiscordTarget(trimmed, { defaultKind })?.normalized;
71
+ }
72
+ function buildDiscordConversationIdentity(kind, rawId) {
73
+ const trimmed = normalizeOptionalString(rawId);
74
+ return trimmed ? `${kind}:${trimmed}` : void 0;
75
+ }
76
+ function resolveDiscordConversationIdentity(params) {
77
+ return params.isDirectMessage ? buildDiscordConversationIdentity("user", params.userId) : buildDiscordConversationIdentity("channel", params.channelId);
78
+ }
79
+ function resolveDiscordCurrentConversationIdentity(params) {
80
+ if (normalizeOptionalLowercaseString(params.chatType) === "direct") {
81
+ const senderTarget = normalizeDiscordTarget(params.from, "user");
82
+ if (senderTarget?.startsWith("user:")) return senderTarget;
83
+ }
84
+ for (const candidate of [
85
+ params.originatingTo,
86
+ params.commandTo,
87
+ params.fallbackTo
88
+ ]) {
89
+ const target = normalizeDiscordTarget(candidate, "channel");
90
+ if (target) return target;
91
+ }
92
+ }
93
+ //#endregion
94
+ export { isDiscordExecApprovalClientEnabled as a, createChannelApproverDmTargetResolver as c, matchesApprovalRequestFilters as d, isDiscordExecApprovalApprover as i, createChannelNativeOriginTargetResolver as l, resolveDiscordCurrentConversationIdentity as n, shouldSuppressLocalDiscordExecApprovalPrompt as o, getDiscordExecApprovalApprovers as r, createApproverRestrictedNativeApprovalCapability as s, resolveDiscordConversationIdentity as t, isChannelExecApprovalClientEnabledFromConfig as u };
@@ -1,4 +1,4 @@
1
- import { Jt as __exportAll } from "./send.receipt-BpBQED_5.js";
1
+ import { Xt as __exportAll } from "./send.receipt-aegk69an.js";
2
2
  import { a as mergeDiscordAccountConfig, c as resolveDiscordAccountAllowFrom, o as resolveDefaultDiscordAccountId } from "./accounts-P19jm1lf.js";
3
3
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
4
4
  import { createResolvedDirectoryEntriesLister } from "openclaw/plugin-sdk/directory-config-runtime";
@@ -1,4 +1,4 @@
1
- import { n as listDiscordDirectoryGroupsFromConfig, r as listDiscordDirectoryPeersFromConfig } from "./directory-config-ByaUKL1f.js";
1
+ import { n as listDiscordDirectoryGroupsFromConfig, r as listDiscordDirectoryPeersFromConfig } from "./directory-config-BUe-qq_C.js";
2
2
  //#region extensions/discord/directory-contract-api.ts
3
3
  const discordDirectoryContractPlugin = {
4
4
  id: "discord",
@@ -1,7 +1,7 @@
1
- import { Jt as __exportAll } from "./send.receipt-BpBQED_5.js";
1
+ import { Xt as __exportAll } from "./send.receipt-aegk69an.js";
2
2
  import { p as normalizeDiscordToken, s as resolveDiscordAccount } from "./accounts-P19jm1lf.js";
3
- import { n as fetchDiscord } from "./api-DPRqYi4Z.js";
4
- import { a as normalizeDiscordSlug } from "./allow-list-C-MqM-B_.js";
3
+ import { r as fetchDiscord, t as DISCORD_DIRECTORY_LOOKUP_TIMEOUT_MS } from "./api--f62GFaS.js";
4
+ import { a as normalizeDiscordSlug } from "./allow-list-B7qOH5xu.js";
5
5
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/routing";
7
7
  //#region extensions/discord/src/directory-cache.ts
@@ -88,7 +88,7 @@ function resolveDiscordDirectoryAccess(params) {
88
88
  };
89
89
  }
90
90
  async function listDiscordGuilds(token) {
91
- return (await fetchDiscord("/users/@me/guilds", token)).filter((guild) => guild.id && guild.name);
91
+ return (await fetchDiscord("/users/@me/guilds", token, fetch, { timeoutMs: DISCORD_DIRECTORY_LOOKUP_TIMEOUT_MS })).filter((guild) => guild.id && guild.name);
92
92
  }
93
93
  async function listDiscordDirectoryGroupsLive(params) {
94
94
  const access = resolveDiscordDirectoryAccess(params);
@@ -97,7 +97,7 @@ async function listDiscordDirectoryGroupsLive(params) {
97
97
  const guilds = await listDiscordGuilds(token);
98
98
  const rows = [];
99
99
  for (const guild of guilds) {
100
- const channels = await fetchDiscord(`/guilds/${guild.id}/channels`, token);
100
+ const channels = await fetchDiscord(`/guilds/${guild.id}/channels`, token, fetch, { timeoutMs: DISCORD_DIRECTORY_LOOKUP_TIMEOUT_MS });
101
101
  for (const channel of channels) {
102
102
  const name = channel.name?.trim();
103
103
  if (!name) continue;
@@ -127,7 +127,7 @@ async function listDiscordDirectoryPeersLive(params) {
127
127
  query,
128
128
  limit: String(Math.min(limit, 100))
129
129
  });
130
- const members = await fetchDiscord(`/guilds/${guild.id}/members/search?${paramsObj.toString()}`, token);
130
+ const members = await fetchDiscord(`/guilds/${guild.id}/members/search?${paramsObj.toString()}`, token, fetch, { timeoutMs: DISCORD_DIRECTORY_LOOKUP_TIMEOUT_MS });
131
131
  for (const member of members) {
132
132
  const user = member.user;
133
133
  if (!user?.id) continue;
@@ -1,9 +1,10 @@
1
1
  import { t as inspectDiscordAccount } from "./account-inspect-DDZtT5C8.js";
2
2
  import { o as resolveDefaultDiscordAccountId } from "./accounts-P19jm1lf.js";
3
- import { r as DISCORD_LEGACY_CONFIG_RULES } from "./shared-BQVKfnev.js";
4
- import { n as normalizeCompatibilityConfig } from "./doctor-contract-CU7lnZTF.js";
3
+ import { r as DISCORD_LEGACY_CONFIG_RULES } from "./shared-BLYBt5SA.js";
4
+ import { n as normalizeCompatibilityConfig } from "./doctor-contract-wId6pV41.js";
5
5
  import { t as isDiscordMutableAllowEntry } from "./security-doctor-uUo8hTD5.js";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
7
8
  import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime-doctor";
8
9
  //#region extensions/discord/src/doctor.ts
9
10
  function asObjectRecord$1(value) {
@@ -121,11 +122,11 @@ function collectDiscordNumericIdWarnings(params) {
121
122
  else repairableHits.push(...hits);
122
123
  const lines = [];
123
124
  if (repairableHits.length > 0) {
124
- const sample = repairableHits[0];
125
+ const sample = expectDefined(repairableHits.at(0), "non-empty repairable Discord ID hits");
125
126
  lines.push(`- Discord allowlists contain ${repairableHits.length} numeric ${repairableHits.length === 1 ? "entry" : "entries"} (e.g. ${sanitizeForLog(sample.path)}=${sanitizeForLog(String(sample.entry))}).`, `- Discord IDs must be strings; run "${params.doctorFixCommand}" to convert numeric IDs to quoted strings.`);
126
127
  }
127
128
  if (blockedHits.length > 0) {
128
- const sample = blockedHits[0];
129
+ const sample = expectDefined(blockedHits.at(0), "non-empty blocked Discord ID hits");
129
130
  lines.push(`- Discord allowlists contain ${blockedHits.length} numeric ${blockedHits.length === 1 ? "entry" : "entries"} in lists that cannot be auto-repaired (e.g. ${sanitizeForLog(sample.path)}).`, `- These lists include invalid or precision-losing numeric IDs; manually quote the original values in your config file, then rerun "${params.doctorFixCommand}".`);
130
131
  }
131
132
  return lines;
@@ -1,2 +1,2 @@
1
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CU7lnZTF.js";
1
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-wId6pV41.js";
2
2
  export { legacyConfigRules, normalizeCompatibilityConfig };
@@ -1,6 +1,5 @@
1
- import { t as resolveDiscordPreviewStreamMode } from "./preview-streaming-DXT8oJdo.js";
2
1
  import { isSupportedRealtimeVoiceActivationName, normalizeRealtimeVoiceActivationNamePrefix } from "openclaw/plugin-sdk/realtime-voice";
3
- import { asObjectRecord, normalizeLegacyChannelAliases } from "openclaw/plugin-sdk/runtime-doctor";
2
+ import { asObjectRecord, defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor";
4
3
  //#region extensions/discord/src/doctor-contract.ts
5
4
  const LEGACY_TTS_PROVIDER_KEYS = [
6
5
  "openai",
@@ -8,6 +7,33 @@ const LEGACY_TTS_PROVIDER_KEYS = [
8
7
  "microsoft",
9
8
  "edge"
10
9
  ];
10
+ const streamingAliasMigration = defineChannelAliasMigration({
11
+ channelId: "discord",
12
+ streaming: {
13
+ defaultMode: "off",
14
+ absentObjectDefault: "progress",
15
+ includePreviewChunk: true
16
+ },
17
+ accountStreamingReplacesRoot: true,
18
+ dm: {
19
+ root: true,
20
+ accounts: true
21
+ },
22
+ normalizeAccountExtra: ({ account, pathPrefix, changes }) => {
23
+ const accountVoice = asObjectRecord(account.voice);
24
+ if (!accountVoice || !migrateLegacyTtsConfig(asObjectRecord(accountVoice.tts), `${pathPrefix}.voice.tts`, changes)) return {
25
+ entry: account,
26
+ changed: false
27
+ };
28
+ return {
29
+ entry: {
30
+ ...account,
31
+ voice: accountVoice
32
+ },
33
+ changed: true
34
+ };
35
+ }
36
+ });
11
37
  function hasLegacyTtsProviderKeys(value) {
12
38
  const tts = asObjectRecord(value);
13
39
  if (!tts) return false;
@@ -349,45 +375,23 @@ const legacyConfigRules = [
349
375
  ],
350
376
  message: "channels.discord.accounts.<id>.voice.realtime.wakeNames entries longer than two words are unsupported; use one- or two-word activation names. Run \"openclaw doctor --fix\".",
351
377
  match: hasUnsupportedDiscordAccountRealtimeWakeNames
352
- }
378
+ },
379
+ ...streamingAliasMigration.legacyConfigRules
353
380
  ];
354
381
  function normalizeCompatibilityConfig({ cfg }) {
355
- const rawEntry = asObjectRecord(cfg.channels?.discord);
382
+ const changes = [];
383
+ const bindingsToAdd = [];
384
+ const aliases = streamingAliasMigration.normalizeChannelConfig({
385
+ cfg,
386
+ changes
387
+ });
388
+ const rawEntry = asObjectRecord(aliases.config.channels?.discord);
356
389
  if (!rawEntry) return {
357
390
  config: cfg,
358
391
  changes: []
359
392
  };
360
- const changes = [];
361
- let updated;
362
- let changed;
363
- const bindingsToAdd = [];
364
- const aliases = normalizeLegacyChannelAliases({
365
- entry: rawEntry,
366
- pathPrefix: "channels.discord",
367
- changes,
368
- normalizeDm: true,
369
- normalizeAccountDm: true,
370
- resolveStreamingOptions: (entry) => ({
371
- resolvedMode: resolveDiscordPreviewStreamMode(entry),
372
- includePreviewChunk: true
373
- }),
374
- normalizeAccountExtra: ({ account, pathPrefix }) => {
375
- const accountVoice = asObjectRecord(account.voice);
376
- if (!accountVoice || !migrateLegacyTtsConfig(asObjectRecord(accountVoice.tts), `${pathPrefix}.voice.tts`, changes)) return {
377
- entry: account,
378
- changed: false
379
- };
380
- return {
381
- entry: {
382
- ...account,
383
- voice: accountVoice
384
- },
385
- changed: true
386
- };
387
- }
388
- });
389
- updated = aliases.entry;
390
- changed = aliases.changed;
393
+ let updated = rawEntry;
394
+ let changed = aliases.config !== cfg;
391
395
  const guildAliases = normalizeDiscordGuildChannelAllowAliases({
392
396
  entry: updated,
393
397
  pathPrefix: "channels.discord",
@@ -460,9 +464,9 @@ function normalizeCompatibilityConfig({ cfg }) {
460
464
  };
461
465
  return {
462
466
  config: {
463
- ...cfg,
467
+ ...aliases.config,
464
468
  channels: {
465
- ...cfg.channels,
469
+ ...aliases.config.channels,
466
470
  discord: updated
467
471
  },
468
472
  bindings: bindingsToAdd.length > 0 ? [...cfg.bindings ?? [], ...bindingsToAdd] : cfg.bindings
@@ -1,6 +1,6 @@
1
- import { a as readDiscordChannelCreateParams, n as isDiscordModerationAction, o as readDiscordChannelEditParams, r as readDiscordModerationCommand, s as readDiscordChannelMoveParams, t as handleDiscordAction } from "./runtime-C6rRmQ6l.js";
1
+ import { a as readDiscordChannelCreateParams, n as isDiscordModerationAction, o as readDiscordChannelEditParams, r as readDiscordModerationCommand, s as readDiscordChannelMoveParams, t as handleDiscordAction } from "./runtime-C618lis9.js";
2
2
  import "./action-runtime-api.js";
3
- import { n as isTrustedRequesterGuildAdminAction } from "./channel-actions-D_fjVUZ_.js";
3
+ import { n as isTrustedRequesterGuildAdminAction } from "./channel-actions-ChpBSRcu.js";
4
4
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { readNonNegativeIntegerParam, readPositiveIntegerParam, readStringArrayParam, readStringParam } from "openclaw/plugin-sdk/agent-runtime";
6
6
  //#region extensions/discord/src/actions/handle-action.guild-admin.ts
@@ -13,7 +13,7 @@ function senderParam(senderUserId) {
13
13
  return senderUserId ? { senderUserId } : {};
14
14
  }
15
15
  async function tryHandleDiscordMessageActionGuildAdmin(params) {
16
- const { ctx, resolveChannelId } = params;
16
+ const { ctx, resolveChannelId, readPolicyOptions } = params;
17
17
  const { action, params: actionParams, cfg } = ctx;
18
18
  const accountId = ctx.accountId ?? readStringParam(actionParams, "accountId");
19
19
  const senderUserId = readDiscordRequesterSenderId(ctx);
@@ -25,7 +25,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
25
25
  accountId: accountId ?? void 0,
26
26
  guildId,
27
27
  userId
28
- }, cfg);
28
+ }, cfg, readPolicyOptions);
29
29
  }
30
30
  if (action === "role-info") {
31
31
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -33,7 +33,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
33
33
  action: "roleInfo",
34
34
  accountId: accountId ?? void 0,
35
35
  guildId
36
- }, cfg);
36
+ }, cfg, readPolicyOptions);
37
37
  }
38
38
  if (action === "emoji-list") {
39
39
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -41,7 +41,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
41
41
  action: "emojiList",
42
42
  accountId: accountId ?? void 0,
43
43
  guildId
44
- }, cfg);
44
+ }, cfg, readPolicyOptions);
45
45
  }
46
46
  if (action === "emoji-upload") {
47
47
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -100,7 +100,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
100
100
  action: "channelInfo",
101
101
  accountId: accountId ?? void 0,
102
102
  channelId
103
- }, cfg);
103
+ }, cfg, readPolicyOptions);
104
104
  }
105
105
  if (action === "channel-list") {
106
106
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -108,7 +108,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
108
108
  action: "channelList",
109
109
  accountId: accountId ?? void 0,
110
110
  guildId
111
- }, cfg);
111
+ }, cfg, readPolicyOptions);
112
112
  }
113
113
  if (action === "channel-create") {
114
114
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -200,7 +200,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
200
200
  accountId: accountId ?? void 0,
201
201
  guildId,
202
202
  userId
203
- }, cfg);
203
+ }, cfg, readPolicyOptions);
204
204
  }
205
205
  if (action === "event-list") {
206
206
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -208,7 +208,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
208
208
  action: "eventList",
209
209
  accountId: accountId ?? void 0,
210
210
  guildId
211
- }, cfg);
211
+ }, cfg, readPolicyOptions);
212
212
  }
213
213
  if (action === "event-create") {
214
214
  const guildId = readStringParam(actionParams, "guildId", { required: true });
@@ -270,7 +270,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
270
270
  includeArchived,
271
271
  before,
272
272
  limit
273
- }, cfg);
273
+ }, cfg, readPolicyOptions);
274
274
  }
275
275
  if (action === "thread-reply") {
276
276
  const content = readStringParam(actionParams, "message", { required: true });
@@ -305,7 +305,7 @@ async function tryHandleDiscordMessageActionGuildAdmin(params) {
305
305
  authorId: readStringParam(actionParams, "authorId"),
306
306
  authorIds: readStringArrayParam(actionParams, "authorIds"),
307
307
  limit: readPositiveIntegerParam(actionParams, "limit")
308
- }, cfg);
308
+ }, cfg, readPolicyOptions);
309
309
  }
310
310
  }
311
311
  //#endregion
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { registerDiscordSubagentHooks } from "./subagent-hooks-api.js";
2
- import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-BvvM5Ns9.js";
2
+ import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-CLZ5X8fh.js";
3
3
  import "./transcripts-source-api.js";
4
4
  import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
5
5
  //#region extensions/discord/index.ts
@@ -1,4 +1,4 @@
1
- import { a as THREAD_BINDINGS_MAX_ENTRIES, f as normalizePersistedBinding, j as toBindingRecordKey, o as THREAD_BINDINGS_NAMESPACE } from "./thread-bindings.state-iSWjCYjB.js";
1
+ import { a as THREAD_BINDINGS_MAX_ENTRIES, f as normalizePersistedBinding, j as toBindingRecordKey, o as THREAD_BINDINGS_NAMESPACE } from "./thread-bindings.state-9VAxhVf6.js";
2
2
  import { MAX_DATE_TIMESTAMP_MS, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
3
3
  import { createHash } from "node:crypto";
4
4
  import path from "node:path";
@@ -69,6 +69,28 @@ function legacyUpdatedAtForIndex(updatedAt, index, total) {
69
69
  const baseMs = timestampMs(updatedAt);
70
70
  return timestampMsToIsoString(Math.min(baseMs + Math.max(0, total), MAX_DATE_TIMESTAMP_MS) - Math.max(0, index)) ?? timestampMsToIsoString(baseMs) ?? timestampMsToIsoString(Math.max(0, total - index)) ?? "1970-01-01T00:00:00.000Z";
71
71
  }
72
+ function readFiniteNumberField(entry, key) {
73
+ const value = entry[key];
74
+ return typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : void 0;
75
+ }
76
+ function upgradeLegacyThreadBindingShape(rawEntry) {
77
+ if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) return rawEntry;
78
+ const entry = { ...rawEntry };
79
+ if (entry.targetSessionKey === void 0 && typeof entry.sessionKey === "string") entry.targetSessionKey = entry.sessionKey;
80
+ delete entry.sessionKey;
81
+ const expiresAt = readFiniteNumberField(entry, "expiresAt");
82
+ delete entry.expiresAt;
83
+ if (entry.idleTimeoutMs === void 0 && entry.maxAgeMs === void 0 && expiresAt !== void 0) {
84
+ entry.idleTimeoutMs = 0;
85
+ if (expiresAt <= 0) entry.maxAgeMs = 0;
86
+ else {
87
+ const boundAt = readFiniteNumberField(entry, "boundAt") ?? 0;
88
+ const lastActivityAt = readFiniteNumberField(entry, "lastActivityAt") ?? 0;
89
+ entry.maxAgeMs = Math.max(1, expiresAt - Math.max(0, boundAt > 0 ? boundAt : lastActivityAt));
90
+ }
91
+ }
92
+ return entry;
93
+ }
72
94
  const detectDiscordLegacyStateMigrations = ({ stateDir }) => {
73
95
  const plans = [];
74
96
  const modelPickerSourcePath = path.join(stateDir, "discord", "model-picker-preferences.json");
@@ -119,7 +141,7 @@ const detectDiscordLegacyStateMigrations = ({ stateDir }) => {
119
141
  if (store?.version !== 1 || !store.bindings || typeof store.bindings !== "object") throw new Error("legacy Discord thread bindings store must have version 1 bindings");
120
142
  const out = [];
121
143
  for (const [rawKey, rawEntry] of Object.entries(store.bindings)) {
122
- const normalized = normalizePersistedBinding(rawKey, rawEntry);
144
+ const normalized = normalizePersistedBinding(rawKey, upgradeLegacyThreadBindingShape(rawEntry));
123
145
  if (normalized) out.push({
124
146
  key: toBindingRecordKey(normalized),
125
147
  value: normalized