@openclaw/discord 2026.7.2-beta.7 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/{account-inspect-SXiCBCgP.js → account-inspect-D5xmUpnj.js} +49 -43
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/accounts-DNl-oCEw.js +371 -0
  4. package/dist/action-runtime-api.js +1 -1
  5. package/dist/{active-turn-thread-route-MlKWp8RD.js → active-turn-thread-route-fmHl1HNd.js} +9 -11
  6. package/dist/{activities-api-DTKsuDxf.js → activities-api-KCBAmWxD.js} +133 -175
  7. package/dist/activities-api.js +1 -1
  8. package/dist/{allow-list-DzKrtfah.js → allow-list-BDMimASD.js} +2 -3
  9. package/dist/{api-DnrK9gML.js → api-DNVbJyEy.js} +7 -2
  10. package/dist/api.js +18 -18
  11. package/dist/{approval-handler.runtime-HymvFTYn.js → approval-handler.runtime-B12RCYr8.js} +6 -6
  12. package/dist/{approval-message-safety-DEyKVsAw.js → approval-message-safety-XGFPbDoI.js} +89 -3
  13. package/dist/{audit-CjsrabP8.js → audit-Yc7eZDd6.js} +5 -5
  14. package/dist/{channel-actions-uXFTJX3o.js → channel-actions-Dr2QI8Bf.js} +11 -7
  15. package/dist/{channel-actions.runtime-CIYU90sl.js → channel-actions.runtime-CODTvo7c.js} +99 -97
  16. package/dist/{channel-vKa-76xY.js → channel-cXYzGc4L.js} +102 -104
  17. package/dist/channel-config-api.js +1 -1
  18. package/dist/channel-plugin-api.js +1 -1
  19. package/dist/{channel.setup-CmcwZ_qd.js → channel.setup-CSiwnMgs.js} +16 -54
  20. package/dist/{outbound-session-route-H-HoyH5U.js → components-ezGKo_Yh.js} +69 -142
  21. package/dist/{config-schema-WHRQcY19.js → config-schema-DC5kDtQ5.js} +42 -11
  22. package/dist/conversation-binding-route-D6rCQ5S0.js +43 -0
  23. package/dist/{directory-config-D_LojtCe.js → directory-config-XyCfoqLg.js} +2 -2
  24. package/dist/directory-contract-api.js +1 -1
  25. package/dist/{directory-live-Bw5Ephzv.js → directory-live-Df0NbFJB.js} +7 -4
  26. package/dist/{doctor-Bc4S1qyd.js → doctor-DYY31GrO.js} +34 -13
  27. package/dist/{doctor-contract-CCFMIDk9.js → doctor-contract-DsWvKIXF.js} +2 -3
  28. package/dist/doctor-contract-api.js +147 -2
  29. package/dist/{handle-action.guild-admin-D6VRRW-C.js → handle-action.guild-admin-6Tvvinsa.js} +4 -7
  30. package/dist/{inbound-event-delivery-CEPlt2uz.js → inbound-event-delivery-De2qxTrP.js} +4 -25
  31. package/dist/index.js +5 -4
  32. package/dist/{message-handler-CeiSulIw.js → message-handler-B61q5MOF.js} +118 -33
  33. package/dist/{message-handler.preflight-DibgQFf2.js → message-handler.preflight-Bzg_Hawz.js} +75 -61
  34. package/dist/{message-handler.process-Dnc_oz9y.js → message-handler.process--4_712Ec.js} +236 -296
  35. package/dist/{message-utils-BgTwT_q7.js → message-utils-BXLLnFl6.js} +34 -35
  36. package/dist/{model-picker-preference-primitives-D25pnCOu.js → model-picker-preference-primitives-BHT1LkR_.js} +3 -3
  37. package/dist/{outbound-adapter-DtE-i2OJ.js → outbound-adapter-DxHTOqiN.js} +157 -68
  38. package/dist/{outbound-components-C7GVyfPh.js → outbound-components-B2PL0ooH.js} +3 -3
  39. package/dist/owner-access-Bg41VzAf.js +107 -0
  40. package/dist/{pluralkit-CD4B11JB.js → pluralkit-dUl4v1X3.js} +1 -1
  41. package/dist/{preflight-audio-BjnQZ5yS.js → preflight-audio-DrnqFA8U.js} +22 -28
  42. package/dist/{probe-BeWKhjqX.js → probe-BTdtVRwU.js} +37 -13
  43. package/dist/probe.runtime-Dq5-PRKT.js +2 -0
  44. package/dist/{provider-DfQJX3z5.js → provider-DlK_ynqt.js} +1131 -2021
  45. package/dist/{provider-session.runtime-Bo88k0pV.js → provider-session.runtime-CvvphHvs.js} +3 -3
  46. package/dist/provider.runtime-CzKGRiAF.js +2 -0
  47. package/dist/realtime-session.runtime-ysbMG8v7.js +1351 -0
  48. package/dist/{resolve-allowlist-common-DHERApxi.js → resolve-allowlist-common-DIVXbD1P.js} +3 -3
  49. package/dist/{resolve-channels-BjepAj8b.js → resolve-channels-CGeUoIqb.js} +4 -4
  50. package/dist/{resolve-users-Bc28htgt.js → resolve-users-BtKxLUZv.js} +3 -3
  51. package/dist/{chunk-DX4jpFQP.js → retry-DNsVTeTo.js} +308 -131
  52. package/dist/{runtime-BmW_j_hl.js → runtime-CQovMsH3.js} +12 -15
  53. package/dist/{runtime-_aRC6V2s.js → runtime-D71_VFLr.js} +49 -58
  54. package/dist/runtime-api.actions.js +2 -2
  55. package/dist/runtime-api.js +23 -23
  56. package/dist/runtime-api.lookup.js +5 -5
  57. package/dist/runtime-api.monitor-CzeTYF32.js +6 -0
  58. package/dist/runtime-api.monitor.js +6 -5
  59. package/dist/runtime-api.send.js +6 -6
  60. package/dist/runtime-api.threads.js +5 -6
  61. package/dist/sdk-runtime-37OU6_Yh.js +369 -0
  62. package/dist/{secret-config-contract-B1akrlyY.js → secret-config-contract-B3i6WxV5.js} +53 -25
  63. package/dist/secret-contract-api.js +1 -1
  64. package/dist/security-audit-contract-api.js +1 -1
  65. package/dist/{security-audit.runtime-CPN0xFYz.js → security-audit.runtime-BVKF9VtY.js} +1 -1
  66. package/dist/{send-CdkZ6zeW.js → send-rQIffSYu.js} +180 -91
  67. package/dist/{send.components-5uqwkjgE.js → send.components-ChK1wS7w.js} +32 -46
  68. package/dist/{send.outbound-D-80Ej-t.js → send.outbound-HIkgji_7.js} +57 -34
  69. package/dist/{send.reactions-CtlmGd_o.js → send.reactions-gmJ6yFPo.js} +14 -21
  70. package/dist/{send.receipt-CuwRrpFz.js → send.receipt-Cnhoautb.js} +29 -1
  71. package/dist/{send.shared-jpvAG_u6.js → send.shared-B3fpaLWJ.js} +112 -181
  72. package/dist/{sender-identity-mG_zsdox.js → sender-identity-BJtfyise.js} +1 -1
  73. package/dist/setup-entry.js +0 -5
  74. package/dist/setup-plugin-api.js +1 -1
  75. package/dist/{shared-CXF1tup1.js → shared-CaAaoksy.js} +27 -23
  76. package/dist/subagent-hooks-CNT2ZJTJ.js +49 -0
  77. package/dist/subagent-hooks-api.js +2 -6
  78. package/dist/subagent-progress-FPLqBY1s.js +110 -0
  79. package/dist/{system-events-DdUqf91B.js → system-events-Nuwoq_au.js} +2 -2
  80. package/dist/{target-resolver-C3s_2m0t.js → target-resolver-DIXShHaZ.js} +3 -3
  81. package/dist/targets-B2pf49zc.js +3 -0
  82. package/dist/{thread-bindings-C-M-ly7P.js → thread-bindings-VlymUnbz.js} +29 -28
  83. package/dist/{thread-bindings.manager-DYvSlrva.js → thread-bindings.manager-CngsgErq.js} +258 -29
  84. package/dist/{thread-bindings.session-updates-DAIlp7Px.js → thread-bindings.session-updates-CJb-BpPL.js} +1 -1
  85. package/dist/{thread-bindings.state-Cgi5uRn-.js → thread-bindings.state-D8OMdk4O.js} +3 -16
  86. package/dist/threading-DU6pgeGm.js +456 -0
  87. package/dist/transcripts-source-DeIMNbRh.js +265 -0
  88. package/dist/transcripts-source-api.js +7 -2
  89. package/dist/{typing--Mzo2kM7.js → typing-oLiS5Z13.js} +1 -1
  90. package/dist/voice-runtime-CjNFaynA.js +2549 -0
  91. package/openclaw.plugin.json +215 -25
  92. package/package.json +15 -11
  93. package/skills/discord/SKILL.md +22 -115
  94. package/dist/accounts-DJTf1LtA.js +0 -219
  95. package/dist/channel-api-CAJ0wMoV.js +0 -21
  96. package/dist/legacy-state-migrations-api.js +0 -138
  97. package/dist/manager.runtime-Bjav3pfC.js +0 -3655
  98. package/dist/preflight-audio.runtime-CoCXMM8r.js +0 -7
  99. package/dist/probe.runtime-DqFGFMbZ.js +0 -2
  100. package/dist/provider.runtime-Cy7XZmyh.js +0 -2
  101. package/dist/runtime-api.monitor-CCRubd3-.js +0 -5
  102. package/dist/session-binding-contract-api.js +0 -2
  103. package/dist/subagent-hooks-Dxy7Fmnq.js +0 -120
  104. package/dist/subagent-progress-DI3sYLLo.js +0 -664
  105. package/dist/targets-Dv_RN7vT.js +0 -3
  106. package/dist/thread-bindings.discord-api-BoDNqamb.js +0 -246
  107. package/dist/transcripts-source-DIKGsTGT.js +0 -123
  108. package/node_modules/p-map/index.d.ts +0 -155
  109. package/node_modules/p-map/index.js +0 -285
  110. package/node_modules/p-map/license +0 -9
  111. package/node_modules/p-map/package.json +0 -57
  112. package/node_modules/p-map/readme.md +0 -190
  113. package/dist/{audit-core-xwjIczO0.js → audit-core-BCX8UeFH.js} +1 -1
  114. package/dist/{security-audit-jGnJF__m.js → security-audit-BfJhAXZp.js} +1 -1
