@openclaw/discord 2026.10.5 → 2026.10.10-beta.2

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 (64) hide show
  1. package/dist/.setup/{approval-handler.runtime-D88-QVlO.mjs → approval-handler.runtime-BvF8oUpa.mjs} +9 -9
  2. package/dist/.setup/{approval-message-updates-CIGByUcE.mjs → approval-message-updates-wbUqLtLI.mjs} +2 -2
  3. package/dist/.setup/{audit-TGIPL0f_.mjs → audit-UwzCdDJk.mjs} +3 -3
  4. package/dist/.setup/{channel-C24afwHP.mjs → channel-BlvyeRAu.mjs} +72 -49
  5. package/dist/.setup/{channel.setup-B3KEh05g.mjs → channel.setup-By7wNopw.mjs} +1 -1
  6. package/dist/.setup/{command-owners-C7rUdqUX.mjs → command-owners-D78sAoOz.mjs} +1 -1
  7. package/dist/.setup/{components-HN9052L1.mjs → components-C_cuoW1q.mjs} +1 -1
  8. package/dist/.setup/conversation-binding-route-fdYsXexV.mjs +68 -0
  9. package/dist/.setup/{discord-BGqgx-yd.mjs → discord-Cl2OPTZ6.mjs} +18 -14
  10. package/dist/.setup/{doctor-9Ka20lDD.mjs → doctor-94GSI6lY.mjs} +2 -2
  11. package/dist/.setup/{handle-action-CsQmpfGZ.mjs → handle-action-0-FZD3Am.mjs} +7 -7
  12. package/dist/.setup/{handle-action.guild-admin-Aw-vutu3.mjs → handle-action.guild-admin-DfAWANBE.mjs} +2 -2
  13. package/dist/.setup/{message-handler-C_BQFYCW.mjs → message-handler-CtR2w7W_.mjs} +4 -4
  14. package/dist/.setup/{message-handler.preflight-C_xFOxPN.mjs → message-handler.preflight-BqUw7LWb.mjs} +11 -10
  15. package/dist/.setup/{message-handler.preflight-pluralkit-CfRsEwg4.mjs → message-handler.preflight-pluralkit-BU9I_X-b.mjs} +4 -6
  16. package/dist/.setup/{message-handler.process-_np9if7z.mjs → message-handler.process-BZeWmSNC.mjs} +40 -50
  17. package/dist/.setup/{outbound-adapter-L3UDoMOG.mjs → outbound-adapter-DBwgKemw.mjs} +5 -5
  18. package/dist/.setup/{outbound-session-route-iwnu6IK8.mjs → outbound-session-route-CsHHPxsQ.mjs} +24 -7
  19. package/dist/.setup/{provider-CbmXLYrW.mjs → provider-DUTsZJHz.mjs} +230 -205
  20. package/dist/.setup/{provider-session.runtime-BMOuDbod.mjs → provider-session.runtime-Ucr3XodI.mjs} +3 -3
  21. package/dist/.setup/provider.runtime-J8NuMCNs.mjs +1 -0
  22. package/dist/.setup/{realtime-session.runtime-YCumnRSE.mjs → realtime-session.runtime-bNnPOKP_.mjs} +161 -150
  23. package/dist/.setup/{receive-recovery-eVmK2_hl.mjs → receive-recovery-CKvaG2Hf.mjs} +4 -25
  24. package/dist/.setup/{runtime-DPm3gZXk.mjs → runtime-Cg5pIBm_.mjs} +1 -1
  25. package/dist/.setup/{runtime-CRTkKU1G.mjs → runtime-DE6W14G3.mjs} +7 -7
  26. package/dist/.setup/{send-DbILKgcm.mjs → send-yR1hfybW.mjs} +26 -12
  27. package/dist/.setup/{send.components-FxOS3CeP.mjs → send.components--dxMjc2O.mjs} +5 -5
  28. package/dist/.setup/{send.outbound-_jx4Xpd3.mjs → send.outbound-B5vQwgUK.mjs} +10 -4
  29. package/dist/.setup/{send.receipt-BAlzXd8e.mjs → send.receipt-eE3zLTDH.mjs} +3 -9
  30. package/dist/.setup/{send.shared-CjQ4jbMA.mjs → send.shared-iGf1rqAm.mjs} +4 -3
  31. package/dist/.setup/setup-plugin-api-DVpA_yqS.mjs +2 -0
  32. package/dist/.setup/{shared-C027f-lL.mjs → shared-DgzLizSt.mjs} +1 -1
  33. package/dist/.setup/{subagent-hooks-CME7dSGf.mjs → subagent-hooks-tBtaqWkD.mjs} +21 -12
  34. package/dist/.setup/{system-events-BwwSDCw4.mjs → system-events-DGsxf-l9.mjs} +1 -1
  35. package/dist/.setup/{target-resolver-Bse8fuTi.mjs → target-resolver-BFOKmBni.mjs} +1 -1
  36. package/dist/.setup/{targets-DZJjkzwv.mjs → targets-UbCpek5o.mjs} +1 -1
  37. package/dist/.setup/{thread-bindings-0hPTSK14.mjs → thread-bindings-Dsc3zsrI.mjs} +29 -7
  38. package/dist/.setup/{thread-bindings.manager-CdNa6UIs.mjs → thread-bindings.manager-BMpmKLQh.mjs} +63 -19
  39. package/dist/.setup/{thread-bindings.session-updates-DGSljVf6.mjs → thread-bindings.session-updates-BMdJeJMc.mjs} +39 -122
  40. package/dist/.setup/{threading-BWz5lW0p.mjs → threading-Cg_QRc8s.mjs} +2 -2
  41. package/dist/.setup/{transcripts-source-EMrQb132.mjs → transcripts-source-fRvFsZng.mjs} +12 -17
  42. package/dist/.setup/{voice-runtime-BFEBhf2B.mjs → voice-runtime-DHDNu0fz.mjs} +73 -120
  43. package/dist/action-runtime-api.js +1 -1
  44. package/dist/activities-api.js +3 -3
  45. package/dist/api.js +11 -11
  46. package/dist/channel-plugin-api.js +1 -1
  47. package/dist/runtime-api.js +15 -15
  48. package/dist/setup-entry.js +1 -1
  49. package/dist/setup-plugin-api.js +1 -1
  50. package/dist/src/voice/audio-worker.runtime.js +8 -11
  51. package/dist/subagent-hooks-api.js +1 -1
  52. package/dist/transcripts-source-api.js +1 -1
  53. package/node_modules/mdast-util-from-markdown/dev/lib/index.js +152 -45
  54. package/node_modules/mdast-util-from-markdown/dev/lib/types.d.ts +26 -0
  55. package/node_modules/mdast-util-from-markdown/lib/index.js +133 -40
  56. package/node_modules/mdast-util-from-markdown/lib/types.d.ts +26 -0
  57. package/node_modules/mdast-util-from-markdown/package.json +6 -7
  58. package/node_modules/mdast-util-from-markdown/readme.md +15 -1
  59. package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
  60. package/node_modules/typebox/package.json +1 -1
  61. package/package.json +6 -6
  62. package/dist/.setup/conversation-binding-route-r4XyytCE.mjs +0 -51
  63. package/dist/.setup/provider.runtime-1yN_05AK.mjs +0 -1
  64. package/dist/.setup/setup-plugin-api-BFl2FljJ.mjs +0 -2
