@kodelyth/discord 2026.5.39 → 2026.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-inspect-Dqw-enky.js +81 -0
- package/dist/account-inspect-api.js +10 -0
- package/dist/accounts-B7OBFePq.js +224 -0
- package/dist/action-runtime-api.js +2 -0
- package/dist/agent-components.runtime-DVY_1VB4.js +4 -0
- package/dist/allow-list-B0s7evD7.js +354 -0
- package/dist/api-CXAcv9nZ.js +130 -0
- package/dist/api.js +23 -0
- package/dist/approval-handler.runtime-B9xUAF3n.js +426 -0
- package/dist/audit-DoiK49WO.js +24 -0
- package/dist/audit-core-BGrq3G7r.js +105 -0
- package/dist/channel-U_aeoFwW.js +795 -0
- package/dist/channel-actions-BxEBnEuv.js +173 -0
- package/dist/channel-actions.runtime-CPtpH-yl.js +263 -0
- package/dist/channel-api-BfjklLby.js +21 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.setup-BUSC0apv.js +337 -0
- package/dist/components-luonoe13.js +909 -0
- package/dist/config-api-DSYGqaLQ.js +2 -0
- package/dist/config-schema-DIqJBGwC.js +357 -0
- package/dist/configured-state.js +6 -0
- package/dist/contract-api.js +8 -0
- package/dist/conversation-identity-DXAm0_Mk.js +270 -0
- package/dist/directory-config-CYbuMmPS.js +49 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/directory-live-DX4dLRpJ.js +159 -0
- package/dist/doctor-bbKSvGVD.js +244 -0
- package/dist/doctor-contract-Btjt6NJD.js +383 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/gateway-registry-BKSpa4GB.js +74 -0
- package/dist/handle-action.guild-admin-B5BArS2n.js +286 -0
- package/dist/inbound-context-WAOqhGlT.js +48 -0
- package/dist/inbound-event-delivery-C-1Ji3WP.js +65 -0
- package/dist/index.js +26 -0
- package/dist/manager.runtime-DXHynKE4.js +2356 -0
- package/dist/message-handler-mXzc3tA_.js +381 -0
- package/dist/message-handler.preflight-BPD1a347.js +1113 -0
- package/dist/message-handler.process-GUa3aV8z.js +1438 -0
- package/dist/message-utils-dUbem16p.js +549 -0
- package/dist/outbound-adapter-C18OAc1y.js +536 -0
- package/dist/pluralkit-D1Q2x0w5.js +22 -0
- package/dist/preflight-audio-CZtpWcIm.js +72 -0
- package/dist/preflight-audio.runtime-Brx_0_xW.js +7 -0
- package/dist/preview-streaming-D_slNIiO.js +8 -0
- package/dist/probe-D--Ca4JF.js +139 -0
- package/dist/probe.runtime-DQBchZzv.js +2 -0
- package/dist/provider-B2-31CIT.js +9565 -0
- package/dist/provider-session.runtime-BwzzSsrH.js +6 -0
- package/dist/provider.runtime-CP3oHLls.js +2 -0
- package/dist/resolve-allowlist-common-CqxPLcJO.js +34 -0
- package/dist/resolve-channels-0LX4pUbB.js +265 -0
- package/dist/resolve-users-CztOv0Qs.js +120 -0
- package/dist/runtime-DUaw66V_.js +1073 -0
- package/dist/runtime-api.actions.js +3 -0
- package/dist/runtime-api.js +30 -0
- package/dist/runtime-api.lookup.js +7 -0
- package/dist/runtime-api.monitor-CvVKvEXW.js +5 -0
- package/dist/runtime-api.monitor.js +8 -0
- package/dist/runtime-api.send.js +6 -0
- package/dist/runtime-api.threads.js +6 -0
- package/dist/runtime-fC6f4UF2.js +8 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/secret-config-contract-B6WW5V88.js +115 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-CnyIQKz6.js +120 -0
- package/dist/security-audit-contract-api.js +2 -0
- package/dist/security-audit.runtime-CQSkjNLu.js +2 -0
- package/dist/security-contract-DLvYOgLM.js +26 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/security-doctor-DepqtNCI.js +18 -0
- package/dist/send-DCtPCHGk.js +881 -0
- package/dist/send.components-Bcgxvm52.js +474 -0
- package/dist/send.outbound-S9t0UuHc.js +330 -0
- package/dist/send.receipt-CDn3GBWC.js +3119 -0
- package/dist/send.shared-D4iBnAmn.js +669 -0
- package/dist/sender-identity-CxCe3_1a.js +43 -0
- package/dist/session-contract-Dwhw3RTY.js +6 -0
- package/dist/session-key-api.js +2 -0
- package/dist/session-key-normalization-CP8dPUid.js +23 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/shared-AIlvuZXt.js +171 -0
- package/dist/subagent-hooks-8bK-mgiU.js +120 -0
- package/dist/subagent-hooks-api.js +22 -0
- package/dist/system-events-Ba1TklaL.js +34 -0
- package/dist/target-resolver-BrtFQtoK.js +82 -0
- package/dist/targets-DWLLZE2l.js +3 -0
- package/dist/test-api.js +45 -0
- package/dist/thread-binding-api.js +4 -0
- package/dist/thread-bindings-9aKRmZv0.js +255 -0
- package/dist/thread-bindings.discord-api-ssGH5wc2.js +244 -0
- package/dist/thread-bindings.manager-0YBHGemk.js +534 -0
- package/dist/thread-bindings.session-updates-DJZGIwaU.js +54 -0
- package/dist/thread-bindings.state-eTFl-PqJ.js +318 -0
- package/dist/timeouts-CEwuGaWT.js +52 -0
- package/dist/timeouts.js +2 -0
- package/dist/typing-BmJKRpCS.js +14 -0
- package/package.json +19 -7
- package/account-inspect-api.js +0 -7
- package/action-runtime-api.js +0 -7
- package/api.js +0 -7
- package/channel-config-api.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/configured-state.js +0 -7
- package/contract-api.js +0 -7
- package/directory-contract-api.js +0 -7
- package/doctor-contract-api.js +0 -7
- package/index.js +0 -7
- package/runtime-api.actions.js +0 -7
- package/runtime-api.js +0 -7
- package/runtime-api.lookup.js +0 -7
- package/runtime-api.monitor.js +0 -7
- package/runtime-api.send.js +0 -7
- package/runtime-api.threads.js +0 -7
- package/runtime-setter-api.js +0 -7
- package/secret-contract-api.js +0 -7
- package/security-audit-contract-api.js +0 -7
- package/security-contract-api.js +0 -7
- package/session-key-api.js +0 -7
- package/setup-entry.js +0 -7
- package/setup-plugin-api.js +0 -7
- package/subagent-hooks-api.js +0 -7
- package/test-api.js +0 -7
- package/thread-binding-api.js +0 -7
- package/timeouts.js +0 -7
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
import { Ht as parseDiscordTarget, a as normalizeDiscordMessagingTarget, i as looksLikeDiscordTargetId } from "./send.receipt-CDn3GBWC.js";
|
|
2
|
+
import { c as resolveDiscordAccountAllowFrom, r as listDiscordAccountIds, s as resolveDiscordAccount } from "./accounts-B7OBFePq.js";
|
|
3
|
+
import { a as projectCredentialSnapshotFields, n as PAIRING_APPROVED_MESSAGE, o as resolveConfiguredFromCredentialStatuses, r as buildTokenChannelStatusSummary, t as DEFAULT_ACCOUNT_ID } from "./channel-api-BfjklLby.js";
|
|
4
|
+
import { x as resolveDiscordOutboundSessionRoute } from "./components-luonoe13.js";
|
|
5
|
+
import { t as getDiscordRuntime } from "./runtime-fC6f4UF2.js";
|
|
6
|
+
import { c as shouldSuppressLocalDiscordExecApprovalPrompt, n as resolveDiscordCurrentConversationIdentity, r as getDiscordApprovalCapability } from "./conversation-identity-DXAm0_Mk.js";
|
|
7
|
+
import { r as resolveRequiredDiscordChannelPermissions } from "./audit-core-BGrq3G7r.js";
|
|
8
|
+
import { t as discordMessageActions$1 } from "./channel-actions-BxEBnEuv.js";
|
|
9
|
+
import { n as setThreadBindingMaxAgeBySessionKey, t as setThreadBindingIdleTimeoutBySessionKey } from "./thread-bindings.session-updates-DJZGIwaU.js";
|
|
10
|
+
import { n as discordOutbound } from "./outbound-adapter-C18OAc1y.js";
|
|
11
|
+
import { a as discordSecurityAdapter, i as discordSetupAdapter, n as discordConfigAdapter, t as createDiscordPluginBase } from "./shared-AIlvuZXt.js";
|
|
12
|
+
import { t as normalizeExplicitDiscordSessionKey } from "./session-key-normalization-CP8dPUid.js";
|
|
13
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
14
|
+
import { createChatChannelPlugin } from "klaw/plugin-sdk/channel-core";
|
|
15
|
+
import { sleepWithAbort } from "klaw/plugin-sdk/runtime-env";
|
|
16
|
+
import { formatErrorMessage } from "klaw/plugin-sdk/error-runtime";
|
|
17
|
+
import { createChannelMessageAdapterFromOutbound } from "klaw/plugin-sdk/channel-message";
|
|
18
|
+
import { buildLegacyDmAccountAllowlistAdapter, createAccountScopedAllowlistNameResolver, createNestedAllowlistOverrideResolver } from "klaw/plugin-sdk/allowlist-config-edit";
|
|
19
|
+
import { createPairingPrefixStripper } from "klaw/plugin-sdk/channel-pairing";
|
|
20
|
+
import { createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter } from "klaw/plugin-sdk/directory-runtime";
|
|
21
|
+
import { appendMatchMetadata, asString, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, isRecord as isRecord$1, resolveEnabledConfiguredAccountId } from "klaw/plugin-sdk/status-helpers";
|
|
22
|
+
import { resolveTargetsWithOptionalToken } from "klaw/plugin-sdk/target-resolver-runtime";
|
|
23
|
+
import { createLazyRuntimeModule } from "klaw/plugin-sdk/lazy-runtime";
|
|
24
|
+
import { resolveToolsBySender } from "klaw/plugin-sdk/channel-policy";
|
|
25
|
+
import { normalizeAtHashSlug } from "klaw/plugin-sdk/string-normalization-runtime";
|
|
26
|
+
//#region extensions/discord/src/channel.conversation.ts
|
|
27
|
+
function resolveDiscordAttachedOutboundTarget(params) {
|
|
28
|
+
if (params.threadId == null) return params.to;
|
|
29
|
+
const threadId = normalizeOptionalStringifiedId(params.threadId) ?? "";
|
|
30
|
+
return threadId ? `channel:${threadId}` : params.to;
|
|
31
|
+
}
|
|
32
|
+
function buildDiscordCrossContextPresentation(params) {
|
|
33
|
+
return {
|
|
34
|
+
tone: "neutral",
|
|
35
|
+
blocks: [...params.message.trim() ? [{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: params.message
|
|
38
|
+
}, { type: "divider" }] : [], {
|
|
39
|
+
type: "context",
|
|
40
|
+
text: `From ${params.originLabel}`
|
|
41
|
+
}]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function normalizeDiscordAcpConversationId(conversationId) {
|
|
45
|
+
const normalized = conversationId.trim();
|
|
46
|
+
return normalized ? { conversationId: normalized } : null;
|
|
47
|
+
}
|
|
48
|
+
function matchDiscordAcpConversation(params) {
|
|
49
|
+
if (params.bindingConversationId === params.conversationId) return {
|
|
50
|
+
conversationId: params.conversationId,
|
|
51
|
+
matchPriority: 2
|
|
52
|
+
};
|
|
53
|
+
if (params.parentConversationId && params.parentConversationId !== params.conversationId && params.bindingConversationId === params.parentConversationId) return {
|
|
54
|
+
conversationId: params.parentConversationId,
|
|
55
|
+
matchPriority: 1
|
|
56
|
+
};
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function resolveDiscordConversationIdFromTargets(targets) {
|
|
60
|
+
for (const raw of targets) {
|
|
61
|
+
const trimmed = raw?.trim();
|
|
62
|
+
if (!trimmed) continue;
|
|
63
|
+
try {
|
|
64
|
+
const target = parseDiscordTarget(trimmed, { defaultKind: "channel" });
|
|
65
|
+
if (target?.normalized) return target.normalized;
|
|
66
|
+
} catch {
|
|
67
|
+
const mentionMatch = trimmed.match(/^<#(\d+)>$/);
|
|
68
|
+
if (mentionMatch?.[1]) return `channel:${mentionMatch[1]}`;
|
|
69
|
+
if (/^\d{6,}$/.test(trimmed)) return normalizeDiscordMessagingTarget(trimmed);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function parseDiscordParentChannelFromSessionKey(raw) {
|
|
74
|
+
const sessionKey = normalizeLowercaseStringOrEmpty(raw);
|
|
75
|
+
if (!sessionKey) return;
|
|
76
|
+
const match = sessionKey.match(/(?:^|:)channel:([^:]+)$/);
|
|
77
|
+
return match?.[1] ? `channel:${match[1]}` : void 0;
|
|
78
|
+
}
|
|
79
|
+
function resolveDiscordCommandConversation(params) {
|
|
80
|
+
const targets = [
|
|
81
|
+
params.originatingTo,
|
|
82
|
+
params.commandTo,
|
|
83
|
+
params.fallbackTo
|
|
84
|
+
];
|
|
85
|
+
if (params.threadId) {
|
|
86
|
+
const parentConversationId = normalizeDiscordMessagingTarget(normalizeOptionalString(params.threadParentId) ?? "") || parseDiscordParentChannelFromSessionKey(params.parentSessionKey) || resolveDiscordConversationIdFromTargets(targets);
|
|
87
|
+
return {
|
|
88
|
+
conversationId: params.threadId,
|
|
89
|
+
...parentConversationId && parentConversationId !== params.threadId ? { parentConversationId } : {}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const conversationId = resolveDiscordCurrentConversationIdentity({
|
|
93
|
+
from: params.from,
|
|
94
|
+
chatType: params.chatType,
|
|
95
|
+
originatingTo: params.originatingTo,
|
|
96
|
+
commandTo: params.commandTo,
|
|
97
|
+
fallbackTo: params.fallbackTo
|
|
98
|
+
});
|
|
99
|
+
return conversationId ? { conversationId } : null;
|
|
100
|
+
}
|
|
101
|
+
function resolveDiscordInboundConversation(params) {
|
|
102
|
+
const conversationId = resolveDiscordCurrentConversationIdentity({
|
|
103
|
+
from: params.from,
|
|
104
|
+
chatType: params.isGroup ? "group" : "direct",
|
|
105
|
+
originatingTo: params.to,
|
|
106
|
+
fallbackTo: params.conversationId
|
|
107
|
+
});
|
|
108
|
+
return conversationId ? { conversationId } : null;
|
|
109
|
+
}
|
|
110
|
+
function parseDiscordExplicitTarget(raw) {
|
|
111
|
+
try {
|
|
112
|
+
const target = parseDiscordTarget(raw, { defaultKind: "channel" });
|
|
113
|
+
if (!target) return null;
|
|
114
|
+
return {
|
|
115
|
+
to: target.normalized,
|
|
116
|
+
chatType: target.kind === "user" ? "direct" : "channel"
|
|
117
|
+
};
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region extensions/discord/src/channel.loaders.ts
|
|
124
|
+
let discordProviderRuntimePromise;
|
|
125
|
+
let discordProbeRuntimePromise;
|
|
126
|
+
let discordAuditModulePromise;
|
|
127
|
+
let discordSendModulePromise;
|
|
128
|
+
let discordDirectoryLiveModulePromise;
|
|
129
|
+
const loadDiscordDirectoryConfigModule = createLazyRuntimeModule(() => import("./directory-config-CYbuMmPS.js").then((n) => n.t));
|
|
130
|
+
const loadDiscordResolveChannelsModule = createLazyRuntimeModule(() => import("./resolve-channels-0LX4pUbB.js").then((n) => n.n));
|
|
131
|
+
const loadDiscordResolveUsersModule = createLazyRuntimeModule(() => import("./resolve-users-CztOv0Qs.js").then((n) => n.n));
|
|
132
|
+
const loadDiscordThreadBindingsManagerModule = createLazyRuntimeModule(() => import("./thread-bindings.manager-0YBHGemk.js").then((n) => n.a));
|
|
133
|
+
const loadDiscordTargetResolverModule = createLazyRuntimeModule(() => import("./target-resolver-BrtFQtoK.js").then((n) => n.r));
|
|
134
|
+
async function loadDiscordProviderRuntime() {
|
|
135
|
+
discordProviderRuntimePromise ??= import("./provider.runtime-CP3oHLls.js");
|
|
136
|
+
return await discordProviderRuntimePromise;
|
|
137
|
+
}
|
|
138
|
+
async function loadDiscordProbeRuntime() {
|
|
139
|
+
discordProbeRuntimePromise ??= import("./probe.runtime-DQBchZzv.js");
|
|
140
|
+
return await discordProbeRuntimePromise;
|
|
141
|
+
}
|
|
142
|
+
async function loadDiscordAuditModule() {
|
|
143
|
+
discordAuditModulePromise ??= import("./audit-DoiK49WO.js").then((n) => n.n);
|
|
144
|
+
return await discordAuditModulePromise;
|
|
145
|
+
}
|
|
146
|
+
async function loadDiscordSendModule() {
|
|
147
|
+
discordSendModulePromise ??= import("./send-DCtPCHGk.js").then((n) => n.t);
|
|
148
|
+
return await discordSendModulePromise;
|
|
149
|
+
}
|
|
150
|
+
async function loadDiscordDirectoryLiveModule() {
|
|
151
|
+
discordDirectoryLiveModulePromise ??= import("./directory-live-DX4dLRpJ.js").then((n) => n.t);
|
|
152
|
+
return await discordDirectoryLiveModulePromise;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region extensions/discord/src/group-policy.ts
|
|
156
|
+
function normalizeDiscordSlug(value) {
|
|
157
|
+
return normalizeAtHashSlug(value);
|
|
158
|
+
}
|
|
159
|
+
function resolveDiscordGuildEntry(guilds, groupSpace) {
|
|
160
|
+
if (!guilds || Object.keys(guilds).length === 0) return null;
|
|
161
|
+
const space = normalizeOptionalString(groupSpace) ?? "";
|
|
162
|
+
if (space && guilds[space]) return guilds[space];
|
|
163
|
+
const normalized = normalizeDiscordSlug(space);
|
|
164
|
+
if (normalized && guilds[normalized]) return guilds[normalized];
|
|
165
|
+
if (normalized) {
|
|
166
|
+
const match = Object.values(guilds).find((entry) => normalizeDiscordSlug(entry?.slug ?? void 0) === normalized);
|
|
167
|
+
if (match) return match;
|
|
168
|
+
}
|
|
169
|
+
return guilds["*"] ?? null;
|
|
170
|
+
}
|
|
171
|
+
function resolveDiscordChannelEntry(channelEntries, params) {
|
|
172
|
+
if (!channelEntries || Object.keys(channelEntries).length === 0) return;
|
|
173
|
+
const groupChannel = params.groupChannel;
|
|
174
|
+
const channelSlug = normalizeDiscordSlug(groupChannel);
|
|
175
|
+
return (params.groupId ? channelEntries[params.groupId] : void 0) ?? (channelSlug ? channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`] : void 0) ?? (groupChannel ? channelEntries[normalizeDiscordSlug(groupChannel)] : void 0);
|
|
176
|
+
}
|
|
177
|
+
function resolveSenderToolsEntry(entry, params) {
|
|
178
|
+
if (!entry) return;
|
|
179
|
+
return resolveToolsBySender({
|
|
180
|
+
toolsBySender: entry.toolsBySender,
|
|
181
|
+
senderId: params.senderId,
|
|
182
|
+
senderName: params.senderName,
|
|
183
|
+
senderUsername: params.senderUsername,
|
|
184
|
+
senderE164: params.senderE164
|
|
185
|
+
}) ?? entry.tools;
|
|
186
|
+
}
|
|
187
|
+
function resolveDiscordPolicyContext(params) {
|
|
188
|
+
const guildEntry = resolveDiscordGuildEntry((params.accountId ? params.cfg.channels?.discord?.accounts?.[params.accountId]?.guilds : void 0) ?? params.cfg.channels?.discord?.guilds, params.groupSpace);
|
|
189
|
+
const channelEntries = guildEntry?.channels;
|
|
190
|
+
return {
|
|
191
|
+
guildEntry,
|
|
192
|
+
channelEntry: channelEntries && Object.keys(channelEntries).length > 0 ? resolveDiscordChannelEntry(channelEntries, params) : void 0
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function resolveDiscordGroupRequireMention(params) {
|
|
196
|
+
const context = resolveDiscordPolicyContext(params);
|
|
197
|
+
if (typeof context.channelEntry?.requireMention === "boolean") return context.channelEntry.requireMention;
|
|
198
|
+
if (typeof context.guildEntry?.requireMention === "boolean") return context.guildEntry.requireMention;
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
function resolveDiscordGroupToolPolicy(params) {
|
|
202
|
+
const context = resolveDiscordPolicyContext(params);
|
|
203
|
+
const channelPolicy = resolveSenderToolsEntry(context.channelEntry, params);
|
|
204
|
+
if (channelPolicy) return channelPolicy;
|
|
205
|
+
return resolveSenderToolsEntry(context.guildEntry, params);
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region extensions/discord/src/status-issues.ts
|
|
209
|
+
function readDiscordAccountStatus(value) {
|
|
210
|
+
if (!isRecord$1(value)) return null;
|
|
211
|
+
return {
|
|
212
|
+
accountId: value.accountId,
|
|
213
|
+
enabled: value.enabled,
|
|
214
|
+
configured: value.configured,
|
|
215
|
+
running: value.running,
|
|
216
|
+
connected: value.connected,
|
|
217
|
+
healthState: value.healthState,
|
|
218
|
+
application: value.application,
|
|
219
|
+
audit: value.audit
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function readDiscordApplicationSummary(value) {
|
|
223
|
+
if (!isRecord$1(value)) return {};
|
|
224
|
+
const intentsRaw = value.intents;
|
|
225
|
+
if (!isRecord$1(intentsRaw)) return {};
|
|
226
|
+
return { intents: { messageContent: intentsRaw.messageContent === "enabled" || intentsRaw.messageContent === "limited" || intentsRaw.messageContent === "disabled" ? intentsRaw.messageContent : void 0 } };
|
|
227
|
+
}
|
|
228
|
+
function readDiscordPermissionsAuditSummary(value) {
|
|
229
|
+
if (!isRecord$1(value)) return {};
|
|
230
|
+
const unresolvedChannels = typeof value.unresolvedChannels === "number" && Number.isFinite(value.unresolvedChannels) ? value.unresolvedChannels : void 0;
|
|
231
|
+
const channelsRaw = value.channels;
|
|
232
|
+
return {
|
|
233
|
+
unresolvedChannels,
|
|
234
|
+
channels: Array.isArray(channelsRaw) ? channelsRaw.map((entry) => {
|
|
235
|
+
if (!isRecord$1(entry)) return null;
|
|
236
|
+
const channelId = asString(entry.channelId);
|
|
237
|
+
if (!channelId) return null;
|
|
238
|
+
const ok = typeof entry.ok === "boolean" ? entry.ok : void 0;
|
|
239
|
+
const missing = Array.isArray(entry.missing) ? entry.missing.map((v) => asString(v)).filter(Boolean) : void 0;
|
|
240
|
+
const error = asString(entry.error) ?? null;
|
|
241
|
+
const matchKey = asString(entry.matchKey) ?? void 0;
|
|
242
|
+
const matchSource = asString(entry.matchSource) ?? void 0;
|
|
243
|
+
return {
|
|
244
|
+
channelId,
|
|
245
|
+
ok,
|
|
246
|
+
missing: missing?.length ? missing : void 0,
|
|
247
|
+
error,
|
|
248
|
+
matchKey,
|
|
249
|
+
matchSource
|
|
250
|
+
};
|
|
251
|
+
}).filter(Boolean) : void 0
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function collectDiscordStatusIssues(accounts) {
|
|
255
|
+
const issues = [];
|
|
256
|
+
for (const entry of accounts) {
|
|
257
|
+
const account = readDiscordAccountStatus(entry);
|
|
258
|
+
if (!account) continue;
|
|
259
|
+
const accountId = resolveEnabledConfiguredAccountId(account);
|
|
260
|
+
if (!accountId) continue;
|
|
261
|
+
const running = account.running === true;
|
|
262
|
+
const healthState = asString(account.healthState);
|
|
263
|
+
if (healthState === "stale-socket" || healthState === "stuck" || healthState === "disconnected" || healthState === "not-running") {
|
|
264
|
+
const runningLabel = running ? "running" : "not running";
|
|
265
|
+
issues.push({
|
|
266
|
+
channel: "discord",
|
|
267
|
+
accountId,
|
|
268
|
+
kind: "runtime",
|
|
269
|
+
message: `Discord gateway transport is degraded (${healthState}; account is ${runningLabel}).`,
|
|
270
|
+
fix: "Check gateway event-loop health and Discord connectivity, then restart the Discord channel or gateway if the transport does not recover."
|
|
271
|
+
});
|
|
272
|
+
} else if (running && account.connected === false) issues.push({
|
|
273
|
+
channel: "discord",
|
|
274
|
+
accountId,
|
|
275
|
+
kind: "runtime",
|
|
276
|
+
message: "Discord gateway transport is running but disconnected.",
|
|
277
|
+
fix: "Check gateway logs for Discord websocket errors and wait for reconnect; restart the Discord channel or gateway if it does not recover."
|
|
278
|
+
});
|
|
279
|
+
if (readDiscordApplicationSummary(account.application).intents?.messageContent === "disabled") issues.push({
|
|
280
|
+
channel: "discord",
|
|
281
|
+
accountId,
|
|
282
|
+
kind: "intent",
|
|
283
|
+
message: "Message Content Intent is disabled. Bot may not see normal channel messages.",
|
|
284
|
+
fix: "Enable Message Content Intent in Discord Dev Portal → Bot → Privileged Gateway Intents, or require mention-only operation."
|
|
285
|
+
});
|
|
286
|
+
const audit = readDiscordPermissionsAuditSummary(account.audit);
|
|
287
|
+
if (audit.unresolvedChannels && audit.unresolvedChannels > 0) issues.push({
|
|
288
|
+
channel: "discord",
|
|
289
|
+
accountId,
|
|
290
|
+
kind: "config",
|
|
291
|
+
message: `Some configured guild channels are not numeric IDs (unresolvedChannels=${audit.unresolvedChannels}). Permission audit can only check numeric channel IDs.`,
|
|
292
|
+
fix: "Use numeric channel IDs as keys in channels.discord.guilds.*.channels (then rerun channels status --probe)."
|
|
293
|
+
});
|
|
294
|
+
for (const channel of audit.channels ?? []) {
|
|
295
|
+
if (channel.ok === true) continue;
|
|
296
|
+
const missing = channel.missing?.length ? ` missing ${channel.missing.join(", ")}` : "";
|
|
297
|
+
const error = channel.error ? `: ${channel.error}` : "";
|
|
298
|
+
const baseMessage = `Channel ${channel.channelId} permission check failed.${missing}${error}`;
|
|
299
|
+
issues.push({
|
|
300
|
+
channel: "discord",
|
|
301
|
+
accountId,
|
|
302
|
+
kind: "permissions",
|
|
303
|
+
message: appendMatchMetadata(baseMessage, {
|
|
304
|
+
matchKey: channel.matchKey,
|
|
305
|
+
matchSource: channel.matchSource
|
|
306
|
+
}),
|
|
307
|
+
fix: "Ensure the bot role can view + send in this channel (and that channel overrides don't deny it)."
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return issues;
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region extensions/discord/src/channel.ts
|
|
315
|
+
const DISCORD_ACCOUNT_STARTUP_STAGGER_MS = 1e4;
|
|
316
|
+
const discordMessageAdapter = createChannelMessageAdapterFromOutbound({
|
|
317
|
+
id: "discord",
|
|
318
|
+
outbound: discordOutbound,
|
|
319
|
+
live: {
|
|
320
|
+
capabilities: {
|
|
321
|
+
draftPreview: true,
|
|
322
|
+
previewFinalization: true,
|
|
323
|
+
progressUpdates: true
|
|
324
|
+
},
|
|
325
|
+
finalizer: { capabilities: {
|
|
326
|
+
finalEdit: true,
|
|
327
|
+
normalFallback: true,
|
|
328
|
+
discardPending: true
|
|
329
|
+
} }
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
function startDiscordStartupProbe(params) {
|
|
333
|
+
(async () => {
|
|
334
|
+
try {
|
|
335
|
+
const probe = await (await loadDiscordProbeRuntime()).probeDiscord(params.token, 2500, { includeApplication: true });
|
|
336
|
+
if (params.abortSignal.aborted) return;
|
|
337
|
+
params.setStatus({
|
|
338
|
+
accountId: params.accountId,
|
|
339
|
+
bot: probe.bot,
|
|
340
|
+
application: probe.application
|
|
341
|
+
});
|
|
342
|
+
if (probe.ok) {
|
|
343
|
+
const username = probe.bot?.username?.trim();
|
|
344
|
+
if (username) params.log?.info?.(`[${params.accountId}] Discord bot probe resolved @${username}`);
|
|
345
|
+
} else if (getDiscordRuntime().logging.shouldLogVerbose()) params.log?.debug?.(`[${params.accountId}] bot probe degraded: ${probe.error ?? `status ${probe.status ?? "unknown"}`}`);
|
|
346
|
+
const messageContent = probe.application?.intents?.messageContent;
|
|
347
|
+
if (messageContent === "disabled") params.log?.warn?.(`[${params.accountId}] Discord Message Content Intent is disabled; bot may not respond to channel messages. Enable it in Discord Dev Portal (Bot → Privileged Gateway Intents) or require mentions.`);
|
|
348
|
+
else if (messageContent === "limited") params.log?.info?.(`[${params.accountId}] Discord Message Content Intent is limited; bots under 100 servers can use it without verification.`);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (!params.abortSignal.aborted) params.setStatus({
|
|
351
|
+
accountId: params.accountId,
|
|
352
|
+
bot: void 0,
|
|
353
|
+
application: void 0
|
|
354
|
+
});
|
|
355
|
+
if (getDiscordRuntime().logging.shouldLogVerbose()) params.log?.debug?.(`[${params.accountId}] bot probe failed: ${String(err)}`);
|
|
356
|
+
}
|
|
357
|
+
})();
|
|
358
|
+
}
|
|
359
|
+
function shouldTreatDiscordDeliveredTextAsVisible(params) {
|
|
360
|
+
return params.kind === "block" && typeof params.text === "string" && params.text.trim().length > 0;
|
|
361
|
+
}
|
|
362
|
+
function resolveRuntimeDiscordMessageActions() {
|
|
363
|
+
try {
|
|
364
|
+
return getDiscordRuntime().channel?.discord?.messageActions ?? null;
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const discordMessageActions = {
|
|
370
|
+
resolveExecutionMode: (ctx) => resolveRuntimeDiscordMessageActions()?.resolveExecutionMode?.(ctx) ?? discordMessageActions$1.resolveExecutionMode?.(ctx) ?? "local",
|
|
371
|
+
describeMessageTool: (ctx) => resolveRuntimeDiscordMessageActions()?.describeMessageTool?.(ctx) ?? discordMessageActions$1.describeMessageTool?.(ctx) ?? null,
|
|
372
|
+
extractToolSend: (ctx) => resolveRuntimeDiscordMessageActions()?.extractToolSend?.(ctx) ?? discordMessageActions$1.extractToolSend?.(ctx) ?? null,
|
|
373
|
+
prepareSendPayload: (ctx) => resolveRuntimeDiscordMessageActions()?.prepareSendPayload?.(ctx) ?? discordMessageActions$1.prepareSendPayload?.(ctx) ?? null,
|
|
374
|
+
handleAction: async (ctx) => {
|
|
375
|
+
const runtimeHandleAction = resolveRuntimeDiscordMessageActions()?.handleAction;
|
|
376
|
+
if (runtimeHandleAction) return await runtimeHandleAction(ctx);
|
|
377
|
+
if (!discordMessageActions$1.handleAction) throw new Error("Discord message actions not available");
|
|
378
|
+
return await discordMessageActions$1.handleAction(ctx);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
function resolveDiscordStartupDelayMs(cfg, accountId) {
|
|
382
|
+
const startupIndex = listDiscordAccountIds(cfg).filter((candidateId) => {
|
|
383
|
+
const candidate = resolveDiscordAccount({
|
|
384
|
+
cfg,
|
|
385
|
+
accountId: candidateId
|
|
386
|
+
});
|
|
387
|
+
return candidate.enabled && (resolveConfiguredFromCredentialStatuses(candidate) ?? Boolean(normalizeOptionalString(candidate.token)));
|
|
388
|
+
}).findIndex((candidateId) => candidateId === accountId);
|
|
389
|
+
return startupIndex <= 0 ? 0 : startupIndex * DISCORD_ACCOUNT_STARTUP_STAGGER_MS;
|
|
390
|
+
}
|
|
391
|
+
function formatDiscordIntents(intents) {
|
|
392
|
+
if (!intents) return "unknown";
|
|
393
|
+
return [
|
|
394
|
+
`messageContent=${intents.messageContent ?? "unknown"}`,
|
|
395
|
+
`guildMembers=${intents.guildMembers ?? "unknown"}`,
|
|
396
|
+
`presence=${intents.presence ?? "unknown"}`
|
|
397
|
+
].join(" ");
|
|
398
|
+
}
|
|
399
|
+
const resolveDiscordAllowlistGroupOverrides = createNestedAllowlistOverrideResolver({
|
|
400
|
+
resolveRecord: (account) => account.config.guilds,
|
|
401
|
+
outerLabel: (guildKey) => `guild ${guildKey}`,
|
|
402
|
+
resolveOuterEntries: (guildCfg) => guildCfg?.users,
|
|
403
|
+
resolveChildren: (guildCfg) => guildCfg?.channels,
|
|
404
|
+
innerLabel: (guildKey, channelKey) => `guild ${guildKey} / channel ${channelKey}`,
|
|
405
|
+
resolveInnerEntries: (channelCfg) => channelCfg?.users
|
|
406
|
+
});
|
|
407
|
+
const resolveDiscordAllowlistNames = createAccountScopedAllowlistNameResolver({
|
|
408
|
+
resolveAccount: resolveDiscordAccount,
|
|
409
|
+
resolveToken: (account) => account.token,
|
|
410
|
+
resolveNames: async ({ token, entries }) => (await loadDiscordResolveUsersModule()).resolveDiscordUserAllowlist({
|
|
411
|
+
token,
|
|
412
|
+
entries
|
|
413
|
+
})
|
|
414
|
+
});
|
|
415
|
+
function toConversationLifecycleBinding(binding) {
|
|
416
|
+
return {
|
|
417
|
+
boundAt: binding.boundAt,
|
|
418
|
+
lastActivityAt: typeof binding.lastActivityAt === "number" ? binding.lastActivityAt : binding.boundAt,
|
|
419
|
+
idleTimeoutMs: typeof binding.idleTimeoutMs === "number" ? binding.idleTimeoutMs : void 0,
|
|
420
|
+
maxAgeMs: typeof binding.maxAgeMs === "number" ? binding.maxAgeMs : void 0
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const discordPlugin = createChatChannelPlugin({
|
|
424
|
+
base: {
|
|
425
|
+
...createDiscordPluginBase({ setup: discordSetupAdapter }),
|
|
426
|
+
allowlist: {
|
|
427
|
+
...buildLegacyDmAccountAllowlistAdapter({
|
|
428
|
+
channelId: "discord",
|
|
429
|
+
resolveAccount: resolveDiscordAccount,
|
|
430
|
+
normalize: ({ cfg, accountId, values }) => discordConfigAdapter.formatAllowFrom({
|
|
431
|
+
cfg,
|
|
432
|
+
accountId,
|
|
433
|
+
allowFrom: values
|
|
434
|
+
}),
|
|
435
|
+
resolveDmAllowFrom: (account, { cfg }) => resolveDiscordAccountAllowFrom({
|
|
436
|
+
cfg,
|
|
437
|
+
accountId: account.accountId
|
|
438
|
+
}),
|
|
439
|
+
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
440
|
+
resolveGroupOverrides: resolveDiscordAllowlistGroupOverrides
|
|
441
|
+
}),
|
|
442
|
+
resolveNames: resolveDiscordAllowlistNames
|
|
443
|
+
},
|
|
444
|
+
groups: {
|
|
445
|
+
resolveRequireMention: resolveDiscordGroupRequireMention,
|
|
446
|
+
resolveToolPolicy: resolveDiscordGroupToolPolicy
|
|
447
|
+
},
|
|
448
|
+
mentions: { stripPatterns: () => ["<@!?\\d+>"] },
|
|
449
|
+
agentPrompt: { messageToolHints: () => [
|
|
450
|
+
"- Discord mentions: use canonical outbound syntax: users `<@USER_ID>`, channels `<#CHANNEL_ID>`, and roles `<@&ROLE_ID>`. Plain `@name` text only pings when a configured `mentionAliases` entry rewrites it; do not use the legacy `<@!USER_ID>` nickname form.",
|
|
451
|
+
"- Discord components: set `components` when sending messages to include buttons, selects, or v2 containers.",
|
|
452
|
+
"- Forms: add `components.modal` (title, fields). Klaw adds a trigger button and routes submissions as new messages."
|
|
453
|
+
] },
|
|
454
|
+
messaging: {
|
|
455
|
+
targetPrefixes: ["discord"],
|
|
456
|
+
normalizeTarget: normalizeDiscordMessagingTarget,
|
|
457
|
+
resolveInboundConversation: ({ from, to, conversationId, isGroup }) => resolveDiscordInboundConversation({
|
|
458
|
+
from,
|
|
459
|
+
to,
|
|
460
|
+
conversationId,
|
|
461
|
+
isGroup
|
|
462
|
+
}),
|
|
463
|
+
normalizeExplicitSessionKey: ({ sessionKey, ctx }) => normalizeExplicitDiscordSessionKey(sessionKey, ctx),
|
|
464
|
+
resolveSessionTarget: ({ id }) => normalizeDiscordMessagingTarget(`channel:${id}`),
|
|
465
|
+
parseExplicitTarget: ({ raw }) => parseDiscordExplicitTarget(raw),
|
|
466
|
+
inferTargetChatType: ({ to }) => parseDiscordExplicitTarget(to)?.chatType,
|
|
467
|
+
buildCrossContextPresentation: buildDiscordCrossContextPresentation,
|
|
468
|
+
resolveOutboundSessionRoute: (params) => resolveDiscordOutboundSessionRoute(params),
|
|
469
|
+
targetResolver: {
|
|
470
|
+
looksLikeId: looksLikeDiscordTargetId,
|
|
471
|
+
hint: "<channelId|user:ID|channel:ID>",
|
|
472
|
+
resolveTarget: async ({ cfg, accountId, input, normalized, preferredKind }) => {
|
|
473
|
+
const resolved = await (await loadDiscordTargetResolverModule()).resolveDiscordTarget(input, {
|
|
474
|
+
cfg,
|
|
475
|
+
accountId
|
|
476
|
+
}, preferredKind === "user" ? { defaultKind: "user" } : preferredKind === "channel" || preferredKind === "group" ? { defaultKind: "channel" } : {});
|
|
477
|
+
if (!resolved) return null;
|
|
478
|
+
return {
|
|
479
|
+
to: resolved.normalized,
|
|
480
|
+
kind: resolved.kind === "user" ? "user" : "channel",
|
|
481
|
+
display: resolved.raw,
|
|
482
|
+
source: resolved.normalized === normalized ? "normalized" : "directory"
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
approvalCapability: getDiscordApprovalCapability(),
|
|
488
|
+
directory: createChannelDirectoryAdapter({
|
|
489
|
+
listPeers: async (params) => (await loadDiscordDirectoryConfigModule()).listDiscordDirectoryPeersFromConfig(params),
|
|
490
|
+
listGroups: async (params) => (await loadDiscordDirectoryConfigModule()).listDiscordDirectoryGroupsFromConfig(params),
|
|
491
|
+
...createRuntimeDirectoryLiveAdapter({
|
|
492
|
+
getRuntime: loadDiscordDirectoryLiveModule,
|
|
493
|
+
listPeersLive: (runtime) => runtime.listDiscordDirectoryPeersLive,
|
|
494
|
+
listGroupsLive: (runtime) => runtime.listDiscordDirectoryGroupsLive
|
|
495
|
+
})
|
|
496
|
+
}),
|
|
497
|
+
message: discordMessageAdapter,
|
|
498
|
+
resolver: { resolveTargets: async ({ cfg, accountId, inputs, kind }) => {
|
|
499
|
+
const account = resolveDiscordAccount({
|
|
500
|
+
cfg,
|
|
501
|
+
accountId
|
|
502
|
+
});
|
|
503
|
+
if (kind === "group") return resolveTargetsWithOptionalToken({
|
|
504
|
+
token: account.token,
|
|
505
|
+
inputs,
|
|
506
|
+
missingTokenNote: "missing Discord token",
|
|
507
|
+
resolveWithToken: async ({ token, inputs }) => (await loadDiscordResolveChannelsModule()).resolveDiscordChannelAllowlist({
|
|
508
|
+
token,
|
|
509
|
+
entries: inputs
|
|
510
|
+
}),
|
|
511
|
+
mapResolved: (entry) => ({
|
|
512
|
+
input: entry.input,
|
|
513
|
+
resolved: entry.resolved,
|
|
514
|
+
id: entry.channelId ?? entry.guildId,
|
|
515
|
+
name: entry.channelName ?? entry.guildName ?? (entry.guildId && !entry.channelId ? entry.guildId : void 0),
|
|
516
|
+
note: entry.note
|
|
517
|
+
})
|
|
518
|
+
});
|
|
519
|
+
return resolveTargetsWithOptionalToken({
|
|
520
|
+
token: account.token,
|
|
521
|
+
inputs,
|
|
522
|
+
missingTokenNote: "missing Discord token",
|
|
523
|
+
resolveWithToken: async ({ token, inputs }) => (await loadDiscordResolveUsersModule()).resolveDiscordUserAllowlist({
|
|
524
|
+
token,
|
|
525
|
+
entries: inputs
|
|
526
|
+
}),
|
|
527
|
+
mapResolved: (entry) => ({
|
|
528
|
+
input: entry.input,
|
|
529
|
+
resolved: entry.resolved,
|
|
530
|
+
id: entry.id,
|
|
531
|
+
name: entry.name,
|
|
532
|
+
note: entry.note
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
} },
|
|
536
|
+
actions: discordMessageActions,
|
|
537
|
+
bindings: {
|
|
538
|
+
compileConfiguredBinding: ({ conversationId }) => normalizeDiscordAcpConversationId(conversationId),
|
|
539
|
+
matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) => matchDiscordAcpConversation({
|
|
540
|
+
bindingConversationId: compiledBinding.conversationId,
|
|
541
|
+
conversationId,
|
|
542
|
+
parentConversationId
|
|
543
|
+
}),
|
|
544
|
+
resolveCommandConversation: ({ threadId, threadParentId, parentSessionKey, from, chatType, originatingTo, commandTo, fallbackTo }) => resolveDiscordCommandConversation({
|
|
545
|
+
threadId,
|
|
546
|
+
threadParentId,
|
|
547
|
+
parentSessionKey,
|
|
548
|
+
from,
|
|
549
|
+
chatType,
|
|
550
|
+
originatingTo,
|
|
551
|
+
commandTo,
|
|
552
|
+
fallbackTo
|
|
553
|
+
})
|
|
554
|
+
},
|
|
555
|
+
conversationBindings: {
|
|
556
|
+
supportsCurrentConversationBinding: true,
|
|
557
|
+
defaultTopLevelPlacement: "child",
|
|
558
|
+
createManager: async ({ cfg, accountId }) => (await loadDiscordThreadBindingsManagerModule()).createThreadBindingManager({
|
|
559
|
+
cfg,
|
|
560
|
+
accountId: accountId ?? void 0,
|
|
561
|
+
persist: false,
|
|
562
|
+
enableSweeper: false
|
|
563
|
+
}),
|
|
564
|
+
setIdleTimeoutBySessionKey: ({ targetSessionKey, accountId, idleTimeoutMs }) => setThreadBindingIdleTimeoutBySessionKey({
|
|
565
|
+
targetSessionKey,
|
|
566
|
+
accountId: accountId ?? void 0,
|
|
567
|
+
idleTimeoutMs
|
|
568
|
+
}).map(toConversationLifecycleBinding),
|
|
569
|
+
setMaxAgeBySessionKey: ({ targetSessionKey, accountId, maxAgeMs }) => setThreadBindingMaxAgeBySessionKey({
|
|
570
|
+
targetSessionKey,
|
|
571
|
+
accountId: accountId ?? void 0,
|
|
572
|
+
maxAgeMs
|
|
573
|
+
}).map(toConversationLifecycleBinding)
|
|
574
|
+
},
|
|
575
|
+
heartbeat: { sendTyping: async ({ cfg, to, accountId, threadId }) => {
|
|
576
|
+
const target = parseDiscordTarget(resolveDiscordAttachedOutboundTarget({
|
|
577
|
+
to,
|
|
578
|
+
threadId
|
|
579
|
+
}), { defaultKind: "channel" });
|
|
580
|
+
if (!target || target.kind !== "channel") return;
|
|
581
|
+
await (await loadDiscordSendModule()).sendTypingDiscord(target.id, {
|
|
582
|
+
cfg,
|
|
583
|
+
accountId: accountId ?? void 0
|
|
584
|
+
});
|
|
585
|
+
} },
|
|
586
|
+
status: createComputedAccountStatusAdapter({
|
|
587
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, {
|
|
588
|
+
connected: false,
|
|
589
|
+
reconnectAttempts: 0,
|
|
590
|
+
lastConnectedAt: null,
|
|
591
|
+
lastDisconnect: null,
|
|
592
|
+
lastEventAt: null
|
|
593
|
+
}),
|
|
594
|
+
collectStatusIssues: collectDiscordStatusIssues,
|
|
595
|
+
buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot, { includeMode: false }),
|
|
596
|
+
probeAccount: async ({ account, timeoutMs }) => (await loadDiscordProbeRuntime()).probeDiscord(account.token, timeoutMs, { includeApplication: true }),
|
|
597
|
+
formatCapabilitiesProbe: ({ probe }) => {
|
|
598
|
+
const discordProbe = probe;
|
|
599
|
+
const lines = [];
|
|
600
|
+
if (discordProbe?.bot?.username) {
|
|
601
|
+
const botId = discordProbe.bot.id ? ` (${discordProbe.bot.id})` : "";
|
|
602
|
+
lines.push({ text: `Bot: @${discordProbe.bot.username}${botId}` });
|
|
603
|
+
}
|
|
604
|
+
if (discordProbe?.application?.intents) lines.push({ text: `Intents: ${formatDiscordIntents(discordProbe.application.intents)}` });
|
|
605
|
+
return lines;
|
|
606
|
+
},
|
|
607
|
+
buildCapabilitiesDiagnostics: async ({ account, target }) => {
|
|
608
|
+
if (!target?.trim()) return;
|
|
609
|
+
const parsedTarget = parseDiscordTarget(target.trim(), { defaultKind: "channel" });
|
|
610
|
+
const details = { target: {
|
|
611
|
+
raw: target,
|
|
612
|
+
normalized: parsedTarget?.normalized,
|
|
613
|
+
kind: parsedTarget?.kind,
|
|
614
|
+
channelId: parsedTarget?.kind === "channel" ? parsedTarget.id : void 0
|
|
615
|
+
} };
|
|
616
|
+
if (!parsedTarget || parsedTarget.kind !== "channel") return {
|
|
617
|
+
details,
|
|
618
|
+
lines: [{
|
|
619
|
+
text: "Permissions: Target looks like a DM user; pass channel:<id> to audit channel permissions.",
|
|
620
|
+
tone: "error"
|
|
621
|
+
}]
|
|
622
|
+
};
|
|
623
|
+
const token = account.token?.trim();
|
|
624
|
+
if (!token) return {
|
|
625
|
+
details,
|
|
626
|
+
lines: [{
|
|
627
|
+
text: "Permissions: Discord bot token missing for permission audit.",
|
|
628
|
+
tone: "error"
|
|
629
|
+
}]
|
|
630
|
+
};
|
|
631
|
+
const statusCfg = { channels: { discord: { accounts: { [account.accountId]: {
|
|
632
|
+
...account.config,
|
|
633
|
+
token
|
|
634
|
+
} } } } };
|
|
635
|
+
try {
|
|
636
|
+
const perms = await (await loadDiscordSendModule()).fetchChannelPermissionsDiscord(parsedTarget.id, {
|
|
637
|
+
cfg: statusCfg,
|
|
638
|
+
token,
|
|
639
|
+
accountId: account.accountId ?? void 0
|
|
640
|
+
});
|
|
641
|
+
const missingRequired = resolveRequiredDiscordChannelPermissions(perms.channelType).filter((permission) => !perms.permissions.includes(permission));
|
|
642
|
+
details.permissions = {
|
|
643
|
+
channelId: perms.channelId,
|
|
644
|
+
guildId: perms.guildId,
|
|
645
|
+
isDm: perms.isDm,
|
|
646
|
+
channelType: perms.channelType,
|
|
647
|
+
permissions: perms.permissions,
|
|
648
|
+
missingRequired,
|
|
649
|
+
raw: perms.raw
|
|
650
|
+
};
|
|
651
|
+
return {
|
|
652
|
+
details,
|
|
653
|
+
lines: [{ text: `Permissions (${perms.channelId}): ${perms.permissions.length ? perms.permissions.join(", ") : "none"}` }, missingRequired.length > 0 ? {
|
|
654
|
+
text: `Missing required: ${missingRequired.join(", ")}`,
|
|
655
|
+
tone: "warn"
|
|
656
|
+
} : {
|
|
657
|
+
text: "Missing required: none",
|
|
658
|
+
tone: "success"
|
|
659
|
+
}]
|
|
660
|
+
};
|
|
661
|
+
} catch (err) {
|
|
662
|
+
const message = formatErrorMessage(err);
|
|
663
|
+
details.permissions = {
|
|
664
|
+
channelId: parsedTarget.id,
|
|
665
|
+
error: message
|
|
666
|
+
};
|
|
667
|
+
return {
|
|
668
|
+
details,
|
|
669
|
+
lines: [{
|
|
670
|
+
text: `Permissions: ${message}`,
|
|
671
|
+
tone: "error"
|
|
672
|
+
}]
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
auditAccount: async ({ account, timeoutMs, cfg }) => {
|
|
677
|
+
const { auditDiscordChannelPermissions, collectDiscordAuditChannelIds } = await loadDiscordAuditModule();
|
|
678
|
+
const { channelIds, unresolvedChannels } = collectDiscordAuditChannelIds({
|
|
679
|
+
cfg,
|
|
680
|
+
accountId: account.accountId
|
|
681
|
+
});
|
|
682
|
+
if (!channelIds.length && unresolvedChannels === 0) return;
|
|
683
|
+
const botToken = account.token?.trim();
|
|
684
|
+
if (!botToken) return {
|
|
685
|
+
ok: unresolvedChannels === 0,
|
|
686
|
+
checkedChannels: 0,
|
|
687
|
+
unresolvedChannels,
|
|
688
|
+
channels: [],
|
|
689
|
+
elapsedMs: 0
|
|
690
|
+
};
|
|
691
|
+
return {
|
|
692
|
+
...await auditDiscordChannelPermissions({
|
|
693
|
+
cfg,
|
|
694
|
+
token: botToken,
|
|
695
|
+
accountId: account.accountId,
|
|
696
|
+
channelIds,
|
|
697
|
+
timeoutMs
|
|
698
|
+
}),
|
|
699
|
+
unresolvedChannels
|
|
700
|
+
};
|
|
701
|
+
},
|
|
702
|
+
resolveAccountSnapshot: ({ account, runtime, probe, audit }) => {
|
|
703
|
+
const configured = resolveConfiguredFromCredentialStatuses(account) ?? Boolean(account.token?.trim());
|
|
704
|
+
const app = runtime?.application ?? probe?.application;
|
|
705
|
+
const bot = runtime?.bot ?? probe?.bot;
|
|
706
|
+
return {
|
|
707
|
+
accountId: account.accountId,
|
|
708
|
+
name: account.name,
|
|
709
|
+
enabled: account.enabled,
|
|
710
|
+
configured,
|
|
711
|
+
extra: {
|
|
712
|
+
...projectCredentialSnapshotFields(account),
|
|
713
|
+
connected: runtime?.connected ?? false,
|
|
714
|
+
reconnectAttempts: runtime?.reconnectAttempts,
|
|
715
|
+
lastConnectedAt: runtime?.lastConnectedAt ?? null,
|
|
716
|
+
lastDisconnect: runtime?.lastDisconnect ?? null,
|
|
717
|
+
lastEventAt: runtime?.lastEventAt ?? null,
|
|
718
|
+
application: app ?? void 0,
|
|
719
|
+
bot: bot ?? void 0,
|
|
720
|
+
audit
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
}),
|
|
725
|
+
gateway: { startAccount: async (ctx) => {
|
|
726
|
+
const account = ctx.account;
|
|
727
|
+
if (account.tokenStatus === "configured_unavailable") throw new Error(`Discord bot token configured for account "${account.accountId}" is unavailable; resolve SecretRefs against the active runtime snapshot before using this account.`);
|
|
728
|
+
const startupDelayMs = resolveDiscordStartupDelayMs(ctx.cfg, account.accountId);
|
|
729
|
+
if (startupDelayMs > 0) {
|
|
730
|
+
ctx.log?.info(`[${account.accountId}] delaying provider startup ${Math.round(startupDelayMs / 1e3)}s to reduce Discord startup rate limits`);
|
|
731
|
+
try {
|
|
732
|
+
await sleepWithAbort(startupDelayMs, ctx.abortSignal);
|
|
733
|
+
} catch {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
const token = account.token.trim();
|
|
738
|
+
startDiscordStartupProbe({
|
|
739
|
+
accountId: account.accountId,
|
|
740
|
+
token,
|
|
741
|
+
abortSignal: ctx.abortSignal,
|
|
742
|
+
setStatus: ctx.setStatus,
|
|
743
|
+
log: ctx.log
|
|
744
|
+
});
|
|
745
|
+
ctx.log?.info(`[${account.accountId}] starting provider`);
|
|
746
|
+
return (await loadDiscordProviderRuntime()).monitorDiscordProvider({
|
|
747
|
+
token,
|
|
748
|
+
accountId: account.accountId,
|
|
749
|
+
config: ctx.cfg,
|
|
750
|
+
runtime: ctx.runtime,
|
|
751
|
+
channelRuntime: ctx.channelRuntime,
|
|
752
|
+
abortSignal: ctx.abortSignal,
|
|
753
|
+
mediaMaxMb: account.config.mediaMaxMb,
|
|
754
|
+
historyLimit: account.config.historyLimit,
|
|
755
|
+
setStatus: (patch) => ctx.setStatus({
|
|
756
|
+
accountId: account.accountId,
|
|
757
|
+
...patch
|
|
758
|
+
})
|
|
759
|
+
});
|
|
760
|
+
} }
|
|
761
|
+
},
|
|
762
|
+
pairing: { text: {
|
|
763
|
+
idLabel: "discordUserId",
|
|
764
|
+
message: PAIRING_APPROVED_MESSAGE,
|
|
765
|
+
normalizeAllowEntry: createPairingPrefixStripper(/^(discord|user):/i),
|
|
766
|
+
notify: async ({ cfg, id, message, accountId }) => {
|
|
767
|
+
await (await loadDiscordSendModule()).sendMessageDiscord(`user:${id}`, message, {
|
|
768
|
+
cfg,
|
|
769
|
+
...accountId ? { accountId } : {}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
} },
|
|
773
|
+
security: discordSecurityAdapter,
|
|
774
|
+
threading: { scopedAccountReplyToMode: {
|
|
775
|
+
resolveAccount: (cfg, accountId) => resolveDiscordAccount({
|
|
776
|
+
cfg,
|
|
777
|
+
accountId
|
|
778
|
+
}),
|
|
779
|
+
resolveReplyToMode: (account) => account.config.replyToMode,
|
|
780
|
+
fallback: "off"
|
|
781
|
+
} },
|
|
782
|
+
outbound: {
|
|
783
|
+
...discordOutbound,
|
|
784
|
+
preferFinalAssistantVisibleText: true,
|
|
785
|
+
shouldTreatDeliveredTextAsVisible: shouldTreatDiscordDeliveredTextAsVisible,
|
|
786
|
+
shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload, hint }) => shouldSuppressLocalDiscordExecApprovalPrompt({
|
|
787
|
+
cfg,
|
|
788
|
+
accountId,
|
|
789
|
+
payload,
|
|
790
|
+
hint
|
|
791
|
+
})
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
//#endregion
|
|
795
|
+
export { resolveDiscordGroupToolPolicy as i, collectDiscordStatusIssues as n, resolveDiscordGroupRequireMention as r, discordPlugin as t };
|