@openclaw/discord 2026.7.2-beta.7 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-inspect-SXiCBCgP.js → account-inspect-D5xmUpnj.js} +49 -43
- package/dist/account-inspect-api.js +1 -1
- package/dist/accounts-DNl-oCEw.js +371 -0
- package/dist/action-runtime-api.js +1 -1
- package/dist/{active-turn-thread-route-MlKWp8RD.js → active-turn-thread-route-fmHl1HNd.js} +9 -11
- package/dist/{activities-api-DTKsuDxf.js → activities-api-KCBAmWxD.js} +133 -175
- package/dist/activities-api.js +1 -1
- package/dist/{allow-list-DzKrtfah.js → allow-list-BDMimASD.js} +2 -3
- package/dist/{api-DnrK9gML.js → api-DNVbJyEy.js} +7 -2
- package/dist/api.js +18 -18
- package/dist/{approval-handler.runtime-HymvFTYn.js → approval-handler.runtime-B12RCYr8.js} +6 -6
- package/dist/{approval-message-safety-DEyKVsAw.js → approval-message-safety-XGFPbDoI.js} +89 -3
- package/dist/{audit-CjsrabP8.js → audit-Yc7eZDd6.js} +5 -5
- package/dist/{channel-actions-uXFTJX3o.js → channel-actions-Dr2QI8Bf.js} +11 -7
- package/dist/{channel-actions.runtime-CIYU90sl.js → channel-actions.runtime-CODTvo7c.js} +99 -97
- package/dist/{channel-vKa-76xY.js → channel-cXYzGc4L.js} +102 -104
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-CmcwZ_qd.js → channel.setup-CSiwnMgs.js} +16 -54
- package/dist/{outbound-session-route-H-HoyH5U.js → components-ezGKo_Yh.js} +69 -142
- package/dist/{config-schema-WHRQcY19.js → config-schema-DC5kDtQ5.js} +42 -11
- package/dist/conversation-binding-route-D6rCQ5S0.js +43 -0
- package/dist/{directory-config-D_LojtCe.js → directory-config-XyCfoqLg.js} +2 -2
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-Bw5Ephzv.js → directory-live-Df0NbFJB.js} +7 -4
- package/dist/{doctor-Bc4S1qyd.js → doctor-DYY31GrO.js} +34 -13
- package/dist/{doctor-contract-CCFMIDk9.js → doctor-contract-DsWvKIXF.js} +2 -3
- package/dist/doctor-contract-api.js +147 -2
- package/dist/{handle-action.guild-admin-D6VRRW-C.js → handle-action.guild-admin-6Tvvinsa.js} +4 -7
- package/dist/{inbound-event-delivery-CEPlt2uz.js → inbound-event-delivery-De2qxTrP.js} +4 -25
- package/dist/index.js +5 -4
- package/dist/{message-handler-CeiSulIw.js → message-handler-B61q5MOF.js} +118 -33
- package/dist/{message-handler.preflight-DibgQFf2.js → message-handler.preflight-Bzg_Hawz.js} +75 -61
- package/dist/{message-handler.process-Dnc_oz9y.js → message-handler.process--4_712Ec.js} +236 -296
- package/dist/{message-utils-BgTwT_q7.js → message-utils-BXLLnFl6.js} +34 -35
- package/dist/{model-picker-preference-primitives-D25pnCOu.js → model-picker-preference-primitives-BHT1LkR_.js} +3 -3
- package/dist/{outbound-adapter-DtE-i2OJ.js → outbound-adapter-DxHTOqiN.js} +157 -68
- package/dist/{outbound-components-C7GVyfPh.js → outbound-components-B2PL0ooH.js} +3 -3
- package/dist/owner-access-Bg41VzAf.js +107 -0
- package/dist/{pluralkit-CD4B11JB.js → pluralkit-dUl4v1X3.js} +1 -1
- package/dist/{preflight-audio-BjnQZ5yS.js → preflight-audio-DrnqFA8U.js} +22 -28
- package/dist/{probe-BeWKhjqX.js → probe-BTdtVRwU.js} +37 -13
- package/dist/probe.runtime-Dq5-PRKT.js +2 -0
- package/dist/{provider-DfQJX3z5.js → provider-DlK_ynqt.js} +1131 -2021
- package/dist/{provider-session.runtime-Bo88k0pV.js → provider-session.runtime-CvvphHvs.js} +3 -3
- package/dist/provider.runtime-CzKGRiAF.js +2 -0
- package/dist/realtime-session.runtime-ysbMG8v7.js +1351 -0
- package/dist/{resolve-allowlist-common-DHERApxi.js → resolve-allowlist-common-DIVXbD1P.js} +3 -3
- package/dist/{resolve-channels-BjepAj8b.js → resolve-channels-CGeUoIqb.js} +4 -4
- package/dist/{resolve-users-Bc28htgt.js → resolve-users-BtKxLUZv.js} +3 -3
- package/dist/{chunk-DX4jpFQP.js → retry-DNsVTeTo.js} +308 -131
- package/dist/{runtime-BmW_j_hl.js → runtime-CQovMsH3.js} +12 -15
- package/dist/{runtime-_aRC6V2s.js → runtime-D71_VFLr.js} +49 -58
- 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-CzeTYF32.js +6 -0
- package/dist/runtime-api.monitor.js +6 -5
- package/dist/runtime-api.send.js +6 -6
- package/dist/runtime-api.threads.js +5 -6
- package/dist/sdk-runtime-37OU6_Yh.js +369 -0
- package/dist/{secret-config-contract-B1akrlyY.js → secret-config-contract-B3i6WxV5.js} +53 -25
- package/dist/secret-contract-api.js +1 -1
- package/dist/security-audit-contract-api.js +1 -1
- package/dist/{security-audit.runtime-CPN0xFYz.js → security-audit.runtime-BVKF9VtY.js} +1 -1
- package/dist/{send-CdkZ6zeW.js → send-rQIffSYu.js} +180 -91
- package/dist/{send.components-5uqwkjgE.js → send.components-ChK1wS7w.js} +32 -46
- package/dist/{send.outbound-D-80Ej-t.js → send.outbound-HIkgji_7.js} +57 -34
- package/dist/{send.reactions-CtlmGd_o.js → send.reactions-gmJ6yFPo.js} +14 -21
- package/dist/{send.receipt-CuwRrpFz.js → send.receipt-Cnhoautb.js} +29 -1
- package/dist/{send.shared-jpvAG_u6.js → send.shared-B3fpaLWJ.js} +112 -181
- package/dist/{sender-identity-mG_zsdox.js → sender-identity-BJtfyise.js} +1 -1
- package/dist/setup-entry.js +0 -5
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{shared-CXF1tup1.js → shared-CaAaoksy.js} +27 -23
- package/dist/subagent-hooks-CNT2ZJTJ.js +49 -0
- package/dist/subagent-hooks-api.js +2 -6
- package/dist/subagent-progress-FPLqBY1s.js +110 -0
- package/dist/{system-events-DdUqf91B.js → system-events-Nuwoq_au.js} +2 -2
- package/dist/{target-resolver-C3s_2m0t.js → target-resolver-DIXShHaZ.js} +3 -3
- package/dist/targets-B2pf49zc.js +3 -0
- package/dist/{thread-bindings-C-M-ly7P.js → thread-bindings-VlymUnbz.js} +29 -28
- package/dist/{thread-bindings.manager-DYvSlrva.js → thread-bindings.manager-CngsgErq.js} +258 -29
- package/dist/{thread-bindings.session-updates-DAIlp7Px.js → thread-bindings.session-updates-CJb-BpPL.js} +1 -1
- package/dist/{thread-bindings.state-Cgi5uRn-.js → thread-bindings.state-D8OMdk4O.js} +3 -16
- package/dist/threading-DU6pgeGm.js +456 -0
- package/dist/transcripts-source-DeIMNbRh.js +265 -0
- package/dist/transcripts-source-api.js +7 -2
- package/dist/{typing--Mzo2kM7.js → typing-oLiS5Z13.js} +1 -1
- package/dist/voice-runtime-CjNFaynA.js +2549 -0
- package/openclaw.plugin.json +215 -25
- package/package.json +15 -11
- package/skills/discord/SKILL.md +22 -115
- package/dist/accounts-DJTf1LtA.js +0 -219
- package/dist/channel-api-CAJ0wMoV.js +0 -21
- package/dist/legacy-state-migrations-api.js +0 -138
- package/dist/manager.runtime-Bjav3pfC.js +0 -3655
- package/dist/preflight-audio.runtime-CoCXMM8r.js +0 -7
- package/dist/probe.runtime-DqFGFMbZ.js +0 -2
- package/dist/provider.runtime-Cy7XZmyh.js +0 -2
- package/dist/runtime-api.monitor-CCRubd3-.js +0 -5
- package/dist/session-binding-contract-api.js +0 -2
- package/dist/subagent-hooks-Dxy7Fmnq.js +0 -120
- package/dist/subagent-progress-DI3sYLLo.js +0 -664
- package/dist/targets-Dv_RN7vT.js +0 -3
- package/dist/thread-bindings.discord-api-BoDNqamb.js +0 -246
- package/dist/transcripts-source-DIKGsTGT.js +0 -123
- package/node_modules/p-map/index.d.ts +0 -155
- package/node_modules/p-map/index.js +0 -285
- package/node_modules/p-map/license +0 -9
- package/node_modules/p-map/package.json +0 -57
- package/node_modules/p-map/readme.md +0 -190
- package/dist/{audit-core-xwjIczO0.js → audit-core-BCX8UeFH.js} +1 -1
- package/dist/{security-audit-jGnJF__m.js → security-audit-BfJhAXZp.js} +1 -1
|
@@ -1,84 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { D as canViewDiscordGuildChannel, F as createDiscordRestClient, H as registerGateway, K as validateDiscordProxyUrl, U as unregisterGateway, q as withValidatedDiscordProxy, z as DISCORD_REST_TIMEOUT_MS } from "./send.shared-jpvAG_u6.js";
|
|
6
|
-
import { _ as resolveDiscordModalEntryWithPersistence, g as resolveDiscordComponentEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-5uqwkjgE.js";
|
|
1
|
+
import { A as RateLimitError, B as hasDiscordV2Components, C as ThreadDeleteListener, D as Plugin, E as Client, F as Command, G as Modal, I as CommandWithSubcommands, J as Button, L as Guild, R as Message, X as Container, _ as MessageCreateListener, a as chunkDiscordTextWithMode, at as StringSelectMenu, b as PresenceUpdateListener, g as InteractionCreateListener, h as GuildDeleteListener, it as Separator, lt as BaseMessageInteractiveComponent, m as GuildCreateListener, nt as Row, ot as TextDisplay, p as discord_exports, v as MessageReactionAddListener, w as ThreadUpdateListener, x as ReadyListener, y as MessageReactionRemoveListener, z as User } from "./retry-DNsVTeTo.js";
|
|
2
|
+
import { C as parseDiscordComponentCustomId, D as decodeCustomIdComponent, E as parseDiscordModalCustomIdForInteraction, O as encodeCustomIdComponent, S as parseDiscordActivityCustomIdForInteraction, T as parseDiscordModalCustomId, c as parseExecApprovalData, o as parseDiscordQuestionData, v as buildDiscordActivityCustomId, w as parseDiscordComponentCustomIdForInteraction } from "./components-ezGKo_Yh.js";
|
|
3
|
+
import { c as resolveDiscordAccount, f as resolveDiscordAccountDmPolicy, l as resolveDiscordAccountAllowFrom, m as normalizeDiscordToken, p as resolveDiscordMaxLinesPerMessage } from "./accounts-DNl-oCEw.js";
|
|
4
|
+
import { a as resolveDiscordComponentEntryWithPersistence, o as resolveDiscordModalEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-ChK1wS7w.js";
|
|
7
5
|
import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
|
|
8
|
-
import { a as isDiscordRateLimitResponseBody, o as summarizeDiscordResponseBody } from "./api-
|
|
9
|
-
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,
|
|
10
|
-
import {
|
|
6
|
+
import { a as isDiscordRateLimitResponseBody, o as summarizeDiscordResponseBody } from "./api-DNVbJyEy.js";
|
|
7
|
+
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-BDMimASD.js";
|
|
8
|
+
import { F as createDiscordRestClient, G as registerGateway, H as withValidatedDiscordProxy, K as unregisterGateway, V as validateDiscordProxyUrl, b as canViewDiscordGuildChannel, z as DISCORD_REST_TIMEOUT_MS } from "./send.shared-B3fpaLWJ.js";
|
|
9
|
+
import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-HIkgji_7.js";
|
|
10
|
+
import { n as getDiscordActivitiesRuntime } from "./runtime-CQovMsH3.js";
|
|
11
|
+
import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-Bg41VzAf.js";
|
|
11
12
|
import { i as setPresence, t as clearPresences } from "./presence-cache-z1N8MbJ3.js";
|
|
12
|
-
import {
|
|
13
|
+
import { r as sendVoiceMessageDiscord } from "./send-rQIffSYu.js";
|
|
13
14
|
import { c as raceWithTimeout, u as withAbortTimeout } from "./timeouts-B9cy6RvQ.js";
|
|
14
|
-
import { a as getDiscordExecApprovalApprovers, n as formatDiscordApprovalDisplayValue, r as
|
|
15
|
-
import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-
|
|
16
|
-
import { t as resolveDiscordUserAllowlist } from "./resolve-users-
|
|
17
|
-
import { a as isThreadArchived,
|
|
18
|
-
import {
|
|
19
|
-
import { i as
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { buildAgentSessionKey, deriveLastRoutePolicy, isAcpSessionKey, isSubagentSessionKey, parseAgentSessionKey, resolveAgentIdFromSessionKey, resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
24
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeStringEntries, normalizeStringEntriesLower, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
25
|
-
import { getRuntimeConfig, getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
15
|
+
import { a as resolveDiscordBoundConversationRoute, f as getDiscordExecApprovalApprovers, l as resolveDiscordConversationIdentity, m as isDiscordExecApprovalClientEnabled, n as formatDiscordApprovalDisplayValue, r as buildDiscordConversationRouteContext, s as resolveDiscordEffectiveRoute, t as DISCORD_APPROVAL_ALLOWED_MENTIONS } from "./approval-message-safety-XGFPbDoI.js";
|
|
16
|
+
import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-CGeUoIqb.js";
|
|
17
|
+
import { t as resolveDiscordUserAllowlist } from "./resolve-users-BtKxLUZv.js";
|
|
18
|
+
import { a as isThreadArchived, c as resolveDiscordChannelInfoSafe, f as resolveDiscordChannelTopicSafe, h as formatThreadBindingDurationLabel, l as resolveDiscordChannelNameSafe, r as getThreadBindingManager, s as resolveDiscordChannelIdSafe, u as resolveDiscordChannelParentIdSafe } from "./thread-bindings.manager-CngsgErq.js";
|
|
19
|
+
import { a as probeDiscordApplicationId, r as parseApplicationIdFromToken } from "./probe-BTdtVRwU.js";
|
|
20
|
+
import { i as sanitizeRecentModels, n as normalizeModelRef, r as preferenceTimestampMs, t as buildPreferenceModelKey } from "./model-picker-preference-primitives-BHT1LkR_.js";
|
|
21
|
+
import { d as resolveDiscordMessageStickers, f as resolveDiscordChannelInfo } from "./message-utils-BXLLnFl6.js";
|
|
22
|
+
import { i as resolveDiscordThreadParentInfo } from "./threading-DU6pgeGm.js";
|
|
23
|
+
import { t as resolveDiscordSenderIdentity } from "./sender-identity-BJtfyise.js";
|
|
26
24
|
import { logDebug, logError } from "openclaw/plugin-sdk/logging-core";
|
|
27
25
|
import { ApplicationCommandOptionType, ButtonStyle, ChannelType, ComponentType, GatewayCloseCodes, GatewayCloseCodes as GatewayCloseCodes$1, GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes } from "discord-api-types/v10";
|
|
28
26
|
import { createHash, randomUUID } from "node:crypto";
|
|
29
|
-
import { MAX_DATE_TIMESTAMP_MS, asDateTimestampMs,
|
|
27
|
+
import { MAX_DATE_TIMESTAMP_MS, MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, asSafeIntegerInRange, parseFiniteNumber, parseStrictInteger, parseStrictPositiveInteger, resolveDateTimestampMs, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
30
28
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
31
29
|
import { Type } from "typebox";
|
|
32
30
|
import { Check, Errors } from "typebox/value";
|
|
33
31
|
import { inspect } from "node:util";
|
|
34
32
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
35
|
-
import { PlatformMessageNotDispatchedError, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
33
|
+
import { PlatformMessageNotDispatchedError, formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
36
34
|
import { fetchWithSsrFGuard, formatErrorMessage as formatErrorMessage$1 } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
35
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
36
|
+
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
37
|
+
import { asFiniteNumber, asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, parseFiniteNumber as parseFiniteNumber$1, parseStrictNonNegativeInteger as parseStrictNonNegativeInteger$1, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
38
|
+
import { getRuntimeConfig, getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
37
39
|
import { createHttp1EnvHttpProxyAgent, createHttp1ProxyAgent, createNodeProxyAgent, resolveEnvHttpProxyAgentOptions, wrapFetchWithAbortSignal } from "openclaw/plugin-sdk/fetch-runtime";
|
|
38
40
|
import { createNonExitingRuntime, createSubsystemLogger, danger, formatDurationSeconds, isVerbose, logVerbose, shouldLogVerbose, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
39
41
|
import { sliceUtf16Safe, truncateUtf16Safe, withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
40
42
|
import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary, canonicalizeAllowlistWithResolvedIds, patchAllowlistUsersInConfigEntries, summarizeMapping } from "openclaw/plugin-sdk/allow-from";
|
|
41
43
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
42
44
|
import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
43
|
-
import { chunkItems, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithProfile } from "openclaw/plugin-sdk/text-chunking";
|
|
44
|
-
import {
|
|
45
|
-
import { buildOutboundSessionContext, resolveChannelStreamingBlockEnabled, sendDurableMessageBatch } from "openclaw/plugin-sdk/channel-outbound";
|
|
45
|
+
import { chunkItems, findCodeRegions, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithProfile } from "openclaw/plugin-sdk/text-chunking";
|
|
46
|
+
import { buildOutboundSessionContext, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled, sendDurableMessageBatch } from "openclaw/plugin-sdk/channel-outbound";
|
|
46
47
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
47
48
|
import { resolveSendableOutboundReplyParts, resolveTextChunksWithFallback } from "openclaw/plugin-sdk/reply-payload";
|
|
48
49
|
import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
|
|
49
|
-
import { createLazyRuntimeModule
|
|
50
|
-
import {
|
|
50
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
51
|
+
import { buildCommandTextFromArgs, findCommandByNativeName, formatCommandArgMenuTitle, listChatCommands, listNativeCommandSpecsForConfig, listSkillCommandsForAgents, resolveCommandAuthorizedFromAuthorizers, resolveEffectiveAgentRuntime, resolveNativeCommandSessionTargets, resolveStoredModelOverride, serializeCommandArgs } from "openclaw/plugin-sdk/command-auth-native";
|
|
51
52
|
import { GROUP_POLICY_BLOCKED_LABEL, resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
53
|
+
import { resolvePinnedMainDmOwnerFromAllowlist as resolvePinnedMainDmOwnerFromAllowlist$1 } from "openclaw/plugin-sdk/security-runtime";
|
|
52
54
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
53
55
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
|
54
|
-
import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
|
|
55
56
|
import { buildPairingReply, upsertChannelPairingRequest, upsertChannelPairingRequest as upsertChannelPairingRequest$1 } from "openclaw/plugin-sdk/conversation-runtime";
|
|
56
|
-
import
|
|
57
|
+
import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
|
|
58
|
+
import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
|
|
59
|
+
import { clearExpiredCooldowns, ensureAuthProfileStore, formatReasoningMessage, getPreparedModelCatalogSnapshot, isProfileInCooldown, listAgentIds, loadPreparedModelCatalog, resolveAgentAvatar, resolveAgentDir, resolveDefaultModelForAgent, resolveHumanDelayConfig, resolveProfilesUnavailableReason } from "openclaw/plugin-sdk/agent-runtime";
|
|
57
60
|
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
|
58
61
|
import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "openclaw/plugin-sdk/native-command-config-runtime";
|
|
59
|
-
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|
60
62
|
import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
|
|
61
|
-
import {
|
|
63
|
+
import { enqueueRoutedSystemEvent, enqueueRoutedSystemEvent as enqueueRoutedSystemEvent$1 } from "openclaw/plugin-sdk/system-event-runtime";
|
|
64
|
+
import { deleteSessionEntry, getSessionEntry, listSessionEntries, readSessionUpdatedAt as readSessionUpdatedAt$1, resolveStorePath, resolveStorePath as resolveStorePath$1 } from "openclaw/plugin-sdk/session-store-runtime";
|
|
62
65
|
import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
63
66
|
import { createChannelPartialDeliveryError, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, resolveEnvelopeFormatOptions, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent } from "openclaw/plugin-sdk/channel-inbound";
|
|
64
|
-
import { ModelSelectionLockedError, applyModelOverrideToSessionEntry, resolveChannelModelOverride } from "openclaw/plugin-sdk/model-session-runtime";
|
|
65
|
-
import { completeWithPreparedSimpleCompletionModel, extractAssistantText, prepareSimpleCompletionModelForAgent } from "openclaw/plugin-sdk/simple-completion-runtime";
|
|
66
|
-
import { deleteSessionEntry, getSessionEntry, listSessionEntries, patchSessionEntry, readSessionUpdatedAt as readSessionUpdatedAt$1, resolveStorePath, resolveStorePath as resolveStorePath$1 } from "openclaw/plugin-sdk/session-store-runtime";
|
|
67
|
-
import { buildCommandTextFromArgs, findCommandByNativeName, formatCommandArgMenuTitle, listChatCommands, listNativeCommandSpecsForConfig, listSkillCommandsForAgents, resolveCommandAuthorizedFromAuthorizers, resolveEffectiveAgentRuntime, resolveNativeCommandSessionTargets, resolveStoredModelOverride, serializeCommandArgs } from "openclaw/plugin-sdk/command-auth-native";
|
|
68
|
-
import { buildCommandTextFromArgs as buildCommandTextFromArgs$1, findCommandByNativeName as findCommandByNativeName$1, parseCommandArgs, resolveCommandArgChoices, resolveCommandArgMenu, serializeCommandArgs as serializeCommandArgs$1 } from "openclaw/plugin-sdk/native-command-registry";
|
|
69
|
-
import { resolveDirectStatusReplyForSession } from "openclaw/plugin-sdk/command-status-runtime";
|
|
70
|
-
import * as pluginRuntime from "openclaw/plugin-sdk/plugin-runtime";
|
|
71
|
-
import { createInteractiveConversationBindingHelpers, dispatchPluginInteractiveHandler } from "openclaw/plugin-sdk/plugin-runtime";
|
|
72
|
-
import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
|
|
73
67
|
import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
|
74
|
-
import {
|
|
68
|
+
import { buildCommandTextFromArgs as buildCommandTextFromArgs$1, findCommandByNativeName as findCommandByNativeName$1, mergeNativeCommandSpecs, parseCommandArgs, resolveCommandArgChoices, resolveCommandArgMenu, serializeCommandArgs as serializeCommandArgs$1 } from "openclaw/plugin-sdk/native-command-registry";
|
|
69
|
+
import { PLUGIN_COMMAND_DISPATCH } from "openclaw/plugin-sdk/plugin-command-runtime";
|
|
70
|
+
import { resolveDirectStatusReplyForSession } from "openclaw/plugin-sdk/command-status-runtime";
|
|
71
|
+
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|
75
72
|
import { EventEmitter } from "node:events";
|
|
76
73
|
import * as ws from "ws";
|
|
77
74
|
import { Agent } from "node:https";
|
|
78
75
|
import { captureHttpExchange, captureWsEvent, resolveDebugProxySettings, resolveEffectiveDebugProxyUrl } from "openclaw/plugin-sdk/proxy-capture";
|
|
79
76
|
import * as dns from "node:dns";
|
|
77
|
+
import { channelReadyPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
80
78
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
81
79
|
import { stripPlainTextToolCallBlocks } from "openclaw/plugin-sdk/tool-payload";
|
|
80
|
+
import { createChannelInteractiveDispatcher } from "openclaw/plugin-sdk/plugin-runtime";
|
|
82
81
|
import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway-runtime";
|
|
83
82
|
import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
|
|
84
83
|
import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
|
|
@@ -141,6 +140,205 @@ async function runDiscordListenerWithSlowLog(params) {
|
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
//#endregion
|
|
143
|
+
//#region extensions/discord/src/monitor/presence-events.ts
|
|
144
|
+
const DISCORD_PRESENCE_GREETING_COOLDOWN_MS = 480 * 60 * 1e3;
|
|
145
|
+
function isDiscordOnlineStatus(status) {
|
|
146
|
+
return status === "online" || status === "idle" || status === "dnd";
|
|
147
|
+
}
|
|
148
|
+
function isDiscordOfflineStatus(status) {
|
|
149
|
+
return status === "offline";
|
|
150
|
+
}
|
|
151
|
+
function resolveDiscordOnlinePresenceEvent(params) {
|
|
152
|
+
const config = params.config;
|
|
153
|
+
const userId = params.data.user?.id?.trim();
|
|
154
|
+
if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
|
|
155
|
+
if (config.users !== void 0 && !config.users.includes(userId)) return null;
|
|
156
|
+
if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
|
|
157
|
+
const lines = [
|
|
158
|
+
"Discord online-presence event:",
|
|
159
|
+
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.`,
|
|
160
|
+
`The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
|
|
161
|
+
"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.",
|
|
162
|
+
"Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
|
|
163
|
+
];
|
|
164
|
+
return {
|
|
165
|
+
channelId: config.channelId,
|
|
166
|
+
userId,
|
|
167
|
+
text: lines.join("\n")
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region extensions/discord/src/monitor/presence-cooldown-store.ts
|
|
172
|
+
const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
|
|
173
|
+
function openDiscordPresenceCooldownStore() {
|
|
174
|
+
return getDiscordRuntime().state.openSyncKeyedStore({
|
|
175
|
+
namespace: "presence-greeting-cooldowns",
|
|
176
|
+
maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
|
|
177
|
+
overflowPolicy: "reject-new",
|
|
178
|
+
defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region extensions/discord/src/monitor/presence-emission-gate.ts
|
|
183
|
+
const DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS = 300 * 1e3;
|
|
184
|
+
const DISCORD_PRESENCE_BURST_LIMIT = 8;
|
|
185
|
+
const DISCORD_PRESENCE_BURST_WINDOW_MS = 60 * 1e3;
|
|
186
|
+
function resolveDiscordPresenceGateOptions(config) {
|
|
187
|
+
return {
|
|
188
|
+
reconnectSuppressMs: config?.reconnectSuppressSeconds !== void 0 ? config.reconnectSuppressSeconds * 1e3 : DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS,
|
|
189
|
+
burstLimit: config?.burstLimit ?? DISCORD_PRESENCE_BURST_LIMIT,
|
|
190
|
+
burstWindowMs: config?.burstWindowSeconds !== void 0 ? config.burstWindowSeconds * 1e3 : DISCORD_PRESENCE_BURST_WINDOW_MS
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Per-account lifecycle gate for online-presence events. Reconnect state belongs to the Gateway
|
|
195
|
+
* session, while burst state follows the guild-owned configuration that supplies its limits.
|
|
196
|
+
*/
|
|
197
|
+
var DiscordPresenceEmissionGate = class {
|
|
198
|
+
constructor() {
|
|
199
|
+
this.reconnectLogged = false;
|
|
200
|
+
this.burstByGuild = /* @__PURE__ */ new Map();
|
|
201
|
+
this.nextReservationId = 0;
|
|
202
|
+
}
|
|
203
|
+
noteGatewaySessionReset(nowMs) {
|
|
204
|
+
this.lastSessionResetAtMs = nowMs;
|
|
205
|
+
this.reconnectLogged = false;
|
|
206
|
+
}
|
|
207
|
+
evaluateReconnectWindow(nowMs, options) {
|
|
208
|
+
if (this.lastSessionResetAtMs !== void 0 && options.reconnectSuppressMs > 0 && nowMs - this.lastSessionResetAtMs < options.reconnectSuppressMs) {
|
|
209
|
+
const shouldLog = !this.reconnectLogged;
|
|
210
|
+
this.reconnectLogged = true;
|
|
211
|
+
return {
|
|
212
|
+
allowed: false,
|
|
213
|
+
reason: "reconnect-window",
|
|
214
|
+
shouldLog
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return { allowed: true };
|
|
218
|
+
}
|
|
219
|
+
reserveBurst(guildId, nowMs, options) {
|
|
220
|
+
const state = this.burstByGuild.get(guildId) ?? {
|
|
221
|
+
reservations: [],
|
|
222
|
+
logged: false
|
|
223
|
+
};
|
|
224
|
+
state.reservations = state.reservations.filter((reservation) => !reservation.committed || nowMs - reservation.atMs < options.burstWindowMs);
|
|
225
|
+
this.burstByGuild.set(guildId, state);
|
|
226
|
+
if (state.reservations.length >= options.burstLimit) {
|
|
227
|
+
const shouldLog = !state.logged;
|
|
228
|
+
state.logged = true;
|
|
229
|
+
return {
|
|
230
|
+
allowed: false,
|
|
231
|
+
reason: state.reservations.some((reservation) => !reservation.committed) ? "burst-pending" : "burst",
|
|
232
|
+
shouldLog
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
state.logged = false;
|
|
236
|
+
const reservation = {
|
|
237
|
+
id: this.nextReservationId++,
|
|
238
|
+
atMs: nowMs,
|
|
239
|
+
committed: false
|
|
240
|
+
};
|
|
241
|
+
state.reservations.push(reservation);
|
|
242
|
+
return {
|
|
243
|
+
allowed: true,
|
|
244
|
+
reservation: reservation.id
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
commitBurst(guildId, reservation, nowMs) {
|
|
248
|
+
const entry = this.burstByGuild.get(guildId)?.reservations.find((candidate) => candidate.id === reservation);
|
|
249
|
+
if (!entry) return;
|
|
250
|
+
entry.atMs = nowMs;
|
|
251
|
+
entry.committed = true;
|
|
252
|
+
}
|
|
253
|
+
releaseBurst(guildId, reservation) {
|
|
254
|
+
const state = this.burstByGuild.get(guildId);
|
|
255
|
+
if (!state) return;
|
|
256
|
+
state.reservations = state.reservations.filter((candidate) => candidate.id !== reservation);
|
|
257
|
+
if (state.reservations.length === 0 && !state.logged) this.burstByGuild.delete(guildId);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region extensions/discord/src/monitor/presence-transition-cache.ts
|
|
262
|
+
const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
|
|
263
|
+
var DiscordPresenceBaselineCache = class {
|
|
264
|
+
constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
|
|
265
|
+
this.maxEntries = maxEntries;
|
|
266
|
+
this.offlineByKey = /* @__PURE__ */ new Map();
|
|
267
|
+
this.onlineByKey = /* @__PURE__ */ new Map();
|
|
268
|
+
}
|
|
269
|
+
clear() {
|
|
270
|
+
this.offlineByKey.clear();
|
|
271
|
+
this.onlineByKey.clear();
|
|
272
|
+
}
|
|
273
|
+
clearScope(scope) {
|
|
274
|
+
for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
|
|
275
|
+
}
|
|
276
|
+
isOffline(scope, key) {
|
|
277
|
+
return this.offlineByKey.get(key) === scope;
|
|
278
|
+
}
|
|
279
|
+
isOnline(scope, key) {
|
|
280
|
+
return this.onlineByKey.get(key) === scope;
|
|
281
|
+
}
|
|
282
|
+
observeOffline(scope, key) {
|
|
283
|
+
this.deleteMarker(this.onlineByKey, scope, key);
|
|
284
|
+
return this.observe(this.offlineByKey, scope, key);
|
|
285
|
+
}
|
|
286
|
+
observeOnline(scope, key) {
|
|
287
|
+
this.deleteMarker(this.offlineByKey, scope, key);
|
|
288
|
+
return this.observe(this.onlineByKey, scope, key);
|
|
289
|
+
}
|
|
290
|
+
deleteMarker(markers, scope, key) {
|
|
291
|
+
if (markers.get(key) === scope) markers.delete(key);
|
|
292
|
+
}
|
|
293
|
+
observe(markers, scope, key) {
|
|
294
|
+
markers.delete(key);
|
|
295
|
+
markers.set(key, scope);
|
|
296
|
+
let evictedScope;
|
|
297
|
+
while (markers.size > this.maxEntries) {
|
|
298
|
+
const oldestKey = markers.keys().next().value;
|
|
299
|
+
if (oldestKey === void 0) break;
|
|
300
|
+
evictedScope = markers.get(oldestKey);
|
|
301
|
+
markers.delete(oldestKey);
|
|
302
|
+
}
|
|
303
|
+
return evictedScope;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region extensions/discord/src/monitor/thread-session-close.ts
|
|
308
|
+
/**
|
|
309
|
+
* Closes every session entry in the store whose key contains {@link threadId}.
|
|
310
|
+
* The explicit lifecycle deletion archives the old transcript and guarantees
|
|
311
|
+
* that a later inbound message starts a fresh session in every reset mode.
|
|
312
|
+
*/
|
|
313
|
+
async function closeDiscordThreadSessions(params) {
|
|
314
|
+
const { cfg, threadId } = params;
|
|
315
|
+
const normalizedThreadId = normalizeOptionalLowercaseString(threadId) ?? "";
|
|
316
|
+
if (!normalizedThreadId) return 0;
|
|
317
|
+
const segmentRe = new RegExp(`:${normalizedThreadId}(?::|$)`, "i");
|
|
318
|
+
function sessionKeyContainsThreadId(key) {
|
|
319
|
+
return segmentRe.test(key);
|
|
320
|
+
}
|
|
321
|
+
let resetCount = 0;
|
|
322
|
+
for (const agentId of listAgentIds(cfg)) {
|
|
323
|
+
const storePath = resolveStorePath(cfg.session?.store, { agentId });
|
|
324
|
+
for (const { sessionKey, entry } of listSessionEntries({
|
|
325
|
+
agentId,
|
|
326
|
+
storePath,
|
|
327
|
+
readOnly: true
|
|
328
|
+
})) {
|
|
329
|
+
if (!sessionKeyContainsThreadId(sessionKey)) continue;
|
|
330
|
+
if (await deleteSessionEntry({
|
|
331
|
+
archiveTranscript: true,
|
|
332
|
+
expectedSessionId: entry.sessionId ?? null,
|
|
333
|
+
expectedUpdatedAt: entry.updatedAt,
|
|
334
|
+
sessionKey,
|
|
335
|
+
storePath
|
|
336
|
+
})) resetCount += 1;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return resetCount;
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
144
342
|
//#region extensions/discord/src/monitor/dm-command-auth.ts
|
|
145
343
|
const DISCORD_ALLOW_LIST_PREFIXES = [
|
|
146
344
|
"discord:",
|
|
@@ -253,8 +451,11 @@ async function resolveDiscordDmCommandAccess(params) {
|
|
|
253
451
|
subject: createDiscordDmIngressSubject(params.sender),
|
|
254
452
|
conversation: {
|
|
255
453
|
kind: "direct",
|
|
256
|
-
id: params.sender.id
|
|
454
|
+
id: params.conversationId ?? params.sender.id,
|
|
455
|
+
parentId: params.conversationParentId,
|
|
456
|
+
threadId: params.conversationThreadId
|
|
257
457
|
},
|
|
458
|
+
...params.contextBinding ? { contextBinding: params.contextBinding } : {},
|
|
258
459
|
event: {
|
|
259
460
|
kind: params.eventKind ?? "native-command",
|
|
260
461
|
authMode: "inbound",
|
|
@@ -275,7 +476,7 @@ async function resolveDiscordTextCommandAccess(params) {
|
|
|
275
476
|
const memberAccessGroup = "discord-member-access";
|
|
276
477
|
const commandGroup = params.memberAccessConfigured ? [`accessGroup:${memberAccessGroup}`] : [];
|
|
277
478
|
const accessGroupMembership = params.memberAccessConfigured ? [syntheticAccessGroupMembership(memberAccessGroup, params.memberAllowed)] : [];
|
|
278
|
-
return
|
|
479
|
+
return await createDiscordIngressResolver({
|
|
279
480
|
accountId: params.accountId,
|
|
280
481
|
cfg: params.cfg,
|
|
281
482
|
token: params.token,
|
|
@@ -283,9 +484,12 @@ async function resolveDiscordTextCommandAccess(params) {
|
|
|
283
484
|
}).command({
|
|
284
485
|
subject: createDiscordDmIngressSubject(params.sender),
|
|
285
486
|
conversation: {
|
|
286
|
-
kind: "
|
|
287
|
-
id: "discord-command"
|
|
487
|
+
kind: "channel",
|
|
488
|
+
id: params.conversationId ?? "discord-command",
|
|
489
|
+
parentId: params.conversationParentId,
|
|
490
|
+
threadId: params.conversationThreadId
|
|
288
491
|
},
|
|
492
|
+
...params.contextBinding ? { contextBinding: params.contextBinding } : {},
|
|
289
493
|
accessGroupMembership,
|
|
290
494
|
dmPolicy: "allowlist",
|
|
291
495
|
groupPolicy: "allowlist",
|
|
@@ -297,525 +501,38 @@ async function resolveDiscordTextCommandAccess(params) {
|
|
|
297
501
|
hasControlCommand: params.hasControlCommand,
|
|
298
502
|
modeWhenAccessGroupsOff: "configured"
|
|
299
503
|
}
|
|
300
|
-
})).commandAccess;
|
|
301
|
-
}
|
|
302
|
-
//#endregion
|
|
303
|
-
//#region extensions/discord/src/monitor/thread-title.ts
|
|
304
|
-
const DEFAULT_THREAD_TITLE_TIMEOUT_MS = 6e4;
|
|
305
|
-
const MAX_THREAD_TITLE_SOURCE_CHARS = 600;
|
|
306
|
-
const MAX_THREAD_TITLE_CHANNEL_NAME_CHARS = 120;
|
|
307
|
-
const MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS = 320;
|
|
308
|
-
const DISCORD_THREAD_TITLE_MAX_TOKENS = 4096;
|
|
309
|
-
const DISCORD_THREAD_TITLE_SYSTEM_PROMPT = "Generate a concise Discord thread title (3-6 words). Return only the title. Use channel context when provided and avoid redundant channel-name words unless needed for clarity.";
|
|
310
|
-
async function generateThreadTitle(params) {
|
|
311
|
-
const sourceText = params.messageText.trim();
|
|
312
|
-
if (!sourceText) return null;
|
|
313
|
-
const prepared = await prepareSimpleCompletionModelForAgent({
|
|
314
|
-
cfg: params.cfg,
|
|
315
|
-
agentId: params.agentId,
|
|
316
|
-
...params.modelRef ? { modelRef: params.modelRef } : {},
|
|
317
|
-
useUtilityModel: true,
|
|
318
|
-
allowMissingApiKeyModes: ["aws-sdk"]
|
|
319
|
-
});
|
|
320
|
-
if ("error" in prepared) {
|
|
321
|
-
const modelLabel = prepared.selection ? `${prepared.selection.provider}/${prepared.selection.modelId}` : "unknown";
|
|
322
|
-
logVerbose(`thread-title: ${prepared.error} (agent=${params.agentId}, model=${modelLabel})`);
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
try {
|
|
326
|
-
const userMessage = buildThreadTitleCompletionUserMessage({
|
|
327
|
-
sourceText,
|
|
328
|
-
channelName: params.channelName,
|
|
329
|
-
channelDescription: params.channelDescription
|
|
330
|
-
});
|
|
331
|
-
const timeoutMs = resolveThreadTitleTimeoutMs(params.timeoutMs);
|
|
332
|
-
return normalizeGeneratedThreadTitle(extractAssistantText(await completeThreadTitle({
|
|
333
|
-
model: prepared.model,
|
|
334
|
-
auth: prepared.auth,
|
|
335
|
-
userMessage,
|
|
336
|
-
timeoutMs
|
|
337
|
-
}))) || null;
|
|
338
|
-
} catch (err) {
|
|
339
|
-
logVerbose(`thread-title: title generation failed for agent ${params.agentId}: ${String(err)}`);
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
async function completeThreadTitle(params) {
|
|
344
|
-
const maxTokens = Math.min(DISCORD_THREAD_TITLE_MAX_TOKENS, Math.floor(params.model.maxTokens));
|
|
345
|
-
return await withAbortTimeout({
|
|
346
|
-
timeoutMs: params.timeoutMs,
|
|
347
|
-
createTimeoutError: () => /* @__PURE__ */ new Error(`thread-title timed out after ${params.timeoutMs}ms`),
|
|
348
|
-
run: async (signal) => await completeWithPreparedSimpleCompletionModel({
|
|
349
|
-
model: params.model,
|
|
350
|
-
auth: params.auth,
|
|
351
|
-
context: {
|
|
352
|
-
systemPrompt: DISCORD_THREAD_TITLE_SYSTEM_PROMPT,
|
|
353
|
-
messages: [{
|
|
354
|
-
role: "user",
|
|
355
|
-
content: params.userMessage,
|
|
356
|
-
timestamp: Date.now()
|
|
357
|
-
}]
|
|
358
|
-
},
|
|
359
|
-
options: {
|
|
360
|
-
maxTokens,
|
|
361
|
-
signal
|
|
362
|
-
}
|
|
363
|
-
})
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
function buildThreadTitleCompletionUserMessage(params) {
|
|
367
|
-
const sourceText = truncateThreadTitleSourceText(params.sourceText);
|
|
368
|
-
const channelName = normalizeTitleContextField(params.channelName, MAX_THREAD_TITLE_CHANNEL_NAME_CHARS);
|
|
369
|
-
const channelDescription = normalizeTitleContextField(params.channelDescription, MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS);
|
|
370
|
-
const messageLines = [];
|
|
371
|
-
if (channelName) messageLines.push(`Channel: ${channelName}`);
|
|
372
|
-
if (channelDescription) messageLines.push(`Channel description: ${channelDescription}`);
|
|
373
|
-
messageLines.push(`Message:\n${sourceText}`);
|
|
374
|
-
return messageLines.join("\n\n");
|
|
375
|
-
}
|
|
376
|
-
function truncateThreadTitleSourceText(sourceText) {
|
|
377
|
-
if (sourceText.length <= MAX_THREAD_TITLE_SOURCE_CHARS) return sourceText;
|
|
378
|
-
return `${truncateUtf16Safe(sourceText, MAX_THREAD_TITLE_SOURCE_CHARS)}...`;
|
|
379
|
-
}
|
|
380
|
-
function resolveThreadTitleTimeoutMs(timeoutMs) {
|
|
381
|
-
return Math.max(100, Math.floor(timeoutMs ?? DEFAULT_THREAD_TITLE_TIMEOUT_MS));
|
|
382
|
-
}
|
|
383
|
-
function normalizeGeneratedThreadTitle(raw) {
|
|
384
|
-
const lines = raw.replace(/\r/g, "").split("\n");
|
|
385
|
-
let firstLine = "";
|
|
386
|
-
for (const line of lines) {
|
|
387
|
-
const trimmed = line.trim();
|
|
388
|
-
if (!trimmed) continue;
|
|
389
|
-
if (!firstLine && trimmed.startsWith("```")) continue;
|
|
390
|
-
firstLine = trimmed;
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
return stripThreadTitleWrappers(firstLine);
|
|
394
|
-
}
|
|
395
|
-
function stripThreadTitleWrappers(raw) {
|
|
396
|
-
let current = raw.trim();
|
|
397
|
-
let previous = "";
|
|
398
|
-
while (current && current !== previous) {
|
|
399
|
-
previous = current;
|
|
400
|
-
current = current.replace(/^["'`]+|["'`]+$/g, "").trim();
|
|
401
|
-
current = stripBalancedWrapper(current, "**");
|
|
402
|
-
current = stripBalancedWrapper(current, "__");
|
|
403
|
-
current = stripBalancedWrapper(current, "*");
|
|
404
|
-
current = stripBalancedWrapper(current, "_");
|
|
405
|
-
current = stripBalancedWrapper(current, "~~");
|
|
406
|
-
}
|
|
407
|
-
return current;
|
|
408
|
-
}
|
|
409
|
-
function stripBalancedWrapper(text, marker) {
|
|
410
|
-
if (text.length < marker.length * 2 + 1) return text;
|
|
411
|
-
if (!text.startsWith(marker) || !text.endsWith(marker)) return text;
|
|
412
|
-
const inner = text.slice(marker.length, text.length - marker.length);
|
|
413
|
-
if (!inner || inner.includes(marker)) return text;
|
|
414
|
-
return inner;
|
|
415
|
-
}
|
|
416
|
-
function normalizeTitleContextField(raw, maxChars) {
|
|
417
|
-
const value = raw?.trim();
|
|
418
|
-
if (!value) return;
|
|
419
|
-
const singleLine = value.replace(/\s+/g, " ");
|
|
420
|
-
if (singleLine.length <= maxChars) return singleLine;
|
|
421
|
-
return `${truncateUtf16Safe(singleLine, maxChars)}...`;
|
|
422
|
-
}
|
|
423
|
-
//#endregion
|
|
424
|
-
//#region extensions/discord/src/monitor/threading.cache.ts
|
|
425
|
-
const DISCORD_THREAD_STARTER_CACHE_TTL_MS = 300 * 1e3;
|
|
426
|
-
const DISCORD_THREAD_STARTER_CACHE_MAX = 500;
|
|
427
|
-
const DISCORD_THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
|
|
428
|
-
function resetDiscordThreadStarterCacheForTest() {
|
|
429
|
-
DISCORD_THREAD_STARTER_CACHE.clear();
|
|
430
|
-
}
|
|
431
|
-
function getCachedThreadStarter(key, now) {
|
|
432
|
-
const entry = DISCORD_THREAD_STARTER_CACHE.get(key);
|
|
433
|
-
if (!entry) return;
|
|
434
|
-
if (now - entry.updatedAt > DISCORD_THREAD_STARTER_CACHE_TTL_MS) {
|
|
435
|
-
DISCORD_THREAD_STARTER_CACHE.delete(key);
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
DISCORD_THREAD_STARTER_CACHE.delete(key);
|
|
439
|
-
DISCORD_THREAD_STARTER_CACHE.set(key, {
|
|
440
|
-
...entry,
|
|
441
|
-
updatedAt: now
|
|
442
|
-
});
|
|
443
|
-
return entry.value;
|
|
444
|
-
}
|
|
445
|
-
function setCachedThreadStarter(key, value, now) {
|
|
446
|
-
DISCORD_THREAD_STARTER_CACHE.delete(key);
|
|
447
|
-
DISCORD_THREAD_STARTER_CACHE.set(key, {
|
|
448
|
-
value,
|
|
449
|
-
updatedAt: now
|
|
450
504
|
});
|
|
451
|
-
while (DISCORD_THREAD_STARTER_CACHE.size > DISCORD_THREAD_STARTER_CACHE_MAX) {
|
|
452
|
-
const iter = DISCORD_THREAD_STARTER_CACHE.keys().next();
|
|
453
|
-
if (iter.done) break;
|
|
454
|
-
DISCORD_THREAD_STARTER_CACHE.delete(iter.value);
|
|
455
|
-
}
|
|
456
505
|
}
|
|
457
506
|
//#endregion
|
|
458
|
-
//#region extensions/discord/src/monitor/
|
|
459
|
-
function
|
|
507
|
+
//#region extensions/discord/src/monitor/thread-channel-context.ts
|
|
508
|
+
function isDiscordThreadChannelType(type) {
|
|
460
509
|
return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
|
|
461
510
|
}
|
|
462
|
-
function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
function resolveDiscordThreadChannel(params) {
|
|
466
|
-
if (!params.isGuildMessage) return null;
|
|
467
|
-
const { message, channelInfo } = params;
|
|
468
|
-
const channel = "channel" in message ? message.channel : void 0;
|
|
469
|
-
if (channel && typeof channel === "object" && "isThread" in channel && typeof channel.isThread === "function" && channel.isThread()) return channel;
|
|
470
|
-
if (!isDiscordThreadType(channelInfo?.type)) return null;
|
|
471
|
-
const messageChannelId = params.messageChannelId || resolveDiscordMessageChannelId({ message });
|
|
472
|
-
if (!messageChannelId) return null;
|
|
473
|
-
return {
|
|
474
|
-
id: messageChannelId,
|
|
475
|
-
name: channelInfo?.name ?? void 0,
|
|
476
|
-
parentId: channelInfo?.parentId ?? void 0,
|
|
477
|
-
parent: void 0,
|
|
478
|
-
ownerId: channelInfo?.ownerId ?? void 0
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
async function resolveDiscordThreadParentInfo(params) {
|
|
482
|
-
const { threadChannel, channelInfo, client } = params;
|
|
483
|
-
const parent = resolveDiscordChannelParentSafe(threadChannel);
|
|
484
|
-
let parentId = resolveDiscordChannelParentIdSafe(threadChannel) ?? resolveDiscordChannelIdSafe(parent) ?? channelInfo?.parentId ?? void 0;
|
|
485
|
-
if (!parentId && threadChannel.id) parentId = (await resolveDiscordChannelInfo(client, threadChannel.id))?.parentId ?? void 0;
|
|
486
|
-
if (!parentId) return {};
|
|
487
|
-
let parentName = resolveDiscordChannelNameSafe(parent);
|
|
488
|
-
const parentInfo = await resolveDiscordChannelInfo(client, parentId);
|
|
489
|
-
parentName = parentName ?? parentInfo?.name;
|
|
490
|
-
const parentType = parentInfo?.type;
|
|
511
|
+
function buildFetchedChannelInfo(channel) {
|
|
512
|
+
const channelInfo = resolveDiscordChannelInfoSafe(channel);
|
|
513
|
+
if (channelInfo.type === void 0) return null;
|
|
491
514
|
return {
|
|
492
|
-
|
|
493
|
-
name:
|
|
494
|
-
|
|
515
|
+
type: channelInfo.type,
|
|
516
|
+
name: channelInfo.name,
|
|
517
|
+
topic: channelInfo.topic,
|
|
518
|
+
parentId: channelInfo.parentId,
|
|
519
|
+
ownerId: channelInfo.ownerId
|
|
495
520
|
};
|
|
496
521
|
}
|
|
497
|
-
async function
|
|
498
|
-
const
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
starter,
|
|
512
|
-
resolveTimestampMs: params.resolveTimestampMs
|
|
513
|
-
});
|
|
514
|
-
if (!payload) return null;
|
|
515
|
-
setCachedThreadStarter(cacheKey, payload, Date.now());
|
|
516
|
-
return payload;
|
|
517
|
-
} catch {
|
|
518
|
-
return null;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
function resolveDiscordThreadStarterMessageChannelId(params) {
|
|
522
|
-
return isDiscordForumParentType(params.parentType) ? params.channel.id : params.parentId;
|
|
523
|
-
}
|
|
524
|
-
async function fetchDiscordThreadStarterMessage(params) {
|
|
525
|
-
const starter = await getChannelMessage(params.client.rest, params.messageChannelId, params.threadId);
|
|
526
|
-
return starter ? starter : null;
|
|
527
|
-
}
|
|
528
|
-
function buildDiscordThreadStarterPayload(params) {
|
|
529
|
-
const text = resolveDiscordThreadStarterText(params.starter);
|
|
530
|
-
if (!text) return null;
|
|
531
|
-
return {
|
|
532
|
-
text,
|
|
533
|
-
...resolveDiscordThreadStarterIdentity(params.starter),
|
|
534
|
-
timestamp: params.resolveTimestampMs(params.starter.timestamp) ?? void 0
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
function resolveDiscordThreadStarterText(starter) {
|
|
538
|
-
const content = normalizeOptionalString(starter.content) ?? "";
|
|
539
|
-
const embedText = resolveDiscordEmbedText(starter.embeds?.[0]);
|
|
540
|
-
const forwardedText = resolveDiscordForwardedMessagesTextFromSnapshots(starter.message_snapshots);
|
|
541
|
-
return [content || embedText || forwardedText, formatDiscordMediaText({
|
|
542
|
-
attachments: starter.attachments ?? void 0,
|
|
543
|
-
stickers: starter.sticker_items ?? void 0
|
|
544
|
-
})].filter(Boolean).join("\n");
|
|
545
|
-
}
|
|
546
|
-
function resolveDiscordThreadStarterIdentity(starter) {
|
|
547
|
-
return {
|
|
548
|
-
author: resolveDiscordThreadStarterAuthor(starter),
|
|
549
|
-
authorId: starter.author?.id ?? void 0,
|
|
550
|
-
authorName: starter.author?.username ?? void 0,
|
|
551
|
-
authorTag: resolveDiscordThreadStarterAuthorTag(starter.author),
|
|
552
|
-
memberRoleIds: resolveDiscordThreadStarterRoleIds(starter.member)
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
function resolveDiscordThreadStarterAuthor(starter) {
|
|
556
|
-
return starter.member?.nick ?? starter.member?.displayName ?? resolveDiscordThreadStarterAuthorTag(starter.author) ?? starter.author?.username ?? starter.author?.id ?? "Unknown";
|
|
557
|
-
}
|
|
558
|
-
function resolveDiscordThreadStarterAuthorTag(author) {
|
|
559
|
-
if (!author?.username || !author.discriminator) return;
|
|
560
|
-
if (author.discriminator !== "0") return `${author.username}#${author.discriminator}`;
|
|
561
|
-
return author.username;
|
|
562
|
-
}
|
|
563
|
-
function resolveDiscordThreadStarterRoleIds(member) {
|
|
564
|
-
return Array.isArray(member?.roles) ? member.roles : void 0;
|
|
565
|
-
}
|
|
566
|
-
function resolveDiscordReplyTarget(opts) {
|
|
567
|
-
if (opts.replyToMode === "off") return;
|
|
568
|
-
const replyToId = normalizeOptionalString(opts.replyToId);
|
|
569
|
-
if (!replyToId) return;
|
|
570
|
-
if (opts.replyToMode === "all") return replyToId;
|
|
571
|
-
return opts.hasReplied ? void 0 : replyToId;
|
|
572
|
-
}
|
|
573
|
-
function sanitizeDiscordThreadName(rawName, fallbackId) {
|
|
574
|
-
return truncateUtf16Safe(truncateUtf16Safe(rawName.replace(/<@!?\d+>/g, "").replace(/<@&\d+>/g, "").replace(/<#\d+>/g, "").replace(/\s+/g, " ").trim() || `Thread ${fallbackId}`, 80), 100) || `Thread ${fallbackId}`;
|
|
575
|
-
}
|
|
576
|
-
function resolveDiscordReplyDeliveryPlan(params) {
|
|
577
|
-
const originalReplyTarget = params.replyTarget;
|
|
578
|
-
let deliverTarget = originalReplyTarget;
|
|
579
|
-
let replyTarget = originalReplyTarget;
|
|
580
|
-
if (params.createdThreadId) {
|
|
581
|
-
deliverTarget = `channel:${params.createdThreadId}`;
|
|
582
|
-
replyTarget = deliverTarget;
|
|
583
|
-
}
|
|
584
|
-
const allowReference = deliverTarget === originalReplyTarget;
|
|
585
|
-
const replyReference = createReplyReferencePlanner({
|
|
586
|
-
replyToMode: allowReference ? params.replyToMode : "off",
|
|
587
|
-
existingId: params.threadChannel ? params.messageId : void 0,
|
|
588
|
-
startId: params.messageId,
|
|
589
|
-
allowReference
|
|
590
|
-
});
|
|
591
|
-
return {
|
|
592
|
-
deliverTarget,
|
|
593
|
-
replyTarget,
|
|
594
|
-
replyReference
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
//#endregion
|
|
598
|
-
//#region extensions/discord/src/monitor/threading.auto-thread.ts
|
|
599
|
-
function resolveTrimmedDiscordMessageChannelId(params) {
|
|
600
|
-
return (params.messageChannelId || resolveDiscordMessageChannelId({ message: params.message })).trim();
|
|
601
|
-
}
|
|
602
|
-
function resolveDiscordAutoThreadContext(params) {
|
|
603
|
-
const createdThreadId = normalizeOptionalStringifiedId(params.createdThreadId) ?? "";
|
|
604
|
-
if (!createdThreadId) return null;
|
|
605
|
-
const messageChannelId = normalizeOptionalString(params.messageChannelId) ?? "";
|
|
606
|
-
if (!messageChannelId) return null;
|
|
607
|
-
const threadSessionKey = buildAgentSessionKey({
|
|
608
|
-
agentId: params.agentId,
|
|
609
|
-
channel: params.channel,
|
|
610
|
-
peer: {
|
|
611
|
-
kind: "channel",
|
|
612
|
-
id: createdThreadId
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
|
-
const parentSessionKey = buildAgentSessionKey({
|
|
616
|
-
agentId: params.agentId,
|
|
617
|
-
channel: params.channel,
|
|
618
|
-
peer: {
|
|
619
|
-
kind: "channel",
|
|
620
|
-
id: messageChannelId
|
|
621
|
-
}
|
|
622
|
-
});
|
|
623
|
-
return {
|
|
624
|
-
createdThreadId,
|
|
625
|
-
From: `${params.channel}:channel:${createdThreadId}`,
|
|
626
|
-
To: `channel:${createdThreadId}`,
|
|
627
|
-
OriginatingTo: `channel:${createdThreadId}`,
|
|
628
|
-
SessionKey: threadSessionKey,
|
|
629
|
-
ModelParentSessionKey: parentSessionKey,
|
|
630
|
-
...params.parentInheritanceEnabled === true ? { ParentSessionKey: parentSessionKey } : {}
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
async function resolveDiscordAutoThreadReplyPlan(params) {
|
|
634
|
-
const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
|
|
635
|
-
const originalReplyTarget = `channel:${params.threadChannel?.id ?? (messageChannelId || "unknown")}`;
|
|
636
|
-
const createdThreadId = await maybeCreateDiscordAutoThread({
|
|
637
|
-
client: params.client,
|
|
638
|
-
message: params.message,
|
|
639
|
-
messageChannelId: messageChannelId || void 0,
|
|
640
|
-
channel: params.channel,
|
|
641
|
-
isGuildMessage: params.isGuildMessage,
|
|
642
|
-
channelConfig: params.channelConfig,
|
|
643
|
-
threadChannel: params.threadChannel,
|
|
644
|
-
channelType: params.channelType,
|
|
645
|
-
channelName: params.channelName,
|
|
646
|
-
channelDescription: params.channelDescription,
|
|
647
|
-
baseText: params.baseText,
|
|
648
|
-
combinedBody: params.combinedBody,
|
|
649
|
-
cfg: params.cfg,
|
|
650
|
-
agentId: params.agentId
|
|
651
|
-
});
|
|
652
|
-
const deliveryPlan = resolveDiscordReplyDeliveryPlan({
|
|
653
|
-
replyTarget: originalReplyTarget,
|
|
654
|
-
replyToMode: params.replyToMode,
|
|
655
|
-
messageId: params.message.id,
|
|
656
|
-
threadChannel: params.threadChannel,
|
|
657
|
-
createdThreadId
|
|
658
|
-
});
|
|
659
|
-
const autoThreadContext = params.isGuildMessage ? resolveDiscordAutoThreadContext({
|
|
660
|
-
agentId: params.agentId,
|
|
661
|
-
channel: params.channel,
|
|
662
|
-
messageChannelId,
|
|
663
|
-
createdThreadId,
|
|
664
|
-
parentInheritanceEnabled: params.threadParentInheritanceEnabled
|
|
665
|
-
}) : null;
|
|
666
|
-
return {
|
|
667
|
-
...deliveryPlan,
|
|
668
|
-
createdThreadId,
|
|
669
|
-
autoThreadContext
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
async function maybeCreateDiscordAutoThread(params) {
|
|
673
|
-
if (!params.isGuildMessage) return;
|
|
674
|
-
if (!params.channelConfig?.autoThread) return;
|
|
675
|
-
if (params.threadChannel) return;
|
|
676
|
-
if (params.channelType === discord_exports.ChannelType.GuildForum || params.channelType === discord_exports.ChannelType.GuildMedia || params.channelType === discord_exports.ChannelType.GuildVoice || params.channelType === discord_exports.ChannelType.GuildStageVoice) return;
|
|
677
|
-
const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
|
|
678
|
-
if (!messageChannelId) return;
|
|
679
|
-
try {
|
|
680
|
-
try {
|
|
681
|
-
const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id;
|
|
682
|
-
if (existingThreadId) {
|
|
683
|
-
logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
|
|
684
|
-
return existingThreadId;
|
|
685
|
-
}
|
|
686
|
-
} catch {}
|
|
687
|
-
if (params.message.author?.bot) {
|
|
688
|
-
logVerbose(`discord: autoThread skipped for bot-authored message ${messageChannelId}/${params.message.id}`);
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
const rawThreadSource = params.baseText || params.combinedBody || "Thread";
|
|
692
|
-
const threadName = sanitizeDiscordThreadName(rawThreadSource, params.message.id);
|
|
693
|
-
const archiveDuration = params.channelConfig?.autoArchiveDuration ? Number(params.channelConfig.autoArchiveDuration) : 60;
|
|
694
|
-
const createdId = (await createThread(params.client.rest, messageChannelId, { body: {
|
|
695
|
-
name: threadName,
|
|
696
|
-
auto_archive_duration: archiveDuration
|
|
697
|
-
} }, params.message.id))?.id || "";
|
|
698
|
-
if (createdId && params.channelConfig?.autoThreadName === "generated" && params.cfg && params.agentId) {
|
|
699
|
-
const modelRef = resolveDiscordThreadTitleModelRef({
|
|
700
|
-
cfg: params.cfg,
|
|
701
|
-
channel: params.channel,
|
|
702
|
-
agentId: params.agentId,
|
|
703
|
-
threadId: createdId,
|
|
704
|
-
messageChannelId,
|
|
705
|
-
channelName: params.channelName
|
|
706
|
-
});
|
|
707
|
-
maybeRenameDiscordAutoThread({
|
|
708
|
-
client: params.client,
|
|
709
|
-
threadId: createdId,
|
|
710
|
-
currentName: threadName,
|
|
711
|
-
fallbackId: params.message.id,
|
|
712
|
-
sourceText: rawThreadSource,
|
|
713
|
-
modelRef,
|
|
714
|
-
channelName: params.channelName,
|
|
715
|
-
channelDescription: params.channelDescription,
|
|
716
|
-
cfg: params.cfg,
|
|
717
|
-
agentId: params.agentId
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
return createdId || void 0;
|
|
721
|
-
} catch (err) {
|
|
722
|
-
logVerbose(`discord: autoThread creation failed for ${messageChannelId}/${params.message.id}: ${String(err)}`);
|
|
723
|
-
try {
|
|
724
|
-
const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id || "";
|
|
725
|
-
if (existingThreadId) {
|
|
726
|
-
logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
|
|
727
|
-
return existingThreadId;
|
|
728
|
-
}
|
|
729
|
-
} catch {}
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
function resolveDiscordThreadTitleModelRef(params) {
|
|
734
|
-
const channel = params.channel?.trim();
|
|
735
|
-
if (!channel) return;
|
|
736
|
-
const parentSessionKey = buildAgentSessionKey({
|
|
737
|
-
agentId: params.agentId,
|
|
738
|
-
channel,
|
|
739
|
-
peer: {
|
|
740
|
-
kind: "channel",
|
|
741
|
-
id: params.messageChannelId
|
|
742
|
-
}
|
|
743
|
-
});
|
|
744
|
-
const channelLabel = params.channelName?.trim();
|
|
745
|
-
const groupChannel = channelLabel ? `#${channelLabel}` : void 0;
|
|
746
|
-
return resolveChannelModelOverride({
|
|
747
|
-
cfg: params.cfg,
|
|
748
|
-
channel,
|
|
749
|
-
groupId: params.threadId,
|
|
750
|
-
groupChatType: "channel",
|
|
751
|
-
groupChannel,
|
|
752
|
-
groupSubject: groupChannel,
|
|
753
|
-
parentSessionKey
|
|
754
|
-
})?.model;
|
|
755
|
-
}
|
|
756
|
-
async function maybeRenameDiscordAutoThread(params) {
|
|
757
|
-
try {
|
|
758
|
-
const fallbackName = sanitizeDiscordThreadName("", params.fallbackId);
|
|
759
|
-
const generated = await generateThreadTitle({
|
|
760
|
-
cfg: params.cfg,
|
|
761
|
-
agentId: params.agentId,
|
|
762
|
-
messageText: params.sourceText,
|
|
763
|
-
modelRef: params.modelRef,
|
|
764
|
-
channelName: params.channelName,
|
|
765
|
-
channelDescription: params.channelDescription
|
|
766
|
-
});
|
|
767
|
-
if (!generated) return;
|
|
768
|
-
const nextName = sanitizeDiscordThreadName(generated, params.fallbackId);
|
|
769
|
-
if (!nextName || nextName === params.currentName || nextName === fallbackName) return;
|
|
770
|
-
await editChannel(params.client.rest, params.threadId, { body: { name: nextName } });
|
|
771
|
-
} catch (err) {
|
|
772
|
-
logVerbose(`discord: autoThread rename failed for ${params.threadId}: ${String(err)}`);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
//#endregion
|
|
776
|
-
//#region extensions/discord/src/monitor/threading.ts
|
|
777
|
-
var threading_exports = /* @__PURE__ */ __exportAll({
|
|
778
|
-
maybeCreateDiscordAutoThread: () => maybeCreateDiscordAutoThread,
|
|
779
|
-
resetDiscordThreadStarterCacheForTest: () => resetDiscordThreadStarterCacheForTest,
|
|
780
|
-
resolveDiscordAutoThreadContext: () => resolveDiscordAutoThreadContext,
|
|
781
|
-
resolveDiscordAutoThreadReplyPlan: () => resolveDiscordAutoThreadReplyPlan,
|
|
782
|
-
resolveDiscordReplyDeliveryPlan: () => resolveDiscordReplyDeliveryPlan,
|
|
783
|
-
resolveDiscordReplyTarget: () => resolveDiscordReplyTarget,
|
|
784
|
-
resolveDiscordThreadChannel: () => resolveDiscordThreadChannel,
|
|
785
|
-
resolveDiscordThreadParentInfo: () => resolveDiscordThreadParentInfo,
|
|
786
|
-
resolveDiscordThreadStarter: () => resolveDiscordThreadStarter,
|
|
787
|
-
sanitizeDiscordThreadName: () => sanitizeDiscordThreadName
|
|
788
|
-
});
|
|
789
|
-
//#endregion
|
|
790
|
-
//#region extensions/discord/src/monitor/thread-channel-context.ts
|
|
791
|
-
function isDiscordThreadChannelType(type) {
|
|
792
|
-
return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
|
|
793
|
-
}
|
|
794
|
-
function buildFetchedChannelInfo(channel) {
|
|
795
|
-
const channelInfo = resolveDiscordChannelInfoSafe(channel);
|
|
796
|
-
if (channelInfo.type === void 0) return null;
|
|
797
|
-
return {
|
|
798
|
-
type: channelInfo.type,
|
|
799
|
-
name: channelInfo.name,
|
|
800
|
-
topic: channelInfo.topic,
|
|
801
|
-
parentId: channelInfo.parentId,
|
|
802
|
-
ownerId: channelInfo.ownerId
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
async function resolveDiscordThreadLikeChannelContext(params) {
|
|
806
|
-
const safeChannelInfo = resolveDiscordChannelInfoSafe(params.channel);
|
|
807
|
-
const channelId = resolveDiscordChannelIdSafe(params.channel) ?? params.channelIdFallback ?? "";
|
|
808
|
-
const channelInfo = params.channelInfo !== void 0 ? params.channelInfo : channelId ? await resolveDiscordChannelInfo(params.client, channelId) : null;
|
|
809
|
-
const channelType = safeChannelInfo.type ?? channelInfo?.type;
|
|
810
|
-
const channelName = safeChannelInfo.name ?? channelInfo?.name;
|
|
811
|
-
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
|
812
|
-
const parentId = resolveDiscordChannelParentIdSafe(params.channel) ?? channelInfo?.parentId;
|
|
813
|
-
const isThreadChannel = isDiscordThreadChannelType(channelType);
|
|
814
|
-
let threadParentId;
|
|
815
|
-
let threadParentName;
|
|
816
|
-
let threadParentSlug = "";
|
|
817
|
-
if (channelId && isThreadChannel) {
|
|
818
|
-
const parentInfo = await resolveDiscordThreadParentInfo({
|
|
522
|
+
async function resolveDiscordThreadLikeChannelContext(params) {
|
|
523
|
+
const safeChannelInfo = resolveDiscordChannelInfoSafe(params.channel);
|
|
524
|
+
const channelId = resolveDiscordChannelIdSafe(params.channel) ?? params.channelIdFallback ?? "";
|
|
525
|
+
const channelInfo = params.channelInfo !== void 0 ? params.channelInfo : channelId ? await resolveDiscordChannelInfo(params.client, channelId) : null;
|
|
526
|
+
const channelType = safeChannelInfo.type ?? channelInfo?.type;
|
|
527
|
+
const channelName = safeChannelInfo.name ?? channelInfo?.name;
|
|
528
|
+
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
|
529
|
+
const parentId = resolveDiscordChannelParentIdSafe(params.channel) ?? channelInfo?.parentId;
|
|
530
|
+
const isThreadChannel = isDiscordThreadChannelType(channelType);
|
|
531
|
+
let threadParentId;
|
|
532
|
+
let threadParentName;
|
|
533
|
+
let threadParentSlug = "";
|
|
534
|
+
if (channelId && isThreadChannel) {
|
|
535
|
+
const parentInfo = await resolveDiscordThreadParentInfo({
|
|
819
536
|
client: params.client,
|
|
820
537
|
threadChannel: {
|
|
821
538
|
id: channelId,
|
|
@@ -1113,7 +830,7 @@ async function handleDiscordReactionEvent(params) {
|
|
|
1113
830
|
const resolveReactionBase = () => {
|
|
1114
831
|
if (reactionBase) return reactionBase;
|
|
1115
832
|
const emojiLabel = formatDiscordReactionEmoji(data.emoji);
|
|
1116
|
-
const actorLabel = formatDiscordUserTag(user);
|
|
833
|
+
const actorLabel = formatDiscordUserTag(user) || user.id;
|
|
1117
834
|
const guildSlug = guildInfo?.slug || (data.guild?.name ? normalizeDiscordSlug(data.guild.name) : data.guild_id ?? (isGroupDm ? "group-dm" : "dm"));
|
|
1118
835
|
const channelLabel = channelSlug ? `#${channelSlug}` : channelName ? `#${normalizeDiscordSlug(channelName)}` : `#${data.channel_id}`;
|
|
1119
836
|
reactionBase = {
|
|
@@ -1124,24 +841,21 @@ async function handleDiscordReactionEvent(params) {
|
|
|
1124
841
|
};
|
|
1125
842
|
const emitReaction = (text, parentPeerId) => {
|
|
1126
843
|
const { contextKey } = resolveReactionBase();
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
}).sessionKey,
|
|
1143
|
-
contextKey
|
|
1144
|
-
});
|
|
844
|
+
enqueueRoutedSystemEvent(text, resolveAgentRoute({
|
|
845
|
+
cfg: params.cfg,
|
|
846
|
+
channel: "discord",
|
|
847
|
+
accountId: params.accountId,
|
|
848
|
+
guildId: data.guild_id ?? void 0,
|
|
849
|
+
memberRoleIds,
|
|
850
|
+
peer: {
|
|
851
|
+
kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
|
|
852
|
+
id: isDirectMessage ? user.id : data.channel_id
|
|
853
|
+
},
|
|
854
|
+
parentPeer: parentPeerId ? {
|
|
855
|
+
kind: "channel",
|
|
856
|
+
id: parentPeerId
|
|
857
|
+
} : void 0
|
|
858
|
+
}), { contextKey });
|
|
1145
859
|
};
|
|
1146
860
|
const shouldNotifyReaction = (options) => shouldEmitDiscordReactionNotification({
|
|
1147
861
|
mode: options.mode,
|
|
@@ -1222,232 +936,39 @@ async function handleDiscordReactionEvent(params) {
|
|
|
1222
936
|
}
|
|
1223
937
|
}
|
|
1224
938
|
//#endregion
|
|
1225
|
-
//#region extensions/discord/src/monitor/
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
function isDiscordOfflineStatus(status) {
|
|
1231
|
-
return status === "offline";
|
|
1232
|
-
}
|
|
1233
|
-
function resolveDiscordOnlinePresenceEvent(params) {
|
|
1234
|
-
const config = params.config;
|
|
1235
|
-
const userId = params.data.user?.id?.trim();
|
|
1236
|
-
if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
|
|
1237
|
-
if (config.users !== void 0 && !config.users.includes(userId)) return null;
|
|
1238
|
-
if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
|
|
1239
|
-
const lines = [
|
|
1240
|
-
"Discord online-presence event:",
|
|
1241
|
-
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.`,
|
|
1242
|
-
`The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
|
|
1243
|
-
"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.",
|
|
1244
|
-
"Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
|
|
1245
|
-
];
|
|
1246
|
-
return {
|
|
1247
|
-
channelId: config.channelId,
|
|
1248
|
-
userId,
|
|
1249
|
-
text: lines.join("\n")
|
|
1250
|
-
};
|
|
1251
|
-
}
|
|
1252
|
-
//#endregion
|
|
1253
|
-
//#region extensions/discord/src/monitor/presence-cooldown-store.ts
|
|
1254
|
-
const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
|
|
1255
|
-
function openDiscordPresenceCooldownStore() {
|
|
1256
|
-
return getDiscordRuntime().state.openSyncKeyedStore({
|
|
1257
|
-
namespace: "presence-greeting-cooldowns",
|
|
1258
|
-
maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
|
|
1259
|
-
overflowPolicy: "reject-new",
|
|
1260
|
-
defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
|
|
1261
|
-
});
|
|
1262
|
-
}
|
|
1263
|
-
//#endregion
|
|
1264
|
-
//#region extensions/discord/src/monitor/presence-emission-gate.ts
|
|
1265
|
-
const DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS = 300 * 1e3;
|
|
1266
|
-
const DISCORD_PRESENCE_BURST_LIMIT = 8;
|
|
1267
|
-
const DISCORD_PRESENCE_BURST_WINDOW_MS = 60 * 1e3;
|
|
1268
|
-
function resolveDiscordPresenceGateOptions(config) {
|
|
1269
|
-
return {
|
|
1270
|
-
reconnectSuppressMs: config?.reconnectSuppressSeconds !== void 0 ? config.reconnectSuppressSeconds * 1e3 : DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS,
|
|
1271
|
-
burstLimit: config?.burstLimit ?? DISCORD_PRESENCE_BURST_LIMIT,
|
|
1272
|
-
burstWindowMs: config?.burstWindowSeconds !== void 0 ? config.burstWindowSeconds * 1e3 : DISCORD_PRESENCE_BURST_WINDOW_MS
|
|
1273
|
-
};
|
|
939
|
+
//#region extensions/discord/src/monitor/listeners.ts
|
|
940
|
+
function registerDiscordListener(listeners, listener) {
|
|
941
|
+
if (listeners.some((existing) => existing.constructor === listener.constructor)) return false;
|
|
942
|
+
listeners.push(listener);
|
|
943
|
+
return true;
|
|
1274
944
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
this.reconnectLogged = false;
|
|
1282
|
-
this.burstByGuild = /* @__PURE__ */ new Map();
|
|
1283
|
-
this.nextReservationId = 0;
|
|
1284
|
-
}
|
|
1285
|
-
noteGatewaySessionReset(nowMs) {
|
|
1286
|
-
this.lastSessionResetAtMs = nowMs;
|
|
1287
|
-
this.reconnectLogged = false;
|
|
1288
|
-
}
|
|
1289
|
-
evaluateReconnectWindow(nowMs, options) {
|
|
1290
|
-
if (this.lastSessionResetAtMs !== void 0 && options.reconnectSuppressMs > 0 && nowMs - this.lastSessionResetAtMs < options.reconnectSuppressMs) {
|
|
1291
|
-
const shouldLog = !this.reconnectLogged;
|
|
1292
|
-
this.reconnectLogged = true;
|
|
1293
|
-
return {
|
|
1294
|
-
allowed: false,
|
|
1295
|
-
reason: "reconnect-window",
|
|
1296
|
-
shouldLog
|
|
1297
|
-
};
|
|
1298
|
-
}
|
|
1299
|
-
return { allowed: true };
|
|
945
|
+
var DiscordMessageListener = class extends MessageCreateListener {
|
|
946
|
+
constructor(handler, logger, onEvent) {
|
|
947
|
+
super();
|
|
948
|
+
this.handler = handler;
|
|
949
|
+
this.logger = logger;
|
|
950
|
+
this.onEvent = onEvent;
|
|
1300
951
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
this.burstByGuild.set(guildId, state);
|
|
1308
|
-
if (state.reservations.length >= options.burstLimit) {
|
|
1309
|
-
const shouldLog = !state.logged;
|
|
1310
|
-
state.logged = true;
|
|
1311
|
-
return {
|
|
1312
|
-
allowed: false,
|
|
1313
|
-
reason: state.reservations.some((reservation) => !reservation.committed) ? "burst-pending" : "burst",
|
|
1314
|
-
shouldLog
|
|
1315
|
-
};
|
|
952
|
+
async handle(data, client) {
|
|
953
|
+
this.onEvent?.();
|
|
954
|
+
try {
|
|
955
|
+
await this.handler(data, client);
|
|
956
|
+
} catch (err) {
|
|
957
|
+
(this.logger ?? discordEventQueueLog).error(danger(`discord handler failed: ${String(err)}`));
|
|
1316
958
|
}
|
|
1317
|
-
state.logged = false;
|
|
1318
|
-
const reservation = {
|
|
1319
|
-
id: this.nextReservationId++,
|
|
1320
|
-
atMs: nowMs,
|
|
1321
|
-
committed: false
|
|
1322
|
-
};
|
|
1323
|
-
state.reservations.push(reservation);
|
|
1324
|
-
return {
|
|
1325
|
-
allowed: true,
|
|
1326
|
-
reservation: reservation.id
|
|
1327
|
-
};
|
|
1328
959
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
960
|
+
};
|
|
961
|
+
var DiscordInteractionListener = class extends InteractionCreateListener {
|
|
962
|
+
constructor(logger, onEvent) {
|
|
963
|
+
super();
|
|
964
|
+
this.logger = logger;
|
|
965
|
+
this.onEvent = onEvent;
|
|
1334
966
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1341
|
-
};
|
|
1342
|
-
//#endregion
|
|
1343
|
-
//#region extensions/discord/src/monitor/presence-transition-cache.ts
|
|
1344
|
-
const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
|
|
1345
|
-
var DiscordPresenceBaselineCache = class {
|
|
1346
|
-
constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
|
|
1347
|
-
this.maxEntries = maxEntries;
|
|
1348
|
-
this.offlineByKey = /* @__PURE__ */ new Map();
|
|
1349
|
-
this.onlineByKey = /* @__PURE__ */ new Map();
|
|
1350
|
-
}
|
|
1351
|
-
clear() {
|
|
1352
|
-
this.offlineByKey.clear();
|
|
1353
|
-
this.onlineByKey.clear();
|
|
1354
|
-
}
|
|
1355
|
-
clearScope(scope) {
|
|
1356
|
-
for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
|
|
1357
|
-
}
|
|
1358
|
-
isOffline(scope, key) {
|
|
1359
|
-
return this.offlineByKey.get(key) === scope;
|
|
1360
|
-
}
|
|
1361
|
-
isOnline(scope, key) {
|
|
1362
|
-
return this.onlineByKey.get(key) === scope;
|
|
1363
|
-
}
|
|
1364
|
-
observeOffline(scope, key) {
|
|
1365
|
-
this.deleteMarker(this.onlineByKey, scope, key);
|
|
1366
|
-
return this.observe(this.offlineByKey, scope, key);
|
|
1367
|
-
}
|
|
1368
|
-
observeOnline(scope, key) {
|
|
1369
|
-
this.deleteMarker(this.offlineByKey, scope, key);
|
|
1370
|
-
return this.observe(this.onlineByKey, scope, key);
|
|
1371
|
-
}
|
|
1372
|
-
deleteMarker(markers, scope, key) {
|
|
1373
|
-
if (markers.get(key) === scope) markers.delete(key);
|
|
1374
|
-
}
|
|
1375
|
-
observe(markers, scope, key) {
|
|
1376
|
-
markers.delete(key);
|
|
1377
|
-
markers.set(key, scope);
|
|
1378
|
-
let evictedScope;
|
|
1379
|
-
while (markers.size > this.maxEntries) {
|
|
1380
|
-
const oldestKey = markers.keys().next().value;
|
|
1381
|
-
if (oldestKey === void 0) break;
|
|
1382
|
-
evictedScope = markers.get(oldestKey);
|
|
1383
|
-
markers.delete(oldestKey);
|
|
1384
|
-
}
|
|
1385
|
-
return evictedScope;
|
|
1386
|
-
}
|
|
1387
|
-
};
|
|
1388
|
-
//#endregion
|
|
1389
|
-
//#region extensions/discord/src/monitor/thread-session-close.ts
|
|
1390
|
-
/**
|
|
1391
|
-
* Closes every session entry in the store whose key contains {@link threadId}.
|
|
1392
|
-
* The explicit lifecycle deletion archives the old transcript and guarantees
|
|
1393
|
-
* that a later inbound message starts a fresh session in every reset mode.
|
|
1394
|
-
*/
|
|
1395
|
-
async function closeDiscordThreadSessions(params) {
|
|
1396
|
-
const { cfg, accountId, threadId } = params;
|
|
1397
|
-
const normalizedThreadId = normalizeOptionalLowercaseString(threadId) ?? "";
|
|
1398
|
-
if (!normalizedThreadId) return 0;
|
|
1399
|
-
const segmentRe = new RegExp(`:${normalizedThreadId}(?::|$)`, "i");
|
|
1400
|
-
function sessionKeyContainsThreadId(key) {
|
|
1401
|
-
return segmentRe.test(key);
|
|
1402
|
-
}
|
|
1403
|
-
const storePath = resolveStorePath(cfg.session?.store, { agentId: accountId });
|
|
1404
|
-
let resetCount = 0;
|
|
1405
|
-
for (const { sessionKey, entry } of listSessionEntries({ storePath })) {
|
|
1406
|
-
if (!sessionKeyContainsThreadId(sessionKey)) continue;
|
|
1407
|
-
if (await deleteSessionEntry({
|
|
1408
|
-
archiveTranscript: true,
|
|
1409
|
-
expectedSessionId: entry.sessionId ?? null,
|
|
1410
|
-
expectedUpdatedAt: entry.updatedAt,
|
|
1411
|
-
sessionKey,
|
|
1412
|
-
storePath
|
|
1413
|
-
})) resetCount += 1;
|
|
1414
|
-
}
|
|
1415
|
-
return resetCount;
|
|
1416
|
-
}
|
|
1417
|
-
//#endregion
|
|
1418
|
-
//#region extensions/discord/src/monitor/listeners.ts
|
|
1419
|
-
function registerDiscordListener(listeners, listener) {
|
|
1420
|
-
if (listeners.some((existing) => existing.constructor === listener.constructor)) return false;
|
|
1421
|
-
listeners.push(listener);
|
|
1422
|
-
return true;
|
|
1423
|
-
}
|
|
1424
|
-
var DiscordMessageListener = class extends MessageCreateListener {
|
|
1425
|
-
constructor(handler, logger, onEvent) {
|
|
1426
|
-
super();
|
|
1427
|
-
this.handler = handler;
|
|
1428
|
-
this.logger = logger;
|
|
1429
|
-
this.onEvent = onEvent;
|
|
1430
|
-
}
|
|
1431
|
-
async handle(data, client) {
|
|
1432
|
-
this.onEvent?.();
|
|
1433
|
-
try {
|
|
1434
|
-
await this.handler(data, client);
|
|
1435
|
-
} catch (err) {
|
|
1436
|
-
(this.logger ?? discordEventQueueLog).error(danger(`discord handler failed: ${String(err)}`));
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
};
|
|
1440
|
-
var DiscordInteractionListener = class extends InteractionCreateListener {
|
|
1441
|
-
constructor(logger, onEvent) {
|
|
1442
|
-
super();
|
|
1443
|
-
this.logger = logger;
|
|
1444
|
-
this.onEvent = onEvent;
|
|
1445
|
-
}
|
|
1446
|
-
async handle(data, client) {
|
|
1447
|
-
this.onEvent?.();
|
|
1448
|
-
Promise.resolve().then(() => client.handleInteraction(data, {})).catch((err) => {
|
|
1449
|
-
(this.logger ?? discordEventQueueLog).error(danger(`discord interaction handler failed: ${String(err)}`));
|
|
1450
|
-
});
|
|
967
|
+
async handle(data, client) {
|
|
968
|
+
this.onEvent?.();
|
|
969
|
+
Promise.resolve().then(() => client.handleInteraction(data, {})).catch((err) => {
|
|
970
|
+
(this.logger ?? discordEventQueueLog).error(danger(`discord interaction handler failed: ${String(err)}`));
|
|
971
|
+
});
|
|
1451
972
|
}
|
|
1452
973
|
};
|
|
1453
974
|
var DiscordPresenceListener = class extends PresenceUpdateListener {
|
|
@@ -1612,8 +1133,7 @@ var DiscordPresenceListener = class extends PresenceUpdateListener {
|
|
|
1612
1133
|
(this.params.logger ?? discordEventQueueLog).warn(danger(`discord presence cooldown persistence failed: ${String(err)}`));
|
|
1613
1134
|
return;
|
|
1614
1135
|
}
|
|
1615
|
-
if (!
|
|
1616
|
-
sessionKey: route.sessionKey,
|
|
1136
|
+
if (!enqueueRoutedSystemEvent(presenceEvent.text, route, {
|
|
1617
1137
|
contextKey: `discord:presence-online:${this.params.accountId}:${data.guild_id}:${userId}`,
|
|
1618
1138
|
deliveryContext: {
|
|
1619
1139
|
channel: "discord",
|
|
@@ -1679,10 +1199,9 @@ var DiscordPresenceReadyListener = class extends ReadyListener {
|
|
|
1679
1199
|
}
|
|
1680
1200
|
};
|
|
1681
1201
|
var DiscordThreadUpdateListener = class extends ThreadUpdateListener {
|
|
1682
|
-
constructor(cfg,
|
|
1202
|
+
constructor(cfg, logger) {
|
|
1683
1203
|
super();
|
|
1684
1204
|
this.cfg = cfg;
|
|
1685
|
-
this.accountId = accountId;
|
|
1686
1205
|
this.logger = logger;
|
|
1687
1206
|
}
|
|
1688
1207
|
async handle(data) {
|
|
@@ -1697,7 +1216,6 @@ var DiscordThreadUpdateListener = class extends ThreadUpdateListener {
|
|
|
1697
1216
|
const logger = this.logger ?? discordEventQueueLog;
|
|
1698
1217
|
const count = await closeDiscordThreadSessions({
|
|
1699
1218
|
cfg: this.cfg,
|
|
1700
|
-
accountId: this.accountId,
|
|
1701
1219
|
threadId
|
|
1702
1220
|
});
|
|
1703
1221
|
if (count > 0) logger.info("Discord thread archived — reset sessions", {
|
|
@@ -1711,6 +1229,40 @@ var DiscordThreadUpdateListener = class extends ThreadUpdateListener {
|
|
|
1711
1229
|
});
|
|
1712
1230
|
}
|
|
1713
1231
|
};
|
|
1232
|
+
var DiscordThreadDeleteListener = class extends ThreadDeleteListener {
|
|
1233
|
+
constructor(cfg, accountId, logger) {
|
|
1234
|
+
super();
|
|
1235
|
+
this.cfg = cfg;
|
|
1236
|
+
this.accountId = accountId;
|
|
1237
|
+
this.logger = logger;
|
|
1238
|
+
}
|
|
1239
|
+
async handle(data) {
|
|
1240
|
+
await runDiscordListenerWithSlowLog({
|
|
1241
|
+
logger: this.logger,
|
|
1242
|
+
listener: this.constructor.name,
|
|
1243
|
+
event: this.type,
|
|
1244
|
+
run: async () => {
|
|
1245
|
+
const threadId = data.id;
|
|
1246
|
+
getThreadBindingManager(this.accountId)?.unbindThread({
|
|
1247
|
+
threadId,
|
|
1248
|
+
reason: "thread-delete",
|
|
1249
|
+
sendFarewell: false
|
|
1250
|
+
});
|
|
1251
|
+
const count = await closeDiscordThreadSessions({
|
|
1252
|
+
cfg: this.cfg,
|
|
1253
|
+
threadId
|
|
1254
|
+
});
|
|
1255
|
+
if (count > 0) (this.logger ?? discordEventQueueLog).info("Discord thread deleted — reset sessions", {
|
|
1256
|
+
threadId,
|
|
1257
|
+
count
|
|
1258
|
+
});
|
|
1259
|
+
},
|
|
1260
|
+
onError: (err) => {
|
|
1261
|
+
(this.logger ?? discordEventQueueLog).error(danger(`discord thread-delete handler failed: ${String(err)}`));
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1714
1266
|
//#endregion
|
|
1715
1267
|
//#region extensions/discord/src/internal/gateway-dispatch.ts
|
|
1716
1268
|
function dispatchVoiceGatewayEvent(client, type, data) {
|
|
@@ -1808,11 +1360,16 @@ function isDiscordUnknownInteraction(error) {
|
|
|
1808
1360
|
if (/Unknown interaction/i.test(err.rawBody?.message ?? "")) return true;
|
|
1809
1361
|
return false;
|
|
1810
1362
|
}
|
|
1363
|
+
function resolveDiscordInteractionMessageParts(payload) {
|
|
1364
|
+
const { components, embeds } = payload.channelData?.discord ?? {};
|
|
1365
|
+
return {
|
|
1366
|
+
components: Array.isArray(components) && components.length > 0 ? components : void 0,
|
|
1367
|
+
embeds: Array.isArray(embeds) && embeds.length > 0 ? embeds : void 0
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1811
1370
|
function hasRenderableReplyPayload(payload) {
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
if (Array.isArray(discordData?.components) && discordData.components.length > 0) return true;
|
|
1815
|
-
return false;
|
|
1371
|
+
const { components, embeds } = resolveDiscordInteractionMessageParts(payload);
|
|
1372
|
+
return resolveSendableOutboundReplyParts(payload).hasContent || Boolean(components || embeds);
|
|
1816
1373
|
}
|
|
1817
1374
|
async function safeDiscordInteractionCall(label, fn) {
|
|
1818
1375
|
try {
|
|
@@ -1832,43 +1389,25 @@ async function settleDiscordInteractionWithoutVisibleReply(interaction) {
|
|
|
1832
1389
|
async function deliverDiscordInteractionReply(params) {
|
|
1833
1390
|
const { interaction, payload, textLimit, maxLinesPerMessage, preferFollowUp, chunkMode } = params;
|
|
1834
1391
|
const reply = resolveSendableOutboundReplyParts(payload);
|
|
1835
|
-
|
|
1836
|
-
let firstMessageComponents = Array.isArray(discordData?.components) && discordData.components.length > 0 ? discordData.components : void 0;
|
|
1392
|
+
let { components: firstMessageComponents, embeds: firstMessageEmbeds } = resolveDiscordInteractionMessageParts(payload);
|
|
1837
1393
|
let payloadDelivered = false;
|
|
1838
|
-
const sendMessage = async (content, files, components) => {
|
|
1839
|
-
const
|
|
1840
|
-
const payloadLocal =
|
|
1841
|
-
...
|
|
1394
|
+
const sendMessage = async (content, files, components, embeds) => {
|
|
1395
|
+
const hasV2 = hasDiscordV2Components(components);
|
|
1396
|
+
const payloadLocal = {
|
|
1397
|
+
...content && !hasV2 ? { content } : {},
|
|
1842
1398
|
...components ? { components } : {},
|
|
1399
|
+
...embeds && !hasV2 ? { embeds } : {},
|
|
1843
1400
|
...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {},
|
|
1844
|
-
files
|
|
1845
|
-
if (file.data instanceof Blob) return {
|
|
1846
|
-
name: file.name,
|
|
1847
|
-
data: file.data
|
|
1848
|
-
};
|
|
1849
|
-
const arrayBuffer = Uint8Array.from(file.data).buffer;
|
|
1850
|
-
return {
|
|
1851
|
-
name: file.name,
|
|
1852
|
-
data: new Blob([arrayBuffer])
|
|
1853
|
-
};
|
|
1854
|
-
})
|
|
1855
|
-
} : {
|
|
1856
|
-
...contentPayload,
|
|
1857
|
-
...components ? { components } : {},
|
|
1858
|
-
...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {}
|
|
1401
|
+
...files?.length ? { files } : {}
|
|
1859
1402
|
};
|
|
1860
1403
|
let result;
|
|
1861
1404
|
try {
|
|
1862
1405
|
result = await safeDiscordInteractionCall("interaction send", async () => {
|
|
1863
|
-
if (!preferFollowUp && !payloadDelivered)
|
|
1864
|
-
|
|
1865
|
-
payloadDelivered = true;
|
|
1866
|
-
firstMessageComponents = void 0;
|
|
1867
|
-
return;
|
|
1868
|
-
}
|
|
1869
|
-
await interaction.followUp(payloadLocal);
|
|
1406
|
+
if (!preferFollowUp && !payloadDelivered) await interaction.reply(payloadLocal);
|
|
1407
|
+
else await interaction.followUp(payloadLocal);
|
|
1870
1408
|
payloadDelivered = true;
|
|
1871
1409
|
firstMessageComponents = void 0;
|
|
1410
|
+
firstMessageEmbeds = void 0;
|
|
1872
1411
|
});
|
|
1873
1412
|
} catch (error) {
|
|
1874
1413
|
if (!payloadDelivered) throw error;
|
|
@@ -1884,7 +1423,8 @@ async function deliverDiscordInteractionReply(params) {
|
|
|
1884
1423
|
const loaded = await loadWebMedia(url, { localRoots: params.mediaLocalRoots });
|
|
1885
1424
|
return {
|
|
1886
1425
|
name: loaded.fileName ?? "upload",
|
|
1887
|
-
data: loaded.buffer
|
|
1426
|
+
data: loaded.buffer,
|
|
1427
|
+
contentType: loaded.contentType
|
|
1888
1428
|
};
|
|
1889
1429
|
}));
|
|
1890
1430
|
const chunks = resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
|
|
@@ -1892,99 +1432,27 @@ async function deliverDiscordInteractionReply(params) {
|
|
|
1892
1432
|
maxLines: maxLinesPerMessage,
|
|
1893
1433
|
chunkMode
|
|
1894
1434
|
}));
|
|
1895
|
-
await sendMessage(chunks[0] ?? "", media, firstMessageComponents);
|
|
1435
|
+
await sendMessage(chunks[0] ?? "", media, firstMessageComponents, firstMessageEmbeds);
|
|
1896
1436
|
for (const chunk of chunks.slice(1)) {
|
|
1897
1437
|
if (!chunk.trim()) continue;
|
|
1898
1438
|
await sendMessage(chunk);
|
|
1899
1439
|
}
|
|
1900
1440
|
return payloadDelivered;
|
|
1901
1441
|
}
|
|
1902
|
-
if (!reply.hasText && !firstMessageComponents) return false;
|
|
1903
|
-
|
|
1442
|
+
if (!reply.hasText && !firstMessageComponents && !firstMessageEmbeds) return false;
|
|
1443
|
+
const chunks = resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
|
|
1904
1444
|
maxChars: textLimit,
|
|
1905
1445
|
maxLines: maxLinesPerMessage,
|
|
1906
1446
|
chunkMode
|
|
1907
|
-
}))
|
|
1908
|
-
if (chunks.length === 0
|
|
1447
|
+
}));
|
|
1448
|
+
if (chunks.length === 0) chunks.push("");
|
|
1909
1449
|
for (const chunk of chunks) {
|
|
1910
|
-
if (!chunk.trim() && !firstMessageComponents) continue;
|
|
1911
|
-
await sendMessage(chunk, void 0, firstMessageComponents);
|
|
1450
|
+
if (!chunk.trim() && !firstMessageComponents && !firstMessageEmbeds) continue;
|
|
1451
|
+
await sendMessage(chunk, void 0, firstMessageComponents, firstMessageEmbeds);
|
|
1912
1452
|
}
|
|
1913
1453
|
return payloadDelivered;
|
|
1914
1454
|
}
|
|
1915
1455
|
//#endregion
|
|
1916
|
-
//#region extensions/discord/src/monitor/route-resolution.ts
|
|
1917
|
-
function buildDiscordRoutePeer(params) {
|
|
1918
|
-
return {
|
|
1919
|
-
kind: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
|
|
1920
|
-
id: params.isDirectMessage ? params.directUserId?.trim() || params.conversationId : params.conversationId
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
function resolveDiscordConversationRoute(params) {
|
|
1924
|
-
return resolveAgentRoute({
|
|
1925
|
-
cfg: params.cfg,
|
|
1926
|
-
channel: "discord",
|
|
1927
|
-
accountId: params.accountId,
|
|
1928
|
-
guildId: params.guildId ?? void 0,
|
|
1929
|
-
memberRoleIds: params.memberRoleIds,
|
|
1930
|
-
peer: params.peer,
|
|
1931
|
-
parentPeer: params.parentConversationId ? {
|
|
1932
|
-
kind: "channel",
|
|
1933
|
-
id: params.parentConversationId
|
|
1934
|
-
} : void 0
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
|
-
function resolveDiscordBoundConversationRoute(params) {
|
|
1938
|
-
return resolveDiscordEffectiveRoute({
|
|
1939
|
-
route: resolveDiscordConversationRoute({
|
|
1940
|
-
cfg: params.cfg,
|
|
1941
|
-
accountId: params.accountId,
|
|
1942
|
-
guildId: params.guildId,
|
|
1943
|
-
memberRoleIds: params.memberRoleIds,
|
|
1944
|
-
peer: buildDiscordRoutePeer({
|
|
1945
|
-
isDirectMessage: params.isDirectMessage,
|
|
1946
|
-
isGroupDm: params.isGroupDm,
|
|
1947
|
-
directUserId: params.directUserId,
|
|
1948
|
-
conversationId: params.conversationId
|
|
1949
|
-
}),
|
|
1950
|
-
parentConversationId: params.parentConversationId
|
|
1951
|
-
}),
|
|
1952
|
-
boundSessionKey: params.boundSessionKey,
|
|
1953
|
-
configuredRoute: params.configuredRoute,
|
|
1954
|
-
matchedBy: params.matchedBy
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
function resolveDiscordEffectiveRoute(params) {
|
|
1958
|
-
const boundSessionKey = params.boundSessionKey?.trim();
|
|
1959
|
-
if (!boundSessionKey) return params.configuredRoute?.route ?? params.route;
|
|
1960
|
-
return {
|
|
1961
|
-
...params.route,
|
|
1962
|
-
sessionKey: boundSessionKey,
|
|
1963
|
-
agentId: resolveAgentIdFromSessionKey(boundSessionKey),
|
|
1964
|
-
lastRoutePolicy: deriveLastRoutePolicy({
|
|
1965
|
-
sessionKey: boundSessionKey,
|
|
1966
|
-
mainSessionKey: params.route.mainSessionKey
|
|
1967
|
-
}),
|
|
1968
|
-
...params.matchedBy ? { matchedBy: params.matchedBy } : {}
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
function hasExplicitRuntimeBindingIntent(record) {
|
|
1972
|
-
if (record.targetKind === "subagent") return true;
|
|
1973
|
-
if (isAcpSessionKey(record.targetSessionKey) || isSubagentSessionKey(record.targetSessionKey)) return true;
|
|
1974
|
-
const metadata = record.metadata;
|
|
1975
|
-
if (!metadata || typeof metadata !== "object") return false;
|
|
1976
|
-
return typeof metadata.boundBy === "string" || typeof metadata.label === "string" || typeof metadata.threadName === "string" || metadata.pluginBindingOwner === "plugin";
|
|
1977
|
-
}
|
|
1978
|
-
function shouldIgnoreStaleDiscordRouteBinding(params) {
|
|
1979
|
-
const bindingRecord = params.bindingRecord;
|
|
1980
|
-
const boundSessionKey = bindingRecord?.targetSessionKey?.trim();
|
|
1981
|
-
if (!bindingRecord || !boundSessionKey || hasExplicitRuntimeBindingIntent(bindingRecord)) return false;
|
|
1982
|
-
const bound = parseAgentSessionKey(boundSessionKey);
|
|
1983
|
-
const routed = parseAgentSessionKey(params.route.sessionKey);
|
|
1984
|
-
if (!bound || !routed || bound.rest !== routed.rest) return false;
|
|
1985
|
-
return bound.agentId !== params.route.agentId;
|
|
1986
|
-
}
|
|
1987
|
-
//#endregion
|
|
1988
1456
|
//#region extensions/discord/src/monitor/native-command-route.ts
|
|
1989
1457
|
async function resolveDiscordNativeInteractionRouteState(params) {
|
|
1990
1458
|
const route = resolveDiscordBoundConversationRoute({
|
|
@@ -2031,8 +1499,6 @@ async function resolveDiscordNativeInteractionRouteState(params) {
|
|
|
2031
1499
|
//#endregion
|
|
2032
1500
|
//#region extensions/discord/src/monitor/native-command.runtime.ts
|
|
2033
1501
|
const nativeCommandRuntime = {
|
|
2034
|
-
matchPluginCommand: pluginRuntime.matchPluginCommand,
|
|
2035
|
-
executePluginCommand: pluginRuntime.executePluginCommand,
|
|
2036
1502
|
dispatchChannelInboundTurn,
|
|
2037
1503
|
resolveDirectStatusReplyForSession,
|
|
2038
1504
|
resolveDiscordNativeInteractionRouteState,
|
|
@@ -2043,7 +1509,8 @@ const nativeCommandRuntime = {
|
|
|
2043
1509
|
async function dispatchDiscordNativeAgentReply(params) {
|
|
2044
1510
|
const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(params.discordConfig);
|
|
2045
1511
|
let didReply = false;
|
|
2046
|
-
let
|
|
1512
|
+
let finalReplyOutcome;
|
|
1513
|
+
let hiddenFinalReply;
|
|
2047
1514
|
const turnResult = await nativeCommandRuntime.dispatchChannelInboundTurn({
|
|
2048
1515
|
cfg: params.cfg,
|
|
2049
1516
|
channel: "discord",
|
|
@@ -2079,14 +1546,17 @@ async function dispatchDiscordNativeAgentReply(params) {
|
|
|
2079
1546
|
suppression: { reason: "no_visible_result" }
|
|
2080
1547
|
};
|
|
2081
1548
|
},
|
|
2082
|
-
onDelivered: (
|
|
2083
|
-
if (result?.
|
|
1549
|
+
onDelivered: (payload, info, result) => {
|
|
1550
|
+
if (params.suppressReplies && info.kind === "final" && result?.suppression?.reason === "no_visible_result" && payload.text?.trim()) hiddenFinalReply = payload;
|
|
1551
|
+
if (info.kind === "final" && result?.visibleReplySent !== void 0 && (result.visibleReplySent || finalReplyOutcome !== "failed")) finalReplyOutcome = result.visibleReplySent ? "accepted" : "suppressed";
|
|
2084
1552
|
},
|
|
2085
1553
|
onError: (err, info) => {
|
|
2086
|
-
|
|
1554
|
+
const partialDelivery = isChannelPartialDeliveryError(err);
|
|
1555
|
+
if (partialDelivery) {
|
|
2087
1556
|
didReply = true;
|
|
2088
1557
|
logVerbose("discord: interaction reply partially delivered before expiry");
|
|
2089
1558
|
}
|
|
1559
|
+
if (info.kind === "final") finalReplyOutcome = partialDelivery ? "accepted" : "failed";
|
|
2090
1560
|
const message = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
2091
1561
|
params.log.error(`discord slash ${info.kind} reply failed: ${message}`);
|
|
2092
1562
|
}
|
|
@@ -2095,14 +1565,20 @@ async function dispatchDiscordNativeAgentReply(params) {
|
|
|
2095
1565
|
dispatcherOptions: { humanDelay: resolveHumanDelayConfig(params.cfg, params.effectiveRoute.agentId) },
|
|
2096
1566
|
replyOptions: {
|
|
2097
1567
|
skillFilter: params.channelConfig?.skills,
|
|
1568
|
+
[PLUGIN_COMMAND_DISPATCH]: params.pluginCommandDispatch,
|
|
2098
1569
|
disableBlockStreaming: typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0
|
|
2099
1570
|
}
|
|
2100
1571
|
});
|
|
2101
|
-
|
|
1572
|
+
const deliberateSilentTerminalReply = turnResult.dispatched && turnResult.dispatchResult.deliberateSilentTerminalReply === true;
|
|
1573
|
+
const dispatchResult = {
|
|
1574
|
+
dispatched: turnResult.dispatched,
|
|
1575
|
+
...hiddenFinalReply ? { hiddenFinalReply } : {}
|
|
1576
|
+
};
|
|
1577
|
+
if (!didReply && (params.suppressReplies || finalReplyOutcome === "suppressed" || deliberateSilentTerminalReply)) {
|
|
2102
1578
|
await settleDiscordInteractionWithoutVisibleReply(params.interaction);
|
|
2103
|
-
return;
|
|
1579
|
+
return dispatchResult;
|
|
2104
1580
|
}
|
|
2105
|
-
if (
|
|
1581
|
+
if (didReply || turnResult.dispatched && hasVisibleInboundReplyDispatch(turnResult.dispatchResult)) return dispatchResult;
|
|
2106
1582
|
await safeDiscordInteractionCall("interaction empty fallback", async () => {
|
|
2107
1583
|
const payload = {
|
|
2108
1584
|
content: DISCORD_EMPTY_VISIBLE_REPLY_WARNING,
|
|
@@ -2114,6 +1590,7 @@ async function dispatchDiscordNativeAgentReply(params) {
|
|
|
2114
1590
|
}
|
|
2115
1591
|
await params.interaction.reply(payload);
|
|
2116
1592
|
});
|
|
1593
|
+
return dispatchResult;
|
|
2117
1594
|
}
|
|
2118
1595
|
//#endregion
|
|
2119
1596
|
//#region extensions/discord/src/monitor/native-interaction-channel-context.ts
|
|
@@ -2391,11 +1868,11 @@ async function resolveDiscordNativeAutocompleteAuthorized(params) {
|
|
|
2391
1868
|
//#endregion
|
|
2392
1869
|
//#region extensions/discord/src/monitor/native-command-bypass.ts
|
|
2393
1870
|
function shouldBypassConfiguredAcpEnsure(commandName) {
|
|
2394
|
-
return
|
|
1871
|
+
return commandName.trim().toLowerCase() === "acp";
|
|
2395
1872
|
}
|
|
2396
1873
|
function shouldBypassConfiguredAcpGuildGuards(commandName) {
|
|
2397
|
-
const
|
|
2398
|
-
return
|
|
1874
|
+
const command = commandName.trim().toLowerCase();
|
|
1875
|
+
return command === "new" || command === "reset";
|
|
2399
1876
|
}
|
|
2400
1877
|
//#endregion
|
|
2401
1878
|
//#region extensions/discord/src/monitor/inbound-context.ts
|
|
@@ -2474,6 +1951,14 @@ function buildDiscordNativeCommandContext(params) {
|
|
|
2474
1951
|
CommandTargetSessionKey: params.commandTargetSessionKey,
|
|
2475
1952
|
AccountId: params.accountId ?? void 0,
|
|
2476
1953
|
ChatType: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
|
|
1954
|
+
...buildDiscordConversationRouteContext({
|
|
1955
|
+
isDirectMessage: params.isDirectMessage,
|
|
1956
|
+
isGroupDm: params.isGroupDm,
|
|
1957
|
+
directUserId: params.user.id,
|
|
1958
|
+
conversationId: params.channelId,
|
|
1959
|
+
isThread: params.isThreadChannel,
|
|
1960
|
+
parentConversationId: params.threadParentId
|
|
1961
|
+
}),
|
|
2477
1962
|
ConversationLabel: conversationLabel,
|
|
2478
1963
|
GroupSubject: params.isGuild ? params.guildName : void 0,
|
|
2479
1964
|
GroupSpace: params.isGuild ? params.guildInfo?.id ?? params.guildInfo?.slug ?? params.guildId : void 0,
|
|
@@ -2489,7 +1974,6 @@ function buildDiscordNativeCommandContext(params) {
|
|
|
2489
1974
|
Surface: "discord",
|
|
2490
1975
|
WasMentioned: true,
|
|
2491
1976
|
MessageSid: params.interactionId,
|
|
2492
|
-
MessageThreadId: params.isThreadChannel ? params.channelId : void 0,
|
|
2493
1977
|
Timestamp: params.timestampMs ?? Date.now(),
|
|
2494
1978
|
CommandAuthorized: params.commandAuthorized,
|
|
2495
1979
|
CommandTurn: {
|
|
@@ -2504,8 +1988,7 @@ function buildDiscordNativeCommandContext(params) {
|
|
|
2504
1988
|
isDirectMessage: params.isDirectMessage,
|
|
2505
1989
|
userId: params.user.id,
|
|
2506
1990
|
channelId: params.channelId
|
|
2507
|
-
}) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`)
|
|
2508
|
-
ThreadParentId: params.isThreadChannel ? params.threadParentId : void 0
|
|
1991
|
+
}) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`)
|
|
2509
1992
|
});
|
|
2510
1993
|
}
|
|
2511
1994
|
//#endregion
|
|
@@ -2623,7 +2106,8 @@ async function handleDiscordCommandArgInteraction(params) {
|
|
|
2623
2106
|
sessionPrefix: ctx.sessionPrefix,
|
|
2624
2107
|
preferFollowUp: true,
|
|
2625
2108
|
threadBindings: ctx.threadBindings,
|
|
2626
|
-
responseEphemeral: resolveDiscordSlashCommandConfig(ctx.discordConfig?.slashCommand).ephemeral
|
|
2109
|
+
responseEphemeral: resolveDiscordSlashCommandConfig(ctx.discordConfig?.slashCommand).ephemeral,
|
|
2110
|
+
pluginCommandDispatch: { kind: "non-plugin" }
|
|
2627
2111
|
});
|
|
2628
2112
|
}
|
|
2629
2113
|
async function runDiscordCommandArgButton(params) {
|
|
@@ -2730,10 +2214,10 @@ function timestampOrder(value) {
|
|
|
2730
2214
|
return value !== void 0 && value >= 0 ? value : 0;
|
|
2731
2215
|
}
|
|
2732
2216
|
function comparePreferenceEntries(left, right) {
|
|
2733
|
-
return
|
|
2217
|
+
return preferenceTimestampMs(right.value.updatedAt) - preferenceTimestampMs(left.value.updatedAt) || timestampOrder(right.value.updatedOrder) - timestampOrder(left.value.updatedOrder) || left.key.localeCompare(right.key);
|
|
2734
2218
|
}
|
|
2735
2219
|
function nextPreferenceTimestamp(existingEntries) {
|
|
2736
|
-
const existingMaxTimestampMs = existingEntries.reduce((max, entry) => Math.max(max,
|
|
2220
|
+
const existingMaxTimestampMs = existingEntries.reduce((max, entry) => Math.max(max, preferenceTimestampMs(entry.updatedAt)), 0);
|
|
2737
2221
|
lastPreferenceTimestampMs = Math.min(Math.max(resolveDateTimestampMs(Date.now(), 0), lastPreferenceTimestampMs + 1, existingMaxTimestampMs + 1), MAX_DATE_TIMESTAMP_MS);
|
|
2738
2222
|
const existingMaxOrder = existingEntries.reduce((max, entry) => Math.max(max, timestampOrder(entry.updatedOrder)), 0);
|
|
2739
2223
|
lastPreferenceOrder = Math.max(lastPreferenceOrder + 1, existingMaxOrder + 1);
|
|
@@ -2782,8 +2266,6 @@ async function recordDiscordModelPickerRecentModel(params) {
|
|
|
2782
2266
|
const DISCORD_MODEL_PICKER_CUSTOM_ID_KEY = "mdlpk";
|
|
2783
2267
|
const DISCORD_CUSTOM_ID_MAX_CHARS = 100;
|
|
2784
2268
|
const DISCORD_COMPONENT_MAX_SELECT_OPTIONS = 25;
|
|
2785
|
-
const DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2786
|
-
const DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2787
2269
|
function compareBucketItems(left, right) {
|
|
2788
2270
|
const normalized = left.toLowerCase().localeCompare(right.toLowerCase());
|
|
2789
2271
|
return normalized === 0 ? left.localeCompare(right) : normalized;
|
|
@@ -2843,15 +2325,15 @@ function parseRawPage(value) {
|
|
|
2843
2325
|
}
|
|
2844
2326
|
return 1;
|
|
2845
2327
|
}
|
|
2846
|
-
function parseRawPositiveInt(value) {
|
|
2847
|
-
return parseStrictPositiveInteger(value);
|
|
2848
|
-
}
|
|
2849
2328
|
function coerceString(value) {
|
|
2850
2329
|
return typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
2851
2330
|
}
|
|
2852
|
-
function clampPageSize(rawPageSize
|
|
2853
|
-
if (!Number.isFinite(rawPageSize)) return
|
|
2854
|
-
return Math.min(
|
|
2331
|
+
function clampPageSize(rawPageSize) {
|
|
2332
|
+
if (!Number.isFinite(rawPageSize)) return DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
|
|
2333
|
+
return Math.min(DISCORD_COMPONENT_MAX_SELECT_OPTIONS, Math.max(1, Math.floor(rawPageSize ?? DISCORD_COMPONENT_MAX_SELECT_OPTIONS)));
|
|
2334
|
+
}
|
|
2335
|
+
function normalizeOptionalModelPickerIndex(value) {
|
|
2336
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : void 0;
|
|
2855
2337
|
}
|
|
2856
2338
|
function paginateItems(params) {
|
|
2857
2339
|
const totalItems = params.items.length;
|
|
@@ -2877,10 +2359,10 @@ function buildDiscordModelPickerCustomId(params) {
|
|
|
2877
2359
|
const userId = params.userId.trim();
|
|
2878
2360
|
if (!userId) throw new Error("Discord model picker custom_id requires userId");
|
|
2879
2361
|
const page = normalizeModelPickerPage(params.page);
|
|
2880
|
-
const providerPage =
|
|
2362
|
+
const providerPage = normalizeOptionalModelPickerIndex(params.providerPage);
|
|
2881
2363
|
const normalizedProvider = params.provider ? normalizeProviderId(params.provider) : void 0;
|
|
2882
|
-
const modelIndex =
|
|
2883
|
-
const recentSlot =
|
|
2364
|
+
const modelIndex = normalizeOptionalModelPickerIndex(params.modelIndex);
|
|
2365
|
+
const recentSlot = normalizeOptionalModelPickerIndex(params.recentSlot);
|
|
2884
2366
|
const modelToken = params.modelToken?.trim();
|
|
2885
2367
|
if (modelToken && !DISCORD_MODEL_PICKER_MODEL_TOKEN_PATTERN.test(modelToken)) throw new Error("Discord model picker model token is invalid");
|
|
2886
2368
|
const parts = [
|
|
@@ -2893,7 +2375,7 @@ function buildDiscordModelPickerCustomId(params) {
|
|
|
2893
2375
|
if (normalizedProvider) parts.push(`p=${encodeCustomIdComponent(normalizedProvider)}`);
|
|
2894
2376
|
const runtime = params.runtime?.trim();
|
|
2895
2377
|
if (runtime) parts.push(`r=${encodeCustomIdComponent(runtime)}`);
|
|
2896
|
-
const runtimeIndex =
|
|
2378
|
+
const runtimeIndex = normalizeOptionalModelPickerIndex(params.runtimeIndex);
|
|
2897
2379
|
if (runtimeIndex) parts.push(`ri=${String(runtimeIndex)}`);
|
|
2898
2380
|
if (providerPage) parts.push(`pp=${String(providerPage)}`);
|
|
2899
2381
|
if (modelToken) parts.push(`m=${modelToken}`);
|
|
@@ -2917,13 +2399,13 @@ function parseDiscordModelPickerData(data) {
|
|
|
2917
2399
|
const userId = decodeCustomIdComponent(coerceString(data.u));
|
|
2918
2400
|
const providerRaw = decodeCustomIdComponent(coerceString(data.p));
|
|
2919
2401
|
const runtimeRaw = decodeCustomIdComponent(coerceString(data.r));
|
|
2920
|
-
const runtimeIndex =
|
|
2402
|
+
const runtimeIndex = parseStrictPositiveInteger(data.ri);
|
|
2921
2403
|
const page = parseRawPage(data.g ?? data.pg);
|
|
2922
|
-
const providerPage =
|
|
2923
|
-
const modelIndex =
|
|
2404
|
+
const providerPage = parseStrictPositiveInteger(data.pp);
|
|
2405
|
+
const modelIndex = parseStrictPositiveInteger(data.mi);
|
|
2924
2406
|
const modelTokenRaw = coerceString(data.m).trim();
|
|
2925
2407
|
const modelToken = DISCORD_MODEL_PICKER_MODEL_TOKEN_PATTERN.test(modelTokenRaw) ? modelTokenRaw : void 0;
|
|
2926
|
-
const recentSlot =
|
|
2408
|
+
const recentSlot = parseStrictPositiveInteger(data.rs);
|
|
2927
2409
|
const providerBucketRaw = decodeCustomIdComponent(coerceString(data.pb)).trim().toLowerCase();
|
|
2928
2410
|
const modelBucketRaw = decodeCustomIdComponent(coerceString(data.mb)).trim().toLowerCase();
|
|
2929
2411
|
if (!isValidCommandContext(command) || !isValidPickerAction(action) || !isValidPickerView(view)) return null;
|
|
@@ -3039,17 +2521,7 @@ function findProviderBucketId(data, provider) {
|
|
|
3039
2521
|
return findProviderBucketLocation(data, provider)?.bucket;
|
|
3040
2522
|
}
|
|
3041
2523
|
function findProviderBucketLocation(data, provider) {
|
|
3042
|
-
|
|
3043
|
-
const sorted = [...data.providers].toSorted();
|
|
3044
|
-
const idx = sorted.indexOf(normalized);
|
|
3045
|
-
if (idx < 0) return;
|
|
3046
|
-
const containing = computeAlphaBuckets(sorted).find((bucket) => idx >= bucket.start && idx < bucket.end);
|
|
3047
|
-
if (!containing) return;
|
|
3048
|
-
const page = Math.floor((idx - containing.start) / DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE) + 1;
|
|
3049
|
-
return {
|
|
3050
|
-
...containing.id !== "all" ? { bucket: containing.id } : {},
|
|
3051
|
-
page
|
|
3052
|
-
};
|
|
2524
|
+
return findModelPickerBucketLocation([...data.providers].toSorted(), normalizeProviderId(provider));
|
|
3053
2525
|
}
|
|
3054
2526
|
/**
|
|
3055
2527
|
* Derive the alpha-bucket id that contains a given model id within the
|
|
@@ -3059,28 +2531,24 @@ function findProviderBucketLocation(data, provider) {
|
|
|
3059
2531
|
*/
|
|
3060
2532
|
function findModelBucketId(data, provider, model) {
|
|
3061
2533
|
const modelSet = data.byProvider.get(normalizeProviderId(provider));
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
const
|
|
3067
|
-
return
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
}
|
|
3075
|
-
function getDiscordModelPickerProviderPage(params) {
|
|
3076
|
-
const allItems = buildDiscordModelPickerProviderItems(params.data);
|
|
3077
|
-
const buckets = computeAlphaBuckets(allItems.map((item) => item.id));
|
|
2534
|
+
return modelSet ? findModelPickerBucketLocation([...modelSet].toSorted(compareBucketItems), model)?.bucket : void 0;
|
|
2535
|
+
}
|
|
2536
|
+
function findModelPickerBucketLocation(sortedItems, item, pageSize = DISCORD_COMPONENT_MAX_SELECT_OPTIONS) {
|
|
2537
|
+
const index = sortedItems.indexOf(item);
|
|
2538
|
+
const bucket = index < 0 ? void 0 : computeAlphaBuckets(sortedItems).find((entry) => index >= entry.start && index < entry.end);
|
|
2539
|
+
return bucket ? {
|
|
2540
|
+
...bucket.id === "all" ? {} : { bucket: bucket.id },
|
|
2541
|
+
page: Math.floor((index - bucket.start) / pageSize) + 1
|
|
2542
|
+
} : void 0;
|
|
2543
|
+
}
|
|
2544
|
+
function paginateDiscordModelPickerBucket(params) {
|
|
2545
|
+
const buckets = computeAlphaBuckets(params.itemLabels);
|
|
3078
2546
|
const bucket = resolveBucket(buckets, params.bucket);
|
|
3079
|
-
const
|
|
3080
|
-
const pageSize = clampPageSize(params.pageSize
|
|
2547
|
+
const items = bucket ? params.items.slice(bucket.start, bucket.end) : params.items;
|
|
2548
|
+
const pageSize = clampPageSize(params.pageSize);
|
|
3081
2549
|
return {
|
|
3082
2550
|
...paginateItems({
|
|
3083
|
-
items
|
|
2551
|
+
items,
|
|
3084
2552
|
page: normalizeModelPickerPage(params.page),
|
|
3085
2553
|
pageSize
|
|
3086
2554
|
}),
|
|
@@ -3088,24 +2556,29 @@ function getDiscordModelPickerProviderPage(params) {
|
|
|
3088
2556
|
buckets
|
|
3089
2557
|
};
|
|
3090
2558
|
}
|
|
2559
|
+
function getDiscordModelPickerProviderPage(params) {
|
|
2560
|
+
const providers = [...params.data.providers].toSorted();
|
|
2561
|
+
return paginateDiscordModelPickerBucket({
|
|
2562
|
+
...params,
|
|
2563
|
+
itemLabels: providers,
|
|
2564
|
+
items: providers.map((provider) => ({
|
|
2565
|
+
id: provider,
|
|
2566
|
+
count: params.data.byProvider.get(provider)?.size ?? 0
|
|
2567
|
+
}))
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
3091
2570
|
function getDiscordModelPickerModelPage(params) {
|
|
3092
2571
|
const provider = normalizeProviderId(params.provider);
|
|
3093
2572
|
const modelSet = params.data.byProvider.get(provider);
|
|
3094
2573
|
if (!modelSet) return null;
|
|
3095
2574
|
const allModels = [...modelSet].toSorted(compareBucketItems);
|
|
3096
|
-
const buckets = computeAlphaBuckets(allModels);
|
|
3097
|
-
const bucket = resolveBucket(buckets, params.bucket);
|
|
3098
|
-
const bucketItems = bucket ? allModels.slice(bucket.start, bucket.end) : allModels;
|
|
3099
|
-
const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE);
|
|
3100
2575
|
return {
|
|
3101
|
-
...
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
2576
|
+
...paginateDiscordModelPickerBucket({
|
|
2577
|
+
...params,
|
|
2578
|
+
items: allModels,
|
|
2579
|
+
itemLabels: allModels
|
|
3105
2580
|
}),
|
|
3106
|
-
provider
|
|
3107
|
-
bucket,
|
|
3108
|
-
buckets
|
|
2581
|
+
provider
|
|
3109
2582
|
};
|
|
3110
2583
|
}
|
|
3111
2584
|
function resolveDiscordModelPickerPageForModel(params) {
|
|
@@ -3113,16 +2586,8 @@ function resolveDiscordModelPickerPageForModel(params) {
|
|
|
3113
2586
|
const modelSet = params.data.byProvider.get(provider);
|
|
3114
2587
|
if (!modelSet) return { page: 1 };
|
|
3115
2588
|
const sorted = [...modelSet].toSorted(compareBucketItems);
|
|
3116
|
-
const
|
|
3117
|
-
|
|
3118
|
-
const pageSize = clampPageSize(params.pageSize, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE, DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE);
|
|
3119
|
-
const containingBucket = computeAlphaBuckets(sorted).find((bucket) => index >= bucket.start && index < bucket.end);
|
|
3120
|
-
if (!containingBucket) return { page: Math.floor(index / pageSize) + 1 };
|
|
3121
|
-
const offsetInBucket = index - containingBucket.start;
|
|
3122
|
-
return {
|
|
3123
|
-
page: Math.floor(offsetInBucket / pageSize) + 1,
|
|
3124
|
-
bucket: containingBucket.id === "all" ? void 0 : containingBucket.id
|
|
3125
|
-
};
|
|
2589
|
+
const pageSize = clampPageSize(params.pageSize);
|
|
2590
|
+
return findModelPickerBucketLocation(sorted, params.model, pageSize) ?? { page: 1 };
|
|
3126
2591
|
}
|
|
3127
2592
|
//#endregion
|
|
3128
2593
|
//#region extensions/discord/src/monitor/model-picker.view.ts
|
|
@@ -3302,53 +2767,25 @@ function buildProviderSelectRow(params) {
|
|
|
3302
2767
|
}
|
|
3303
2768
|
function buildPaginationRow(params) {
|
|
3304
2769
|
if (params.totalPages <= 1) return null;
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
view: params.view,
|
|
3314
|
-
provider: params.provider,
|
|
3315
|
-
runtime: params.runtime,
|
|
3316
|
-
runtimeIndex: params.runtimeIndex,
|
|
3317
|
-
page: Math.max(1, params.page - 1),
|
|
3318
|
-
providerPage: params.providerPage,
|
|
3319
|
-
modelIndex: params.modelIndex,
|
|
3320
|
-
modelToken: params.modelToken,
|
|
3321
|
-
providerBucket: params.providerBucket,
|
|
3322
|
-
modelBucket: params.modelBucket,
|
|
3323
|
-
userId: params.userId
|
|
3324
|
-
})
|
|
3325
|
-
}),
|
|
3326
|
-
createModelPickerButton({
|
|
3327
|
-
label: `Page ${params.page}/${params.totalPages}`,
|
|
3328
|
-
style: ButtonStyle.Secondary,
|
|
3329
|
-
disabled: true,
|
|
3330
|
-
customId: DISCORD_MODEL_PICKER_PAGE_INDICATOR_CUSTOM_ID
|
|
3331
|
-
}),
|
|
3332
|
-
createModelPickerButton({
|
|
3333
|
-
label: "Next ▶",
|
|
3334
|
-
style: ButtonStyle.Secondary,
|
|
3335
|
-
disabled: !params.hasNext,
|
|
3336
|
-
customId: buildDiscordModelPickerCustomId({
|
|
3337
|
-
command: params.command,
|
|
3338
|
-
action: "nav",
|
|
3339
|
-
view: params.view,
|
|
3340
|
-
provider: params.provider,
|
|
3341
|
-
runtime: params.runtime,
|
|
3342
|
-
runtimeIndex: params.runtimeIndex,
|
|
3343
|
-
page: Math.min(params.totalPages, params.page + 1),
|
|
3344
|
-
providerPage: params.providerPage,
|
|
3345
|
-
modelIndex: params.modelIndex,
|
|
3346
|
-
modelToken: params.modelToken,
|
|
3347
|
-
providerBucket: params.providerBucket,
|
|
3348
|
-
modelBucket: params.modelBucket,
|
|
3349
|
-
userId: params.userId
|
|
3350
|
-
})
|
|
2770
|
+
const { page, totalPages, hasPrev, hasNext, ...navigationState } = params;
|
|
2771
|
+
const createNavigationButton = (label, targetPage, enabled) => createModelPickerButton({
|
|
2772
|
+
label,
|
|
2773
|
+
disabled: !enabled,
|
|
2774
|
+
customId: buildDiscordModelPickerCustomId({
|
|
2775
|
+
...navigationState,
|
|
2776
|
+
action: "nav",
|
|
2777
|
+
page: targetPage
|
|
3351
2778
|
})
|
|
2779
|
+
});
|
|
2780
|
+
const indicatorButton = createModelPickerButton({
|
|
2781
|
+
label: `Page ${page}/${totalPages}`,
|
|
2782
|
+
disabled: true,
|
|
2783
|
+
customId: DISCORD_MODEL_PICKER_PAGE_INDICATOR_CUSTOM_ID
|
|
2784
|
+
});
|
|
2785
|
+
return new Row([
|
|
2786
|
+
createNavigationButton("◀ Prev", Math.max(1, page - 1), hasPrev),
|
|
2787
|
+
indicatorButton,
|
|
2788
|
+
createNavigationButton("Next ▶", Math.min(totalPages, page + 1), hasNext)
|
|
3352
2789
|
]);
|
|
3353
2790
|
}
|
|
3354
2791
|
function buildModelRows(params) {
|
|
@@ -3461,10 +2898,17 @@ function buildModelRows(params) {
|
|
|
3461
2898
|
const resolvedDefault = params.data.resolvedDefault;
|
|
3462
2899
|
const shouldDisableReset = Boolean(parsedCurrentModel) && parsedCurrentModel?.provider === resolvedDefault.provider && parsedCurrentModel?.model === resolvedDefault.model;
|
|
3463
2900
|
const hasPendingSelection = Boolean(parsedPendingModel) && parsedPendingModel?.provider === params.modelPage.provider && typeof params.pendingModelIndex === "number" && params.pendingModelIndex > 0;
|
|
2901
|
+
const modelActionState = {
|
|
2902
|
+
command: params.command,
|
|
2903
|
+
provider: params.modelPage.provider,
|
|
2904
|
+
...compactRuntime,
|
|
2905
|
+
page: params.modelPage.page,
|
|
2906
|
+
providerPage: providerPage.page,
|
|
2907
|
+
userId: params.userId
|
|
2908
|
+
};
|
|
3464
2909
|
const buttonRowItems = [
|
|
3465
2910
|
createModelPickerButton({
|
|
3466
2911
|
label: "Providers",
|
|
3467
|
-
style: ButtonStyle.Secondary,
|
|
3468
2912
|
customId: buildDiscordModelPickerCustomId({
|
|
3469
2913
|
command: params.command,
|
|
3470
2914
|
action: "back",
|
|
@@ -3476,47 +2920,29 @@ function buildModelRows(params) {
|
|
|
3476
2920
|
}),
|
|
3477
2921
|
createModelPickerButton({
|
|
3478
2922
|
label: "Cancel",
|
|
3479
|
-
style: ButtonStyle.Secondary,
|
|
3480
2923
|
customId: buildDiscordModelPickerCustomId({
|
|
3481
|
-
|
|
2924
|
+
...modelActionState,
|
|
3482
2925
|
action: "cancel",
|
|
3483
|
-
view: "models"
|
|
3484
|
-
provider: params.modelPage.provider,
|
|
3485
|
-
...compactRuntime,
|
|
3486
|
-
page: params.modelPage.page,
|
|
3487
|
-
providerPage: providerPage.page,
|
|
3488
|
-
userId: params.userId
|
|
2926
|
+
view: "models"
|
|
3489
2927
|
})
|
|
3490
2928
|
}),
|
|
3491
2929
|
createModelPickerButton({
|
|
3492
2930
|
label: "Reset to default",
|
|
3493
|
-
style: ButtonStyle.Secondary,
|
|
3494
2931
|
disabled: shouldDisableReset,
|
|
3495
2932
|
customId: buildDiscordModelPickerCustomId({
|
|
3496
|
-
|
|
2933
|
+
...modelActionState,
|
|
3497
2934
|
action: "reset",
|
|
3498
|
-
view: "models"
|
|
3499
|
-
provider: params.modelPage.provider,
|
|
3500
|
-
...compactRuntime,
|
|
3501
|
-
page: params.modelPage.page,
|
|
3502
|
-
providerPage: providerPage.page,
|
|
3503
|
-
userId: params.userId
|
|
2935
|
+
view: "models"
|
|
3504
2936
|
})
|
|
3505
2937
|
})
|
|
3506
2938
|
];
|
|
3507
2939
|
if (hasQuickModels) buttonRowItems.push(createModelPickerButton({
|
|
3508
2940
|
label: "Recents",
|
|
3509
|
-
style: ButtonStyle.Secondary,
|
|
3510
2941
|
customId: buildDiscordModelPickerCustomId({
|
|
3511
|
-
|
|
2942
|
+
...modelActionState,
|
|
3512
2943
|
action: "recents",
|
|
3513
2944
|
view: "recents",
|
|
3514
|
-
|
|
3515
|
-
...compactRuntime,
|
|
3516
|
-
page: params.modelPage.page,
|
|
3517
|
-
providerPage: providerPage.page,
|
|
3518
|
-
modelBucket: activeModelBucket,
|
|
3519
|
-
userId: params.userId
|
|
2945
|
+
modelBucket: activeModelBucket
|
|
3520
2946
|
})
|
|
3521
2947
|
}));
|
|
3522
2948
|
buttonRowItems.push(createModelPickerButton({
|
|
@@ -3524,16 +2950,11 @@ function buildModelRows(params) {
|
|
|
3524
2950
|
style: ButtonStyle.Primary,
|
|
3525
2951
|
disabled: !hasPendingSelection,
|
|
3526
2952
|
customId: buildDiscordModelPickerCustomId({
|
|
3527
|
-
|
|
2953
|
+
...modelActionState,
|
|
3528
2954
|
action: "submit",
|
|
3529
2955
|
view: "models",
|
|
3530
|
-
provider: params.modelPage.provider,
|
|
3531
|
-
...compactRuntime,
|
|
3532
|
-
page: params.modelPage.page,
|
|
3533
|
-
providerPage: providerPage.page,
|
|
3534
2956
|
modelIndex: params.pendingModelIndex,
|
|
3535
|
-
modelToken: pendingModelToken
|
|
3536
|
-
userId: params.userId
|
|
2957
|
+
modelToken: pendingModelToken
|
|
3537
2958
|
})
|
|
3538
2959
|
}));
|
|
3539
2960
|
return {
|
|
@@ -3680,32 +3101,14 @@ function createModelRefToken(modelRef) {
|
|
|
3680
3101
|
function renderDiscordModelPickerRecentsView(params) {
|
|
3681
3102
|
const defaultModelRef = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
|
|
3682
3103
|
const rows = [];
|
|
3683
|
-
const
|
|
3684
|
-
rows.push(new Row([createModelPickerButton({
|
|
3685
|
-
label: formatRecentsButtonLabel(
|
|
3686
|
-
style: ButtonStyle.Secondary,
|
|
3687
|
-
customId: buildDiscordModelPickerCustomId({
|
|
3688
|
-
command: params.command,
|
|
3689
|
-
action: "submit",
|
|
3690
|
-
view: "recents",
|
|
3691
|
-
recentSlot: 1,
|
|
3692
|
-
modelToken: createModelRefToken(defaultModelRef),
|
|
3693
|
-
provider: params.provider,
|
|
3694
|
-
runtime: params.runtime,
|
|
3695
|
-
runtimeIndex: params.runtimeIndex,
|
|
3696
|
-
page: params.page,
|
|
3697
|
-
providerPage: params.providerPage,
|
|
3698
|
-
userId: params.userId
|
|
3699
|
-
})
|
|
3700
|
-
})]));
|
|
3701
|
-
for (const [i, modelRef] of dedupedQuickModels.entries()) rows.push(new Row([createModelPickerButton({
|
|
3702
|
-
label: formatRecentsButtonLabel(modelRef),
|
|
3703
|
-
style: ButtonStyle.Secondary,
|
|
3104
|
+
const recentModels = [defaultModelRef, ...params.quickModels.filter((modelRef) => modelRef !== defaultModelRef)];
|
|
3105
|
+
for (const [index, modelRef] of recentModels.entries()) rows.push(new Row([createModelPickerButton({
|
|
3106
|
+
label: formatRecentsButtonLabel(modelRef, index === 0 ? "(default)" : void 0),
|
|
3704
3107
|
customId: buildDiscordModelPickerCustomId({
|
|
3705
3108
|
command: params.command,
|
|
3706
3109
|
action: "submit",
|
|
3707
3110
|
view: "recents",
|
|
3708
|
-
recentSlot:
|
|
3111
|
+
recentSlot: index + 1,
|
|
3709
3112
|
modelToken: createModelRefToken(modelRef),
|
|
3710
3113
|
provider: params.provider,
|
|
3711
3114
|
runtime: params.runtime,
|
|
@@ -3717,7 +3120,6 @@ function renderDiscordModelPickerRecentsView(params) {
|
|
|
3717
3120
|
})]));
|
|
3718
3121
|
const backRow = new Row([createModelPickerButton({
|
|
3719
3122
|
label: "Back",
|
|
3720
|
-
style: ButtonStyle.Secondary,
|
|
3721
3123
|
customId: buildDiscordModelPickerCustomId({
|
|
3722
3124
|
command: params.command,
|
|
3723
3125
|
action: "back",
|
|
@@ -3749,43 +3151,10 @@ function toDiscordModelPickerMessagePayload(view) {
|
|
|
3749
3151
|
}
|
|
3750
3152
|
//#endregion
|
|
3751
3153
|
//#region extensions/discord/src/monitor/native-command-model-picker-apply.ts
|
|
3752
|
-
|
|
3753
|
-
const
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
storePath,
|
|
3757
|
-
sessionKey: params.route.sessionKey,
|
|
3758
|
-
fallbackEntry: {
|
|
3759
|
-
sessionId: randomUUID(),
|
|
3760
|
-
updatedAt: Date.now()
|
|
3761
|
-
},
|
|
3762
|
-
replaceEntry: true,
|
|
3763
|
-
update: (entry) => {
|
|
3764
|
-
persisted = applyModelOverrideToSessionEntry({
|
|
3765
|
-
entry,
|
|
3766
|
-
selection: {
|
|
3767
|
-
provider: params.provider,
|
|
3768
|
-
model: params.model,
|
|
3769
|
-
isDefault: params.isDefault
|
|
3770
|
-
},
|
|
3771
|
-
markLiveSwitchPending: true
|
|
3772
|
-
}).updated || persisted;
|
|
3773
|
-
const runtime = params.runtime?.trim();
|
|
3774
|
-
if (runtime && runtime !== "auto" && runtime !== "default") {
|
|
3775
|
-
if (entry.agentRuntimeOverride !== runtime) {
|
|
3776
|
-
entry.agentRuntimeOverride = runtime;
|
|
3777
|
-
delete entry.agentHarnessId;
|
|
3778
|
-
persisted = true;
|
|
3779
|
-
}
|
|
3780
|
-
} else if (runtime && entry.agentRuntimeOverride) {
|
|
3781
|
-
delete entry.agentRuntimeOverride;
|
|
3782
|
-
delete entry.agentHarnessId;
|
|
3783
|
-
persisted = true;
|
|
3784
|
-
}
|
|
3785
|
-
return entry;
|
|
3786
|
-
}
|
|
3787
|
-
});
|
|
3788
|
-
return persisted;
|
|
3154
|
+
function normalizeExpectedRuntime(value) {
|
|
3155
|
+
const runtime = value?.trim();
|
|
3156
|
+
if (!runtime) return;
|
|
3157
|
+
return runtime === "auto" || runtime === "default" ? "auto" : runtime;
|
|
3789
3158
|
}
|
|
3790
3159
|
async function applyDiscordModelPickerSelection(params) {
|
|
3791
3160
|
try {
|
|
@@ -3800,79 +3169,42 @@ async function applyDiscordModelPickerSelection(params) {
|
|
|
3800
3169
|
sessionPrefix: params.sessionPrefix,
|
|
3801
3170
|
preferFollowUp: true,
|
|
3802
3171
|
threadBindings: params.threadBindings,
|
|
3803
|
-
suppressReplies: true
|
|
3172
|
+
suppressReplies: true,
|
|
3173
|
+
pluginCommandDispatch: { kind: "non-plugin" }
|
|
3804
3174
|
}), 12e3);
|
|
3805
3175
|
if (!dispatchResult.accepted) return {
|
|
3806
3176
|
status: "rejected",
|
|
3807
3177
|
noticeMessage: `❌ Failed to apply ${params.resolvedModelRef}. Try /model ${params.resolvedModelRef} directly.`
|
|
3808
3178
|
};
|
|
3809
|
-
const
|
|
3179
|
+
const hiddenFinalReply = dispatchResult.hiddenFinalReply;
|
|
3180
|
+
const effectiveRoute = dispatchResult.effectiveRoute ?? params.route;
|
|
3810
3181
|
if (params.settleMs > 0) await new Promise((resolve) => {
|
|
3811
3182
|
setTimeout(resolve, params.settleMs);
|
|
3812
3183
|
});
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
});
|
|
3824
|
-
await new Promise((resolve) => {
|
|
3825
|
-
setTimeout(resolve, 100);
|
|
3826
|
-
});
|
|
3827
|
-
effectiveModelRef = params.resolveCurrentModel(fallbackRoute);
|
|
3828
|
-
persisted = effectiveModelRef === params.resolvedModelRef;
|
|
3829
|
-
}
|
|
3830
|
-
if (!persisted) {
|
|
3831
|
-
logVerbose(`discord: model picker override mismatch — expected ${params.resolvedModelRef} but read ${effectiveModelRef} from session key ${fallbackRoute.sessionKey}; attempting direct session override persist`);
|
|
3832
|
-
try {
|
|
3833
|
-
const directlyPersisted = await persistDiscordModelPickerOverride({
|
|
3834
|
-
cfg: params.cfg,
|
|
3835
|
-
route: fallbackRoute,
|
|
3836
|
-
provider: params.selectedProvider,
|
|
3837
|
-
model: params.selectedModel,
|
|
3838
|
-
isDefault: params.selectedProvider === params.defaultProvider && params.selectedModel === params.defaultModel,
|
|
3839
|
-
runtime: params.selectedRuntime
|
|
3840
|
-
});
|
|
3841
|
-
await new Promise((resolve) => {
|
|
3842
|
-
setTimeout(resolve, 100);
|
|
3843
|
-
});
|
|
3844
|
-
effectiveModelRef = params.resolveCurrentModel(fallbackRoute);
|
|
3845
|
-
persisted = effectiveModelRef === params.resolvedModelRef;
|
|
3846
|
-
if (!persisted) logVerbose(`discord: direct session override persist failed — expected ${params.resolvedModelRef} but read ${effectiveModelRef} from session key ${fallbackRoute.sessionKey}`);
|
|
3847
|
-
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}`);
|
|
3848
|
-
} catch (error) {
|
|
3849
|
-
if (error instanceof ModelSelectionLockedError) return {
|
|
3850
|
-
status: "rejected",
|
|
3851
|
-
noticeMessage: `❌ ${error.message}`
|
|
3852
|
-
};
|
|
3853
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3854
|
-
logVerbose(`discord: direct session override persist threw for session key ${fallbackRoute.sessionKey}: ${message}`);
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
if (persisted) await recordDiscordModelPickerRecentModel({
|
|
3184
|
+
const effectiveModelRef = params.resolveCurrentModel(effectiveRoute);
|
|
3185
|
+
const effectiveRuntime = params.resolveCurrentRuntime(effectiveRoute);
|
|
3186
|
+
const currentSelection = `Current selection: ${effectiveModelRef} with runtime ${effectiveRuntime}.`;
|
|
3187
|
+
if (hiddenFinalReply?.isError) return {
|
|
3188
|
+
status: "rejected",
|
|
3189
|
+
noticeMessage: `${hiddenFinalReply.text?.trim()}\n${currentSelection}`
|
|
3190
|
+
};
|
|
3191
|
+
const expectedRuntime = normalizeExpectedRuntime(params.selectedRuntime);
|
|
3192
|
+
const verified = effectiveModelRef === params.resolvedModelRef && (expectedRuntime === void 0 || effectiveRuntime === expectedRuntime);
|
|
3193
|
+
if (verified) await recordDiscordModelPickerRecentModel({
|
|
3858
3194
|
scope: params.preferenceScope,
|
|
3859
3195
|
modelRef: params.resolvedModelRef,
|
|
3860
3196
|
limit: 5
|
|
3861
3197
|
}).catch(() => void 0);
|
|
3862
|
-
return
|
|
3198
|
+
return verified ? {
|
|
3863
3199
|
status: "success",
|
|
3864
3200
|
effectiveModelRef,
|
|
3865
|
-
noticeMessage: `✅ Model set to ${params.resolvedModelRef}.`
|
|
3201
|
+
noticeMessage: hiddenFinalReply?.text?.trim() || `✅ Model set to ${params.resolvedModelRef}.`
|
|
3866
3202
|
} : {
|
|
3867
3203
|
status: "mismatch",
|
|
3868
3204
|
effectiveModelRef,
|
|
3869
|
-
noticeMessage: `⚠️ Tried to set ${params.resolvedModelRef}, but current
|
|
3205
|
+
noticeMessage: `⚠️ Tried to set ${params.resolvedModelRef}${expectedRuntime ? ` with runtime ${expectedRuntime}` : ""}, but current selection is ${effectiveModelRef} with runtime ${effectiveRuntime}.`
|
|
3870
3206
|
};
|
|
3871
3207
|
} catch (error) {
|
|
3872
|
-
if (error instanceof ModelSelectionLockedError) return {
|
|
3873
|
-
status: "rejected",
|
|
3874
|
-
noticeMessage: `❌ ${error.message}`
|
|
3875
|
-
};
|
|
3876
3208
|
if (error instanceof Error && error.message === "timeout") return {
|
|
3877
3209
|
status: "timeout",
|
|
3878
3210
|
noticeMessage: `⏳ Model change to ${params.resolvedModelRef} is still processing. Check /status in a few seconds.`
|
|
@@ -3902,9 +3234,6 @@ function shouldOpenDiscordModelPickerFromCommand(params) {
|
|
|
3902
3234
|
if (context === "model") return !resolveCommandArgStringValue(params.commandArgs, "model") && !serializedArgs ? context : null;
|
|
3903
3235
|
return serializedArgs ? null : context;
|
|
3904
3236
|
}
|
|
3905
|
-
function buildDiscordModelPickerCurrentModel(defaultProvider, defaultModel) {
|
|
3906
|
-
return `${defaultProvider}/${defaultModel}`;
|
|
3907
|
-
}
|
|
3908
3237
|
function buildDiscordModelPickerAllowedModelRefs(data) {
|
|
3909
3238
|
const out = /* @__PURE__ */ new Set();
|
|
3910
3239
|
for (const provider of data.providers) {
|
|
@@ -4000,7 +3329,7 @@ async function resolveDiscordNativeChoiceContext(params) {
|
|
|
4000
3329
|
}
|
|
4001
3330
|
}
|
|
4002
3331
|
function resolveDiscordModelPickerCurrentModel(params) {
|
|
4003
|
-
const fallback =
|
|
3332
|
+
const fallback = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
|
|
4004
3333
|
try {
|
|
4005
3334
|
const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.route.agentId });
|
|
4006
3335
|
const override = resolveStoredModelOverride({
|
|
@@ -4175,7 +3504,7 @@ function buildDiscordModelPickerSelectionCommand(params) {
|
|
|
4175
3504
|
if (!commandDefinition) return null;
|
|
4176
3505
|
const commandArgs = {
|
|
4177
3506
|
values: { model: params.modelRef },
|
|
4178
|
-
raw: params.modelRef
|
|
3507
|
+
raw: params.runtime ? `${params.modelRef} --runtime ${params.runtime}` : params.modelRef
|
|
4179
3508
|
};
|
|
4180
3509
|
return {
|
|
4181
3510
|
command: commandDefinition,
|
|
@@ -4186,7 +3515,7 @@ function buildDiscordModelPickerSelectionCommand(params) {
|
|
|
4186
3515
|
function listDiscordModelPickerProviderModels(data, provider) {
|
|
4187
3516
|
const modelSet = data.byProvider.get(provider);
|
|
4188
3517
|
if (!modelSet) return [];
|
|
4189
|
-
return [...modelSet].toSorted();
|
|
3518
|
+
return [...modelSet].toSorted((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
4190
3519
|
}
|
|
4191
3520
|
function resolveDiscordModelPickerModelRefByToken(data, modelToken) {
|
|
4192
3521
|
const matchingRefs = [];
|
|
@@ -4224,10 +3553,6 @@ function resolveDiscordModelPickerSubmissionRuntime(params) {
|
|
|
4224
3553
|
provider: params.provider,
|
|
4225
3554
|
runtime: params.parsedRuntime,
|
|
4226
3555
|
allowResetRuntime: true
|
|
4227
|
-
}) ?? resolveDiscordModelPickerRuntimeForProvider({
|
|
4228
|
-
data: params.data,
|
|
4229
|
-
provider: params.provider,
|
|
4230
|
-
runtime: params.currentRuntime
|
|
4231
3556
|
});
|
|
4232
3557
|
}
|
|
4233
3558
|
async function handleDiscordModelPickerInteraction(params) {
|
|
@@ -4277,6 +3602,22 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4277
3602
|
});
|
|
4278
3603
|
const updatePicker = async (payload) => await params.safeInteractionCall("model picker update", () => deferredUpdate ? interaction.editReply(payload) : interaction.update(payload));
|
|
4279
3604
|
const showNotice = async (message) => await updatePicker(buildDiscordModelPickerNoticePayload(message));
|
|
3605
|
+
const updateModelsView = async (provider, state = {}) => {
|
|
3606
|
+
const rendered = renderDiscordModelPickerModelsView({
|
|
3607
|
+
command: parsed.command,
|
|
3608
|
+
userId: parsed.userId,
|
|
3609
|
+
data: pickerData,
|
|
3610
|
+
provider,
|
|
3611
|
+
page: parsed.page,
|
|
3612
|
+
providerPage: parsed.providerPage ?? 1,
|
|
3613
|
+
providerBucket: parsed.providerBucket ?? findProviderBucketId(pickerData, provider),
|
|
3614
|
+
currentModel: currentModelRef,
|
|
3615
|
+
currentRuntime,
|
|
3616
|
+
quickModels,
|
|
3617
|
+
...state
|
|
3618
|
+
});
|
|
3619
|
+
return await updatePicker(toDiscordModelPickerMessagePayload(rendered));
|
|
3620
|
+
};
|
|
4280
3621
|
if (parsed.action === "recents") {
|
|
4281
3622
|
await updatePicker(toDiscordModelPickerMessagePayload(renderDiscordModelPickerRecentsView({
|
|
4282
3623
|
command: parsed.command,
|
|
@@ -4293,35 +3634,14 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4293
3634
|
})));
|
|
4294
3635
|
return;
|
|
4295
3636
|
}
|
|
4296
|
-
if (parsed.action === "back"
|
|
4297
|
-
|
|
4298
|
-
command: parsed.command,
|
|
4299
|
-
userId: parsed.userId,
|
|
4300
|
-
data: pickerData,
|
|
4301
|
-
page: parsed.page,
|
|
4302
|
-
providerBucket: parsed.providerBucket,
|
|
4303
|
-
currentModel: currentModelRef
|
|
4304
|
-
})));
|
|
4305
|
-
return;
|
|
4306
|
-
}
|
|
4307
|
-
if (parsed.action === "nav" && parsed.view === "providers") {
|
|
4308
|
-
await updatePicker(toDiscordModelPickerMessagePayload(renderDiscordModelPickerProvidersView({
|
|
4309
|
-
command: parsed.command,
|
|
4310
|
-
userId: parsed.userId,
|
|
4311
|
-
data: pickerData,
|
|
4312
|
-
page: parsed.page,
|
|
4313
|
-
providerBucket: parsed.providerBucket,
|
|
4314
|
-
currentModel: currentModelRef
|
|
4315
|
-
})));
|
|
4316
|
-
return;
|
|
4317
|
-
}
|
|
4318
|
-
if (parsed.action === "bucket" && parsed.view === "providers") {
|
|
3637
|
+
if (parsed.view === "providers" && (parsed.action === "back" || parsed.action === "nav" || parsed.action === "bucket")) {
|
|
3638
|
+
const selectingBucket = parsed.action === "bucket";
|
|
4319
3639
|
await updatePicker(toDiscordModelPickerMessagePayload(renderDiscordModelPickerProvidersView({
|
|
4320
3640
|
command: parsed.command,
|
|
4321
3641
|
userId: parsed.userId,
|
|
4322
3642
|
data: pickerData,
|
|
4323
|
-
page: 1,
|
|
4324
|
-
providerBucket: resolveSelectedBucket(interaction),
|
|
3643
|
+
page: selectingBucket ? 1 : parsed.page,
|
|
3644
|
+
providerBucket: selectingBucket ? resolveSelectedBucket(interaction) : parsed.providerBucket,
|
|
4325
3645
|
currentModel: currentModelRef
|
|
4326
3646
|
})));
|
|
4327
3647
|
return;
|
|
@@ -4332,24 +3652,15 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4332
3652
|
currentModelRef,
|
|
4333
3653
|
data: pickerData
|
|
4334
3654
|
});
|
|
4335
|
-
await
|
|
4336
|
-
command: parsed.command,
|
|
4337
|
-
userId: parsed.userId,
|
|
4338
|
-
data: pickerData,
|
|
4339
|
-
provider,
|
|
3655
|
+
await updateModelsView(provider, {
|
|
4340
3656
|
page: 1,
|
|
4341
|
-
providerPage: parsed.providerPage ?? 1,
|
|
4342
|
-
providerBucket: parsed.providerBucket ?? findProviderBucketId(pickerData, provider),
|
|
4343
3657
|
modelBucket: resolveSelectedBucket(interaction),
|
|
4344
|
-
currentModel: currentModelRef,
|
|
4345
|
-
currentRuntime,
|
|
4346
3658
|
pendingRuntime: resolvePendingRuntime({
|
|
4347
3659
|
data: pickerData,
|
|
4348
3660
|
provider,
|
|
4349
3661
|
parsed
|
|
4350
|
-
})
|
|
4351
|
-
|
|
4352
|
-
})));
|
|
3662
|
+
})
|
|
3663
|
+
});
|
|
4353
3664
|
return;
|
|
4354
3665
|
}
|
|
4355
3666
|
if (parsed.action === "nav" && parsed.view === "models") {
|
|
@@ -4374,26 +3685,16 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4374
3685
|
provider,
|
|
4375
3686
|
model: pendingModel
|
|
4376
3687
|
}) : void 0;
|
|
4377
|
-
await
|
|
4378
|
-
command: parsed.command,
|
|
4379
|
-
userId: parsed.userId,
|
|
4380
|
-
data: pickerData,
|
|
4381
|
-
provider,
|
|
4382
|
-
page: parsed.page,
|
|
4383
|
-
providerPage: parsed.providerPage ?? 1,
|
|
4384
|
-
providerBucket: parsed.providerBucket ?? findProviderBucketId(pickerData, provider),
|
|
3688
|
+
await updateModelsView(provider, {
|
|
4385
3689
|
modelBucket: parsed.modelBucket,
|
|
4386
|
-
currentModel: currentModelRef,
|
|
4387
|
-
currentRuntime,
|
|
4388
3690
|
...pendingModel ? { pendingModel: `${provider}/${pendingModel}` } : {},
|
|
4389
3691
|
pendingModelIndex: pendingModelIndex ?? void 0,
|
|
4390
3692
|
pendingRuntime: resolvePendingRuntime({
|
|
4391
3693
|
data: pickerData,
|
|
4392
3694
|
provider,
|
|
4393
3695
|
parsed
|
|
4394
|
-
})
|
|
4395
|
-
|
|
4396
|
-
})));
|
|
3696
|
+
})
|
|
3697
|
+
});
|
|
4397
3698
|
return;
|
|
4398
3699
|
}
|
|
4399
3700
|
if (parsed.action === "back" && parsed.view === "models") {
|
|
@@ -4402,24 +3703,14 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4402
3703
|
currentModelRef,
|
|
4403
3704
|
data: pickerData
|
|
4404
3705
|
});
|
|
4405
|
-
await
|
|
4406
|
-
command: parsed.command,
|
|
4407
|
-
userId: parsed.userId,
|
|
4408
|
-
data: pickerData,
|
|
4409
|
-
provider,
|
|
4410
|
-
page: parsed.page ?? 1,
|
|
4411
|
-
providerPage: parsed.providerPage ?? 1,
|
|
4412
|
-
providerBucket: parsed.providerBucket ?? findProviderBucketId(pickerData, provider),
|
|
3706
|
+
await updateModelsView(provider, {
|
|
4413
3707
|
modelBucket: parsed.modelBucket,
|
|
4414
|
-
currentModel: currentModelRef,
|
|
4415
|
-
currentRuntime,
|
|
4416
3708
|
pendingRuntime: resolvePendingRuntime({
|
|
4417
3709
|
data: pickerData,
|
|
4418
3710
|
provider,
|
|
4419
3711
|
parsed
|
|
4420
|
-
})
|
|
4421
|
-
|
|
4422
|
-
})));
|
|
3712
|
+
})
|
|
3713
|
+
});
|
|
4423
3714
|
return;
|
|
4424
3715
|
}
|
|
4425
3716
|
if (parsed.action === "provider") {
|
|
@@ -4428,18 +3719,10 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4428
3719
|
await showNotice("Sorry, that provider isn't available anymore.");
|
|
4429
3720
|
return;
|
|
4430
3721
|
}
|
|
4431
|
-
await
|
|
4432
|
-
command: parsed.command,
|
|
4433
|
-
userId: parsed.userId,
|
|
4434
|
-
data: pickerData,
|
|
4435
|
-
provider: selectedProvider,
|
|
3722
|
+
await updateModelsView(selectedProvider, {
|
|
4436
3723
|
page: 1,
|
|
4437
|
-
providerPage: parsed.providerPage ?? parsed.page
|
|
4438
|
-
|
|
4439
|
-
currentModel: currentModelRef,
|
|
4440
|
-
currentRuntime,
|
|
4441
|
-
quickModels
|
|
4442
|
-
})));
|
|
3724
|
+
providerPage: parsed.providerPage ?? parsed.page
|
|
3725
|
+
});
|
|
4443
3726
|
return;
|
|
4444
3727
|
}
|
|
4445
3728
|
if (parsed.action === "model") {
|
|
@@ -4459,28 +3742,16 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4459
3742
|
return;
|
|
4460
3743
|
}
|
|
4461
3744
|
const modelRef = `${provider}/${selectedModel}`;
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
await updatePicker(toDiscordModelPickerMessagePayload(renderDiscordModelPickerModelsView({
|
|
4465
|
-
command: parsed.command,
|
|
4466
|
-
userId: parsed.userId,
|
|
4467
|
-
data: pickerData,
|
|
4468
|
-
provider,
|
|
4469
|
-
page: parsed.page,
|
|
4470
|
-
providerPage: parsed.providerPage ?? 1,
|
|
4471
|
-
providerBucket: derivedProviderBucket,
|
|
4472
|
-
modelBucket: derivedModelBucket,
|
|
4473
|
-
currentModel: currentModelRef,
|
|
4474
|
-
currentRuntime,
|
|
3745
|
+
await updateModelsView(provider, {
|
|
3746
|
+
modelBucket: parsed.modelBucket ?? findModelBucketId(pickerData, provider, selectedModel),
|
|
4475
3747
|
pendingModel: modelRef,
|
|
4476
3748
|
pendingModelIndex: modelIndex,
|
|
4477
3749
|
pendingRuntime: resolvePendingRuntime({
|
|
4478
3750
|
data: pickerData,
|
|
4479
3751
|
provider,
|
|
4480
3752
|
parsed
|
|
4481
|
-
})
|
|
4482
|
-
|
|
4483
|
-
})));
|
|
3753
|
+
})
|
|
3754
|
+
});
|
|
4484
3755
|
return;
|
|
4485
3756
|
}
|
|
4486
3757
|
if (parsed.action === "runtime") {
|
|
@@ -4507,25 +3778,13 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4507
3778
|
provider,
|
|
4508
3779
|
model: selectedModel
|
|
4509
3780
|
}) : void 0;
|
|
4510
|
-
const derivedProviderBucket = parsed.providerBucket ?? findProviderBucketId(pickerData, provider);
|
|
4511
3781
|
const currentModelOnly = splitDiscordModelRef(currentModelRef ?? "");
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
command: parsed.command,
|
|
4515
|
-
userId: parsed.userId,
|
|
4516
|
-
data: pickerData,
|
|
4517
|
-
provider,
|
|
4518
|
-
page: parsed.page,
|
|
4519
|
-
providerPage: parsed.providerPage ?? 1,
|
|
4520
|
-
providerBucket: derivedProviderBucket,
|
|
4521
|
-
modelBucket: derivedModelBucket,
|
|
4522
|
-
currentModel: currentModelRef,
|
|
4523
|
-
currentRuntime,
|
|
3782
|
+
await updateModelsView(provider, {
|
|
3783
|
+
modelBucket: parsed.modelBucket ?? (selectedModel ? findModelBucketId(pickerData, provider, selectedModel) : currentModelOnly && currentModelOnly.provider === provider ? findModelBucketId(pickerData, provider, currentModelOnly.model) : void 0),
|
|
4524
3784
|
...pendingModel ? { pendingModel } : {},
|
|
4525
3785
|
pendingModelIndex: pendingModelIndex ?? void 0,
|
|
4526
|
-
pendingRuntime: selectedRuntime
|
|
4527
|
-
|
|
4528
|
-
})));
|
|
3786
|
+
pendingRuntime: selectedRuntime
|
|
3787
|
+
});
|
|
4529
3788
|
return;
|
|
4530
3789
|
}
|
|
4531
3790
|
if (parsed.action === "submit" || parsed.action === "reset" || parsed.action === "quick") {
|
|
@@ -4548,10 +3807,12 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4548
3807
|
data: pickerData,
|
|
4549
3808
|
parsed,
|
|
4550
3809
|
selectedProvider: parsedModelRef.provider
|
|
4551
|
-
})
|
|
4552
|
-
|
|
3810
|
+
})
|
|
3811
|
+
});
|
|
3812
|
+
const selectionCommand = buildDiscordModelPickerSelectionCommand({
|
|
3813
|
+
modelRef: resolvedModelRef,
|
|
3814
|
+
runtime: selectedRuntime
|
|
4553
3815
|
});
|
|
4554
|
-
const selectionCommand = buildDiscordModelPickerSelectionCommand({ modelRef: resolvedModelRef });
|
|
4555
3816
|
if (!selectionCommand) {
|
|
4556
3817
|
await showNotice("Sorry, /model is unavailable right now.");
|
|
4557
3818
|
return;
|
|
@@ -4568,17 +3829,17 @@ async function handleDiscordModelPickerInteraction(params) {
|
|
|
4568
3829
|
threadBindings: ctx.threadBindings,
|
|
4569
3830
|
route,
|
|
4570
3831
|
resolvedModelRef,
|
|
4571
|
-
selectedProvider: parsedModelRef.provider,
|
|
4572
|
-
selectedModel: parsedModelRef.model,
|
|
4573
3832
|
selectedRuntime,
|
|
4574
|
-
defaultProvider: pickerData.resolvedDefault.provider,
|
|
4575
|
-
defaultModel: pickerData.resolvedDefault.model,
|
|
4576
3833
|
preferenceScope,
|
|
4577
3834
|
settleMs: ctx.postApplySettleMs ?? 250,
|
|
4578
3835
|
resolveCurrentModel: (currentRoute) => resolveDiscordModelPickerCurrentModel({
|
|
4579
3836
|
cfg,
|
|
4580
3837
|
route: currentRoute,
|
|
4581
3838
|
data: pickerData
|
|
3839
|
+
}),
|
|
3840
|
+
resolveCurrentRuntime: (currentRoute) => resolveDiscordModelPickerCurrentRuntime({
|
|
3841
|
+
cfg,
|
|
3842
|
+
route: currentRoute
|
|
4582
3843
|
})
|
|
4583
3844
|
});
|
|
4584
3845
|
await params.safeInteractionCall("model picker follow-up", () => interaction.followUp({
|
|
@@ -4661,7 +3922,7 @@ const DISCORD_COMMAND_DESCRIPTION_MAX = 100;
|
|
|
4661
3922
|
function truncateDiscordCommandDescription(params) {
|
|
4662
3923
|
const { value, label } = params;
|
|
4663
3924
|
if (value.length <= DISCORD_COMMAND_DESCRIPTION_MAX) return value;
|
|
4664
|
-
log$1.
|
|
3925
|
+
log$1.debug(`discord: truncating native command description (${label}) from ${value.length} to ${DISCORD_COMMAND_DESCRIPTION_MAX}: ${JSON.stringify(value)}`);
|
|
4665
3926
|
return truncateUtf16Safe(value, DISCORD_COMMAND_DESCRIPTION_MAX);
|
|
4666
3927
|
}
|
|
4667
3928
|
function truncateDiscordCommandDescriptionLocalizations(params) {
|
|
@@ -4756,12 +4017,13 @@ function buildDiscordCommandOptions(params) {
|
|
|
4756
4017
|
//#endregion
|
|
4757
4018
|
//#region extensions/discord/src/monitor/native-command.ts
|
|
4758
4019
|
const log = createSubsystemLogger("discord/native-command");
|
|
4020
|
+
const NON_PLUGIN_COMMAND_DISPATCH = Object.freeze({ kind: "non-plugin" });
|
|
4759
4021
|
function createDiscordNativeCommand(params) {
|
|
4760
4022
|
const { command, cfg, discordConfig, accountId, sessionPrefix, ephemeralDefault, threadBindings } = params;
|
|
4761
4023
|
const fallbackCommandDefinition = createNativeCommandDefinition(command);
|
|
4762
|
-
const
|
|
4763
|
-
const commandDefinition =
|
|
4764
|
-
const argDefinitions = commandDefinition.args ?? command.args;
|
|
4024
|
+
const pluginCommandCandidate = "prepareDispatch" in command ? command : void 0;
|
|
4025
|
+
const commandDefinition = pluginCommandCandidate ? fallbackCommandDefinition : findCommandByNativeName$1(command.name, "discord", { includeBundledChannelFallback: false }) ?? fallbackCommandDefinition;
|
|
4026
|
+
const argDefinitions = commandDefinition.args ?? ("args" in command ? command.args : void 0);
|
|
4765
4027
|
const resolveCurrentConfig = () => getRuntimeConfigSnapshot() ?? cfg;
|
|
4766
4028
|
const commandOptions = buildDiscordCommandOptions({
|
|
4767
4029
|
command: commandDefinition,
|
|
@@ -4772,7 +4034,7 @@ function createDiscordNativeCommand(params) {
|
|
|
4772
4034
|
cfg: resolveCurrentConfig(),
|
|
4773
4035
|
discordConfig,
|
|
4774
4036
|
accountId,
|
|
4775
|
-
skipCommandOwnerAllowFrom:
|
|
4037
|
+
skipCommandOwnerAllowFrom: pluginCommandCandidate !== void 0
|
|
4776
4038
|
}),
|
|
4777
4039
|
resolveChoiceContext: async (interaction) => resolveDiscordNativeChoiceContext({
|
|
4778
4040
|
interaction,
|
|
@@ -4810,9 +4072,11 @@ function createDiscordNativeCommand(params) {
|
|
|
4810
4072
|
...commandArgs,
|
|
4811
4073
|
raw: serializeCommandArgs$1(commandDefinition, commandArgs) ?? commandArgs.raw
|
|
4812
4074
|
} : void 0;
|
|
4075
|
+
const prompt = buildCommandTextFromArgs$1(commandDefinition, commandArgsWithRaw);
|
|
4076
|
+
const preparedPluginCommand = pluginCommandCandidate?.prepareDispatch(commandArgsWithRaw?.raw);
|
|
4813
4077
|
await dispatchDiscordCommandInteraction({
|
|
4814
4078
|
interaction,
|
|
4815
|
-
prompt
|
|
4079
|
+
prompt,
|
|
4816
4080
|
command: commandDefinition,
|
|
4817
4081
|
commandArgs: commandArgsWithRaw,
|
|
4818
4082
|
cfg,
|
|
@@ -4821,7 +4085,8 @@ function createDiscordNativeCommand(params) {
|
|
|
4821
4085
|
sessionPrefix,
|
|
4822
4086
|
preferFollowUp: true,
|
|
4823
4087
|
threadBindings,
|
|
4824
|
-
responseEphemeral: ephemeralDefault
|
|
4088
|
+
responseEphemeral: ephemeralDefault,
|
|
4089
|
+
pluginCommandDispatch: preparedPluginCommand ?? NON_PLUGIN_COMMAND_DISPATCH
|
|
4825
4090
|
});
|
|
4826
4091
|
}
|
|
4827
4092
|
}();
|
|
@@ -4884,7 +4149,8 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
4884
4149
|
allowNameMatching
|
|
4885
4150
|
});
|
|
4886
4151
|
const commandOwnerAllowAll = commandOwnerAllowFrom?.includes("*") === true;
|
|
4887
|
-
const senderIsCommandOwner = commandOwnerOk
|
|
4152
|
+
const senderIsCommandOwner = commandOwnerOk;
|
|
4153
|
+
const commandOwnerAccessAllowed = senderIsCommandOwner || commandOwnerAllowAll;
|
|
4888
4154
|
const ownerAllowListConfigured = discordOwnerAllowList != null;
|
|
4889
4155
|
const ownerOk = discordOwnerOk;
|
|
4890
4156
|
const { commandsAllowFromAccess, guildInfo, channelConfig } = resolveDiscordNativeCommandChannelAccessContext({
|
|
@@ -5024,8 +4290,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
5024
4290
|
return { accepted: false };
|
|
5025
4291
|
}
|
|
5026
4292
|
}
|
|
5027
|
-
|
|
5028
|
-
if (commandOwnerAllowFrom && !senderIsCommandOwner && !commandsAllowFromAccess.allowed && commandName !== "status" && !pluginMatch) {
|
|
4293
|
+
if (commandOwnerAllowFrom && !commandOwnerAccessAllowed && !commandsAllowFromAccess.allowed && commandName !== "status" && params.pluginCommandDispatch.kind !== "plugin") {
|
|
5029
4294
|
await respond("You are not authorized to use this command.", { ephemeral: true });
|
|
5030
4295
|
return { accepted: false };
|
|
5031
4296
|
}
|
|
@@ -5085,7 +4350,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
5085
4350
|
}));
|
|
5086
4351
|
return { accepted: true };
|
|
5087
4352
|
}
|
|
5088
|
-
if (
|
|
4353
|
+
if (params.pluginCommandDispatch.kind === "plugin" && commandName !== "status") {
|
|
5089
4354
|
if (suppressReplies) {
|
|
5090
4355
|
await settleDiscordInteractionWithoutVisibleReply(interaction);
|
|
5091
4356
|
return { accepted: true };
|
|
@@ -5099,9 +4364,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
5099
4364
|
agentId: pluginCommandAgentId,
|
|
5100
4365
|
sessionKey: effectiveRoute.sessionKey
|
|
5101
4366
|
});
|
|
5102
|
-
const pluginReply = await
|
|
5103
|
-
command: pluginMatch.command,
|
|
5104
|
-
args: pluginMatch.args,
|
|
4367
|
+
const pluginReply = await params.pluginCommandDispatch.execute({
|
|
5105
4368
|
senderId: sender.id,
|
|
5106
4369
|
channel: "discord",
|
|
5107
4370
|
channelId,
|
|
@@ -5242,7 +4505,7 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
5242
4505
|
respond
|
|
5243
4506
|
});
|
|
5244
4507
|
if (directStatusResult) return directStatusResult;
|
|
5245
|
-
await dispatchDiscordNativeAgentReply({
|
|
4508
|
+
const { dispatched, hiddenFinalReply } = await dispatchDiscordNativeAgentReply({
|
|
5246
4509
|
cfg,
|
|
5247
4510
|
discordConfig,
|
|
5248
4511
|
accountId,
|
|
@@ -5254,11 +4517,13 @@ async function dispatchDiscordCommandInteraction(params) {
|
|
|
5254
4517
|
preferFollowUp,
|
|
5255
4518
|
responseEphemeral,
|
|
5256
4519
|
suppressReplies,
|
|
5257
|
-
log
|
|
4520
|
+
log,
|
|
4521
|
+
pluginCommandDispatch: params.pluginCommandDispatch
|
|
5258
4522
|
});
|
|
5259
4523
|
return {
|
|
5260
|
-
accepted:
|
|
5261
|
-
effectiveRoute
|
|
4524
|
+
accepted: dispatched,
|
|
4525
|
+
effectiveRoute,
|
|
4526
|
+
hiddenFinalReply
|
|
5262
4527
|
};
|
|
5263
4528
|
}
|
|
5264
4529
|
function createDiscordCommandArgFallbackButton(params) {
|
|
@@ -5516,7 +4781,7 @@ var DiscordGatewayVoiceStateCache = class {
|
|
|
5516
4781
|
}
|
|
5517
4782
|
listVoiceChannelStates(guildId, channelId) {
|
|
5518
4783
|
const states = this.statesByGuild.get(guildId);
|
|
5519
|
-
if (!states) return
|
|
4784
|
+
if (!states) return null;
|
|
5520
4785
|
const result = [];
|
|
5521
4786
|
for (const state of states.values()) if (state.channel_id === channelId) result.push({ ...state });
|
|
5522
4787
|
return result;
|
|
@@ -5729,7 +4994,10 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5729
4994
|
if (payload.s !== null && payload.s !== void 0) this.sequence = payload.s;
|
|
5730
4995
|
switch (payload.op) {
|
|
5731
4996
|
case GatewayOpcodes.Hello: {
|
|
5732
|
-
this.startHeartbeat(payload.d
|
|
4997
|
+
this.startHeartbeat(asSafeIntegerInRange(asOptionalRecord(payload.d)?.heartbeat_interval, {
|
|
4998
|
+
min: 1,
|
|
4999
|
+
max: MAX_TIMER_TIMEOUT_MS
|
|
5000
|
+
}) ?? 45e3);
|
|
5733
5001
|
const resumeState = resume ? this.getResumeState() : null;
|
|
5734
5002
|
if (resumeState) this.send({
|
|
5735
5003
|
op: GatewayOpcodes.Resume,
|
|
@@ -5932,12 +5200,6 @@ var GatewayPlugin = class extends Plugin {
|
|
|
5932
5200
|
}
|
|
5933
5201
|
};
|
|
5934
5202
|
//#endregion
|
|
5935
|
-
//#region extensions/discord/src/voice/config.ts
|
|
5936
|
-
function resolveDiscordVoiceEnabled(voice) {
|
|
5937
|
-
if (voice?.enabled !== void 0) return voice.enabled;
|
|
5938
|
-
return voice !== void 0;
|
|
5939
|
-
}
|
|
5940
|
-
//#endregion
|
|
5941
5203
|
//#region extensions/discord/src/monitor/presence.ts
|
|
5942
5204
|
const DEFAULT_CUSTOM_ACTIVITY_TYPE$1 = 4;
|
|
5943
5205
|
const CUSTOM_STATUS_NAME$1 = "Custom Status";
|
|
@@ -6429,8 +5691,7 @@ function readStringProperty(value, key) {
|
|
|
6429
5691
|
return typeof property === "string" && property ? property : void 0;
|
|
6430
5692
|
}
|
|
6431
5693
|
function readNumberProperty(value, key) {
|
|
6432
|
-
|
|
6433
|
-
return typeof property === "number" && Number.isFinite(property) ? property : void 0;
|
|
5694
|
+
return asFiniteNumber(value[key]);
|
|
6434
5695
|
}
|
|
6435
5696
|
function describeDiscordGatewayTransportError(error) {
|
|
6436
5697
|
const code = readStringProperty(error, "code");
|
|
@@ -6480,7 +5741,8 @@ function resolveDiscordGatewayIntents(params) {
|
|
|
6480
5741
|
const intentsConfig = params?.intentsConfig;
|
|
6481
5742
|
const voiceEnabled = params?.voiceEnabled;
|
|
6482
5743
|
const voiceStatesEnabled = intentsConfig?.voiceStates ?? voiceEnabled ?? false;
|
|
6483
|
-
let intents = GatewayIntents.Guilds | GatewayIntents.GuildMessages | GatewayIntents.
|
|
5744
|
+
let intents = GatewayIntents.Guilds | GatewayIntents.GuildMessages | GatewayIntents.DirectMessages | GatewayIntents.GuildMessageReactions | GatewayIntents.DirectMessageReactions;
|
|
5745
|
+
if (intentsConfig?.messageContent !== false) intents |= GatewayIntents.MessageContent;
|
|
6484
5746
|
if (voiceStatesEnabled) intents |= GatewayIntents.GuildVoiceStates;
|
|
6485
5747
|
if (intentsConfig?.presence) intents |= GatewayIntents.GuildPresences;
|
|
6486
5748
|
if (intentsConfig?.guildMembers) intents |= GatewayIntents.GuildMembers;
|
|
@@ -6860,7 +6122,7 @@ async function waitForDiscordGatewayStop(params) {
|
|
|
6860
6122
|
gateway?.disconnect?.();
|
|
6861
6123
|
} finally {
|
|
6862
6124
|
cleanup();
|
|
6863
|
-
reject(
|
|
6125
|
+
reject(toErrorObject(err, "Non-Error rejection"));
|
|
6864
6126
|
}
|
|
6865
6127
|
};
|
|
6866
6128
|
const onAbort = () => {
|
|
@@ -6881,12 +6143,15 @@ async function waitForDiscordGatewayStop(params) {
|
|
|
6881
6143
|
params.registerForceStop?.(onForceStop);
|
|
6882
6144
|
});
|
|
6883
6145
|
}
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6146
|
+
//#endregion
|
|
6147
|
+
//#region extensions/discord/src/monitor/status.ts
|
|
6148
|
+
/** READY proves a prior terminal failure was repaired, so the account is restartable again. */
|
|
6149
|
+
function createDiscordReadyStatusPatch(at = Date.now()) {
|
|
6150
|
+
return channelReadyPatch({
|
|
6151
|
+
lastConnectedAt: at,
|
|
6152
|
+
lastEventAt: at,
|
|
6153
|
+
lastDisconnect: null
|
|
6154
|
+
});
|
|
6890
6155
|
}
|
|
6891
6156
|
//#endregion
|
|
6892
6157
|
//#region extensions/discord/src/monitor/provider.lifecycle.ts
|
|
@@ -7018,11 +6283,7 @@ function createGatewayStatusObserver(params) {
|
|
|
7018
6283
|
queuedForceStopError = err;
|
|
7019
6284
|
};
|
|
7020
6285
|
const pushConnectedStatus = (at) => {
|
|
7021
|
-
params.pushStatus(
|
|
7022
|
-
...createConnectedChannelStatusPatch(at),
|
|
7023
|
-
lastDisconnect: null,
|
|
7024
|
-
lastError: null
|
|
7025
|
-
});
|
|
6286
|
+
params.pushStatus(createDiscordReadyStatusPatch(at));
|
|
7026
6287
|
};
|
|
7027
6288
|
const startReadyWatch = () => {
|
|
7028
6289
|
clearReadyWatch();
|
|
@@ -7046,6 +6307,7 @@ function createGatewayStatusObserver(params) {
|
|
|
7046
6307
|
const error = /* @__PURE__ */ new Error(`discord gateway opened but did not reach READY within ${params.runtimeReadyTimeoutMs}ms`);
|
|
7047
6308
|
params.pushStatus({
|
|
7048
6309
|
connected: false,
|
|
6310
|
+
lifecycle: "recovering",
|
|
7049
6311
|
lastEventAt: at,
|
|
7050
6312
|
lastDisconnect: {
|
|
7051
6313
|
at,
|
|
@@ -7074,8 +6336,14 @@ function createGatewayStatusObserver(params) {
|
|
|
7074
6336
|
if (message.includes("Gateway websocket closed")) {
|
|
7075
6337
|
clearReadyWatch();
|
|
7076
6338
|
const code = parseGatewayCloseCode(message);
|
|
6339
|
+
const terminalDisconnect = code !== void 0 && isFatalGatewayCloseCode(code);
|
|
7077
6340
|
params.pushStatus({
|
|
7078
6341
|
connected: false,
|
|
6342
|
+
lifecycle: terminalDisconnect ? "blocked" : "recovering",
|
|
6343
|
+
...terminalDisconnect ? {
|
|
6344
|
+
terminalDisconnect: true,
|
|
6345
|
+
lastError: message
|
|
6346
|
+
} : {},
|
|
7079
6347
|
lastEventAt: at,
|
|
7080
6348
|
lastDisconnect: {
|
|
7081
6349
|
at,
|
|
@@ -7088,6 +6356,7 @@ function createGatewayStatusObserver(params) {
|
|
|
7088
6356
|
clearReadyWatch();
|
|
7089
6357
|
params.pushStatus({
|
|
7090
6358
|
connected: false,
|
|
6359
|
+
lifecycle: "recovering",
|
|
7091
6360
|
lastEventAt: at,
|
|
7092
6361
|
lastError: message
|
|
7093
6362
|
});
|
|
@@ -7118,11 +6387,7 @@ async function waitForGatewayReady(params) {
|
|
|
7118
6387
|
if (await params.beforePoll?.() === "stop") return "stopped";
|
|
7119
6388
|
if (params.gateway?.isConnected === true) {
|
|
7120
6389
|
const at = Date.now();
|
|
7121
|
-
params.pushStatus?.(
|
|
7122
|
-
...createConnectedChannelStatusPatch(at),
|
|
7123
|
-
lastDisconnect: null,
|
|
7124
|
-
lastError: null
|
|
7125
|
-
});
|
|
6390
|
+
params.pushStatus?.(createDiscordReadyStatusPatch(at));
|
|
7126
6391
|
return "ready";
|
|
7127
6392
|
}
|
|
7128
6393
|
if (Date.now() >= deadlineAt) return "timeout";
|
|
@@ -7144,6 +6409,7 @@ async function waitForGatewayReady(params) {
|
|
|
7144
6409
|
params.runtime.error?.(danger(`discord: gateway READY wait timed out after ${params.readyTimeoutMs}ms; reconnecting with backoff (attempt ${attempt})`));
|
|
7145
6410
|
params.pushStatus?.({
|
|
7146
6411
|
connected: false,
|
|
6412
|
+
lifecycle: "recovering",
|
|
7147
6413
|
lastEventAt: restartAt,
|
|
7148
6414
|
lastDisconnect: {
|
|
7149
6415
|
at: restartAt,
|
|
@@ -7252,7 +6518,7 @@ async function runDiscordGatewayLifecycle(params) {
|
|
|
7252
6518
|
gatewayEmitter?.removeListener(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, onGatewayTransportActivity);
|
|
7253
6519
|
if (params.voiceManager) {
|
|
7254
6520
|
await params.voiceManager.destroy();
|
|
7255
|
-
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-
|
|
6521
|
+
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-DeIMNbRh.js").then((n) => n.n);
|
|
7256
6522
|
setDiscordTranscriptsVoiceManager({
|
|
7257
6523
|
accountId: params.accountId,
|
|
7258
6524
|
manager: null
|
|
@@ -7267,7 +6533,7 @@ async function runDiscordGatewayLifecycle(params) {
|
|
|
7267
6533
|
let discordVoiceRuntimePromise;
|
|
7268
6534
|
let discordProviderSessionRuntimePromise;
|
|
7269
6535
|
async function loadDiscordVoiceRuntime() {
|
|
7270
|
-
const promise = discordVoiceRuntimePromise ?? import("./
|
|
6536
|
+
const promise = discordVoiceRuntimePromise ?? import("./voice-runtime-CjNFaynA.js");
|
|
7271
6537
|
discordVoiceRuntimePromise = promise;
|
|
7272
6538
|
try {
|
|
7273
6539
|
return await promise;
|
|
@@ -7277,7 +6543,7 @@ async function loadDiscordVoiceRuntime() {
|
|
|
7277
6543
|
}
|
|
7278
6544
|
}
|
|
7279
6545
|
async function loadDiscordProviderSessionRuntime() {
|
|
7280
|
-
const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-
|
|
6546
|
+
const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-CvvphHvs.js");
|
|
7281
6547
|
discordProviderSessionRuntimePromise = promise;
|
|
7282
6548
|
try {
|
|
7283
6549
|
return await promise;
|
|
@@ -7287,13 +6553,12 @@ async function loadDiscordProviderSessionRuntime() {
|
|
|
7287
6553
|
}
|
|
7288
6554
|
}
|
|
7289
6555
|
const discordProviderRuntime = {
|
|
7290
|
-
|
|
6556
|
+
probeDiscordApplicationId,
|
|
7291
6557
|
createDiscordNativeCommand,
|
|
7292
6558
|
runDiscordGatewayLifecycle,
|
|
7293
6559
|
loadDiscordVoiceRuntime,
|
|
7294
6560
|
loadDiscordProviderSessionRuntime,
|
|
7295
6561
|
createClient: (...args) => new Client(...args),
|
|
7296
|
-
getPluginCommandSpecs: void 0,
|
|
7297
6562
|
resolveDiscordAccount,
|
|
7298
6563
|
resolveNativeCommandsEnabled,
|
|
7299
6564
|
resolveNativeSkillsEnabled,
|
|
@@ -7639,80 +6904,303 @@ async function cleanupDiscordProviderStartup(params) {
|
|
|
7639
6904
|
if (!params.lifecycleStarted) params.threadBindings.stop();
|
|
7640
6905
|
}
|
|
7641
6906
|
//#endregion
|
|
7642
|
-
//#region extensions/discord/src/
|
|
7643
|
-
const
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
const
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
})
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
runtime: params.runtime,
|
|
6907
|
+
//#region extensions/discord/src/voice/command.ts
|
|
6908
|
+
const VOICE_CHANNEL_TYPES = [ChannelType.GuildVoice, ChannelType.GuildStageVoice];
|
|
6909
|
+
const DISCORD_VOICE_COMMAND_SPEC = {
|
|
6910
|
+
name: "vc",
|
|
6911
|
+
description: "Voice channel controls",
|
|
6912
|
+
acceptsArgs: false
|
|
6913
|
+
};
|
|
6914
|
+
async function authorizeVoiceCommand(interaction, params, options) {
|
|
6915
|
+
const channelOverride = options?.channelOverride;
|
|
6916
|
+
const channel = channelOverride ? void 0 : interaction.channel;
|
|
6917
|
+
if (!interaction.guild) return {
|
|
6918
|
+
ok: false,
|
|
6919
|
+
message: "Voice commands are only available in guilds."
|
|
6920
|
+
};
|
|
6921
|
+
const user = interaction.user;
|
|
6922
|
+
if (!user) return {
|
|
6923
|
+
ok: false,
|
|
6924
|
+
message: "Unable to resolve command user."
|
|
6925
|
+
};
|
|
6926
|
+
const channelId = channelOverride?.id ?? channel?.id ?? "";
|
|
6927
|
+
const channelContext = await resolveDiscordThreadLikeChannelContext({
|
|
6928
|
+
client: interaction.client,
|
|
6929
|
+
channel: channelOverride ?? channel,
|
|
6930
|
+
channelIdFallback: channelId
|
|
6931
|
+
});
|
|
6932
|
+
const channelName = channelOverride?.name ?? channelContext.channelName;
|
|
6933
|
+
const memberRoleIds = Array.isArray(interaction.rawData.member?.roles) ? interaction.rawData.member.roles.map((roleId) => roleId) : [];
|
|
6934
|
+
const sender = resolveDiscordSenderIdentity({
|
|
6935
|
+
author: user,
|
|
6936
|
+
member: interaction.rawData.member
|
|
6937
|
+
});
|
|
6938
|
+
const voiceAccess = resolveDiscordVoiceAccess(params);
|
|
6939
|
+
const access = await authorizeDiscordVoiceIngress({
|
|
7676
6940
|
cfg: params.cfg,
|
|
7677
|
-
|
|
6941
|
+
discordConfig: params.discordConfig,
|
|
6942
|
+
accountId: params.accountId,
|
|
6943
|
+
groupPolicy: params.groupPolicy,
|
|
6944
|
+
useAccessGroups: params.useAccessGroups,
|
|
6945
|
+
guild: interaction.guild,
|
|
6946
|
+
guildId: interaction.guild.id,
|
|
6947
|
+
channelId,
|
|
6948
|
+
channelName,
|
|
6949
|
+
channelSlug: channelContext.channelSlug,
|
|
6950
|
+
parentId: channelOverride?.parentId ?? channelContext.threadParentId,
|
|
6951
|
+
parentName: channelContext.threadParentName,
|
|
6952
|
+
parentSlug: channelContext.threadParentSlug,
|
|
6953
|
+
scope: channelContext.isThreadChannel ? "thread" : "channel",
|
|
6954
|
+
channelLabel: channelId ? formatMention({ channelId }) : "This channel",
|
|
6955
|
+
memberRoleIds,
|
|
6956
|
+
admissionAllowFrom: voiceAccess.admissionAllowFrom,
|
|
6957
|
+
sender: {
|
|
6958
|
+
id: sender.id,
|
|
6959
|
+
name: sender.name,
|
|
6960
|
+
tag: sender.tag
|
|
6961
|
+
}
|
|
7678
6962
|
});
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
6963
|
+
if (!access.ok) return {
|
|
6964
|
+
ok: false,
|
|
6965
|
+
message: access.message
|
|
6966
|
+
};
|
|
6967
|
+
return {
|
|
6968
|
+
ok: true,
|
|
6969
|
+
guildId: interaction.guild.id
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
async function resolveVoiceCommandRuntimeContext(interaction, params) {
|
|
6973
|
+
const guildId = interaction.guild?.id;
|
|
6974
|
+
if (!guildId) {
|
|
6975
|
+
await interaction.reply({
|
|
6976
|
+
content: "Unable to resolve guild for this command.",
|
|
6977
|
+
ephemeral: true
|
|
7685
6978
|
});
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
6979
|
+
return null;
|
|
6980
|
+
}
|
|
6981
|
+
const manager = params.getManager();
|
|
6982
|
+
if (!manager) {
|
|
6983
|
+
await interaction.reply({
|
|
6984
|
+
content: "Voice manager is not available yet.",
|
|
6985
|
+
ephemeral: true
|
|
7691
6986
|
});
|
|
7692
|
-
|
|
6987
|
+
return null;
|
|
7693
6988
|
}
|
|
7694
|
-
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.`));
|
|
7695
6989
|
return {
|
|
7696
|
-
|
|
7697
|
-
|
|
6990
|
+
guildId,
|
|
6991
|
+
manager
|
|
7698
6992
|
};
|
|
7699
6993
|
}
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
6994
|
+
async function ensureVoiceCommandAccess(params) {
|
|
6995
|
+
const access = await authorizeVoiceCommand(params.interaction, params.context, { channelOverride: params.channelOverride });
|
|
6996
|
+
if (access.ok) return true;
|
|
6997
|
+
await params.interaction.reply({
|
|
6998
|
+
content: access.message ?? "Not authorized.",
|
|
6999
|
+
ephemeral: true
|
|
7000
|
+
});
|
|
7001
|
+
return false;
|
|
7002
|
+
}
|
|
7003
|
+
function createDiscordVoiceCommand(params) {
|
|
7004
|
+
const resolveSessionChannelId = (manager, guildId) => manager.status().find((entry) => entry.guildId === guildId)?.channelId;
|
|
7005
|
+
class JoinCommand extends Command {
|
|
7006
|
+
constructor(..._args) {
|
|
7007
|
+
super(..._args);
|
|
7008
|
+
this.name = "join";
|
|
7009
|
+
this.description = "Join a voice channel";
|
|
7010
|
+
this.defer = true;
|
|
7011
|
+
this.ephemeral = params.ephemeralDefault;
|
|
7012
|
+
this.options = [{
|
|
7013
|
+
name: "channel",
|
|
7014
|
+
description: "Voice channel to join",
|
|
7015
|
+
type: ApplicationCommandOptionType.Channel,
|
|
7016
|
+
required: true,
|
|
7017
|
+
channel_types: VOICE_CHANNEL_TYPES
|
|
7018
|
+
}];
|
|
7019
|
+
}
|
|
7020
|
+
async run(interaction) {
|
|
7021
|
+
const channel = await interaction.options.getChannel("channel", true);
|
|
7022
|
+
if (!channel || !("id" in channel)) {
|
|
7023
|
+
await interaction.reply({
|
|
7024
|
+
content: "Voice channel not found.",
|
|
7025
|
+
ephemeral: true
|
|
7026
|
+
});
|
|
7027
|
+
return;
|
|
7028
|
+
}
|
|
7029
|
+
const access = await authorizeVoiceCommand(interaction, params, { channelOverride: {
|
|
7030
|
+
id: channel.id,
|
|
7031
|
+
name: resolveDiscordChannelNameSafe(channel)
|
|
7032
|
+
} });
|
|
7033
|
+
if (!access.ok) {
|
|
7034
|
+
await interaction.reply({
|
|
7035
|
+
content: access.message ?? "Not authorized.",
|
|
7036
|
+
ephemeral: true
|
|
7037
|
+
});
|
|
7038
|
+
return;
|
|
7039
|
+
}
|
|
7040
|
+
if (!isVoiceChannelType(channel.type)) {
|
|
7041
|
+
await interaction.reply({
|
|
7042
|
+
content: "That is not a voice channel.",
|
|
7043
|
+
ephemeral: true
|
|
7044
|
+
});
|
|
7045
|
+
return;
|
|
7046
|
+
}
|
|
7047
|
+
const guildId = access.guildId ?? ("guildId" in channel ? channel.guildId : void 0);
|
|
7048
|
+
if (!guildId) {
|
|
7049
|
+
await interaction.reply({
|
|
7050
|
+
content: "Unable to resolve guild for this voice channel.",
|
|
7051
|
+
ephemeral: true
|
|
7052
|
+
});
|
|
7053
|
+
return;
|
|
7054
|
+
}
|
|
7055
|
+
const manager = params.getManager();
|
|
7056
|
+
if (!manager) {
|
|
7057
|
+
await interaction.reply({
|
|
7058
|
+
content: "Voice manager is not available yet.",
|
|
7059
|
+
ephemeral: true
|
|
7060
|
+
});
|
|
7061
|
+
return;
|
|
7062
|
+
}
|
|
7063
|
+
const result = await manager.join({
|
|
7064
|
+
guildId,
|
|
7065
|
+
channelId: channel.id
|
|
7066
|
+
});
|
|
7067
|
+
await interaction.reply({
|
|
7068
|
+
content: result.message,
|
|
7069
|
+
ephemeral: true
|
|
7070
|
+
});
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
class LeaveCommand extends Command {
|
|
7074
|
+
constructor(..._args2) {
|
|
7075
|
+
super(..._args2);
|
|
7076
|
+
this.name = "leave";
|
|
7077
|
+
this.description = "Leave the current voice channel";
|
|
7078
|
+
this.defer = true;
|
|
7079
|
+
this.ephemeral = params.ephemeralDefault;
|
|
7080
|
+
}
|
|
7081
|
+
async run(interaction) {
|
|
7082
|
+
const runtimeContext = await resolveVoiceCommandRuntimeContext(interaction, params);
|
|
7083
|
+
if (!runtimeContext) return;
|
|
7084
|
+
const sessionChannelId = resolveSessionChannelId(runtimeContext.manager, runtimeContext.guildId);
|
|
7085
|
+
if (!await ensureVoiceCommandAccess({
|
|
7086
|
+
interaction,
|
|
7087
|
+
context: params,
|
|
7088
|
+
channelOverride: sessionChannelId ? { id: sessionChannelId } : void 0
|
|
7089
|
+
})) return;
|
|
7090
|
+
const result = await runtimeContext.manager.leave({ guildId: runtimeContext.guildId });
|
|
7091
|
+
await interaction.reply({
|
|
7092
|
+
content: result.message,
|
|
7093
|
+
ephemeral: true
|
|
7094
|
+
});
|
|
7095
|
+
}
|
|
7096
|
+
}
|
|
7097
|
+
class StatusCommand extends Command {
|
|
7098
|
+
constructor(..._args3) {
|
|
7099
|
+
super(..._args3);
|
|
7100
|
+
this.name = "status";
|
|
7101
|
+
this.description = "Show active voice sessions";
|
|
7102
|
+
this.defer = true;
|
|
7103
|
+
this.ephemeral = params.ephemeralDefault;
|
|
7104
|
+
}
|
|
7105
|
+
async run(interaction) {
|
|
7106
|
+
const runtimeContext = await resolveVoiceCommandRuntimeContext(interaction, params);
|
|
7107
|
+
if (!runtimeContext) return;
|
|
7108
|
+
const sessions = runtimeContext.manager.status().filter((entry) => entry.guildId === runtimeContext.guildId);
|
|
7109
|
+
const sessionChannelId = sessions[0]?.channelId;
|
|
7110
|
+
if (!await ensureVoiceCommandAccess({
|
|
7111
|
+
interaction,
|
|
7112
|
+
context: params,
|
|
7113
|
+
channelOverride: sessionChannelId ? { id: sessionChannelId } : void 0
|
|
7114
|
+
})) return;
|
|
7115
|
+
if (sessions.length === 0) {
|
|
7116
|
+
await interaction.reply({
|
|
7117
|
+
content: "No active voice sessions.",
|
|
7118
|
+
ephemeral: true
|
|
7119
|
+
});
|
|
7120
|
+
return;
|
|
7121
|
+
}
|
|
7122
|
+
const lines = sessions.map((entry) => `• ${formatMention({ channelId: entry.channelId })} (guild ${entry.guildId})`);
|
|
7123
|
+
await interaction.reply({
|
|
7124
|
+
content: lines.join("\n"),
|
|
7125
|
+
ephemeral: true
|
|
7126
|
+
});
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
return new class extends CommandWithSubcommands {
|
|
7130
|
+
constructor(..._args4) {
|
|
7131
|
+
super(..._args4);
|
|
7132
|
+
this.name = DISCORD_VOICE_COMMAND_SPEC.name;
|
|
7133
|
+
this.description = DISCORD_VOICE_COMMAND_SPEC.description;
|
|
7134
|
+
this.subcommands = [
|
|
7135
|
+
new JoinCommand(),
|
|
7136
|
+
new LeaveCommand(),
|
|
7137
|
+
new StatusCommand()
|
|
7138
|
+
];
|
|
7139
|
+
}
|
|
7140
|
+
}();
|
|
7141
|
+
}
|
|
7142
|
+
function isVoiceChannelType(type) {
|
|
7143
|
+
return type === ChannelType.GuildVoice || type === ChannelType.GuildStageVoice;
|
|
7144
|
+
}
|
|
7145
|
+
//#endregion
|
|
7146
|
+
//#region extensions/discord/src/monitor/provider.commands.ts
|
|
7147
|
+
const loadPluginCommandRuntime = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/plugin-command-runtime"));
|
|
7148
|
+
async function resolveDiscordProviderCommandSpecs(params) {
|
|
7149
|
+
const listSkillCommands = params.listSkillCommandsForAgents ?? listSkillCommandsForAgents;
|
|
7150
|
+
const listNativeCommandSpecs = params.listNativeCommandSpecsForConfig ?? listNativeCommandSpecsForConfig;
|
|
7151
|
+
const maxDiscordCommands = params.maxDiscordCommands ?? 100;
|
|
7152
|
+
let pluginCommandRuntime;
|
|
7153
|
+
if (params.nativeEnabled) pluginCommandRuntime = (await loadPluginCommandRuntime()).createPluginCommandRuntime();
|
|
7154
|
+
const pluginCommandSpecs = pluginCommandRuntime?.listNativeCandidates("discord") ?? [];
|
|
7155
|
+
const onCollision = (normalizedName) => {
|
|
7156
|
+
params.runtime.error?.(danger(`discord: plugin command "/${normalizedName}" duplicates an existing native command. Skipping.`));
|
|
7157
|
+
};
|
|
7158
|
+
const mergePluginCommandSpecs = (primary, collisionHandler) => mergeNativeCommandSpecs({
|
|
7159
|
+
primary,
|
|
7160
|
+
secondary: pluginCommandSpecs,
|
|
7161
|
+
onCollision: collisionHandler
|
|
7162
|
+
});
|
|
7163
|
+
const listPrimaryCommandSpecs = (skillCommands) => {
|
|
7164
|
+
const standardSpecs = listNativeCommandSpecs(params.cfg, {
|
|
7165
|
+
skillCommands,
|
|
7166
|
+
provider: "discord"
|
|
7167
|
+
});
|
|
7168
|
+
if (!params.voiceEnabled) return standardSpecs;
|
|
7169
|
+
const voiceName = DISCORD_VOICE_COMMAND_SPEC.name;
|
|
7170
|
+
return [...standardSpecs.filter((spec) => normalizeLowercaseStringOrEmpty(spec.name) !== voiceName), DISCORD_VOICE_COMMAND_SPEC];
|
|
7171
|
+
};
|
|
7172
|
+
const provisionalCollisions = [];
|
|
7173
|
+
let skillCommands = params.nativeEnabled && params.nativeSkillsEnabled ? listSkillCommands({ cfg: params.cfg }) : [];
|
|
7174
|
+
let commandSpecs = params.nativeEnabled ? mergePluginCommandSpecs(listPrimaryCommandSpecs(skillCommands), (normalizedName) => provisionalCollisions.push(normalizedName)) : [];
|
|
7175
|
+
const initialCommandCount = commandSpecs.length;
|
|
7176
|
+
if (params.nativeEnabled && params.nativeSkillsEnabled && commandSpecs.length > maxDiscordCommands) {
|
|
7177
|
+
skillCommands = [];
|
|
7178
|
+
commandSpecs = mergePluginCommandSpecs(listPrimaryCommandSpecs([]), onCollision);
|
|
7179
|
+
params.runtime.log?.(warn(`${initialCommandCount} commands exceed the ${maxDiscordCommands}-command Discord limit; removing per-skill commands and keeping /skill.`));
|
|
7180
|
+
} else for (const normalizedName of provisionalCollisions) onCollision(normalizedName);
|
|
7181
|
+
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.`));
|
|
7182
|
+
if (commandSpecs.some((command) => "prepareDispatch" in command)) pluginCommandRuntime?.retainNativeCatalog("discord");
|
|
7183
|
+
return {
|
|
7184
|
+
skillCommands,
|
|
7185
|
+
commandSpecs
|
|
7186
|
+
};
|
|
7187
|
+
}
|
|
7188
|
+
//#endregion
|
|
7189
|
+
//#region extensions/discord/src/monitor/provider.config-log.ts
|
|
7190
|
+
function formatThreadBindingDurationForConfigLabel(durationMs) {
|
|
7191
|
+
const label = formatThreadBindingDurationLabel(durationMs);
|
|
7192
|
+
return label === "disabled" ? "off" : label;
|
|
7193
|
+
}
|
|
7194
|
+
function logDiscordResolvedConfig(params) {
|
|
7195
|
+
const allowFromSummary = summarizeStringEntries({
|
|
7196
|
+
entries: params.allowFrom ?? [],
|
|
7197
|
+
limit: 4,
|
|
7198
|
+
emptyText: "any"
|
|
7199
|
+
});
|
|
7200
|
+
const groupDmChannelSummary = summarizeStringEntries({
|
|
7201
|
+
entries: params.groupDmChannels ?? [],
|
|
7202
|
+
limit: 4,
|
|
7203
|
+
emptyText: "any"
|
|
7716
7204
|
});
|
|
7717
7205
|
const guildSummary = summarizeStringEntries({
|
|
7718
7206
|
entries: Object.keys(params.guildEntries ?? {}),
|
|
@@ -7762,13 +7250,6 @@ function resolveDiscordRejectedDeployEntriesSource(rawBody) {
|
|
|
7762
7250
|
function readDiscordDeployObjectField(value, field) {
|
|
7763
7251
|
return value && typeof value === "object" && field in value ? value[field] : void 0;
|
|
7764
7252
|
}
|
|
7765
|
-
function readFiniteNumber(value) {
|
|
7766
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7767
|
-
if (typeof value === "string" && value.trim().length > 0) return parseStrictFiniteNumber(value);
|
|
7768
|
-
}
|
|
7769
|
-
function readNonNegativeInteger(value) {
|
|
7770
|
-
return parseStrictNonNegativeInteger(value);
|
|
7771
|
-
}
|
|
7772
7253
|
function isAbortLikeError(err) {
|
|
7773
7254
|
if (!err || typeof err !== "object") return false;
|
|
7774
7255
|
const name = "name" in err && typeof err.name === "string" ? err.name : void 0;
|
|
@@ -7786,8 +7267,8 @@ function formatDiscordDeployRestOperation(err) {
|
|
|
7786
7267
|
function formatDiscordDeployErrorMessage(err) {
|
|
7787
7268
|
if (!isAbortLikeError(err)) return formatErrorMessage$1(err);
|
|
7788
7269
|
const deployErr = err && typeof err === "object" ? err : {};
|
|
7789
|
-
const requestMs =
|
|
7790
|
-
const timeoutMs =
|
|
7270
|
+
const requestMs = parseFiniteNumber$1(deployErr.deployRequestMs);
|
|
7271
|
+
const timeoutMs = parseFiniteNumber$1(deployErr.deployTimeoutMs);
|
|
7791
7272
|
const operation = formatDiscordDeployRestOperation(deployErr);
|
|
7792
7273
|
if (!(requestMs !== void 0 || timeoutMs !== void 0 || deployErr.deployRestMethod !== void 0 || deployErr.deployRestPath !== void 0)) return "Discord REST request was aborted";
|
|
7793
7274
|
const timing = [];
|
|
@@ -7800,8 +7281,8 @@ function formatDiscordDeployErrorMessage(err) {
|
|
|
7800
7281
|
function resolveDiscordDeployRateLimitDetails(err) {
|
|
7801
7282
|
if (!err || typeof err !== "object") return;
|
|
7802
7283
|
const deployErr = err;
|
|
7803
|
-
const status =
|
|
7804
|
-
const retryAfterSeconds =
|
|
7284
|
+
const status = parseStrictNonNegativeInteger$1(deployErr.status) ?? parseStrictNonNegativeInteger$1(deployErr.statusCode);
|
|
7285
|
+
const retryAfterSeconds = parseFiniteNumber$1(deployErr.retryAfter) ?? parseFiniteNumber$1(readDiscordDeployObjectField(deployErr.rawBody, "retry_after"));
|
|
7805
7286
|
if (!(err instanceof RateLimitError || status === 429 || retryAfterSeconds !== void 0)) return;
|
|
7806
7287
|
const rawGlobal = readDiscordDeployObjectField(deployErr.rawBody, "global");
|
|
7807
7288
|
const scope = typeof deployErr.scope === "string" && deployErr.scope.trim().length > 0 ? deployErr.scope : rawGlobal === true ? "global" : rawGlobal === false ? "route" : void 0;
|
|
@@ -7891,8 +7372,8 @@ function formatDiscordDeployErrorDetails(err) {
|
|
|
7891
7372
|
function isDiscordDeployDailyCreateLimit(err) {
|
|
7892
7373
|
if (!err || typeof err !== "object") return false;
|
|
7893
7374
|
const deployErr = err;
|
|
7894
|
-
const discordCode =
|
|
7895
|
-
const rawCode =
|
|
7375
|
+
const discordCode = parseStrictNonNegativeInteger$1(deployErr.discordCode);
|
|
7376
|
+
const rawCode = parseStrictNonNegativeInteger$1(readDiscordDeployObjectField(deployErr.rawBody, "code"));
|
|
7896
7377
|
return (discordCode === 30034 || rawCode === 30034) && /daily application command creates/i.test(formatErrorMessage$1(err));
|
|
7897
7378
|
}
|
|
7898
7379
|
//#endregion
|
|
@@ -8013,428 +7494,96 @@ function runDiscordCommandDeployInBackground(params) {
|
|
|
8013
7494
|
logDiscordStartupPhase$1({
|
|
8014
7495
|
runtime: params.runtime,
|
|
8015
7496
|
accountId: params.accountId,
|
|
8016
|
-
phase: "deploy-commands:scheduled",
|
|
8017
|
-
startAt: params.startupStartedAt,
|
|
8018
|
-
details: "mode=reconcile background=true",
|
|
8019
|
-
isVerbose: params.isVerbose
|
|
8020
|
-
});
|
|
8021
|
-
deployDiscordCommands(params).then(() => {
|
|
8022
|
-
logDiscordStartupPhase$1({
|
|
8023
|
-
runtime: params.runtime,
|
|
8024
|
-
accountId: params.accountId,
|
|
8025
|
-
phase: "deploy-commands:done",
|
|
8026
|
-
startAt: params.startupStartedAt,
|
|
8027
|
-
details: "background=true",
|
|
8028
|
-
isVerbose: params.isVerbose
|
|
8029
|
-
});
|
|
8030
|
-
}).catch((err) => {
|
|
8031
|
-
params.runtime.log?.(warn(`[${params.accountId}] slash command deploy failed in background (message send/receive unaffected): ${formatErrorMessage$1(err)}`));
|
|
8032
|
-
});
|
|
8033
|
-
}
|
|
8034
|
-
//#endregion
|
|
8035
|
-
//#region extensions/discord/src/monitor/agent-components-reply.ts
|
|
8036
|
-
async function replySilently(interaction, params) {
|
|
8037
|
-
try {
|
|
8038
|
-
await interaction.reply(params);
|
|
8039
|
-
} catch {}
|
|
8040
|
-
}
|
|
8041
|
-
//#endregion
|
|
8042
|
-
//#region extensions/discord/src/activities/interaction.ts
|
|
8043
|
-
const REGISTRATION_WIDGET_ID = "AAAAAAAAAAAAAAAAAAAAAA";
|
|
8044
|
-
const PENDING_LAUNCH_WRITE_BUDGET_MS = 250;
|
|
8045
|
-
var DiscordActivityButton = class extends Button {
|
|
8046
|
-
constructor(ctx, deps) {
|
|
8047
|
-
super();
|
|
8048
|
-
this.ctx = ctx;
|
|
8049
|
-
this.deps = deps;
|
|
8050
|
-
this.label = "Open widget";
|
|
8051
|
-
this.customId = buildDiscordActivityCustomId(REGISTRATION_WIDGET_ID);
|
|
8052
|
-
this.customIdParser = parseDiscordActivityCustomIdForInteraction;
|
|
8053
|
-
this.pendingLaunchFailureLogged = false;
|
|
8054
|
-
}
|
|
8055
|
-
logPendingLaunchFailure(error) {
|
|
8056
|
-
if (this.pendingLaunchFailureLogged) return;
|
|
8057
|
-
this.pendingLaunchFailureLogged = true;
|
|
8058
|
-
this.deps.logError(`discord activity: failed to record pending launch: ${String(error)}`);
|
|
8059
|
-
}
|
|
8060
|
-
async run(interaction, data) {
|
|
8061
|
-
if (typeof data.widgetId !== "string") {
|
|
8062
|
-
await this.deps.reply(interaction, {
|
|
8063
|
-
content: "This widget is no longer valid.",
|
|
8064
|
-
ephemeral: true
|
|
8065
|
-
});
|
|
8066
|
-
return;
|
|
8067
|
-
}
|
|
8068
|
-
const runtime = getDiscordActivitiesRuntime();
|
|
8069
|
-
const channelId = interaction.rawData.channel_id;
|
|
8070
|
-
const discordUserId = interaction.userId;
|
|
8071
|
-
if (!runtime || !channelId || !discordUserId) this.logPendingLaunchFailure(/* @__PURE__ */ new Error("missing activity runtime or interaction identity"));
|
|
8072
|
-
else {
|
|
8073
|
-
const write = runtime.store.recordPendingLaunch({
|
|
8074
|
-
accountId: this.ctx.accountId,
|
|
8075
|
-
channelId,
|
|
8076
|
-
discordUserId,
|
|
8077
|
-
widgetId: data.widgetId,
|
|
8078
|
-
createdAt: Date.now()
|
|
8079
|
-
}).then(() => "written").catch((error) => {
|
|
8080
|
-
this.logPendingLaunchFailure(error);
|
|
8081
|
-
return "failed";
|
|
8082
|
-
});
|
|
8083
|
-
let timer;
|
|
8084
|
-
const timeout = new Promise((resolve) => {
|
|
8085
|
-
timer = setTimeout(() => resolve("timeout"), PENDING_LAUNCH_WRITE_BUDGET_MS);
|
|
8086
|
-
timer.unref?.();
|
|
8087
|
-
});
|
|
8088
|
-
try {
|
|
8089
|
-
if (await Promise.race([write, timeout]) === "timeout") this.logPendingLaunchFailure(/* @__PURE__ */ new Error(`pending launch write exceeded ${PENDING_LAUNCH_WRITE_BUDGET_MS}ms`));
|
|
8090
|
-
} finally {
|
|
8091
|
-
if (timer) clearTimeout(timer);
|
|
8092
|
-
}
|
|
8093
|
-
}
|
|
8094
|
-
await interaction.launchActivity();
|
|
8095
|
-
}
|
|
8096
|
-
};
|
|
8097
|
-
function createDiscordActivityButton(ctx, applicationId, deps = {}) {
|
|
8098
|
-
const runtime = getDiscordActivitiesRuntime();
|
|
8099
|
-
if (!runtime || !runtime.isAccountEnabled(ctx.accountId, ctx.cfg)) return null;
|
|
8100
|
-
if (applicationId) runtime.registerApplicationId(ctx.accountId, applicationId);
|
|
8101
|
-
if (!runtime.resolveAccount(ctx.accountId, ctx.cfg)) return null;
|
|
8102
|
-
return new DiscordActivityButton(ctx, {
|
|
8103
|
-
reply: deps.reply ?? replySilently,
|
|
8104
|
-
logError: deps.logError ?? logError
|
|
8105
|
-
});
|
|
8106
|
-
}
|
|
8107
|
-
//#endregion
|
|
8108
|
-
//#region extensions/discord/src/voice/access.ts
|
|
8109
|
-
async function authorizeDiscordVoiceIngress(params) {
|
|
8110
|
-
const groupPolicy = params.groupPolicy ?? resolveOpenProviderRuntimeGroupPolicy({
|
|
8111
|
-
providerConfigPresent: params.cfg.channels?.discord !== void 0,
|
|
8112
|
-
groupPolicy: params.discordConfig.groupPolicy,
|
|
8113
|
-
defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy
|
|
8114
|
-
}).groupPolicy;
|
|
8115
|
-
const guildInfo = resolveDiscordGuildEntry({
|
|
8116
|
-
guild: params.guild ?? {
|
|
8117
|
-
id: params.guildId,
|
|
8118
|
-
...params.guildName ? { name: params.guildName } : {}
|
|
8119
|
-
},
|
|
8120
|
-
guildId: params.guildId,
|
|
8121
|
-
guildEntries: params.discordConfig.guilds
|
|
8122
|
-
});
|
|
8123
|
-
const channelConfig = params.channelId ? resolveDiscordChannelConfigWithFallback({
|
|
8124
|
-
guildInfo,
|
|
8125
|
-
channelId: params.channelId,
|
|
8126
|
-
channelName: params.channelName,
|
|
8127
|
-
channelSlug: params.channelSlug,
|
|
8128
|
-
parentId: params.parentId,
|
|
8129
|
-
parentName: params.parentName,
|
|
8130
|
-
parentSlug: params.parentSlug,
|
|
8131
|
-
scope: params.scope
|
|
8132
|
-
}) : null;
|
|
8133
|
-
if (channelConfig?.enabled === false) return {
|
|
8134
|
-
ok: false,
|
|
8135
|
-
message: "This channel is disabled."
|
|
8136
|
-
};
|
|
8137
|
-
const channelAllowlistConfigured = Boolean(guildInfo?.channels) && Object.keys(guildInfo?.channels ?? {}).length > 0;
|
|
8138
|
-
if (!params.channelId && groupPolicy === "allowlist" && channelAllowlistConfigured) return {
|
|
8139
|
-
ok: false,
|
|
8140
|
-
message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
|
|
8141
|
-
};
|
|
8142
|
-
const channelAllowed = channelConfig ? channelConfig.allowed : !channelAllowlistConfigured;
|
|
8143
|
-
if (!isDiscordGroupAllowedByPolicy({
|
|
8144
|
-
groupPolicy,
|
|
8145
|
-
guildAllowlisted: Boolean(guildInfo),
|
|
8146
|
-
channelAllowlistConfigured,
|
|
8147
|
-
channelAllowed
|
|
8148
|
-
}) || channelConfig?.allowed === false) return {
|
|
8149
|
-
ok: false,
|
|
8150
|
-
message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
|
|
8151
|
-
};
|
|
8152
|
-
const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
|
|
8153
|
-
channelConfig,
|
|
8154
|
-
guildInfo,
|
|
8155
|
-
memberRoleIds: params.memberRoleIds,
|
|
8156
|
-
sender: params.sender,
|
|
8157
|
-
allowNameMatching: false
|
|
8158
|
-
});
|
|
8159
|
-
const admissionAllowList = normalizeDiscordAllowList(params.admissionAllowFrom ?? params.discordConfig.allowFrom ?? params.discordConfig.allowFrom, [
|
|
8160
|
-
"discord:",
|
|
8161
|
-
"user:",
|
|
8162
|
-
"pk:"
|
|
8163
|
-
]);
|
|
8164
|
-
const admissionAllowed = admissionAllowList ? allowListMatches(admissionAllowList, params.sender, { allowNameMatching: false }) : false;
|
|
8165
|
-
const useAccessGroups = params.useAccessGroups ?? true;
|
|
8166
|
-
return resolveCommandAuthorizedFromAuthorizers({
|
|
8167
|
-
useAccessGroups,
|
|
8168
|
-
authorizers: useAccessGroups ? [{
|
|
8169
|
-
configured: admissionAllowList != null,
|
|
8170
|
-
allowed: admissionAllowed
|
|
8171
|
-
}, {
|
|
8172
|
-
configured: hasAccessRestrictions,
|
|
8173
|
-
allowed: memberAllowed
|
|
8174
|
-
}] : [{
|
|
8175
|
-
configured: hasAccessRestrictions,
|
|
8176
|
-
allowed: memberAllowed
|
|
8177
|
-
}],
|
|
8178
|
-
modeWhenAccessGroupsOff: "configured"
|
|
8179
|
-
}) ? {
|
|
8180
|
-
ok: true,
|
|
8181
|
-
channelConfig
|
|
8182
|
-
} : {
|
|
8183
|
-
ok: false,
|
|
8184
|
-
message: "You are not authorized to use this command."
|
|
8185
|
-
};
|
|
8186
|
-
}
|
|
8187
|
-
//#endregion
|
|
8188
|
-
//#region extensions/discord/src/voice/owner-access.ts
|
|
8189
|
-
function resolveDiscordVoiceAccess(params) {
|
|
8190
|
-
const commandOwnerAllowFrom = resolveDiscordCommandOwnerAllowFrom(params.cfg);
|
|
8191
|
-
if (commandOwnerAllowFrom) return {
|
|
8192
|
-
admissionAllowFrom: commandOwnerAllowFrom,
|
|
8193
|
-
ownerAllowFrom: commandOwnerAllowFrom,
|
|
8194
|
-
ownerAllowAll: commandOwnerAllowFrom.includes("*")
|
|
8195
|
-
};
|
|
8196
|
-
return {
|
|
8197
|
-
admissionAllowFrom: resolveDiscordAccountAllowFrom({
|
|
8198
|
-
cfg: params.cfg,
|
|
8199
|
-
accountId: params.accountId
|
|
8200
|
-
}) ?? params.discordConfig.allowFrom ?? params.discordConfig.allowFrom ?? [],
|
|
8201
|
-
ownerAllowFrom: [],
|
|
8202
|
-
ownerAllowAll: false
|
|
8203
|
-
};
|
|
8204
|
-
}
|
|
8205
|
-
//#endregion
|
|
8206
|
-
//#region extensions/discord/src/voice/command.ts
|
|
8207
|
-
const VOICE_CHANNEL_TYPES = [ChannelType.GuildVoice, ChannelType.GuildStageVoice];
|
|
8208
|
-
async function authorizeVoiceCommand(interaction, params, options) {
|
|
8209
|
-
const channelOverride = options?.channelOverride;
|
|
8210
|
-
const channel = channelOverride ? void 0 : interaction.channel;
|
|
8211
|
-
if (!interaction.guild) return {
|
|
8212
|
-
ok: false,
|
|
8213
|
-
message: "Voice commands are only available in guilds."
|
|
8214
|
-
};
|
|
8215
|
-
const user = interaction.user;
|
|
8216
|
-
if (!user) return {
|
|
8217
|
-
ok: false,
|
|
8218
|
-
message: "Unable to resolve command user."
|
|
8219
|
-
};
|
|
8220
|
-
const channelId = channelOverride?.id ?? channel?.id ?? "";
|
|
8221
|
-
const channelContext = await resolveDiscordThreadLikeChannelContext({
|
|
8222
|
-
client: interaction.client,
|
|
8223
|
-
channel: channelOverride ?? channel,
|
|
8224
|
-
channelIdFallback: channelId
|
|
8225
|
-
});
|
|
8226
|
-
const channelName = channelOverride?.name ?? channelContext.channelName;
|
|
8227
|
-
const memberRoleIds = Array.isArray(interaction.rawData.member?.roles) ? interaction.rawData.member.roles.map((roleId) => roleId) : [];
|
|
8228
|
-
const sender = resolveDiscordSenderIdentity({
|
|
8229
|
-
author: user,
|
|
8230
|
-
member: interaction.rawData.member
|
|
8231
|
-
});
|
|
8232
|
-
const voiceAccess = resolveDiscordVoiceAccess(params);
|
|
8233
|
-
const access = await authorizeDiscordVoiceIngress({
|
|
8234
|
-
cfg: params.cfg,
|
|
8235
|
-
discordConfig: params.discordConfig,
|
|
8236
|
-
accountId: params.accountId,
|
|
8237
|
-
groupPolicy: params.groupPolicy,
|
|
8238
|
-
useAccessGroups: params.useAccessGroups,
|
|
8239
|
-
guild: interaction.guild,
|
|
8240
|
-
guildId: interaction.guild.id,
|
|
8241
|
-
channelId,
|
|
8242
|
-
channelName,
|
|
8243
|
-
channelSlug: channelContext.channelSlug,
|
|
8244
|
-
parentId: channelOverride?.parentId ?? channelContext.threadParentId,
|
|
8245
|
-
parentName: channelContext.threadParentName,
|
|
8246
|
-
parentSlug: channelContext.threadParentSlug,
|
|
8247
|
-
scope: channelContext.isThreadChannel ? "thread" : "channel",
|
|
8248
|
-
channelLabel: channelId ? formatMention({ channelId }) : "This channel",
|
|
8249
|
-
memberRoleIds,
|
|
8250
|
-
admissionAllowFrom: voiceAccess.admissionAllowFrom,
|
|
8251
|
-
sender: {
|
|
8252
|
-
id: sender.id,
|
|
8253
|
-
name: sender.name,
|
|
8254
|
-
tag: sender.tag
|
|
8255
|
-
}
|
|
8256
|
-
});
|
|
8257
|
-
if (!access.ok) return {
|
|
8258
|
-
ok: false,
|
|
8259
|
-
message: access.message
|
|
8260
|
-
};
|
|
8261
|
-
return {
|
|
8262
|
-
ok: true,
|
|
8263
|
-
guildId: interaction.guild.id
|
|
8264
|
-
};
|
|
8265
|
-
}
|
|
8266
|
-
async function resolveVoiceCommandRuntimeContext(interaction, params) {
|
|
8267
|
-
const guildId = interaction.guild?.id;
|
|
8268
|
-
if (!guildId) {
|
|
8269
|
-
await interaction.reply({
|
|
8270
|
-
content: "Unable to resolve guild for this command.",
|
|
8271
|
-
ephemeral: true
|
|
8272
|
-
});
|
|
8273
|
-
return null;
|
|
8274
|
-
}
|
|
8275
|
-
const manager = params.getManager();
|
|
8276
|
-
if (!manager) {
|
|
8277
|
-
await interaction.reply({
|
|
8278
|
-
content: "Voice manager is not available yet.",
|
|
8279
|
-
ephemeral: true
|
|
8280
|
-
});
|
|
8281
|
-
return null;
|
|
8282
|
-
}
|
|
8283
|
-
return {
|
|
8284
|
-
guildId,
|
|
8285
|
-
manager
|
|
8286
|
-
};
|
|
8287
|
-
}
|
|
8288
|
-
async function ensureVoiceCommandAccess(params) {
|
|
8289
|
-
const access = await authorizeVoiceCommand(params.interaction, params.context, { channelOverride: params.channelOverride });
|
|
8290
|
-
if (access.ok) return true;
|
|
8291
|
-
await params.interaction.reply({
|
|
8292
|
-
content: access.message ?? "Not authorized.",
|
|
8293
|
-
ephemeral: true
|
|
8294
|
-
});
|
|
8295
|
-
return false;
|
|
8296
|
-
}
|
|
8297
|
-
function createDiscordVoiceCommand(params) {
|
|
8298
|
-
const resolveSessionChannelId = (manager, guildId) => manager.status().find((entry) => entry.guildId === guildId)?.channelId;
|
|
8299
|
-
class JoinCommand extends Command {
|
|
8300
|
-
constructor(..._args) {
|
|
8301
|
-
super(..._args);
|
|
8302
|
-
this.name = "join";
|
|
8303
|
-
this.description = "Join a voice channel";
|
|
8304
|
-
this.defer = true;
|
|
8305
|
-
this.ephemeral = params.ephemeralDefault;
|
|
8306
|
-
this.options = [{
|
|
8307
|
-
name: "channel",
|
|
8308
|
-
description: "Voice channel to join",
|
|
8309
|
-
type: ApplicationCommandOptionType.Channel,
|
|
8310
|
-
required: true,
|
|
8311
|
-
channel_types: VOICE_CHANNEL_TYPES
|
|
8312
|
-
}];
|
|
8313
|
-
}
|
|
8314
|
-
async run(interaction) {
|
|
8315
|
-
const channel = await interaction.options.getChannel("channel", true);
|
|
8316
|
-
if (!channel || !("id" in channel)) {
|
|
8317
|
-
await interaction.reply({
|
|
8318
|
-
content: "Voice channel not found.",
|
|
8319
|
-
ephemeral: true
|
|
8320
|
-
});
|
|
8321
|
-
return;
|
|
8322
|
-
}
|
|
8323
|
-
const access = await authorizeVoiceCommand(interaction, params, { channelOverride: {
|
|
8324
|
-
id: channel.id,
|
|
8325
|
-
name: resolveDiscordChannelNameSafe(channel)
|
|
8326
|
-
} });
|
|
8327
|
-
if (!access.ok) {
|
|
8328
|
-
await interaction.reply({
|
|
8329
|
-
content: access.message ?? "Not authorized.",
|
|
8330
|
-
ephemeral: true
|
|
8331
|
-
});
|
|
8332
|
-
return;
|
|
8333
|
-
}
|
|
8334
|
-
if (!isVoiceChannelType(channel.type)) {
|
|
8335
|
-
await interaction.reply({
|
|
8336
|
-
content: "That is not a voice channel.",
|
|
8337
|
-
ephemeral: true
|
|
8338
|
-
});
|
|
8339
|
-
return;
|
|
8340
|
-
}
|
|
8341
|
-
const guildId = access.guildId ?? ("guildId" in channel ? channel.guildId : void 0);
|
|
8342
|
-
if (!guildId) {
|
|
8343
|
-
await interaction.reply({
|
|
8344
|
-
content: "Unable to resolve guild for this voice channel.",
|
|
8345
|
-
ephemeral: true
|
|
8346
|
-
});
|
|
8347
|
-
return;
|
|
8348
|
-
}
|
|
8349
|
-
const manager = params.getManager();
|
|
8350
|
-
if (!manager) {
|
|
8351
|
-
await interaction.reply({
|
|
8352
|
-
content: "Voice manager is not available yet.",
|
|
8353
|
-
ephemeral: true
|
|
8354
|
-
});
|
|
8355
|
-
return;
|
|
8356
|
-
}
|
|
8357
|
-
const result = await manager.join({
|
|
8358
|
-
guildId,
|
|
8359
|
-
channelId: channel.id
|
|
8360
|
-
});
|
|
8361
|
-
await interaction.reply({
|
|
8362
|
-
content: result.message,
|
|
8363
|
-
ephemeral: true
|
|
8364
|
-
});
|
|
8365
|
-
}
|
|
7497
|
+
phase: "deploy-commands:scheduled",
|
|
7498
|
+
startAt: params.startupStartedAt,
|
|
7499
|
+
details: "mode=reconcile background=true",
|
|
7500
|
+
isVerbose: params.isVerbose
|
|
7501
|
+
});
|
|
7502
|
+
deployDiscordCommands(params).then(() => {
|
|
7503
|
+
logDiscordStartupPhase$1({
|
|
7504
|
+
runtime: params.runtime,
|
|
7505
|
+
accountId: params.accountId,
|
|
7506
|
+
phase: "deploy-commands:done",
|
|
7507
|
+
startAt: params.startupStartedAt,
|
|
7508
|
+
details: "background=true",
|
|
7509
|
+
isVerbose: params.isVerbose
|
|
7510
|
+
});
|
|
7511
|
+
}).catch((err) => {
|
|
7512
|
+
params.runtime.log?.(warn(`[${params.accountId}] slash command deploy failed in background (message send/receive unaffected): ${formatErrorMessage$1(err)}`));
|
|
7513
|
+
});
|
|
7514
|
+
}
|
|
7515
|
+
//#endregion
|
|
7516
|
+
//#region extensions/discord/src/monitor/agent-components-reply.ts
|
|
7517
|
+
async function replySilently(interaction, params) {
|
|
7518
|
+
try {
|
|
7519
|
+
await interaction.reply(params);
|
|
7520
|
+
} catch {}
|
|
7521
|
+
}
|
|
7522
|
+
//#endregion
|
|
7523
|
+
//#region extensions/discord/src/activities/interaction.ts
|
|
7524
|
+
const REGISTRATION_WIDGET_ID = "AAAAAAAAAAAAAAAAAAAAAA";
|
|
7525
|
+
const PENDING_LAUNCH_WRITE_BUDGET_MS = 250;
|
|
7526
|
+
var DiscordActivityButton = class extends Button {
|
|
7527
|
+
constructor(ctx, deps) {
|
|
7528
|
+
super();
|
|
7529
|
+
this.ctx = ctx;
|
|
7530
|
+
this.deps = deps;
|
|
7531
|
+
this.label = "Open widget";
|
|
7532
|
+
this.customId = buildDiscordActivityCustomId(REGISTRATION_WIDGET_ID);
|
|
7533
|
+
this.customIdParser = parseDiscordActivityCustomIdForInteraction;
|
|
7534
|
+
this.pendingLaunchFailureLogged = false;
|
|
8366
7535
|
}
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
const runtimeContext = await resolveVoiceCommandRuntimeContext(interaction, params);
|
|
8377
|
-
if (!runtimeContext) return;
|
|
8378
|
-
const sessionChannelId = resolveSessionChannelId(runtimeContext.manager, runtimeContext.guildId);
|
|
8379
|
-
if (!await ensureVoiceCommandAccess({
|
|
8380
|
-
interaction,
|
|
8381
|
-
context: params,
|
|
8382
|
-
channelOverride: sessionChannelId ? { id: sessionChannelId } : void 0
|
|
8383
|
-
})) return;
|
|
8384
|
-
const result = await runtimeContext.manager.leave({ guildId: runtimeContext.guildId });
|
|
8385
|
-
await interaction.reply({
|
|
8386
|
-
content: result.message,
|
|
7536
|
+
logPendingLaunchFailure(error) {
|
|
7537
|
+
if (this.pendingLaunchFailureLogged) return;
|
|
7538
|
+
this.pendingLaunchFailureLogged = true;
|
|
7539
|
+
this.deps.logError(`discord activity: failed to record pending launch: ${String(error)}`);
|
|
7540
|
+
}
|
|
7541
|
+
async run(interaction, data) {
|
|
7542
|
+
if (typeof data.widgetId !== "string") {
|
|
7543
|
+
await this.deps.reply(interaction, {
|
|
7544
|
+
content: "This widget is no longer valid.",
|
|
8387
7545
|
ephemeral: true
|
|
8388
7546
|
});
|
|
7547
|
+
return;
|
|
8389
7548
|
}
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
if (!await ensureVoiceCommandAccess({
|
|
8405
|
-
interaction,
|
|
8406
|
-
context: params,
|
|
8407
|
-
channelOverride: sessionChannelId ? { id: sessionChannelId } : void 0
|
|
8408
|
-
})) return;
|
|
8409
|
-
if (sessions.length === 0) {
|
|
8410
|
-
await interaction.reply({
|
|
8411
|
-
content: "No active voice sessions.",
|
|
8412
|
-
ephemeral: true
|
|
8413
|
-
});
|
|
8414
|
-
return;
|
|
8415
|
-
}
|
|
8416
|
-
const lines = sessions.map((entry) => `• ${formatMention({ channelId: entry.channelId })} (guild ${entry.guildId})`);
|
|
8417
|
-
await interaction.reply({
|
|
8418
|
-
content: lines.join("\n"),
|
|
8419
|
-
ephemeral: true
|
|
7549
|
+
const runtime = getDiscordActivitiesRuntime();
|
|
7550
|
+
const channelId = interaction.rawData.channel_id;
|
|
7551
|
+
const discordUserId = interaction.userId;
|
|
7552
|
+
if (!runtime || !channelId || !discordUserId) this.logPendingLaunchFailure(/* @__PURE__ */ new Error("missing activity runtime or interaction identity"));
|
|
7553
|
+
else {
|
|
7554
|
+
const write = runtime.store.recordPendingLaunch({
|
|
7555
|
+
accountId: this.ctx.accountId,
|
|
7556
|
+
channelId,
|
|
7557
|
+
discordUserId,
|
|
7558
|
+
widgetId: data.widgetId,
|
|
7559
|
+
createdAt: Date.now()
|
|
7560
|
+
}).then(() => "written").catch((error) => {
|
|
7561
|
+
this.logPendingLaunchFailure(error);
|
|
7562
|
+
return "failed";
|
|
8420
7563
|
});
|
|
7564
|
+
let timer;
|
|
7565
|
+
const timeout = new Promise((resolve) => {
|
|
7566
|
+
timer = setTimeout(() => resolve("timeout"), PENDING_LAUNCH_WRITE_BUDGET_MS);
|
|
7567
|
+
timer.unref?.();
|
|
7568
|
+
});
|
|
7569
|
+
try {
|
|
7570
|
+
if (await Promise.race([write, timeout]) === "timeout") this.logPendingLaunchFailure(/* @__PURE__ */ new Error(`pending launch write exceeded ${PENDING_LAUNCH_WRITE_BUDGET_MS}ms`));
|
|
7571
|
+
} finally {
|
|
7572
|
+
if (timer) clearTimeout(timer);
|
|
7573
|
+
}
|
|
8421
7574
|
}
|
|
7575
|
+
await interaction.launchActivity();
|
|
8422
7576
|
}
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
}
|
|
8434
|
-
}();
|
|
8435
|
-
}
|
|
8436
|
-
function isVoiceChannelType(type) {
|
|
8437
|
-
return type === ChannelType.GuildVoice || type === ChannelType.GuildStageVoice;
|
|
7577
|
+
};
|
|
7578
|
+
function createDiscordActivityButton(ctx, applicationId, deps = {}) {
|
|
7579
|
+
const runtime = getDiscordActivitiesRuntime();
|
|
7580
|
+
if (!runtime || !runtime.isAccountEnabled(ctx.accountId, ctx.cfg)) return null;
|
|
7581
|
+
if (applicationId) runtime.registerApplicationId(ctx.accountId, applicationId);
|
|
7582
|
+
if (!runtime.resolveAccount(ctx.accountId, ctx.cfg)) return null;
|
|
7583
|
+
return new DiscordActivityButton(ctx, {
|
|
7584
|
+
reply: deps.reply ?? replySilently,
|
|
7585
|
+
logError: deps.logError ?? logError
|
|
7586
|
+
});
|
|
8438
7587
|
}
|
|
8439
7588
|
//#endregion
|
|
8440
7589
|
//#region extensions/discord/src/monitor/agent-components-context.ts
|
|
@@ -8472,6 +7621,14 @@ async function ackComponentInteraction(params) {
|
|
|
8472
7621
|
logError(`${params.label}: failed to acknowledge interaction: ${String(err)}`);
|
|
8473
7622
|
}
|
|
8474
7623
|
}
|
|
7624
|
+
async function replyUnavailableComponentInteraction(interaction, content) {
|
|
7625
|
+
try {
|
|
7626
|
+
await interaction.reply({
|
|
7627
|
+
content,
|
|
7628
|
+
ephemeral: true
|
|
7629
|
+
});
|
|
7630
|
+
} catch {}
|
|
7631
|
+
}
|
|
8475
7632
|
function resolveDiscordChannelContext(interaction) {
|
|
8476
7633
|
const channel = interaction.channel;
|
|
8477
7634
|
const channelInfo = resolveDiscordChannelInfoSafe(channel);
|
|
@@ -8967,12 +8124,6 @@ function formatModalSubmissionText(entry, interaction) {
|
|
|
8967
8124
|
if (lines.length === 1) lines.push("- (no values)");
|
|
8968
8125
|
return lines.join("\n");
|
|
8969
8126
|
}
|
|
8970
|
-
function resolveDiscordInteractionId(interaction) {
|
|
8971
|
-
const rawId = interaction.rawData && typeof interaction.rawData === "object" && "id" in interaction.rawData ? interaction.rawData.id : void 0;
|
|
8972
|
-
if (typeof rawId === "string" && rawId.trim()) return rawId.trim();
|
|
8973
|
-
if (typeof rawId === "number" && Number.isFinite(rawId)) return String(rawId);
|
|
8974
|
-
return `discord-interaction:${Date.now()}`;
|
|
8975
|
-
}
|
|
8976
8127
|
//#endregion
|
|
8977
8128
|
//#region extensions/discord/src/monitor/agent-components-helpers.ts
|
|
8978
8129
|
const AGENT_BUTTON_KEY = "agent";
|
|
@@ -8983,7 +8134,8 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
|
|
|
8983
8134
|
const referenced = message.referencedMessage;
|
|
8984
8135
|
if (!referenced?.author) return null;
|
|
8985
8136
|
const referencedText = resolveDiscordMessageText(referenced, { includeForwarded: true });
|
|
8986
|
-
|
|
8137
|
+
const hasVisibleMedia = referenced.attachments.length > 0 || !referencedText && resolveDiscordMessageStickers(referenced).length > 0;
|
|
8138
|
+
if (!referencedText && !hasVisibleMedia) return null;
|
|
8987
8139
|
const sender = resolveDiscordSenderIdentity({
|
|
8988
8140
|
author: referenced.author,
|
|
8989
8141
|
pluralkitInfo: null
|
|
@@ -8999,7 +8151,7 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
|
|
|
8999
8151
|
const roles = referenced.member?.roles;
|
|
9000
8152
|
return Array.isArray(roles) ? roles.map((roleId) => roleId) : void 0;
|
|
9001
8153
|
})(),
|
|
9002
|
-
body: referencedText,
|
|
8154
|
+
...referencedText ? { body: referencedText } : {},
|
|
9003
8155
|
timestamp: resolveTimestampMs(referenced.timestamp)
|
|
9004
8156
|
};
|
|
9005
8157
|
}
|
|
@@ -9046,7 +8198,7 @@ function stripDiscordInternalChannelLines(text) {
|
|
|
9046
8198
|
return kept.join("\n");
|
|
9047
8199
|
}
|
|
9048
8200
|
function sanitizeDiscordFrontChannelText(text) {
|
|
9049
|
-
return collapseExcessBlankLines(stripDiscordInternalChannelLines(stripPlainTextToolCallBlocks(sanitizeAssistantVisibleText(stripPlainTextToolCallBlocks(text))))).trim();
|
|
8201
|
+
return collapseExcessBlankLines(stripDiscordInternalChannelLines(stripPlainTextToolCallBlocks(sanitizeAssistantVisibleText(stripPlainTextToolCallBlocks(text, { resolveProtectedRanges: findCodeRegions })), { resolveProtectedRanges: findCodeRegions }))).trim();
|
|
9050
8202
|
}
|
|
9051
8203
|
function sanitizeDiscordFrontChannelReplyPayloads(payloads, options = {}) {
|
|
9052
8204
|
const preserveVerboseToolProgress = options.kind === "tool";
|
|
@@ -9143,7 +8295,7 @@ function formatDiscordReasoningPayload(payload) {
|
|
|
9143
8295
|
async function deliverDiscordReply(params) {
|
|
9144
8296
|
params.runtime;
|
|
9145
8297
|
const delivery = resolveDiscordDeliveryOptions(params);
|
|
9146
|
-
const payloads = sanitizeDiscordFrontChannelReplyPayloads(params.replies, { kind: params.kind }).map(formatDiscordReasoningPayload);
|
|
8298
|
+
const payloads = sanitizeDiscordFrontChannelReplyPayloads(params.replies, { kind: params.kind }).map(formatDiscordReasoningPayload).map((payload) => params.bindPendingFinalDelivery?.(payload) ?? payload);
|
|
9147
8299
|
if (payloads.length === 0) return {
|
|
9148
8300
|
visibleReplySent: false,
|
|
9149
8301
|
suppression: { reason: "no_visible_result" }
|
|
@@ -9173,7 +8325,7 @@ async function deliverDiscordReply(params) {
|
|
|
9173
8325
|
requesterAccountId: params.accountId
|
|
9174
8326
|
})
|
|
9175
8327
|
});
|
|
9176
|
-
if (send.status === "failed"
|
|
8328
|
+
if (send.status === "failed") throw send.error;
|
|
9177
8329
|
if (send.status === "suppressed") {
|
|
9178
8330
|
const hookEffect = send.payloadOutcomes?.find((outcome) => outcome.status === "suppressed")?.hookEffect;
|
|
9179
8331
|
return {
|
|
@@ -9185,17 +8337,19 @@ async function deliverDiscordReply(params) {
|
|
|
9185
8337
|
}
|
|
9186
8338
|
};
|
|
9187
8339
|
}
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
8340
|
+
if (send.results.length === 0) throw new Error(`discord final reply produced no delivered message for ${delivery.to}`);
|
|
8341
|
+
const deliveryResult = {
|
|
8342
|
+
messageIds: listMessageReceiptPlatformIds(send.receipt),
|
|
8343
|
+
receipt: send.receipt,
|
|
9192
8344
|
visibleReplySent: true
|
|
9193
8345
|
};
|
|
8346
|
+
if (send.status === "partial_failed") throw createChannelPartialDeliveryError(send.error, deliveryResult);
|
|
8347
|
+
return deliveryResult;
|
|
9194
8348
|
}
|
|
9195
8349
|
//#endregion
|
|
9196
8350
|
//#region extensions/discord/src/monitor/agent-components.dispatch.ts
|
|
9197
8351
|
const loadConversationRuntime$1 = createLazyRuntimeModule(() => import("./agent-components.runtime-CEPrf2SY.js"));
|
|
9198
|
-
const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing
|
|
8352
|
+
const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-oLiS5Z13.js").then((n) => n.n));
|
|
9199
8353
|
function buildDiscordComponentConversationLabel(params) {
|
|
9200
8354
|
if (params.interactionCtx.isDirectMessage) return buildDirectLabel(params.interactionCtx.user);
|
|
9201
8355
|
if (params.interactionCtx.isGroupDm) return `Group DM #${params.channelCtx.channelName ?? params.interactionCtx.channelId} channel id:${params.interactionCtx.channelId}`;
|
|
@@ -9317,6 +8471,14 @@ async function dispatchDiscordComponentEvent(params) {
|
|
|
9317
8471
|
SessionKey: sessionKey,
|
|
9318
8472
|
AccountId: accountId,
|
|
9319
8473
|
ChatType: chatType,
|
|
8474
|
+
...buildDiscordConversationRouteContext({
|
|
8475
|
+
isDirectMessage: interactionCtx.isDirectMessage,
|
|
8476
|
+
isGroupDm: interactionCtx.isGroupDm,
|
|
8477
|
+
directUserId: interactionCtx.userId,
|
|
8478
|
+
conversationId: interactionCtx.channelId,
|
|
8479
|
+
isThread: channelCtx.isThread,
|
|
8480
|
+
parentConversationId: channelCtx.parentId
|
|
8481
|
+
}),
|
|
9320
8482
|
ConversationLabel: fromLabel,
|
|
9321
8483
|
SenderName: senderName,
|
|
9322
8484
|
SenderId: interactionCtx.userId,
|
|
@@ -9385,6 +8547,7 @@ async function dispatchDiscordComponentEvent(params) {
|
|
|
9385
8547
|
sessionKey
|
|
9386
8548
|
},
|
|
9387
8549
|
ctxPayload,
|
|
8550
|
+
dispatchReplyFromConfig: ctx.channelRuntime?.reply?.dispatchReplyFromConfig,
|
|
9388
8551
|
record: {
|
|
9389
8552
|
updateLastRoute: interactionCtx.isDirectMessage ? {
|
|
9390
8553
|
sessionKey: inboundLastRouteSessionKey,
|
|
@@ -9425,7 +8588,8 @@ async function dispatchDiscordComponentEvent(params) {
|
|
|
9425
8588
|
tableMode,
|
|
9426
8589
|
chunkMode: resolveChunkMode(ctx.cfg, "discord", accountId),
|
|
9427
8590
|
mediaLocalRoots,
|
|
9428
|
-
kind: info.kind
|
|
8591
|
+
kind: info.kind,
|
|
8592
|
+
bindPendingFinalDelivery: info.bindPendingFinalDelivery
|
|
9429
8593
|
});
|
|
9430
8594
|
if (result.visibleReplySent) replyReference.markSent();
|
|
9431
8595
|
return result;
|
|
@@ -9455,33 +8619,14 @@ async function dispatchDiscordComponentEvent(params) {
|
|
|
9455
8619
|
}
|
|
9456
8620
|
//#endregion
|
|
9457
8621
|
//#region extensions/discord/src/interactive-dispatch.ts
|
|
8622
|
+
const dispatchDiscordInteractive = createChannelInteractiveDispatcher({
|
|
8623
|
+
channel: "discord",
|
|
8624
|
+
interactiveKey: "interaction"
|
|
8625
|
+
});
|
|
9458
8626
|
async function dispatchDiscordPluginInteractiveHandler(params) {
|
|
9459
|
-
return await
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
dedupeId: params.interactionId,
|
|
9463
|
-
onMatched: params.onMatched,
|
|
9464
|
-
invoke: ({ registration, namespace, payload }) => registration.handler({
|
|
9465
|
-
...params.ctx,
|
|
9466
|
-
channel: "discord",
|
|
9467
|
-
interaction: {
|
|
9468
|
-
...params.ctx.interaction,
|
|
9469
|
-
data: params.data,
|
|
9470
|
-
namespace,
|
|
9471
|
-
payload
|
|
9472
|
-
},
|
|
9473
|
-
respond: params.respond,
|
|
9474
|
-
...createInteractiveConversationBindingHelpers({
|
|
9475
|
-
registration,
|
|
9476
|
-
senderId: params.ctx.senderId,
|
|
9477
|
-
conversation: {
|
|
9478
|
-
channel: "discord",
|
|
9479
|
-
accountId: params.ctx.accountId,
|
|
9480
|
-
conversationId: params.ctx.conversationId,
|
|
9481
|
-
parentConversationId: params.ctx.parentConversationId
|
|
9482
|
-
}
|
|
9483
|
-
})
|
|
9484
|
-
})
|
|
8627
|
+
return await dispatchDiscordInteractive({
|
|
8628
|
+
...params,
|
|
8629
|
+
dedupeId: params.interactionId
|
|
9485
8630
|
});
|
|
9486
8631
|
}
|
|
9487
8632
|
//#endregion
|
|
@@ -9512,10 +8657,11 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
|
|
|
9512
8657
|
},
|
|
9513
8658
|
reply: async ({ text, ephemeral = true }) => {
|
|
9514
8659
|
responded = true;
|
|
9515
|
-
|
|
8660
|
+
const payload = {
|
|
9516
8661
|
content: text,
|
|
9517
8662
|
ephemeral
|
|
9518
|
-
}
|
|
8663
|
+
};
|
|
8664
|
+
await (acknowledged ? params.interaction.followUp(payload) : params.interaction.reply(payload));
|
|
9519
8665
|
},
|
|
9520
8666
|
followUp: async ({ text, ephemeral = true }) => {
|
|
9521
8667
|
responded = true;
|
|
@@ -9573,10 +8719,10 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
|
|
|
9573
8719
|
}
|
|
9574
8720
|
const dispatched = await dispatchDiscordPluginInteractiveHandler({
|
|
9575
8721
|
data: params.data,
|
|
9576
|
-
interactionId:
|
|
8722
|
+
interactionId: params.interaction.id,
|
|
9577
8723
|
ctx: {
|
|
9578
8724
|
accountId: params.ctx.accountId,
|
|
9579
|
-
interactionId:
|
|
8725
|
+
interactionId: params.interaction.id,
|
|
9580
8726
|
conversationId: normalizedConversationId,
|
|
9581
8727
|
parentConversationId: params.channelCtx.parentId,
|
|
9582
8728
|
guildId: params.interactionCtx.rawGuildId,
|
|
@@ -9608,17 +8754,12 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
|
|
|
9608
8754
|
}
|
|
9609
8755
|
//#endregion
|
|
9610
8756
|
//#region extensions/discord/src/monitor/agent-components.handlers.ts
|
|
9611
|
-
const loadComponentsRuntime = createLazyRuntimeModule(() => import("./
|
|
8757
|
+
const loadComponentsRuntime = createLazyRuntimeModule(() => import("./components-ezGKo_Yh.js").then((n) => n.t));
|
|
9612
8758
|
async function handleDiscordComponentEvent(params) {
|
|
9613
8759
|
const parsed = parseDiscordComponentData(params.data, resolveInteractionCustomId(params.interaction));
|
|
9614
8760
|
if (!parsed) {
|
|
9615
8761
|
logError(`${params.label}: failed to parse component data`);
|
|
9616
|
-
|
|
9617
|
-
await params.interaction.reply({
|
|
9618
|
-
content: "This component is no longer valid.",
|
|
9619
|
-
ephemeral: true
|
|
9620
|
-
});
|
|
9621
|
-
} catch {}
|
|
8762
|
+
await replyUnavailableComponentInteraction(params.interaction, "This component is no longer valid.");
|
|
9622
8763
|
return;
|
|
9623
8764
|
}
|
|
9624
8765
|
const entry = await resolveDiscordComponentEntryWithPersistence({
|
|
@@ -9626,12 +8767,7 @@ async function handleDiscordComponentEvent(params) {
|
|
|
9626
8767
|
consume: false
|
|
9627
8768
|
});
|
|
9628
8769
|
if (!entry) {
|
|
9629
|
-
|
|
9630
|
-
await params.interaction.reply({
|
|
9631
|
-
content: "This component has expired.",
|
|
9632
|
-
ephemeral: true
|
|
9633
|
-
});
|
|
9634
|
-
} catch {}
|
|
8770
|
+
await replyUnavailableComponentInteraction(params.interaction, "This component has expired.");
|
|
9635
8771
|
return;
|
|
9636
8772
|
}
|
|
9637
8773
|
const unauthorizedReply = `You are not authorized to use this ${params.componentLabel}.`;
|
|
@@ -9659,21 +8795,11 @@ async function handleDiscordComponentEvent(params) {
|
|
|
9659
8795
|
consume: !entry.reusable
|
|
9660
8796
|
});
|
|
9661
8797
|
if (!consumed) {
|
|
9662
|
-
|
|
9663
|
-
await params.interaction.reply({
|
|
9664
|
-
content: "This component has expired.",
|
|
9665
|
-
ephemeral: true
|
|
9666
|
-
});
|
|
9667
|
-
} catch {}
|
|
8798
|
+
await replyUnavailableComponentInteraction(params.interaction, "This component has expired.");
|
|
9668
8799
|
return;
|
|
9669
8800
|
}
|
|
9670
8801
|
if (consumed.kind === "modal-trigger") {
|
|
9671
|
-
|
|
9672
|
-
await params.interaction.reply({
|
|
9673
|
-
content: "This form is no longer available.",
|
|
9674
|
-
ephemeral: true
|
|
9675
|
-
});
|
|
9676
|
-
} catch {}
|
|
8802
|
+
await replyUnavailableComponentInteraction(params.interaction, "This form is no longer available.");
|
|
9677
8803
|
return;
|
|
9678
8804
|
}
|
|
9679
8805
|
const values = params.values ? mapSelectValues(consumed, params.values) : void 0;
|
|
@@ -9726,12 +8852,7 @@ async function handleDiscordModalTrigger(params) {
|
|
|
9726
8852
|
const parsed = parseDiscordComponentData(params.data, resolveInteractionCustomId(params.interaction));
|
|
9727
8853
|
if (!parsed) {
|
|
9728
8854
|
logError(`${params.label}: failed to parse modal trigger data`);
|
|
9729
|
-
|
|
9730
|
-
await params.interaction.reply({
|
|
9731
|
-
content: "This button is no longer valid.",
|
|
9732
|
-
ephemeral: true
|
|
9733
|
-
});
|
|
9734
|
-
} catch {}
|
|
8855
|
+
await replyUnavailableComponentInteraction(params.interaction, "This button is no longer valid.");
|
|
9735
8856
|
return;
|
|
9736
8857
|
}
|
|
9737
8858
|
const entry = await resolveDiscordComponentEntryWithPersistence({
|
|
@@ -9739,22 +8860,12 @@ async function handleDiscordModalTrigger(params) {
|
|
|
9739
8860
|
consume: false
|
|
9740
8861
|
});
|
|
9741
8862
|
if (!entry || entry.kind !== "modal-trigger") {
|
|
9742
|
-
|
|
9743
|
-
await params.interaction.reply({
|
|
9744
|
-
content: "This button has expired.",
|
|
9745
|
-
ephemeral: true
|
|
9746
|
-
});
|
|
9747
|
-
} catch {}
|
|
8863
|
+
await replyUnavailableComponentInteraction(params.interaction, "This button has expired.");
|
|
9748
8864
|
return;
|
|
9749
8865
|
}
|
|
9750
8866
|
const modalId = entry.modalId ?? parsed.modalId;
|
|
9751
8867
|
if (!modalId) {
|
|
9752
|
-
|
|
9753
|
-
await params.interaction.reply({
|
|
9754
|
-
content: "This form is no longer available.",
|
|
9755
|
-
ephemeral: true
|
|
9756
|
-
});
|
|
9757
|
-
} catch {}
|
|
8868
|
+
await replyUnavailableComponentInteraction(params.interaction, "This form is no longer available.");
|
|
9758
8869
|
return;
|
|
9759
8870
|
}
|
|
9760
8871
|
const unauthorizedReply = "You are not authorized to use this form.";
|
|
@@ -9782,12 +8893,7 @@ async function handleDiscordModalTrigger(params) {
|
|
|
9782
8893
|
consume: !entry.reusable
|
|
9783
8894
|
});
|
|
9784
8895
|
if (!consumed) {
|
|
9785
|
-
|
|
9786
|
-
await params.interaction.reply({
|
|
9787
|
-
content: "This form has expired.",
|
|
9788
|
-
ephemeral: true
|
|
9789
|
-
});
|
|
9790
|
-
} catch {}
|
|
8896
|
+
await replyUnavailableComponentInteraction(params.interaction, "This form has expired.");
|
|
9791
8897
|
return;
|
|
9792
8898
|
}
|
|
9793
8899
|
const modalEntry = await resolveDiscordModalEntryWithPersistence({
|
|
@@ -9795,18 +8901,14 @@ async function handleDiscordModalTrigger(params) {
|
|
|
9795
8901
|
consume: false
|
|
9796
8902
|
});
|
|
9797
8903
|
if (!modalEntry) {
|
|
9798
|
-
|
|
9799
|
-
await params.interaction.reply({
|
|
9800
|
-
content: "This form has expired.",
|
|
9801
|
-
ephemeral: true
|
|
9802
|
-
});
|
|
9803
|
-
} catch {}
|
|
8904
|
+
await replyUnavailableComponentInteraction(params.interaction, "This form has expired.");
|
|
9804
8905
|
return;
|
|
9805
8906
|
}
|
|
9806
8907
|
try {
|
|
9807
8908
|
await params.interaction.showModal((await loadComponentsRuntime()).createDiscordFormModal(modalEntry));
|
|
9808
8909
|
} catch (err) {
|
|
9809
8910
|
logError(`${params.label}: failed to show modal: ${String(err)}`);
|
|
8911
|
+
await replyUnavailableComponentInteraction(params.interaction, "Could not open this form. Request a new form and try again.");
|
|
9810
8912
|
}
|
|
9811
8913
|
}
|
|
9812
8914
|
const discordComponentControlHandlers = {
|
|
@@ -9828,12 +8930,7 @@ var DiscordComponentModal = class extends Modal {
|
|
|
9828
8930
|
const modalId = parseDiscordModalId(data, resolveInteractionCustomId(interaction));
|
|
9829
8931
|
if (!modalId) {
|
|
9830
8932
|
logError("discord component modal: missing modal id");
|
|
9831
|
-
|
|
9832
|
-
await interaction.reply({
|
|
9833
|
-
content: "This form is no longer valid.",
|
|
9834
|
-
ephemeral: true
|
|
9835
|
-
});
|
|
9836
|
-
} catch {}
|
|
8933
|
+
await replyUnavailableComponentInteraction(interaction, "This form is no longer valid.");
|
|
9837
8934
|
return;
|
|
9838
8935
|
}
|
|
9839
8936
|
const modalEntry = await resolveDiscordModalEntryWithPersistence({
|
|
@@ -9841,12 +8938,7 @@ var DiscordComponentModal = class extends Modal {
|
|
|
9841
8938
|
consume: false
|
|
9842
8939
|
});
|
|
9843
8940
|
if (!modalEntry) {
|
|
9844
|
-
|
|
9845
|
-
await interaction.reply({
|
|
9846
|
-
content: "This form has expired.",
|
|
9847
|
-
ephemeral: true
|
|
9848
|
-
});
|
|
9849
|
-
} catch {}
|
|
8941
|
+
await replyUnavailableComponentInteraction(interaction, "This form has expired.");
|
|
9850
8942
|
return;
|
|
9851
8943
|
}
|
|
9852
8944
|
const unauthorizedReply = "You are not authorized to use this form.";
|
|
@@ -9879,12 +8971,7 @@ var DiscordComponentModal = class extends Modal {
|
|
|
9879
8971
|
consume: !modalEntry.reusable
|
|
9880
8972
|
});
|
|
9881
8973
|
if (!consumed) {
|
|
9882
|
-
|
|
9883
|
-
await interaction.reply({
|
|
9884
|
-
content: "This form has expired.",
|
|
9885
|
-
ephemeral: true
|
|
9886
|
-
});
|
|
9887
|
-
} catch {}
|
|
8974
|
+
await replyUnavailableComponentInteraction(interaction, "This form has expired.");
|
|
9888
8975
|
return;
|
|
9889
8976
|
}
|
|
9890
8977
|
if (consumed.callbackData) {
|
|
@@ -9933,12 +9020,7 @@ async function runAgentSystemControlInteraction(params) {
|
|
|
9933
9020
|
const parsed = parseAgentComponentData(params.data);
|
|
9934
9021
|
if (!parsed) {
|
|
9935
9022
|
logError(`${params.label}: failed to parse component data`);
|
|
9936
|
-
|
|
9937
|
-
await params.interaction.reply({
|
|
9938
|
-
content: params.invalidReply,
|
|
9939
|
-
ephemeral: true
|
|
9940
|
-
});
|
|
9941
|
-
} catch {}
|
|
9023
|
+
await replyUnavailableComponentInteraction(params.interaction, params.invalidReply);
|
|
9942
9024
|
return;
|
|
9943
9025
|
}
|
|
9944
9026
|
const { componentId } = parsed;
|
|
@@ -9979,10 +9061,7 @@ async function runAgentSystemControlInteraction(params) {
|
|
|
9979
9061
|
userId
|
|
9980
9062
|
});
|
|
9981
9063
|
logDebug(`${params.label}: enqueuing event for channel ${channelId}: ${eventText}`);
|
|
9982
|
-
|
|
9983
|
-
sessionKey: route.sessionKey,
|
|
9984
|
-
contextKey: `${params.contextKeyPrefix}:${channelId}:${componentId}:${userId}`
|
|
9985
|
-
});
|
|
9064
|
+
enqueueRoutedSystemEvent$1(eventText, route, { contextKey: `${params.contextKeyPrefix}:${channelId}:${componentId}:${userId}:${params.interaction.id}` });
|
|
9986
9065
|
await ackComponentInteraction({
|
|
9987
9066
|
interaction: params.interaction,
|
|
9988
9067
|
replyOpts,
|
|
@@ -10237,7 +9316,7 @@ var ExecApprovalButton = class extends Button {
|
|
|
10237
9316
|
try {
|
|
10238
9317
|
await interaction.acknowledge();
|
|
10239
9318
|
} catch {}
|
|
10240
|
-
const result = await this.ctx.resolveApproval(parsed.approvalId, parsed.approvalKind, parsed.action);
|
|
9319
|
+
const result = await this.ctx.resolveApproval(parsed.approvalId, parsed.approvalKind, parsed.action, userId);
|
|
10241
9320
|
if (!result.ok) {
|
|
10242
9321
|
try {
|
|
10243
9322
|
await interaction.followUp({
|
|
@@ -10274,7 +9353,7 @@ function createDiscordExecApprovalButtonContext(params) {
|
|
|
10274
9353
|
accountId: params.accountId,
|
|
10275
9354
|
configOverride: params.config
|
|
10276
9355
|
}),
|
|
10277
|
-
resolveApproval: async (approvalId, approvalKind, decision) => {
|
|
9356
|
+
resolveApproval: async (approvalId, approvalKind, decision, senderId) => {
|
|
10278
9357
|
try {
|
|
10279
9358
|
return {
|
|
10280
9359
|
ok: true,
|
|
@@ -10283,8 +9362,10 @@ function createDiscordExecApprovalButtonContext(params) {
|
|
|
10283
9362
|
approvalId,
|
|
10284
9363
|
approvalKind,
|
|
10285
9364
|
decision,
|
|
10286
|
-
|
|
10287
|
-
|
|
9365
|
+
channel: "discord",
|
|
9366
|
+
accountId: params.accountId,
|
|
9367
|
+
senderId,
|
|
9368
|
+
gatewayUrl: params.gatewayUrl
|
|
10288
9369
|
})
|
|
10289
9370
|
};
|
|
10290
9371
|
} catch (err) {
|
|
@@ -10368,24 +9449,26 @@ function createDiscordQuestionButton(params) {
|
|
|
10368
9449
|
//#region extensions/discord/src/monitor/provider.interactions.ts
|
|
10369
9450
|
function createDiscordProviderInteractionSurface(params) {
|
|
10370
9451
|
const createNativeCommand = params.createNativeCommand ?? createDiscordNativeCommand;
|
|
10371
|
-
const commands = params.commandSpecs.map((spec) =>
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
9452
|
+
const commands = params.commandSpecs.map((spec) => {
|
|
9453
|
+
if (params.nativeEnabled && params.voiceEnabled && spec.name === DISCORD_VOICE_COMMAND_SPEC.name) return createDiscordVoiceCommand({
|
|
9454
|
+
cfg: params.cfg,
|
|
9455
|
+
discordConfig: params.discordConfig,
|
|
9456
|
+
accountId: params.accountId,
|
|
9457
|
+
groupPolicy: params.groupPolicy,
|
|
9458
|
+
useAccessGroups: params.useAccessGroups,
|
|
9459
|
+
getManager: () => params.voiceManagerRef.current,
|
|
9460
|
+
ephemeralDefault: params.ephemeralDefault
|
|
9461
|
+
});
|
|
9462
|
+
return createNativeCommand({
|
|
9463
|
+
command: spec,
|
|
9464
|
+
cfg: params.cfg,
|
|
9465
|
+
discordConfig: params.discordConfig,
|
|
9466
|
+
accountId: params.accountId,
|
|
9467
|
+
sessionPrefix: params.sessionPrefix,
|
|
9468
|
+
ephemeralDefault: params.ephemeralDefault,
|
|
9469
|
+
threadBindings: params.threadBindings
|
|
9470
|
+
});
|
|
9471
|
+
});
|
|
10389
9472
|
const execApprovalsConfig = params.discordConfig.execApprovals ?? {};
|
|
10390
9473
|
const execApprovalsEnabled = isDiscordExecApprovalClientEnabled({
|
|
10391
9474
|
cfg: params.cfg,
|
|
@@ -10453,6 +9536,7 @@ function createDiscordProviderInteractionSurface(params) {
|
|
|
10453
9536
|
allowFrom: params.allowFrom,
|
|
10454
9537
|
dmPolicy: params.dmPolicy,
|
|
10455
9538
|
runtime: params.runtime,
|
|
9539
|
+
channelRuntime: params.channelRuntime,
|
|
10456
9540
|
token: params.token
|
|
10457
9541
|
}, params.applicationId);
|
|
10458
9542
|
if (activityButton) components.push(activityButton);
|
|
@@ -10665,7 +9749,8 @@ function registerDiscordMonitorListeners(params) {
|
|
|
10665
9749
|
registerDiscordListener(params.client.listeners, new DiscordReactionListener(reactionListenerOptions));
|
|
10666
9750
|
registerDiscordListener(params.client.listeners, new DiscordReactionRemoveListener(reactionListenerOptions));
|
|
10667
9751
|
}
|
|
10668
|
-
registerDiscordListener(params.client.listeners, new DiscordThreadUpdateListener(params.cfg, params.
|
|
9752
|
+
registerDiscordListener(params.client.listeners, new DiscordThreadUpdateListener(params.cfg, params.logger));
|
|
9753
|
+
registerDiscordListener(params.client.listeners, new DiscordThreadDeleteListener(params.cfg, params.accountId, params.logger));
|
|
10669
9754
|
if (params.discordConfig.intents?.presence) {
|
|
10670
9755
|
const presenceListener = new DiscordPresenceListener({
|
|
10671
9756
|
cfg: params.cfg,
|
|
@@ -10866,8 +9951,31 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10866
9951
|
phase: "fetch-application-id:start",
|
|
10867
9952
|
startAt: startupStartedAt
|
|
10868
9953
|
});
|
|
10869
|
-
const
|
|
10870
|
-
|
|
9954
|
+
const parsedApplicationId = (typeof discordCfg.applicationId === "string" && discordCfg.applicationId.trim() ? discordCfg.applicationId.trim() : void 0) ?? parseApplicationIdFromToken(token);
|
|
9955
|
+
const applicationIdProbe = parsedApplicationId ? {
|
|
9956
|
+
kind: "resolved",
|
|
9957
|
+
applicationId: parsedApplicationId
|
|
9958
|
+
} : await discordProviderRuntime.probeDiscordApplicationId(token, 4e3, discordRestFetch);
|
|
9959
|
+
if (applicationIdProbe.kind !== "resolved") {
|
|
9960
|
+
const at = Date.now();
|
|
9961
|
+
const terminal = applicationIdProbe.kind === "rejected";
|
|
9962
|
+
const probeError = formatErrorMessage$1(applicationIdProbe.error);
|
|
9963
|
+
const message = `Failed to resolve Discord application id: ${probeError}`;
|
|
9964
|
+
opts.setStatus?.({
|
|
9965
|
+
connected: false,
|
|
9966
|
+
lifecycle: terminal ? "blocked" : "recovering",
|
|
9967
|
+
terminalDisconnect: terminal ? true : void 0,
|
|
9968
|
+
lastEventAt: at,
|
|
9969
|
+
lastDisconnect: {
|
|
9970
|
+
at,
|
|
9971
|
+
...applicationIdProbe.status !== null ? { status: applicationIdProbe.status } : {},
|
|
9972
|
+
error: probeError
|
|
9973
|
+
},
|
|
9974
|
+
lastError: message
|
|
9975
|
+
});
|
|
9976
|
+
throw new Error(message, { cause: applicationIdProbe.error });
|
|
9977
|
+
}
|
|
9978
|
+
const applicationId = applicationIdProbe.applicationId;
|
|
10871
9979
|
logDiscordStartupPhase({
|
|
10872
9980
|
runtime,
|
|
10873
9981
|
accountId: account.accountId,
|
|
@@ -10880,9 +9988,9 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10880
9988
|
runtime,
|
|
10881
9989
|
nativeEnabled,
|
|
10882
9990
|
nativeSkillsEnabled,
|
|
9991
|
+
voiceEnabled,
|
|
10883
9992
|
listSkillCommandsForAgents: discordProviderRuntime.listSkillCommandsForAgents,
|
|
10884
|
-
listNativeCommandSpecsForConfig: discordProviderRuntime.listNativeCommandSpecsForConfig
|
|
10885
|
-
getPluginCommandSpecs: discordProviderRuntime.getPluginCommandSpecs
|
|
9993
|
+
listNativeCommandSpecsForConfig: discordProviderRuntime.listNativeCommandSpecsForConfig
|
|
10886
9994
|
});
|
|
10887
9995
|
const voiceManagerRef = { current: null };
|
|
10888
9996
|
const threadBindings = threadBindingsEnabled ? discordProviderSessionRuntime.createThreadBindingManager({
|
|
@@ -10921,6 +10029,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10921
10029
|
let earlyGatewayEmitter = gatewaySupervisor?.emitter;
|
|
10922
10030
|
let onEarlyGatewayDebug;
|
|
10923
10031
|
try {
|
|
10032
|
+
const pluginChannelRuntime = opts.channelRuntime;
|
|
10924
10033
|
const { commands, components, modals } = createDiscordProviderInteractionSurface({
|
|
10925
10034
|
cfg,
|
|
10926
10035
|
discordConfig: discordCfg,
|
|
@@ -10940,7 +10049,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
10940
10049
|
allowFrom,
|
|
10941
10050
|
dmPolicy,
|
|
10942
10051
|
runtime,
|
|
10943
|
-
channelRuntime:
|
|
10052
|
+
channelRuntime: pluginChannelRuntime,
|
|
10944
10053
|
abortSignal: opts.abortSignal,
|
|
10945
10054
|
createNativeCommand: discordProviderRuntime.createDiscordNativeCommand
|
|
10946
10055
|
});
|
|
@@ -11014,7 +10123,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
11014
10123
|
runtime,
|
|
11015
10124
|
botUserId
|
|
11016
10125
|
});
|
|
11017
|
-
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-
|
|
10126
|
+
const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-DeIMNbRh.js").then((n) => n.n);
|
|
11018
10127
|
setDiscordTranscriptsVoiceManager({
|
|
11019
10128
|
accountId: account.accountId,
|
|
11020
10129
|
manager: voiceManager
|
|
@@ -11032,6 +10141,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
11032
10141
|
accountId: account.accountId,
|
|
11033
10142
|
token,
|
|
11034
10143
|
runtime,
|
|
10144
|
+
buildContext: pluginChannelRuntime?.inbound.buildContext,
|
|
11035
10145
|
setStatus: opts.setStatus,
|
|
11036
10146
|
abortSignal: opts.abortSignal,
|
|
11037
10147
|
botUserId,
|
|
@@ -11087,7 +10197,7 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
11087
10197
|
gatewayReady: lifecycleGateway?.isConnected === true,
|
|
11088
10198
|
botIdentity: botIdentity || void 0
|
|
11089
10199
|
}));
|
|
11090
|
-
if (lifecycleGateway?.isConnected) opts.setStatus?.(
|
|
10200
|
+
if (lifecycleGateway?.isConnected) opts.setStatus?.(createDiscordReadyStatusPatch());
|
|
11091
10201
|
lifecycleStarted = true;
|
|
11092
10202
|
earlyGatewayEmitter?.removeListener("debug", onEarlyGatewayDebug);
|
|
11093
10203
|
onEarlyGatewayDebug = void 0;
|
|
@@ -11119,4 +10229,4 @@ async function monitorDiscordProvider(opts = {}) {
|
|
|
11119
10229
|
}
|
|
11120
10230
|
}
|
|
11121
10231
|
//#endregion
|
|
11122
|
-
export {
|
|
10232
|
+
export { mapGatewayDispatchData as _, sanitizeDiscordFrontChannelReplyPayloads as a, resolveDiscordDmCommandAccess as b, resolveReplyContext as c, waitForDiscordGatewayPluginRegistration as d, createDiscordNativeCommand as f, handleDiscordDmCommandDecision as g, createDiscordSupplementalContextAccessChecker as h, formatDiscordReplySkip as i, createDiscordGatewayPlugin as l, buildDiscordInboundAccessContext as m, deliverDiscordReply as n, buildDirectLabel as o, buildDiscordGroupSystemPrompt as p, formatDiscordReplyDeliveryFailure as r, buildGuildLabel as s, monitorDiscordProvider as t, resolveDiscordGatewayIntents as u, registerDiscordListener as v, resolveDiscordTextCommandAccess as x, resolveFetchedDiscordThreadLikeChannelContext as y };
|