@@ -5,8 +5,8 @@ import { asOptionalObjectRecord, normalizeLowercaseStringOrEmpty, normalizeOptio
5
5
  import { resolveNonNegativeIntegerOption, resolveOptionalIntegerOption } from "openclaw/plugin-sdk/number-runtime";
6
6
  import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
7
7
  import { createPluginStateErrorReporter } from "openclaw/plugin-sdk/plugin-state-runtime";
8
+ import { resolveThreadBindingExpiry, warnPluginSdkDeprecation } from "openclaw/plugin-sdk/thread-bindings-session-runtime";
8
9
  import { recordOutboundMessageIdentity } from "openclaw/plugin-sdk/outbound-echo-runtime";
9
- import { resolveThreadBindingExpiry } from "openclaw/plugin-sdk/thread-bindings-session-runtime";
10
10
  //#region extensions/discord/src/monitor/thread-bindings.state.ts
11
11
  const THREAD_BINDINGS_STATE_KEY = Symbol.for("openclaw.discordThreadBindingsState");
12
12
  function createThreadBindingsGlobalState() {
@@ -21,7 +21,6 @@ function createThreadBindingsGlobalState() {
21
21
  loadedPersistentBindings: false,
22
22
  persistenceAvailable: true,
23
23
  lastPersistedAtMs: 0,
24
- revision: 0,
25
24
  mutationTail: Promise.resolve(),
26
25
  accountOperationTails: /* @__PURE__ */ new WeakMap()
27
26
  };
@@ -59,7 +58,7 @@ function openThreadBindingsStore() {
59
58
  });
60
59
  }
