@openclaw/discord 2026.8.1-beta.2 → 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.
Files changed (100) hide show
  1. package/dist/{account-inspect-CqzrH14k.js → account-inspect-D5xmUpnj.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/accounts-DNl-oCEw.js +371 -0
  4. package/dist/action-runtime-api.js +1 -1
  5. package/dist/{activities-api-DS7vpdtp.js → activities-api-KCBAmWxD.js} +133 -175
  6. package/dist/activities-api.js +1 -1
  7. package/dist/api.js +17 -17
  8. package/dist/{approval-handler.runtime-B6yEV79q.js → approval-handler.runtime-B12RCYr8.js} +6 -6
  9. package/dist/{approval-message-safety-AcIxvrf3.js → approval-message-safety-XGFPbDoI.js} +89 -3
  10. package/dist/{audit-Db-rlntl.js → audit-Yc7eZDd6.js} +5 -5
  11. package/dist/{channel-actions-5s_hjYKd.js → channel-actions-Dr2QI8Bf.js} +10 -6
  12. package/dist/{channel-actions.runtime-CLKrx6tr.js → channel-actions.runtime-CODTvo7c.js} +53 -68
  13. package/dist/{channel-CnnbJSt5.js → channel-cXYzGc4L.js} +74 -50
  14. package/dist/channel-config-api.js +1 -1
  15. package/dist/channel-plugin-api.js +1 -1
  16. package/dist/{channel.setup-BFAO-V1P.js → channel.setup-CSiwnMgs.js} +5 -5
  17. package/dist/{outbound-session-route-DxLkrgKQ.js → components-ezGKo_Yh.js} +3 -49
  18. package/dist/{config-schema-BgEskFAs.js → config-schema-DC5kDtQ5.js} +33 -7
  19. package/dist/conversation-binding-route-D6rCQ5S0.js +43 -0
  20. package/dist/{directory-config-BVX7Cflc.js → directory-config-XyCfoqLg.js} +2 -2
  21. package/dist/directory-contract-api.js +1 -1
  22. package/dist/{directory-live-DGIGxpIo.js → directory-live-Df0NbFJB.js} +3 -3
  23. package/dist/{doctor-BzC-TLnB.js → doctor-DYY31GrO.js} +33 -12
  24. package/dist/doctor-contract-api.js +3 -3
  25. package/dist/{handle-action.guild-admin-BNVTUISF.js → handle-action.guild-admin-6Tvvinsa.js} +2 -2
  26. package/dist/index.js +3 -4
  27. package/dist/{message-handler-Unk_f10F.js → message-handler-B61q5MOF.js} +82 -6
  28. package/dist/{message-handler.preflight-C9zecxR9.js → message-handler.preflight-Bzg_Hawz.js} +36 -46
  29. package/dist/{message-handler.process-DWQ6UHHu.js → message-handler.process--4_712Ec.js} +159 -183
  30. package/dist/{message-utils-BSCPVgy0.js → message-utils-BXLLnFl6.js} +31 -32
  31. package/dist/{outbound-adapter-D9nRivT1.js → outbound-adapter-DxHTOqiN.js} +83 -53
  32. package/dist/{outbound-components-cTzwBJ60.js → outbound-components-B2PL0ooH.js} +3 -3
  33. package/dist/owner-access-Bg41VzAf.js +107 -0
  34. package/dist/{pluralkit-DdiJ9KMK.js → pluralkit-dUl4v1X3.js} +1 -1
  35. package/dist/{probe-Bl-k5_Ku.js → probe-BTdtVRwU.js} +1 -1
  36. package/dist/{probe.runtime-M9fREKvp.js → probe.runtime-Dq5-PRKT.js} +1 -1
  37. package/dist/{provider-DvSNb-2v.js → provider-DlK_ynqt.js} +356 -967
  38. package/dist/{provider-session.runtime-BUEqD0rA.js → provider-session.runtime-CvvphHvs.js} +3 -3
  39. package/dist/provider.runtime-CzKGRiAF.js +2 -0
  40. package/dist/{realtime-session.runtime-C7wen-aF.js → realtime-session.runtime-ysbMG8v7.js} +24 -13
  41. package/dist/{resolve-allowlist-common-D04lpEzb.js → resolve-allowlist-common-DIVXbD1P.js} +2 -2
  42. package/dist/{resolve-channels-Dm7jEeg6.js → resolve-channels-CGeUoIqb.js} +3 -3
  43. package/dist/{resolve-users-bktrCUUU.js → resolve-users-BtKxLUZv.js} +2 -2
  44. package/dist/{retry-CLpJcXXf.js → retry-DNsVTeTo.js} +18 -64
  45. package/dist/{runtime-DoHjckYm.js → runtime-CQovMsH3.js} +9 -10
  46. package/dist/{runtime-DGsSyDTS.js → runtime-D71_VFLr.js} +29 -43
  47. package/dist/runtime-api.actions.js +2 -2
  48. package/dist/runtime-api.js +22 -21
  49. package/dist/runtime-api.lookup.js +5 -5
  50. package/dist/runtime-api.monitor-CzeTYF32.js +6 -0
  51. package/dist/runtime-api.monitor.js +6 -5
  52. package/dist/runtime-api.send.js +6 -6
  53. package/dist/runtime-api.threads.js +4 -4
  54. package/dist/{sdk-runtime-QpY8PvCh.js → sdk-runtime-37OU6_Yh.js} +3 -4
  55. package/dist/{secret-config-contract-B1akrlyY.js → secret-config-contract-B3i6WxV5.js} +53 -25
  56. package/dist/secret-contract-api.js +1 -1
  57. package/dist/security-audit-contract-api.js +1 -1
  58. package/dist/{security-audit.runtime-CPN0xFYz.js → security-audit.runtime-BVKF9VtY.js} +1 -1
  59. package/dist/{send-B7Q6Oc1I.js → send-rQIffSYu.js} +10 -10
  60. package/dist/{send.components-iJyFXw9L.js → send.components-ChK1wS7w.js} +14 -24
  61. package/dist/{send.outbound-DS5tHwfR.js → send.outbound-HIkgji_7.js} +26 -16
  62. package/dist/{send.reactions-BAn0iiVQ.js → send.reactions-gmJ6yFPo.js} +2 -2
  63. package/dist/{send.receipt-IPI1p-SP.js → send.receipt-Cnhoautb.js} +11 -1
  64. package/dist/{send.shared-BmwFjW6T.js → send.shared-B3fpaLWJ.js} +104 -104
  65. package/dist/{sender-identity-QtalnCYv.js → sender-identity-BJtfyise.js} +1 -1
  66. package/dist/setup-plugin-api.js +1 -1
  67. package/dist/{shared-BIXIX6uJ.js → shared-CaAaoksy.js} +15 -10
  68. package/dist/subagent-hooks-CNT2ZJTJ.js +49 -0
  69. package/dist/subagent-hooks-api.js +2 -2
  70. package/dist/{subagent-progress-DhaE_h8a.js → subagent-progress-FPLqBY1s.js} +5 -4
  71. package/dist/{system-events-CTqDf6_s.js → system-events-Nuwoq_au.js} +2 -2
  72. package/dist/{target-resolver-maOKSqO0.js → target-resolver-DIXShHaZ.js} +3 -3
  73. package/dist/targets-B2pf49zc.js +3 -0
  74. package/dist/{thread-bindings-3L8p82GV.js → thread-bindings-VlymUnbz.js} +29 -26
  75. package/dist/{thread-bindings.manager-DCIH6WDC.js → thread-bindings.manager-CngsgErq.js} +9 -8
  76. package/dist/{thread-bindings.session-updates-bJ2KQFY6.js → thread-bindings.session-updates-CJb-BpPL.js} +1 -1
  77. package/dist/{thread-bindings.state-aeV8HNgV.js → thread-bindings.state-D8OMdk4O.js} +1 -1
  78. package/dist/threading-DU6pgeGm.js +456 -0
  79. package/dist/transcripts-source-DeIMNbRh.js +265 -0
  80. package/dist/transcripts-source-api.js +7 -2
  81. package/dist/{typing-CS-nNVoe.js → typing-oLiS5Z13.js} +1 -1
  82. package/dist/{voice-runtime-BEdqNcST.js → voice-runtime-CjNFaynA.js} +181 -91
  83. package/openclaw.plugin.json +12 -6
  84. package/package.json +4 -6
  85. package/dist/accounts-DJTf1LtA.js +0 -219
  86. package/dist/channel-api-CAJ0wMoV.js +0 -21
  87. package/dist/provider.runtime-C9PnJZbZ.js +0 -2
  88. package/dist/runtime-api.monitor-CHxrFPnW.js +0 -5
  89. package/dist/subagent-hooks-CTt5KCJr.js +0 -120
  90. package/dist/targets-yUFTHoAx.js +0 -3
  91. package/dist/transcripts-source-CfFMexGt.js +0 -123
  92. package/node_modules/p-map/index.d.ts +0 -155
  93. package/node_modules/p-map/index.js +0 -285
  94. package/node_modules/p-map/license +0 -9
  95. package/node_modules/p-map/package.json +0 -57
  96. package/node_modules/p-map/readme.md +0 -190
  97. package/dist/{allow-list-DnxIlPP2.js → allow-list-BDMimASD.js} +1 -1
  98. package/dist/{audit-core-xwjIczO0.js → audit-core-BCX8UeFH.js} +1 -1
  99. package/dist/{doctor-contract-DLa-BTiU.js → doctor-contract-DsWvKIXF.js} +1 -1
  100. package/dist/{security-audit-jGnJF__m.js → security-audit-BfJhAXZp.js} +1 -1
@@ -1,32 +1,30 @@
1
- import { B as Message, C as ReadyListener, Ct as editChannel, E as ThreadUpdateListener, Et as getChannelMessage, K as Modal, L as Command, M as RateLimitError, O as Client, R as CommandWithSubcommands, S as PresenceUpdateListener, T as ThreadDeleteListener, V as User, Y as Button, Z as Container, _ as GuildDeleteListener, a as chunkDiscordTextWithMode, at as Separator, b as MessageReactionAddListener, bt as createThread, g as GuildCreateListener, h as discord_exports, k as Plugin, ot as StringSelectMenu, rt as Row, st as TextDisplay, tn as __exportAll, ut as BaseMessageInteractiveComponent, v as InteractionCreateListener, x as MessageReactionRemoveListener, y as MessageCreateListener, z as Guild } from "./retry-CLpJcXXf.js";
2
- import { c as resolveDiscordAccountAllowFrom, d as resolveDiscordAccountDmPolicy, f as resolveDiscordMaxLinesPerMessage, p as normalizeDiscordToken, s as resolveDiscordAccount } from "./accounts-DJTf1LtA.js";
3
- import { n as getDiscordActivitiesRuntime } from "./runtime-DoHjckYm.js";
4
- import { C as parseDiscordActivityCustomIdForInteraction, D as parseDiscordModalCustomIdForInteraction, E as parseDiscordModalCustomId, O as decodeCustomIdComponent, T as parseDiscordComponentCustomIdForInteraction, k as encodeCustomIdComponent, l as parseExecApprovalData, s as parseDiscordQuestionData, w as parseDiscordComponentCustomId, y as buildDiscordActivityCustomId } from "./outbound-session-route-DxLkrgKQ.js";
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-BmwFjW6T.js";
6
- import { _ as resolveDiscordModalEntryWithPersistence, g as resolveDiscordComponentEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-iJyFXw9L.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
6
  import { a as isDiscordRateLimitResponseBody, o as summarizeDiscordResponseBody } from "./api-DNVbJyEy.js";
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, t as allowListMatches, v as resolveGroupDmAllow, x as formatDiscordUserTag, y as shouldEmitDiscordReactionNotification } from "./allow-list-DnxIlPP2.js";
10
- import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-DS5tHwfR.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 { r as sendVoiceMessageDiscord } from "./send-B7Q6Oc1I.js";
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 resolveDiscordConversationIdentity, s as isDiscordExecApprovalClientEnabled, t as DISCORD_APPROVAL_ALLOWED_MENTIONS } from "./approval-message-safety-AcIxvrf3.js";
15
- import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-Dm7jEeg6.js";
16
- import { t as resolveDiscordUserAllowlist } from "./resolve-users-bktrCUUU.js";
17
- import { a as isThreadArchived, c as resolveDiscordChannelInfoSafe, d as resolveDiscordChannelParentSafe, f as resolveDiscordChannelTopicSafe, h as formatThreadBindingDurationLabel, l as resolveDiscordChannelNameSafe, r as getThreadBindingManager, s as resolveDiscordChannelIdSafe, u as resolveDiscordChannelParentIdSafe } from "./thread-bindings.manager-DCIH6WDC.js";
18
- import { a as probeDiscordApplicationId, r as parseApplicationIdFromToken } from "./probe-Bl-k5_Ku.js";
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";
19
20
  import { i as sanitizeRecentModels, n as normalizeModelRef, r as preferenceTimestampMs, t as buildPreferenceModelKey } from "./model-picker-preference-primitives-BHT1LkR_.js";
