@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,534 @@
|
|
|
1
|
+
import { Ut as resolveDiscordChannelId, Wt as __exportAll, pt as getChannel } from "./send.receipt-CDn3GBWC.js";
|
|
2
|
+
import { N as createDiscordRestClient } from "./send.shared-D4iBnAmn.js";
|
|
3
|
+
import { A as DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS, C as resolveThreadBindingMaxAgeMs$1, D as shouldDefaultPersist, E as setBindingRecord, M as THREAD_BINDINGS_SWEEP_INTERVAL_MS, S as resolveThreadBindingMaxAgeExpiresAt, T as saveBindingsToDisk, _ as resetThreadBindingsForTests, a as THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS, b as resolveThreadBindingIdleTimeoutMs$1, c as getThreadBindingToken, d as normalizeThreadBindingDurationMs, f as normalizeThreadId, g as removeBindingRecord, h as rememberThreadBindingToken, n as MANAGERS_BY_ACCOUNT_ID, o as ensureBindingsLoaded, p as rememberRecentUnboundWebhookEcho, r as PERSIST_BY_ACCOUNT_ID, s as forgetThreadBindingToken, t as BINDINGS_BY_THREAD_ID, u as normalizeTargetKind, v as resolveBindingIdsForSession, w as resolveThreadBindingsPath, x as resolveThreadBindingInactivityExpiresAt, y as resolveBindingRecordKey } from "./thread-bindings.state-eTFl-PqJ.js";
|
|
4
|
+
import { a as isThreadArchived, b as resolveThreadBindingThreadName, c as summarizeDiscordError, i as isDiscordThreadGoneError, n as createWebhookForChannel, o as maybeSendBindingMessage, r as findReusableWebhook, s as resolveChannelIdForBinding, t as createThreadForBinding, v as resolveThreadBindingFarewellText } from "./thread-bindings.discord-api-ssGH5wc2.js";
|
|
5
|
+
import { normalizeOptionalString } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
+
import { normalizeAccountId, resolveAgentIdFromSessionKey } from "klaw/plugin-sdk/routing";
|
|
7
|
+
import { getRuntimeConfigSnapshot } from "klaw/plugin-sdk/runtime-config-snapshot";
|
|
8
|
+
import { logVerbose } from "klaw/plugin-sdk/runtime-env";
|
|
9
|
+
import { registerSessionBindingAdapter, resolveThreadBindingConversationIdFromBindingId, unregisterSessionBindingAdapter } from "klaw/plugin-sdk/conversation-runtime";
|
|
10
|
+
//#region extensions/discord/src/monitor/thread-bindings.session-adapter.ts
|
|
11
|
+
function normalizeChildBindingParentChannelId(raw) {
|
|
12
|
+
const trimmed = normalizeOptionalString(raw) ?? "";
|
|
13
|
+
if (!trimmed) return;
|
|
14
|
+
try {
|
|
15
|
+
return resolveDiscordChannelId(trimmed);
|
|
16
|
+
} catch {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function toSessionBindingTargetKind(raw) {
|
|
21
|
+
return raw === "subagent" ? "subagent" : "session";
|
|
22
|
+
}
|
|
23
|
+
function toThreadBindingTargetKind(raw) {
|
|
24
|
+
return raw === "subagent" ? "subagent" : "acp";
|
|
25
|
+
}
|
|
26
|
+
function resolveEffectiveBindingExpiresAt(params) {
|
|
27
|
+
const inactivityExpiresAt = resolveThreadBindingInactivityExpiresAt({
|
|
28
|
+
record: params.record,
|
|
29
|
+
defaultIdleTimeoutMs: params.defaultIdleTimeoutMs
|
|
30
|
+
});
|
|
31
|
+
const maxAgeExpiresAt = resolveThreadBindingMaxAgeExpiresAt({
|
|
32
|
+
record: params.record,
|
|
33
|
+
defaultMaxAgeMs: params.defaultMaxAgeMs
|
|
34
|
+
});
|
|
35
|
+
if (inactivityExpiresAt != null && maxAgeExpiresAt != null) return Math.min(inactivityExpiresAt, maxAgeExpiresAt);
|
|
36
|
+
return inactivityExpiresAt ?? maxAgeExpiresAt;
|
|
37
|
+
}
|
|
38
|
+
function toSessionBindingRecord(record, defaults) {
|
|
39
|
+
return {
|
|
40
|
+
bindingId: resolveBindingRecordKey({
|
|
41
|
+
accountId: record.accountId,
|
|
42
|
+
threadId: record.threadId
|
|
43
|
+
}) ?? `${record.accountId}:${record.threadId}`,
|
|
44
|
+
targetSessionKey: record.targetSessionKey,
|
|
45
|
+
targetKind: toSessionBindingTargetKind(record.targetKind),
|
|
46
|
+
conversation: {
|
|
47
|
+
channel: "discord",
|
|
48
|
+
accountId: record.accountId,
|
|
49
|
+
conversationId: record.threadId,
|
|
50
|
+
parentConversationId: record.channelId
|
|
51
|
+
},
|
|
52
|
+
status: "active",
|
|
53
|
+
boundAt: record.boundAt,
|
|
54
|
+
expiresAt: resolveEffectiveBindingExpiresAt({
|
|
55
|
+
record,
|
|
56
|
+
defaultIdleTimeoutMs: defaults.idleTimeoutMs,
|
|
57
|
+
defaultMaxAgeMs: defaults.maxAgeMs
|
|
58
|
+
}),
|
|
59
|
+
metadata: {
|
|
60
|
+
agentId: record.agentId,
|
|
61
|
+
label: record.label,
|
|
62
|
+
webhookId: record.webhookId,
|
|
63
|
+
webhookToken: record.webhookToken,
|
|
64
|
+
boundBy: record.boundBy,
|
|
65
|
+
lastActivityAt: record.lastActivityAt,
|
|
66
|
+
idleTimeoutMs: resolveThreadBindingIdleTimeoutMs$1({
|
|
67
|
+
record,
|
|
68
|
+
defaultIdleTimeoutMs: defaults.idleTimeoutMs
|
|
69
|
+
}),
|
|
70
|
+
maxAgeMs: resolveThreadBindingMaxAgeMs$1({
|
|
71
|
+
record,
|
|
72
|
+
defaultMaxAgeMs: defaults.maxAgeMs
|
|
73
|
+
}),
|
|
74
|
+
...record.metadata
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function createThreadBindingSessionAdapter(params) {
|
|
79
|
+
const toRecord = (entry) => toSessionBindingRecord(entry, params.defaults);
|
|
80
|
+
return {
|
|
81
|
+
channel: "discord",
|
|
82
|
+
accountId: params.accountId,
|
|
83
|
+
capabilities: { placements: ["current", "child"] },
|
|
84
|
+
bind: async (input) => {
|
|
85
|
+
if (input.conversation.channel !== "discord") return null;
|
|
86
|
+
const targetSessionKey = input.targetSessionKey.trim();
|
|
87
|
+
if (!targetSessionKey) return null;
|
|
88
|
+
const conversationId = normalizeOptionalString(input.conversation.conversationId) ?? "";
|
|
89
|
+
const placement = input.placement === "child" ? "child" : "current";
|
|
90
|
+
const metadata = input.metadata ?? {};
|
|
91
|
+
const label = normalizeOptionalString(metadata.label);
|
|
92
|
+
const threadName = typeof metadata.threadName === "string" ? normalizeOptionalString(metadata.threadName) : void 0;
|
|
93
|
+
const introText = typeof metadata.introText === "string" ? normalizeOptionalString(metadata.introText) : void 0;
|
|
94
|
+
const boundBy = typeof metadata.boundBy === "string" ? normalizeOptionalString(metadata.boundBy) : void 0;
|
|
95
|
+
const agentId = typeof metadata.agentId === "string" ? normalizeOptionalString(metadata.agentId) : void 0;
|
|
96
|
+
let threadId;
|
|
97
|
+
let channelId;
|
|
98
|
+
let createThread = false;
|
|
99
|
+
if (placement === "child") {
|
|
100
|
+
createThread = true;
|
|
101
|
+
channelId = normalizeChildBindingParentChannelId(input.conversation.parentConversationId);
|
|
102
|
+
if (!channelId && conversationId) channelId = await resolveChannelIdForBinding({
|
|
103
|
+
cfg: params.resolveCurrentCfg(),
|
|
104
|
+
accountId: params.accountId,
|
|
105
|
+
token: params.resolveCurrentToken(),
|
|
106
|
+
threadId: conversationId
|
|
107
|
+
}) ?? void 0;
|
|
108
|
+
} else threadId = conversationId || void 0;
|
|
109
|
+
const bound = await params.manager.bindTarget({
|
|
110
|
+
threadId,
|
|
111
|
+
channelId,
|
|
112
|
+
createThread,
|
|
113
|
+
threadName,
|
|
114
|
+
targetKind: toThreadBindingTargetKind(input.targetKind),
|
|
115
|
+
targetSessionKey,
|
|
116
|
+
agentId,
|
|
117
|
+
label,
|
|
118
|
+
boundBy,
|
|
119
|
+
introText,
|
|
120
|
+
metadata
|
|
121
|
+
});
|
|
122
|
+
return bound ? toRecord(bound) : null;
|
|
123
|
+
},
|
|
124
|
+
listBySession: (targetSessionKey) => params.manager.listBySessionKey(targetSessionKey).map(toRecord),
|
|
125
|
+
resolveByConversation: (ref) => {
|
|
126
|
+
if (ref.channel !== "discord") return null;
|
|
127
|
+
const binding = params.manager.getByThreadId(ref.conversationId);
|
|
128
|
+
return binding ? toRecord(binding) : null;
|
|
129
|
+
},
|
|
130
|
+
touch: (bindingId, at) => {
|
|
131
|
+
const threadId = resolveThreadBindingConversationIdFromBindingId({
|
|
132
|
+
accountId: params.accountId,
|
|
133
|
+
bindingId
|
|
134
|
+
});
|
|
135
|
+
if (!threadId) return;
|
|
136
|
+
params.manager.touchThread({
|
|
137
|
+
threadId,
|
|
138
|
+
at,
|
|
139
|
+
persist: true
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
unbind: async (input) => {
|
|
143
|
+
if (input.targetSessionKey?.trim()) return params.manager.unbindBySessionKey({
|
|
144
|
+
targetSessionKey: input.targetSessionKey,
|
|
145
|
+
reason: input.reason
|
|
146
|
+
}).map(toRecord);
|
|
147
|
+
const threadId = resolveThreadBindingConversationIdFromBindingId({
|
|
148
|
+
accountId: params.accountId,
|
|
149
|
+
bindingId: input.bindingId
|
|
150
|
+
});
|
|
151
|
+
if (!threadId) return [];
|
|
152
|
+
const removed = params.manager.unbindThread({
|
|
153
|
+
threadId,
|
|
154
|
+
reason: input.reason
|
|
155
|
+
});
|
|
156
|
+
return removed ? [toRecord(removed)] : [];
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region extensions/discord/src/monitor/thread-bindings.manager.ts
|
|
162
|
+
var thread_bindings_manager_exports = /* @__PURE__ */ __exportAll({
|
|
163
|
+
__testing: () => testing,
|
|
164
|
+
createNoopThreadBindingManager: () => createNoopThreadBindingManager,
|
|
165
|
+
createThreadBindingManager: () => createThreadBindingManager,
|
|
166
|
+
getThreadBindingManager: () => getThreadBindingManager,
|
|
167
|
+
testing: () => testing
|
|
168
|
+
});
|
|
169
|
+
function registerManager(manager) {
|
|
170
|
+
MANAGERS_BY_ACCOUNT_ID.set(manager.accountId, manager);
|
|
171
|
+
}
|
|
172
|
+
function unregisterManager(accountId, manager) {
|
|
173
|
+
if (MANAGERS_BY_ACCOUNT_ID.get(accountId) === manager) MANAGERS_BY_ACCOUNT_ID.delete(accountId);
|
|
174
|
+
}
|
|
175
|
+
const SWEEPERS_BY_ACCOUNT_ID = /* @__PURE__ */ new Map();
|
|
176
|
+
function createNoopManager(accountIdRaw) {
|
|
177
|
+
return {
|
|
178
|
+
accountId: normalizeAccountId(accountIdRaw),
|
|
179
|
+
getIdleTimeoutMs: () => DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS,
|
|
180
|
+
getMaxAgeMs: () => 0,
|
|
181
|
+
getByThreadId: () => void 0,
|
|
182
|
+
getBySessionKey: () => void 0,
|
|
183
|
+
listBySessionKey: () => [],
|
|
184
|
+
listBindings: () => [],
|
|
185
|
+
touchThread: () => null,
|
|
186
|
+
bindTarget: async () => null,
|
|
187
|
+
unbindThread: () => null,
|
|
188
|
+
unbindBySessionKey: () => [],
|
|
189
|
+
stop: () => {}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function isDirectConversationBindingId(value) {
|
|
193
|
+
const trimmed = normalizeOptionalString(value);
|
|
194
|
+
return Boolean(trimmed && /^(user:|channel:)/i.test(trimmed));
|
|
195
|
+
}
|
|
196
|
+
function createThreadBindingManager(params) {
|
|
197
|
+
ensureBindingsLoaded();
|
|
198
|
+
const accountId = normalizeAccountId(params.accountId);
|
|
199
|
+
const existing = MANAGERS_BY_ACCOUNT_ID.get(accountId);
|
|
200
|
+
if (existing) {
|
|
201
|
+
rememberThreadBindingToken({
|
|
202
|
+
accountId,
|
|
203
|
+
token: params.token
|
|
204
|
+
});
|
|
205
|
+
return existing;
|
|
206
|
+
}
|
|
207
|
+
rememberThreadBindingToken({
|
|
208
|
+
accountId,
|
|
209
|
+
token: params.token
|
|
210
|
+
});
|
|
211
|
+
const persist = params.persist ?? shouldDefaultPersist();
|
|
212
|
+
PERSIST_BY_ACCOUNT_ID.set(accountId, persist);
|
|
213
|
+
const idleTimeoutMs = normalizeThreadBindingDurationMs(params.idleTimeoutMs, DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS);
|
|
214
|
+
const maxAgeMs = normalizeThreadBindingDurationMs(params.maxAgeMs, 0);
|
|
215
|
+
const resolveCurrentCfg = () => getRuntimeConfigSnapshot() ?? params.cfg;
|
|
216
|
+
const resolveCurrentToken = () => getThreadBindingToken(accountId) ?? params.token;
|
|
217
|
+
let sweepTimer = null;
|
|
218
|
+
const runSweepOnce = async () => {
|
|
219
|
+
const bindings = manager.listBindings();
|
|
220
|
+
if (bindings.length === 0) return;
|
|
221
|
+
let rest = null;
|
|
222
|
+
for (const snapshotBinding of bindings) {
|
|
223
|
+
const binding = manager.getByThreadId(snapshotBinding.threadId);
|
|
224
|
+
if (!binding) continue;
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
const inactivityExpiresAt = resolveThreadBindingInactivityExpiresAt({
|
|
227
|
+
record: binding,
|
|
228
|
+
defaultIdleTimeoutMs: idleTimeoutMs
|
|
229
|
+
});
|
|
230
|
+
const maxAgeExpiresAt = resolveThreadBindingMaxAgeExpiresAt({
|
|
231
|
+
record: binding,
|
|
232
|
+
defaultMaxAgeMs: maxAgeMs
|
|
233
|
+
});
|
|
234
|
+
const expirationCandidates = [];
|
|
235
|
+
if (inactivityExpiresAt != null && now >= inactivityExpiresAt) expirationCandidates.push({
|
|
236
|
+
reason: "idle-expired",
|
|
237
|
+
at: inactivityExpiresAt
|
|
238
|
+
});
|
|
239
|
+
if (maxAgeExpiresAt != null && now >= maxAgeExpiresAt) expirationCandidates.push({
|
|
240
|
+
reason: "max-age-expired",
|
|
241
|
+
at: maxAgeExpiresAt
|
|
242
|
+
});
|
|
243
|
+
if (expirationCandidates.length > 0) {
|
|
244
|
+
expirationCandidates.sort((a, b) => a.at - b.at);
|
|
245
|
+
const reason = expirationCandidates[0]?.reason ?? "idle-expired";
|
|
246
|
+
manager.unbindThread({
|
|
247
|
+
threadId: binding.threadId,
|
|
248
|
+
reason,
|
|
249
|
+
sendFarewell: true,
|
|
250
|
+
farewellText: resolveThreadBindingFarewellText({
|
|
251
|
+
reason,
|
|
252
|
+
idleTimeoutMs: resolveThreadBindingIdleTimeoutMs$1({
|
|
253
|
+
record: binding,
|
|
254
|
+
defaultIdleTimeoutMs: idleTimeoutMs
|
|
255
|
+
}),
|
|
256
|
+
maxAgeMs: resolveThreadBindingMaxAgeMs$1({
|
|
257
|
+
record: binding,
|
|
258
|
+
defaultMaxAgeMs: maxAgeMs
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (isDirectConversationBindingId(binding.threadId)) continue;
|
|
265
|
+
if (!rest) try {
|
|
266
|
+
rest = createDiscordRestClient({
|
|
267
|
+
cfg: resolveCurrentCfg(),
|
|
268
|
+
accountId,
|
|
269
|
+
token: resolveCurrentToken()
|
|
270
|
+
}).rest;
|
|
271
|
+
} catch {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const channel = await getChannel(rest, binding.threadId);
|
|
276
|
+
if (!channel || typeof channel !== "object") {
|
|
277
|
+
logVerbose(`discord thread binding sweep probe returned invalid payload for ${binding.threadId}`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (isThreadArchived(channel)) manager.unbindThread({
|
|
281
|
+
threadId: binding.threadId,
|
|
282
|
+
reason: "thread-archived",
|
|
283
|
+
sendFarewell: true
|
|
284
|
+
});
|
|
285
|
+
} catch (err) {
|
|
286
|
+
if (isDiscordThreadGoneError(err)) {
|
|
287
|
+
logVerbose(`discord thread binding sweep removing stale binding ${binding.threadId}: ${summarizeDiscordError(err)}`);
|
|
288
|
+
manager.unbindThread({
|
|
289
|
+
threadId: binding.threadId,
|
|
290
|
+
reason: "thread-delete",
|
|
291
|
+
sendFarewell: false
|
|
292
|
+
});
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
logVerbose(`discord thread binding sweep probe failed for ${binding.threadId}: ${summarizeDiscordError(err)}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
SWEEPERS_BY_ACCOUNT_ID.set(accountId, runSweepOnce);
|
|
300
|
+
const manager = {
|
|
301
|
+
accountId,
|
|
302
|
+
getIdleTimeoutMs: () => idleTimeoutMs,
|
|
303
|
+
getMaxAgeMs: () => maxAgeMs,
|
|
304
|
+
getByThreadId: (threadId) => {
|
|
305
|
+
const key = resolveBindingRecordKey({
|
|
306
|
+
accountId,
|
|
307
|
+
threadId
|
|
308
|
+
});
|
|
309
|
+
if (!key) return;
|
|
310
|
+
const entry = BINDINGS_BY_THREAD_ID.get(key);
|
|
311
|
+
if (!entry || entry.accountId !== accountId) return;
|
|
312
|
+
return entry;
|
|
313
|
+
},
|
|
314
|
+
getBySessionKey: (targetSessionKey) => {
|
|
315
|
+
return manager.listBySessionKey(targetSessionKey)[0];
|
|
316
|
+
},
|
|
317
|
+
listBySessionKey: (targetSessionKey) => {
|
|
318
|
+
return resolveBindingIdsForSession({
|
|
319
|
+
targetSessionKey,
|
|
320
|
+
accountId
|
|
321
|
+
}).map((bindingKey) => BINDINGS_BY_THREAD_ID.get(bindingKey)).filter((entry) => Boolean(entry));
|
|
322
|
+
},
|
|
323
|
+
listBindings: () => [...BINDINGS_BY_THREAD_ID.values()].filter((entry) => entry.accountId === accountId),
|
|
324
|
+
touchThread: (touchParams) => {
|
|
325
|
+
const key = resolveBindingRecordKey({
|
|
326
|
+
accountId,
|
|
327
|
+
threadId: touchParams.threadId
|
|
328
|
+
});
|
|
329
|
+
if (!key) return null;
|
|
330
|
+
const existing = BINDINGS_BY_THREAD_ID.get(key);
|
|
331
|
+
if (!existing || existing.accountId !== accountId) return null;
|
|
332
|
+
const now = Date.now();
|
|
333
|
+
const at = typeof touchParams.at === "number" && Number.isFinite(touchParams.at) ? Math.max(0, Math.floor(touchParams.at)) : now;
|
|
334
|
+
const nextRecord = {
|
|
335
|
+
...existing,
|
|
336
|
+
lastActivityAt: Math.max(existing.lastActivityAt || 0, at)
|
|
337
|
+
};
|
|
338
|
+
setBindingRecord(nextRecord);
|
|
339
|
+
if (touchParams.persist ?? persist) saveBindingsToDisk({ minIntervalMs: THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS });
|
|
340
|
+
return nextRecord;
|
|
341
|
+
},
|
|
342
|
+
bindTarget: async (bindParams) => {
|
|
343
|
+
const cfg = resolveCurrentCfg();
|
|
344
|
+
let threadId = normalizeThreadId(bindParams.threadId);
|
|
345
|
+
let channelId = normalizeOptionalString(bindParams.channelId) ?? "";
|
|
346
|
+
const directConversationBinding = isDirectConversationBindingId(threadId) || isDirectConversationBindingId(channelId);
|
|
347
|
+
if (!threadId && bindParams.createThread) {
|
|
348
|
+
if (!channelId) return null;
|
|
349
|
+
const threadName = resolveThreadBindingThreadName({
|
|
350
|
+
agentId: bindParams.agentId,
|
|
351
|
+
label: bindParams.label
|
|
352
|
+
});
|
|
353
|
+
threadId = await createThreadForBinding({
|
|
354
|
+
cfg,
|
|
355
|
+
accountId,
|
|
356
|
+
token: resolveCurrentToken(),
|
|
357
|
+
channelId,
|
|
358
|
+
threadName: normalizeOptionalString(bindParams.threadName) ?? threadName
|
|
359
|
+
}) ?? void 0;
|
|
360
|
+
}
|
|
361
|
+
if (!threadId) return null;
|
|
362
|
+
if (!channelId && directConversationBinding) channelId = threadId;
|
|
363
|
+
if (!channelId) channelId = await resolveChannelIdForBinding({
|
|
364
|
+
cfg,
|
|
365
|
+
accountId,
|
|
366
|
+
token: resolveCurrentToken(),
|
|
367
|
+
threadId,
|
|
368
|
+
channelId: bindParams.channelId
|
|
369
|
+
}) ?? "";
|
|
370
|
+
if (!channelId) return null;
|
|
371
|
+
const existing = manager.getByThreadId(threadId);
|
|
372
|
+
const targetSessionKey = normalizeOptionalString(bindParams.targetSessionKey) ?? "";
|
|
373
|
+
if (!targetSessionKey) return null;
|
|
374
|
+
const targetKind = normalizeTargetKind(bindParams.targetKind, targetSessionKey);
|
|
375
|
+
let webhookId = normalizeOptionalString(bindParams.webhookId) ?? normalizeOptionalString(existing?.webhookId) ?? "";
|
|
376
|
+
let webhookToken = normalizeOptionalString(bindParams.webhookToken) ?? normalizeOptionalString(existing?.webhookToken) ?? "";
|
|
377
|
+
if (!directConversationBinding && (!webhookId || !webhookToken)) {
|
|
378
|
+
const cachedWebhook = findReusableWebhook({
|
|
379
|
+
accountId,
|
|
380
|
+
channelId
|
|
381
|
+
});
|
|
382
|
+
webhookId = cachedWebhook.webhookId ?? "";
|
|
383
|
+
webhookToken = cachedWebhook.webhookToken ?? "";
|
|
384
|
+
}
|
|
385
|
+
if (!directConversationBinding && (!webhookId || !webhookToken)) {
|
|
386
|
+
const createdWebhook = await createWebhookForChannel({
|
|
387
|
+
cfg,
|
|
388
|
+
accountId,
|
|
389
|
+
token: resolveCurrentToken(),
|
|
390
|
+
channelId
|
|
391
|
+
});
|
|
392
|
+
webhookId = createdWebhook.webhookId ?? "";
|
|
393
|
+
webhookToken = createdWebhook.webhookToken ?? "";
|
|
394
|
+
}
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
const record = {
|
|
397
|
+
accountId,
|
|
398
|
+
channelId,
|
|
399
|
+
threadId,
|
|
400
|
+
targetKind,
|
|
401
|
+
targetSessionKey,
|
|
402
|
+
agentId: normalizeOptionalString(bindParams.agentId) ?? normalizeOptionalString(existing?.agentId) ?? resolveAgentIdFromSessionKey(targetSessionKey),
|
|
403
|
+
label: normalizeOptionalString(bindParams.label) ?? normalizeOptionalString(existing?.label),
|
|
404
|
+
webhookId: webhookId || void 0,
|
|
405
|
+
webhookToken: webhookToken || void 0,
|
|
406
|
+
boundBy: normalizeOptionalString(bindParams.boundBy) ?? normalizeOptionalString(existing?.boundBy) ?? "system",
|
|
407
|
+
boundAt: now,
|
|
408
|
+
lastActivityAt: now,
|
|
409
|
+
idleTimeoutMs: typeof existing?.idleTimeoutMs === "number" ? existing.idleTimeoutMs : idleTimeoutMs,
|
|
410
|
+
maxAgeMs: typeof existing?.maxAgeMs === "number" ? existing.maxAgeMs : maxAgeMs,
|
|
411
|
+
metadata: bindParams.metadata && typeof bindParams.metadata === "object" ? {
|
|
412
|
+
...existing?.metadata,
|
|
413
|
+
...bindParams.metadata
|
|
414
|
+
} : existing?.metadata ? { ...existing.metadata } : void 0
|
|
415
|
+
};
|
|
416
|
+
setBindingRecord(record);
|
|
417
|
+
if (persist) saveBindingsToDisk();
|
|
418
|
+
const introText = bindParams.introText?.trim();
|
|
419
|
+
if (introText && cfg) maybeSendBindingMessage({
|
|
420
|
+
cfg,
|
|
421
|
+
record,
|
|
422
|
+
text: introText
|
|
423
|
+
});
|
|
424
|
+
return record;
|
|
425
|
+
},
|
|
426
|
+
unbindThread: (unbindParams) => {
|
|
427
|
+
const bindingKey = resolveBindingRecordKey({
|
|
428
|
+
accountId,
|
|
429
|
+
threadId: unbindParams.threadId
|
|
430
|
+
});
|
|
431
|
+
if (!bindingKey) return null;
|
|
432
|
+
const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
|
|
433
|
+
if (!existing || existing.accountId !== accountId) return null;
|
|
434
|
+
const removed = removeBindingRecord(bindingKey);
|
|
435
|
+
if (!removed) return null;
|
|
436
|
+
rememberRecentUnboundWebhookEcho(removed);
|
|
437
|
+
if (persist) saveBindingsToDisk();
|
|
438
|
+
if (unbindParams.sendFarewell !== false) {
|
|
439
|
+
const cfg = resolveCurrentCfg();
|
|
440
|
+
const farewell = resolveThreadBindingFarewellText({
|
|
441
|
+
reason: unbindParams.reason,
|
|
442
|
+
farewellText: unbindParams.farewellText,
|
|
443
|
+
idleTimeoutMs: resolveThreadBindingIdleTimeoutMs$1({
|
|
444
|
+
record: removed,
|
|
445
|
+
defaultIdleTimeoutMs: idleTimeoutMs
|
|
446
|
+
}),
|
|
447
|
+
maxAgeMs: resolveThreadBindingMaxAgeMs$1({
|
|
448
|
+
record: removed,
|
|
449
|
+
defaultMaxAgeMs: maxAgeMs
|
|
450
|
+
})
|
|
451
|
+
});
|
|
452
|
+
if (cfg) maybeSendBindingMessage({
|
|
453
|
+
cfg,
|
|
454
|
+
record: removed,
|
|
455
|
+
text: farewell,
|
|
456
|
+
preferWebhook: false
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
return removed;
|
|
460
|
+
},
|
|
461
|
+
unbindBySessionKey: (unbindParams) => {
|
|
462
|
+
const ids = resolveBindingIdsForSession({
|
|
463
|
+
targetSessionKey: unbindParams.targetSessionKey,
|
|
464
|
+
accountId,
|
|
465
|
+
targetKind: unbindParams.targetKind
|
|
466
|
+
});
|
|
467
|
+
if (ids.length === 0) return [];
|
|
468
|
+
const removed = [];
|
|
469
|
+
for (const bindingKey of ids) {
|
|
470
|
+
const binding = BINDINGS_BY_THREAD_ID.get(bindingKey);
|
|
471
|
+
if (!binding) continue;
|
|
472
|
+
const entry = manager.unbindThread({
|
|
473
|
+
threadId: binding.threadId,
|
|
474
|
+
reason: unbindParams.reason,
|
|
475
|
+
sendFarewell: unbindParams.sendFarewell,
|
|
476
|
+
farewellText: unbindParams.farewellText
|
|
477
|
+
});
|
|
478
|
+
if (entry) removed.push(entry);
|
|
479
|
+
}
|
|
480
|
+
return removed;
|
|
481
|
+
},
|
|
482
|
+
stop: () => {
|
|
483
|
+
if (sweepTimer) {
|
|
484
|
+
clearInterval(sweepTimer);
|
|
485
|
+
sweepTimer = null;
|
|
486
|
+
}
|
|
487
|
+
SWEEPERS_BY_ACCOUNT_ID.delete(accountId);
|
|
488
|
+
unregisterManager(accountId, manager);
|
|
489
|
+
unregisterSessionBindingAdapter({
|
|
490
|
+
channel: "discord",
|
|
491
|
+
accountId,
|
|
492
|
+
adapter: sessionBindingAdapter
|
|
493
|
+
});
|
|
494
|
+
forgetThreadBindingToken(accountId);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
if (params.enableSweeper !== false) {
|
|
498
|
+
sweepTimer = setInterval(() => {
|
|
499
|
+
runSweepOnce();
|
|
500
|
+
}, THREAD_BINDINGS_SWEEP_INTERVAL_MS);
|
|
501
|
+
if (!(process.env.VITEST || false)) sweepTimer.unref?.();
|
|
502
|
+
}
|
|
503
|
+
const sessionBindingAdapter = createThreadBindingSessionAdapter({
|
|
504
|
+
accountId,
|
|
505
|
+
manager,
|
|
506
|
+
defaults: {
|
|
507
|
+
idleTimeoutMs,
|
|
508
|
+
maxAgeMs
|
|
509
|
+
},
|
|
510
|
+
resolveCurrentCfg,
|
|
511
|
+
resolveCurrentToken
|
|
512
|
+
});
|
|
513
|
+
registerSessionBindingAdapter(sessionBindingAdapter);
|
|
514
|
+
registerManager(manager);
|
|
515
|
+
return manager;
|
|
516
|
+
}
|
|
517
|
+
function createNoopThreadBindingManager(accountId) {
|
|
518
|
+
return createNoopManager(accountId);
|
|
519
|
+
}
|
|
520
|
+
function getThreadBindingManager(accountId) {
|
|
521
|
+
const normalized = normalizeAccountId(accountId);
|
|
522
|
+
return MANAGERS_BY_ACCOUNT_ID.get(normalized) ?? null;
|
|
523
|
+
}
|
|
524
|
+
const testing = {
|
|
525
|
+
resolveThreadBindingsPath,
|
|
526
|
+
resolveThreadBindingThreadName,
|
|
527
|
+
resetThreadBindingsForTests,
|
|
528
|
+
runThreadBindingSweepForAccount: async (accountId) => {
|
|
529
|
+
const sweep = SWEEPERS_BY_ACCOUNT_ID.get(normalizeAccountId(accountId));
|
|
530
|
+
if (sweep) await sweep();
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
//#endregion
|
|
534
|
+
export { thread_bindings_manager_exports as a, testing as i, createThreadBindingManager as n, getThreadBindingManager as r, createNoopThreadBindingManager as t };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { E as setBindingRecord, O as shouldPersistBindingMutations, T as saveBindingsToDisk, o as ensureBindingsLoaded, t as BINDINGS_BY_THREAD_ID, v as resolveBindingIdsForSession } from "./thread-bindings.state-eTFl-PqJ.js";
|
|
2
|
+
import { normalizeAccountId } from "klaw/plugin-sdk/routing";
|
|
3
|
+
//#region extensions/discord/src/monitor/thread-bindings.session-shared.ts
|
|
4
|
+
function normalizeNonNegativeMs(raw) {
|
|
5
|
+
if (!Number.isFinite(raw)) return 0;
|
|
6
|
+
return Math.max(0, Math.floor(raw));
|
|
7
|
+
}
|
|
8
|
+
function resolveBindingIdsForTargetSession(params) {
|
|
9
|
+
ensureBindingsLoaded();
|
|
10
|
+
const targetSessionKey = params.targetSessionKey.trim();
|
|
11
|
+
if (!targetSessionKey) return [];
|
|
12
|
+
return resolveBindingIdsForSession({
|
|
13
|
+
targetSessionKey,
|
|
14
|
+
accountId: params.accountId ? normalizeAccountId(params.accountId) : void 0,
|
|
15
|
+
targetKind: params.targetKind
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function updateBindingsForTargetSession(ids, update) {
|
|
19
|
+
if (ids.length === 0) return [];
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const updated = [];
|
|
22
|
+
for (const bindingKey of ids) {
|
|
23
|
+
const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
|
|
24
|
+
if (!existing) continue;
|
|
25
|
+
const nextRecord = update(existing, now);
|
|
26
|
+
setBindingRecord(nextRecord);
|
|
27
|
+
updated.push(nextRecord);
|
|
28
|
+
}
|
|
29
|
+
if (updated.length > 0 && shouldPersistBindingMutations()) saveBindingsToDisk({ force: true });
|
|
30
|
+
return updated;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region extensions/discord/src/monitor/thread-bindings.session-updates.ts
|
|
34
|
+
function setThreadBindingIdleTimeoutBySessionKey(params) {
|
|
35
|
+
const ids = resolveBindingIdsForTargetSession(params);
|
|
36
|
+
const idleTimeoutMs = normalizeNonNegativeMs(params.idleTimeoutMs);
|
|
37
|
+
return updateBindingsForTargetSession(ids, (existing, now) => ({
|
|
38
|
+
...existing,
|
|
39
|
+
idleTimeoutMs,
|
|
40
|
+
lastActivityAt: now
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
function setThreadBindingMaxAgeBySessionKey(params) {
|
|
44
|
+
const ids = resolveBindingIdsForTargetSession(params);
|
|
45
|
+
const maxAgeMs = normalizeNonNegativeMs(params.maxAgeMs);
|
|
46
|
+
return updateBindingsForTargetSession(ids, (existing, now) => ({
|
|
47
|
+
...existing,
|
|
48
|
+
maxAgeMs,
|
|
49
|
+
boundAt: now,
|
|
50
|
+
lastActivityAt: now
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { setThreadBindingMaxAgeBySessionKey as n, resolveBindingIdsForTargetSession as r, setThreadBindingIdleTimeoutBySessionKey as t };
|