@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.
Files changed (126) hide show
  1. package/dist/account-inspect-Dqw-enky.js +81 -0
  2. package/dist/account-inspect-api.js +10 -0
  3. package/dist/accounts-B7OBFePq.js +224 -0
  4. package/dist/action-runtime-api.js +2 -0
  5. package/dist/agent-components.runtime-DVY_1VB4.js +4 -0
  6. package/dist/allow-list-B0s7evD7.js +354 -0
  7. package/dist/api-CXAcv9nZ.js +130 -0
  8. package/dist/api.js +23 -0
  9. package/dist/approval-handler.runtime-B9xUAF3n.js +426 -0
  10. package/dist/audit-DoiK49WO.js +24 -0
  11. package/dist/audit-core-BGrq3G7r.js +105 -0
  12. package/dist/channel-U_aeoFwW.js +795 -0
  13. package/dist/channel-actions-BxEBnEuv.js +173 -0
  14. package/dist/channel-actions.runtime-CPtpH-yl.js +263 -0
  15. package/dist/channel-api-BfjklLby.js +21 -0
  16. package/dist/channel-config-api.js +2 -0
  17. package/dist/channel-plugin-api.js +2 -0
  18. package/dist/channel.setup-BUSC0apv.js +337 -0
  19. package/dist/components-luonoe13.js +909 -0
  20. package/dist/config-api-DSYGqaLQ.js +2 -0
  21. package/dist/config-schema-DIqJBGwC.js +357 -0
  22. package/dist/configured-state.js +6 -0
  23. package/dist/contract-api.js +8 -0
  24. package/dist/conversation-identity-DXAm0_Mk.js +270 -0
  25. package/dist/directory-config-CYbuMmPS.js +49 -0
  26. package/dist/directory-contract-api.js +2 -0
  27. package/dist/directory-live-DX4dLRpJ.js +159 -0
  28. package/dist/doctor-bbKSvGVD.js +244 -0
  29. package/dist/doctor-contract-Btjt6NJD.js +383 -0
  30. package/dist/doctor-contract-api.js +2 -0
  31. package/dist/gateway-registry-BKSpa4GB.js +74 -0
  32. package/dist/handle-action.guild-admin-B5BArS2n.js +286 -0
  33. package/dist/inbound-context-WAOqhGlT.js +48 -0
  34. package/dist/inbound-event-delivery-C-1Ji3WP.js +65 -0
  35. package/dist/index.js +26 -0
  36. package/dist/manager.runtime-DXHynKE4.js +2356 -0
  37. package/dist/message-handler-mXzc3tA_.js +381 -0
  38. package/dist/message-handler.preflight-BPD1a347.js +1113 -0
  39. package/dist/message-handler.process-GUa3aV8z.js +1438 -0
  40. package/dist/message-utils-dUbem16p.js +549 -0
  41. package/dist/outbound-adapter-C18OAc1y.js +536 -0
  42. package/dist/pluralkit-D1Q2x0w5.js +22 -0
  43. package/dist/preflight-audio-CZtpWcIm.js +72 -0
  44. package/dist/preflight-audio.runtime-Brx_0_xW.js +7 -0
  45. package/dist/preview-streaming-D_slNIiO.js +8 -0
  46. package/dist/probe-D--Ca4JF.js +139 -0
  47. package/dist/probe.runtime-DQBchZzv.js +2 -0
  48. package/dist/provider-B2-31CIT.js +9565 -0
  49. package/dist/provider-session.runtime-BwzzSsrH.js +6 -0
  50. package/dist/provider.runtime-CP3oHLls.js +2 -0
  51. package/dist/resolve-allowlist-common-CqxPLcJO.js +34 -0
  52. package/dist/resolve-channels-0LX4pUbB.js +265 -0
  53. package/dist/resolve-users-CztOv0Qs.js +120 -0
  54. package/dist/runtime-DUaw66V_.js +1073 -0
  55. package/dist/runtime-api.actions.js +3 -0
  56. package/dist/runtime-api.js +30 -0
  57. package/dist/runtime-api.lookup.js +7 -0
  58. package/dist/runtime-api.monitor-CvVKvEXW.js +5 -0
  59. package/dist/runtime-api.monitor.js +8 -0
  60. package/dist/runtime-api.send.js +6 -0
  61. package/dist/runtime-api.threads.js +6 -0
  62. package/dist/runtime-fC6f4UF2.js +8 -0
  63. package/dist/runtime-setter-api.js +2 -0
  64. package/dist/secret-config-contract-B6WW5V88.js +115 -0
  65. package/dist/secret-contract-api.js +2 -0
  66. package/dist/security-audit-CnyIQKz6.js +120 -0
  67. package/dist/security-audit-contract-api.js +2 -0
  68. package/dist/security-audit.runtime-CQSkjNLu.js +2 -0
  69. package/dist/security-contract-DLvYOgLM.js +26 -0
  70. package/dist/security-contract-api.js +2 -0
  71. package/dist/security-doctor-DepqtNCI.js +18 -0
  72. package/dist/send-DCtPCHGk.js +881 -0
  73. package/dist/send.components-Bcgxvm52.js +474 -0
  74. package/dist/send.outbound-S9t0UuHc.js +330 -0
  75. package/dist/send.receipt-CDn3GBWC.js +3119 -0
  76. package/dist/send.shared-D4iBnAmn.js +669 -0
  77. package/dist/sender-identity-CxCe3_1a.js +43 -0
  78. package/dist/session-contract-Dwhw3RTY.js +6 -0
  79. package/dist/session-key-api.js +2 -0
  80. package/dist/session-key-normalization-CP8dPUid.js +23 -0
  81. package/dist/setup-entry.js +11 -0
  82. package/dist/setup-plugin-api.js +2 -0
  83. package/dist/shared-AIlvuZXt.js +171 -0
  84. package/dist/subagent-hooks-8bK-mgiU.js +120 -0
  85. package/dist/subagent-hooks-api.js +22 -0
  86. package/dist/system-events-Ba1TklaL.js +34 -0
  87. package/dist/target-resolver-BrtFQtoK.js +82 -0
  88. package/dist/targets-DWLLZE2l.js +3 -0
  89. package/dist/test-api.js +45 -0
  90. package/dist/thread-binding-api.js +4 -0
  91. package/dist/thread-bindings-9aKRmZv0.js +255 -0
  92. package/dist/thread-bindings.discord-api-ssGH5wc2.js +244 -0
  93. package/dist/thread-bindings.manager-0YBHGemk.js +534 -0
  94. package/dist/thread-bindings.session-updates-DJZGIwaU.js +54 -0
  95. package/dist/thread-bindings.state-eTFl-PqJ.js +318 -0
  96. package/dist/timeouts-CEwuGaWT.js +52 -0
  97. package/dist/timeouts.js +2 -0
  98. package/dist/typing-BmJKRpCS.js +14 -0
  99. package/package.json +19 -7
  100. package/account-inspect-api.js +0 -7
  101. package/action-runtime-api.js +0 -7
  102. package/api.js +0 -7
  103. package/channel-config-api.js +0 -7
  104. package/channel-plugin-api.js +0 -7
  105. package/configured-state.js +0 -7
  106. package/contract-api.js +0 -7
  107. package/directory-contract-api.js +0 -7
  108. package/doctor-contract-api.js +0 -7
  109. package/index.js +0 -7
  110. package/runtime-api.actions.js +0 -7
  111. package/runtime-api.js +0 -7
  112. package/runtime-api.lookup.js +0 -7
  113. package/runtime-api.monitor.js +0 -7
  114. package/runtime-api.send.js +0 -7
  115. package/runtime-api.threads.js +0 -7
  116. package/runtime-setter-api.js +0 -7
  117. package/secret-contract-api.js +0 -7
  118. package/security-audit-contract-api.js +0 -7
  119. package/security-contract-api.js +0 -7
  120. package/session-key-api.js +0 -7
  121. package/setup-entry.js +0 -7
  122. package/setup-plugin-api.js +0 -7
  123. package/subagent-hooks-api.js +0 -7
  124. package/test-api.js +0 -7
  125. package/thread-binding-api.js +0 -7
  126. 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 };