@openclaw/discord 2026.7.1-beta.6 → 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.
- package/dist/action-runtime-api.js +1 -1
- package/dist/{allow-list-C-MqM-B_.js → allow-list-B7qOH5xu.js} +22 -1
- package/dist/{api-DPRqYi4Z.js → api--f62GFaS.js} +38 -23
- package/dist/api.js +15 -15
- package/dist/approval-custom-id-NYPYj9Po.js +58 -0
- package/dist/{approval-handler.runtime-Bw2pZNk1.js → approval-handler.runtime-DeyIiw5I.js} +28 -23
- package/dist/approval-message-safety-C-8BL6Kv.js +47 -0
- package/dist/{audit-MLpDpN3Z.js → audit-Df2w046a.js} +3 -3
- package/dist/{channel-actions-D_fjVUZ_.js → channel-actions-ChpBSRcu.js} +6 -4
- package/dist/{channel-actions.runtime-CVHlFqQg.js → channel-actions.runtime-D8V8tyCq.js} +41 -12
- package/dist/channel-config-api.js +1 -1
- package/dist/{channel-DGWPH5u3.js → channel-nYmJ7FKQ.js} +275 -62
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-tZcpIn4e.js → channel.setup-DyihrqHH.js} +3 -3
- package/dist/{components-BIBizbtZ.js → components-BVrfRS-F.js} +78 -52
- package/dist/config-api-B18fR8M5.js +3 -0
- package/dist/{config-schema-BgIhO3ko.js → config-schema-_bUKcWRm.js} +42 -82
- package/dist/conversation-identity-BpkAzklg.js +94 -0
- package/dist/{directory-config-ByaUKL1f.js → directory-config-BUe-qq_C.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-BX79ImQA.js → directory-live-DZByMdPO.js} +6 -6
- package/dist/{doctor-Bgoxm5AN.js → doctor-bMaWXFKu.js} +5 -4
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{doctor-contract-CU7lnZTF.js → doctor-contract-wId6pV41.js} +41 -37
- package/dist/{handle-action.guild-admin-CgJSNVui.js → handle-action.guild-admin-C_PQcBEV.js} +12 -12
- package/dist/index.js +1 -1
- package/dist/legacy-state-migrations-api.js +24 -2
- package/dist/{manager.runtime-DaB-CTrt.js → manager.runtime-3RVMVkyx.js} +546 -154
- package/dist/{message-handler-DcgvugZt.js → message-handler-CIxWq5Yh.js} +8 -9
- package/dist/{message-handler.preflight-DvLXiYVH.js → message-handler.preflight-BI86IbHz.js} +46 -22
- package/dist/{message-handler.process-Cjf30P_B.js → message-handler.process-bPS_9_tb.js} +241 -107
- package/dist/{message-utils-B_3yA5bN.js → message-utils-D4j07taS.js} +2 -3
- package/dist/{outbound-adapter-82RWi_SR.js → outbound-adapter-Czny0Fae.js} +16 -68
- package/dist/outbound-components-oJKLj62G.js +99 -0
- package/dist/pluralkit-UXq1OyUI.js +40 -0
- package/dist/{probe-D1ImJEo2.js → probe-DxjMBeVG.js} +1 -1
- package/dist/{probe.runtime-B1dsEeBD.js → probe.runtime-Bc8Oo6e8.js} +1 -1
- package/dist/{provider-BkfbC9FQ.js → provider-DWvDNb4J.js} +702 -236
- package/dist/{provider-session.runtime-BDjyFw6Y.js → provider-session.runtime-D5pCGibh.js} +3 -3
- package/dist/provider.runtime-C2iIfbTs.js +2 -0
- package/dist/{resolve-allowlist-common-CWbE8XlH.js → resolve-allowlist-common-B9S8LLBd.js} +5 -5
- package/dist/{resolve-channels-CcpvWoRa.js → resolve-channels-BrkWOKdG.js} +7 -7
- package/dist/{resolve-users-DJamun9s.js → resolve-users-OjrXXvJP.js} +5 -5
- package/dist/{runtime-C6rRmQ6l.js → runtime-C618lis9.js} +223 -33
- package/dist/runtime-api.actions.js +2 -2
- package/dist/runtime-api.js +23 -23
- package/dist/runtime-api.lookup.js +5 -5
- package/dist/runtime-api.monitor-DSbfGnzS.js +6 -0
- package/dist/runtime-api.monitor.js +7 -7
- package/dist/runtime-api.send.js +5 -5
- package/dist/runtime-api.threads.js +5 -5
- package/dist/{send-iJ0B4bAX.js → send-BFpa_DHn.js} +21 -12
- package/dist/{send.components-D0I39utR.js → send.components-BaXQJKWT.js} +15 -10
- package/dist/{send.outbound-Bj61Q3a0.js → send.outbound-BlA6VzSE.js} +22 -12
- package/dist/{send.receipt-BpBQED_5.js → send.receipt-aegk69an.js} +59 -75
- package/dist/{send.shared-wAwMhHDN.js → send.shared-Xz7IMVpu.js} +44 -20
- package/dist/{sender-identity-CEsfDF0L.js → sender-identity-C62lBe18.js} +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{shared-BQVKfnev.js → shared-BLYBt5SA.js} +3 -3
- package/dist/{subagent-hooks-B-bJxxGg.js → subagent-hooks-DKJ4p-Oy.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{system-events-DVnqSyTt.js → system-events-DKwfE-2z.js} +2 -2
- package/dist/{target-resolver-CwHaSLt3.js → target-resolver-BQyEHyeS.js} +2 -2
- package/dist/targets-DvItNIvP.js +3 -0
- package/dist/thread-binding-api-BDZJD4na.js +4 -0
- package/dist/thread-binding-api.js +1 -3
- package/dist/{thread-bindings-DsTxqu3E.js → thread-bindings-BbzjUuNB.js} +26 -43
- package/dist/{thread-bindings.discord-api-BjIf9lGs.js → thread-bindings.discord-api-DJD3qwIN.js} +8 -10
- package/dist/{thread-bindings.manager-Bwmtqc3E.js → thread-bindings.manager-BIBm6bQd.js} +4 -4
- package/dist/{thread-bindings.session-updates-Chs3HgGa.js → thread-bindings.session-updates-eRcJlzIU.js} +1 -1
- package/dist/{thread-bindings.state-iSWjCYjB.js → thread-bindings.state-9VAxhVf6.js} +3 -14
- package/dist/{timeouts-C5TBc_9x.js → timeouts-ZRc9Lw4y.js} +3 -15
- package/dist/timeouts.js +1 -1
- package/dist/{transcripts-source-BvvM5Ns9.js → transcripts-source-CLZ5X8fh.js} +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-BVcJgILg.js → typing-BnYT7Szq.js} +2 -2
- package/node_modules/p-map/index.d.ts +155 -0
- package/node_modules/p-map/index.js +284 -0
- package/node_modules/p-map/license +9 -0
- package/node_modules/p-map/package.json +57 -0
- package/node_modules/p-map/readme.md +190 -0
- package/npm-shrinkwrap.json +16 -3
- package/openclaw.plugin.json +70 -2
- package/package.json +6 -4
- package/dist/config-api-JiPdJeb0.js +0 -2
- package/dist/conversation-identity-DWC5d8Sy.js +0 -272
- package/dist/pluralkit-DcD-3EcL.js +0 -24
- package/dist/preview-streaming-DXT8oJdo.js +0 -8
- package/dist/provider.runtime-WrJXikSv.js +0 -2
- package/dist/runtime-api.monitor-C2_WVtJf.js +0 -6
- package/dist/targets-69w7c3vU.js +0 -3
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as Client, D as RateLimitError, F as Message, I as User, K as Container, M as Command, N as CommandWithSubcommands, P as Guild, Q as Row, V as Modal, W as Button, Xt as __exportAll, _ as MessageReactionRemoveListener, _t as editChannel, at as BaseMessageInteractiveComponent, bt as getChannelMessage, d as discord_exports, et as Separator, f as GuildCreateListener, g as MessageReactionAddListener, h as MessageCreateListener, m as InteractionCreateListener, mt as createThread, nt as TextDisplay, p as GuildDeleteListener, tt as StringSelectMenu, u as chunkDiscordTextWithMode, v as PresenceUpdateListener, w as Plugin, x as ThreadUpdateListener, y as ReadyListener } from "./send.receipt-aegk69an.js";
|
|
2
2
|
import { c as resolveDiscordAccountAllowFrom, d as resolveDiscordAccountDmPolicy, f as resolveDiscordMaxLinesPerMessage, p as normalizeDiscordToken, s as resolveDiscordAccount } from "./accounts-P19jm1lf.js";
|
|
3
|
-
import { _ as parseDiscordComponentCustomId, b as parseDiscordModalCustomIdForInteraction, v as parseDiscordComponentCustomIdForInteraction, y as parseDiscordModalCustomId } from "./components-
|
|
4
|
-
import { a as resolveDiscordComponentEntryWithPersistence, o as resolveDiscordModalEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-
|
|
5
|
-
import { i as setPresence } from "./presence-cache-z1N8MbJ3.js";
|
|
6
|
-
import { B as DISCORD_REST_TIMEOUT_MS, D as canViewDiscordGuildChannel, G as registerGateway, H as withValidatedDiscordProxy, I as createDiscordRestClient, K as unregisterGateway, V as validateDiscordProxyUrl } from "./send.shared-
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import { s as sendVoiceMessageDiscord } from "./send-
|
|
10
|
-
import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-
|
|
3
|
+
import { S as encodeCustomIdComponent, _ as parseDiscordComponentCustomId, b as parseDiscordModalCustomIdForInteraction, v as parseDiscordComponentCustomIdForInteraction, x as decodeCustomIdComponent, y as parseDiscordModalCustomId } from "./components-BVrfRS-F.js";
|
|
4
|
+
import { a as resolveDiscordComponentEntryWithPersistence, o as resolveDiscordModalEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-BaXQJKWT.js";
|
|
5
|
+
import { i as setPresence, t as clearPresences } from "./presence-cache-z1N8MbJ3.js";
|
|
6
|
+
import { B as DISCORD_REST_TIMEOUT_MS, D as canViewDiscordGuildChannel, G as registerGateway, H as withValidatedDiscordProxy, I as createDiscordRestClient, K as unregisterGateway, V as validateDiscordProxyUrl } from "./send.shared-Xz7IMVpu.js";
|
|
7
|
+
import { a as isDiscordRateLimitResponseBody, o as summarizeDiscordResponseBody } from "./api--f62GFaS.js";
|
|
8
|
+
import { C as resolveTimestampMs, a as normalizeDiscordSlug, b as formatDiscordReactionEmoji, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordCommandOwnerAllowFrom, f as resolveDiscordGuildEntry, g as resolveDiscordOwnerAllowFrom, h as resolveDiscordOwnerAccess, i as normalizeDiscordDisplaySlug, l as resolveDiscordChannelPolicyCommandAuthorizer, m as resolveDiscordMemberAllowed, n as isDiscordGroupAllowedByPolicy, o as resolveDiscordAllowListMatch, p as resolveDiscordMemberAccessState, r as normalizeDiscordAllowList, v as resolveGroupDmAllow, x as formatDiscordUserTag, y as shouldEmitDiscordReactionNotification } from "./allow-list-B7qOH5xu.js";
|
|
9
|
+
import { s as sendVoiceMessageDiscord } from "./send-BFpa_DHn.js";
|
|
10
|
+
import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-BlA6VzSE.js";
|
|
11
|
+
import { d as withAbortTimeout, l as raceWithTimeout } from "./timeouts-ZRc9Lw4y.js";
|
|
12
|
+
import { r as parseExecApprovalData } from "./approval-custom-id-NYPYj9Po.js";
|
|
11
13
|
import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
|
|
12
|
-
import { a as
|
|
13
|
-
import {
|
|
14
|
-
import { t as
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { t as
|
|
20
|
-
import "./approval-handler.runtime-Bw2pZNk1.js";
|
|
14
|
+
import { a as isDiscordExecApprovalClientEnabled, r as getDiscordExecApprovalApprovers, t as resolveDiscordConversationIdentity } from "./conversation-identity-BpkAzklg.js";
|
|
15
|
+
import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-BrkWOKdG.js";
|
|
16
|
+
import { t as resolveDiscordUserAllowlist } from "./resolve-users-OjrXXvJP.js";
|
|
17
|
+
import { _ as formatThreadBindingDurationLabel, a as isThreadArchived, d as resolveDiscordChannelNameSafe, f as resolveDiscordChannelParentIdSafe, l as resolveDiscordChannelIdSafe, m as resolveDiscordChannelTopicSafe, p as resolveDiscordChannelParentSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-DJD3qwIN.js";
|
|
18
|
+
import { r as parseApplicationIdFromToken, t as fetchDiscordApplicationId } from "./probe-DxjMBeVG.js";
|
|
19
|
+
import { d as resolveDiscordMessageChannelId, n as resolveDiscordForwardedMessagesTextFromSnapshots, t as resolveDiscordEmbedText, u as resolveDiscordChannelInfo } from "./message-utils-D4j07taS.js";
|
|
20
|
+
import { t as resolveDiscordSenderIdentity } from "./sender-identity-C62lBe18.js";
|
|
21
|
+
import { n as formatDiscordApprovalDisplayValue, t as DISCORD_APPROVAL_ALLOWED_MENTIONS } from "./approval-message-safety-C-8BL6Kv.js";
|
|
21
22
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
22
23
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
23
24
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeStringEntries, normalizeStringEntriesLower, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -28,6 +29,7 @@ import { MAX_DATE_TIMESTAMP_MS, asDateTimestampMs, parseFiniteNumber, parseStric
|
|
|
28
29
|
import { createHash, randomUUID } from "node:crypto";
|
|
29
30
|
import path from "node:path";
|
|
30
31
|
import { resolvePinnedMainDmOwnerFromAllowlist as resolvePinnedMainDmOwnerFromAllowlist$1 } from "openclaw/plugin-sdk/security-runtime";
|
|
32
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
31
33
|
import { Type } from "typebox";
|
|
32
34
|
import { Check, Errors } from "typebox/value";
|
|
33
35
|
import { inspect } from "node:util";
|
|
@@ -54,9 +56,10 @@ import { clearExpiredCooldowns, ensureAuthProfileStore, formatReasoningMessage,
|
|
|
54
56
|
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
|
55
57
|
import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "openclaw/plugin-sdk/native-command-config-runtime";
|
|
56
58
|
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|
59
|
+
import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
|
|
57
60
|
import { enqueueSystemEvent, enqueueSystemEvent as enqueueSystemEvent$1 } from "openclaw/plugin-sdk/system-event-runtime";
|
|
58
61
|
import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
59
|
-
import { applyModelOverrideToSessionEntry, resolveChannelModelOverride } from "openclaw/plugin-sdk/model-session-runtime";
|
|
62
|
+
import { ModelSelectionLockedError, applyModelOverrideToSessionEntry, resolveChannelModelOverride } from "openclaw/plugin-sdk/model-session-runtime";
|
|
60
63
|
import { completeWithPreparedSimpleCompletionModel, extractAssistantText, prepareSimpleCompletionModelForAgent } from "openclaw/plugin-sdk/simple-completion-runtime";
|
|
61
64
|
import { getSessionEntry, listSessionEntries, patchSessionEntry, readSessionUpdatedAt as readSessionUpdatedAt$1, resolveStorePath, resolveStorePath as resolveStorePath$1 } from "openclaw/plugin-sdk/session-store-runtime";
|
|
62
65
|
import { formatInboundEnvelope, resolveEnvelopeFormatOptions, runChannelInboundEvent } from "openclaw/plugin-sdk/channel-inbound";
|
|
@@ -79,7 +82,8 @@ import { stripPlainTextToolCallBlocks } from "openclaw/plugin-sdk/tool-payload";
|
|
|
79
82
|
import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway-runtime";
|
|
80
83
|
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
81
84
|
import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
|
|
82
|
-
import {
|
|
85
|
+
import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
|
|
86
|
+
import { Agent as Agent$1 } from "undici";
|
|
83
87
|
//#region extensions/discord/src/monitor/listeners.queue.ts
|
|
84
88
|
const DISCORD_SLOW_LISTENER_THRESHOLD_MS = 3e4;
|
|
85
89
|
const discordEventQueueLog = createSubsystemLogger("discord/event-queue");
|
|
@@ -1216,6 +1220,91 @@ async function handleDiscordReactionEvent(params) {
|
|
|
1216
1220
|
}
|
|
1217
1221
|
}
|
|
1218
1222
|
//#endregion
|
|
1223
|
+
//#region extensions/discord/src/monitor/presence-events.ts
|
|
1224
|
+
const DISCORD_PRESENCE_GREETING_COOLDOWN_MS = 480 * 60 * 1e3;
|
|
1225
|
+
function isDiscordOnlineStatus(status) {
|
|
1226
|
+
return status === "online" || status === "idle" || status === "dnd";
|
|
1227
|
+
}
|
|
1228
|
+
function isDiscordOfflineStatus(status) {
|
|
1229
|
+
return status === "offline";
|
|
1230
|
+
}
|
|
1231
|
+
function resolveDiscordOnlinePresenceEvent(params) {
|
|
1232
|
+
const config = params.config;
|
|
1233
|
+
const userId = params.data.user?.id?.trim();
|
|
1234
|
+
if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
|
|
1235
|
+
if (config.users !== void 0 && !config.users.includes(userId)) return null;
|
|
1236
|
+
if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
|
|
1237
|
+
const lines = [
|
|
1238
|
+
"Discord online-presence event:",
|
|
1239
|
+
params.availabilityKind === "observed-offline" ? `A human member became available online after being observed offline in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}.` : `A human member became newly visible online since the current guild snapshot in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}. They may have come online or joined after the snapshot; do not claim an exact prior status.`,
|
|
1240
|
+
`The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
|
|
1241
|
+
"Before greeting, retrieve relevant memory and wiki context for this immutable user_id, including a known timezone when available. Use their local time for the greeting; if their timezone is unknown, do not guess.",
|
|
1242
|
+
"Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
|
|
1243
|
+
];
|
|
1244
|
+
return {
|
|
1245
|
+
channelId: config.channelId,
|
|
1246
|
+
userId,
|
|
1247
|
+
text: lines.join("\n")
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
//#endregion
|
|
1251
|
+
//#region extensions/discord/src/monitor/presence-cooldown-store.ts
|
|
1252
|
+
const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
|
|
1253
|
+
function openDiscordPresenceCooldownStore() {
|
|
1254
|
+
return getDiscordRuntime().state.openSyncKeyedStore({
|
|
1255
|
+
namespace: "presence-greeting-cooldowns",
|
|
1256
|
+
maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
|
|
1257
|
+
overflowPolicy: "reject-new",
|
|
1258
|
+
defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
//#endregion
|
|
1262
|
+
//#region extensions/discord/src/monitor/presence-transition-cache.ts
|
|
1263
|
+
const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
|
|
1264
|
+
var DiscordPresenceBaselineCache = class {
|
|
1265
|
+
constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
|
|
1266
|
+
this.maxEntries = maxEntries;
|
|
1267
|
+
this.offlineByKey = /* @__PURE__ */ new Map();
|
|
1268
|
+
this.onlineByKey = /* @__PURE__ */ new Map();
|
|
1269
|
+
}
|
|
1270
|
+
clear() {
|
|
1271
|
+
this.offlineByKey.clear();
|
|
1272
|
+
this.onlineByKey.clear();
|
|
1273
|
+
}
|
|
1274
|
+
clearScope(scope) {
|
|
1275
|
+
for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
|
|
1276
|
+
}
|
|
1277
|
+
isOffline(scope, key) {
|
|
1278
|
+
return this.offlineByKey.get(key) === scope;
|
|
1279
|
+
}
|
|
1280
|
+
isOnline(scope, key) {
|
|
1281
|
+
return this.onlineByKey.get(key) === scope;
|
|
1282
|
+
}
|
|
1283
|
+
observeOffline(scope, key) {
|
|
1284
|
+
this.deleteMarker(this.onlineByKey, scope, key);
|
|
1285
|
+
return this.observe(this.offlineByKey, scope, key);
|
|
1286
|
+
}
|
|
1287
|
+
observeOnline(scope, key) {
|
|
1288
|
+
this.deleteMarker(this.offlineByKey, scope, key);
|
|
1289
|
+
return this.observe(this.onlineByKey, scope, key);
|
|
1290
|
+
}
|
|
1291
|
+
deleteMarker(markers, scope, key) {
|
|
1292
|
+
if (markers.get(key) === scope) markers.delete(key);
|
|
1293
|
+
}
|
|
1294
|
+
observe(markers, scope, key) {
|
|
1295
|
+
markers.delete(key);
|
|
1296
|
+
markers.set(key, scope);
|
|
1297
|
+
let evictedScope;
|
|
1298
|
+
while (markers.size > this.maxEntries) {
|
|
1299
|
+
const oldestKey = markers.keys().next().value;
|
|
1300
|
+
if (oldestKey === void 0) break;
|
|
1301
|
+
evictedScope = markers.get(oldestKey);
|
|
1302
|
+
markers.delete(oldestKey);
|
|
1303
|
+
}
|
|
1304
|
+
return evictedScope;
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
//#endregion
|
|
1219
1308
|
//#region extensions/discord/src/monitor/thread-session-close.ts
|
|
1220
1309
|
/**
|
|
1221
1310
|
* Marks every session entry in the store whose key contains {@link threadId}
|
|
@@ -1294,18 +1383,194 @@ var DiscordInteractionListener = class extends InteractionCreateListener {
|
|
|
1294
1383
|
var DiscordPresenceListener = class extends PresenceUpdateListener {
|
|
1295
1384
|
constructor(params) {
|
|
1296
1385
|
super();
|
|
1297
|
-
this.
|
|
1298
|
-
this.
|
|
1386
|
+
this.params = params;
|
|
1387
|
+
this.pendingByGuildUser = /* @__PURE__ */ new Map();
|
|
1388
|
+
this.guildPresenceState = /* @__PURE__ */ new Map();
|
|
1389
|
+
this.gatewayGeneration = 0;
|
|
1390
|
+
this.cooldownStore = params.cooldownStore ?? openDiscordPresenceCooldownStore();
|
|
1391
|
+
this.presenceBaseline = params.presenceBaseline ?? new DiscordPresenceBaselineCache();
|
|
1392
|
+
}
|
|
1393
|
+
seedGuildSnapshot(data) {
|
|
1394
|
+
const config = resolveDiscordGuildEntry({
|
|
1395
|
+
guildId: data.id,
|
|
1396
|
+
guildEntries: this.params.guildEntries
|
|
1397
|
+
})?.presenceEvents;
|
|
1398
|
+
if (!config || config.enabled === false) return;
|
|
1399
|
+
const keyPrefix = `${this.params.accountId}:${data.id}:`;
|
|
1400
|
+
const generation = (this.guildPresenceState.get(data.id)?.generation ?? 0) + 1;
|
|
1401
|
+
this.guildPresenceState.set(data.id, {
|
|
1402
|
+
generation,
|
|
1403
|
+
inferUnknownAsNewlyAvailable: false
|
|
1404
|
+
});
|
|
1405
|
+
this.detachPendingPrefix(keyPrefix);
|
|
1406
|
+
this.presenceBaseline.clearScope(data.id);
|
|
1407
|
+
if (data.unavailable === true || !("presences" in data) || !Array.isArray(data.presences)) return;
|
|
1408
|
+
this.guildPresenceState.set(data.id, {
|
|
1409
|
+
generation,
|
|
1410
|
+
inferUnknownAsNewlyAvailable: data.member_count <= 75e3
|
|
1411
|
+
});
|
|
1412
|
+
for (const presence of data.presences) {
|
|
1413
|
+
const userId = presence.user?.id;
|
|
1414
|
+
if (!userId || config.users !== void 0 && !config.users.includes(userId)) continue;
|
|
1415
|
+
const key = `${keyPrefix}${userId}`;
|
|
1416
|
+
if (isDiscordOfflineStatus(presence.status)) this.recordPresenceBaseline(data.id, key, "offline");
|
|
1417
|
+
else if (isDiscordOnlineStatus(presence.status)) this.recordPresenceBaseline(data.id, key, "online");
|
|
1418
|
+
}
|
|
1299
1419
|
}
|
|
1300
|
-
async handle(data) {
|
|
1420
|
+
async handle(data, client) {
|
|
1421
|
+
const userId = data.user?.id;
|
|
1422
|
+
if (!userId) return;
|
|
1423
|
+
setPresence(this.params.accountId, userId, data);
|
|
1424
|
+
const presenceKey = `${this.params.accountId}:${data.guild_id}:${userId}`;
|
|
1425
|
+
const gatewayGeneration = this.gatewayGeneration;
|
|
1426
|
+
const guildGeneration = this.guildPresenceState.get(data.guild_id)?.generation ?? 0;
|
|
1427
|
+
const run = (this.pendingByGuildUser.get(presenceKey) ?? Promise.resolve()).then(() => this.handleSerial(data, client, userId, presenceKey, gatewayGeneration, guildGeneration), () => this.handleSerial(data, client, userId, presenceKey, gatewayGeneration, guildGeneration));
|
|
1428
|
+
this.pendingByGuildUser.set(presenceKey, run);
|
|
1301
1429
|
try {
|
|
1302
|
-
|
|
1303
|
-
if (!userId) return;
|
|
1304
|
-
setPresence(this.accountId, userId, data);
|
|
1430
|
+
await run;
|
|
1305
1431
|
} catch (err) {
|
|
1306
|
-
(this.logger ?? discordEventQueueLog).error(danger(`discord presence handler failed: ${String(err)}`));
|
|
1432
|
+
(this.params.logger ?? discordEventQueueLog).error(danger(`discord presence handler failed: ${String(err)}`));
|
|
1433
|
+
} finally {
|
|
1434
|
+
if (this.pendingByGuildUser.get(presenceKey) === run) this.pendingByGuildUser.delete(presenceKey);
|
|
1307
1435
|
}
|
|
1308
1436
|
}
|
|
1437
|
+
resetGatewaySession() {
|
|
1438
|
+
this.gatewayGeneration += 1;
|
|
1439
|
+
this.presenceBaseline.clear();
|
|
1440
|
+
this.guildPresenceState.clear();
|
|
1441
|
+
this.pendingByGuildUser.clear();
|
|
1442
|
+
clearPresences(this.params.accountId);
|
|
1443
|
+
}
|
|
1444
|
+
invalidateGuild(guildId) {
|
|
1445
|
+
const keyPrefix = `${this.params.accountId}:${guildId}:`;
|
|
1446
|
+
const generation = (this.guildPresenceState.get(guildId)?.generation ?? 0) + 1;
|
|
1447
|
+
this.guildPresenceState.set(guildId, {
|
|
1448
|
+
generation,
|
|
1449
|
+
inferUnknownAsNewlyAvailable: false
|
|
1450
|
+
});
|
|
1451
|
+
this.presenceBaseline.clearScope(guildId);
|
|
1452
|
+
this.detachPendingPrefix(keyPrefix);
|
|
1453
|
+
}
|
|
1454
|
+
detachPendingPrefix(prefix) {
|
|
1455
|
+
for (const key of this.pendingByGuildUser.keys()) if (key.startsWith(prefix)) this.pendingByGuildUser.delete(key);
|
|
1456
|
+
}
|
|
1457
|
+
async handleSerial(data, client, userId, presenceKey, gatewayGeneration, guildGeneration) {
|
|
1458
|
+
if (!this.isCurrentGeneration(data.guild_id, gatewayGeneration, guildGeneration)) return;
|
|
1459
|
+
const config = resolveDiscordGuildEntry({
|
|
1460
|
+
guildId: data.guild_id,
|
|
1461
|
+
guildEntries: this.params.guildEntries
|
|
1462
|
+
})?.presenceEvents;
|
|
1463
|
+
if (!config || config.enabled === false) return;
|
|
1464
|
+
if (config.users !== void 0 && !config.users.includes(userId)) return;
|
|
1465
|
+
const nowMs = this.params.nowMs?.() ?? Date.now();
|
|
1466
|
+
const presenceScope = data.guild_id;
|
|
1467
|
+
const presenceEvent = resolveDiscordOnlinePresenceEvent({
|
|
1468
|
+
config,
|
|
1469
|
+
data,
|
|
1470
|
+
availabilityKind: this.presenceBaseline.isOffline(presenceScope, presenceKey) ? "observed-offline" : this.guildPresenceState.get(data.guild_id)?.inferUnknownAsNewlyAvailable === true && !this.presenceBaseline.isOnline(presenceScope, presenceKey) ? "first-seen-after-snapshot" : null,
|
|
1471
|
+
botUserId: this.params.botUserId,
|
|
1472
|
+
nowMs,
|
|
1473
|
+
lastEmittedAtMs: this.cooldownStore.lookup(presenceKey)
|
|
1474
|
+
});
|
|
1475
|
+
if (!presenceEvent) {
|
|
1476
|
+
if (isDiscordOfflineStatus(data.status)) this.recordPresenceBaseline(data.guild_id, presenceKey, "offline");
|
|
1477
|
+
else if (isDiscordOnlineStatus(data.status)) this.recordPresenceBaseline(data.guild_id, presenceKey, "online");
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
const fetchedUserIsBot = data.user.bot === void 0 && (await client.fetchUser(userId)).bot === true;
|
|
1481
|
+
if (!this.isCurrentGeneration(data.guild_id, gatewayGeneration, guildGeneration)) return;
|
|
1482
|
+
if (fetchedUserIsBot) {
|
|
1483
|
+
this.recordPresenceBaseline(data.guild_id, presenceKey, "online");
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
const route = resolveAgentRoute({
|
|
1487
|
+
cfg: this.params.cfg,
|
|
1488
|
+
channel: "discord",
|
|
1489
|
+
accountId: this.params.accountId,
|
|
1490
|
+
guildId: data.guild_id,
|
|
1491
|
+
peer: {
|
|
1492
|
+
kind: "channel",
|
|
1493
|
+
id: presenceEvent.channelId
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
try {
|
|
1497
|
+
if (!this.cooldownStore.registerIfAbsent(presenceKey, nowMs, { ttlMs: 288e5 })) {
|
|
1498
|
+
this.recordPresenceBaseline(data.guild_id, presenceKey, "online");
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
} catch (err) {
|
|
1502
|
+
(this.params.logger ?? discordEventQueueLog).warn(danger(`discord presence cooldown persistence failed: ${String(err)}`));
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
let queued;
|
|
1506
|
+
try {
|
|
1507
|
+
queued = enqueueSystemEvent(presenceEvent.text, {
|
|
1508
|
+
sessionKey: route.sessionKey,
|
|
1509
|
+
contextKey: `discord:presence-online:${this.params.accountId}:${data.guild_id}:${userId}`,
|
|
1510
|
+
deliveryContext: {
|
|
1511
|
+
channel: "discord",
|
|
1512
|
+
to: `channel:${presenceEvent.channelId}`,
|
|
1513
|
+
accountId: this.params.accountId
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
} catch (err) {
|
|
1517
|
+
if (this.cooldownStore.lookup(presenceKey) === nowMs) this.cooldownStore.delete(presenceKey);
|
|
1518
|
+
throw err;
|
|
1519
|
+
}
|
|
1520
|
+
if (!queued) {
|
|
1521
|
+
if (this.cooldownStore.lookup(presenceKey) === nowMs) this.cooldownStore.delete(presenceKey);
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
this.recordPresenceBaseline(data.guild_id, presenceKey, "online");
|
|
1525
|
+
requestHeartbeat({
|
|
1526
|
+
source: "notifications-event",
|
|
1527
|
+
intent: "immediate",
|
|
1528
|
+
reason: "wake",
|
|
1529
|
+
agentId: route.agentId,
|
|
1530
|
+
sessionKey: route.sessionKey,
|
|
1531
|
+
heartbeat: {
|
|
1532
|
+
target: "discord",
|
|
1533
|
+
to: `channel:${presenceEvent.channelId}`,
|
|
1534
|
+
accountId: this.params.accountId
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
isCurrentGeneration(guildId, gatewayGeneration, guildGeneration) {
|
|
1539
|
+
return gatewayGeneration === this.gatewayGeneration && guildGeneration === (this.guildPresenceState.get(guildId)?.generation ?? 0);
|
|
1540
|
+
}
|
|
1541
|
+
recordPresenceBaseline(guildId, key, status) {
|
|
1542
|
+
const evictedGuildId = status === "offline" ? this.presenceBaseline.observeOffline(guildId, key) : this.presenceBaseline.observeOnline(guildId, key);
|
|
1543
|
+
if (!evictedGuildId) return;
|
|
1544
|
+
const state = this.guildPresenceState.get(evictedGuildId);
|
|
1545
|
+
if (state) state.inferUnknownAsNewlyAvailable = false;
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
var DiscordPresenceGuildCreateListener = class extends GuildCreateListener {
|
|
1549
|
+
constructor(presenceListener) {
|
|
1550
|
+
super();
|
|
1551
|
+
this.presenceListener = presenceListener;
|
|
1552
|
+
}
|
|
1553
|
+
handle(data) {
|
|
1554
|
+
this.presenceListener.seedGuildSnapshot(data);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
var DiscordPresenceGuildDeleteListener = class extends GuildDeleteListener {
|
|
1558
|
+
constructor(presenceListener) {
|
|
1559
|
+
super();
|
|
1560
|
+
this.presenceListener = presenceListener;
|
|
1561
|
+
}
|
|
1562
|
+
handle(data) {
|
|
1563
|
+
this.presenceListener.invalidateGuild(data.id);
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
var DiscordPresenceReadyListener = class extends ReadyListener {
|
|
1567
|
+
constructor(presenceListener) {
|
|
1568
|
+
super();
|
|
1569
|
+
this.presenceListener = presenceListener;
|
|
1570
|
+
}
|
|
1571
|
+
handle() {
|
|
1572
|
+
this.presenceListener.resetGatewaySession();
|
|
1573
|
+
}
|
|
1309
1574
|
};
|
|
1310
1575
|
var DiscordThreadUpdateListener = class extends ThreadUpdateListener {
|
|
1311
1576
|
constructor(cfg, accountId, logger) {
|
|
@@ -1754,27 +2019,6 @@ function resolveDiscordNativeCommandChannelAccessContext(params) {
|
|
|
1754
2019
|
}) : null
|
|
1755
2020
|
};
|
|
1756
2021
|
}
|
|
1757
|
-
function resolveDiscordCommandOwnerAllowFrom(cfg) {
|
|
1758
|
-
const raw = cfg.commands?.ownerAllowFrom;
|
|
1759
|
-
if (!Array.isArray(raw) || raw.length === 0) return;
|
|
1760
|
-
const entries = [];
|
|
1761
|
-
for (const entry of raw) {
|
|
1762
|
-
const trimmed = normalizeOptionalString(String(entry ?? "")) ?? "";
|
|
1763
|
-
if (!trimmed) continue;
|
|
1764
|
-
const separatorIndex = trimmed.indexOf(":");
|
|
1765
|
-
if (separatorIndex > 0) {
|
|
1766
|
-
const prefix = trimmed.slice(0, separatorIndex).toLowerCase();
|
|
1767
|
-
if (prefix === "discord") {
|
|
1768
|
-
const remainder = normalizeOptionalString(trimmed.slice(separatorIndex + 1)) ?? "";
|
|
1769
|
-
if (remainder) entries.push(remainder);
|
|
1770
|
-
continue;
|
|
1771
|
-
}
|
|
1772
|
-
if (prefix !== "user" && prefix !== "pk") continue;
|
|
1773
|
-
}
|
|
1774
|
-
entries.push(trimmed);
|
|
1775
|
-
}
|
|
1776
|
-
return entries.length > 0 ? entries : void 0;
|
|
1777
|
-
}
|
|
1778
2022
|
async function resolveDiscordGuildNativeCommandAuthorized(params) {
|
|
1779
2023
|
const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
|
|
1780
2024
|
providerConfigPresent: params.cfg.channels?.discord !== void 0,
|
|
@@ -2121,22 +2365,12 @@ const DISCORD_COMMAND_ARG_CUSTOM_ID_KEY = "cmdarg";
|
|
|
2121
2365
|
function createCommandArgsWithValue(params) {
|
|
2122
2366
|
return { values: { [params.argName]: params.value } };
|
|
2123
2367
|
}
|
|
2124
|
-
function encodeDiscordCommandArgValue(value) {
|
|
2125
|
-
return encodeURIComponent(value);
|
|
2126
|
-
}
|
|
2127
|
-
function decodeDiscordCommandArgValue(value) {
|
|
2128
|
-
try {
|
|
2129
|
-
return decodeURIComponent(value);
|
|
2130
|
-
} catch {
|
|
2131
|
-
return value;
|
|
2132
|
-
}
|
|
2133
|
-
}
|
|
2134
2368
|
function buildDiscordCommandArgCustomId(params) {
|
|
2135
2369
|
return [
|
|
2136
|
-
`${DISCORD_COMMAND_ARG_CUSTOM_ID_KEY}:command=${
|
|
2137
|
-
`arg=${
|
|
2138
|
-
`value=${
|
|
2139
|
-
`user=${
|
|
2370
|
+
`${DISCORD_COMMAND_ARG_CUSTOM_ID_KEY}:command=${encodeCustomIdComponent(params.command)}`,
|
|
2371
|
+
`arg=${encodeCustomIdComponent(params.arg)}`,
|
|
2372
|
+
`value=${encodeCustomIdComponent(params.value)}`,
|
|
2373
|
+
`user=${encodeCustomIdComponent(params.userId)}`
|
|
2140
2374
|
].join(";");
|
|
2141
2375
|
}
|
|
2142
2376
|
function parseDiscordCommandArgData(data) {
|
|
@@ -2148,10 +2382,10 @@ function parseDiscordCommandArgData(data) {
|
|
|
2148
2382
|
const rawUser = coerce(data.user);
|
|
2149
2383
|
if (!rawCommand || !rawArg || !rawValue || !rawUser) return null;
|
|
2150
2384
|
return {
|
|
2151
|
-
command:
|
|
2152
|
-
arg:
|
|
2153
|
-
value:
|
|
2154
|
-
userId:
|
|
2385
|
+
command: decodeCustomIdComponent(rawCommand),
|
|
2386
|
+
arg: decodeCustomIdComponent(rawArg),
|
|
2387
|
+
value: decodeCustomIdComponent(rawValue),
|
|
2388
|
+
userId: decodeCustomIdComponent(rawUser)
|
|
2155
2389
|
};
|
|
2156
2390
|
}
|
|
2157
2391
|
async function handleDiscordCommandArgInteraction(params) {
|
|
@@ -2384,6 +2618,10 @@ async function recordDiscordModelPickerRecentModel(params) {
|
|
|
2384
2618
|
//#endregion
|
|
2385
2619
|
//#region extensions/discord/src/monitor/model-picker.state.ts
|
|
2386
2620
|
const DISCORD_MODEL_PICKER_CUSTOM_ID_KEY = "mdlpk";
|
|
2621
|
+
const DISCORD_CUSTOM_ID_MAX_CHARS = 100;
|
|
2622
|
+
const DISCORD_COMPONENT_MAX_SELECT_OPTIONS = 25;
|
|
2623
|
+
const DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2624
|
+
const DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2387
2625
|
function compareBucketItems(left, right) {
|
|
2388
2626
|
const normalized = left.toLowerCase().localeCompare(right.toLowerCase());
|
|
2389
2627
|
return normalized === 0 ? left.localeCompare(right) : normalized;
|
|
@@ -2408,17 +2646,19 @@ const PICKER_VIEWS = [
|
|
|
2408
2646
|
"models",
|
|
2409
2647
|
"recents"
|
|
2410
2648
|
];
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2649
|
+
/**
|
|
2650
|
+
* Alpha buckets engage only when the sorted item list exceeds the single-page
|
|
2651
|
+
* select cap. Below this threshold the user gets the existing flat list +
|
|
2652
|
+
* prev/next behavior unchanged.
|
|
2653
|
+
*/
|
|
2654
|
+
const DISCORD_MODEL_PICKER_BUCKET_THRESHOLD = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2655
|
+
/** Target items per alpha bucket. Discord caps selects at 25 options. */
|
|
2656
|
+
const DISCORD_MODEL_PICKER_BUCKET_TARGET_SIZE = 20;
|
|
2657
|
+
const DISCORD_MODEL_PICKER_MODEL_TOKEN_PATTERN = /^[A-Za-z0-9_-]{8}$/u;
|
|
2658
|
+
function createDiscordModelPickerModelToken(provider, model) {
|
|
2659
|
+
return createHash("sha256").update(JSON.stringify([normalizeProviderId(provider), model]), "utf8").digest("base64url").slice(0, 8);
|
|
2421
2660
|
}
|
|
2661
|
+
const loadModelsProviderRuntime = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/models-provider-runtime"));
|
|
2422
2662
|
function isValidCommandContext(value) {
|
|
2423
2663
|
return COMMAND_CONTEXTS.includes(value);
|
|
2424
2664
|
}
|
|
@@ -2479,44 +2719,51 @@ function buildDiscordModelPickerCustomId(params) {
|
|
|
2479
2719
|
const normalizedProvider = params.provider ? normalizeProviderId(params.provider) : void 0;
|
|
2480
2720
|
const modelIndex = typeof params.modelIndex === "number" && Number.isFinite(params.modelIndex) ? Math.max(1, Math.floor(params.modelIndex)) : void 0;
|
|
2481
2721
|
const recentSlot = typeof params.recentSlot === "number" && Number.isFinite(params.recentSlot) ? Math.max(1, Math.floor(params.recentSlot)) : void 0;
|
|
2722
|
+
const modelToken = params.modelToken?.trim();
|
|
2723
|
+
if (modelToken && !DISCORD_MODEL_PICKER_MODEL_TOKEN_PATTERN.test(modelToken)) throw new Error("Discord model picker model token is invalid");
|
|
2482
2724
|
const parts = [
|
|
2483
|
-
`${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:c=${
|
|
2484
|
-
`a=${
|
|
2485
|
-
`v=${
|
|
2486
|
-
`u=${
|
|
2725
|
+
`${DISCORD_MODEL_PICKER_CUSTOM_ID_KEY}:c=${encodeCustomIdComponent(params.command)}`,
|
|
2726
|
+
`a=${encodeCustomIdComponent(params.action)}`,
|
|
2727
|
+
`v=${encodeCustomIdComponent(params.view)}`,
|
|
2728
|
+
`u=${encodeCustomIdComponent(userId)}`,
|
|
2487
2729
|
`g=${String(page)}`
|
|
2488
2730
|
];
|
|
2489
|
-
if (normalizedProvider) parts.push(`p=${
|
|
2731
|
+
if (normalizedProvider) parts.push(`p=${encodeCustomIdComponent(normalizedProvider)}`);
|
|
2490
2732
|
const runtime = params.runtime?.trim();
|
|
2491
|
-
if (runtime) parts.push(`r=${
|
|
2733
|
+
if (runtime) parts.push(`r=${encodeCustomIdComponent(runtime)}`);
|
|
2492
2734
|
const runtimeIndex = typeof params.runtimeIndex === "number" && Number.isFinite(params.runtimeIndex) ? Math.max(1, Math.floor(params.runtimeIndex)) : void 0;
|
|
2493
2735
|
if (runtimeIndex) parts.push(`ri=${String(runtimeIndex)}`);
|
|
2494
2736
|
if (providerPage) parts.push(`pp=${String(providerPage)}`);
|
|
2495
|
-
if (
|
|
2496
|
-
|
|
2737
|
+
if (modelToken) parts.push(`m=${modelToken}`);
|
|
2738
|
+
else {
|
|
2739
|
+
if (modelIndex) parts.push(`mi=${String(modelIndex)}`);
|
|
2740
|
+
if (recentSlot) parts.push(`rs=${String(recentSlot)}`);
|
|
2741
|
+
}
|
|
2497
2742
|
const providerBucket = params.providerBucket?.trim().toLowerCase();
|
|
2498
|
-
if (providerBucket) parts.push(`pb=${
|
|
2743
|
+
if (providerBucket) parts.push(`pb=${encodeCustomIdComponent(providerBucket)}`);
|
|
2499
2744
|
const modelBucket = params.modelBucket?.trim().toLowerCase();
|
|
2500
|
-
if (modelBucket) parts.push(`mb=${
|
|
2745
|
+
if (modelBucket) parts.push(`mb=${encodeCustomIdComponent(modelBucket)}`);
|
|
2501
2746
|
const customId = parts.join(";");
|
|
2502
|
-
if (customId.length >
|
|
2747
|
+
if (customId.length > DISCORD_CUSTOM_ID_MAX_CHARS) throw new Error(`Discord model picker custom_id exceeds ${DISCORD_CUSTOM_ID_MAX_CHARS} chars (${customId.length})`);
|
|
2503
2748
|
return customId;
|
|
2504
2749
|
}
|
|
2505
2750
|
function parseDiscordModelPickerData(data) {
|
|
2506
2751
|
if (!data || typeof data !== "object") return null;
|
|
2507
|
-
const command =
|
|
2508
|
-
const action =
|
|
2509
|
-
const view =
|
|
2510
|
-
const userId =
|
|
2511
|
-
const providerRaw =
|
|
2512
|
-
const runtimeRaw =
|
|
2752
|
+
const command = decodeCustomIdComponent(coerceString(data.c ?? data.cmd));
|
|
2753
|
+
const action = decodeCustomIdComponent(coerceString(data.a ?? data.act));
|
|
2754
|
+
const view = decodeCustomIdComponent(coerceString(data.v ?? data.view));
|
|
2755
|
+
const userId = decodeCustomIdComponent(coerceString(data.u));
|
|
2756
|
+
const providerRaw = decodeCustomIdComponent(coerceString(data.p));
|
|
2757
|
+
const runtimeRaw = decodeCustomIdComponent(coerceString(data.r));
|
|
2513
2758
|
const runtimeIndex = parseRawPositiveInt(data.ri);
|
|
2514
2759
|
const page = parseRawPage(data.g ?? data.pg);
|
|
2515
2760
|
const providerPage = parseRawPositiveInt(data.pp);
|
|
2516
2761
|
const modelIndex = parseRawPositiveInt(data.mi);
|
|
2762
|
+
const modelTokenRaw = coerceString(data.m).trim();
|
|
2763
|
+
const modelToken = DISCORD_MODEL_PICKER_MODEL_TOKEN_PATTERN.test(modelTokenRaw) ? modelTokenRaw : void 0;
|
|
2517
2764
|
const recentSlot = parseRawPositiveInt(data.rs);
|
|
2518
|
-
const providerBucketRaw =
|
|
2519
|
-
const modelBucketRaw =
|
|
2765
|
+
const providerBucketRaw = decodeCustomIdComponent(coerceString(data.pb)).trim().toLowerCase();
|
|
2766
|
+
const modelBucketRaw = decodeCustomIdComponent(coerceString(data.mb)).trim().toLowerCase();
|
|
2520
2767
|
if (!isValidCommandContext(command) || !isValidPickerAction(action) || !isValidPickerView(view)) return null;
|
|
2521
2768
|
const trimmedUserId = userId.trim();
|
|
2522
2769
|
if (!trimmedUserId) return null;
|
|
@@ -2531,6 +2778,7 @@ function parseDiscordModelPickerData(data) {
|
|
|
2531
2778
|
page,
|
|
2532
2779
|
...typeof providerPage === "number" ? { providerPage } : {},
|
|
2533
2780
|
...typeof modelIndex === "number" ? { modelIndex } : {},
|
|
2781
|
+
...modelToken ? { modelToken } : {},
|
|
2534
2782
|
...typeof recentSlot === "number" ? { recentSlot } : {},
|
|
2535
2783
|
...providerBucketRaw ? { providerBucket: providerBucketRaw } : {},
|
|
2536
2784
|
...modelBucketRaw ? { modelBucket: modelBucketRaw } : {}
|
|
@@ -2549,25 +2797,26 @@ function parseDiscordModelPickerData(data) {
|
|
|
2549
2797
|
*/
|
|
2550
2798
|
function computeAlphaBuckets(sortedItems) {
|
|
2551
2799
|
if (sortedItems.length === 0) return [];
|
|
2552
|
-
if (sortedItems.length <=
|
|
2800
|
+
if (sortedItems.length <= DISCORD_MODEL_PICKER_BUCKET_THRESHOLD) return [{
|
|
2553
2801
|
id: "all",
|
|
2554
2802
|
label: `All (${sortedItems.length})`,
|
|
2555
2803
|
start: 0,
|
|
2556
2804
|
end: sortedItems.length
|
|
2557
2805
|
}];
|
|
2558
2806
|
const firstLetter = (value) => value.charAt(0).toLowerCase();
|
|
2559
|
-
|
|
2807
|
+
const firstItem = expectDefined(sortedItems.at(0), "non-empty sorted model picker items");
|
|
2808
|
+
if (sortedItems.every((item) => firstLetter(item) === firstLetter(firstItem))) return chunkBucketsByCount(sortedItems);
|
|
2560
2809
|
const buckets = [];
|
|
2561
2810
|
const target = computeBucketTargetSize(sortedItems.length);
|
|
2562
2811
|
let start = 0;
|
|
2563
2812
|
while (start < sortedItems.length) {
|
|
2564
2813
|
let end = Math.min(sortedItems.length, start + target);
|
|
2565
2814
|
if (end < sortedItems.length) {
|
|
2566
|
-
const last = firstLetter(sortedItems[end - 1]);
|
|
2567
|
-
while (end < sortedItems.length && firstLetter(sortedItems[end]) === last) end += 1;
|
|
2815
|
+
const last = firstLetter(expectDefined(sortedItems[end - 1], "bucket end predecessor"));
|
|
2816
|
+
while (end < sortedItems.length && firstLetter(expectDefined(sortedItems[end], "bucket extension index")) === last) end += 1;
|
|
2568
2817
|
}
|
|
2569
|
-
const startLetter = firstLetter(sortedItems[start]);
|
|
2570
|
-
const endLetter = firstLetter(sortedItems[end - 1]);
|
|
2818
|
+
const startLetter = firstLetter(expectDefined(sortedItems[start], "bucket start index"));
|
|
2819
|
+
const endLetter = firstLetter(expectDefined(sortedItems[end - 1], "bucket end predecessor"));
|
|
2571
2820
|
const id = startLetter === endLetter ? startLetter : `${startLetter}-${endLetter}`;
|
|
2572
2821
|
const label = startLetter === endLetter ? `${startLetter.toUpperCase()} (${end - start})` : `${startLetter.toUpperCase()}–${endLetter.toUpperCase()} (${end - start})`;
|
|
2573
2822
|
buckets.push({
|
|
@@ -2587,8 +2836,8 @@ function computeAlphaBuckets(sortedItems) {
|
|
|
2587
2836
|
* for typical inputs and grows linearly for very large lists.
|
|
2588
2837
|
*/
|
|
2589
2838
|
function computeBucketTargetSize(totalItems) {
|
|
2590
|
-
const minTarget =
|
|
2591
|
-
const capByBucketCount = Math.ceil(totalItems /
|
|
2839
|
+
const minTarget = DISCORD_MODEL_PICKER_BUCKET_TARGET_SIZE;
|
|
2840
|
+
const capByBucketCount = Math.ceil(totalItems / DISCORD_COMPONENT_MAX_SELECT_OPTIONS);
|
|
2592
2841
|
return Math.max(minTarget, capByBucketCount);
|
|
2593
2842
|
}
|
|
2594
2843
|
function chunkBucketsByCount(sortedItems) {
|
|
@@ -2613,8 +2862,8 @@ function chunkBucketsByCount(sortedItems) {
|
|
|
2613
2862
|
*/
|
|
2614
2863
|
function resolveBucket(buckets, id) {
|
|
2615
2864
|
if (buckets.length === 0) return null;
|
|
2616
|
-
if (!id) return buckets
|
|
2617
|
-
return buckets.find((bucket) => bucket.id === id) ?? buckets
|
|
2865
|
+
if (!id) return expectDefined(buckets.at(0), "non-empty model picker buckets");
|
|
2866
|
+
return buckets.find((bucket) => bucket.id === id) ?? expectDefined(buckets.at(0), "non-empty model picker buckets");
|
|
2618
2867
|
}
|
|
2619
2868
|
/**
|
|
2620
2869
|
* Derive the alpha-bucket id that contains a given provider id. Returns
|
|
@@ -2634,7 +2883,7 @@ function findProviderBucketLocation(data, provider) {
|
|
|
2634
2883
|
if (idx < 0) return;
|
|
2635
2884
|
const containing = computeAlphaBuckets(sorted).find((bucket) => idx >= bucket.start && idx < bucket.end);
|
|
2636
2885
|
if (!containing) return;
|
|
2637
|
-
const page = Math.floor((idx - containing.start) /
|
|
2886
|
+
const page = Math.floor((idx - containing.start) / DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE) + 1;
|
|
2638
2887
|
return {
|
|
2639
2888
|
...containing.id !== "all" ? { bucket: containing.id } : {},
|
|
2640
2889
|
page
|
|
@@ -2666,7 +2915,7 @@ function getDiscordModelPickerProviderPage(params) {
|
|
|
2666
2915
|
const buckets = computeAlphaBuckets(allItems.map((item) => item.id));
|
|
2667
2916
|
const bucket = resolveBucket(buckets, params.bucket);
|
|
2668
2917
|
const bucketItems = bucket ? allItems.slice(bucket.start, bucket.end) : allItems;
|
|
2669
|
-
const pageSize = clampPageSize(params.pageSize,
|
|
2918
|
+
const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE, DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE);
|
|
2670
2919
|
return {
|
|
2671
2920
|
...paginateItems({
|
|
2672
2921
|
items: bucketItems,
|
|
@@ -2685,7 +2934,7 @@ function getDiscordModelPickerModelPage(params) {
|
|
|
2685
2934
|
const buckets = computeAlphaBuckets(allModels);
|
|
2686
2935
|
const bucket = resolveBucket(buckets, params.bucket);
|
|
2687
2936
|
const bucketItems = bucket ? allModels.slice(bucket.start, bucket.end) : allModels;
|
|
2688
|
-
const pageSize = clampPageSize(params.pageSize,
|
|
2937
|
+
const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE);
|
|
2689
2938
|
return {
|
|
2690
2939
|
...paginateItems({
|
|
2691
2940
|
items: bucketItems,
|
|
@@ -2704,7 +2953,7 @@ function resolveDiscordModelPickerPageForModel(params) {
|
|
|
2704
2953
|
const sorted = [...modelSet].toSorted(compareBucketItems);
|
|
2705
2954
|
const index = sorted.indexOf(params.model);
|
|
2706
2955
|
if (index < 0) return { page: 1 };
|
|
2707
|
-
const pageSize = clampPageSize(params.pageSize,
|
|
2956
|
+
const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE);
|
|
2708
2957
|
const containingBucket = computeAlphaBuckets(sorted).find((bucket) => index >= bucket.start && index < bucket.end);
|
|
2709
2958
|
if (!containingBucket) return { page: Math.floor(index / pageSize) + 1 };
|
|
2710
2959
|
const offsetInBucket = index - containingBucket.start;
|
|
@@ -2719,8 +2968,10 @@ const DISCORD_MODEL_PICKER_PAGE_INDICATOR_CUSTOM_ID = "mdlpk:nav-indicator";
|
|
|
2719
2968
|
function parseCurrentModelRef(raw) {
|
|
2720
2969
|
const match = (raw?.trim())?.match(/^([^/]+)\/(.+)$/u);
|
|
2721
2970
|
if (!match) return null;
|
|
2722
|
-
const
|
|
2971
|
+
const providerText = match[1];
|
|
2723
2972
|
const model = match[2];
|
|
2973
|
+
if (providerText === void 0 || model === void 0) return null;
|
|
2974
|
+
const provider = normalizeProviderId(providerText);
|
|
2724
2975
|
if (!provider || !model) return null;
|
|
2725
2976
|
return {
|
|
2726
2977
|
provider,
|
|
@@ -2904,6 +3155,7 @@ function buildPaginationRow(params) {
|
|
|
2904
3155
|
page: Math.max(1, params.page - 1),
|
|
2905
3156
|
providerPage: params.providerPage,
|
|
2906
3157
|
modelIndex: params.modelIndex,
|
|
3158
|
+
modelToken: params.modelToken,
|
|
2907
3159
|
providerBucket: params.providerBucket,
|
|
2908
3160
|
modelBucket: params.modelBucket,
|
|
2909
3161
|
userId: params.userId
|
|
@@ -2929,6 +3181,7 @@ function buildPaginationRow(params) {
|
|
|
2929
3181
|
page: Math.min(params.totalPages, params.page + 1),
|
|
2930
3182
|
providerPage: params.providerPage,
|
|
2931
3183
|
modelIndex: params.modelIndex,
|
|
3184
|
+
modelToken: params.modelToken,
|
|
2932
3185
|
providerBucket: params.providerBucket,
|
|
2933
3186
|
modelBucket: params.modelBucket,
|
|
2934
3187
|
userId: params.userId
|
|
@@ -2939,6 +3192,7 @@ function buildPaginationRow(params) {
|
|
|
2939
3192
|
function buildModelRows(params) {
|
|
2940
3193
|
const parsedCurrentModel = parseCurrentModelRef(params.currentModel);
|
|
2941
3194
|
const parsedPendingModel = parseCurrentModelRef(params.pendingModel);
|
|
3195
|
+
const pendingModelToken = parsedPendingModel ? createDiscordModelPickerModelToken(parsedPendingModel.provider, parsedPendingModel.model) : void 0;
|
|
2942
3196
|
const rows = [];
|
|
2943
3197
|
const hasQuickModels = (params.quickModels ?? []).length > 0;
|
|
2944
3198
|
const providerPage = getDiscordModelPickerProviderPage({
|
|
@@ -2991,6 +3245,7 @@ function buildModelRows(params) {
|
|
|
2991
3245
|
page: params.modelPage.page,
|
|
2992
3246
|
providerPage: providerPage.page,
|
|
2993
3247
|
modelIndex: params.pendingModelIndex,
|
|
3248
|
+
modelToken: pendingModelToken,
|
|
2994
3249
|
...params.pendingModelIndex === void 0 && activeModelBucket ? { modelBucket: activeModelBucket } : {},
|
|
2995
3250
|
userId: params.userId
|
|
2996
3251
|
}),
|
|
@@ -3037,6 +3292,7 @@ function buildModelRows(params) {
|
|
|
3037
3292
|
...compactRuntime,
|
|
3038
3293
|
providerPage: providerPage.page,
|
|
3039
3294
|
modelIndex: params.pendingModelIndex,
|
|
3295
|
+
modelToken: pendingModelToken,
|
|
3040
3296
|
modelBucket: params.modelPage.bucket && params.modelPage.bucket.id !== "all" ? params.modelPage.bucket.id : void 0
|
|
3041
3297
|
});
|
|
3042
3298
|
if (modelNavRow) rows.push(modelNavRow);
|
|
@@ -3114,6 +3370,7 @@ function buildModelRows(params) {
|
|
|
3114
3370
|
page: params.modelPage.page,
|
|
3115
3371
|
providerPage: providerPage.page,
|
|
3116
3372
|
modelIndex: params.pendingModelIndex,
|
|
3373
|
+
modelToken: pendingModelToken,
|
|
3117
3374
|
userId: params.userId
|
|
3118
3375
|
})
|
|
3119
3376
|
}));
|
|
@@ -3254,6 +3511,10 @@ function formatRecentsButtonLabel(modelRef, suffix) {
|
|
|
3254
3511
|
if (label.length <= maxLen) return label;
|
|
3255
3512
|
return suffix ? `${sliceUtf16Safe(modelRef, 0, maxLen - suffix.length - 2)}… ${suffix}` : `${sliceUtf16Safe(modelRef, 0, maxLen - 1)}…`;
|
|
3256
3513
|
}
|
|
3514
|
+
function createModelRefToken(modelRef) {
|
|
3515
|
+
const parsed = parseCurrentModelRef(modelRef);
|
|
3516
|
+
return parsed ? createDiscordModelPickerModelToken(parsed.provider, parsed.model) : void 0;
|
|
3517
|
+
}
|
|
3257
3518
|
function renderDiscordModelPickerRecentsView(params) {
|
|
3258
3519
|
const defaultModelRef = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
|
|
3259
3520
|
const rows = [];
|
|
@@ -3266,6 +3527,24 @@ function renderDiscordModelPickerRecentsView(params) {
|
|
|
3266
3527
|
action: "submit",
|
|
3267
3528
|
view: "recents",
|
|
3268
3529
|
recentSlot: 1,
|
|
3530
|
+
modelToken: createModelRefToken(defaultModelRef),
|
|
3531
|
+
provider: params.provider,
|
|
3532
|
+
runtime: params.runtime,
|
|
3533
|
+
runtimeIndex: params.runtimeIndex,
|
|
3534
|
+
page: params.page,
|
|
3535
|
+
providerPage: params.providerPage,
|
|
3536
|
+
userId: params.userId
|
|
3537
|
+
})
|
|
3538
|
+
})]));
|
|
3539
|
+
for (const [i, modelRef] of dedupedQuickModels.entries()) rows.push(new Row([createModelPickerButton({
|
|
3540
|
+
label: formatRecentsButtonLabel(modelRef),
|
|
3541
|
+
style: ButtonStyle.Secondary,
|
|
3542
|
+
customId: buildDiscordModelPickerCustomId({
|
|
3543
|
+
command: params.command,
|
|
3544
|
+
action: "submit",
|
|
3545
|
+
view: "recents",
|
|
3546
|
+
recentSlot: i + 2,
|
|
3547
|
+
modelToken: createModelRefToken(modelRef),
|
|
3269
3548
|
provider: params.provider,
|
|
3270
3549
|
runtime: params.runtime,
|
|
3271
3550
|
runtimeIndex: params.runtimeIndex,
|
|
@@ -3274,25 +3553,6 @@ function renderDiscordModelPickerRecentsView(params) {
|
|
|
3274
3553
|
userId: params.userId
|
|
3275
3554
|
})
|
|
3276
3555
|
})]));
|
|
3277
|
-
for (let i = 0; i < dedupedQuickModels.length; i++) {
|
|
3278
|
-
const modelRef = dedupedQuickModels[i];
|
|
3279
|
-
rows.push(new Row([createModelPickerButton({
|
|
3280
|
-
label: formatRecentsButtonLabel(modelRef),
|
|
3281
|
-
style: ButtonStyle.Secondary,
|
|
3282
|
-
customId: buildDiscordModelPickerCustomId({
|
|
3283
|
-
command: params.command,
|
|
3284
|
-
action: "submit",
|
|
3285
|
-
view: "recents",
|
|
3286
|
-
recentSlot: i + 2,
|
|
3287
|
-
provider: params.provider,
|
|
3288
|
-
runtime: params.runtime,
|
|
3289
|
-
runtimeIndex: params.runtimeIndex,
|
|
3290
|
-
page: params.page,
|
|
3291
|
-
providerPage: params.providerPage,
|
|
3292
|
-
userId: params.userId
|
|
3293
|
-
})
|
|
3294
|
-
})]));
|
|
3295
|
-
}
|
|
3296
3556
|
const backRow = new Row([createModelPickerButton({
|
|
3297
3557
|
label: "Back",
|
|
3298
3558
|
style: ButtonStyle.Secondary,
|
|
@@ -3424,6 +3684,10 @@ async function applyDiscordModelPickerSelection(params) {
|
|
|
3424
3684
|
if (!persisted) logVerbose(`discord: direct session override persist failed — expected ${params.resolvedModelRef} but read ${effectiveModelRef} from session key ${fallbackRoute.sessionKey}`);
|
|
3425
3685
|
else if (!directlyPersisted) logVerbose(`discord: direct session override persist became a no-op because ${params.resolvedModelRef} was already present on re-read for session key ${fallbackRoute.sessionKey}`);
|
|
3426
3686
|
} catch (error) {
|
|
3687
|
+
if (error instanceof ModelSelectionLockedError) return {
|
|
3688
|
+
status: "rejected",
|
|
3689
|
+
noticeMessage: `❌ ${error.message}`
|
|
3690
|
+
};
|
|
3427
3691
|
const message = error instanceof Error ? error.message : String(error);
|
|
3428
3692
|
logVerbose(`discord: direct session override persist threw for session key ${fallbackRoute.sessionKey}: ${message}`);
|
|
3429
3693
|
}
|
|
@@ -3443,6 +3707,10 @@ async function applyDiscordModelPickerSelection(params) {
|
|
|
3443
3707
|
noticeMessage: `⚠️ Tried to set ${params.resolvedModelRef}, but current model is ${effectiveModelRef}.`
|
|
3444
3708
|
};
|
|
3445
3709
|
} catch (error) {
|
|
3710
|
+
if (error instanceof ModelSelectionLockedError) return {
|
|
3711
|
+
status: "rejected",
|
|
3712
|
+
noticeMessage: `❌ ${error.message}`
|
|
3713
|
+
};
|
|
3446
3714
|
if (error instanceof Error && error.message === "timeout") return {
|
|
3447
3715
|
status: "timeout",
|
|
3448
3716
|
noticeMessage: `⏳ Model change to ${params.resolvedModelRef} is still processing. Check /status in a few seconds.`
|
|
@@ -3715,11 +3983,14 @@ function resolveParsedRuntimeForSubmission(params) {
|
|
|
3715
3983
|
}
|
|
3716
3984
|
function resolveSubmittedModelRef(params) {
|
|
3717
3985
|
if (params.parsed.action === "reset") return `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
|
|
3986
|
+
if (params.parsed.modelToken) return resolveDiscordModelPickerModelRefByToken(params.data, params.parsed.modelToken);
|
|
3718
3987
|
if (params.parsed.action === "quick") {
|
|
3988
|
+
if (params.requireModelToken) return null;
|
|
3719
3989
|
const slot = params.parsed.recentSlot ?? 0;
|
|
3720
3990
|
return slot >= 1 ? params.quickModels[slot - 1] ?? null : null;
|
|
3721
3991
|
}
|
|
3722
3992
|
if (params.parsed.view === "recents") {
|
|
3993
|
+
if (params.requireModelToken) return null;
|
|
3723
3994
|
const defaultModelRef = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
|
|
3724
3995
|
const dedupedRecents = params.quickModels.filter((ref) => ref !== defaultModelRef);
|
|
3725
3996
|
const slot = params.parsed.recentSlot ?? 0;
|
|
@@ -3727,10 +3998,12 @@ function resolveSubmittedModelRef(params) {
|
|
|
3727
3998
|
return slot >= 2 ? dedupedRecents[slot - 2] ?? null : null;
|
|
3728
3999
|
}
|
|
3729
4000
|
const provider = params.parsed.provider;
|
|
3730
|
-
const selectedModel =
|
|
4001
|
+
const selectedModel = resolveDiscordModelPickerModelSelection({
|
|
3731
4002
|
data: params.data,
|
|
3732
4003
|
provider: provider ?? "",
|
|
3733
|
-
modelIndex: params.parsed.modelIndex
|
|
4004
|
+
modelIndex: params.parsed.modelIndex,
|
|
4005
|
+
modelToken: params.parsed.modelToken,
|
|
4006
|
+
requireModelToken: params.requireModelToken
|
|
3734
4007
|
});
|
|
3735
4008
|
return provider && selectedModel ? `${provider}/${selectedModel}` : null;
|
|
3736
4009
|
}
|
|
@@ -3752,6 +4025,11 @@ function listDiscordModelPickerProviderModels(data, provider) {
|
|
|
3752
4025
|
if (!modelSet) return [];
|
|
3753
4026
|
return [...modelSet].toSorted();
|
|
3754
4027
|
}
|
|
4028
|
+
function resolveDiscordModelPickerModelRefByToken(data, modelToken) {
|
|
4029
|
+
const matchingRefs = [];
|
|
4030
|
+
for (const [provider, models] of data.byProvider) for (const model of models) if (createDiscordModelPickerModelToken(provider, model) === modelToken) matchingRefs.push(`${provider}/${model}`);
|
|
4031
|
+
return matchingRefs.length === 1 ? matchingRefs[0] ?? null : null;
|
|
4032
|
+
}
|
|
3755
4033
|
function resolveDiscordModelPickerModelIndex(params) {
|
|
3756
4034
|
const models = listDiscordModelPickerProviderModels(params.data, params.provider);
|
|
3757
4035
|
if (!models.length) return null;
|
|
@@ -3759,10 +4037,14 @@ function resolveDiscordModelPickerModelIndex(params) {
|
|
|
3759
4037
|
if (index < 0) return null;
|
|
3760
4038
|
return index + 1;
|
|
3761
4039
|
}
|
|
3762
|
-
function
|
|
3763
|
-
if (!params.modelIndex || params.modelIndex < 1) return null;
|
|
4040
|
+
function resolveDiscordModelPickerModelSelection(params) {
|
|
3764
4041
|
const models = listDiscordModelPickerProviderModels(params.data, params.provider);
|
|
3765
4042
|
if (!models.length) return null;
|
|
4043
|
+
if (params.modelToken) {
|
|
4044
|
+
const matchingModels = models.filter((model) => createDiscordModelPickerModelToken(params.provider, model) === params.modelToken);
|
|
4045
|
+
return matchingModels.length === 1 ? matchingModels[0] ?? null : null;
|
|
4046
|
+
}
|
|
4047
|
+
if (params.requireModelToken || !params.modelIndex || params.modelIndex < 1) return null;
|
|
3766
4048
|
return models[params.modelIndex - 1] ?? null;
|
|
3767
4049
|
}
|
|
3768
4050
|
function resolveDiscordModelPickerRuntimeForProvider(params) {
|
|
@@ -3801,20 +4083,22 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
3801
4083
|
if (await params.safeInteractionCall("model picker defer", () => interaction.acknowledge()) === null) return;
|
|
3802
4084
|
deferredUpdate = true;
|
|
3803
4085
|
}
|
|
4086
|
+
const cfg = getRuntimeConfigSnapshot() ?? ctx.cfg;
|
|
4087
|
+
const requireModelToken = cfg !== ctx.cfg;
|
|
3804
4088
|
const route = await resolveDiscordModelPickerRoute({
|
|
3805
4089
|
interaction,
|
|
3806
|
-
cfg
|
|
4090
|
+
cfg,
|
|
3807
4091
|
accountId: ctx.accountId,
|
|
3808
4092
|
threadBindings: ctx.threadBindings
|
|
3809
4093
|
});
|
|
3810
|
-
const pickerData = await loadDiscordModelPickerData(
|
|
4094
|
+
const pickerData = await loadDiscordModelPickerData(cfg, route.agentId);
|
|
3811
4095
|
const currentModelRef = resolveDiscordModelPickerCurrentModel({
|
|
3812
|
-
cfg
|
|
4096
|
+
cfg,
|
|
3813
4097
|
route,
|
|
3814
4098
|
data: pickerData
|
|
3815
4099
|
});
|
|
3816
4100
|
const currentRuntime = resolveDiscordModelPickerCurrentRuntime({
|
|
3817
|
-
cfg
|
|
4101
|
+
cfg,
|
|
3818
4102
|
route
|
|
3819
4103
|
});
|
|
3820
4104
|
const allowedModelRefs = buildDiscordModelPickerAllowedModelRefs(pickerData);
|
|
@@ -3911,11 +4195,22 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
3911
4195
|
currentModelRef,
|
|
3912
4196
|
data: pickerData
|
|
3913
4197
|
});
|
|
3914
|
-
const pendingModel =
|
|
4198
|
+
const pendingModel = resolveDiscordModelPickerModelSelection({
|
|
3915
4199
|
data: pickerData,
|
|
3916
4200
|
provider,
|
|
3917
|
-
modelIndex: parsed.modelIndex
|
|
4201
|
+
modelIndex: parsed.modelIndex,
|
|
4202
|
+
modelToken: parsed.modelToken,
|
|
4203
|
+
requireModelToken
|
|
3918
4204
|
});
|
|
4205
|
+
if ((parsed.modelIndex || parsed.modelToken) && !pendingModel) {
|
|
4206
|
+
await showNotice("That selection expired. Please choose a model again.");
|
|
4207
|
+
return;
|
|
4208
|
+
}
|
|
4209
|
+
const pendingModelIndex = pendingModel ? resolveDiscordModelPickerModelIndex({
|
|
4210
|
+
data: pickerData,
|
|
4211
|
+
provider,
|
|
4212
|
+
model: pendingModel
|
|
4213
|
+
}) : void 0;
|
|
3919
4214
|
await updatePicker(toDiscordModelPickerMessagePayload(renderDiscordModelPickerModelsView({
|
|
3920
4215
|
command: parsed.command,
|
|
3921
4216
|
userId: parsed.userId,
|
|
@@ -3928,7 +4223,7 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
3928
4223
|
currentModel: currentModelRef,
|
|
3929
4224
|
currentRuntime,
|
|
3930
4225
|
...pendingModel ? { pendingModel: `${provider}/${pendingModel}` } : {},
|
|
3931
|
-
pendingModelIndex:
|
|
4226
|
+
pendingModelIndex: pendingModelIndex ?? void 0,
|
|
3932
4227
|
pendingRuntime: resolvePendingRuntime({
|
|
3933
4228
|
data: pickerData,
|
|
3934
4229
|
provider,
|
|
@@ -4032,12 +4327,23 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4032
4327
|
await showNotice("Sorry, that provider isn't available anymore.");
|
|
4033
4328
|
return;
|
|
4034
4329
|
}
|
|
4035
|
-
const selectedModel =
|
|
4330
|
+
const selectedModel = resolveDiscordModelPickerModelSelection({
|
|
4036
4331
|
data: pickerData,
|
|
4037
4332
|
provider,
|
|
4038
|
-
modelIndex: parsed.modelIndex
|
|
4333
|
+
modelIndex: parsed.modelIndex,
|
|
4334
|
+
modelToken: parsed.modelToken,
|
|
4335
|
+
requireModelToken
|
|
4039
4336
|
});
|
|
4337
|
+
if ((parsed.modelIndex || parsed.modelToken) && !selectedModel) {
|
|
4338
|
+
await showNotice("That selection expired. Please choose a model again.");
|
|
4339
|
+
return;
|
|
4340
|
+
}
|
|
4040
4341
|
const pendingModel = selectedModel ? `${provider}/${selectedModel}` : void 0;
|
|
4342
|
+
const pendingModelIndex = selectedModel ? resolveDiscordModelPickerModelIndex({
|
|
4343
|
+
data: pickerData,
|
|
4344
|
+
provider,
|
|
4345
|
+
model: selectedModel
|
|
4346
|
+
}) : void 0;
|
|
4041
4347
|
const derivedProviderBucket = parsed.providerBucket ?? findProviderBucketId(pickerData, provider);
|
|
4042
4348
|
const currentModelOnly = splitDiscordModelRef(currentModelRef ?? "");
|
|
4043
4349
|
const derivedModelBucket = parsed.modelBucket ?? (selectedModel ? findModelBucketId(pickerData, provider, selectedModel) : currentModelOnly && currentModelOnly.provider === provider ? findModelBucketId(pickerData, provider, currentModelOnly.model) : void 0);
|
|
@@ -4053,7 +4359,7 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4053
4359
|
currentModel: currentModelRef,
|
|
4054
4360
|
currentRuntime,
|
|
4055
4361
|
...pendingModel ? { pendingModel } : {},
|
|
4056
|
-
pendingModelIndex:
|
|
4362
|
+
pendingModelIndex: pendingModelIndex ?? void 0,
|
|
4057
4363
|
pendingRuntime: selectedRuntime,
|
|
4058
4364
|
quickModels
|
|
4059
4365
|
})));
|
|
@@ -4063,7 +4369,8 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4063
4369
|
const modelRef = resolveSubmittedModelRef({
|
|
4064
4370
|
data: pickerData,
|
|
4065
4371
|
parsed,
|
|
4066
|
-
quickModels
|
|
4372
|
+
quickModels,
|
|
4373
|
+
requireModelToken
|
|
4067
4374
|
});
|
|
4068
4375
|
const parsedModelRef = modelRef ? splitDiscordModelRef(modelRef) : null;
|
|
4069
4376
|
if (!parsedModelRef || !pickerData.byProvider.get(parsedModelRef.provider)?.has(parsedModelRef.model)) {
|
|
@@ -4091,7 +4398,7 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4091
4398
|
interaction,
|
|
4092
4399
|
selectionCommand,
|
|
4093
4400
|
dispatchCommandInteraction: params.dispatchCommandInteraction,
|
|
4094
|
-
cfg
|
|
4401
|
+
cfg,
|
|
4095
4402
|
discordConfig: ctx.discordConfig,
|
|
4096
4403
|
accountId: ctx.accountId,
|
|
4097
4404
|
sessionPrefix: ctx.sessionPrefix,
|
|
@@ -4106,7 +4413,7 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4106
4413
|
preferenceScope,
|
|
4107
4414
|
settleMs: ctx.postApplySettleMs ?? 250,
|
|
4108
4415
|
resolveCurrentModel: (currentRoute) => resolveDiscordModelPickerCurrentModel({
|
|
4109
|
-
cfg
|
|
4416
|
+
cfg,
|
|
4110
4417
|
route: currentRoute,
|
|
4111
4418
|
data: pickerData
|
|
4112
4419
|
})
|
|
@@ -4960,6 +5267,22 @@ var GatewayReconnectTimer = class {
|
|
|
4960
5267
|
}
|
|
4961
5268
|
};
|
|
4962
5269
|
//#endregion
|
|
5270
|
+
//#region extensions/discord/src/internal/gateway-payload.ts
|
|
5271
|
+
function ensureGatewayParams(url) {
|
|
5272
|
+
const parsed = new URL(url);
|
|
5273
|
+
parsed.searchParams.set("v", parsed.searchParams.get("v") ?? "10");
|
|
5274
|
+
parsed.searchParams.set("encoding", parsed.searchParams.get("encoding") ?? "json");
|
|
5275
|
+
return parsed.toString();
|
|
5276
|
+
}
|
|
5277
|
+
function decodeGatewayMessage(incoming) {
|
|
5278
|
+
const text = Buffer.isBuffer(incoming) ? incoming.toString("utf8") : incoming instanceof ArrayBuffer ? Buffer.from(incoming).toString("utf8") : Array.isArray(incoming) ? Buffer.concat(incoming.map((entry) => Buffer.from(entry))).toString("utf8") : String(incoming);
|
|
5279
|
+
try {
|
|
5280
|
+
return JSON.parse(text);
|
|
5281
|
+
} catch {
|
|
5282
|
+
return null;
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
//#endregion
|
|
4963
5286
|
//#region extensions/discord/src/internal/gateway-rate-limit.ts
|
|
4964
5287
|
const GATEWAY_SEND_LIMIT = 120;
|
|
4965
5288
|
const GATEWAY_SEND_WINDOW_MS = 6e4;
|
|
@@ -5036,6 +5359,77 @@ var GatewaySendLimiter = class {
|
|
|
5036
5359
|
}
|
|
5037
5360
|
};
|
|
5038
5361
|
//#endregion
|
|
5362
|
+
//#region extensions/discord/src/internal/gateway-voice-state-cache.ts
|
|
5363
|
+
var DiscordGatewayVoiceStateCache = class {
|
|
5364
|
+
constructor() {
|
|
5365
|
+
this.statesByGuild = /* @__PURE__ */ new Map();
|
|
5366
|
+
this.transitionsByState = /* @__PURE__ */ new WeakMap();
|
|
5367
|
+
}
|
|
5368
|
+
clear() {
|
|
5369
|
+
this.statesByGuild.clear();
|
|
5370
|
+
this.transitionsByState = /* @__PURE__ */ new WeakMap();
|
|
5371
|
+
}
|
|
5372
|
+
listVoiceChannelStates(guildId, channelId) {
|
|
5373
|
+
const states = this.statesByGuild.get(guildId);
|
|
5374
|
+
if (!states) return [];
|
|
5375
|
+
const result = [];
|
|
5376
|
+
for (const state of states.values()) if (state.channel_id === channelId) result.push({ ...state });
|
|
5377
|
+
return result;
|
|
5378
|
+
}
|
|
5379
|
+
takeTransition(state) {
|
|
5380
|
+
const transition = this.transitionsByState.get(state);
|
|
5381
|
+
if (!transition) return null;
|
|
5382
|
+
this.transitionsByState.delete(state);
|
|
5383
|
+
return {
|
|
5384
|
+
current: { ...transition.current },
|
|
5385
|
+
...transition.previous ? { previous: { ...transition.previous } } : {}
|
|
5386
|
+
};
|
|
5387
|
+
}
|
|
5388
|
+
apply(payload) {
|
|
5389
|
+
if (payload.t === GatewayDispatchEvents.Ready) {
|
|
5390
|
+
this.clear();
|
|
5391
|
+
return;
|
|
5392
|
+
}
|
|
5393
|
+
if (payload.t === GatewayDispatchEvents.GuildCreate) {
|
|
5394
|
+
const guild = payload.d;
|
|
5395
|
+
if (guild.unavailable) {
|
|
5396
|
+
this.statesByGuild.delete(guild.id);
|
|
5397
|
+
return;
|
|
5398
|
+
}
|
|
5399
|
+
const states = /* @__PURE__ */ new Map();
|
|
5400
|
+
for (const state of guild.voice_states) if (state.channel_id) states.set(state.user_id, {
|
|
5401
|
+
...state,
|
|
5402
|
+
guild_id: guild.id
|
|
5403
|
+
});
|
|
5404
|
+
this.statesByGuild.set(guild.id, states);
|
|
5405
|
+
return;
|
|
5406
|
+
}
|
|
5407
|
+
if (payload.t === GatewayDispatchEvents.VoiceStateUpdate) {
|
|
5408
|
+
const state = payload.d;
|
|
5409
|
+
const guildId = state.guild_id?.trim();
|
|
5410
|
+
if (!guildId) return;
|
|
5411
|
+
const states = this.statesByGuild.get(guildId) ?? /* @__PURE__ */ new Map();
|
|
5412
|
+
const previous = states.get(state.user_id);
|
|
5413
|
+
const current = {
|
|
5414
|
+
...state,
|
|
5415
|
+
guild_id: guildId
|
|
5416
|
+
};
|
|
5417
|
+
this.transitionsByState.set(state, {
|
|
5418
|
+
current,
|
|
5419
|
+
...previous ? { previous: { ...previous } } : {}
|
|
5420
|
+
});
|
|
5421
|
+
if (state.channel_id) states.set(state.user_id, current);
|
|
5422
|
+
else states.delete(state.user_id);
|
|
5423
|
+
this.statesByGuild.set(guildId, states);
|
|
5424
|
+
return;
|
|
5425
|
+
}
|
|
5426
|
+
if (payload.t === GatewayDispatchEvents.GuildDelete) {
|
|
5427
|
+
const guild = payload.d;
|
|
5428
|
+
this.statesByGuild.delete(guild.id);
|
|
5429
|
+
}
|
|
5430
|
+
}
|
|
5431
|
+
};
|
|
5432
|
+
//#endregion
|
|
5039
5433
|
//#region extensions/discord/src/internal/gateway.ts
|
|
5040
5434
|
const GatewayIntents = GatewayIntentBits;
|
|
5041
5435
|
const READY_STATE_OPEN = 1;
|
|
@@ -5045,20 +5439,6 @@ const DISCORD_GATEWAY_WS_CLIENT_OPTIONS = Object.freeze({ maxPayload: 16 * 1024
|
|
|
5045
5439
|
const INVALID_SESSION_MIN_DELAY_MS = 1e3;
|
|
5046
5440
|
const INVALID_SESSION_JITTER_MS = 4e3;
|
|
5047
5441
|
const RESUME_FAILURE_THRESHOLD = 3;
|
|
5048
|
-
function ensureGatewayParams(url) {
|
|
5049
|
-
const parsed = new URL(url);
|
|
5050
|
-
parsed.searchParams.set("v", parsed.searchParams.get("v") ?? "10");
|
|
5051
|
-
parsed.searchParams.set("encoding", parsed.searchParams.get("encoding") ?? "json");
|
|
5052
|
-
return parsed.toString();
|
|
5053
|
-
}
|
|
5054
|
-
function decodeGatewayMessage(incoming) {
|
|
5055
|
-
const text = Buffer.isBuffer(incoming) ? incoming.toString("utf8") : incoming instanceof ArrayBuffer ? Buffer.from(incoming).toString("utf8") : Array.isArray(incoming) ? Buffer.concat(incoming.map((entry) => Buffer.from(entry))).toString("utf8") : String(incoming);
|
|
5056
|
-
try {
|
|
5057
|
-
return JSON.parse(text);
|
|
5058
|
-
} catch {
|
|
5059
|
-
return null;
|
|
5060
|
-
}
|
|
5061
|
-
}
|
|
5062
5442
|
var GatewayPlugin = class extends Plugin {
|
|
5063
5443
|
constructor(options, gatewayInfo) {
|
|
5064
5444
|
super();
|
|
@@ -5076,6 +5456,7 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5076
5456
|
this.isConnecting = false;
|
|
5077
5457
|
this.heartbeatTimers = new GatewayHeartbeatTimers();
|
|
5078
5458
|
this.reconnectTimer = new GatewayReconnectTimer();
|
|
5459
|
+
this.voiceStateCache = new DiscordGatewayVoiceStateCache();
|
|
5079
5460
|
this.outboundLimiter = new GatewaySendLimiter((payload) => this.sendSerializedGatewayEvent(payload), (error) => this.emitter.emit("error", error));
|
|
5080
5461
|
this.options = {
|
|
5081
5462
|
...options,
|
|
@@ -5091,6 +5472,12 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5091
5472
|
get ping() {
|
|
5092
5473
|
return null;
|
|
5093
5474
|
}
|
|
5475
|
+
listVoiceChannelStates(guildId, channelId) {
|
|
5476
|
+
return this.voiceStateCache.listVoiceChannelStates(guildId, channelId);
|
|
5477
|
+
}
|
|
5478
|
+
takeVoiceStateTransition(state) {
|
|
5479
|
+
return this.voiceStateCache.takeTransition(state);
|
|
5480
|
+
}
|
|
5094
5481
|
get heartbeatInterval() {
|
|
5095
5482
|
return this.heartbeatTimers.heartbeatInterval;
|
|
5096
5483
|
}
|
|
@@ -5138,6 +5525,7 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5138
5525
|
this.isConnected = false;
|
|
5139
5526
|
this.reconnectAttempts = 0;
|
|
5140
5527
|
this.consecutiveResumeFailures = 0;
|
|
5528
|
+
this.voiceStateCache.clear();
|
|
5141
5529
|
}
|
|
5142
5530
|
createWebSocket(url) {
|
|
5143
5531
|
return new ws.WebSocket(url, DISCORD_GATEWAY_WS_CLIENT_OPTIONS);
|
|
@@ -5316,6 +5704,7 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5316
5704
|
this.consecutiveResumeFailures = 0;
|
|
5317
5705
|
this.isConnected = true;
|
|
5318
5706
|
}
|
|
5707
|
+
this.voiceStateCache.apply(payload);
|
|
5319
5708
|
dispatchVoiceGatewayEvent(this.client, payload.t, payload.d);
|
|
5320
5709
|
const data = mapGatewayDispatchData(this.client, payload.t, payload.d);
|
|
5321
5710
|
await this.client.dispatchGatewayEvent(payload.t, data);
|
|
@@ -5326,6 +5715,7 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5326
5715
|
this.resumeGatewayUrl = null;
|
|
5327
5716
|
this.sequence = null;
|
|
5328
5717
|
this.consecutiveResumeFailures = 0;
|
|
5718
|
+
this.voiceStateCache.clear();
|
|
5329
5719
|
}
|
|
5330
5720
|
getResumeState() {
|
|
5331
5721
|
return this.sessionId && this.sequence !== null ? {
|
|
@@ -5917,7 +6307,7 @@ function formatDiscordGatewayCloseReason(reason) {
|
|
|
5917
6307
|
const text = reason.toString("utf8").replaceAll(/\s+/g, " ").trim();
|
|
5918
6308
|
if (!text) return `<${reason.length} bytes>`;
|
|
5919
6309
|
if (text.length <= DISCORD_GATEWAY_CLOSE_REASON_LOG_MAX_CHARS) return text;
|
|
5920
|
-
return `${text
|
|
6310
|
+
return `${truncateUtf16Safe(text, DISCORD_GATEWAY_CLOSE_REASON_LOG_MAX_CHARS)}...`;
|
|
5921
6311
|
}
|
|
5922
6312
|
function formatDiscordGatewayTransportErrorLog(params) {
|
|
5923
6313
|
return `discord: gateway websocket error ${[
|
|
@@ -6131,6 +6521,26 @@ var DiscordGatewayLifecycleError = class extends Error {
|
|
|
6131
6521
|
function getDiscordGatewayEmitter(gateway) {
|
|
6132
6522
|
return gateway?.emitter;
|
|
6133
6523
|
}
|
|
6524
|
+
const discordGatewayLog = createSubsystemLogger("discord/gateway");
|
|
6525
|
+
const discordGatewayLateErrorGuards = /* @__PURE__ */ new WeakMap();
|
|
6526
|
+
function removeDiscordGatewayLateErrorGuard(emitter) {
|
|
6527
|
+
const guard = discordGatewayLateErrorGuards.get(emitter);
|
|
6528
|
+
if (!guard) return;
|
|
6529
|
+
emitter.off("error", guard);
|
|
6530
|
+
discordGatewayLateErrorGuards.delete(emitter);
|
|
6531
|
+
}
|
|
6532
|
+
function ensureDiscordGatewayLateErrorGuard(emitter) {
|
|
6533
|
+
if (emitter.listenerCount("error") > 0) return;
|
|
6534
|
+
const seenMessages = /* @__PURE__ */ new Set();
|
|
6535
|
+
const guard = (err) => {
|
|
6536
|
+
const message = formatDiscordGatewayErrorMessage(err);
|
|
6537
|
+
if (seenMessages.has(message)) return;
|
|
6538
|
+
seenMessages.add(message);
|
|
6539
|
+
discordGatewayLog.error(`suppressed late gateway error after dispose: ${message}`);
|
|
6540
|
+
};
|
|
6541
|
+
discordGatewayLateErrorGuards.set(emitter, guard);
|
|
6542
|
+
emitter.on("error", guard);
|
|
6543
|
+
}
|
|
6134
6544
|
function readFirstStackFrame(err) {
|
|
6135
6545
|
const stack = err.stack;
|
|
6136
6546
|
if (!stack) return;
|
|
@@ -6212,6 +6622,7 @@ function createDiscordGatewaySupervisor(params) {
|
|
|
6212
6622
|
case "buffering": pending.push(event);
|
|
6213
6623
|
}
|
|
6214
6624
|
};
|
|
6625
|
+
removeDiscordGatewayLateErrorGuard(emitter);
|
|
6215
6626
|
emitter.on("error", onGatewayError);
|
|
6216
6627
|
return {
|
|
6217
6628
|
emitter,
|
|
@@ -6232,6 +6643,8 @@ function createDiscordGatewaySupervisor(params) {
|
|
|
6232
6643
|
},
|
|
6233
6644
|
dispose: () => {
|
|
6234
6645
|
if (phase === "disposed") return;
|
|
6646
|
+
emitter.off("error", onGatewayError);
|
|
6647
|
+
ensureDiscordGatewayLateErrorGuard(emitter);
|
|
6235
6648
|
lifecycleHandler = void 0;
|
|
6236
6649
|
phase = "disposed";
|
|
6237
6650
|
pending.length = 0;
|
|
@@ -6344,11 +6757,12 @@ function formatDiscordChannelUnresolved(entry) {
|
|
|
6344
6757
|
function formatDiscordUserResolved(entry) {
|
|
6345
6758
|
const displayName = entry.name?.trim();
|
|
6346
6759
|
const target = displayName || entry.id;
|
|
6347
|
-
|
|
6348
|
-
displayName && entry.id ? `id:${entry.id}` : void 0,
|
|
6760
|
+
const formatted = formatResolutionLogDetails(formatResolvedBase(entry.input, target), [
|
|
6761
|
+
displayName && entry.id && entry.id !== entry.input ? `id:${entry.id}` : void 0,
|
|
6349
6762
|
entry.guildName ? `guild:${entry.guildName}` : void 0,
|
|
6350
6763
|
entry.note
|
|
6351
6764
|
]);
|
|
6765
|
+
return formatted === entry.input ? null : formatted;
|
|
6352
6766
|
}
|
|
6353
6767
|
function formatDiscordUserUnresolved(entry) {
|
|
6354
6768
|
return formatResolutionLogDetails(entry.input, [
|
|
@@ -6634,9 +7048,9 @@ async function resolveDiscordProviderCommandSpecs(params) {
|
|
|
6634
7048
|
cfg: params.cfg,
|
|
6635
7049
|
getPluginCommandSpecs: params.getPluginCommandSpecs
|
|
6636
7050
|
});
|
|
6637
|
-
params.runtime.log?.(warn(
|
|
7051
|
+
params.runtime.log?.(warn(`${initialCommandCount} commands exceed the ${maxDiscordCommands}-command Discord limit; removing per-skill commands and keeping /skill.`));
|
|
6638
7052
|
}
|
|
6639
|
-
if (params.nativeEnabled && commandSpecs.length > maxDiscordCommands) params.runtime.log?.(warn(
|
|
7053
|
+
if (params.nativeEnabled && commandSpecs.length > maxDiscordCommands) params.runtime.log?.(warn(`${commandSpecs.length} commands exceed the ${maxDiscordCommands}-command Discord limit; some commands may fail to deploy.`));
|
|
6640
7054
|
return {
|
|
6641
7055
|
skillCommands,
|
|
6642
7056
|
commandSpecs
|
|
@@ -6714,9 +7128,6 @@ function readFiniteNumber(value) {
|
|
|
6714
7128
|
function readNonNegativeInteger(value) {
|
|
6715
7129
|
return parseStrictNonNegativeInteger(value);
|
|
6716
7130
|
}
|
|
6717
|
-
function formatDurationMs(ms) {
|
|
6718
|
-
return formatDurationSeconds(ms, { decimals: ms >= 1e3 ? 1 : 0 });
|
|
6719
|
-
}
|
|
6720
7131
|
function isAbortLikeError(err) {
|
|
6721
7132
|
if (!err || typeof err !== "object") return false;
|
|
6722
7133
|
const name = "name" in err && typeof err.name === "string" ? err.name : void 0;
|
|
@@ -6739,8 +7150,8 @@ function formatDiscordDeployErrorMessage(err) {
|
|
|
6739
7150
|
const operation = formatDiscordDeployRestOperation(deployErr);
|
|
6740
7151
|
if (!(requestMs !== void 0 || timeoutMs !== void 0 || deployErr.deployRestMethod !== void 0 || deployErr.deployRestPath !== void 0)) return "Discord REST request was aborted";
|
|
6741
7152
|
const timing = [];
|
|
6742
|
-
if (timeoutMs !== void 0) timing.push(`timeout=${
|
|
6743
|
-
if (requestMs !== void 0) timing.push(`observed=${
|
|
7153
|
+
if (timeoutMs !== void 0) timing.push(`timeout=${formatDurationSeconds(timeoutMs, { decimals: timeoutMs >= 1e3 ? 1 : 0 })}`);
|
|
7154
|
+
if (requestMs !== void 0) timing.push(`observed=${formatDurationSeconds(requestMs, { decimals: requestMs >= 1e3 ? 1 : 0 })}`);
|
|
6744
7155
|
const timingText = timing.length > 0 ? ` (${timing.join(", ")})` : "";
|
|
6745
7156
|
if (timeoutMs !== void 0 && requestMs !== void 0 && requestMs >= timeoutMs) return `Discord REST ${operation} timed out${timingText}`;
|
|
6746
7157
|
return `Discord REST ${operation} was aborted${timingText}`;
|
|
@@ -6774,7 +7185,7 @@ function formatDiscordDeployRateLimitDetails(err) {
|
|
|
6774
7185
|
function formatDiscordDeployRateLimitWarning(err, accountId) {
|
|
6775
7186
|
const rateLimit = resolveDiscordDeployRateLimitDetails(err);
|
|
6776
7187
|
if (!rateLimit) return;
|
|
6777
|
-
const parts = [`
|
|
7188
|
+
const parts = [`[${accountId}] slash command deploy rate limited`];
|
|
6778
7189
|
if (typeof rateLimit.retryAfterMs === "number") parts.push(`retry after ${formatDurationSeconds(rateLimit.retryAfterMs, { decimals: 1 })}`);
|
|
6779
7190
|
if (rateLimit.scope) parts.push(`scope=${rateLimit.scope}`);
|
|
6780
7191
|
if (typeof rateLimit.discordCode === "number" || typeof rateLimit.discordCode === "string") parts.push(`code=${rateLimit.discordCode}`);
|
|
@@ -6797,6 +7208,11 @@ function formatDiscordRejectedDeployEntries(params) {
|
|
|
6797
7208
|
return [parts.join(" ")];
|
|
6798
7209
|
});
|
|
6799
7210
|
}
|
|
7211
|
+
function isRedundantDiscordDeployBody(rawBody) {
|
|
7212
|
+
if (!rawBody || typeof rawBody !== "object" || Array.isArray(rawBody)) return false;
|
|
7213
|
+
const keys = Object.keys(rawBody);
|
|
7214
|
+
return keys.length > 0 && keys.every((key) => key === "message" || key === "code");
|
|
7215
|
+
}
|
|
6800
7216
|
function formatDiscordDeployErrorDetails(err) {
|
|
6801
7217
|
if (!err || typeof err !== "object") return "";
|
|
6802
7218
|
const rateLimitDetails = formatDiscordDeployRateLimitDetails(err);
|
|
@@ -6808,7 +7224,7 @@ function formatDiscordDeployErrorDetails(err) {
|
|
|
6808
7224
|
const details = [];
|
|
6809
7225
|
if (typeof status === "number") details.push(`status=${status}`);
|
|
6810
7226
|
if (typeof discordCode === "number" || typeof discordCode === "string") details.push(`code=${discordCode}`);
|
|
6811
|
-
if (rawBody !== void 0) {
|
|
7227
|
+
if (rawBody !== void 0 && !isRedundantDiscordDeployBody(rawBody)) {
|
|
6812
7228
|
let bodyText;
|
|
6813
7229
|
try {
|
|
6814
7230
|
bodyText = JSON.stringify(rawBody);
|
|
@@ -6820,7 +7236,7 @@ function formatDiscordDeployErrorDetails(err) {
|
|
|
6820
7236
|
}
|
|
6821
7237
|
if (bodyText) {
|
|
6822
7238
|
const maxLen = 800;
|
|
6823
|
-
const trimmed = bodyText.length > maxLen ? `${bodyText
|
|
7239
|
+
const trimmed = bodyText.length > maxLen ? `${truncateUtf16Safe(bodyText, maxLen)}...` : bodyText;
|
|
6824
7240
|
details.push(`body=${trimmed}`);
|
|
6825
7241
|
}
|
|
6826
7242
|
}
|
|
@@ -6856,8 +7272,12 @@ function logDiscordStartupPhase$1(params) {
|
|
|
6856
7272
|
function readDeployRequestBody(data) {
|
|
6857
7273
|
return data && typeof data === "object" && "body" in data ? data.body : void 0;
|
|
6858
7274
|
}
|
|
7275
|
+
function isDeployCommandsPath(path) {
|
|
7276
|
+
return path.startsWith("/applications/") && path.includes("/commands");
|
|
7277
|
+
}
|
|
6859
7278
|
function wrapDeployRestMethod(params) {
|
|
6860
7279
|
return async (path, data, query) => {
|
|
7280
|
+
if (!isDeployCommandsPath(path)) return params.original[params.method](path, data, query);
|
|
6861
7281
|
const startedAt = Date.now();
|
|
6862
7282
|
const body = readDeployRequestBody(data);
|
|
6863
7283
|
const commandCount = Array.isArray(body) ? body.length : void 0;
|
|
@@ -6879,7 +7299,7 @@ function wrapDeployRestMethod(params) {
|
|
|
6879
7299
|
const rateLimitDetails = formatDiscordDeployRateLimitDetails(err);
|
|
6880
7300
|
if (rateLimitDetails) {
|
|
6881
7301
|
if (params.shouldLogVerbose()) params.runtime.log?.(warn(`discord startup [${params.accountId}] native-slash-command-deploy-rest:${params.method}:rate-limited ${Math.max(0, Date.now() - params.startupStartedAt)}ms path=${path} requestMs=${requestMs}${rateLimitDetails}`));
|
|
6882
|
-
} else {
|
|
7302
|
+
} else if (params.shouldLogVerbose()) {
|
|
6883
7303
|
const details = formatDiscordDeployErrorDetails(err);
|
|
6884
7304
|
params.runtime.error?.(`discord startup [${params.accountId}] native-slash-command-deploy-rest:${params.method}:error ${Math.max(0, Date.now() - params.startupStartedAt)}ms path=${path} requestMs=${requestMs} error=${formatDiscordDeployErrorMessage(err)}${details}`);
|
|
6885
7305
|
}
|
|
@@ -6931,7 +7351,7 @@ async function deployDiscordCommands(params) {
|
|
|
6931
7351
|
await params.client.deployCommands({ mode: "reconcile" });
|
|
6932
7352
|
} catch (err) {
|
|
6933
7353
|
if (isDiscordDeployDailyCreateLimit(err)) {
|
|
6934
|
-
params.runtime.log?.(warn(`
|
|
7354
|
+
params.runtime.log?.(warn(`[${accountId}] slash command deploy skipped: daily application command create limit reached. Existing slash commands stay active until Discord resets the quota; message send/receive is unaffected.`));
|
|
6935
7355
|
return;
|
|
6936
7356
|
}
|
|
6937
7357
|
const rateLimitWarning = formatDiscordDeployRateLimitWarning(err, accountId);
|
|
@@ -6942,7 +7362,7 @@ async function deployDiscordCommands(params) {
|
|
|
6942
7362
|
throw err;
|
|
6943
7363
|
}
|
|
6944
7364
|
} catch (err) {
|
|
6945
|
-
params.runtime.log?.(warn(`
|
|
7365
|
+
params.runtime.log?.(warn(`[${accountId}] slash command deploy failed (message send/receive unaffected): ${formatDiscordDeployErrorMessage(err)}${formatDiscordDeployErrorDetails(err)}`));
|
|
6946
7366
|
} finally {
|
|
6947
7367
|
restoreDeployRestLogging();
|
|
6948
7368
|
}
|
|
@@ -6967,7 +7387,7 @@ function runDiscordCommandDeployInBackground(params) {
|
|
|
6967
7387
|
isVerbose: params.isVerbose
|
|
6968
7388
|
});
|
|
6969
7389
|
}).catch((err) => {
|
|
6970
|
-
params.runtime.log?.(warn(`
|
|
7390
|
+
params.runtime.log?.(warn(`[${params.accountId}] slash command deploy failed in background (message send/receive unaffected): ${formatErrorMessage$1(err)}`));
|
|
6971
7391
|
});
|
|
6972
7392
|
}
|
|
6973
7393
|
//#endregion
|
|
@@ -7031,8 +7451,8 @@ async function authorizeDiscordVoiceIngress(params) {
|
|
|
7031
7451
|
return resolveCommandAuthorizedFromAuthorizers({
|
|
7032
7452
|
useAccessGroups,
|
|
7033
7453
|
authorizers: useAccessGroups ? [{
|
|
7034
|
-
configured: ownerAllowList != null,
|
|
7035
|
-
allowed: ownerAllowed
|
|
7454
|
+
configured: params.ownerAllowAll === true || ownerAllowList != null,
|
|
7455
|
+
allowed: params.ownerAllowAll === true || ownerAllowed
|
|
7036
7456
|
}, {
|
|
7037
7457
|
configured: hasAccessRestrictions,
|
|
7038
7458
|
allowed: memberAllowed
|
|
@@ -7050,6 +7470,22 @@ async function authorizeDiscordVoiceIngress(params) {
|
|
|
7050
7470
|
};
|
|
7051
7471
|
}
|
|
7052
7472
|
//#endregion
|
|
7473
|
+
//#region extensions/discord/src/voice/owner-access.ts
|
|
7474
|
+
function resolveDiscordVoiceOwnerAccess(params) {
|
|
7475
|
+
const commandOwnerAllowFrom = resolveDiscordCommandOwnerAllowFrom(params.cfg);
|
|
7476
|
+
if (commandOwnerAllowFrom) return {
|
|
7477
|
+
ownerAllowFrom: commandOwnerAllowFrom,
|
|
7478
|
+
ownerAllowAll: commandOwnerAllowFrom.includes("*")
|
|
7479
|
+
};
|
|
7480
|
+
return {
|
|
7481
|
+
ownerAllowFrom: resolveDiscordAccountAllowFrom({
|
|
7482
|
+
cfg: params.cfg,
|
|
7483
|
+
accountId: params.accountId
|
|
7484
|
+
}) ?? params.discordConfig.allowFrom ?? params.discordConfig.dm?.allowFrom ?? [],
|
|
7485
|
+
ownerAllowAll: false
|
|
7486
|
+
};
|
|
7487
|
+
}
|
|
7488
|
+
//#endregion
|
|
7053
7489
|
//#region extensions/discord/src/voice/command.ts
|
|
7054
7490
|
const VOICE_CHANNEL_TYPES = [ChannelType.GuildVoice, ChannelType.GuildStageVoice];
|
|
7055
7491
|
async function authorizeVoiceCommand(interaction, params, options) {
|
|
@@ -7076,6 +7512,7 @@ async function authorizeVoiceCommand(interaction, params, options) {
|
|
|
7076
7512
|
author: user,
|
|
7077
7513
|
member: interaction.rawData.member
|
|
7078
7514
|
});
|
|
7515
|
+
const ownerAccess = resolveDiscordVoiceOwnerAccess(params);
|
|
7079
7516
|
const access = await authorizeDiscordVoiceIngress({
|
|
7080
7517
|
cfg: params.cfg,
|
|
7081
7518
|
discordConfig: params.discordConfig,
|
|
@@ -7093,10 +7530,8 @@ async function authorizeVoiceCommand(interaction, params, options) {
|
|
|
7093
7530
|
scope: channelContext.isThreadChannel ? "thread" : "channel",
|
|
7094
7531
|
channelLabel: channelId ? formatMention({ channelId }) : "This channel",
|
|
7095
7532
|
memberRoleIds,
|
|
7096
|
-
ownerAllowFrom:
|
|
7097
|
-
|
|
7098
|
-
accountId: params.accountId
|
|
7099
|
-
}),
|
|
7533
|
+
ownerAllowFrom: ownerAccess.ownerAllowFrom,
|
|
7534
|
+
ownerAllowAll: ownerAccess.ownerAllowAll,
|
|
7100
7535
|
sender: {
|
|
7101
7536
|
id: sender.id,
|
|
7102
7537
|
name: sender.name,
|
|
@@ -7741,16 +8176,7 @@ function mapOptionLabels(options, values) {
|
|
|
7741
8176
|
}
|
|
7742
8177
|
function parseAgentComponentData(data) {
|
|
7743
8178
|
const raw = readParsedComponentId(data);
|
|
7744
|
-
const
|
|
7745
|
-
if (!value.includes("%")) return value;
|
|
7746
|
-
if (!/%[0-9A-Fa-f]{2}/.test(value)) return value;
|
|
7747
|
-
try {
|
|
7748
|
-
return decodeURIComponent(value);
|
|
7749
|
-
} catch {
|
|
7750
|
-
return value;
|
|
7751
|
-
}
|
|
7752
|
-
};
|
|
7753
|
-
const componentId = typeof raw === "string" ? decodeSafe(raw) : typeof raw === "number" ? String(raw) : null;
|
|
8179
|
+
const componentId = typeof raw === "string" ? decodeCustomIdComponent(raw) : typeof raw === "number" ? String(raw) : null;
|
|
7754
8180
|
if (!componentId) return null;
|
|
7755
8181
|
return { componentId };
|
|
7756
8182
|
}
|
|
@@ -7933,6 +8359,10 @@ function sanitizeDiscordFrontChannelReplyPayloads(payloads, options = {}) {
|
|
|
7933
8359
|
}
|
|
7934
8360
|
//#endregion
|
|
7935
8361
|
//#region extensions/discord/src/monitor/reply-delivery.ts
|
|
8362
|
+
function formatDiscordReplyDeliveryFailure(params) {
|
|
8363
|
+
const context = [`target=${params.target}`, params.sessionKey ? `session=${params.sessionKey}` : void 0].filter(Boolean).join(" ");
|
|
8364
|
+
return `discord ${params.kind} reply failed (${context}): ${String(params.err)}`;
|
|
8365
|
+
}
|
|
7936
8366
|
function resolveTargetChannelId(target) {
|
|
7937
8367
|
if (!target.startsWith("channel:")) return;
|
|
7938
8368
|
return target.slice(8).trim() || void 0;
|
|
@@ -8037,7 +8467,7 @@ async function deliverDiscordReply(params) {
|
|
|
8037
8467
|
//#endregion
|
|
8038
8468
|
//#region extensions/discord/src/monitor/agent-components.dispatch.ts
|
|
8039
8469
|
const loadConversationRuntime$1 = createLazyRuntimeModule(() => import("./agent-components.runtime-CEPrf2SY.js"));
|
|
8040
|
-
const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-
|
|
8470
|
+
const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-BnYT7Szq.js").then((n) => n.n));
|
|
8041
8471
|
function buildDiscordComponentConversationLabel(params) {
|
|
8042
8472
|
if (params.interactionCtx.isDirectMessage) return buildDirectLabel(params.interactionCtx.user);
|
|
8043
8473
|
if (params.interactionCtx.isGroupDm) return `Group DM #${params.channelCtx.channelName ?? params.interactionCtx.channelId} channel id:${params.interactionCtx.channelId}`;
|
|
@@ -8450,7 +8880,7 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
|
|
|
8450
8880
|
}
|
|
8451
8881
|
//#endregion
|
|
8452
8882
|
//#region extensions/discord/src/monitor/agent-components.handlers.ts
|
|
8453
|
-
const loadComponentsRuntime = createLazyRuntimeModule(() => import("./components-
|
|
8883
|
+
const loadComponentsRuntime = createLazyRuntimeModule(() => import("./components-BVrfRS-F.js").then((n) => n.t));
|
|
8454
8884
|
async function handleDiscordComponentEvent(params) {
|
|
8455
8885
|
const parsed = parseDiscordComponentData(params.data, resolveInteractionCustomId(params.interaction));
|
|
8456
8886
|
if (!parsed) {
|
|
@@ -9017,24 +9447,26 @@ function createDiscordComponentModal(ctx) {
|
|
|
9017
9447
|
}
|
|
9018
9448
|
//#endregion
|
|
9019
9449
|
//#region extensions/discord/src/monitor/exec-approvals.ts
|
|
9020
|
-
function
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
}
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
const coerce = (value) => typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
9030
|
-
const rawId = coerce(data.id);
|
|
9031
|
-
const rawAction = coerce(data.action);
|
|
9032
|
-
if (!rawId || !rawAction) return null;
|
|
9033
|
-
const action = rawAction;
|
|
9034
|
-
if (action !== "allow-once" && action !== "allow-always" && action !== "deny") return null;
|
|
9450
|
+
function resolveTerminalLabel(approval) {
|
|
9451
|
+
if (approval.status === "allowed") return approval.decision === "allow-always" ? "Allowed always" : "Allowed once";
|
|
9452
|
+
if (approval.status === "denied") return "Denied";
|
|
9453
|
+
return approval.status === "expired" ? "Expired" : "Cancelled";
|
|
9454
|
+
}
|
|
9455
|
+
function buildTerminalPayload(params) {
|
|
9456
|
+
const { approval } = params;
|
|
9457
|
+
const label = resolveTerminalLabel(approval);
|
|
9458
|
+
const accentColor = approval.status === "denied" ? "#ED4245" : approval.status === "allowed" ? "#57F287" : "#99AAB5";
|
|
9035
9459
|
return {
|
|
9036
|
-
|
|
9037
|
-
|
|
9460
|
+
allowed_mentions: DISCORD_APPROVAL_ALLOWED_MENTIONS,
|
|
9461
|
+
components: [new Container([
|
|
9462
|
+
new TextDisplay(params.applied ? "## Approval resolved" : "## Approval already resolved"),
|
|
9463
|
+
new TextDisplay(`Canonical result: **${label}**`),
|
|
9464
|
+
new Separator({
|
|
9465
|
+
divider: false,
|
|
9466
|
+
spacing: "small"
|
|
9467
|
+
}),
|
|
9468
|
+
new TextDisplay(`-# ID: ${formatDiscordApprovalDisplayValue(approval.id)}`)
|
|
9469
|
+
], { accentColor })]
|
|
9038
9470
|
};
|
|
9039
9471
|
}
|
|
9040
9472
|
function isStructuredApprovalNotFoundError(err) {
|
|
@@ -9067,7 +9499,7 @@ var ExecApprovalButton = class extends Button {
|
|
|
9067
9499
|
if (!approvers.some((id) => id === userId)) {
|
|
9068
9500
|
try {
|
|
9069
9501
|
await interaction.reply({
|
|
9070
|
-
content: "⛔ You are not authorized to approve
|
|
9502
|
+
content: "⛔ You are not authorized to approve requests.",
|
|
9071
9503
|
ephemeral: true
|
|
9072
9504
|
});
|
|
9073
9505
|
} catch {}
|
|
@@ -9077,10 +9509,28 @@ var ExecApprovalButton = class extends Button {
|
|
|
9077
9509
|
try {
|
|
9078
9510
|
await interaction.acknowledge();
|
|
9079
9511
|
} catch {}
|
|
9080
|
-
const result = await this.ctx.resolveApproval(parsed.approvalId, parsed.action);
|
|
9081
|
-
if (!result.ok)
|
|
9512
|
+
const result = await this.ctx.resolveApproval(parsed.approvalId, parsed.approvalKind, parsed.action);
|
|
9513
|
+
if (!result.ok) {
|
|
9514
|
+
try {
|
|
9515
|
+
await interaction.followUp({
|
|
9516
|
+
content: result.reason === "not-found" ? `That approval request is no longer pending. It may have expired or already been resolved.` : `Failed to submit approval decision for **${decisionLabel}**. The request may have expired or already been resolved.`,
|
|
9517
|
+
ephemeral: true
|
|
9518
|
+
});
|
|
9519
|
+
} catch {}
|
|
9520
|
+
return;
|
|
9521
|
+
}
|
|
9522
|
+
const terminalLabel = resolveTerminalLabel(result.resolution.approval);
|
|
9523
|
+
let terminalized = false;
|
|
9524
|
+
try {
|
|
9525
|
+
await interaction.editReply(buildTerminalPayload({
|
|
9526
|
+
approval: result.resolution.approval,
|
|
9527
|
+
applied: result.resolution.applied
|
|
9528
|
+
}));
|
|
9529
|
+
terminalized = true;
|
|
9530
|
+
} catch {}
|
|
9531
|
+
if (!terminalized || !result.resolution.applied) try {
|
|
9082
9532
|
await interaction.followUp({
|
|
9083
|
-
content: result.
|
|
9533
|
+
content: result.resolution.applied ? `Approval resolved: ${terminalLabel}.` : `This approval was already resolved: ${terminalLabel}.`,
|
|
9084
9534
|
ephemeral: true
|
|
9085
9535
|
});
|
|
9086
9536
|
} catch {}
|
|
@@ -9096,16 +9546,19 @@ function createDiscordExecApprovalButtonContext(params) {
|
|
|
9096
9546
|
accountId: params.accountId,
|
|
9097
9547
|
configOverride: params.config
|
|
9098
9548
|
}),
|
|
9099
|
-
resolveApproval: async (approvalId, decision) => {
|
|
9549
|
+
resolveApproval: async (approvalId, approvalKind, decision) => {
|
|
9100
9550
|
try {
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9551
|
+
return {
|
|
9552
|
+
ok: true,
|
|
9553
|
+
resolution: await resolveApprovalOverGateway({
|
|
9554
|
+
cfg: params.cfg,
|
|
9555
|
+
approvalId,
|
|
9556
|
+
approvalKind,
|
|
9557
|
+
decision,
|
|
9558
|
+
gatewayUrl: params.gatewayUrl,
|
|
9559
|
+
clientDisplayName: `Discord approval (${params.accountId})`
|
|
9560
|
+
})
|
|
9561
|
+
};
|
|
9109
9562
|
} catch (err) {
|
|
9110
9563
|
return {
|
|
9111
9564
|
ok: false,
|
|
@@ -9143,6 +9596,11 @@ function createDiscordProviderInteractionSurface(params) {
|
|
|
9143
9596
|
accountId: params.accountId,
|
|
9144
9597
|
configOverride: execApprovalsConfig
|
|
9145
9598
|
});
|
|
9599
|
+
const approvalActionsEnabled = getDiscordExecApprovalApprovers({
|
|
9600
|
+
cfg: params.cfg,
|
|
9601
|
+
accountId: params.accountId,
|
|
9602
|
+
configOverride: execApprovalsConfig
|
|
9603
|
+
}).length > 0;
|
|
9146
9604
|
if (execApprovalsEnabled) registerChannelRuntimeContext({
|
|
9147
9605
|
channelRuntime: params.channelRuntime,
|
|
9148
9606
|
channelId: "discord",
|
|
@@ -9178,7 +9636,7 @@ function createDiscordProviderInteractionSurface(params) {
|
|
|
9178
9636
|
})
|
|
9179
9637
|
];
|
|
9180
9638
|
const modals = [];
|
|
9181
|
-
if (
|
|
9639
|
+
if (approvalActionsEnabled) components.push(createExecApprovalButton(createDiscordExecApprovalButtonContext({
|
|
9182
9640
|
cfg: params.cfg,
|
|
9183
9641
|
accountId: params.accountId,
|
|
9184
9642
|
config: execApprovalsConfig
|
|
@@ -9668,7 +10126,7 @@ async function runDiscordGatewayLifecycle(params) {
|
|
|
9668
10126
|
gatewayEmitter?.removeListener(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, onGatewayTransportActivity);
|
|
9669
10127
|
if (params.voiceManager) {
|
|
9670
10128
|
await params.voiceManager.destroy();
|
|
9671
|
-
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-
|
|
10129
|
+
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CLZ5X8fh.js").then((n) => n.n);
|
|
9672
10130
|
setDiscordTranscriptsVoiceManager({
|
|
9673
10131
|
accountId: params.accountId,
|
|
9674
10132
|
manager: null
|
|
@@ -9863,10 +10321,17 @@ function registerDiscordMonitorListeners(params) {
|
|
|
9863
10321
|
}
|
|
9864
10322
|
registerDiscordListener(params.client.listeners, new DiscordThreadUpdateListener(params.cfg, params.accountId, params.logger));
|
|
9865
10323
|
if (params.discordConfig.intents?.presence) {
|
|
9866
|
-
|
|
10324
|
+
const presenceListener = new DiscordPresenceListener({
|
|
10325
|
+
cfg: params.cfg,
|
|
9867
10326
|
logger: params.logger,
|
|
9868
|
-
accountId: params.accountId
|
|
9869
|
-
|
|
10327
|
+
accountId: params.accountId,
|
|
10328
|
+
botUserId: params.botUserId,
|
|
10329
|
+
guildEntries: params.guildEntries
|
|
10330
|
+
});
|
|
10331
|
+
registerDiscordListener(params.client.listeners, presenceListener);
|
|
10332
|
+
registerDiscordListener(params.client.listeners, new DiscordPresenceGuildCreateListener(presenceListener));
|
|
10333
|
+
registerDiscordListener(params.client.listeners, new DiscordPresenceGuildDeleteListener(presenceListener));
|
|
10334
|
+
registerDiscordListener(params.client.listeners, new DiscordPresenceReadyListener(presenceListener));
|
|
9870
10335
|
params.runtime.log?.("discord: GuildPresences intent enabled — presence listener registered");
|
|
9871
10336
|
}
|
|
9872
10337
|
}
|
|
@@ -9900,7 +10365,7 @@ function createEnvProxyDiscordRestDispatcher(runtime) {
|
|
|
9900
10365
|
}
|
|
9901
10366
|
}
|
|
9902
10367
|
function createDiscordRestFetchWithDispatcher(dispatcher) {
|
|
9903
|
-
return wrapFetchWithAbortSignal(((input, init) =>
|
|
10368
|
+
return wrapFetchWithAbortSignal(((input, init) => fetchWithRuntimeDispatcher(input, {
|
|
9904
10369
|
...init,
|
|
9905
10370
|
dispatcher
|
|
9906
10371
|
}).then((response) => {
|
|
@@ -9946,7 +10411,7 @@ function logDiscordStartupPhase(params) {
|
|
|
9946
10411
|
});
|
|
9947
10412
|
}
|
|
9948
10413
|
async function loadDiscordVoiceRuntime() {
|
|
9949
|
-
const promise = discordVoiceRuntimePromise ?? import("./manager.runtime-
|
|
10414
|
+
const promise = discordVoiceRuntimePromise ?? import("./manager.runtime-3RVMVkyx.js");
|
|
9950
10415
|
discordVoiceRuntimePromise = promise;
|
|
9951
10416
|
try {
|
|
9952
10417
|
return await promise;
|
|
@@ -9956,7 +10421,7 @@ async function loadDiscordVoiceRuntime() {
|
|
|
9956
10421
|
}
|
|
9957
10422
|
}
|
|
9958
10423
|
async function loadDiscordProviderSessionRuntime() {
|
|
9959
|
-
const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-
|
|
10424
|
+
const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-D5pCGibh.js");
|
|
9960
10425
|
discordProviderSessionRuntimePromise = promise;
|
|
9961
10426
|
try {
|
|
9962
10427
|
return await promise;
|
|
@@ -10218,7 +10683,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10218
10683
|
});
|
|
10219
10684
|
let voiceManager = null;
|
|
10220
10685
|
if (voiceEnabled) {
|
|
10221
|
-
const { DiscordVoiceManager, DiscordVoiceReadyListener, DiscordVoiceResumedListener, DiscordVoiceStateUpdateListener } = await loadDiscordVoiceRuntime();
|
|
10686
|
+
const { DiscordVoiceGuildCreateListener, DiscordVoiceManager, DiscordVoiceReadyListener, DiscordVoiceResumedListener, DiscordVoiceStateUpdateListener } = await loadDiscordVoiceRuntime();
|
|
10222
10687
|
voiceManager = new DiscordVoiceManager({
|
|
10223
10688
|
client,
|
|
10224
10689
|
cfg,
|
|
@@ -10227,12 +10692,13 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10227
10692
|
runtime,
|
|
10228
10693
|
botUserId
|
|
10229
10694
|
});
|
|
10230
|
-
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-
|
|
10695
|
+
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CLZ5X8fh.js").then((n) => n.n);
|
|
10231
10696
|
setDiscordTranscriptsVoiceManager({
|
|
10232
10697
|
accountId: account.accountId,
|
|
10233
10698
|
manager: voiceManager
|
|
10234
10699
|
});
|
|
10235
10700
|
voiceManagerRef.current = voiceManager;
|
|
10701
|
+
registerDiscordListener(client.listeners, new DiscordVoiceGuildCreateListener(voiceManager));
|
|
10236
10702
|
registerDiscordListener(client.listeners, new DiscordVoiceReadyListener(voiceManager));
|
|
10237
10703
|
registerDiscordListener(client.listeners, new DiscordVoiceResumedListener(voiceManager));
|
|
10238
10704
|
registerDiscordListener(client.listeners, new DiscordVoiceStateUpdateListener(voiceManager));
|
|
@@ -10332,4 +10798,4 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10332
10798
|
}
|
|
10333
10799
|
}
|
|
10334
10800
|
//#endregion
|
|
10335
|
-
export {
|
|
10801
|
+
export { resolveDiscordTextCommandAccess as A, registerDiscordListener as C, resolveDiscordThreadStarter as D, resolveDiscordReplyTarget as E, sanitizeDiscordThreadName as O, handleDiscordDmCommandDecision as S, resolveDiscordAutoThreadReplyPlan as T, createDiscordSupplementalContextAccessChecker as _, buildDirectLabel as a, resolveDiscordEffectiveRoute as b, resolveDiscordVoiceOwnerAccess as c, resolveDiscordGatewayIntents as d, waitForDiscordGatewayPluginRegistration as f, buildDiscordInboundAccessContext as g, buildDiscordGroupSystemPrompt as h, sanitizeDiscordFrontChannelReplyPayloads as i, resolveDiscordDmCommandAccess as k, authorizeDiscordVoiceIngress as l, createDiscordNativeCommand as m, deliverDiscordReply as n, buildGuildLabel as o, resolveDiscordVoiceEnabled as p, formatDiscordReplyDeliveryFailure as r, resolveReplyContext as s, monitorDiscordProvider as t, createDiscordGatewayPlugin as u, buildDiscordRoutePeer as v, threading_exports as w, shouldIgnoreStaleDiscordRouteBinding as x, resolveDiscordConversationRoute as y };
|