20
- import { a as formatDiscordMediaText, d as resolveDiscordChannelInfo, f as resolveDiscordMessageChannelId, n as resolveDiscordForwardedMessagesTextFromSnapshots, t as resolveDiscordEmbedText } from "./message-utils-BSCPVgy0.js";
21
- import { t as resolveDiscordSenderIdentity } from "./sender-identity-QtalnCYv.js";
22
- import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
23
- import { buildAgentSessionKey, deriveLastRoutePolicy, isAcpSessionKey, isSubagentSessionKey, parseAgentSessionKey, resolveAgentIdFromSessionKey, resolveAgentRoute } from "openclaw/plugin-sdk/routing";
24
- import { asFiniteNumber, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeStringEntries, parseFiniteNumber, parseStrictNonNegativeInteger, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
25
- import { getRuntimeConfig, getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
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, parseFiniteNumber as parseFiniteNumber$1, parseStrictInteger, parseStrictPositiveInteger, resolveDateTimestampMs, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
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";
@@ -34,6 +32,10 @@ import { inspect } from "node:util";
34
32
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
35
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";
@@ -41,33 +43,31 @@ import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary
41
43
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
42
44
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
43
45
  import { chunkItems, findCodeRegions, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithProfile } from "openclaw/plugin-sdk/text-chunking";
44
- import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
45
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
50
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
50
- import { resolvePinnedMainDmOwnerFromAllowlist as resolvePinnedMainDmOwnerFromAllowlist$1 } from "openclaw/plugin-sdk/security-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";
57
+ import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
58
+ import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
56
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
62
  import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
60
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";
61
65
  import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
62
66
  import { createChannelPartialDeliveryError, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, resolveEnvelopeFormatOptions, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent } from "openclaw/plugin-sdk/channel-inbound";
63
- import { resolveChannelModelOverride } from "openclaw/plugin-sdk/model-session-runtime";
64
- import { finalizeInboundContext, generateConversationLabel } from "openclaw/plugin-sdk/reply-dispatch-runtime";
65
- import { deleteSessionEntry, getSessionEntry, listSessionEntries, readSessionUpdatedAt as readSessionUpdatedAt$1, resolveStorePath, resolveStorePath as resolveStorePath$1 } from "openclaw/plugin-sdk/session-store-runtime";
66
- import { buildCommandTextFromArgs, findCommandByNativeName, formatCommandArgMenuTitle, listChatCommands, listNativeCommandSpecsForConfig, listSkillCommandsForAgents, resolveCommandAuthorizedFromAuthorizers, resolveEffectiveAgentRuntime, resolveNativeCommandSessionTargets, resolveStoredModelOverride, serializeCommandArgs } from "openclaw/plugin-sdk/command-auth-native";
67
+ import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
67
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";
68
69
  import { PLUGIN_COMMAND_DISPATCH } from "openclaw/plugin-sdk/plugin-command-runtime";
69
70
  import { resolveDirectStatusReplyForSession } from "openclaw/plugin-sdk/command-status-runtime";
70
- import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
71
71
  import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
72
72
  import { EventEmitter } from "node:events";
73
73
  import * as ws from "ws";
@@ -140,6 +140,205 @@ async function runDiscordListenerWithSlowLog(params) {
140
140
  }
141
141
  }
142
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
143
342
  //#region extensions/discord/src/monitor/dm-command-auth.ts
144
343
  const DISCORD_ALLOW_LIST_PREFIXES = [
145
344
  "discord:",
@@ -305,461 +504,6 @@ async function resolveDiscordTextCommandAccess(params) {
305
504
  });
306
505
  }
307
506
  //#endregion
