@openclaw/buzz 2026.9.8 → 2026.10.1-beta.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.
@@ -1,9 +1,10 @@
1
1
  import { m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
2
- import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-7of91SyC.mjs";
2
+ import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-CDDvS9rG.mjs";
3
3
  import { i as buildBuzzMessageTags, s as parseBuzzMessageEvent } from "./message-event-X_aBiM1X.mjs";
4
4
  import { z } from "zod";
5
5
  import { finalizeEvent } from "nostr-tools";
6
6
  import { isLoopbackHost } from "openclaw/plugin-sdk/ssrf-runtime";
7
+ import { createDeferred } from "openclaw/plugin-sdk/concurrency-runtime";
7
8
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
8
9
  import fs from "node:fs/promises";
9
10
  import path from "node:path";
@@ -65,54 +66,29 @@ async function readBuzzQaCredentialFile(params) {
65
66
  }
66
67
  //#endregion
67
68
  //#region extensions/buzz/src/qa/relay-client.ts
68
- const BUZZ_MESSAGE_KIND = 9;
69
- const MEMBERSHIP_TIMEOUT_MS = 1e4;
70
69
  const OBSERVER_READY_TIMEOUT_MS = 1e4;
71
70
  async function loadBuzzQaRoomMembership(params) {
72
- return await new Promise((resolve, reject) => {
73
- let latest;
74
- let settled = false;
75
- let receivedEose = false;
76
- const subscriptionRef = {};
77
- const finish = (error) => {
78
- if (settled) return;
79
- settled = true;
80
- clearTimeout(timeout);
81
- if (receivedEose) subscriptionRef.current?.close("membership loaded");
82
- if (error) reject(error);
83
- else if (latest) resolve(latest);
84
- else reject(/* @__PURE__ */ new Error(`Buzz QA room ${params.roomId} has no membership roster.`));
85
- };
86
- const timeout = setTimeout(() => {
87
- finish(/* @__PURE__ */ new Error(`Timed out loading Buzz QA room ${params.roomId} membership.`));
88
- params.relay.close();
89
- }, MEMBERSHIP_TIMEOUT_MS);
90
- try {
91
- subscriptionRef.current = openBuzzRelaySubscription(params.relay, [{
92
- kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
93
- authors: [params.relayPublicKey],
94
- "#d": [params.roomId],
95
- limit: 1
96
- }], {
97
- onevent: (event) => {
98
- const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
99
- if (membership?.roomId === params.roomId && isNewerBuzzRevision(membership, latest)) latest = membership;
100
- },
101
- oneose: () => {
102
- receivedEose = true;
103
- if (settled) subscriptionRef.current?.close("membership loaded");
104
- else finish();
105
- },
106
- onclose: (reason) => {
107
- if (reason !== "membership loaded") finish(/* @__PURE__ */ new Error(`Buzz QA membership subscription closed: ${reason}`));
108
- }
109
- });
110
- } catch (error) {
111
- finish(error instanceof Error ? error : new Error(String(error)));
112
- return;
113
- }
114
- if (settled && receivedEose) subscriptionRef.current.close("membership loaded");
71
+ let latest;
72
+ await queryBuzzRelaySnapshot({
73
+ relay: params.relay,
74
+ filters: [{
75
+ kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
76
+ authors: [params.relayPublicKey],
77
+ "#d": [params.roomId],
78
+ limit: 1
79
+ }],
80
+ timeoutMessage: `Timed out loading Buzz QA room ${params.roomId} membership.`,
81
+ abortMessage: "Buzz QA membership query aborted",
82
+ closeReason: "membership loaded",
83
+ closeMessage: (reason) => `Buzz QA membership subscription closed: ${reason}`,
84
+ onEvent: (event) => {
85
+ const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
86
+ if (membership?.roomId === params.roomId && isNewerBuzzRevision(membership, latest)) latest = membership;
87
+ },
88
+ result: () => {}
115
89
  });
90
+ if (!latest) throw new Error(`Buzz QA room ${params.roomId} has no membership roster.`);
91
+ return latest;
116
92
  }
117
93
  function assertBuzzQaMembership(membership, credentials) {
118
94
  if (!membership.members.has(credentials.driverPublicKey)) throw new Error(`Buzz QA driver ${credentials.driverPublicKey} is not a member of room ${credentials.roomId}.`);
@@ -143,19 +119,14 @@ async function createBuzzQaRelayDriver(params) {
143
119
  throw error;
144
120
  }
145
121
  let observerReady = false;
146
- let resolveObserverReady;
147
- let rejectObserverReady;
148
- const observerReadyPromise = new Promise((resolve, reject) => {
149
- resolveObserverReady = resolve;
150
- rejectObserverReady = reject;
151
- });
122
+ const readiness = createDeferred();
152
123
  const observerReadyTimeout = setTimeout(() => {
153
- rejectObserverReady?.(/* @__PURE__ */ new Error("Timed out waiting for the Buzz QA message observer."));
124
+ readiness.reject(/* @__PURE__ */ new Error("Timed out waiting for the Buzz QA message observer."));
154
125
  }, OBSERVER_READY_TIMEOUT_MS);
155
126
  let subscription;
156
127
  try {
157
128
  subscription = openBuzzRelaySubscription(relay, [{
158
- kinds: [BUZZ_MESSAGE_KIND],
129
+ kinds: [9],
159
130
  authors: [credentials.sutPublicKey],
160
131
  "#h": [credentials.roomId],
161
132
  since: Math.floor(Date.now() / 1e3) - 5
@@ -173,12 +144,12 @@ async function createBuzzQaRelayDriver(params) {
173
144
  oneose: () => {
174
145
  observerReady = true;
175
146
  clearTimeout(observerReadyTimeout);
176
- resolveObserverReady?.();
147
+ readiness.resolve();
177
148
  },
178
149
  onclose: (reason) => {
179
150
  if (!observerReady) {
180
151
  clearTimeout(observerReadyTimeout);
181
- rejectObserverReady?.(/* @__PURE__ */ new Error(`Buzz QA message observer closed before it was ready: ${reason}`));
152
+ readiness.reject(/* @__PURE__ */ new Error(`Buzz QA message observer closed before it was ready: ${reason}`));
182
153
  return;
183
154
  }
184
155
  if (reason !== "shutdown" && reason !== "relay connection closed by us") transportError = /* @__PURE__ */ new Error(`Buzz QA message subscription closed: ${reason}`);
@@ -191,7 +162,7 @@ async function createBuzzQaRelayDriver(params) {
191
162
  throw error;
192
163
  }
193
164
  try {
194
- await observerReadyPromise;
165
+ await readiness.promise;
195
166
  } catch (error) {
196
167
  lifecycleAbort.abort(error);
197
168
  relay.close();
@@ -210,7 +181,7 @@ async function createBuzzQaRelayDriver(params) {
210
181
  });
211
182
  if (input.mentionSut) tags.push(["p", credentials.sutPublicKey]);
212
183
  const event = finalizeEvent({
213
- kind: BUZZ_MESSAGE_KIND,
184
+ kind: 9,
214
185
  content: input.text,
215
186
  created_at: Math.floor(Date.now() / 1e3),
216
187
  tags
@@ -1,8 +1,8 @@
1
1
  import { a as resolveBuzzAccountConfig, c as BuzzConfigSchema, d as isConfiguredBuzzChannel, f as looksLikeBuzzTarget, i as resolveBuzzAccount, m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, p as normalizeBuzzTarget, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId, t as assertBuzzAccountAvailable, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
2
2
  import { n as listBuzzDirectoryPeersFromConfig, t as listBuzzDirectoryGroupsFromConfig } from "./directory-config-M9KSexdh.mjs";
3
- import { a as connectAuthenticatedBuzzRelay, c as openBuzzRelaySubscription, d as resolveBuzzSubscriptionBudget, f as BUZZ_ROOM_METADATA_KIND, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelaySession, p as BuzzDirectoryState, r as parseBuzzRoomMembershipChangeEvent, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as BUZZ_MAX_CONFIGURED_ROOMS } from "./room-membership-7of91SyC.mjs";
3
+ import { a as connectAuthenticatedBuzzRelay, c as openBuzzRelaySubscription, d as resolveBuzzSubscriptionBudget, f as BUZZ_ROOM_METADATA_KIND, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelaySession, p as BuzzDirectoryState, r as parseBuzzRoomMembershipChangeEvent, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as BUZZ_MAX_CONFIGURED_ROOMS } from "./room-membership-CDDvS9rG.mjs";
4
4
  import { a as formatBuzzMessageForAgent, c as inspectBuzzMentionSyntax, i as buildBuzzMessageTags, l as resolveBuzzMessageMentions, n as BUZZ_INBOUND_MESSAGE_KINDS, o as isBuzzInboundMessageKind, r as BUZZ_TYPING_INDICATOR_KIND, s as parseBuzzMessageEvent, t as BUZZ_DIFF_MESSAGE_KIND } from "./message-event-X_aBiM1X.mjs";
5
- import { i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-CVXY6_50.mjs";
5
+ import { i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-D3hfsPy6.mjs";
6
6
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-B2-Mrm4v.mjs";
7
7
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
8
8
  import { buildChannelOutboundSessionRoute, buildThreadAwareOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
@@ -11,12 +11,16 @@ import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-run
11
11
  import { parseThreadSessionSuffix } from "openclaw/plugin-sdk/routing";
12
12
  import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
13
13
  import { compareEvents, finalizeEvent, verifyEvent } from "nostr-tools";
14
+ import { asNonArrayRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
14
15
  import { chunkItems } from "openclaw/plugin-sdk/text-chunking";
15
- import { truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
16
+ import { safeParseJson, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
16
17
  import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
18
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
17
19
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
18
20
  import { computeBackoff, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
19
21
  import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
22
+ import { createDeferred as createDeferred$1 } from "openclaw/plugin-sdk/concurrency-runtime";
23
+ import { createAsyncLock } from "openclaw/plugin-sdk/async-lock-runtime";
20
24
  import { normalizeURL } from "nostr-tools/utils";
21
25
  import { buildChannelInboundEventContext, logInboundDrop, recordDroppedChannelInboundHistory, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
22
26
  import { createSubsystemLogger } from "openclaw/plugin-sdk/logging-core";
@@ -222,13 +226,7 @@ const CHANNEL_ADD_POLICIES = /* @__PURE__ */ new Set([
222
226
  "nobody"
223
227
  ]);
224
228
  function parseProfileContent(event) {
225
- if (!event) return {};
226
- try {
227
- const parsed = JSON.parse(event.content);
228
- return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { ...parsed } : {};
229
- } catch {
230
- return {};
231
- }
229
+ return asNonArrayRecord(event ? safeParseJson(event.content) : void 0);
232
230
  }
233
231
  function resolveProfileTags(event, authTag) {
234
232
  const existingTags = event?.tags ?? [];
@@ -240,10 +238,6 @@ function hasConfiguredAuthTag(event, authTag) {
240
238
  const authTags = event?.tags.filter((tag) => tag[0] === "auth") ?? [];
241
239
  return authTags.length === 1 && JSON.stringify(authTags[0]) === JSON.stringify(authTag);
242
240
  }
243
- function readNonEmptyString(content, key) {
244
- const value = content[key];
245
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
246
- }
247
241
  async function queryCurrentProfiles(params) {
248
242
  params.signal?.throwIfAborted();
249
243
  const latestByKind = /* @__PURE__ */ new Map();
@@ -293,7 +287,7 @@ async function syncBuzzProfile(params) {
293
287
  const currentAgentProfile = currentProfiles.get(AGENT_PROFILE_KIND);
294
288
  const metadataContent = parseProfileContent(currentMetadata);
295
289
  const agentContent = parseProfileContent(currentAgentProfile);
296
- const resolvedDisplayName = readNonEmptyString(metadataContent, "display_name") ?? readNonEmptyString(agentContent, "display_name") ?? readNonEmptyString(agentContent, "name") ?? displayName;
290
+ const resolvedDisplayName = normalizeOptionalString(metadataContent.display_name) ?? normalizeOptionalString(agentContent.display_name) ?? normalizeOptionalString(agentContent.name) ?? displayName;
297
291
  const events = [];
298
292
  if (metadataContent.display_name !== resolvedDisplayName || !hasConfiguredAuthTag(currentMetadata, params.authTag)) {
299
293
  metadataContent.display_name = resolvedDisplayName;
@@ -306,11 +300,11 @@ async function syncBuzzProfile(params) {
306
300
  }));
307
301
  }
308
302
  let agentProfileChanged = false;
309
- if (!readNonEmptyString(agentContent, "name")) {
303
+ if (!normalizeOptionalString(agentContent.name)) {
310
304
  agentContent.name = resolvedDisplayName;
311
305
  agentProfileChanged = true;
312
306
  }
313
- if (!readNonEmptyString(agentContent, "display_name")) {
307
+ if (!normalizeOptionalString(agentContent.display_name)) {
314
308
  agentContent.display_name = resolvedDisplayName;
315
309
  agentProfileChanged = true;
316
310
  }
@@ -347,15 +341,9 @@ function createBuzzReplayDispatchQueue(params) {
347
341
  let pendingHead = 0;
348
342
  let active = 0;
349
343
  let closed = false;
350
- let resolveDrained;
351
- const drained = new Promise((resolve) => {
352
- resolveDrained = resolve;
353
- });
344
+ const drained = createDeferred$1();
354
345
  const settleDrained = () => {
355
- if (closed && active === 0) {
356
- resolveDrained?.();
357
- resolveDrained = void 0;
358
- }
346
+ if (closed && active === 0) drained.resolve();
359
347
  };
360
348
  let reserved = 0;
361
349
  const reservationWaiters = [];
@@ -373,7 +361,6 @@ function createBuzzReplayDispatchQueue(params) {
373
361
  const task = pending[pendingHead];
374
362
  pendingHead += 1;
375
363
  compactPending();
376
- if (!task) continue;
377
364
  active += 1;
378
365
  Promise.resolve().then(task).catch(params.onTaskError).finally(() => {
379
366
  active -= 1;
@@ -417,10 +404,6 @@ function createBuzzReplayDispatchQueue(params) {
417
404
  const settleReservationWaiters = () => {
418
405
  while (reservationWaiters.length > 0) {
419
406
  const waiter = reservationWaiters[0];
420
- if (!waiter) {
421
- reservationWaiters.shift();
422
- continue;
423
- }
424
407
  if (closed) {
425
408
  reservationWaiters.shift();
426
409
  waiter.resolve(void 0);
@@ -449,7 +432,7 @@ function createBuzzReplayDispatchQueue(params) {
449
432
  pendingHead = 0;
450
433
  settleReservationWaiters();
451
434
  settleDrained();
452
- await drained;
435
+ await drained.promise;
453
436
  }
454
437
  };
455
438
  }
@@ -460,39 +443,31 @@ function resolveBuzzRoomHistoryLimit(roomCount) {
460
443
  //#region extensions/buzz/src/room-membership-query.ts
461
444
  const RELAY_QUERY_EVENT_LIMIT = 1e3;
462
445
  const MEMBERSHIP_QUERY_COMPLETE_REASON = "membership snapshot loaded";
463
- async function queryBuzzRoomMembershipBatch(params) {
464
- const configuredRooms = new Set(params.channelIds);
465
- const memberships = /* @__PURE__ */ new Map();
466
- return await queryBuzzRelaySnapshot({
467
- relay: params.relay,
468
- filters: [{
469
- kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
470
- authors: [params.relayPublicKey],
471
- "#d": params.channelIds,
472
- limit: params.channelIds.length
473
- }],
474
- signal: params.signal,
475
- timeoutMessage: "Timed out loading Buzz room membership snapshot",
476
- abortMessage: "Buzz room membership query aborted",
477
- failureMessage: "Buzz room membership query failed",
478
- closeReason: MEMBERSHIP_QUERY_COMPLETE_REASON,
479
- closeMessage: (reason) => `Buzz room membership query closed: ${reason}`,
480
- onEvent: (event) => {
481
- const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
482
- if (membership && configuredRooms.has(membership.roomId) && isNewerBuzzRevision(membership, memberships.get(membership.roomId))) memberships.set(membership.roomId, membership);
483
- },
484
- result: () => memberships,
485
- checkAbortAfterSubscribe: true
486
- });
487
- }
488
446
  async function queryBuzzRoomMemberships(params) {
489
447
  const memberships = /* @__PURE__ */ new Map();
490
- for (let index = 0; index < params.channelIds.length; index += RELAY_QUERY_EVENT_LIMIT) {
491
- const batch = await queryBuzzRoomMembershipBatch({
492
- ...params,
493
- channelIds: params.channelIds.slice(index, index + RELAY_QUERY_EVENT_LIMIT)
448
+ for (const channelIds of chunkItems(params.channelIds, RELAY_QUERY_EVENT_LIMIT)) {
449
+ const configuredRooms = new Set(channelIds);
450
+ await queryBuzzRelaySnapshot({
451
+ relay: params.relay,
452
+ filters: [{
453
+ kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
454
+ authors: [params.relayPublicKey],
455
+ "#d": channelIds,
456
+ limit: channelIds.length
457
+ }],
458
+ signal: params.signal,
459
+ timeoutMessage: "Timed out loading Buzz room membership snapshot",
460
+ abortMessage: "Buzz room membership query aborted",
461
+ failureMessage: "Buzz room membership query failed",
462
+ closeReason: MEMBERSHIP_QUERY_COMPLETE_REASON,
463
+ closeMessage: (reason) => `Buzz room membership query closed: ${reason}`,
464
+ onEvent: (event) => {
465
+ const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
466
+ if (membership && configuredRooms.has(membership.roomId) && isNewerBuzzRevision(membership, memberships.get(membership.roomId))) memberships.set(membership.roomId, membership);
467
+ },
468
+ result: () => {},
469
+ checkAbortAfterSubscribe: true
494
470
  });
495
- for (const [roomId, membership] of batch) if (isNewerBuzzRevision(membership, memberships.get(roomId))) memberships.set(roomId, membership);
496
471
  }
497
472
  return memberships;
498
473
  }
@@ -631,21 +606,12 @@ const MEMBERSHIP_REFRESH_DELAYS_MS = [
631
606
  const MEMBERSHIP_EVENT_CACHE_MAX_ENTRIES = 1e4;
632
607
  async function sleepWithSignal(delayMs, signal) {
633
608
  signal?.throwIfAborted();
634
- await new Promise((resolve, reject) => {
635
- let settled = false;
636
- const finish = (error) => {
637
- if (settled) return;
638
- settled = true;
639
- clearTimeout(timer);
640
- signal?.removeEventListener("abort", onAbort);
641
- if (error === void 0) resolve();
642
- else reject(error instanceof Error ? error : new Error("Buzz room membership refresh failed", { cause: error }));
643
- };
644
- const onAbort = () => finish(signal?.reason ?? /* @__PURE__ */ new Error("Buzz room membership refresh aborted"));
645
- const timer = setTimeout(() => finish(), delayMs);
646
- signal?.addEventListener("abort", onAbort, { once: true });
647
- if (signal?.aborted) onAbort();
648
- });
609
+ try {
610
+ await sleepWithAbort(delayMs, signal);
611
+ } catch {
612
+ const error = signal?.reason ?? /* @__PURE__ */ new Error("Buzz room membership refresh aborted");
613
+ throw error instanceof Error ? error : new Error("Buzz room membership refresh failed", { cause: error });
614
+ }
649
615
  }
650
616
  async function createBuzzRoomMembershipTracker(params) {
651
617
  const historicalRooms = /* @__PURE__ */ new Set();
@@ -656,7 +622,7 @@ async function createBuzzRoomMembershipTracker(params) {
656
622
  const pendingMemberships = /* @__PURE__ */ new Map();
657
623
  const refreshes = /* @__PURE__ */ new Map();
658
624
  const restoringRooms = /* @__PURE__ */ new Map();
659
- let membershipQueryTail = Promise.resolve();
625
+ const withMembershipQueryLock = createAsyncLock();
660
626
  const memberships = await queryBuzzRoomMemberships(params);
661
627
  params.signal?.throwIfAborted();
662
628
  const effectiveMemberships = () => {
@@ -700,19 +666,15 @@ async function createBuzzRoomMembershipTracker(params) {
700
666
  memberships.set(membership.roomId, membership);
701
667
  params.onMembershipsChanged?.(effectiveMemberships());
702
668
  };
703
- const queryMembership = (channelId) => {
704
- const query = membershipQueryTail.then(async () => {
705
- params.signal?.throwIfAborted();
706
- return (await queryBuzzRoomMemberships({
707
- relay: params.relay,
708
- relayPublicKey: params.relayPublicKey,
709
- channelIds: [channelId],
710
- signal: params.signal
711
- })).get(channelId);
712
- });
713
- membershipQueryTail = query.then(() => void 0, () => void 0);
714
- return query;
715
- };
669
+ const queryMembership = (channelId) => withMembershipQueryLock(async () => {
670
+ params.signal?.throwIfAborted();
671
+ return (await queryBuzzRoomMemberships({
672
+ relay: params.relay,
673
+ relayPublicKey: params.relayPublicKey,
674
+ channelIds: [channelId],
675
+ signal: params.signal
676
+ })).get(channelId);
677
+ });
716
678
  const refreshMembership = async (channelId, state) => {
717
679
  for (const delayMs of MEMBERSHIP_REFRESH_DELAYS_MS) {
718
680
  const generation = state.generation;
@@ -1010,66 +972,25 @@ function buildBuzzTextEvent(params) {
1010
972
  tags: buildBuzzMessageTags(params)
1011
973
  }, params.secretKey);
1012
974
  }
1013
- function buildBuzzTypingEvent(params) {
1014
- return finalizeEvent({
1015
- kind: BUZZ_TYPING_INDICATOR_KIND,
1016
- content: "",
1017
- created_at: Math.floor(Date.now() / 1e3),
1018
- tags: buildBuzzMessageTags(params)
1019
- }, params.secretKey);
1020
- }
1021
- function buildBuzzPresenceEvent(secretKey) {
1022
- return finalizeEvent({
1023
- kind: PRESENCE_KIND,
1024
- content: "online",
1025
- created_at: Math.floor(Date.now() / 1e3),
1026
- tags: []
1027
- }, secretKey);
1028
- }
1029
- function startBuzzPresenceHeartbeat(params) {
1030
- let stopped = false;
1031
- let publishInFlight = false;
1032
- let errorReported = false;
1033
- const publishOnline = async () => {
1034
- if (stopped || publishInFlight) return;
1035
- publishInFlight = true;
1036
- try {
1037
- await params.relay.publish(buildBuzzPresenceEvent(params.secretKey));
1038
- errorReported = false;
1039
- } catch (error) {
1040
- const failure = error instanceof Error ? error : new Error("Buzz presence heartbeat failed", { cause: error });
1041
- if (!stopped && failure.message === "publish timed out") params.onFatalError(failure);
1042
- else if (!stopped && !errorReported) {
1043
- errorReported = true;
1044
- params.onError?.(failure);
1045
- }
1046
- } finally {
1047
- publishInFlight = false;
1048
- }
1049
- };
1050
- publishOnline();
1051
- const timer = setInterval(() => {
1052
- publishOnline();
1053
- }, PRESENCE_HEARTBEAT_INTERVAL_MS);
1054
- timer.unref?.();
1055
- return () => {
1056
- stopped = true;
1057
- clearInterval(timer);
1058
- };
1059
- }
1060
975
  async function sendBuzzTextOneShot(params) {
1061
976
  const secretKey = decodeBuzzPrivateKey(params.privateKey);
1062
977
  const mentionSyntax = inspectBuzzMentionSyntax(params.text);
1063
- if (mentionSyntax.hasAtMention || mentionSyntax.hasExplicitIdentity) {
1064
- const signal = AbortSignal.timeout(3e4);
1065
- const publicKey = resolveBuzzPublicKey(params.privateKey);
1066
- const { relay, relayPublicKey } = await connectAuthenticatedBuzzRelaySession({
1067
- relayUrl: params.relayUrl,
1068
- secretKey,
1069
- authTag: parseBuzzAuthTag(params.authTag ?? ""),
1070
- signal
1071
- });
1072
- try {
978
+ const needsDirectory = mentionSyntax.hasAtMention || mentionSyntax.hasExplicitIdentity;
979
+ const signal = needsDirectory ? AbortSignal.timeout(3e4) : void 0;
980
+ const publicKey = needsDirectory ? resolveBuzzPublicKey(params.privateKey) : "";
981
+ const connection = {
982
+ relayUrl: params.relayUrl,
983
+ secretKey,
984
+ authTag: parseBuzzAuthTag(params.authTag ?? "")
985
+ };
986
+ const session = needsDirectory ? await connectAuthenticatedBuzzRelaySession({
987
+ ...connection,
988
+ signal
989
+ }) : void 0;
990
+ const relay = session?.relay ?? await connectAuthenticatedBuzzRelay(connection);
991
+ try {
992
+ let mentionedPubkeys;
993
+ if (session) {
1073
994
  const directory = new BuzzDirectoryState({
1074
995
  publicKey,
1075
996
  fallbackProfileName: "OpenClaw",
@@ -1077,7 +998,7 @@ async function sendBuzzTextOneShot(params) {
1077
998
  });
1078
999
  directory.replaceMemberships(await queryBuzzRoomMemberships({
1079
1000
  relay,
1080
- relayPublicKey,
1001
+ relayPublicKey: session.relayPublicKey,
1081
1002
  channelIds: [params.channelId],
1082
1003
  signal
1083
1004
  }));
@@ -1087,31 +1008,16 @@ async function sendBuzzTextOneShot(params) {
1087
1008
  publicKeys: directory.profilePublicKeys(),
1088
1009
  signal
1089
1010
  });
1090
- const mentionedPubkeys = resolveBuzzMessageMentions({
1011
+ mentionedPubkeys = resolveBuzzMessageMentions({
1091
1012
  text: params.text,
1092
1013
  members: directory.mentionMembers(params.channelId),
1093
1014
  senderPublicKey: publicKey
1094
1015
  });
1095
- const event = buildBuzzTextEvent({
1096
- ...params,
1097
- secretKey,
1098
- mentionedPubkeys
1099
- });
1100
- await relay.publish(event);
1101
- return event.id;
1102
- } finally {
1103
- relay.close();
1104
1016
  }
1105
- }
1106
- const relay = await connectAuthenticatedBuzzRelay({
1107
- relayUrl: params.relayUrl,
1108
- secretKey,
1109
- authTag: parseBuzzAuthTag(params.authTag ?? "")
1110
- });
1111
- try {
1112
1017
  const event = buildBuzzTextEvent({
1113
1018
  ...params,
1114
- secretKey
1019
+ secretKey,
1020
+ mentionedPubkeys
1115
1021
  });
1116
1022
  await relay.publish(event);
1117
1023
  return event.id;
@@ -1126,7 +1032,12 @@ async function startBuzzBus(options) {
1126
1032
  const authTag = parseBuzzAuthTag(options.authTag ?? "");
1127
1033
  const sessionStartedAt = Math.floor(Date.now() / 1e3);
1128
1034
  const lifecycleAbort = new AbortController();
1129
- const signal = options.signal ? AbortSignal.any([options.signal, lifecycleAbort.signal]) : lifecycleAbort.signal;
1035
+ const presenceScheduler = options.scheduler.scope();
1036
+ const signal = AbortSignal.any([
1037
+ lifecycleAbort.signal,
1038
+ presenceScheduler.signal,
1039
+ ...options.signal ? [options.signal] : []
1040
+ ]);
1130
1041
  const reportFatalError = (error) => {
1131
1042
  if (signal.aborted) return;
1132
1043
  lifecycleAbort.abort(error);
@@ -1162,7 +1073,6 @@ async function startBuzzBus(options) {
1162
1073
  profileLimit: subscriptionBudget.profileLimit
1163
1074
  });
1164
1075
  let directoryRelay;
1165
- let stopPresenceHeartbeat = () => {};
1166
1076
  let profileTask;
1167
1077
  let membershipTracker;
1168
1078
  const threadRoots = /* @__PURE__ */ new Map();
@@ -1236,24 +1146,28 @@ async function startBuzzBus(options) {
1236
1146
  },
1237
1147
  sendTyping: async ({ channelId, threadId, replyToId }) => {
1238
1148
  if (signal.aborted || !relay.connected) return;
1239
- const event = buildBuzzTypingEvent({
1240
- secretKey,
1241
- channelId,
1242
- threadId,
1243
- replyToId
1244
- });
1149
+ const event = finalizeEvent({
1150
+ kind: BUZZ_TYPING_INDICATOR_KIND,
1151
+ content: "",
1152
+ created_at: Math.floor(Date.now() / 1e3),
1153
+ tags: buildBuzzMessageTags({
1154
+ channelId,
1155
+ threadId,
1156
+ replyToId
1157
+ })
1158
+ }, secretKey);
1245
1159
  await relay.send(JSON.stringify(["EVENT", event]));
1246
1160
  },
1247
1161
  close: async () => {
1248
1162
  lifecycleAbort.abort(/* @__PURE__ */ new Error("Buzz bus closed"));
1163
+ presenceScheduler.beginClose();
1249
1164
  await dispatchQueue.close();
1250
- stopPresenceHeartbeat();
1251
1165
  directoryRelay?.close();
1252
1166
  replayGuard.clearMemory();
1253
1167
  threadRoots.clear();
1254
1168
  relay.close();
1255
1169
  await membershipTracker?.close();
1256
- await profileTask;
1170
+ await Promise.all([presenceScheduler.stop(), profileTask]);
1257
1171
  }
1258
1172
  };
1259
1173
  try {
@@ -1335,11 +1249,30 @@ async function startBuzzBus(options) {
1335
1249
  directory.replaceMemberships(membershipTracker?.memberships() ?? /* @__PURE__ */ new Map());
1336
1250
  directoryRelay.replaceProfilePublicKeys(directory.profilePublicKeys());
1337
1251
  membershipTracker?.catchUpHistory();
1338
- stopPresenceHeartbeat = startBuzzPresenceHeartbeat({
1339
- relay,
1340
- secretKey,
1341
- onError: options.onPresenceError,
1342
- onFatalError: reportFatalError
1252
+ let presenceErrorReported = false;
1253
+ presenceScheduler.schedule({
1254
+ id: "presence",
1255
+ delayMs: 0,
1256
+ everyMs: PRESENCE_HEARTBEAT_INTERVAL_MS,
1257
+ run: async () => {
1258
+ try {
1259
+ await relay.publish(finalizeEvent({
1260
+ kind: PRESENCE_KIND,
1261
+ content: "online",
1262
+ created_at: Math.floor(Date.now() / 1e3),
1263
+ tags: []
1264
+ }, secretKey));
1265
+ presenceErrorReported = false;
1266
+ } catch (error) {
1267
+ if (signal.aborted) return;
1268
+ const failure = error instanceof Error ? error : new Error("Buzz presence heartbeat failed", { cause: error });
1269
+ if (failure.message === "publish timed out") reportFatalError(failure);
1270
+ else if (!presenceErrorReported) {
1271
+ presenceErrorReported = true;
1272
+ options.onPresenceError?.(failure);
1273
+ }
1274
+ }
1275
+ }
1343
1276
  });
1344
1277
  if (options.profileName?.trim()) profileTask = syncBuzzProfile({
1345
1278
  relay,
@@ -1358,9 +1291,11 @@ async function startBuzzBus(options) {
1358
1291
  return bus;
1359
1292
  } catch (error) {
1360
1293
  lifecycleAbort.abort(error);
1294
+ presenceScheduler.beginClose();
1361
1295
  await dispatchQueue.close();
1362
1296
  directoryRelay?.close();
1363
1297
  relay.close();
1298
+ await presenceScheduler.stop();
1364
1299
  throw error;
1365
1300
  }
1366
1301
  }
@@ -1632,7 +1567,7 @@ async function handleBuzzInbound(params) {
1632
1567
  },
1633
1568
  delivery: {
1634
1569
  deliver: async (payload) => {
1635
- const text = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
1570
+ const text = payload.text ?? "";
1636
1571
  if (!text.trim()) return;
1637
1572
  await bus.sendText({
1638
1573
  ...replyTarget,
@@ -1749,10 +1684,7 @@ async function startBuzzGatewayAccount(ctx) {
1749
1684
  let bus;
1750
1685
  let cycleError;
1751
1686
  let connectedAt;
1752
- let reportBusFailure = () => {};
1753
- const busFailure = new Promise((resolve) => {
1754
- reportBusFailure = resolve;
1755
- });
1687
+ const { promise: busFailure, resolve: reportBusFailure } = createDeferred();
1756
1688
  try {
1757
1689
  const nowSeconds = Math.floor(Date.now() / 1e3);
1758
1690
  const sinceByRoom = await resolveBuzzRecoverySince({
@@ -1762,6 +1694,7 @@ async function startBuzzGatewayAccount(ctx) {
1762
1694
  lookbackSeconds: RECONNECT_LOOKBACK_SECONDS
1763
1695
  });
1764
1696
  bus = await startBuzzBus({
1697
+ scheduler: ctx.scheduler,
1765
1698
  accountId: account.accountId,
1766
1699
  relayUrl: account.relayUrl,
1767
1700
  privateKey: account.privateKey,
@@ -1909,54 +1842,41 @@ async function sendBuzzTyping(params) {
1909
1842
  //#endregion
1910
1843
  //#region extensions/buzz/src/directory.ts
1911
1844
  const DIRECTORY_LIVE_TIMEOUT_MS = 1e4;
1912
- function resolveConfiguredRoomIds(account) {
1913
- return Object.entries(account.config.groups ?? {}).filter(([, config]) => config.enabled !== false).map(([roomId]) => parseBuzzTarget(roomId));
1914
- }
1915
- function createConfiguredDirectoryState(params) {
1916
- const account = resolveBuzzAccount({
1917
- cfg: params.cfg,
1918
- accountId: params.accountId
1919
- });
1845
+ async function loadBuzzDirectoryState(params, refreshRooms) {
1846
+ const account = resolveBuzzAccount(params);
1920
1847
  if (account.enabled) assertBuzzAccountAvailable(account);
1921
1848
  if (!account.publicKey) return null;
1922
- const channelIds = resolveConfiguredRoomIds(account);
1923
- return {
1924
- account,
1925
- channelIds,
1926
- state: new BuzzDirectoryState({
1927
- publicKey: account.publicKey,
1928
- fallbackProfileName: account.name ?? "OpenClaw",
1929
- channelIds
1930
- })
1931
- };
1932
- }
1933
- async function loadBuzzDirectoryState(params, options) {
1934
- const configured = createConfiguredDirectoryState(params);
1935
- if (!configured || !configured.account.enabled || !configured.account.configured || configured.channelIds.length === 0) return configured?.state ?? null;
1936
- const activeBus = getActiveBuzzBus(configured.account.accountId);
1849
+ const channelIds = Object.entries(account.config.groups ?? {}).filter(([, config]) => config.enabled !== false).map(([roomId]) => parseBuzzTarget(roomId));
1850
+ const state = new BuzzDirectoryState({
1851
+ publicKey: account.publicKey,
1852
+ fallbackProfileName: account.name ?? "OpenClaw",
1853
+ channelIds
1854
+ });
1855
+ if (!account.enabled || !account.configured || channelIds.length === 0) return state;
1856
+ const activeBus = getActiveBuzzBus(account.accountId);
1937
1857
  if (activeBus) {
1938
- if (options.refreshRooms) try {
1858
+ if (refreshRooms) try {
1939
1859
  await activeBus.refreshDirectory();
1940
1860
  } catch {}
1941
1861
  return activeBus.directory;
1942
1862
  }
1943
1863
  const timeoutSignal = AbortSignal.timeout(DIRECTORY_LIVE_TIMEOUT_MS);
1944
1864
  const { relay, relayPublicKey } = await connectAuthenticatedBuzzRelaySession({
1945
- relayUrl: configured.account.relayUrl,
1946
- secretKey: decodeBuzzPrivateKey(configured.account.privateKey),
1947
- authTag: parseBuzzAuthTag(configured.account.authTag),
1865
+ relayUrl: account.relayUrl,
1866
+ secretKey: decodeBuzzPrivateKey(account.privateKey),
1867
+ authTag: parseBuzzAuthTag(account.authTag),
1948
1868
  signal: timeoutSignal
1949
1869
  });
1950
1870
  try {
1951
1871
  await queryBuzzDirectoryRooms({
1952
1872
  relay,
1953
1873
  relayPublicKey,
1954
- state: configured.state,
1955
- channelIds: configured.channelIds,
1874
+ state,
1875
+ channelIds,
1956
1876
  signal: timeoutSignal
1957
1877
  });
1958
- const activeChannelIds = configured.state.activeRoomIds();
1959
- configured.state.replaceMemberships(activeChannelIds.length > 0 ? await queryBuzzRoomMemberships({
1878
+ const activeChannelIds = state.activeRoomIds();
1879
+ state.replaceMemberships(activeChannelIds.length > 0 ? await queryBuzzRoomMemberships({
1960
1880
  relay,
1961
1881
  relayPublicKey,
1962
1882
  channelIds: activeChannelIds,
@@ -1964,36 +1884,27 @@ async function loadBuzzDirectoryState(params, options) {
1964
1884
  }) : /* @__PURE__ */ new Map());
1965
1885
  await queryBuzzDirectoryProfiles({
1966
1886
  relay,
1967
- state: configured.state,
1968
- publicKeys: configured.state.profilePublicKeys(),
1887
+ state,
1888
+ publicKeys: state.profilePublicKeys(),
1969
1889
  signal: timeoutSignal
1970
1890
  });
1971
- return configured.state;
1891
+ return state;
1972
1892
  } finally {
1973
1893
  relay.close();
1974
1894
  }
1975
1895
  }
1976
1896
  async function getBuzzDirectorySelf(params) {
1977
- return (await loadBuzzDirectoryState(params, { refreshRooms: false }))?.self() ?? null;
1897
+ return (await loadBuzzDirectoryState(params, false))?.self() ?? null;
1978
1898
  }
1979
1899
  async function listBuzzDirectoryPeersLive(params) {
1980
- return (await loadBuzzDirectoryState(params, { refreshRooms: false }))?.listPeers({
1981
- query: params.query,
1982
- limit: params.limit
1983
- }) ?? [];
1900
+ return (await loadBuzzDirectoryState(params, false))?.listPeers(params) ?? [];
1984
1901
  }
1985
1902
  async function listBuzzDirectoryGroupsLive(params) {
1986
1903
  if (!resolveBuzzAccountConfig(params).config.enabled) return listBuzzDirectoryGroupsFromConfig(params);
1987
- return (await loadBuzzDirectoryState(params, { refreshRooms: true }))?.listGroups({
1988
- query: params.query,
1989
- limit: params.limit
1990
- }) ?? [];
1904
+ return (await loadBuzzDirectoryState(params, true))?.listGroups(params) ?? [];
1991
1905
  }
1992
1906
  async function listBuzzDirectoryGroupMembers(params) {
1993
- return (await loadBuzzDirectoryState(params, { refreshRooms: false }))?.listGroupMembers({
1994
- groupId: params.groupId,
1995
- limit: params.limit
1996
- }) ?? [];
1907
+ return (await loadBuzzDirectoryState(params, false))?.listGroupMembers(params) ?? [];
1997
1908
  }
1998
1909
  //#endregion
1999
1910
  //#region extensions/buzz/src/channel.ts
@@ -2148,7 +2059,10 @@ const buzzPlugin = createChatChannelPlugin({
2148
2059
  };
2149
2060
  }
2150
2061
  },
2151
- gateway: { startAccount: startBuzzGatewayAccount },
2062
+ gateway: {
2063
+ apiVersion: 2,
2064
+ startAccount: startBuzzGatewayAccount
2065
+ },
2152
2066
  heartbeat: { sendTyping: sendBuzzTyping },
2153
2067
  directory: createChannelDirectoryAdapter({
2154
2068
  self: getBuzzDirectorySelf,
@@ -1,7 +1,8 @@
1
1
  import { l as BUZZ_CHANNEL_ID_PATTERN, m as parseBuzzTarget, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
2
2
  import { t as applyBuzzDirectoryQueryAndLimit } from "./directory-query-D1BNu8nO.mjs";
3
3
  import { Relay, finalizeEvent } from "nostr-tools";
4
- import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
4
+ import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { safeParseJson, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
5
6
  import { readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
6
7
  import { fetchWithSsrFGuard, ssrfPolicyFromHttpBaseUrlAllowedOrigin } from "openclaw/plugin-sdk/ssrf-runtime";
7
8
  //#region extensions/buzz/src/event-order.ts
@@ -15,10 +16,8 @@ const MAX_DIRECTORY_NAME_CHARS = 512;
15
16
  const MAX_DIRECTORY_HANDLE_CHARS = 320;
16
17
  const MAX_DIRECTORY_URL_CHARS = 4096;
17
18
  function normalizeBoundedString(value, maxChars) {
18
- if (typeof value !== "string") return;
19
- const trimmed = value.trim();
20
- if (!trimmed) return;
21
- return truncateUtf16Safe(trimmed, maxChars);
19
+ const trimmed = normalizeOptionalString(value);
20
+ return trimmed ? truncateUtf16Safe(trimmed, maxChars) : void 0;
22
21
  }
23
22
  function readPreferredString(params) {
24
23
  if (Object.hasOwn(params.content, params.primary)) return normalizeBoundedString(params.content[params.primary], params.maxChars);
@@ -30,14 +29,8 @@ function fallbackPublicKeyLabel(publicKey) {
30
29
  function parseBuzzDirectoryProfileEvent(event) {
31
30
  const publicKey = event.pubkey.trim().toLowerCase();
32
31
  if (event.kind !== 0 || !HEX_PUBLIC_KEY_PATTERN$2.test(publicKey)) return;
33
- let content;
34
- try {
35
- const parsed = JSON.parse(event.content);
36
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
37
- content = parsed;
38
- } catch {
39
- return;
40
- }
32
+ const content = safeParseJson(event.content);
33
+ if (!isRecord(content)) return;
41
34
  return {
42
35
  publicKey,
43
36
  displayName: readPreferredString({
@@ -276,7 +269,7 @@ async function queryBuzzRelaySnapshot(params) {
276
269
  queries.waiting.splice(index, 1);
277
270
  params.signal?.removeEventListener("abort", onAbort);
278
271
  const reason = params.signal?.reason ?? new Error(params.abortMessage);
279
- reject(reason instanceof Error ? reason : new Error(params.failureMessage, { cause: reason }));
272
+ reject(reason instanceof Error ? reason : snapshotFailure(params.failureMessage, reason));
280
273
  };
281
274
  queries.waiting.push(grant);
282
275
  params.signal?.addEventListener("abort", onAbort, { once: true });
@@ -320,7 +313,7 @@ async function queryBuzzRelaySnapshotNow(params) {
320
313
  params.signal?.removeEventListener("abort", onAbort);
321
314
  closeAfterRealEose();
322
315
  if (error === void 0) resolve(params.result());
323
- else reject(error instanceof Error ? error : new Error(params.failureMessage, { cause: error }));
316
+ else reject(error instanceof Error ? error : snapshotFailure(params.failureMessage, error));
324
317
  };
325
318
  const onAbort = () => finish(params.signal?.reason ?? new Error(params.abortMessage));
326
319
  params.signal?.addEventListener("abort", onAbort, { once: true });
@@ -344,6 +337,9 @@ async function queryBuzzRelaySnapshotNow(params) {
344
337
  if (params.checkAbortAfterSubscribe && params.signal?.aborted) onAbort();
345
338
  });
346
339
  }
340
+ function snapshotFailure(message, error) {
341
+ return message === void 0 ? new Error(String(error)) : new Error(message, { cause: error });
342
+ }
347
343
  //#endregion
348
344
  //#region extensions/buzz/src/relay-auth.ts
349
345
  const AUTH_CHALLENGE_TIMEOUT_MS = 2e4;
@@ -1,5 +1,5 @@
1
1
  import { c as BuzzConfigSchema, i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./types-DRUZMyTx.mjs";
2
- import { n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-CVXY6_50.mjs";
2
+ import { n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-D3hfsPy6.mjs";
3
3
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
4
  //#region extensions/buzz/src/channel.setup.ts
5
5
  const buzzSetupPlugin = {
@@ -0,0 +1,2 @@
1
+ import { t as buzzSetupPlugin } from "./setup-plugin-api-1zcpIc0A.mjs";
2
+ export { buzzSetupPlugin };
@@ -1,7 +1,7 @@
1
1
  import { a as resolveBuzzAccountConfig, i as resolveBuzzAccount, l as BUZZ_CHANNEL_ID_PATTERN, m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, r as listBuzzAccountIds } from "./types-DRUZMyTx.mjs";
2
- import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-7of91SyC.mjs";
2
+ import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-CDDvS9rG.mjs";
3
3
  import { generateSecretKey, nip19 } from "nostr-tools";
4
- import { sleep } from "openclaw/plugin-sdk/runtime-env";
4
+ import { sleep, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
5
5
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
6
6
  import { patchScopedAccountConfig, patchTopLevelChannelConfigSection, promptAccountId, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
7
7
  import { isIP } from "node:net";
@@ -271,20 +271,12 @@ async function sleepWithSignal(delayMs, signal) {
271
271
  signal.throwIfAborted();
272
272
  return;
273
273
  }
274
- await new Promise((resolve, reject) => {
275
- const finish = (error) => {
276
- clearTimeout(timer);
277
- signal.removeEventListener("abort", onAbort);
278
- if (error !== void 0) reject(error instanceof Error ? error : new Error("Buzz room access wait failed", { cause: error }));
279
- else resolve();
280
- };
281
- const onAbort = () => {
282
- finish(signal.reason ?? /* @__PURE__ */ new Error("Buzz room access wait aborted"));
283
- };
284
- const timer = setTimeout(() => finish(), delayMs);
285
- signal.addEventListener("abort", onAbort, { once: true });
286
- if (signal.aborted) onAbort();
287
- });
274
+ try {
275
+ await sleepWithAbort(delayMs, signal);
276
+ } catch {
277
+ const error = signal.reason ?? /* @__PURE__ */ new Error("Buzz room access wait aborted");
278
+ throw error instanceof Error ? error : new Error("Buzz room access wait failed", { cause: error });
279
+ }
288
280
  }
289
281
  async function waitForBuzzRoomAccess(params) {
290
282
  const secretKey = decodeBuzzPrivateKey(params.privateKey);
@@ -302,7 +294,6 @@ async function waitForBuzzRoomAccess(params) {
302
294
  let settled = false;
303
295
  let checking = false;
304
296
  let queuedRetry = false;
305
- const subscriptionRef = {};
306
297
  let pollTimer;
307
298
  const seenEvents = /* @__PURE__ */ new Set();
308
299
  const finish = (error, rooms) => {
@@ -357,7 +348,7 @@ async function waitForBuzzRoomAccess(params) {
357
348
  }
358
349
  };
359
350
  signal.addEventListener("abort", onAbort, { once: true });
360
- subscriptionRef.current = openBuzzRelaySubscription(relay, [{
351
+ openBuzzRelaySubscription(relay, [{
361
352
  kinds: [BUZZ_MEMBER_ADDED_NOTIFICATION_KIND],
362
353
  "#p": [publicKey],
363
354
  since: Math.floor(Date.now() / 1e3) - 30
package/dist/api.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./.setup/types-DRUZMyTx.mjs";
2
- import { n as getBuzzRuntime, r as setBuzzRuntime, t as buzzPlugin } from "./.setup/channel-B7r1381j.mjs";
2
+ import { n as getBuzzRuntime, r as setBuzzRuntime, t as buzzPlugin } from "./.setup/channel-BbJ5DvVO.mjs";
3
3
  export { buzzPlugin, getBuzzRuntime, listBuzzAccountIds, resolveBuzzAccount, resolveDefaultBuzzAccountId, setBuzzRuntime };
@@ -1,2 +1,2 @@
1
- import { t as buzzPlugin } from "./.setup/channel-B7r1381j.mjs";
1
+ import { t as buzzPlugin } from "./.setup/channel-BbJ5DvVO.mjs";
2
2
  export { buzzPlugin };
@@ -1,7 +1,7 @@
1
1
  import { createLazyCliRuntimeLoader, createLiveTransportQaCliRegistration, runLiveTransportQaSuiteCommand } from "openclaw/plugin-sdk/qa-runner-runtime";
2
2
  //#region extensions/buzz/src/qa/cli.ts
3
3
  const DEFAULT_BUZZ_QA_SCENARIOS = ["channel-canary", "channel-mention-gating"];
4
- const loadBuzzQaAdapterRuntime = createLazyCliRuntimeLoader(() => import("./.setup/adapter.runtime-Bpx9a0g6.mjs"));
4
+ const loadBuzzQaAdapterRuntime = createLazyCliRuntimeLoader(() => import("./.setup/adapter.runtime-DE9Zjy2H.mjs"));
5
5
  async function runQaBuzz(options) {
6
6
  await runLiveTransportQaSuiteCommand({
7
7
  channelId: "buzz",
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  importMetaUrl: import.meta.url,
6
6
  plugin: {
7
- specifier: fileURLToPath(new URL(".setup/setup-plugin-api-CtM1M1Oa.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/setup-plugin-api-ebTNvTyI.mjs", import.meta.url).href),
8
8
  exportName: "buzzSetupPlugin"
9
9
  },
10
10
  secrets: {
@@ -1,2 +1,2 @@
1
- import { t as buzzSetupPlugin } from "./.setup/setup-plugin-api-m1pk2Cow.mjs";
1
+ import { t as buzzSetupPlugin } from "./.setup/setup-plugin-api-1zcpIc0A.mjs";
2
2
  export { buzzSetupPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/buzz",
3
- "version": "2026.9.8",
3
+ "version": "2026.10.1-beta.1",
4
4
  "description": "Connect OpenClaw agents to Buzz rooms",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  "zod": "4.6.5"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.9.8"
15
+ "openclaw": ">=2026.10.1-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
@@ -84,10 +84,10 @@
84
84
  "minHostVersion": ">=2026.7.2"
85
85
  },
86
86
  "compat": {
87
- "pluginApi": ">=2026.9.8"
87
+ "pluginApi": ">=2026.10.1-beta.1"
88
88
  },
89
89
  "build": {
90
- "openclawVersion": "2026.9.8"
90
+ "openclawVersion": "2026.10.1-beta.1"
91
91
  },
92
92
  "release": {
93
93
  "publishToClawHub": true,
@@ -1,2 +0,0 @@
1
- import { t as buzzSetupPlugin } from "./setup-plugin-api-m1pk2Cow.mjs";
2
- export { buzzSetupPlugin };