@@ -0,0 +1,110 @@
1
+ import { c as resolveDiscordAccount, u as resolveDiscordAccountConfig } from "./accounts-DNl-oCEw.js";
2
+ import { i as removeReactionDiscord } from "./send.reactions-gmJ6yFPo.js";
3
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
4
+ import { listAgentIds, resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
5
+ import { DEFAULT_EMOJIS } from "openclaw/plugin-sdk/channel-feedback";
6
+ //#region extensions/discord/src/subagent-progress.ts
7
+ const PROGRESS_STORE_TTL_MS = 10080 * 6e4;
8
+ const MAX_TRACKED_RUNS = 4096;
9
+ const RETRY_BASE_DELAY_MS = 1e3;
10
+ const RETRY_MAX_DELAY_MS = 60 * 6e4;
11
+ const RETRY_MAX_ATTEMPTS = 12;
12
+ const HISTORICAL_RUNNING_EMOJIS = /* @__PURE__ */ new Set([
13
+ "1️⃣",
14
+ "2️⃣",
15
+ "3️⃣",
16
+ "4️⃣",
17
+ "5️⃣",
18
+ "6️⃣",
19
+ "7️⃣",
20
+ "8️⃣",
21
+ "9️⃣",
22
+ "🔟"
23
+ ]);
24
+ const recoveryRetries = /* @__PURE__ */ new Map();
25
+ function logFailure(api, action, error) {
26
+ const message = error instanceof Error ? error.message : String(error);
27
+ api.logger.debug?.(`discord retired subagent progress ${action} failed: ${message}`);
28
+ }
29
+ function reservedReactionEmojis(api, accountAckReaction) {
30
+ const reserved = new Set(Object.values(DEFAULT_EMOJIS));
31
+ for (const emoji of [api.config.messages?.ackReaction, accountAckReaction]) if (emoji?.trim()) reserved.add(emoji.trim());
32
+ for (const agentId of listAgentIds(api.config)) {
33
+ const emoji = resolveAgentConfig(api.config, agentId)?.identity?.emoji?.trim();
34
+ if (emoji) reserved.add(emoji);
35
+ }
36
+ return reserved;
37
+ }
38
+ function clearRecoveryRetry(api) {
39
+ const retry = recoveryRetries.get(api);
40
+ if (retry?.timer) clearTimeout(retry.timer);
41
+ recoveryRetries.delete(api);
42
+ }
43
+ function scheduleRecoveryRetry(api) {
44
+ const retry = recoveryRetries.get(api) ?? { attempts: 0 };
45
+ if (retry.timer || retry.attempts >= RETRY_MAX_ATTEMPTS) return;
46
+ const delayMs = Math.min(RETRY_BASE_DELAY_MS * 2 ** retry.attempts, RETRY_MAX_DELAY_MS);
47
+ retry.attempts += 1;
48
+ retry.timer = setTimeout(() => {
49
+ retry.timer = void 0;
50
+ recoverDiscordSubagentProgress(api);
51
+ }, delayMs);
52
+ retry.timer.unref?.();
53
+ recoveryRetries.set(api, retry);
54
+ }
55
+ async function cleanPersistedReaction(api, store, entry) {
56
+ try {
57
+ if (entry.value.accountId !== DEFAULT_ACCOUNT_ID && !resolveDiscordAccountConfig(api.config, entry.value.accountId)) return false;
58
+ const account = resolveDiscordAccount({
59
+ cfg: api.config,
60
+ accountId: entry.value.accountId
61
+ });
62
+ if (!account.enabled || account.tokenStatus !== "available") return false;
63
+ const emoji = entry.value.runningEmoji;
64
+ if (emoji && HISTORICAL_RUNNING_EMOJIS.has(emoji) && !reservedReactionEmojis(api, account.config.ackReaction).has(emoji)) await removeReactionDiscord(entry.value.channelId, entry.value.messageId, emoji, {
65
+ cfg: api.config,
66
+ accountId: entry.value.accountId
67
+ });
68
+ await store.consume(entry.key);
69
+ return true;
70
+ } catch (error) {
71
+ logFailure(api, "startup cleanup", error);
72
+ return false;
73
+ }
74
+ }
75
+ async function recoverDiscordSubagentProgressImpl(api) {
76
+ let store;
77
+ try {
78
+ store = api.runtime.state.openKeyedStore({
79
+ namespace: "subagent-progress",
80
+ maxEntries: MAX_TRACKED_RUNS,
81
+ overflowPolicy: "reject-new",
82
+ defaultTtlMs: PROGRESS_STORE_TTL_MS
83
+ });
84
+ } catch (error) {
85
+ logFailure(api, "state store open", error);
86
+ scheduleRecoveryRetry(api);
87
+ return;
88
+ }
89
+ let entries;
90
+ try {
91
+ entries = await store.entries();
92
+ } catch (error) {
93
+ logFailure(api, "startup recovery list", error);
94
+ scheduleRecoveryRetry(api);
95
+ return;
96
+ }
97
+ let retryNeeded = false;
98
+ for (const entry of entries) if (!await cleanPersistedReaction(api, store, entry)) retryNeeded = true;
99
+ if (retryNeeded) scheduleRecoveryRetry(api);
100
+ else clearRecoveryRetry(api);
101
+ }
102
+ function resetDiscordSubagentProgressForTest() {
103
+ for (const [api, retry] of recoveryRetries) {
104
+ if (retry.timer) clearTimeout(retry.timer);
105
+ recoveryRetries.delete(api);
106
+ }
107
+ }
108
+ const recoverDiscordSubagentProgress = Object.assign(recoverDiscordSubagentProgressImpl, { resetForTest: resetDiscordSubagentProgressForTest });
109
+ //#endregion
110
+ export { recoverDiscordSubagentProgress };
@@ -1,5 +1,5 @@
1
- import { d as discord_exports } from "./chunk-DX4jpFQP.js";
2
- import { x as formatDiscordUserTag } from "./allow-list-DzKrtfah.js";
1
+ import { p as discord_exports } from "./retry-DNsVTeTo.js";
2
+ import { x as formatDiscordUserTag } from "./allow-list-BDMimASD.js";
3
3
  //#region extensions/discord/src/monitor/system-events.ts
4
4
  function resolveDiscordSystemEvent(message, location) {
5
5
  switch (message.type) {
@@ -1,6 +1,6 @@
1
- import { Yt as __exportAll, a as allowFromContainsDiscordUserId, l as parseDiscordTarget } from "./chunk-DX4jpFQP.js";
2
- import { c as resolveDiscordAccountAllowFrom, s as resolveDiscordAccount } from "./accounts-DJTf1LtA.js";
3
- import { a as rememberDiscordDirectoryUser, r as listDiscordDirectoryPeersLive } from "./directory-live-Bw5Ephzv.js";
1
+ import { en as __exportAll, l as allowFromContainsDiscordUserId } from "./retry-DNsVTeTo.js";
2
+ import { c as resolveDiscordAccount, k as parseDiscordTarget, l as resolveDiscordAccountAllowFrom } from "./accounts-DNl-oCEw.js";
3
+ import { a as rememberDiscordDirectoryUser, r as listDiscordDirectoryPeersLive } from "./directory-live-Df0NbFJB.js";
4
4
  import { buildMessagingTarget } from "openclaw/plugin-sdk/channel-targets";
5
5
  //#region extensions/discord/src/send-target-parsing.ts
6
6
  const parseDiscordSendTarget = (raw, options = {}) => parseDiscordTarget(raw, options);
@@ -0,0 +1,3 @@
1
+ import "./accounts-DNl-oCEw.js";
2
+ import "./target-resolver-DIXShHaZ.js";
3
+ export {};
@@ -1,14 +1,14 @@
1
- import { Yt as __exportAll, l as parseDiscordTarget } from "./chunk-DX4jpFQP.js";
2
- import "./targets-Dv_RN7vT.js";
3
- import { C as resolveThreadBindingInactivityExpiresAt, E as saveBindingsToDisk, S as resolveThreadBindingIdleTimeoutMs$1, T as resolveThreadBindingMaxAgeMs$1, h as refreshUnboundThreadWebhookIdentity, k as shouldPersistBindingMutations, m as normalizeThreadId, n as MANAGERS_BY_ACCOUNT_ID, t as BINDINGS_BY_THREAD_ID, u as getThreadBindingToken, v as removeBindingRecord, w as resolveThreadBindingMaxAgeExpiresAt } from "./thread-bindings.state-Cgi5uRn-.js";
4
- import { n as setThreadBindingMaxAgeBySessionKey, r as resolveBindingIdsForTargetSession, t as setThreadBindingIdleTimeoutBySessionKey } from "./thread-bindings.session-updates-DAIlp7Px.js";
5
- import { S as resolveThreadBindingThreadName, _ as resolveThreadBindingPersona, s as resolveChannelIdForBinding, v as resolveThreadBindingPersonaFromRecord, x as resolveThreadBindingIntroText, y as formatThreadBindingDurationLabel } from "./thread-bindings.discord-api-BoDNqamb.js";
6
- import { i as testing, n as createThreadBindingManager, r as getThreadBindingManager, t as createNoopThreadBindingManager } from "./thread-bindings.manager-DYvSlrva.js";
1
+ import { en as __exportAll } from "./retry-DNsVTeTo.js";
2
+ import { k as parseDiscordTarget } from "./accounts-DNl-oCEw.js";
3
+ import "./targets-B2pf49zc.js";
4
+ import { C as resolveThreadBindingMaxAgeExpiresAt, O as shouldPersistBindingMutations, S as resolveThreadBindingInactivityExpiresAt, T as saveBindingsToDisk, h as refreshUnboundThreadWebhookIdentity, m as normalizeThreadId, n as MANAGERS_BY_ACCOUNT_ID, t as BINDINGS_BY_THREAD_ID, u as getThreadBindingToken, v as removeBindingRecord, w as resolveThreadBindingMaxAgeMs$1, x as resolveThreadBindingIdleTimeoutMs$1 } from "./thread-bindings.state-D8OMdk4O.js";
5
+ import { n as setThreadBindingMaxAgeBySessionKey, r as resolveBindingIdsForTargetSession, t as setThreadBindingIdleTimeoutBySessionKey } from "./thread-bindings.session-updates-CJb-BpPL.js";
6
+ import { _ as resolveThreadBindingThreadName, g as resolveThreadBindingIntroText, h as formatThreadBindingDurationLabel, m as resolveThreadBindingPersonaFromRecord, n as createThreadBindingManager, o as resolveChannelIdForBinding, p as resolveThreadBindingPersona, r as getThreadBindingManager, t as createNoopThreadBindingManager } from "./thread-bindings.manager-CngsgErq.js";
7
7
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
8
8
  import { normalizeOptionalLowercaseString, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
9
9
  import { resolveThreadBindingIdleTimeoutMs, resolveThreadBindingMaxAgeMs, resolveThreadBindingsEnabled } from "openclaw/plugin-sdk/conversation-runtime";
10
10
  import { readAcpSessionEntry } from "openclaw/plugin-sdk/acp-runtime";
11
- import pMap from "p-map";
11
+ import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
12
12
  //#region extensions/discord/src/monitor/thread-bindings.config.ts
13
13
  function resolveDiscordThreadBindingIdleTimeoutMs(params) {
14
14
  const accountId = normalizeAccountId(params.accountId);
@@ -161,27 +161,29 @@ async function reconcileAcpThreadBindingsOnStartup(params) {
161
161
  });
162
162
  }
163
163
  if (params.healthProbe && probeTargets.length > 0) {
164
- const probeResults = await pMap(probeTargets, async ({ binding, sessionKey, session }) => {
165
- try {
166
- return {
167
- binding,
168
- status: (await params.healthProbe?.({
169
- cfg: params.cfg,
170
- accountId: manager.accountId,
171
- sessionKey,
164
+ const { results: probeResults } = await runTasksWithConcurrency({
165
+ tasks: probeTargets.map(({ binding, sessionKey, session }) => async () => {
166
+ try {
167
+ return {
172
168
  binding,
173
- session
174
- }))?.status ?? "uncertain"
175
- };
176
- } catch {
177
- return {
178
- binding,
179
- status: "uncertain"
180
- };
181
- }
182
- }, {
183
- concurrency: ACP_STARTUP_HEALTH_PROBE_CONCURRENCY_LIMIT,
184
- stopOnError: true
169
+ status: (await params.healthProbe?.({
170
+ cfg: params.cfg,
171
+ accountId: manager.accountId,
172
+ sessionKey,
173
+ binding,
174
+ session
175
+ }))?.status ?? "uncertain"
176
+ };
177
+ } catch {
178
+ return {
179
+ binding,
180
+ status: "uncertain"
181
+ };
182
+ }
183
+ }),
184
+ limit: ACP_STARTUP_HEALTH_PROBE_CONCURRENCY_LIMIT,
185
+ errorMode: "stop",
186
+ throwOnError: true
185
187
  });
186
188
  for (const probeResult of probeResults) if (probeResult.status === "stale") staleBindings.push(probeResult.binding);
187
189
  }
@@ -230,7 +232,6 @@ var thread_bindings_exports = /* @__PURE__ */ __exportAll({
230
232
  resolveThreadBindingsEnabled: () => resolveThreadBindingsEnabled,
231
233
  setThreadBindingIdleTimeoutBySessionKey: () => setThreadBindingIdleTimeoutBySessionKey,
232
234
  setThreadBindingMaxAgeBySessionKey: () => setThreadBindingMaxAgeBySessionKey,
233
- testing: () => testing,
234
235
  unbindThreadBindingsBySessionKey: () => unbindThreadBindingsBySessionKey
235
236
  });
236
237
  //#endregion
@@ -1,13 +1,252 @@
1
- import { Yt as __exportAll, u as resolveDiscordChannelId, yt as getChannel } from "./chunk-DX4jpFQP.js";
2
- import { F as createDiscordRestClient } from "./send.shared-jpvAG_u6.js";
3
- import { C as resolveThreadBindingInactivityExpiresAt, D as setBindingRecord, E as saveBindingsToDisk, O as shouldDefaultPersist, S as resolveThreadBindingIdleTimeoutMs$1, T as resolveThreadBindingMaxAgeMs$1, _ as rememberThreadBindingToken, b as resolveBindingIdsForSession, c as ensureBindingsLoaded, f as normalizeTargetKind, h as refreshUnboundThreadWebhookIdentity, l as forgetThreadBindingToken, m as normalizeThreadId, n as MANAGERS_BY_ACCOUNT_ID, p as normalizeThreadBindingDurationMs, r as PERSIST_BY_ACCOUNT_ID, s as THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS, t as BINDINGS_BY_THREAD_ID, u as getThreadBindingToken, v as removeBindingRecord, w as resolveThreadBindingMaxAgeExpiresAt, x as resolveBindingRecordKey, y as resetThreadBindingsForTests } from "./thread-bindings.state-Cgi5uRn-.js";
4
- import { S as resolveThreadBindingThreadName, a as isThreadArchived, b as resolveThreadBindingFarewellText, c as summarizeDiscordError, i as isDiscordThreadGoneError, l as DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS, n as createWebhookForChannel, o as maybeSendBindingMessage, r as findReusableWebhook, s as resolveChannelIdForBinding, t as createThreadForBinding, u as THREAD_BINDINGS_SWEEP_INTERVAL_MS } from "./thread-bindings.discord-api-BoDNqamb.js";
5
- import { normalizeAccountId, resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
1
+ import { en as __exportAll, ft as createChannelWebhook, wt as getChannel } from "./retry-DNsVTeTo.js";
2
+ import { A as resolveDiscordChannelId } from "./accounts-DNl-oCEw.js";
3
+ import { F as createDiscordRestClient } from "./send.shared-B3fpaLWJ.js";
4
+ import { t as sendMessageDiscord } from "./send.outbound-HIkgji_7.js";
5
+ import { i as sendWebhookMessageDiscord, o as createThreadDiscord } from "./send-rQIffSYu.js";
6
+ import { A as toReusableWebhookKey, C as resolveThreadBindingMaxAgeExpiresAt, D as shouldDefaultPersist, E as setBindingRecord, S as resolveThreadBindingInactivityExpiresAt, T as saveBindingsToDisk, _ as rememberThreadBindingToken, b as resolveBindingRecordKey, c as ensureBindingsLoaded, f as normalizeTargetKind, g as rememberReusableWebhook, h as refreshUnboundThreadWebhookIdentity, i as REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL, l as forgetThreadBindingToken, m as normalizeThreadId, n as MANAGERS_BY_ACCOUNT_ID, p as normalizeThreadBindingDurationMs, r as PERSIST_BY_ACCOUNT_ID, s as THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS, t as BINDINGS_BY_THREAD_ID, u as getThreadBindingToken, v as removeBindingRecord, w as resolveThreadBindingMaxAgeMs$1, x as resolveThreadBindingIdleTimeoutMs$1, y as resolveBindingIdsForSession } from "./thread-bindings.state-D8OMdk4O.js";
7
+ import { ChannelType } from "discord-api-types/v10";
8
+ import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
9
+ import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
6
10
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
11
  import { getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
8
12
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
9
- import { registerSessionBindingAdapter, resolveThreadBindingConversationIdFromBindingId, unregisterSessionBindingAdapter } from "openclaw/plugin-sdk/conversation-runtime";
10
- import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
13
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
14
+ import { SYSTEM_MARK } from "openclaw/plugin-sdk/text-chunking";
15
+ import { formatThreadBindingDurationLabel, registerSessionBindingAdapter, resolveThreadBindingConversationIdFromBindingId, resolveThreadBindingFarewellText, resolveThreadBindingIntroText, resolveThreadBindingThreadName, unregisterSessionBindingAdapter } from "openclaw/plugin-sdk/conversation-runtime";
16
+ import { resolveSessionAgentId } from "openclaw/plugin-sdk/agent-scope-runtime";
17
+ //#region extensions/discord/src/monitor/thread-bindings.persona.ts
18
+ const THREAD_BINDING_PERSONA_MAX_CHARS = 80;
19
+ function normalizePersonaLabel(value) {
20
+ if (!value) return;
21
+ return value.replace(/\s+/g, " ").trim() || void 0;
22
+ }
23
+ function resolveThreadBindingPersona(params) {
24
+ return truncateUtf16Safe(`${SYSTEM_MARK} ${normalizePersonaLabel(params.label) || normalizePersonaLabel(params.agentId) || "agent"}`, THREAD_BINDING_PERSONA_MAX_CHARS);
25
+ }
26
+ function resolveThreadBindingPersonaFromRecord(record) {
27
+ return resolveThreadBindingPersona({
28
+ label: record.label,
29
+ agentId: record.agentId
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region extensions/discord/src/monitor/channel-access.ts
34
+ function readDiscordChannelPropertySafe(channel, key) {
35
+ if (!channel || typeof channel !== "object") return;
36
+ try {
37
+ if (!(key in channel)) return;
38
+ return channel[key];
39
+ } catch {
40
+ return;
41
+ }
42
+ }
43
+ function resolveDiscordChannelStringPropertySafe(channel, key) {
44
+ const value = readDiscordChannelPropertySafe(channel, key);
45
+ return typeof value === "string" ? value : void 0;
46
+ }
47
+ function resolveDiscordChannelNumberPropertySafe(channel, key) {
48
+ const value = readDiscordChannelPropertySafe(channel, key);
49
+ return typeof value === "number" ? value : void 0;
50
+ }
51
+ const DISCORD_CHANNEL_SNAKE_CASE_ALIASES = {
52
+ ownerId: "owner_id",
53
+ parentId: "parent_id"
54
+ };
55
+ function resolveDiscordChannelStringWithAliasSafe(channel, camelKey) {
56
+ const camelValue = resolveDiscordChannelStringPropertySafe(channel, camelKey);
57
+ if (camelValue !== void 0) return camelValue;
58
+ const snakeKey = DISCORD_CHANNEL_SNAKE_CASE_ALIASES[camelKey];
59
+ if (!snakeKey) return;
60
+ const directSnakeValue = resolveDiscordChannelStringPropertySafe(channel, snakeKey);
61
+ if (directSnakeValue !== void 0) return directSnakeValue;
62
+ return resolveDiscordChannelStringPropertySafe(readDiscordChannelPropertySafe(channel, "rawData"), snakeKey);
63
+ }
64
+ function resolveDiscordChannelNameSafe(channel) {
65
+ return resolveDiscordChannelStringPropertySafe(channel, "name");
66
+ }
67
+ function resolveDiscordChannelIdSafe(channel) {
68
+ return resolveDiscordChannelStringPropertySafe(channel, "id");
69
+ }
70
+ function resolveDiscordChannelTopicSafe(channel) {
71
+ return resolveDiscordChannelStringPropertySafe(channel, "topic");
72
+ }
73
+ function resolveDiscordChannelParentIdSafe(channel) {
74
+ return resolveDiscordChannelStringWithAliasSafe(channel, "parentId");
75
+ }
76
+ function resolveDiscordChannelOwnerIdSafe(channel) {
77
+ return resolveDiscordChannelStringWithAliasSafe(channel, "ownerId");
78
+ }
79
+ function resolveDiscordChannelParentSafe(channel) {
80
+ return readDiscordChannelPropertySafe(channel, "parent");
81
+ }
82
+ function resolveDiscordChannelInfoSafe(channel) {
83
+ const parent = resolveDiscordChannelParentSafe(channel);
84
+ return {
85
+ name: resolveDiscordChannelNameSafe(channel),
86
+ topic: resolveDiscordChannelTopicSafe(channel),
87
+ type: resolveDiscordChannelNumberPropertySafe(channel, "type"),
88
+ parentId: resolveDiscordChannelParentIdSafe(channel),
89
+ ownerId: resolveDiscordChannelOwnerIdSafe(channel),
90
+ parentName: resolveDiscordChannelNameSafe(parent)
91
+ };
92
+ }
93
+ //#endregion
94
+ //#region extensions/discord/src/monitor/thread-bindings.types.ts
95
+ const THREAD_BINDINGS_SWEEP_INTERVAL_MS = 12e4;
96
+ const DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS = 1440 * 60 * 1e3;
97
+ //#endregion
98
+ //#region extensions/discord/src/monitor/thread-bindings.discord-api.ts
99
+ function buildThreadTarget(threadId) {
100
+ return /^(channel:|user:)/i.test(threadId) ? threadId : `channel:${threadId}`;
101
+ }
102
+ function isThreadArchived(raw) {
103
+ if (!raw || typeof raw !== "object") return false;
104
+ const asRecord = raw;
105
+ if (asRecord.archived === true) return true;
106
+ if (asRecord.thread_metadata?.archived === true) return true;
107
+ if (asRecord.threadMetadata?.archived === true) return true;
108
+ return false;
109
+ }
110
+ function isThreadChannelType(type) {
111
+ return type === ChannelType.PublicThread || type === ChannelType.PrivateThread || type === ChannelType.AnnouncementThread;
112
+ }
113
+ function normalizeDiscordBindingChannelId(raw) {
114
+ const trimmed = normalizeOptionalString(raw) ?? "";
115
+ if (!trimmed) return null;
116
+ try {
117
+ return resolveDiscordChannelId(trimmed);
118
+ } catch {
119
+ return null;
120
+ }
121
+ }
122
+ function summarizeDiscordError(err) {
123
+ if (err instanceof Error) return err.message;
124
+ if (typeof err === "string") return err;
125
+ if (typeof err === "number" || typeof err === "boolean" || typeof err === "bigint" || typeof err === "symbol") return String(err);
126
+ return "error";
127
+ }
128
+ function extractNumericDiscordErrorValue(value) {
129
+ return parseStrictNonNegativeInteger(value);
130
+ }
131
+ function extractDiscordErrorStatus(err) {
132
+ if (!err || typeof err !== "object") return;
133
+ const candidate = err;
134
+ return extractNumericDiscordErrorValue(candidate.status) ?? extractNumericDiscordErrorValue(candidate.statusCode) ?? extractNumericDiscordErrorValue(candidate.response?.status);
135
+ }
136
+ function extractDiscordErrorCode(err) {
137
+ if (!err || typeof err !== "object") return;
138
+ const candidate = err;
139
+ return extractNumericDiscordErrorValue(candidate.code) ?? extractNumericDiscordErrorValue(candidate.rawError?.code) ?? extractNumericDiscordErrorValue(candidate.body?.code) ?? extractNumericDiscordErrorValue(candidate.response?.body?.code) ?? extractNumericDiscordErrorValue(candidate.response?.data?.code);
140
+ }
141
+ function isDiscordThreadGoneError(err) {
142
+ if (extractDiscordErrorCode(err) === 10003) return true;
143
+ const status = extractDiscordErrorStatus(err);
144
+ return status === 404 || status === 403;
145
+ }
146
+ async function maybeSendBindingMessage(params) {
147
+ const text = params.text.trim();
148
+ if (!text) return;
149
+ const record = params.record;
150
+ if (params.preferWebhook !== false && record.webhookId && record.webhookToken) try {
151
+ await sendWebhookMessageDiscord(text, {
152
+ cfg: params.cfg,
153
+ webhookId: record.webhookId,
154
+ webhookToken: record.webhookToken,
155
+ accountId: record.accountId,
156
+ threadId: record.threadId,
157
+ username: resolveThreadBindingPersonaFromRecord(record)
158
+ });
159
+ return;
160
+ } catch (err) {
161
+ logVerbose(`discord thread binding webhook send failed: ${summarizeDiscordError(err)}`);
162
+ }
163
+ try {
164
+ await sendMessageDiscord(buildThreadTarget(record.threadId), text, {
165
+ cfg: params.cfg,
166
+ accountId: record.accountId
167
+ });
168
+ } catch (err) {
169
+ logVerbose(`discord thread binding fallback send failed: ${summarizeDiscordError(err)}`);
170
+ }
171
+ }
172
+ async function createWebhookForChannel(params) {
173
+ try {
174
+ const rest = createDiscordRestClient({
175
+ cfg: params.cfg,
176
+ accountId: params.accountId,
177
+ token: params.token
178
+ }).rest;
179
+ const created = await createChannelWebhook(rest, params.channelId, { body: { name: "OpenClaw Agents" } });
180
+ const webhookId = normalizeOptionalString(created?.id) ?? "";
181
+ const webhookToken = normalizeOptionalString(created?.token) ?? "";
182
+ if (!webhookId || !webhookToken) return {};
183
+ return {
184
+ webhookId,
185
+ webhookToken
186
+ };
187
+ } catch (err) {
188
+ logVerbose(`discord thread binding webhook create failed for ${params.channelId}: ${summarizeDiscordError(err)}`);
189
+ return {};
190
+ }
191
+ }
192
+ function findReusableWebhook(params) {
193
+ const reusableKey = toReusableWebhookKey({
194
+ accountId: params.accountId,
195
+ channelId: params.channelId
196
+ });
197
+ const cached = REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.get(reusableKey);
198
+ if (cached) return {
199
+ webhookId: cached.webhookId,
200
+ webhookToken: cached.webhookToken
201
+ };
202
+ for (const record of BINDINGS_BY_THREAD_ID.values()) {
203
+ if (record.accountId !== params.accountId) continue;
204
+ if (record.channelId !== params.channelId) continue;
205
+ if (!record.webhookId || !record.webhookToken) continue;
206
+ rememberReusableWebhook(record);
207
+ return {
208
+ webhookId: record.webhookId,
209
+ webhookToken: record.webhookToken
210
+ };
211
+ }
212
+ return {};
213
+ }
214
+ async function resolveChannelIdForBinding(params) {
215
+ const explicit = normalizeDiscordBindingChannelId(params.channelId);
216
+ if (explicit) return explicit;
217
+ const lookupThreadId = normalizeDiscordBindingChannelId(params.threadId);
218
+ if (!lookupThreadId) return null;
219
+ try {
220
+ const rest = createDiscordRestClient({
221
+ cfg: params.cfg,
222
+ accountId: params.accountId,
223
+ token: params.token
224
+ }).rest;
225
+ const channel = await getChannel(rest, lookupThreadId);
226
+ const channelInfo = resolveDiscordChannelInfoSafe(channel);
227
+ const channelId = normalizeOptionalString(resolveDiscordChannelIdSafe(channel)) ?? "";
228
+ const type = channelInfo.type;
229
+ const parentId = normalizeOptionalString(channelInfo.parentId) ?? "";
230
+ if (parentId && isThreadChannelType(type)) return parentId;
231
+ return channelId || null;
232
+ } catch (err) {
233
+ logVerbose(`discord thread binding channel resolve failed for ${lookupThreadId}: ${summarizeDiscordError(err)}`);
234
+ return null;
235
+ }
236
+ }
237
+ async function createThreadForBinding(params) {
238
+ try {
239
+ return (normalizeOptionalString((await createThreadDiscord(params.channelId, { name: params.threadName }, {
240
+ cfg: params.cfg,
241
+ accountId: params.accountId,
242
+ token: params.token
243
+ }))?.id) ?? "") || null;
244
+ } catch (err) {
245
+ logVerbose(`discord thread binding auto-thread create failed for ${params.channelId}: ${summarizeDiscordError(err)}`);
246
+ return null;
247
+ }
248
+ }
249
+ //#endregion
11
250
  //#region extensions/discord/src/monitor/thread-bindings.session-adapter.ts
12
251
  function normalizeChildBindingParentChannelId(raw) {
13
252
  const trimmed = normalizeOptionalString(raw) ?? "";
@@ -77,7 +316,7 @@ function toSessionBindingRecord(record, defaults) {
77
316
  };
78
317
  }
79
318
  function createThreadBindingSessionAdapter(params) {
80
- const toRecord = (entry) => toSessionBindingRecord(entry, params.defaults);
319
+ const serializeBinding = (entry) => toSessionBindingRecord(entry, params.defaults);
81
320
  return {
82
321
  channel: "discord",
83
322
  accountId: params.accountId,
@@ -120,13 +359,13 @@ function createThreadBindingSessionAdapter(params) {
120
359
  introText,
121
360
  metadata
122
361
  });
123
- return bound ? toRecord(bound) : null;
362
+ return bound ? serializeBinding(bound) : null;
124
363
  },
125
- listBySession: (targetSessionKey) => params.manager.listBySessionKey(targetSessionKey).map(toRecord),
364
+ listBySession: (targetSessionKey) => params.manager.listBySessionKey(targetSessionKey).map(serializeBinding),
126
365
  resolveByConversation: (ref) => {
127
366
  if (ref.channel !== "discord") return null;
128
367
  const binding = params.manager.getByThreadId(ref.conversationId);
129
- return binding ? toRecord(binding) : null;
368
+ return binding ? serializeBinding(binding) : null;
130
369
  },
131
370
  touch: (bindingId, at) => {
132
371
  const threadId = resolveThreadBindingConversationIdFromBindingId({
@@ -144,7 +383,7 @@ function createThreadBindingSessionAdapter(params) {
144
383
  if (input.targetSessionKey?.trim()) return params.manager.unbindBySessionKey({
145
384
  targetSessionKey: input.targetSessionKey,
146
385
  reason: input.reason
147
- }).map(toRecord);
386
+ }).map(serializeBinding);
148
387
  const threadId = resolveThreadBindingConversationIdFromBindingId({
149
388
  accountId: params.accountId,
150
389
  bindingId: input.bindingId
@@ -154,18 +393,16 @@ function createThreadBindingSessionAdapter(params) {
154
393
  threadId,
155
394
  reason: input.reason
156
395
  });
157
- return removed ? [toRecord(removed)] : [];
396
+ return removed ? [serializeBinding(removed)] : [];
158
397
  }
159
398
  };
160
399
  }
161
400
  //#endregion
162
401
  //#region extensions/discord/src/monitor/thread-bindings.manager.ts
163
402
  var thread_bindings_manager_exports = /* @__PURE__ */ __exportAll({
164
- __testing: () => testing,
165
403
  createNoopThreadBindingManager: () => createNoopThreadBindingManager,
166
404
  createThreadBindingManager: () => createThreadBindingManager,
167
- getThreadBindingManager: () => getThreadBindingManager,
168
- testing: () => testing
405
+ getThreadBindingManager: () => getThreadBindingManager
169
406
  });
170
407
  function registerManager(manager) {
171
408
  MANAGERS_BY_ACCOUNT_ID.set(manager.accountId, manager);
@@ -173,7 +410,6 @@ function registerManager(manager) {
173
410
  function unregisterManager(accountId, manager) {
174
411
  if (MANAGERS_BY_ACCOUNT_ID.get(accountId) === manager) MANAGERS_BY_ACCOUNT_ID.delete(accountId);
175
412
  }
176
- const SWEEPERS_BY_ACCOUNT_ID = /* @__PURE__ */ new Map();
177
413
  function createNoopManager(accountIdRaw) {
178
414
  return {
179
415
  accountId: normalizeAccountId(accountIdRaw),
@@ -297,7 +533,6 @@ function createThreadBindingManager(params) {
297
533
  }
298
534
  }
299
535
  };
300
- SWEEPERS_BY_ACCOUNT_ID.set(accountId, runSweepOnce);
301
536
  const manager = {
302
537
  accountId,
303
538
  getIdleTimeoutMs: () => idleTimeoutMs,
@@ -400,7 +635,10 @@ function createThreadBindingManager(params) {
400
635
  threadId,
401
636
  targetKind,
402
637
  targetSessionKey,
403
- agentId: normalizeOptionalString(bindParams.agentId) ?? normalizeOptionalString(existingValue?.agentId) ?? resolveAgentIdFromSessionKey(targetSessionKey, resolveDefaultAgentId(cfg)),
638
+ agentId: normalizeOptionalString(bindParams.agentId) ?? normalizeOptionalString(existingValue?.agentId) ?? resolveSessionAgentId({
639
+ config: cfg,
640
+ sessionKey: targetSessionKey
641
+ }),
404
642
  label: normalizeOptionalString(bindParams.label) ?? normalizeOptionalString(existingValue?.label),
405
643
  webhookId: webhookId || void 0,
406
644
  webhookToken: webhookToken || void 0,
@@ -485,7 +723,6 @@ function createThreadBindingManager(params) {
485
723
  clearInterval(sweepTimer);
486
724
  sweepTimer = null;
487
725
  }
488
- SWEEPERS_BY_ACCOUNT_ID.delete(accountId);
489
726
  unregisterManager(accountId, manager);
490
727
  unregisterSessionBindingAdapter({
491
728
  channel: "discord",
@@ -522,13 +759,5 @@ function getThreadBindingManager(accountId) {
522
759
  const normalized = normalizeAccountId(accountId);
523
760
  return MANAGERS_BY_ACCOUNT_ID.get(normalized) ?? null;
524
761
  }
525
- const testing = {
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
762
  //#endregion
534
- export { thread_bindings_manager_exports as a, testing as i, createThreadBindingManager as n, getThreadBindingManager as r, createNoopThreadBindingManager as t };
763
+ export { resolveThreadBindingThreadName as _, isThreadArchived as a, resolveDiscordChannelInfoSafe as c, resolveDiscordChannelParentSafe as d, resolveDiscordChannelTopicSafe as f, resolveThreadBindingIntroText as g, formatThreadBindingDurationLabel as h, thread_bindings_manager_exports as i, resolveDiscordChannelNameSafe as l, resolveThreadBindingPersonaFromRecord as m, createThreadBindingManager as n, resolveChannelIdForBinding as o, resolveThreadBindingPersona as p, getThreadBindingManager as r, resolveDiscordChannelIdSafe as s, createNoopThreadBindingManager as t, resolveDiscordChannelParentIdSafe as u };
@@ -1,4 +1,4 @@
1
- import { D as setBindingRecord, E as saveBindingsToDisk, b as resolveBindingIdsForSession, c as ensureBindingsLoaded, k as shouldPersistBindingMutations, t as BINDINGS_BY_THREAD_ID } from "./thread-bindings.state-Cgi5uRn-.js";
1
+ import { E as setBindingRecord, O as shouldPersistBindingMutations, T as saveBindingsToDisk, c as ensureBindingsLoaded, t as BINDINGS_BY_THREAD_ID, y as resolveBindingIdsForSession } from "./thread-bindings.state-D8OMdk4O.js";
2
2
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
3
3
  //#region extensions/discord/src/monitor/thread-bindings.session-shared.ts
4
4
  function normalizeNonNegativeMs(raw) {
@@ -1,7 +1,7 @@
1
1
  import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
2
2
  import { normalizeAccountId, resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
4
- import { recordOutboundMessageIdentity } from "openclaw/plugin-sdk/channel-outbound";
4
+ import { recordOutboundMessageIdentity } from "openclaw/plugin-sdk/outbound-echo-runtime";
5
5
  //#region extensions/discord/src/monitor/thread-bindings.state.ts
6
6
  const THREAD_BINDINGS_STATE_KEY = Symbol.for("openclaw.discordThreadBindingsState");
7
7
  let threadBindingsState;
@@ -223,7 +223,7 @@ function shouldPersistBindingMutations() {
223
223
  function toPersistedBindingRecord(record) {
224
224
  const serialized = JSON.stringify(record);
225
225
  if (!serialized) return { ...record };
226
- return JSON.parse(serialized);
226
+ return normalizePersistedBinding(record.threadId, JSON.parse(serialized)) ?? { ...record };
227
227
  }
228
228
  function saveBindingsToDisk(params = {}) {
229
229
  if (!params.force && !shouldPersistAnyBindingState()) return;
@@ -284,18 +284,5 @@ function resolveBindingIdsForSession(params) {
284
284
  }
285
285
  return out;
286
286
  }
287
- function resetThreadBindingsForTests() {
288
- for (const manager of MANAGERS_BY_ACCOUNT_ID.values()) manager.stop();
289
- MANAGERS_BY_ACCOUNT_ID.clear();
290
- BINDINGS_BY_THREAD_ID.clear();
291
- BINDINGS_BY_SESSION_KEY.clear();
292
- REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.clear();
293
- TOKENS_BY_ACCOUNT_ID.clear();
294
- PERSIST_BY_ACCOUNT_ID.clear();
295
- THREAD_BINDINGS_STATE.loadedBindings = false;
296
- THREAD_BINDINGS_STATE.loadedPersistentBindings = false;
297
- THREAD_BINDINGS_STATE.persistenceAvailable = true;
298
- THREAD_BINDINGS_STATE.lastPersistedAtMs = 0;
299
- }
300
287
  //#endregion
301
- export { toBindingRecordKey as A, resolveThreadBindingInactivityExpiresAt as C, setBindingRecord as D, saveBindingsToDisk as E, shouldDefaultPersist as O, resolveThreadBindingIdleTimeoutMs as S, resolveThreadBindingMaxAgeMs as T, rememberThreadBindingToken as _, THREAD_BINDINGS_MAX_ENTRIES as a, resolveBindingIdsForSession as b, ensureBindingsLoaded as c, normalizePersistedBinding as d, normalizeTargetKind as f, rememberReusableWebhook as g, refreshUnboundThreadWebhookIdentity as h, REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL as i, toReusableWebhookKey as j, shouldPersistBindingMutations as k, forgetThreadBindingToken as l, normalizeThreadId as m, MANAGERS_BY_ACCOUNT_ID as n, THREAD_BINDINGS_NAMESPACE as o, normalizeThreadBindingDurationMs as p, PERSIST_BY_ACCOUNT_ID as r, THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS as s, BINDINGS_BY_THREAD_ID as t, getThreadBindingToken as u, removeBindingRecord as v, resolveThreadBindingMaxAgeExpiresAt as w, resolveBindingRecordKey as x, resetThreadBindingsForTests as y };
288
+ export { toReusableWebhookKey as A, resolveThreadBindingMaxAgeExpiresAt as C, shouldDefaultPersist as D, setBindingRecord as E, shouldPersistBindingMutations as O, resolveThreadBindingInactivityExpiresAt as S, saveBindingsToDisk as T, rememberThreadBindingToken as _, THREAD_BINDINGS_MAX_ENTRIES as a, resolveBindingRecordKey as b, ensureBindingsLoaded as c, normalizePersistedBinding as d, normalizeTargetKind as f, rememberReusableWebhook as g, refreshUnboundThreadWebhookIdentity as h, REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL as i, toBindingRecordKey as k, forgetThreadBindingToken as l, normalizeThreadId as m, MANAGERS_BY_ACCOUNT_ID as n, THREAD_BINDINGS_NAMESPACE as o, normalizeThreadBindingDurationMs as p, PERSIST_BY_ACCOUNT_ID as r, THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS as s, BINDINGS_BY_THREAD_ID as t, getThreadBindingToken as u, removeBindingRecord as v, resolveThreadBindingMaxAgeMs as w, resolveThreadBindingIdleTimeoutMs as x, resolveBindingIdsForSession as y };