308
- //#region extensions/discord/src/monitor/thread-title.ts
309
- const DEFAULT_THREAD_TITLE_TIMEOUT_MS = 6e4;
310
- const MAX_THREAD_TITLE_SOURCE_CHARS = 600;
311
- const MAX_THREAD_TITLE_CHANNEL_NAME_CHARS = 120;
312
- const MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS = 320;
313
- 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.";
314
- async function generateThreadTitle(params) {
315
- const sourceText = params.messageText.trim();
316
- if (!sourceText) return null;
317
- try {
318
- const userMessage = buildThreadTitleCompletionUserMessage({
319
- sourceText,
320
- channelName: params.channelName,
321
- channelDescription: params.channelDescription
322
- });
323
- const timeoutMs = resolveThreadTitleTimeoutMs(params.timeoutMs);
324
- const generated = await generateConversationLabel({
325
- cfg: params.cfg,
326
- agentId: params.agentId,
327
- userMessage,
328
- prompt: DISCORD_THREAD_TITLE_SYSTEM_PROMPT,
329
- ...params.modelRef ? { modelRef: params.modelRef } : {},
330
- timeoutMs,
331
- maxLength: MAX_THREAD_TITLE_SOURCE_CHARS
332
- });
333
- return generated ? normalizeGeneratedThreadTitle(generated) : null;
334
- } catch (err) {
335
- logVerbose(`thread-title: title generation failed for agent ${params.agentId}: ${String(err)}`);
336
- return null;
337
- }
338
- }
339
- function buildThreadTitleCompletionUserMessage(params) {
340
- const sourceText = truncateThreadTitleSourceText(params.sourceText);
341
- const channelName = normalizeTitleContextField(params.channelName, MAX_THREAD_TITLE_CHANNEL_NAME_CHARS);
342
- const channelDescription = normalizeTitleContextField(params.channelDescription, MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS);
343
- const messageLines = [];
344
- if (channelName) messageLines.push(`Channel: ${channelName}`);
345
- if (channelDescription) messageLines.push(`Channel description: ${channelDescription}`);
346
- messageLines.push(`Message:\n${sourceText}`);
347
- return messageLines.join("\n\n");
348
- }
349
- function truncateThreadTitleSourceText(sourceText) {
350
- if (sourceText.length <= MAX_THREAD_TITLE_SOURCE_CHARS) return sourceText;
351
- return `${truncateUtf16Safe(sourceText, MAX_THREAD_TITLE_SOURCE_CHARS)}...`;
352
- }
353
- function resolveThreadTitleTimeoutMs(timeoutMs) {
354
- return Math.max(100, Math.floor(timeoutMs ?? DEFAULT_THREAD_TITLE_TIMEOUT_MS));
355
- }
356
- function normalizeGeneratedThreadTitle(raw) {
357
- const lines = raw.replace(/\r/g, "").split("\n");
358
- let firstLine = "";
359
- for (const line of lines) {
360
- const trimmed = line.trim();
361
- if (!trimmed) continue;
362
- if (!firstLine && trimmed.startsWith("```")) continue;
363
- firstLine = trimmed;
364
- break;
365
- }
366
- return stripThreadTitleWrappers(firstLine);
367
- }
368
- function stripThreadTitleWrappers(raw) {
369
- let current = raw.trim();
370
- let previous = "";
371
- while (current && current !== previous) {
372
- previous = current;
373
- current = current.replace(/^["'`]+|["'`]+$/g, "").trim();
374
- current = stripBalancedWrapper(current, "**");
375
- current = stripBalancedWrapper(current, "__");
376
- current = stripBalancedWrapper(current, "*");
377
- current = stripBalancedWrapper(current, "_");
378
- current = stripBalancedWrapper(current, "~~");
379
- }
380
- return current;
381
- }
382
- function stripBalancedWrapper(text, marker) {
383
- if (text.length < marker.length * 2 + 1) return text;
384
- if (!text.startsWith(marker) || !text.endsWith(marker)) return text;
385
- const inner = text.slice(marker.length, text.length - marker.length);
386
- if (!inner || inner.includes(marker)) return text;
387
- return inner;
388
- }
389
- function normalizeTitleContextField(raw, maxChars) {
390
- const value = raw?.trim();
391
- if (!value) return;
392
- const singleLine = value.replace(/\s+/g, " ");
393
- if (singleLine.length <= maxChars) return singleLine;
394
- return `${truncateUtf16Safe(singleLine, maxChars)}...`;
395
- }
396
- //#endregion
397
- //#region extensions/discord/src/monitor/threading.cache.ts
398
- const DISCORD_THREAD_STARTER_CACHE_TTL_MS = 300 * 1e3;
399
- const DISCORD_THREAD_STARTER_CACHE_MAX = 500;
400
- const DISCORD_THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
401
- function resetDiscordThreadStarterCacheForTest() {
402
- DISCORD_THREAD_STARTER_CACHE.clear();
403
- }
404
- function getCachedThreadStarter(key, now) {
405
- const entry = DISCORD_THREAD_STARTER_CACHE.get(key);
406
- if (!entry) return;
407
- if (now - entry.updatedAt > DISCORD_THREAD_STARTER_CACHE_TTL_MS) {
408
- DISCORD_THREAD_STARTER_CACHE.delete(key);
409
- return;
410
- }
411
- DISCORD_THREAD_STARTER_CACHE.delete(key);
412
- DISCORD_THREAD_STARTER_CACHE.set(key, {
413
- ...entry,
414
- updatedAt: now
415
- });
416
- return entry.value;
417
- }
418
- function setCachedThreadStarter(key, value, now) {
419
- DISCORD_THREAD_STARTER_CACHE.delete(key);
420
- DISCORD_THREAD_STARTER_CACHE.set(key, {
421
- value,
422
- updatedAt: now
423
- });
424
- while (DISCORD_THREAD_STARTER_CACHE.size > DISCORD_THREAD_STARTER_CACHE_MAX) {
425
- const iter = DISCORD_THREAD_STARTER_CACHE.keys().next();
426
- if (iter.done) break;
427
- DISCORD_THREAD_STARTER_CACHE.delete(iter.value);
428
- }
429
- }
430
- //#endregion
431
- //#region extensions/discord/src/monitor/threading.starter.ts
432
- function isDiscordThreadType(type) {
433
- return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
434
- }
435
- function isDiscordForumParentType(parentType) {
436
- return parentType === discord_exports.ChannelType.GuildForum || parentType === discord_exports.ChannelType.GuildMedia;
437
- }
438
- function resolveDiscordThreadChannel(params) {
439
- if (!params.isGuildMessage) return null;
440
- const { message, channelInfo } = params;
441
- const channel = "channel" in message ? message.channel : void 0;
442
- if (channel && typeof channel === "object" && "isThread" in channel && typeof channel.isThread === "function" && channel.isThread()) return channel;
443
- if (!isDiscordThreadType(channelInfo?.type)) return null;
444
- const messageChannelId = params.messageChannelId || resolveDiscordMessageChannelId({ message });
445
- if (!messageChannelId) return null;
446
- return {
447
- id: messageChannelId,
448
- name: channelInfo?.name ?? void 0,
449
- parentId: channelInfo?.parentId ?? void 0,
450
- parent: void 0,
451
- ownerId: channelInfo?.ownerId ?? void 0
452
- };
453
- }
454
- async function resolveDiscordThreadParentInfo(params) {
455
- const { threadChannel, channelInfo, client } = params;
456
- const parent = resolveDiscordChannelParentSafe(threadChannel);
457
- let parentId = resolveDiscordChannelParentIdSafe(threadChannel) ?? resolveDiscordChannelIdSafe(parent) ?? channelInfo?.parentId ?? void 0;
458
- if (!parentId && threadChannel.id) parentId = (await resolveDiscordChannelInfo(client, threadChannel.id))?.parentId ?? void 0;
459
- if (!parentId) return {};
460
- let parentName = resolveDiscordChannelNameSafe(parent);
461
- const parentInfo = await resolveDiscordChannelInfo(client, parentId);
462
- parentName = parentName ?? parentInfo?.name;
463
- const parentType = parentInfo?.type;
464
- return {
465
- id: parentId,
466
- name: parentName,
467
- type: parentType
468
- };
469
- }
470
- async function resolveDiscordThreadStarter(params) {
471
- const cacheKey = params.channel.id;
472
- const cached = getCachedThreadStarter(cacheKey, Date.now());
473
- if (cached) return cached;
474
- try {
475
- const messageChannelId = resolveDiscordThreadStarterMessageChannelId(params);
476
- if (!messageChannelId) return null;
477
- const starter = await fetchDiscordThreadStarterMessage({
478
- client: params.client,
479
- messageChannelId,
480
- threadId: params.channel.id
481
- });
482
- if (!starter) return null;
483
- const payload = buildDiscordThreadStarterPayload({
484
- starter,
485
- resolveTimestampMs: params.resolveTimestampMs
486
- });
487
- if (!payload) return null;
488
- setCachedThreadStarter(cacheKey, payload, Date.now());
489
- return payload;
490
- } catch {
491
- return null;
492
- }
493
- }
494
- function resolveDiscordThreadStarterMessageChannelId(params) {
495
- return isDiscordForumParentType(params.parentType) ? params.channel.id : params.parentId;
496
- }
497
- async function fetchDiscordThreadStarterMessage(params) {
498
- const starter = await getChannelMessage(params.client.rest, params.messageChannelId, params.threadId);
499
- return starter ? starter : null;
500
- }
501
- function buildDiscordThreadStarterPayload(params) {
502
- const text = resolveDiscordThreadStarterText(params.starter);
503
- if (!text) return null;
504
- return {
505
- text,
506
- ...resolveDiscordThreadStarterIdentity(params.starter),
507
- timestamp: params.resolveTimestampMs(params.starter.timestamp) ?? void 0
508
- };
509
- }
510
- function resolveDiscordThreadStarterText(starter) {
511
- const content = normalizeOptionalString(starter.content) ?? "";
512
- const embedText = resolveDiscordEmbedText(starter.embeds?.[0]);
513
- const forwardedText = resolveDiscordForwardedMessagesTextFromSnapshots(starter.message_snapshots);
514
- return [content || embedText || forwardedText, formatDiscordMediaText({
515
- attachments: starter.attachments ?? void 0,
516
- stickers: starter.sticker_items ?? void 0
517
- })].filter(Boolean).join("\n");
518
- }
519
- function resolveDiscordThreadStarterIdentity(starter) {
520
- return {
521
- author: resolveDiscordThreadStarterAuthor(starter),
522
- authorId: starter.author?.id ?? void 0,
523
- authorName: starter.author?.username ?? void 0,
524
- authorTag: resolveDiscordThreadStarterAuthorTag(starter.author),
525
- memberRoleIds: resolveDiscordThreadStarterRoleIds(starter.member)
526
- };
527
- }
528
- function resolveDiscordThreadStarterAuthor(starter) {
529
- return starter.member?.nick ?? starter.member?.displayName ?? resolveDiscordThreadStarterAuthorTag(starter.author) ?? starter.author?.username ?? starter.author?.id ?? "Unknown";
530
- }
531
- function resolveDiscordThreadStarterAuthorTag(author) {
532
- if (!author?.username || !author.discriminator) return;
533
- if (author.discriminator !== "0") return `${author.username}#${author.discriminator}`;
534
- return author.username;
535
- }
536
- function resolveDiscordThreadStarterRoleIds(member) {
537
- return Array.isArray(member?.roles) ? member.roles : void 0;
538
- }
539
- function resolveDiscordReplyTarget(opts) {
540
- if (opts.replyToMode === "off") return;
541
- const replyToId = normalizeOptionalString(opts.replyToId);
542
- if (!replyToId) return;
543
- if (opts.replyToMode === "all") return replyToId;
544
- return opts.hasReplied ? void 0 : replyToId;
545
- }
546
- function sanitizeDiscordThreadName(rawName, fallbackId) {
547
- return truncateUtf16Safe(truncateUtf16Safe(rawName.replace(/<@!?\d+>/g, "").replace(/<@&\d+>/g, "").replace(/<#\d+>/g, "").replace(/\s+/g, " ").trim() || `Thread ${fallbackId}`, 80), 100) || `Thread ${fallbackId}`;
548
- }
549
- function resolveDiscordReplyDeliveryPlan(params) {
550
- const originalReplyTarget = params.replyTarget;
551
- let deliverTarget = originalReplyTarget;
552
- let replyTarget = originalReplyTarget;
553
- if (params.createdThreadId) {
554
- deliverTarget = `channel:${params.createdThreadId}`;
555
- replyTarget = deliverTarget;
556
- }
557
- const allowReference = deliverTarget === originalReplyTarget;
558
- const replyReference = createReplyReferencePlanner({
559
- replyToMode: allowReference ? params.replyToMode : "off",
560
- existingId: params.threadChannel ? params.messageId : void 0,
561
- startId: params.messageId,
562
- allowReference
563
- });
564
- return {
565
- deliverTarget,
566
- replyTarget,
567
- replyReference
568
- };
569
- }
570
- //#endregion
571
- //#region extensions/discord/src/monitor/threading.auto-thread.ts
572
- function resolveTrimmedDiscordMessageChannelId(params) {
573
- return (params.messageChannelId || resolveDiscordMessageChannelId({ message: params.message })).trim();
574
- }
575
- function resolveDiscordAutoThreadContext(params) {
576
- const createdThreadId = normalizeOptionalStringifiedId(params.createdThreadId) ?? "";
577
- if (!createdThreadId) return null;
578
- const messageChannelId = normalizeOptionalString(params.messageChannelId) ?? "";
579
- if (!messageChannelId) return null;
580
- const threadSessionKey = buildAgentSessionKey({
581
- agentId: params.agentId,
582
- channel: params.channel,
583
- peer: {
584
- kind: "channel",
585
- id: createdThreadId
586
- }
587
- });
588
- const parentSessionKey = buildAgentSessionKey({
589
- agentId: params.agentId,
590
- channel: params.channel,
591
- peer: {
592
- kind: "channel",
593
- id: messageChannelId
594
- }
595
- });
596
- return {
597
- createdThreadId,
598
- From: `${params.channel}:channel:${createdThreadId}`,
599
- To: `channel:${createdThreadId}`,
600
- OriginatingTo: `channel:${createdThreadId}`,
601
- SessionKey: threadSessionKey,
602
- ModelParentSessionKey: parentSessionKey,
603
- ...params.parentInheritanceEnabled === true ? { ParentSessionKey: parentSessionKey } : {}
604
- };
605
- }
606
- async function resolveDiscordAutoThreadReplyPlan(params) {
607
- const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
608
- const originalReplyTarget = `channel:${params.threadChannel?.id ?? (messageChannelId || "unknown")}`;
609
- const createdThreadId = await maybeCreateDiscordAutoThread({
610
- client: params.client,
611
- message: params.message,
612
- messageChannelId: messageChannelId || void 0,
613
- channel: params.channel,
614
- isGuildMessage: params.isGuildMessage,
615
- channelConfig: params.channelConfig,
616
- threadChannel: params.threadChannel,
617
- channelType: params.channelType,
618
- channelName: params.channelName,
619
- channelDescription: params.channelDescription,
620
- baseText: params.baseText,
621
- combinedBody: params.combinedBody,
622
- cfg: params.cfg,
623
- agentId: params.agentId
624
- });
625
- const deliveryPlan = resolveDiscordReplyDeliveryPlan({
626
- replyTarget: originalReplyTarget,
627
- replyToMode: params.replyToMode,
628
- messageId: params.message.id,
629
- threadChannel: params.threadChannel,
630
- createdThreadId
631
- });
632
- const autoThreadContext = params.isGuildMessage ? resolveDiscordAutoThreadContext({
633
- agentId: params.agentId,
634
- channel: params.channel,
635
- messageChannelId,
636
- createdThreadId,
637
- parentInheritanceEnabled: params.threadParentInheritanceEnabled
638
- }) : null;
639
- return {
640
- ...deliveryPlan,
641
- createdThreadId,
642
- autoThreadContext
643
- };
644
- }
645
- async function maybeCreateDiscordAutoThread(params) {
646
- if (!params.isGuildMessage) return;
647
- if (!params.channelConfig?.autoThread) return;
648
- if (params.threadChannel) return;
649
- 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;
650
- const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
651
- if (!messageChannelId) return;
652
- try {
653
- try {
654
- const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id;
655
- if (existingThreadId) {
656
- logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
657
- return existingThreadId;
658
- }
659
- } catch {}
660
- if (params.message.author?.bot) {
661
- logVerbose(`discord: autoThread skipped for bot-authored message ${messageChannelId}/${params.message.id}`);
662
- return;
663
- }
664
- const rawThreadSource = params.baseText || params.combinedBody || "Thread";
665
- const threadName = sanitizeDiscordThreadName(rawThreadSource, params.message.id);
666
- const archiveDuration = params.channelConfig?.autoArchiveDuration ? Number(params.channelConfig.autoArchiveDuration) : 60;
667
- const createdId = (await createThread(params.client.rest, messageChannelId, { body: {
668
- name: threadName,
669
- auto_archive_duration: archiveDuration
670
- } }, params.message.id))?.id || "";
671
- if (createdId && params.channelConfig?.autoThreadName === "generated" && params.cfg && params.agentId) {
672
- const modelRef = resolveDiscordThreadTitleModelRef({
673
- cfg: params.cfg,
674
- channel: params.channel,
675
- agentId: params.agentId,
676
- threadId: createdId,
677
- messageChannelId,
678
- channelName: params.channelName
679
- });
680
- maybeRenameDiscordAutoThread({
681
- client: params.client,
682
- threadId: createdId,
683
- currentName: threadName,
684
- fallbackId: params.message.id,
685
- sourceText: rawThreadSource,
686
- modelRef,
687
- channelName: params.channelName,
688
- channelDescription: params.channelDescription,
689
- cfg: params.cfg,
690
- agentId: params.agentId
691
- });
692
- }
693
- return createdId || void 0;
694
- } catch (err) {
695
- logVerbose(`discord: autoThread creation failed for ${messageChannelId}/${params.message.id}: ${String(err)}`);
696
- try {
697
- const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id || "";
698
- if (existingThreadId) {
699
- logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
700
- return existingThreadId;
701
- }
702
- } catch {}
703
- return;
704
- }
705
- }
706
- function resolveDiscordThreadTitleModelRef(params) {
707
- const channel = params.channel?.trim();
708
- if (!channel) return;
709
- const parentSessionKey = buildAgentSessionKey({
710
- agentId: params.agentId,
711
- channel,
712
- peer: {
713
- kind: "channel",
714
- id: params.messageChannelId
715
- }
716
- });
717
- const channelLabel = params.channelName?.trim();
718
- const groupChannel = channelLabel ? `#${channelLabel}` : void 0;
719
- return resolveChannelModelOverride({
720
- cfg: params.cfg,
721
- channel,
722
- groupId: params.threadId,
723
- groupChatType: "channel",
724
- groupChannel,
725
- groupSubject: groupChannel,
726
- parentSessionKey
727
- })?.model;
728
- }
729
- async function maybeRenameDiscordAutoThread(params) {
730
- try {
731
- const fallbackName = sanitizeDiscordThreadName("", params.fallbackId);
732
- const generated = await generateThreadTitle({
733
- cfg: params.cfg,
734
- agentId: params.agentId,
735
- messageText: params.sourceText,
736
- modelRef: params.modelRef,
737
- channelName: params.channelName,
738
- channelDescription: params.channelDescription
739
- });
740
- if (!generated) return;
741
- const nextName = sanitizeDiscordThreadName(generated, params.fallbackId);
742
- if (!nextName || nextName === params.currentName || nextName === fallbackName) return;
743
- await editChannel(params.client.rest, params.threadId, { body: { name: nextName } });
744
- } catch (err) {
745
- logVerbose(`discord: autoThread rename failed for ${params.threadId}: ${String(err)}`);
746
- }
747
- }
748
- //#endregion
749
- //#region extensions/discord/src/monitor/threading.ts
750
- var threading_exports = /* @__PURE__ */ __exportAll({
751
- maybeCreateDiscordAutoThread: () => maybeCreateDiscordAutoThread,
752
- resetDiscordThreadStarterCacheForTest: () => resetDiscordThreadStarterCacheForTest,
753
- resolveDiscordAutoThreadContext: () => resolveDiscordAutoThreadContext,
754
- resolveDiscordAutoThreadReplyPlan: () => resolveDiscordAutoThreadReplyPlan,
755
- resolveDiscordReplyDeliveryPlan: () => resolveDiscordReplyDeliveryPlan,
756
- resolveDiscordReplyTarget: () => resolveDiscordReplyTarget,
757
- resolveDiscordThreadChannel: () => resolveDiscordThreadChannel,
758
- resolveDiscordThreadParentInfo: () => resolveDiscordThreadParentInfo,
759
- resolveDiscordThreadStarter: () => resolveDiscordThreadStarter,
760
- sanitizeDiscordThreadName: () => sanitizeDiscordThreadName
761
- });
762
- //#endregion
763
507
  //#region extensions/discord/src/monitor/thread-channel-context.ts
764
508
  function isDiscordThreadChannelType(type) {
765
509
  return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
@@ -1131,264 +875,65 @@ async function handleDiscordReactionEvent(params) {
1131
875
  const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
1132
876
  emitReaction(text, parentId);
1133
877
  };
1134
- const resolveThreadChannelConfig = () => resolveDiscordChannelConfigWithFallback({
1135
- guildInfo,
1136
- channelId: data.channel_id,
1137
- channelName,
1138
- channelSlug,
1139
- parentId,
1140
- parentName,
1141
- parentSlug,
1142
- scope: "thread"
1143
- });
1144
- const authorizeReactionIngressForChannel = async (channelConfig) => await authorizeDiscordReactionIngress({
1145
- ...reactionIngressBase,
1146
- channelConfig
1147
- });
1148
- const resolveThreadChannelAccess = async () => {
1149
- const channelConfig = resolveThreadChannelConfig();
1150
- return {
1151
- access: await authorizeReactionIngressForChannel(channelConfig),
1152
- channelConfig
1153
- };
1154
- };
1155
- if (isThreadChannel) {
1156
- await handleDiscordThreadReactionNotification({
1157
- reactionMode,
1158
- message: data.message,
1159
- parentId,
1160
- resolveThreadChannelAccess,
1161
- shouldNotifyReaction,
1162
- resolveReactionBase,
1163
- emitReaction,
1164
- emitReactionWithAuthor
1165
- });
1166
- return;
1167
- }
1168
- const channelConfig = resolveDiscordChannelConfigWithFallback({
1169
- guildInfo,
1170
- channelId: data.channel_id,
1171
- channelName,
1172
- channelSlug,
1173
- parentId,
1174
- parentName,
1175
- parentSlug,
1176
- scope: "channel"
1177
- });
1178
- await handleDiscordChannelReactionNotification({
1179
- isGuildMessage,
1180
- reactionMode,
1181
- message: data.message,
1182
- channelConfig,
1183
- parentId,
1184
- authorizeReactionIngressForChannel,
1185
- shouldNotifyReaction,
1186
- resolveReactionBase,
1187
- emitReaction,
1188
- emitReactionWithAuthor
1189
- });
1190
- } catch (err) {
1191
- params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
1192
- }
1193
- }
1194
- //#endregion
1195
- //#region extensions/discord/src/monitor/presence-events.ts
1196
- const DISCORD_PRESENCE_GREETING_COOLDOWN_MS = 480 * 60 * 1e3;
1197
- function isDiscordOnlineStatus(status) {
1198
- return status === "online" || status === "idle" || status === "dnd";
1199
- }
1200
- function isDiscordOfflineStatus(status) {
1201
- return status === "offline";
1202
- }
1203
- function resolveDiscordOnlinePresenceEvent(params) {
1204
- const config = params.config;
1205
- const userId = params.data.user?.id?.trim();
1206
- if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
1207
- if (config.users !== void 0 && !config.users.includes(userId)) return null;
1208
- if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
1209
- const lines = [
1210
- "Discord online-presence event:",
1211
- 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.`,
1212
- `The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
1213
- "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.",
1214
- "Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
1215
- ];
1216
- return {
1217
- channelId: config.channelId,
1218
- userId,
1219
- text: lines.join("\n")
1220
- };
1221
- }
1222
- //#endregion
1223
- //#region extensions/discord/src/monitor/presence-cooldown-store.ts
1224
- const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
1225
- function openDiscordPresenceCooldownStore() {
1226
- return getDiscordRuntime().state.openSyncKeyedStore({
1227
- namespace: "presence-greeting-cooldowns",
1228
- maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
1229
- overflowPolicy: "reject-new",
1230
- defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
1231
- });
1232
- }
1233
- //#endregion
1234
- //#region extensions/discord/src/monitor/presence-emission-gate.ts
1235
- const DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS = 300 * 1e3;
1236
- const DISCORD_PRESENCE_BURST_LIMIT = 8;
1237
- const DISCORD_PRESENCE_BURST_WINDOW_MS = 60 * 1e3;
1238
- function resolveDiscordPresenceGateOptions(config) {
1239
- return {
1240
- reconnectSuppressMs: config?.reconnectSuppressSeconds !== void 0 ? config.reconnectSuppressSeconds * 1e3 : DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS,
1241
- burstLimit: config?.burstLimit ?? DISCORD_PRESENCE_BURST_LIMIT,
1242
- burstWindowMs: config?.burstWindowSeconds !== void 0 ? config.burstWindowSeconds * 1e3 : DISCORD_PRESENCE_BURST_WINDOW_MS
1243
- };
1244
- }
1245
- /**
1246
- * Per-account lifecycle gate for online-presence events. Reconnect state belongs to the Gateway
1247
- * session, while burst state follows the guild-owned configuration that supplies its limits.
1248
- */
1249
- var DiscordPresenceEmissionGate = class {
1250
- constructor() {
1251
- this.reconnectLogged = false;
1252
- this.burstByGuild = /* @__PURE__ */ new Map();
1253
- this.nextReservationId = 0;
1254
- }
1255
- noteGatewaySessionReset(nowMs) {
1256
- this.lastSessionResetAtMs = nowMs;
1257
- this.reconnectLogged = false;
1258
- }
1259
- evaluateReconnectWindow(nowMs, options) {
1260
- if (this.lastSessionResetAtMs !== void 0 && options.reconnectSuppressMs > 0 && nowMs - this.lastSessionResetAtMs < options.reconnectSuppressMs) {
1261
- const shouldLog = !this.reconnectLogged;
1262
- this.reconnectLogged = true;
1263
- return {
1264
- allowed: false,
1265
- reason: "reconnect-window",
1266
- shouldLog
1267
- };
1268
- }
1269
- return { allowed: true };
1270
- }
1271
- reserveBurst(guildId, nowMs, options) {
1272
- const state = this.burstByGuild.get(guildId) ?? {
1273
- reservations: [],
1274
- logged: false
1275
- };
1276
- state.reservations = state.reservations.filter((reservation) => !reservation.committed || nowMs - reservation.atMs < options.burstWindowMs);
1277
- this.burstByGuild.set(guildId, state);
1278
- if (state.reservations.length >= options.burstLimit) {
1279
- const shouldLog = !state.logged;
1280
- state.logged = true;
878
+ const resolveThreadChannelConfig = () => resolveDiscordChannelConfigWithFallback({
879
+ guildInfo,
880
+ channelId: data.channel_id,
881
+ channelName,
882
+ channelSlug,
883
+ parentId,
884
+ parentName,
885
+ parentSlug,
886
+ scope: "thread"
887
+ });
888
+ const authorizeReactionIngressForChannel = async (channelConfig) => await authorizeDiscordReactionIngress({
889
+ ...reactionIngressBase,
890
+ channelConfig
891
+ });
892
+ const resolveThreadChannelAccess = async () => {
893
+ const channelConfig = resolveThreadChannelConfig();
1281
894
  return {
1282
- allowed: false,
1283
- reason: state.reservations.some((reservation) => !reservation.committed) ? "burst-pending" : "burst",
1284
- shouldLog
895
+ access: await authorizeReactionIngressForChannel(channelConfig),
896
+ channelConfig
1285
897
  };
1286
- }
1287
- state.logged = false;
1288
- const reservation = {
1289
- id: this.nextReservationId++,
1290
- atMs: nowMs,
1291
- committed: false
1292
- };
1293
- state.reservations.push(reservation);
1294
- return {
1295
- allowed: true,
1296
- reservation: reservation.id
1297
898
  };
1298
- }
1299
- commitBurst(guildId, reservation, nowMs) {
1300
- const entry = this.burstByGuild.get(guildId)?.reservations.find((candidate) => candidate.id === reservation);
1301
- if (!entry) return;
1302
- entry.atMs = nowMs;
1303
- entry.committed = true;
1304
- }
1305
- releaseBurst(guildId, reservation) {
1306
- const state = this.burstByGuild.get(guildId);
1307
- if (!state) return;
1308
- state.reservations = state.reservations.filter((candidate) => candidate.id !== reservation);
1309
- if (state.reservations.length === 0 && !state.logged) this.burstByGuild.delete(guildId);
1310
- }
1311
- };
1312
- //#endregion
1313
- //#region extensions/discord/src/monitor/presence-transition-cache.ts
1314
- const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
1315
- var DiscordPresenceBaselineCache = class {
1316
- constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
1317
- this.maxEntries = maxEntries;
1318
- this.offlineByKey = /* @__PURE__ */ new Map();
1319
- this.onlineByKey = /* @__PURE__ */ new Map();
1320
- }
1321
- clear() {
1322
- this.offlineByKey.clear();
1323
- this.onlineByKey.clear();
1324
- }
1325
- clearScope(scope) {
1326
- for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
1327
- }
1328
- isOffline(scope, key) {
1329
- return this.offlineByKey.get(key) === scope;
1330
- }
1331
- isOnline(scope, key) {
1332
- return this.onlineByKey.get(key) === scope;
1333
- }
1334
- observeOffline(scope, key) {
1335
- this.deleteMarker(this.onlineByKey, scope, key);
1336
- return this.observe(this.offlineByKey, scope, key);
1337
- }
1338
- observeOnline(scope, key) {
1339
- this.deleteMarker(this.offlineByKey, scope, key);
1340
- return this.observe(this.onlineByKey, scope, key);
1341
- }
1342
- deleteMarker(markers, scope, key) {
1343
- if (markers.get(key) === scope) markers.delete(key);
1344
- }
1345
- observe(markers, scope, key) {
1346
- markers.delete(key);
1347
- markers.set(key, scope);
1348
- let evictedScope;
1349
- while (markers.size > this.maxEntries) {
1350
- const oldestKey = markers.keys().next().value;
1351
- if (oldestKey === void 0) break;
1352
- evictedScope = markers.get(oldestKey);
1353
- markers.delete(oldestKey);
1354
- }
1355
- return evictedScope;
1356
- }
1357
- };
1358
- //#endregion
1359
- //#region extensions/discord/src/monitor/thread-session-close.ts
1360
- /**
1361
- * Closes every session entry in the store whose key contains {@link threadId}.
1362
- * The explicit lifecycle deletion archives the old transcript and guarantees
1363
- * that a later inbound message starts a fresh session in every reset mode.
1364
- */
1365
- async function closeDiscordThreadSessions(params) {
1366
- const { cfg, threadId } = params;
1367
- const normalizedThreadId = normalizeOptionalLowercaseString(threadId) ?? "";
1368
- if (!normalizedThreadId) return 0;
1369
- const segmentRe = new RegExp(`:${normalizedThreadId}(?::|$)`, "i");
1370
- function sessionKeyContainsThreadId(key) {
1371
- return segmentRe.test(key);
1372
- }
1373
- let resetCount = 0;
1374
- for (const agentId of listAgentIds(cfg)) {
1375
- const storePath = resolveStorePath(cfg.session?.store, { agentId });
1376
- for (const { sessionKey, entry } of listSessionEntries({
1377
- agentId,
1378
- storePath,
1379
- readOnly: true
1380
- })) {
1381
- if (!sessionKeyContainsThreadId(sessionKey)) continue;
1382
- if (await deleteSessionEntry({
1383
- archiveTranscript: true,
1384
- expectedSessionId: entry.sessionId ?? null,
1385
- expectedUpdatedAt: entry.updatedAt,
1386
- sessionKey,
1387
- storePath
1388
- })) resetCount += 1;
899
+ if (isThreadChannel) {
900
+ await handleDiscordThreadReactionNotification({
901
+ reactionMode,
902
+ message: data.message,
903
+ parentId,
904
+ resolveThreadChannelAccess,
905
+ shouldNotifyReaction,
906
+ resolveReactionBase,
907
+ emitReaction,
908
+ emitReactionWithAuthor
909
+ });
910
+ return;
1389
911
  }
912
+ const channelConfig = resolveDiscordChannelConfigWithFallback({
913
+ guildInfo,
914
+ channelId: data.channel_id,
915
+ channelName,
916
+ channelSlug,
917
+ parentId,
918
+ parentName,
919
+ parentSlug,
920
+ scope: "channel"
921
+ });
922
+ await handleDiscordChannelReactionNotification({
923
+ isGuildMessage,
924
+ reactionMode,
925
+ message: data.message,
926
+ channelConfig,
927
+ parentId,
928
+ authorizeReactionIngressForChannel,
929
+ shouldNotifyReaction,
930
+ resolveReactionBase,
931
+ emitReaction,
932
+ emitReactionWithAuthor
933
+ });
934
+ } catch (err) {
935
+ params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
1390
936
  }
1391
- return resetCount;
1392
937
  }
1393
938
  //#endregion
1394
939
  //#region extensions/discord/src/monitor/listeners.ts
@@ -1815,11 +1360,16 @@ function isDiscordUnknownInteraction(error) {
1815
1360
  if (/Unknown interaction/i.test(err.rawBody?.message ?? "")) return true;
1816
1361
  return false;
1817
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
+ }
1818
1370
  function hasRenderableReplyPayload(payload) {
1819
- if (resolveSendableOutboundReplyParts(payload).hasContent) return true;
1820
- const discordData = payload.channelData?.discord;
1821
- if (Array.isArray(discordData?.components) && discordData.components.length > 0) return true;
1822
- return false;
1371
+ const { components, embeds } = resolveDiscordInteractionMessageParts(payload);
1372
+ return resolveSendableOutboundReplyParts(payload).hasContent || Boolean(components || embeds);
1823
1373
  }
1824
1374
  async function safeDiscordInteractionCall(label, fn) {
1825
1375
  try {
@@ -1839,33 +1389,25 @@ async function settleDiscordInteractionWithoutVisibleReply(interaction) {
1839
1389
  async function deliverDiscordInteractionReply(params) {
1840
1390
  const { interaction, payload, textLimit, maxLinesPerMessage, preferFollowUp, chunkMode } = params;
1841
1391
  const reply = resolveSendableOutboundReplyParts(payload);
1842
- const discordData = payload.channelData?.discord;
1843
- let firstMessageComponents = Array.isArray(discordData?.components) && discordData.components.length > 0 ? discordData.components : void 0;
1392
+ let { components: firstMessageComponents, embeds: firstMessageEmbeds } = resolveDiscordInteractionMessageParts(payload);
1844
1393
  let payloadDelivered = false;
1845
- const sendMessage = async (content, files, components) => {
1846
- const contentPayload = content ? { content } : {};
1847
- const payloadLocal = files && files.length > 0 ? {
1848
- ...contentPayload,
1394
+ const sendMessage = async (content, files, components, embeds) => {
1395
+ const hasV2 = hasDiscordV2Components(components);
1396
+ const payloadLocal = {
1397
+ ...content && !hasV2 ? { content } : {},
1849
1398
  ...components ? { components } : {},
1399
+ ...embeds && !hasV2 ? { embeds } : {},
1850
1400
  ...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {},
1851
- files
1852
- } : {
1853
- ...contentPayload,
1854
- ...components ? { components } : {},
1855
- ...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {}
1401
+ ...files?.length ? { files } : {}
1856
1402
  };
1857
1403
  let result;
1858
1404
  try {
1859
1405
  result = await safeDiscordInteractionCall("interaction send", async () => {
1860
- if (!preferFollowUp && !payloadDelivered) {
1861
- await interaction.reply(payloadLocal);
1862
- payloadDelivered = true;
1863
- firstMessageComponents = void 0;
1864
- return;
1865
- }
1866
- await interaction.followUp(payloadLocal);
1406
+ if (!preferFollowUp && !payloadDelivered) await interaction.reply(payloadLocal);
1407
+ else await interaction.followUp(payloadLocal);
1867
1408
  payloadDelivered = true;
1868
1409
  firstMessageComponents = void 0;
1410
+ firstMessageEmbeds = void 0;
1869
1411
  });
1870
1412
  } catch (error) {
1871
1413
  if (!payloadDelivered) throw error;
@@ -1890,99 +1432,27 @@ async function deliverDiscordInteractionReply(params) {
1890
1432
  maxLines: maxLinesPerMessage,
1891
1433
  chunkMode
1892
1434
  }));
1893
- await sendMessage(chunks[0] ?? "", media, firstMessageComponents);
1435
+ await sendMessage(chunks[0] ?? "", media, firstMessageComponents, firstMessageEmbeds);
1894
1436
  for (const chunk of chunks.slice(1)) {
1895
1437
  if (!chunk.trim()) continue;
1896
1438
  await sendMessage(chunk);
1897
1439
  }
1898
1440
  return payloadDelivered;
1899
1441
  }
1900
- if (!reply.hasText && !firstMessageComponents) return false;
1901
- let chunks = reply.text || firstMessageComponents ? resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
1442
+ if (!reply.hasText && !firstMessageComponents && !firstMessageEmbeds) return false;
1443
+ const chunks = resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
1902
1444
  maxChars: textLimit,
1903
1445
  maxLines: maxLinesPerMessage,
1904
1446
  chunkMode
1905
- })) : [];
1906
- if (chunks.length === 0 && firstMessageComponents) chunks = [""];
1447
+ }));
1448
+ if (chunks.length === 0) chunks.push("");
1907
1449
  for (const chunk of chunks) {
1908
- if (!chunk.trim() && !firstMessageComponents) continue;
1909
- await sendMessage(chunk, void 0, firstMessageComponents);
1450
+ if (!chunk.trim() && !firstMessageComponents && !firstMessageEmbeds) continue;
1451
+ await sendMessage(chunk, void 0, firstMessageComponents, firstMessageEmbeds);
1910
1452
  }
1911
1453
  return payloadDelivered;
1912
1454
  }
1913
1455
  //#endregion
1914
- //#region extensions/discord/src/monitor/route-resolution.ts
1915
- function buildDiscordRoutePeer(params) {
1916
- return {
1917
- kind: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
1918
- id: params.isDirectMessage ? params.directUserId?.trim() || params.conversationId : params.conversationId
1919
- };
1920
- }
1921
- function resolveDiscordConversationRoute(params) {
1922
- return resolveAgentRoute({
1923
- cfg: params.cfg,
1924
- channel: "discord",
1925
- accountId: params.accountId,
1926
- guildId: params.guildId ?? void 0,
1927
- memberRoleIds: params.memberRoleIds,
1928
- peer: params.peer,
1929
- parentPeer: params.parentConversationId ? {
1930
- kind: "channel",
1931
- id: params.parentConversationId
1932
- } : void 0
1933
- });
1934
- }
1935
- function resolveDiscordBoundConversationRoute(params) {
1936
- return resolveDiscordEffectiveRoute({
1937
- route: resolveDiscordConversationRoute({
1938
- cfg: params.cfg,
1939
- accountId: params.accountId,
1940
- guildId: params.guildId,
1941
- memberRoleIds: params.memberRoleIds,
1942
- peer: buildDiscordRoutePeer({
1943
- isDirectMessage: params.isDirectMessage,
1944
- isGroupDm: params.isGroupDm,
1945
- directUserId: params.directUserId,
1946
- conversationId: params.conversationId
1947
- }),
1948
- parentConversationId: params.parentConversationId
1949
- }),
1950
- boundSessionKey: params.boundSessionKey,
1951
- configuredRoute: params.configuredRoute,
1952
- matchedBy: params.matchedBy
1953
- });
1954
- }
1955
- function resolveDiscordEffectiveRoute(params) {
1956
- const boundSessionKey = params.boundSessionKey?.trim();
1957
- if (!boundSessionKey) return params.configuredRoute?.route ?? params.route;
1958
- return {
1959
- ...params.route,
1960
- sessionKey: boundSessionKey,
1961
- agentId: resolveAgentIdFromSessionKey(boundSessionKey),
1962
- lastRoutePolicy: deriveLastRoutePolicy({
1963
- sessionKey: boundSessionKey,
1964
- mainSessionKey: params.route.mainSessionKey
1965
- }),
1966
- ...params.matchedBy ? { matchedBy: params.matchedBy } : {}
1967
- };
1968
- }
1969
- function hasExplicitRuntimeBindingIntent(record) {
1970
- if (record.targetKind === "subagent") return true;
1971
- if (isAcpSessionKey(record.targetSessionKey) || isSubagentSessionKey(record.targetSessionKey)) return true;
1972
- const metadata = record.metadata;
1973
- if (!metadata || typeof metadata !== "object") return false;
1974
- return typeof metadata.boundBy === "string" || typeof metadata.label === "string" || typeof metadata.threadName === "string" || metadata.pluginBindingOwner === "plugin";
1975
- }
1976
- function shouldIgnoreStaleDiscordRouteBinding(params) {
1977
- const bindingRecord = params.bindingRecord;
1978
- const boundSessionKey = bindingRecord?.targetSessionKey?.trim();
1979
- if (!bindingRecord || !boundSessionKey || hasExplicitRuntimeBindingIntent(bindingRecord)) return false;
1980
- const bound = parseAgentSessionKey(boundSessionKey);
1981
- const routed = parseAgentSessionKey(params.route.sessionKey);
1982
- if (!bound || !routed || bound.rest !== routed.rest) return false;
1983
- return bound.agentId !== params.route.agentId;
1984
- }
1985
- //#endregion
1986
1456
  //#region extensions/discord/src/monitor/native-command-route.ts
1987
1457
  async function resolveDiscordNativeInteractionRouteState(params) {
1988
1458
  const route = resolveDiscordBoundConversationRoute({
@@ -2398,11 +1868,11 @@ async function resolveDiscordNativeAutocompleteAuthorized(params) {
2398
1868
  //#endregion
2399
1869
  //#region extensions/discord/src/monitor/native-command-bypass.ts
2400
1870
  function shouldBypassConfiguredAcpEnsure(commandName) {
2401
- return normalizeLowercaseStringOrEmpty(commandName) === "acp";
1871
+ return commandName.trim().toLowerCase() === "acp";
2402
1872
  }
2403
1873
  function shouldBypassConfiguredAcpGuildGuards(commandName) {
2404
- const normalized = normalizeLowercaseStringOrEmpty(commandName);
2405
- return normalized === "new" || normalized === "reset";
1874
+ const command = commandName.trim().toLowerCase();
1875
+ return command === "new" || command === "reset";
2406
1876
  }
2407
1877
  //#endregion
2408
1878
  //#region extensions/discord/src/monitor/inbound-context.ts
@@ -2481,6 +1951,14 @@ function buildDiscordNativeCommandContext(params) {
2481
1951
  CommandTargetSessionKey: params.commandTargetSessionKey,
2482
1952
  AccountId: params.accountId ?? void 0,
2483
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
+ }),
2484
1962
  ConversationLabel: conversationLabel,
2485
1963
  GroupSubject: params.isGuild ? params.guildName : void 0,
2486
1964
  GroupSpace: params.isGuild ? params.guildInfo?.id ?? params.guildInfo?.slug ?? params.guildId : void 0,
@@ -2496,7 +1974,6 @@ function buildDiscordNativeCommandContext(params) {
2496
1974
  Surface: "discord",
2497
1975
  WasMentioned: true,
2498
1976
  MessageSid: params.interactionId,
2499
- MessageThreadId: params.isThreadChannel ? params.channelId : void 0,
2500
1977
  Timestamp: params.timestampMs ?? Date.now(),
2501
1978
  CommandAuthorized: params.commandAuthorized,
2502
1979
  CommandTurn: {
@@ -2511,8 +1988,7 @@ function buildDiscordNativeCommandContext(params) {
2511
1988
  isDirectMessage: params.isDirectMessage,
2512
1989
  userId: params.user.id,
2513
1990
  channelId: params.channelId
2514
- }) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`),
2515
- ThreadParentId: params.isThreadChannel ? params.threadParentId : void 0
1991
+ }) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`)
2516
1992
  });
2517
1993
  }
2518
1994
  //#endregion
@@ -4446,7 +3922,7 @@ const DISCORD_COMMAND_DESCRIPTION_MAX = 100;
4446
3922
  function truncateDiscordCommandDescription(params) {
4447
3923
  const { value, label } = params;
4448
3924
  if (value.length <= DISCORD_COMMAND_DESCRIPTION_MAX) return value;
4449
- log$1.warn(`discord: truncating native command description (${label}) from ${value.length} to ${DISCORD_COMMAND_DESCRIPTION_MAX}: ${JSON.stringify(value)}`);
3925
+ log$1.debug(`discord: truncating native command description (${label}) from ${value.length} to ${DISCORD_COMMAND_DESCRIPTION_MAX}: ${JSON.stringify(value)}`);
4450
3926
  return truncateUtf16Safe(value, DISCORD_COMMAND_DESCRIPTION_MAX);
4451
3927
  }
4452
3928
  function truncateDiscordCommandDescriptionLocalizations(params) {
@@ -5097,7 +4573,7 @@ function canResumeAfterGatewayClose(code) {
5097
4573
  //#region extensions/discord/src/internal/gateway-identify-limiter.ts
5098
4574
  const IDENTIFY_WINDOW_MS = 5e3;
5099
4575
  function normalizeMaxConcurrency(value) {
5100
- const parsed = parseFiniteNumber$1(value);
4576
+ const parsed = parseFiniteNumber(value);
5101
4577
  return parsed === void 0 ? 1 : Math.max(1, Math.floor(parsed));
5102
4578
  }
5103
4579
  var GatewayIdentifyLimiter = class {
@@ -5305,7 +4781,7 @@ var DiscordGatewayVoiceStateCache = class {
5305
4781
  }
5306
4782
  listVoiceChannelStates(guildId, channelId) {
5307
4783
  const states = this.statesByGuild.get(guildId);
5308
- if (!states) return [];
4784
+ if (!states) return null;
5309
4785
  const result = [];
5310
4786
  for (const state of states.values()) if (state.channel_id === channelId) result.push({ ...state });
5311
4787
  return result;
@@ -5518,7 +4994,10 @@ var GatewayPlugin = class extends Plugin {
5518
4994
  if (payload.s !== null && payload.s !== void 0) this.sequence = payload.s;
5519
4995
  switch (payload.op) {
5520
4996
  case GatewayOpcodes.Hello: {
5521
- this.startHeartbeat(payload.d.heartbeat_interval ?? 45e3);
4997
+ this.startHeartbeat(asSafeIntegerInRange(asOptionalRecord(payload.d)?.heartbeat_interval, {
4998
+ min: 1,
4999
+ max: MAX_TIMER_TIMEOUT_MS
5000
+ }) ?? 45e3);
5522
5001
  const resumeState = resume ? this.getResumeState() : null;
5523
5002
  if (resumeState) this.send({
5524
5003
  op: GatewayOpcodes.Resume,
@@ -5721,12 +5200,6 @@ var GatewayPlugin = class extends Plugin {
5721
5200
  }
5722
5201
  };
5723
5202
  //#endregion
5724
- //#region extensions/discord/src/voice/config.ts
5725
- function resolveDiscordVoiceEnabled(voice) {
5726
- if (voice?.enabled !== void 0) return voice.enabled;
5727
- return voice !== void 0;
5728
- }
5729
- //#endregion
5730
5203
  //#region extensions/discord/src/monitor/presence.ts
5731
5204
  const DEFAULT_CUSTOM_ACTIVITY_TYPE$1 = 4;
5732
5205
  const CUSTOM_STATUS_NAME$1 = "Custom Status";
@@ -7045,7 +6518,7 @@ async function runDiscordGatewayLifecycle(params) {
7045
6518
  gatewayEmitter?.removeListener(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, onGatewayTransportActivity);
7046
6519
  if (params.voiceManager) {
7047
6520
  await params.voiceManager.destroy();
7048
- const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CfFMexGt.js").then((n) => n.n);
6521
+ const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-DeIMNbRh.js").then((n) => n.n);
7049
6522
  setDiscordTranscriptsVoiceManager({
7050
6523
  accountId: params.accountId,
7051
6524
  manager: null
@@ -7060,7 +6533,7 @@ async function runDiscordGatewayLifecycle(params) {
7060
6533
  let discordVoiceRuntimePromise;
7061
6534
  let discordProviderSessionRuntimePromise;
7062
6535
  async function loadDiscordVoiceRuntime() {
7063
- const promise = discordVoiceRuntimePromise ?? import("./voice-runtime-BEdqNcST.js");
6536
+ const promise = discordVoiceRuntimePromise ?? import("./voice-runtime-CjNFaynA.js");
7064
6537
  discordVoiceRuntimePromise = promise;
7065
6538
  try {
7066
6539
  return await promise;
@@ -7070,7 +6543,7 @@ async function loadDiscordVoiceRuntime() {
7070
6543
  }
7071
6544
  }
7072
6545
  async function loadDiscordProviderSessionRuntime() {
7073
- const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-BUEqD0rA.js");
6546
+ const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-CvvphHvs.js");
7074
6547
  discordProviderSessionRuntimePromise = promise;
7075
6548
  try {
7076
6549
  return await promise;
@@ -7431,102 +6904,6 @@ async function cleanupDiscordProviderStartup(params) {
7431
6904
  if (!params.lifecycleStarted) params.threadBindings.stop();
7432
6905
  }
7433
6906
  //#endregion
7434
- //#region extensions/discord/src/voice/access.ts
7435
- async function authorizeDiscordVoiceIngress(params) {
7436
- const groupPolicy = params.groupPolicy ?? resolveOpenProviderRuntimeGroupPolicy({
7437
- providerConfigPresent: params.cfg.channels?.discord !== void 0,
7438
- groupPolicy: params.discordConfig.groupPolicy,
7439
- defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy
7440
- }).groupPolicy;
7441
- const guildInfo = resolveDiscordGuildEntry({
7442
- guild: params.guild ?? {
7443
- id: params.guildId,
7444
- ...params.guildName ? { name: params.guildName } : {}
7445
- },
7446
- guildId: params.guildId,
7447
- guildEntries: params.discordConfig.guilds
7448
- });
7449
- const channelConfig = params.channelId ? resolveDiscordChannelConfigWithFallback({
7450
- guildInfo,
7451
- channelId: params.channelId,
7452
- channelName: params.channelName,
7453
- channelSlug: params.channelSlug,
7454
- parentId: params.parentId,
7455
- parentName: params.parentName,
7456
- parentSlug: params.parentSlug,
7457
- scope: params.scope
7458
- }) : null;
7459
- if (channelConfig?.enabled === false) return {
7460
- ok: false,
7461
- message: "This channel is disabled."
7462
- };
7463
- const channelAllowlistConfigured = Boolean(guildInfo?.channels) && Object.keys(guildInfo?.channels ?? {}).length > 0;
7464
- if (!params.channelId && groupPolicy === "allowlist" && channelAllowlistConfigured) return {
7465
- ok: false,
7466
- message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
7467
- };
7468
- const channelAllowed = channelConfig ? channelConfig.allowed : !channelAllowlistConfigured;
7469
- if (!isDiscordGroupAllowedByPolicy({
7470
- groupPolicy,
7471
- guildAllowlisted: Boolean(guildInfo),
7472
- channelAllowlistConfigured,
7473
- channelAllowed
7474
- }) || channelConfig?.allowed === false) return {
7475
- ok: false,
7476
- message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
7477
- };
7478
- const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
7479
- channelConfig,
7480
- guildInfo,
7481
- memberRoleIds: params.memberRoleIds,
7482
- sender: params.sender,
7483
- allowNameMatching: false
7484
- });
7485
- const admissionAllowList = normalizeDiscordAllowList(params.admissionAllowFrom ?? params.discordConfig.allowFrom ?? params.discordConfig.allowFrom, [
7486
- "discord:",
7487
- "user:",
7488
- "pk:"
7489
- ]);
7490
- const admissionAllowed = admissionAllowList ? allowListMatches(admissionAllowList, params.sender, { allowNameMatching: false }) : false;
7491
- const useAccessGroups = params.useAccessGroups ?? true;
7492
- return resolveCommandAuthorizedFromAuthorizers({
7493
- useAccessGroups,
7494
- authorizers: useAccessGroups ? [{
7495
- configured: admissionAllowList != null,
7496
- allowed: admissionAllowed
7497
- }, {
7498
- configured: hasAccessRestrictions,
7499
- allowed: memberAllowed
7500
- }] : [{
7501
- configured: hasAccessRestrictions,
7502
- allowed: memberAllowed
7503
- }],
7504
- modeWhenAccessGroupsOff: "configured"
7505
- }) ? {
7506
- ok: true,
7507
- channelConfig
7508
- } : {
7509
- ok: false,
7510
- message: "You are not authorized to use this command."
7511
- };
7512
- }
7513
- //#endregion
7514
- //#region extensions/discord/src/voice/owner-access.ts
7515
- function resolveDiscordVoiceAccess(params) {
7516
- const commandOwnerAllowFrom = resolveDiscordCommandOwnerAllowFrom(params.cfg);
7517
- if (commandOwnerAllowFrom) return {
7518
- admissionAllowFrom: commandOwnerAllowFrom,
7519
- ownerAllowFrom: commandOwnerAllowFrom
7520
- };
7521
- return {
7522
- admissionAllowFrom: resolveDiscordAccountAllowFrom({
7523
- cfg: params.cfg,
7524
- accountId: params.accountId
7525
- }) ?? params.discordConfig.allowFrom ?? [],
7526
- ownerAllowFrom: []
7527
- };
7528
- }
7529
- //#endregion
7530
6907
  //#region extensions/discord/src/voice/command.ts
7531
6908
  const VOICE_CHANNEL_TYPES = [ChannelType.GuildVoice, ChannelType.GuildStageVoice];
7532
6909
  const DISCORD_VOICE_COMMAND_SPEC = {
@@ -7890,8 +7267,8 @@ function formatDiscordDeployRestOperation(err) {
7890
7267
  function formatDiscordDeployErrorMessage(err) {
7891
7268
  if (!isAbortLikeError(err)) return formatErrorMessage$1(err);
7892
7269
  const deployErr = err && typeof err === "object" ? err : {};
7893
- const requestMs = parseFiniteNumber(deployErr.deployRequestMs);
7894
- const timeoutMs = parseFiniteNumber(deployErr.deployTimeoutMs);
7270
+ const requestMs = parseFiniteNumber$1(deployErr.deployRequestMs);
7271
+ const timeoutMs = parseFiniteNumber$1(deployErr.deployTimeoutMs);
7895
7272
  const operation = formatDiscordDeployRestOperation(deployErr);
7896
7273
  if (!(requestMs !== void 0 || timeoutMs !== void 0 || deployErr.deployRestMethod !== void 0 || deployErr.deployRestPath !== void 0)) return "Discord REST request was aborted";
7897
7274
  const timing = [];
@@ -7904,8 +7281,8 @@ function formatDiscordDeployErrorMessage(err) {
7904
7281
  function resolveDiscordDeployRateLimitDetails(err) {
7905
7282
  if (!err || typeof err !== "object") return;
7906
7283
  const deployErr = err;
7907
- const status = parseStrictNonNegativeInteger(deployErr.status) ?? parseStrictNonNegativeInteger(deployErr.statusCode);
7908
- const retryAfterSeconds = parseFiniteNumber(deployErr.retryAfter) ?? parseFiniteNumber(readDiscordDeployObjectField(deployErr.rawBody, "retry_after"));
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"));
7909
7286
  if (!(err instanceof RateLimitError || status === 429 || retryAfterSeconds !== void 0)) return;
7910
7287
  const rawGlobal = readDiscordDeployObjectField(deployErr.rawBody, "global");
7911
7288
  const scope = typeof deployErr.scope === "string" && deployErr.scope.trim().length > 0 ? deployErr.scope : rawGlobal === true ? "global" : rawGlobal === false ? "route" : void 0;
@@ -7995,8 +7372,8 @@ function formatDiscordDeployErrorDetails(err) {
7995
7372
  function isDiscordDeployDailyCreateLimit(err) {
7996
7373
  if (!err || typeof err !== "object") return false;
7997
7374
  const deployErr = err;
7998
- const discordCode = parseStrictNonNegativeInteger(deployErr.discordCode);
7999
- const rawCode = parseStrictNonNegativeInteger(readDiscordDeployObjectField(deployErr.rawBody, "code"));
7375
+ const discordCode = parseStrictNonNegativeInteger$1(deployErr.discordCode);
7376
+ const rawCode = parseStrictNonNegativeInteger$1(readDiscordDeployObjectField(deployErr.rawBody, "code"));
8000
7377
  return (discordCode === 30034 || rawCode === 30034) && /daily application command creates/i.test(formatErrorMessage$1(err));
8001
7378
  }
8002
7379
  //#endregion
@@ -8757,7 +8134,8 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
8757
8134
  const referenced = message.referencedMessage;
8758
8135
  if (!referenced?.author) return null;
8759
8136
  const referencedText = resolveDiscordMessageText(referenced, { includeForwarded: true });
8760
- if (!referencedText) return null;
8137
+ const hasVisibleMedia = referenced.attachments.length > 0 || !referencedText && resolveDiscordMessageStickers(referenced).length > 0;
8138
+ if (!referencedText && !hasVisibleMedia) return null;
8761
8139
  const sender = resolveDiscordSenderIdentity({
8762
8140
  author: referenced.author,
8763
8141
  pluralkitInfo: null
@@ -8773,7 +8151,7 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
8773
8151
  const roles = referenced.member?.roles;
8774
8152
  return Array.isArray(roles) ? roles.map((roleId) => roleId) : void 0;
8775
8153
  })(),
8776
- body: referencedText,
8154
+ ...referencedText ? { body: referencedText } : {},
8777
8155
  timestamp: resolveTimestampMs(referenced.timestamp)
8778
8156
  };
8779
8157
  }
@@ -8971,7 +8349,7 @@ async function deliverDiscordReply(params) {
8971
8349
  //#endregion
8972
8350
  //#region extensions/discord/src/monitor/agent-components.dispatch.ts
8973
8351
  const loadConversationRuntime$1 = createLazyRuntimeModule(() => import("./agent-components.runtime-CEPrf2SY.js"));
8974
- const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-CS-nNVoe.js").then((n) => n.n));
8352
+ const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-oLiS5Z13.js").then((n) => n.n));
8975
8353
  function buildDiscordComponentConversationLabel(params) {
8976
8354
  if (params.interactionCtx.isDirectMessage) return buildDirectLabel(params.interactionCtx.user);
8977
8355
  if (params.interactionCtx.isGroupDm) return `Group DM #${params.channelCtx.channelName ?? params.interactionCtx.channelId} channel id:${params.interactionCtx.channelId}`;
@@ -9093,6 +8471,14 @@ async function dispatchDiscordComponentEvent(params) {
9093
8471
  SessionKey: sessionKey,
9094
8472
  AccountId: accountId,
9095
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
+ }),
9096
8482
  ConversationLabel: fromLabel,
9097
8483
  SenderName: senderName,
9098
8484
  SenderId: interactionCtx.userId,
@@ -9161,6 +8547,7 @@ async function dispatchDiscordComponentEvent(params) {
9161
8547
  sessionKey
9162
8548
  },
9163
8549
  ctxPayload,
8550
+ dispatchReplyFromConfig: ctx.channelRuntime?.reply?.dispatchReplyFromConfig,
9164
8551
  record: {
9165
8552
  updateLastRoute: interactionCtx.isDirectMessage ? {
9166
8553
  sessionKey: inboundLastRouteSessionKey,
@@ -9367,7 +8754,7 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
9367
8754
  }
9368
8755
  //#endregion
9369
8756
  //#region extensions/discord/src/monitor/agent-components.handlers.ts
9370
- const loadComponentsRuntime = createLazyRuntimeModule(() => import("./outbound-session-route-DxLkrgKQ.js").then((n) => n.n));
8757
+ const loadComponentsRuntime = createLazyRuntimeModule(() => import("./components-ezGKo_Yh.js").then((n) => n.t));
9371
8758
  async function handleDiscordComponentEvent(params) {
9372
8759
  const parsed = parseDiscordComponentData(params.data, resolveInteractionCustomId(params.interaction));
9373
8760
  if (!parsed) {
@@ -10149,6 +9536,7 @@ function createDiscordProviderInteractionSurface(params) {
10149
9536
  allowFrom: params.allowFrom,
10150
9537
  dmPolicy: params.dmPolicy,
10151
9538
  runtime: params.runtime,
9539
+ channelRuntime: params.channelRuntime,
10152
9540
  token: params.token
10153
9541
  }, params.applicationId);
10154
9542
  if (activityButton) components.push(activityButton);
@@ -10641,6 +10029,7 @@ async function monitorDiscordProvider(opts = {}) {
10641
10029
  let earlyGatewayEmitter = gatewaySupervisor?.emitter;
10642
10030
  let onEarlyGatewayDebug;
10643
10031
  try {
10032
+ const pluginChannelRuntime = opts.channelRuntime;
10644
10033
  const { commands, components, modals } = createDiscordProviderInteractionSurface({
10645
10034
  cfg,
10646
10035
  discordConfig: discordCfg,
@@ -10660,7 +10049,7 @@ async function monitorDiscordProvider(opts = {}) {
10660
10049
  allowFrom,
10661
10050
  dmPolicy,
10662
10051
  runtime,
10663
- channelRuntime: opts.channelRuntime,
10052
+ channelRuntime: pluginChannelRuntime,
10664
10053
  abortSignal: opts.abortSignal,
10665
10054
  createNativeCommand: discordProviderRuntime.createDiscordNativeCommand
10666
10055
  });
@@ -10734,7 +10123,7 @@ async function monitorDiscordProvider(opts = {}) {
10734
10123
  runtime,
10735
10124
  botUserId
10736
10125
  });
10737
- const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CfFMexGt.js").then((n) => n.n);
10126
+ const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-DeIMNbRh.js").then((n) => n.n);
10738
10127
  setDiscordTranscriptsVoiceManager({
10739
10128
  accountId: account.accountId,
10740
10129
  manager: voiceManager
@@ -10752,7 +10141,7 @@ async function monitorDiscordProvider(opts = {}) {
10752
10141
  accountId: account.accountId,
10753
10142
  token,
10754
10143
  runtime,
10755
- buildContext: opts.channelRuntime?.inbound.buildContext,
10144
+ buildContext: pluginChannelRuntime?.inbound.buildContext,
10756
10145
  setStatus: opts.setStatus,
10757
10146
  abortSignal: opts.abortSignal,
10758
10147
  botUserId,
@@ -10840,4 +10229,4 @@ async function monitorDiscordProvider(opts = {}) {
10840
10229
  }
10841
10230
  }
10842
10231
  //#endregion
10843
- export { sanitizeDiscordThreadName as A, handleDiscordDmCommandDecision as C, resolveDiscordAutoThreadReplyPlan as D, threading_exports as E, resolveDiscordTextCommandAccess as M, resolveDiscordReplyTarget as O, shouldIgnoreStaleDiscordRouteBinding as S, registerDiscordListener as T, buildDiscordInboundAccessContext as _, sanitizeDiscordFrontChannelReplyPayloads as a, resolveDiscordConversationRoute as b, resolveReplyContext as c, createDiscordGatewayPlugin as d, resolveDiscordGatewayIntents as f, buildDiscordGroupSystemPrompt as g, createDiscordNativeCommand as h, formatDiscordReplySkip as i, resolveDiscordDmCommandAccess as j, resolveDiscordThreadStarter as k, resolveDiscordVoiceAccess as l, resolveDiscordVoiceEnabled as m, deliverDiscordReply as n, buildDirectLabel as o, waitForDiscordGatewayPluginRegistration as p, formatDiscordReplyDeliveryFailure as r, buildGuildLabel as s, monitorDiscordProvider as t, authorizeDiscordVoiceIngress as u, createDiscordSupplementalContextAccessChecker as v, mapGatewayDispatchData as w, resolveDiscordEffectiveRoute as x, buildDiscordRoutePeer as y };
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 };