61
60
  function openThreadBindingsStoreAsync() {
62
- return getDiscordRuntime().state.openKeyedStore({
61
+ return getDiscordRuntime().state.openKeyedStoreV2({
63
62
  namespace: THREAD_BINDINGS_NAMESPACE,
64
63
  maxEntries: THREAD_BINDINGS_MAX_ENTRIES
65
64
  });
@@ -200,7 +199,6 @@ function setBindingRecord(record) {
200
199
  const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
201
200
  if (existing) unlinkSessionBinding(existing.targetSessionKey, bindingKey);
202
201
  BINDINGS_BY_THREAD_ID.set(bindingKey, record);
203
- THREAD_BINDINGS_STATE.revision += 1;
204
202
  linkSessionBinding(record.targetSessionKey, bindingKey);
205
203
  rememberReusableWebhook(record);
206
204
  }
@@ -210,14 +208,12 @@ function removeBindingRecord(bindingKeyRaw) {
210
208
  const existing = BINDINGS_BY_THREAD_ID.get(key);
211
209
  if (!existing) return null;
212
210
  BINDINGS_BY_THREAD_ID.delete(key);
213
- THREAD_BINDINGS_STATE.revision += 1;
214
211
  unlinkSessionBinding(existing.targetSessionKey, key);
215
212
  return existing;
216
213
  }
217
214
  function beginBindingsLoad() {
218
215
  THREAD_BINDINGS_STATE.loadedBindings = true;
219
216
  BINDINGS_BY_THREAD_ID.clear();
220
- THREAD_BINDINGS_STATE.revision += 1;
221
217
  BINDINGS_BY_SESSION_KEY.clear();
222
218
  REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL.clear();
223
219
  THREAD_BINDINGS_STATE.loadedPersistentBindings = false;
@@ -317,13 +313,12 @@ function drainThreadBindingMutations() {
317
313
  return THREAD_BINDINGS_STATE.mutationTail;
318
314
  }
319
315
  async function commitBindingRecord(params) {
320
- const revision = THREAD_BINDINGS_STATE.revision;
321
316
  let authorityRefused = false;
322
317
  let targetCommitted = false;
318
+ let committedWrites = 0;
323
319
  const assertCurrent = () => {
324
320
  try {
325
321
  params.assertCurrent?.();
326
- if (THREAD_BINDINGS_STATE.revision !== revision || BINDINGS_BY_THREAD_ID.get(params.bindingKey) !== params.previous) throw new Error("Discord thread binding changed during persistence");
327
322
  } catch (error) {
328
323
  authorityRefused = true;
329
324
  throw error;
@@ -335,34 +330,21 @@ async function commitBindingRecord(params) {
335
330
  const records = new Map(BINDINGS_BY_THREAD_ID);
336
331
  if (params.next) records.set(params.bindingKey, params.next);
337
332
  else records.delete(params.bindingKey);
338
- const active = {
339
- targetKey: params.bindingKey,
340
- deletingTarget: params.next === null,
341
- nextRecord: params.next,
342
- writingKey: void 0,
343
- committedKeys: /* @__PURE__ */ new Set()
344
- };
345
- THREAD_BINDINGS_STATE.activePersistence = active;
346
333
  try {
347
334
  const store = openThreadBindingsStoreAsync();
348
335
  for (const [key, record] of records) {
349
336
  assertCurrent();
350
337
  const persisted = toPersistedBindingRecord(record);
351
- if (key === params.bindingKey) active.nextRecord = persisted;
352
- active.writingKey = key;
353
338
  await store.register(key, persisted, { assertCurrent });
354
- active.writingKey = void 0;
355
- active.committedKeys.add(key);
339
+ committedWrites += 1;
356
340
  targetCommitted ||= key === params.bindingKey;
357
341
  }
358
342
  assertCurrent();
359
343
  const entries = await store.entries();
360
344
  assertCurrent();
361
345
  for (const entry of entries) if (!records.has(entry.key)) {
362
- active.writingKey = entry.key;
363
346
  await store.delete(entry.key, { assertCurrent });
364
- active.writingKey = void 0;
365
- active.committedKeys.add(entry.key);
347
+ committedWrites += 1;
366
348
  targetCommitted ||= entry.key === params.bindingKey;
367
349
  }
368
350
  if (!params.next) targetCommitted = true;
@@ -370,7 +352,6 @@ async function commitBindingRecord(params) {
370
352
  THREAD_BINDINGS_STATE.loadedPersistentBindings = records.size > 0;
371
353
  THREAD_BINDINGS_STATE.lastPersistedAtMs = now;
372
354
  } catch (error) {
373
- const committedWrites = active.committedKeys.size;
374
355
  let failure = error;
375
356
  if (!authorityRefused) try {
376
357
  assertCurrent();
@@ -390,8 +371,6 @@ async function commitBindingRecord(params) {
390
371
  THREAD_BINDINGS_STATE.persistenceAvailable = false;
391
372
  logVerbose("discord thread binding persistence unavailable; keeping bindings in memory");
392
373
  }
393
- } finally {
394
- delete THREAD_BINDINGS_STATE.activePersistence;
395
374
  }
396
375
  }
397
376
  if (!targetCommitted) assertCurrent();
@@ -399,38 +378,17 @@ async function commitBindingRecord(params) {
399
378
  if (params.next) setBindingRecord(params.next);
400
379
  else removeBindingRecord(params.bindingKey);
401
380
  }
402
- function persistBindingsSync(update, removedKey) {
381
+ function persistBindingsSync(update) {
403
382
  const store = openThreadBindingsStore();
404
- const active = THREAD_BINDINGS_STATE.activePersistence;
405
383
  let updatedRecord;
406
- for (const [key, record] of BINDINGS_BY_THREAD_ID) if (key === update?.bindingKey || active?.targetKey === key && active.deletingTarget || active?.writingKey === key || active?.committedKeys.has(key)) {
384
+ for (const [key, record] of BINDINGS_BY_THREAD_ID) if (key === update?.bindingKey) {
407
385
  if (!store.update) throw new Error("Discord synchronous compatibility requires atomic state update");
408
- let next;
409
- store.update(key, (current) => {
410
- let base = record;
411
- if (active?.targetKey === key) {
412
- if (!current && active.deletingTarget) base = void 0;
413
- else if (current && active.nextRecord && (active.writingKey === key || active.committedKeys.has(key)) && JSON.stringify(toPersistedBindingRecord(current)) === JSON.stringify(toPersistedBindingRecord(active.nextRecord))) base = normalizePersistedBinding(key, current) ?? record;
414
- }
415
- if (key === update?.bindingKey) update.observe?.(current && normalizePersistedBinding(key, current) ? base : void 0);
416
- next = base ? key === update?.bindingKey ? update.transform(base) : base : void 0;
417
- return next ? toPersistedBindingRecord(next) : void 0;
418
- });
419
- if (key === update?.bindingKey) updatedRecord = next;
420
- if (next) {
421
- if (next !== record) setBindingRecord(next);
422
- } else if (!(active?.targetKey === key && active.deletingTarget)) removeBindingRecord(key);
386
+ const next = update.transform(record);
387
+ store.update(key, () => toPersistedBindingRecord(next));
388
+ updatedRecord = next;
389
+ if (next !== record) setBindingRecord(next);
423
390
  } else store.register(key, toPersistedBindingRecord(record));
424
- for (const entry of store.entries()) if (!BINDINGS_BY_THREAD_ID.has(entry.key)) {
425
- if (entry.key !== removedKey && active?.targetKey === entry.key && !active.deletingTarget) {
426
- const committed = normalizePersistedBinding(entry.key, entry.value);
427
- if (committed) {
428
- setBindingRecord(committed);
429
- continue;
430
- }
431
- }
432
- store.delete(entry.key);
433
- }
391
+ for (const entry of store.entries()) if (!BINDINGS_BY_THREAD_ID.has(entry.key)) store.delete(entry.key);
434
392
  THREAD_BINDINGS_STATE.loadedPersistentBindings = BINDINGS_BY_THREAD_ID.size > 0;
435
393
  THREAD_BINDINGS_STATE.lastPersistedAtMs = Date.now();
436
394
  return updatedRecord;
@@ -440,76 +398,36 @@ function updateBindingRecordSync(params) {
440
398
  const record = BINDINGS_BY_THREAD_ID.get(params.bindingKey);
441
399
  if (!record) return null;
442
400
  const now = Date.now();
443
- const persist = params.persist && THREAD_BINDINGS_STATE.persistenceAvailable && (THREAD_BINDINGS_STATE.activePersistence || !params.minIntervalMs || now - THREAD_BINDINGS_STATE.lastPersistedAtMs >= params.minIntervalMs);
444
- let observed;
445
- if (persist) try {
446
- return persistBindingsSync({
447
- ...params,
448
- observe: (current) => {
449
- observed = current;
450
- }
451
- }) ?? null;
401
+ if (params.persist && THREAD_BINDINGS_STATE.persistenceAvailable && (!params.minIntervalMs || now - THREAD_BINDINGS_STATE.lastPersistedAtMs >= params.minIntervalMs)) try {
402
+ return persistBindingsSync(params) ?? null;
452
403
  } catch {
453
404
  THREAD_BINDINGS_STATE.persistenceAvailable = false;
454
405
  logVerbose("discord thread binding persistence unavailable; keeping bindings in memory");
455
406
  }
456
407
  const current = BINDINGS_BY_THREAD_ID.get(params.bindingKey);
457
408
  if (current !== record) return current ?? null;
458
- if (THREAD_BINDINGS_STATE.activePersistence?.targetKey === params.bindingKey && !observed) {
459
- logVerbose("discord synchronous binding update skipped: pending target read unavailable");
460
- return null;
461
- }
462
- const next = params.transform(observed ?? record);
409
+ const next = params.transform(record);
463
410
  setBindingRecord(next);
464
411
  return next;
465
412
  }
466
413
  /** Public SDK compatibility only; bundled callers await worker mutations. */
467
414
  function removeBindingRecordSync(bindingKey) {
468
- const record = BINDINGS_BY_THREAD_ID.get(bindingKey);
469
- if (!record) return null;
470
- if (!shouldPersistBindingMutations() || !THREAD_BINDINGS_STATE.persistenceAvailable) return THREAD_BINDINGS_STATE.activePersistence?.targetKey === bindingKey ? null : removeBindingRecord(bindingKey);
415
+ if (!BINDINGS_BY_THREAD_ID.get(bindingKey)) return null;
416
+ if (!shouldPersistBindingMutations() || !THREAD_BINDINGS_STATE.persistenceAvailable) return removeBindingRecord(bindingKey);
471
417
  let removed = null;
472
- let observed = null;
473
- let replacement;
474
418
  try {
475
- const store = openThreadBindingsStore();
476
- if (!store.deleteIf) throw new Error("Discord synchronous compatibility requires atomic state deletion");
477
- const deleted = store.deleteIf(bindingKey, (current) => {
478
- const normalized = normalizePersistedBinding(bindingKey, current);
479
- observed = normalized;
480
- const pending = THREAD_BINDINGS_STATE.activePersistence;
481
- if (normalized && pending?.targetKey === bindingKey && pending.nextRecord && (pending.writingKey === bindingKey || pending.committedKeys.has(bindingKey)) && JSON.stringify(toPersistedBindingRecord(normalized)) === JSON.stringify(toPersistedBindingRecord(pending.nextRecord)) && (normalized.targetSessionKey !== record.targetSessionKey || normalized.targetKind !== record.targetKind)) {
482
- replacement = normalized;
483
- return false;
484
- }
485
- return true;
486
- });
487
- if (replacement) {
488
- setBindingRecord(replacement);
489
- return null;
490
- }
491
- const pending = THREAD_BINDINGS_STATE.activePersistence;
492
- if (!deleted && pending?.targetKey === bindingKey && pending.deletingTarget) return null;
419
+ openThreadBindingsStore().delete(bindingKey);
493
420
  removed = removeBindingRecord(bindingKey);
494
- persistBindingsSync(void 0, bindingKey);
421
+ persistBindingsSync();
495
422
  return removed;
496
423
  } catch {
497
424
  THREAD_BINDINGS_STATE.persistenceAvailable = false;
498
- if (replacement) {
499
- setBindingRecord(replacement);
500
- return null;
501
- }
502
- if (THREAD_BINDINGS_STATE.activePersistence?.targetKey === bindingKey && !observed && !removed) {
503
- logVerbose("discord synchronous binding removal skipped: pending target read unavailable");
504
- return null;
505
- }
506
425
  return removed ?? removeBindingRecord(bindingKey);
507
426
  }
508
427
  }
509
428
  //#endregion
510
429
  //#region extensions/discord/src/monitor/thread-bindings.session-shared.ts
511
430
  function resolveBindingIdsForTargetSession(params) {
512
- ensureBindingsLoaded();
513
431
  return resolveBindingIdsForSession({
514
432
  ...params,
515
433
  accountId: params.accountId ? normalizeAccountId(params.accountId) : void 0
@@ -517,41 +435,33 @@ function resolveBindingIdsForTargetSession(params) {
517
435
  }
518
436
  function mutateBindingsForTargetSession(params, update, onRemoved) {
519
437
  const accountId = params.accountId ? normalizeAccountId(params.accountId) : void 0;
520
- const admittedOwners = /* @__PURE__ */ new Map();
521
- const ownerSnapshot = [...MANAGERS_BY_ACCOUNT_ID];
522
- for (const [ownerAccountId, manager] of ownerSnapshot) if (accountId === void 0 || ownerAccountId === accountId) admittedOwners.set(ownerAccountId, {
523
- manager,
524
- stopping: manager.isStopping()
525
- });
526
- return runThreadBindingAccountOperation([...admittedOwners.values()].map(({ manager }) => manager), () => runThreadBindingMutation(async () => {
438
+ const managers = [];
439
+ const stoppingAtAdmission = /* @__PURE__ */ new Map();
440
+ for (const [ownerAccountId, manager] of MANAGERS_BY_ACCOUNT_ID) if (accountId === void 0 || ownerAccountId === accountId) {
441
+ managers.push(manager);
442
+ stoppingAtAdmission.set(ownerAccountId, manager.isStopping());
443
+ }
444
+ return runThreadBindingAccountOperation(managers, () => runThreadBindingMutation(async () => {
527
445
  await ensureBindingsLoadedAsync();
528
446
  const ids = resolveBindingIdsForTargetSession(params);
529
- const owners = /* @__PURE__ */ new Map();
530
447
  for (const bindingKey of ids) {
531
448
  const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
532
449
  if (!existing) continue;
533
450
  const manager = MANAGERS_BY_ACCOUNT_ID.get(existing.accountId);
534
- const admitted = admittedOwners.get(existing.accountId);
535
- if (admitted?.stopping || !admitted && manager?.isStopping()) throw new Error("Discord thread binding manager is stopping");
536
- if (admitted?.manager !== manager) throw new Error("Discord thread binding manager changed");
537
- owners.set(existing.accountId, manager);
451
+ if (stoppingAtAdmission.get(existing.accountId) ?? manager?.isStopping()) throw new Error("Discord thread binding manager is stopping");
538
452
  }
539
453
  const now = Date.now();
540
454
  const updated = [];
541
455
  for (const bindingKey of ids) {
542
456
  const existing = BINDINGS_BY_THREAD_ID.get(bindingKey);
543
457
  if (!existing) continue;
544
- const manager = owners.get(existing.accountId);
545
- const assertCurrent = () => {
546
- if (MANAGERS_BY_ACCOUNT_ID.get(existing.accountId) !== manager) throw new Error("Discord thread binding manager changed");
547
- };
458
+ const manager = MANAGERS_BY_ACCOUNT_ID.get(existing.accountId);
548
459
  const nextRecord = update(existing, now);
549
460
  await commitBindingRecord({
550
461
  bindingKey,
551
462
  previous: existing,
552
463
  next: nextRecord,
553
- persist: shouldPersistBindingMutations(),
554
- assertCurrent
464
+ persist: shouldPersistBindingMutations()
555
465
  });
556
466
  if (!nextRecord) onRemoved?.(existing, manager);
557
467
  updated.push(nextRecord ?? existing);
@@ -594,6 +504,13 @@ function createDurationSetters(field) {
594
504
  return mutateBindingsForTargetSession(params, createDurationUpdate(field, params[field]));
595
505
  },
596
506
  set: (params) => {
507
+ const method = field === "idleTimeoutMs" ? "setThreadBindingIdleTimeoutBySessionKey" : "setThreadBindingMaxAgeBySessionKey";
508
+ warnPluginSdkDeprecation({
509
+ family: "conversation-bindings",
510
+ method,
511
+ replacement: `${method}Async`
512
+ });
513
+ ensureBindingsLoaded();
597
514
  return updateBindingsForTargetSessionSync(resolveBindingIdsForTargetSession(params), createDurationUpdate(field, params[field]));
598
515
  }
599
516
  };
@@ -602,9 +519,9 @@ const idleSetters = createDurationSetters("idleTimeoutMs");
602
519
  const maxAgeSetters = createDurationSetters("maxAgeMs");
603
520
  const setThreadBindingIdleTimeoutBySessionKeyAsync = idleSetters.setAsync;
604
521
  const setThreadBindingMaxAgeBySessionKeyAsync = maxAgeSetters.setAsync;
605
- /** @deprecated Use the awaited lifecycle setter; retained for the generic SDK contract. */
522
+ /** @deprecated Use setThreadBindingIdleTimeoutBySessionKeyAsync; compatible through 2026-12-31, removed in the first January 2027 stable. */
606
523
  const setThreadBindingIdleTimeoutBySessionKey = idleSetters.set;
607
- /** @deprecated Use the awaited lifecycle setter; retained for the generic SDK contract. */
524
+ /** @deprecated Use setThreadBindingMaxAgeBySessionKeyAsync; compatible through 2026-12-31, removed in the first January 2027 stable. */
608
525
  const setThreadBindingMaxAgeBySessionKey = maxAgeSetters.set;
609
526
  //#endregion
610
- export { resolveBindingRecordKey as A, getThreadBindingToken as C, rememberReusableWebhook as D, refreshUnboundThreadWebhookIdentity as E, resolveThreadBindingMaxAgeMs as F, shouldDefaultPersist as I, toBindingRecordKey as L, resolveThreadBindingIdleTimeoutMs as M, resolveThreadBindingInactivityExpiresAt as N, rememberThreadBindingToken as O, resolveThreadBindingMaxAgeExpiresAt as P, toReusableWebhookKey as R, forgetThreadBindingToken as S, normalizeThreadBindingDurationMs as T, MANAGERS_BY_ACCOUNT_ID as _, mutateBindingsForTargetSession as a, THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS as b, drainThreadBindingAccountOperations as c, runThreadBindingAccountOperation as d, runThreadBindingMutation as f, BINDINGS_BY_THREAD_ID as g, updateBindingRecordSync as h, setThreadBindingMaxAgeBySessionKeyAsync as i, resolvePreparedThreadBindingLifecycle as j, resolveBindingIdsForSession as k, drainThreadBindingMutations as l, snapshotThreadBindingMetadata as m, setThreadBindingIdleTimeoutBySessionKeyAsync as n, resolveBindingIdsForTargetSession as o, shouldPersistAnyBindingState as p, setThreadBindingMaxAgeBySessionKey as r, commitBindingRecord as s, setThreadBindingIdleTimeoutBySessionKey as t, removeBindingRecordSync as u, PERSIST_BY_ACCOUNT_ID as v, normalizeTargetKind as w, ensureBindingsLoadedAsync as x, REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL as y };
527
+ export { resolveBindingIdsForSession as A, forgetThreadBindingToken as C, refreshUnboundThreadWebhookIdentity as D, normalizeThreadBindingDurationMs as E, resolveThreadBindingMaxAgeExpiresAt as F, resolveThreadBindingMaxAgeMs as I, shouldDefaultPersist as L, resolvePreparedThreadBindingLifecycle as M, resolveThreadBindingIdleTimeoutMs as N, rememberReusableWebhook as O, resolveThreadBindingInactivityExpiresAt as P, toBindingRecordKey as R, ensureBindingsLoadedAsync as S, normalizeTargetKind as T, MANAGERS_BY_ACCOUNT_ID as _, mutateBindingsForTargetSession as a, THREAD_BINDING_TOUCH_PERSIST_MIN_INTERVAL_MS as b, drainThreadBindingAccountOperations as c, runThreadBindingAccountOperation as d, runThreadBindingMutation as f, BINDINGS_BY_THREAD_ID as g, updateBindingRecordSync as h, setThreadBindingMaxAgeBySessionKeyAsync as i, resolveBindingRecordKey as j, rememberThreadBindingToken as k, drainThreadBindingMutations as l, snapshotThreadBindingMetadata as m, setThreadBindingIdleTimeoutBySessionKeyAsync as n, resolveBindingIdsForTargetSession as o, shouldPersistAnyBindingState as p, setThreadBindingMaxAgeBySessionKey as r, commitBindingRecord as s, setThreadBindingIdleTimeoutBySessionKey as t, removeBindingRecordSync as u, PERSIST_BY_ACCOUNT_ID as v, getThreadBindingToken as w, ensureBindingsLoaded as x, REUSABLE_WEBHOOKS_BY_ACCOUNT_CHANNEL as y, toReusableWebhookKey as z };
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
2
- import { ct as createThread, ft as getChannelMessage, t as discord_exports } from "./discord-BGqgx-yd.mjs";
3
- import { d as resolveDiscordReplyDeliveryPlan, f as resolveDiscordReplyTarget, g as sanitizeDiscordThreadName, h as resolveDiscordThreadStarter, j as resolveDiscordMessageChannelId, m as resolveDiscordThreadParentInfo, p as resolveDiscordThreadChannel } from "./transcripts-source-EMrQb132.mjs";
2
+ import { lt as createThread, pt as getChannelMessage, t as discord_exports } from "./discord-Cl2OPTZ6.mjs";
3
+ import { d as resolveDiscordReplyDeliveryPlan, f as resolveDiscordReplyTarget, g as sanitizeDiscordThreadName, h as resolveDiscordThreadStarter, j as resolveDiscordMessageChannelId, m as resolveDiscordThreadParentInfo, p as resolveDiscordThreadChannel } from "./transcripts-source-fRvFsZng.mjs";
4
4
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
5
5
  import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing";
6
6
  import { normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -1,8 +1,8 @@
1
1
  import { l as resolveDiscordAccount, o as listEnabledDiscordAccounts, u as resolveDiscordAccountAllowFrom } from "./accounts-ljrzXBJi.mjs";
2
2
  import { A as isDiscordThreadChannelType, D as getDiscordEndpointRuntime, f as resolveDiscordGuildEntry, i as normalizeDiscordAllowList, k as resolveDiscordEndpointMediaGuard, l as resolveDiscordChannelConfigWithFallback, n as hasConfiguredDiscordChannels, o as normalizeDiscordSlug, p as resolveDiscordMemberAccessState, r as isDiscordGroupAllowedByPolicy, t as allowListMatches } from "./allow-list-B_ToXtew.mjs";
3
- import { L as DiscordError, ft as getChannelMessage, t as discord_exports } from "./discord-BGqgx-yd.mjs";
3
+ import { L as DiscordError, pt as getChannelMessage, t as discord_exports } from "./discord-Cl2OPTZ6.mjs";
4
4
  import { l as withAbortTimeout } from "./timeouts-CuNARfyw.mjs";
5
- import { t as resolveDiscordCommandOwnerAllowFrom } from "./command-owners-C7rUdqUX.mjs";
5
+ import { t as resolveDiscordCommandOwnerAllowFrom } from "./command-owners-D78sAoOz.mjs";
6
6
  import { a as resolveDiscordChannelParentSafe, i as resolveDiscordChannelParentIdSafe, n as resolveDiscordChannelInfoSafe, r as resolveDiscordChannelNameSafe, t as resolveDiscordChannelIdSafe } from "./channel-access-DasWY_OV.mjs";
7
7
  import { createSubsystemLogger, getChildLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
8
8
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
@@ -10,7 +10,6 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOpti
10
10
  import { ComponentType, StickerFormatType } from "discord-api-types/v10";
11
11
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
12
12
  import { saveRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
13
- import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
14
13
  import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
15
14
  import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
16
15
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -27,14 +26,11 @@ const DISCORD_CHANNEL_INFO_CACHE_TTL_MS = 3e5;
27
26
  const DISCORD_CHANNEL_INFO_NEGATIVE_CACHE_TTL_MS = 3e4;
28
27
  const DISCORD_CHANNEL_INFO_CACHE_MAX_ENTRIES = 1e3;
29
28
  function cacheDiscordChannelInfo(channelId, value, ttlMs, nowMs) {
30
- const expiresAt = resolveExpiresAtMsFromDurationMs(ttlMs, { nowMs });
31
- if (expiresAt !== void 0) {
32
- discordChannelInfoCacheState.entries.set(channelId, {
33
- value,
34
- expiresAt
35
- });
36
- pruneMapToMaxSize(discordChannelInfoCacheState.entries, DISCORD_CHANNEL_INFO_CACHE_MAX_ENTRIES);
37
- }
29
+ discordChannelInfoCacheState.entries.set(channelId, {
30
+ value,
31
+ expiresAt: nowMs + ttlMs
32
+ });
33
+ pruneMapToMaxSize(discordChannelInfoCacheState.entries, DISCORD_CHANNEL_INFO_CACHE_MAX_ENTRIES);
38
34
  }
39
35
  function normalizeDiscordChannelId(value) {
40
36
  return normalizeOptionalStringifiedId(value) ?? "";
@@ -55,26 +51,25 @@ function buildDiscordChannelInfo(channel, options) {
55
51
  };
56
52
  }
57
53
  async function resolveDiscordChannelInfo(client, channelId) {
58
- const rawNow = Date.now();
59
- const now = asDateTimestampMs(rawNow);
54
+ const now = Date.now();
60
55
  const cached = discordChannelInfoCacheState.entries.get(channelId);
61
56
  if (cached) {
62
- if (now !== void 0 && cached.expiresAt > now) return cached.value;
57
+ if (cached.expiresAt > now) return cached.value;
63
58
  discordChannelInfoCacheState.entries.delete(channelId);
64
59
  }
65
60
  try {
66
61
  const channel = await client.fetchChannel(channelId);
67
62
  if (!channel) {
68
- cacheDiscordChannelInfo(channelId, null, DISCORD_CHANNEL_INFO_NEGATIVE_CACHE_TTL_MS, rawNow);
63
+ cacheDiscordChannelInfo(channelId, null, DISCORD_CHANNEL_INFO_NEGATIVE_CACHE_TTL_MS, now);
69
64
  return null;
70
65
  }
71
66
  const payload = buildDiscordChannelInfo(channel, { rawTypeFallback: true });
72
67
  if (!payload) return null;
73
- cacheDiscordChannelInfo(channelId, payload, DISCORD_CHANNEL_INFO_CACHE_TTL_MS, rawNow);
68
+ cacheDiscordChannelInfo(channelId, payload, DISCORD_CHANNEL_INFO_CACHE_TTL_MS, now);
74
69
  return payload;
75
70
  } catch (err) {
76
71
  logVerbose(`discord: failed to fetch channel ${channelId}: ${String(err)}`);
77
- cacheDiscordChannelInfo(channelId, null, DISCORD_CHANNEL_INFO_NEGATIVE_CACHE_TTL_MS, rawNow);
72
+ cacheDiscordChannelInfo(channelId, null, DISCORD_CHANNEL_INFO_NEGATIVE_CACHE_TTL_MS, now);
78
73
  return null;
79
74
  }
80
75
  }