@openclaw/buzz 2026.8.1-beta.3 → 2026.9.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.
- package/dist/{adapter.runtime-B0130te-.js → adapter.runtime-DA6guOQ_.js} +4 -4
- package/dist/api.js +1 -1
- package/dist/{channel-Bg5qZukN.js → channel-BulZqShF.js} +215 -252
- package/dist/channel-plugin-api.js +1 -1
- package/dist/qa-runner-api.js +2 -2
- package/dist/{room-membership-CRuRK8PE.js → room-membership-DSAkyE0a.js} +54 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-sJVYSY73.js → setup-surface-BvQgEJ7O.js} +19 -41
- package/node_modules/nostr-tools/lib/cjs/abstract-pool.js +7 -5
- package/node_modules/nostr-tools/lib/cjs/abstract-pool.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/abstract-relay.js +6 -4
- package/node_modules/nostr-tools/lib/cjs/abstract-relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/core.js +10 -6
- package/node_modules/nostr-tools/lib/cjs/core.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/index.js +17 -11
- package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/kinds.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/kinds.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip04.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip10.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip10.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip13.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip17.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip17.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip18.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip18.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip19.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip21.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip22.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip22.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip25.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip25.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip27.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip28.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip28.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip29.js +288 -12
- package/node_modules/nostr-tools/lib/cjs/nip29.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip44.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip45.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip45.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip46.js +9 -5
- package/node_modules/nostr-tools/lib/cjs/nip46.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip47.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip47.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip57.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip57.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip59.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip59.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip98.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nip98.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nipb0.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nipb7.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/pool.js +9 -5
- package/node_modules/nostr-tools/lib/cjs/pool.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/pure.js +10 -6
- package/node_modules/nostr-tools/lib/cjs/pure.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/references.js.map +1 -1
- package/node_modules/nostr-tools/lib/cjs/relay.js +8 -4
- package/node_modules/nostr-tools/lib/cjs/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/signer.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/signer.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/utils.js +2 -0
- package/node_modules/nostr-tools/lib/cjs/utils.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/wasm.js +10 -6
- package/node_modules/nostr-tools/lib/cjs/wasm.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/abstract-pool.js +7 -5
- package/node_modules/nostr-tools/lib/esm/abstract-pool.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/abstract-relay.js +6 -4
- package/node_modules/nostr-tools/lib/esm/abstract-relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/core.js +10 -6
- package/node_modules/nostr-tools/lib/esm/core.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/index.js +17 -11
- package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/kinds.js +2 -0
- package/node_modules/nostr-tools/lib/esm/kinds.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip04.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip10.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip10.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip13.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip17.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip17.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip18.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip18.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip19.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip21.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip22.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip22.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip25.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip25.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip27.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip28.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip28.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip29.js +288 -12
- package/node_modules/nostr-tools/lib/esm/nip29.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip44.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/nip45.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip45.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip46.js +9 -5
- package/node_modules/nostr-tools/lib/esm/nip46.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip47.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip47.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip57.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip57.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip59.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip59.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip98.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nip98.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nipb0.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nipb0.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nipb7.js +2 -0
- package/node_modules/nostr-tools/lib/esm/nipb7.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/pool.js +9 -5
- package/node_modules/nostr-tools/lib/esm/pool.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/pure.js +10 -6
- package/node_modules/nostr-tools/lib/esm/pure.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/references.js.map +1 -1
- package/node_modules/nostr-tools/lib/esm/relay.js +8 -4
- package/node_modules/nostr-tools/lib/esm/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/signer.js +2 -0
- package/node_modules/nostr-tools/lib/esm/signer.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/utils.js +2 -0
- package/node_modules/nostr-tools/lib/esm/utils.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/wasm.js +10 -6
- package/node_modules/nostr-tools/lib/esm/wasm.js.map +2 -2
- package/node_modules/nostr-tools/lib/nostr.bundle.js +17 -11
- package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
- package/node_modules/nostr-tools/lib/types/core.d.ts +5 -0
- package/node_modules/nostr-tools/lib/types/nip29.d.ts +243 -3
- package/node_modules/nostr-tools/lib/types/nip29.test.d.ts +1 -0
- package/node_modules/nostr-tools/package.json +1 -1
- package/openclaw.plugin.json +24 -0
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as resolveBuzzPublicKey, c as buildBuzzTarget, d as normalizeBuzzTarget, f as parseBuzzTarget, i as resolveBuzzAccount, l as isConfiguredBuzzChannel, n as decodeBuzzPrivateKey, o as resolveDefaultBuzzAccountId, r as listBuzzAccountIds, t as assertBuzzAccountAvailable, u as looksLikeBuzzTarget } from "./types-BN_HOsnq.js";
|
|
2
|
-
import { a as BuzzConfigSchema, i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-
|
|
2
|
+
import { a as BuzzConfigSchema, i as startBuzzRoomMembershipNotifications, n as buzzSetupContract, r as discoverBuzzRooms, t as buzzSetupWizard } from "./setup-surface-BvQgEJ7O.js";
|
|
3
3
|
import { n as listBuzzDirectoryPeersFromConfig, t as listBuzzDirectoryGroupsFromConfig } from "./directory-config-BesCse0q.js";
|
|
4
|
-
import { a as parseBuzzRoomMembershipEvent, c as parseBuzzAuthTag, i as parseBuzzRoomMembershipChangeEvent, l as openBuzzRelaySubscription, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelay, r as isNewerBuzzRoomMembership, s as connectAuthenticatedBuzzRelaySession, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-
|
|
4
|
+
import { a as parseBuzzRoomMembershipEvent, c as parseBuzzAuthTag, i as parseBuzzRoomMembershipChangeEvent, l as openBuzzRelaySubscription, n as BUZZ_ROOM_SYSTEM_KIND, o as connectAuthenticatedBuzzRelay, r as isNewerBuzzRoomMembership, s as connectAuthenticatedBuzzRelaySession, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as queryBuzzRelaySnapshot } from "./room-membership-DSAkyE0a.js";
|
|
5
5
|
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-CFw7gGLi.js";
|
|
6
6
|
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-D19RiJv_.js";
|
|
7
7
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
@@ -21,6 +21,7 @@ import { buildChannelInboundEventContext, resolveChannelInboundRouteEnvelope } f
|
|
|
21
21
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
22
22
|
import { createSubsystemLogger } from "openclaw/plugin-sdk/logging-core";
|
|
23
23
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
24
25
|
const BUZZ_ROOM_METADATA_KIND = 39e3;
|
|
25
26
|
const DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT = 2e3;
|
|
26
27
|
const HEX_PUBLIC_KEY_PATTERN = /^[0-9a-f]{64}$/u;
|
|
@@ -251,49 +252,23 @@ const PROFILE_SUBSCRIPTION_REPLACED_REASON = "directory profile subscription rep
|
|
|
251
252
|
const PROFILE_SUBSCRIPTION_FAILED_REASON = "directory profile subscription generation failed";
|
|
252
253
|
const DIRECTORY_SHUTDOWN_REASON = "directory shutdown";
|
|
253
254
|
const DIRECTORY_QUERY_COMPLETE_REASON = "directory query complete";
|
|
254
|
-
const DIRECTORY_QUERY_TIMEOUT_MS = 1e4;
|
|
255
255
|
const PROFILE_SUBSCRIPTION_READY_TIMEOUT_MS = 1e4;
|
|
256
256
|
async function queryBuzzDirectoryBatch(params) {
|
|
257
257
|
params.signal?.throwIfAborted();
|
|
258
|
-
await
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
params.signal?.removeEventListener("abort", onAbort);
|
|
273
|
-
if (receivedEose) subscriptionRef.current?.close(DIRECTORY_QUERY_COMPLETE_REASON);
|
|
274
|
-
if (error === void 0) resolve();
|
|
275
|
-
else reject(error instanceof Error ? error : new Error("Buzz directory query failed", { cause: error }));
|
|
276
|
-
};
|
|
277
|
-
const onAbort = () => finish(params.signal?.reason ?? /* @__PURE__ */ new Error("Buzz directory query aborted"));
|
|
278
|
-
params.signal?.addEventListener("abort", onAbort, { once: true });
|
|
279
|
-
try {
|
|
280
|
-
subscriptionRef.current = openBuzzRelaySubscription(params.relay, [params.filter], {
|
|
281
|
-
onevent: params.onEvent,
|
|
282
|
-
oneose: () => {
|
|
283
|
-
receivedEose = true;
|
|
284
|
-
if (settled) subscriptionRef.current?.close(DIRECTORY_QUERY_COMPLETE_REASON);
|
|
285
|
-
else finish();
|
|
286
|
-
},
|
|
287
|
-
onclose: (reason) => {
|
|
288
|
-
if (reason !== DIRECTORY_QUERY_COMPLETE_REASON) finish(/* @__PURE__ */ new Error(`Buzz directory query closed: ${reason}`));
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
} catch (error) {
|
|
292
|
-
finish(error);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
if (settled && receivedEose) subscriptionRef.current.close(DIRECTORY_QUERY_COMPLETE_REASON);
|
|
296
|
-
if (params.signal?.aborted) onAbort();
|
|
258
|
+
await queryBuzzRelaySnapshot({
|
|
259
|
+
relay: params.relay,
|
|
260
|
+
filters: [params.filter],
|
|
261
|
+
signal: params.signal,
|
|
262
|
+
timeoutMessage: "Timed out loading Buzz directory snapshot",
|
|
263
|
+
abortMessage: "Buzz directory query aborted",
|
|
264
|
+
failureMessage: "Buzz directory query failed",
|
|
265
|
+
closeReason: DIRECTORY_QUERY_COMPLETE_REASON,
|
|
266
|
+
closeMessage: (reason) => `Buzz directory query closed: ${reason}`,
|
|
267
|
+
onEvent: params.onEvent,
|
|
268
|
+
result: () => {},
|
|
269
|
+
onTimeout: params.onTimeout,
|
|
270
|
+
closeRelayOnTimeout: !params.onTimeout,
|
|
271
|
+
checkAbortAfterSubscribe: true
|
|
297
272
|
});
|
|
298
273
|
}
|
|
299
274
|
async function queryBuzzDirectoryProfiles(params) {
|
|
@@ -469,7 +444,6 @@ const CHANNEL_ADD_POLICIES = /* @__PURE__ */ new Set([
|
|
|
469
444
|
"owner_only",
|
|
470
445
|
"nobody"
|
|
471
446
|
]);
|
|
472
|
-
const PROFILE_QUERY_TIMEOUT_MS = 1e4;
|
|
473
447
|
function parseProfileContent(event) {
|
|
474
448
|
if (!event) return {};
|
|
475
449
|
try {
|
|
@@ -495,33 +469,10 @@ function readNonEmptyString(content, key) {
|
|
|
495
469
|
}
|
|
496
470
|
async function queryCurrentProfiles(params) {
|
|
497
471
|
params.signal?.throwIfAborted();
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
receivedEose: false
|
|
503
|
-
};
|
|
504
|
-
const timeout = setTimeout(() => {
|
|
505
|
-
const error = /* @__PURE__ */ new Error("Timed out loading current Buzz profile");
|
|
506
|
-
finish(error);
|
|
507
|
-
params.onTimeout?.(error);
|
|
508
|
-
params.relay.close();
|
|
509
|
-
}, PROFILE_QUERY_TIMEOUT_MS);
|
|
510
|
-
const finish = (error) => {
|
|
511
|
-
if (state.settled) return;
|
|
512
|
-
state.settled = true;
|
|
513
|
-
clearTimeout(timeout);
|
|
514
|
-
params.signal?.removeEventListener("abort", onAbort);
|
|
515
|
-
if (state.receivedEose) state.subscription?.close("profile query complete");
|
|
516
|
-
if (error !== void 0) {
|
|
517
|
-
reject(error instanceof Error ? error : new Error("Buzz profile query failed", { cause: error }));
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
resolve(latestByKind);
|
|
521
|
-
};
|
|
522
|
-
const onAbort = () => finish(params.signal?.reason ?? /* @__PURE__ */ new Error("Buzz profile query aborted"));
|
|
523
|
-
params.signal?.addEventListener("abort", onAbort, { once: true });
|
|
524
|
-
state.subscription = openBuzzRelaySubscription(params.relay, [{
|
|
472
|
+
const latestByKind = /* @__PURE__ */ new Map();
|
|
473
|
+
return await queryBuzzRelaySnapshot({
|
|
474
|
+
relay: params.relay,
|
|
475
|
+
filters: [{
|
|
525
476
|
kinds: [PROFILE_KIND],
|
|
526
477
|
authors: [params.publicKey],
|
|
527
478
|
limit: 1
|
|
@@ -529,21 +480,19 @@ async function queryCurrentProfiles(params) {
|
|
|
529
480
|
kinds: [AGENT_PROFILE_KIND],
|
|
530
481
|
authors: [params.publicKey],
|
|
531
482
|
limit: 1
|
|
532
|
-
}],
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
});
|
|
546
|
-
if (state.settled && state.receivedEose) state.subscription.close("profile query complete");
|
|
483
|
+
}],
|
|
484
|
+
signal: params.signal,
|
|
485
|
+
timeoutMessage: "Timed out loading current Buzz profile",
|
|
486
|
+
abortMessage: "Buzz profile query aborted",
|
|
487
|
+
failureMessage: "Buzz profile query failed",
|
|
488
|
+
closeReason: "profile query complete",
|
|
489
|
+
closeMessage: (reason) => `Buzz profile query closed: ${reason}`,
|
|
490
|
+
onEvent: (event) => {
|
|
491
|
+
const current = latestByKind.get(event.kind);
|
|
492
|
+
if (!current || event.created_at > current.created_at) latestByKind.set(event.kind, event);
|
|
493
|
+
},
|
|
494
|
+
result: () => latestByKind,
|
|
495
|
+
onTimeout: params.onTimeout
|
|
547
496
|
});
|
|
548
497
|
}
|
|
549
498
|
function buildProfileEvent(params) {
|
|
@@ -729,56 +678,29 @@ function resolveBuzzRoomHistoryLimit(roomCount) {
|
|
|
729
678
|
//#region extensions/buzz/src/room-membership-query.ts
|
|
730
679
|
const RELAY_QUERY_EVENT_LIMIT = 1e3;
|
|
731
680
|
const MEMBERSHIP_QUERY_COMPLETE_REASON = "membership snapshot loaded";
|
|
732
|
-
const MEMBERSHIP_QUERY_TIMEOUT_MS = 1e4;
|
|
733
681
|
async function queryBuzzRoomMembershipBatch(params) {
|
|
734
682
|
const configuredRooms = new Set(params.channelIds);
|
|
735
683
|
const memberships = /* @__PURE__ */ new Map();
|
|
736
|
-
return await
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
params.
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
subscriptionRef.current = openBuzzRelaySubscription(params.relay, [{
|
|
757
|
-
kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
|
|
758
|
-
authors: [params.relayPublicKey],
|
|
759
|
-
"#d": params.channelIds,
|
|
760
|
-
limit: params.channelIds.length
|
|
761
|
-
}], {
|
|
762
|
-
onevent: (event) => {
|
|
763
|
-
const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
|
|
764
|
-
if (!membership || !configuredRooms.has(membership.roomId) || !isNewerBuzzRoomMembership(membership, memberships.get(membership.roomId))) return;
|
|
765
|
-
memberships.set(membership.roomId, membership);
|
|
766
|
-
},
|
|
767
|
-
oneose: () => {
|
|
768
|
-
receivedEose = true;
|
|
769
|
-
if (settled) subscriptionRef.current?.close(MEMBERSHIP_QUERY_COMPLETE_REASON);
|
|
770
|
-
else finish();
|
|
771
|
-
},
|
|
772
|
-
onclose: (reason) => {
|
|
773
|
-
if (reason !== MEMBERSHIP_QUERY_COMPLETE_REASON) finish(/* @__PURE__ */ new Error(`Buzz room membership query closed: ${reason}`));
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
} catch (error) {
|
|
777
|
-
finish(error);
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
if (settled && receivedEose) subscriptionRef.current.close(MEMBERSHIP_QUERY_COMPLETE_REASON);
|
|
781
|
-
if (params.signal?.aborted) onAbort();
|
|
684
|
+
return await queryBuzzRelaySnapshot({
|
|
685
|
+
relay: params.relay,
|
|
686
|
+
filters: [{
|
|
687
|
+
kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
|
|
688
|
+
authors: [params.relayPublicKey],
|
|
689
|
+
"#d": params.channelIds,
|
|
690
|
+
limit: params.channelIds.length
|
|
691
|
+
}],
|
|
692
|
+
signal: params.signal,
|
|
693
|
+
timeoutMessage: "Timed out loading Buzz room membership snapshot",
|
|
694
|
+
abortMessage: "Buzz room membership query aborted",
|
|
695
|
+
failureMessage: "Buzz room membership query failed",
|
|
696
|
+
closeReason: MEMBERSHIP_QUERY_COMPLETE_REASON,
|
|
697
|
+
closeMessage: (reason) => `Buzz room membership query closed: ${reason}`,
|
|
698
|
+
onEvent: (event) => {
|
|
699
|
+
const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
|
|
700
|
+
if (membership && configuredRooms.has(membership.roomId) && isNewerBuzzRoomMembership(membership, memberships.get(membership.roomId))) memberships.set(membership.roomId, membership);
|
|
701
|
+
},
|
|
702
|
+
result: () => memberships,
|
|
703
|
+
checkAbortAfterSubscribe: true
|
|
782
704
|
});
|
|
783
705
|
}
|
|
784
706
|
async function queryBuzzRoomMemberships(params) {
|
|
@@ -794,62 +716,35 @@ async function queryBuzzRoomMemberships(params) {
|
|
|
794
716
|
}
|
|
795
717
|
//#endregion
|
|
796
718
|
//#region extensions/buzz/src/history-catchup.ts
|
|
797
|
-
const HISTORY_PAGE_TIMEOUT_MS = 1e4;
|
|
798
719
|
const HISTORY_PAGE_COMPLETE_REASON = "buzz room history page loaded";
|
|
799
720
|
async function queryBuzzRoomHistoryPage(params) {
|
|
800
721
|
const events = [];
|
|
801
722
|
let overLimit = false;
|
|
802
|
-
return await
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
params.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
kinds: [...BUZZ_INBOUND_MESSAGE_KINDS],
|
|
828
|
-
"#h": [params.channelId],
|
|
829
|
-
since: params.since,
|
|
830
|
-
until: params.until,
|
|
831
|
-
...params.requestLimit === void 0 ? {} : { limit: params.requestLimit }
|
|
832
|
-
}], {
|
|
833
|
-
onevent: (event) => {
|
|
834
|
-
if (params.skipEventIds?.has(event.id)) return;
|
|
835
|
-
if (events.length < params.maxEvents) events.push(event);
|
|
836
|
-
else overLimit = true;
|
|
837
|
-
},
|
|
838
|
-
oneose: () => {
|
|
839
|
-
receivedEose = true;
|
|
840
|
-
if (settled) subscriptionRef.current?.close(HISTORY_PAGE_COMPLETE_REASON);
|
|
841
|
-
else finish();
|
|
842
|
-
},
|
|
843
|
-
onclose: (reason) => {
|
|
844
|
-
if (reason !== HISTORY_PAGE_COMPLETE_REASON) finish(/* @__PURE__ */ new Error(`Buzz room history query closed for ${params.channelId}: ${reason}`));
|
|
845
|
-
}
|
|
846
|
-
});
|
|
847
|
-
} catch (error) {
|
|
848
|
-
finish(error);
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
if (settled && receivedEose) subscriptionRef.current.close(HISTORY_PAGE_COMPLETE_REASON);
|
|
852
|
-
if (params.signal?.aborted) onAbort();
|
|
723
|
+
return await queryBuzzRelaySnapshot({
|
|
724
|
+
relay: params.relay,
|
|
725
|
+
filters: [{
|
|
726
|
+
kinds: [...BUZZ_INBOUND_MESSAGE_KINDS],
|
|
727
|
+
"#h": [params.channelId],
|
|
728
|
+
since: params.since,
|
|
729
|
+
until: params.until,
|
|
730
|
+
...params.requestLimit === void 0 ? {} : { limit: params.requestLimit }
|
|
731
|
+
}],
|
|
732
|
+
signal: params.signal,
|
|
733
|
+
timeoutMessage: `Timed out loading Buzz room history for ${params.channelId}`,
|
|
734
|
+
abortMessage: "Buzz room history query aborted",
|
|
735
|
+
failureMessage: "Buzz room history query failed",
|
|
736
|
+
closeReason: HISTORY_PAGE_COMPLETE_REASON,
|
|
737
|
+
closeMessage: (reason) => `Buzz room history query closed for ${params.channelId}: ${reason}`,
|
|
738
|
+
onEvent: (event) => {
|
|
739
|
+
if (params.skipEventIds?.has(event.id)) return;
|
|
740
|
+
if (events.length < params.maxEvents) events.push(event);
|
|
741
|
+
else overLimit = true;
|
|
742
|
+
},
|
|
743
|
+
result: () => ({
|
|
744
|
+
events,
|
|
745
|
+
overLimit
|
|
746
|
+
}),
|
|
747
|
+
checkAbortAfterSubscribe: true
|
|
853
748
|
});
|
|
854
749
|
}
|
|
855
750
|
async function drainBuzzRoomHistoryRange(params) {
|
|
@@ -1023,6 +918,14 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1023
918
|
params.onFatalError?.(error instanceof Error ? error : new Error(String(error)));
|
|
1024
919
|
params.relay.close();
|
|
1025
920
|
};
|
|
921
|
+
const replaceMembership = (membership) => {
|
|
922
|
+
if (membership.roles.get(params.botPublicKey) !== "bot" || !membership.members.has(params.botPublicKey)) {
|
|
923
|
+
blockedRooms.add(membership.roomId);
|
|
924
|
+
throw new Error(`Buzz bot no longer has the Bot role in room ${membership.roomId}`);
|
|
925
|
+
}
|
|
926
|
+
memberships.set(membership.roomId, membership);
|
|
927
|
+
params.onMembershipsChanged?.(effectiveMemberships());
|
|
928
|
+
};
|
|
1026
929
|
const queryMembership = (channelId) => {
|
|
1027
930
|
const query = membershipQueryTail.then(async () => (await queryBuzzRoomMemberships({
|
|
1028
931
|
relay: params.relay,
|
|
@@ -1034,8 +937,6 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1034
937
|
return query;
|
|
1035
938
|
};
|
|
1036
939
|
const refreshMembership = async (channelId, state) => {
|
|
1037
|
-
const baseline = memberships.get(channelId);
|
|
1038
|
-
if (!baseline) throw new Error(`Missing Buzz room membership for ${channelId}`);
|
|
1039
940
|
for (const delayMs of MEMBERSHIP_REFRESH_DELAYS_MS) {
|
|
1040
941
|
const generation = state.generation;
|
|
1041
942
|
state.lastAttemptedGeneration = generation;
|
|
@@ -1049,19 +950,14 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1049
950
|
continue;
|
|
1050
951
|
}
|
|
1051
952
|
if (state.generation !== generation || !refreshed) continue;
|
|
953
|
+
const current = memberships.get(channelId);
|
|
954
|
+
if (current && isNewerBuzzRoomMembership(current, refreshed)) refreshed = current;
|
|
1052
955
|
const pending = pendingMemberships.get(channelId);
|
|
1053
|
-
|
|
1054
|
-
const botMembershipChanged = pending?.has(params.botPublicKey) === true;
|
|
1055
|
-
if (!pendingMatches || botMembershipChanged && !isNewerBuzzRoomMembership(refreshed, baseline)) continue;
|
|
1056
|
-
if (refreshed.roles.get(params.botPublicKey) !== "bot" || !refreshed.members.has(params.botPublicKey)) {
|
|
1057
|
-
blockedRooms.add(channelId);
|
|
1058
|
-
throw new Error(`Buzz bot no longer has the Bot role in room ${channelId}`);
|
|
1059
|
-
}
|
|
1060
|
-
memberships.set(channelId, refreshed);
|
|
956
|
+
if (!(!pending || [...pending].every(([publicKey, expected]) => refreshed.members.has(publicKey) === (expected === "present")))) continue;
|
|
1061
957
|
pendingMemberships.delete(channelId);
|
|
1062
958
|
deniedMembers.delete(channelId);
|
|
1063
959
|
blockedRooms.delete(channelId);
|
|
1064
|
-
|
|
960
|
+
replaceMembership(refreshed);
|
|
1065
961
|
return;
|
|
1066
962
|
}
|
|
1067
963
|
if (state.generation !== state.lastAttemptedGeneration) return;
|
|
@@ -1112,6 +1008,15 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1112
1008
|
return refreshMembershipOnce(channelId);
|
|
1113
1009
|
};
|
|
1114
1010
|
const handleRoomEvent = (event, reservation) => {
|
|
1011
|
+
if (event.kind === 39002) {
|
|
1012
|
+
const membership = parseBuzzRoomMembershipEvent(event, params.relayPublicKey);
|
|
1013
|
+
if (membership && memberships.has(membership.roomId) && isNewerBuzzRoomMembership(membership, memberships.get(membership.roomId))) try {
|
|
1014
|
+
replaceMembership(membership);
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
reportSystemEventError(error);
|
|
1017
|
+
}
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1115
1020
|
if (isBuzzInboundMessageKind(event.kind)) {
|
|
1116
1021
|
params.onMessageEvent(event, isMember, reservation);
|
|
1117
1022
|
return;
|
|
@@ -1131,38 +1036,50 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1131
1036
|
}, MEMBERSHIP_READY_TIMEOUT_MS);
|
|
1132
1037
|
const subscriptions = [];
|
|
1133
1038
|
try {
|
|
1134
|
-
for (const channelId of params.channelIds)
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
oldest: event.created_at
|
|
1153
|
-
});
|
|
1039
|
+
for (const channelId of params.channelIds) {
|
|
1040
|
+
const filters = [
|
|
1041
|
+
{
|
|
1042
|
+
kinds: [BUZZ_ROOM_SYSTEM_KIND, BUZZ_ROOM_METADATA_EDIT_KIND],
|
|
1043
|
+
"#h": [channelId],
|
|
1044
|
+
since: params.since
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
kinds: [BUZZ_ROOM_MEMBERSHIP_KIND],
|
|
1048
|
+
authors: [params.relayPublicKey],
|
|
1049
|
+
"#d": [channelId],
|
|
1050
|
+
limit: 1
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
kinds: [...BUZZ_INBOUND_MESSAGE_KINDS],
|
|
1054
|
+
"#h": [channelId],
|
|
1055
|
+
since: params.messageSince(channelId),
|
|
1056
|
+
limit: params.messageLimit
|
|
1154
1057
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1058
|
+
];
|
|
1059
|
+
subscriptions.push(openBuzzRelaySubscription(params.relay, filters, {
|
|
1060
|
+
onevent: (event) => {
|
|
1061
|
+
if (!historicalRooms.has(channelId) && isBuzzInboundMessageKind(event.kind)) {
|
|
1062
|
+
const page = historyPages.get(channelId);
|
|
1063
|
+
if (page) {
|
|
1064
|
+
page.count += 1;
|
|
1065
|
+
page.oldest = Math.min(page.oldest, event.created_at);
|
|
1066
|
+
} else historyPages.set(channelId, {
|
|
1067
|
+
count: 1,
|
|
1068
|
+
oldest: event.created_at
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
handleRoomEvent(event);
|
|
1072
|
+
},
|
|
1073
|
+
oneose: () => {
|
|
1074
|
+
historicalRooms.add(channelId);
|
|
1075
|
+
if (historicalRooms.size === params.channelIds.length) resolveHistorical?.();
|
|
1076
|
+
},
|
|
1077
|
+
onclose: (reason) => {
|
|
1078
|
+
if (!historicalRooms.has(channelId)) rejectHistorical?.(/* @__PURE__ */ new Error(`Buzz membership subscription closed for ${channelId}: ${reason}`));
|
|
1079
|
+
else if (reason !== "shutdown" && reason !== "relay connection closed by us" && reason !== MEMBERSHIP_TRACKER_SETUP_CLOSE_REASON && !params.signal?.aborted) params.onFatalError?.(/* @__PURE__ */ new Error(`Buzz membership subscription closed for ${channelId}: ${reason}`));
|
|
1080
|
+
}
|
|
1081
|
+
}, filters.map((filter) => Object.assign({}, filter, { "#h": [channelId] }))));
|
|
1082
|
+
}
|
|
1166
1083
|
await historicalReady;
|
|
1167
1084
|
} catch (error) {
|
|
1168
1085
|
if (params.relay.connected) {
|
|
@@ -1183,7 +1100,7 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1183
1100
|
if (await catchUpBuzzRoomHistory({
|
|
1184
1101
|
relay: params.relay,
|
|
1185
1102
|
channelId,
|
|
1186
|
-
since: params.messageSince,
|
|
1103
|
+
since: params.messageSince(channelId),
|
|
1187
1104
|
until: page.oldest,
|
|
1188
1105
|
limit: params.messageLimit,
|
|
1189
1106
|
reserveCapacity: params.reserveDispatchCapacity,
|
|
@@ -1204,10 +1121,11 @@ async function createBuzzRoomMembershipTracker(params) {
|
|
|
1204
1121
|
const BUZZ_RELAY_MAX_SUBSCRIPTIONS = 1024;
|
|
1205
1122
|
const BUZZ_RELAY_NON_ROOM_PROFILE_SUBSCRIPTION_RESERVE = 4;
|
|
1206
1123
|
const BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS = 10;
|
|
1124
|
+
const BUZZ_MAX_CONFIGURED_ROOMS = BUZZ_RELAY_MAX_SUBSCRIPTIONS - BUZZ_RELAY_NON_ROOM_PROFILE_SUBSCRIPTION_RESERVE;
|
|
1207
1125
|
function resolveBuzzSubscriptionBudget(roomCount) {
|
|
1208
1126
|
if (!Number.isSafeInteger(roomCount) || roomCount < 0) throw new Error("Buzz configured room count must be a non-negative integer");
|
|
1209
1127
|
const availableProfileSubscriptions = BUZZ_RELAY_MAX_SUBSCRIPTIONS - BUZZ_RELAY_NON_ROOM_PROFILE_SUBSCRIPTION_RESERVE - roomCount;
|
|
1210
|
-
if (availableProfileSubscriptions < 0) throw new Error(`Buzz supports at most ${
|
|
1128
|
+
if (availableProfileSubscriptions < 0) throw new Error(`Buzz supports at most ${BUZZ_MAX_CONFIGURED_ROOMS} configured rooms per account`);
|
|
1211
1129
|
return { profileLimit: Math.min(BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS, availableProfileSubscriptions) * 200 };
|
|
1212
1130
|
}
|
|
1213
1131
|
//#endregion
|
|
@@ -1253,9 +1171,11 @@ function startBuzzPresenceHeartbeat(params) {
|
|
|
1253
1171
|
await params.relay.publish(buildBuzzPresenceEvent(params.secretKey));
|
|
1254
1172
|
errorReported = false;
|
|
1255
1173
|
} catch (error) {
|
|
1256
|
-
|
|
1174
|
+
const failure = error instanceof Error ? error : new Error("Buzz presence heartbeat failed", { cause: error });
|
|
1175
|
+
if (!stopped && failure.message === "publish timed out") params.onFatalError(failure);
|
|
1176
|
+
else if (!stopped && !errorReported) {
|
|
1257
1177
|
errorReported = true;
|
|
1258
|
-
params.onError?.(
|
|
1178
|
+
params.onError?.(failure);
|
|
1259
1179
|
}
|
|
1260
1180
|
} finally {
|
|
1261
1181
|
publishInFlight = false;
|
|
@@ -1341,10 +1261,9 @@ async function startBuzzBus(options) {
|
|
|
1341
1261
|
const sessionStartedAt = Math.floor(Date.now() / 1e3);
|
|
1342
1262
|
const lifecycleAbort = new AbortController();
|
|
1343
1263
|
const signal = options.signal ? AbortSignal.any([options.signal, lifecycleAbort.signal]) : lifecycleAbort.signal;
|
|
1344
|
-
let fatalErrorReported = false;
|
|
1345
1264
|
const reportFatalError = (error) => {
|
|
1346
|
-
if (signal.aborted
|
|
1347
|
-
|
|
1265
|
+
if (signal.aborted) return;
|
|
1266
|
+
lifecycleAbort.abort(error);
|
|
1348
1267
|
options.onFatalError?.(error);
|
|
1349
1268
|
};
|
|
1350
1269
|
const replayGuard = createChannelReplayGuard({
|
|
@@ -1454,7 +1373,7 @@ async function startBuzzBus(options) {
|
|
|
1454
1373
|
channelIds: activeChannelIds,
|
|
1455
1374
|
botPublicKey: publicKey,
|
|
1456
1375
|
since: sessionStartedAt,
|
|
1457
|
-
messageSince: options.since ?? sessionStartedAt,
|
|
1376
|
+
messageSince: (channelId) => options.since?.(channelId) ?? sessionStartedAt,
|
|
1458
1377
|
messageLimit: resolveBuzzRoomHistoryLimit(activeChannelIds.length),
|
|
1459
1378
|
reserveDispatchCapacity: (slots) => dispatchQueue.reserveCapacity(slots),
|
|
1460
1379
|
onHistoryError: options.onHistoryError,
|
|
@@ -1464,7 +1383,12 @@ async function startBuzzBus(options) {
|
|
|
1464
1383
|
if (!message || !isMember(message.channelId, event.pubkey)) return;
|
|
1465
1384
|
if ((reservation ?? dispatchQueue).enqueue(async () => {
|
|
1466
1385
|
await replayGuard.processGuarded(event, async () => {
|
|
1467
|
-
|
|
1386
|
+
const assertCurrent = () => {
|
|
1387
|
+
signal.throwIfAborted();
|
|
1388
|
+
if (!isMember(message.channelId, event.pubkey)) throw new Error("Buzz sender is no longer a room member");
|
|
1389
|
+
};
|
|
1390
|
+
assertCurrent();
|
|
1391
|
+
await options.onMessage(message, bus, signal, assertCurrent);
|
|
1468
1392
|
});
|
|
1469
1393
|
}) !== "overflow") return;
|
|
1470
1394
|
if (reservation) {
|
|
@@ -1491,7 +1415,8 @@ async function startBuzzBus(options) {
|
|
|
1491
1415
|
stopPresenceHeartbeat = startBuzzPresenceHeartbeat({
|
|
1492
1416
|
relay,
|
|
1493
1417
|
secretKey,
|
|
1494
|
-
onError: options.onPresenceError
|
|
1418
|
+
onError: options.onPresenceError,
|
|
1419
|
+
onFatalError: reportFatalError
|
|
1495
1420
|
});
|
|
1496
1421
|
if (options.profileName?.trim()) syncBuzzProfile({
|
|
1497
1422
|
relay,
|
|
@@ -1569,8 +1494,8 @@ async function handleBuzzInbound(params) {
|
|
|
1569
1494
|
canDetectMention: true,
|
|
1570
1495
|
wasMentioned
|
|
1571
1496
|
},
|
|
1572
|
-
groupPolicy: account.config.groupPolicy,
|
|
1573
|
-
groupAllowFrom: account.config.groupAllowFrom,
|
|
1497
|
+
groupPolicy: groupConfig?.groupPolicy ?? account.config.groupPolicy,
|
|
1498
|
+
groupAllowFrom: groupConfig?.groupAllowFrom ?? account.config.groupAllowFrom,
|
|
1574
1499
|
policy: { activation: {
|
|
1575
1500
|
requireMention: groupConfig?.requireMention ?? true,
|
|
1576
1501
|
allowTextCommands: true
|
|
@@ -1580,6 +1505,7 @@ async function handleBuzzInbound(params) {
|
|
|
1580
1505
|
hasControlCommand
|
|
1581
1506
|
} : void 0
|
|
1582
1507
|
});
|
|
1508
|
+
params.assertCurrent();
|
|
1583
1509
|
if (access.ingress.admission !== "dispatch") return;
|
|
1584
1510
|
const senderName = bus.directory.resolveSenderName(message.senderPubkey);
|
|
1585
1511
|
const roomName = bus.directory.resolveRoomName(channelId);
|
|
@@ -1639,6 +1565,11 @@ async function handleBuzzInbound(params) {
|
|
|
1639
1565
|
BuzzEventKind: message.kind
|
|
1640
1566
|
}
|
|
1641
1567
|
});
|
|
1568
|
+
const replyTarget = {
|
|
1569
|
+
channelId,
|
|
1570
|
+
threadId: message.threadId,
|
|
1571
|
+
replyToId: message.threadId ?? message.id
|
|
1572
|
+
};
|
|
1642
1573
|
await runtime.channel.inbound.dispatch({
|
|
1643
1574
|
cfg,
|
|
1644
1575
|
channel: "buzz",
|
|
@@ -1654,10 +1585,8 @@ async function handleBuzzInbound(params) {
|
|
|
1654
1585
|
const text = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
|
|
1655
1586
|
if (!text.trim()) return;
|
|
1656
1587
|
await bus.sendText({
|
|
1657
|
-
|
|
1658
|
-
text
|
|
1659
|
-
threadId: message.threadId,
|
|
1660
|
-
replyToId: message.id
|
|
1588
|
+
...replyTarget,
|
|
1589
|
+
text
|
|
1661
1590
|
});
|
|
1662
1591
|
},
|
|
1663
1592
|
onError: (error) => {
|
|
@@ -1667,11 +1596,7 @@ async function handleBuzzInbound(params) {
|
|
|
1667
1596
|
replyOptions: { abortSignal: signal },
|
|
1668
1597
|
replyPipeline: { typing: {
|
|
1669
1598
|
start: async () => {
|
|
1670
|
-
await bus.sendTyping(
|
|
1671
|
-
channelId,
|
|
1672
|
-
threadId: message.threadId,
|
|
1673
|
-
replyToId: message.id
|
|
1674
|
-
});
|
|
1599
|
+
await bus.sendTyping(replyTarget);
|
|
1675
1600
|
},
|
|
1676
1601
|
keepaliveIntervalMs: 3e3,
|
|
1677
1602
|
onStartError: (error) => {
|
|
@@ -1684,6 +1609,38 @@ async function handleBuzzInbound(params) {
|
|
|
1684
1609
|
});
|
|
1685
1610
|
}
|
|
1686
1611
|
//#endregion
|
|
1612
|
+
//#region extensions/buzz/src/recovery-watermark.ts
|
|
1613
|
+
function roomCursorKey(channelId) {
|
|
1614
|
+
return `room:${channelId}`;
|
|
1615
|
+
}
|
|
1616
|
+
function openBuzzRecoveryWatermarkStore(params) {
|
|
1617
|
+
const accountScope = createHash("sha256").update(params.accountId).digest("hex").slice(0, 16);
|
|
1618
|
+
return getBuzzRuntime().state.openKeyedStore({
|
|
1619
|
+
namespace: `buzz.recovery-watermark-${accountScope}`,
|
|
1620
|
+
maxEntries: BUZZ_MAX_CONFIGURED_ROOMS,
|
|
1621
|
+
overflowPolicy: "reject-new"
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
async function resolveBuzzRecoverySince(params) {
|
|
1625
|
+
const { store, channelIds, nowSeconds, lookbackSeconds } = params;
|
|
1626
|
+
const configuredKeys = new Set(channelIds.map(roomCursorKey));
|
|
1627
|
+
for (const { key } of await store.entries()) if (key.startsWith("room:") && !configuredKeys.has(key)) await store.delete(key);
|
|
1628
|
+
const sinceByRoom = /* @__PURE__ */ new Map();
|
|
1629
|
+
const retentionFloor = nowSeconds - lookbackSeconds;
|
|
1630
|
+
for (const channelId of channelIds) {
|
|
1631
|
+
const key = roomCursorKey(channelId);
|
|
1632
|
+
const activation = await store.lookup(key);
|
|
1633
|
+
if (activation === void 0) {
|
|
1634
|
+
await store.register(key, { seconds: nowSeconds });
|
|
1635
|
+
sinceByRoom.set(channelId, nowSeconds);
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (typeof activation.seconds !== "number" || !Number.isFinite(activation.seconds)) throw new Error(`Invalid Buzz recovery watermark for room ${channelId}`);
|
|
1639
|
+
sinceByRoom.set(channelId, Math.min(Math.max(activation.seconds, retentionFloor), nowSeconds));
|
|
1640
|
+
}
|
|
1641
|
+
return sinceByRoom;
|
|
1642
|
+
}
|
|
1643
|
+
//#endregion
|
|
1687
1644
|
//#region extensions/buzz/src/gateway.ts
|
|
1688
1645
|
const activeBuses = /* @__PURE__ */ new Map();
|
|
1689
1646
|
const RECONNECT_BACKOFF = {
|
|
@@ -1727,7 +1684,7 @@ async function startBuzzGatewayAccount(ctx) {
|
|
|
1727
1684
|
account,
|
|
1728
1685
|
channelIds
|
|
1729
1686
|
});
|
|
1730
|
-
|
|
1687
|
+
const watermarkStore = openBuzzRecoveryWatermarkStore({ accountId: account.accountId });
|
|
1731
1688
|
let reconnectAttempt = 0;
|
|
1732
1689
|
while (!ctx.abortSignal.aborted) {
|
|
1733
1690
|
let bus;
|
|
@@ -1738,8 +1695,13 @@ async function startBuzzGatewayAccount(ctx) {
|
|
|
1738
1695
|
reportBusFailure = resolve;
|
|
1739
1696
|
});
|
|
1740
1697
|
try {
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1698
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
1699
|
+
const sinceByRoom = await resolveBuzzRecoverySince({
|
|
1700
|
+
store: watermarkStore,
|
|
1701
|
+
channelIds,
|
|
1702
|
+
nowSeconds,
|
|
1703
|
+
lookbackSeconds: RECONNECT_LOOKBACK_SECONDS
|
|
1704
|
+
});
|
|
1743
1705
|
bus = await startBuzzBus({
|
|
1744
1706
|
accountId: account.accountId,
|
|
1745
1707
|
relayUrl: account.relayUrl,
|
|
@@ -1747,9 +1709,9 @@ async function startBuzzGatewayAccount(ctx) {
|
|
|
1747
1709
|
authTag: account.authTag,
|
|
1748
1710
|
profileName,
|
|
1749
1711
|
channelIds,
|
|
1750
|
-
since:
|
|
1712
|
+
since: (channelId) => sinceByRoom.get(channelId) ?? nowSeconds,
|
|
1751
1713
|
signal: ctx.abortSignal,
|
|
1752
|
-
onMessage: async (message, sessionBus, signal) => {
|
|
1714
|
+
onMessage: async (message, sessionBus, signal, assertCurrent) => {
|
|
1753
1715
|
if (!isConfiguredBuzzChannel(configuredChannelIds, message.channelId)) return;
|
|
1754
1716
|
await handleBuzzInbound({
|
|
1755
1717
|
account,
|
|
@@ -1757,6 +1719,7 @@ async function startBuzzGatewayAccount(ctx) {
|
|
|
1757
1719
|
bus: sessionBus,
|
|
1758
1720
|
message,
|
|
1759
1721
|
signal,
|
|
1722
|
+
assertCurrent,
|
|
1760
1723
|
buildContext
|
|
1761
1724
|
});
|
|
1762
1725
|
},
|