mirror of
https://github.com/block/buzz.git
synced 2026-11-12 04:12:21 +00:00
desktop: hydrate managed agents before Welcome provisioning
Make the owner-scoped persona sync backfill awaitable and run it before Welcome onboarding inspects local managed agents. This lets a second installation reconstruct relay-canonical identities before deciding whether to mint replacements. Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,23 @@ const PERSONA_SYNC_KINDS = [
|
||||
KIND_DELETION,
|
||||
];
|
||||
|
||||
export async function hydratePersonaSync(
|
||||
pubkey: string,
|
||||
relayUrl: string,
|
||||
onCancelled: () => boolean = () => false,
|
||||
): Promise<void> {
|
||||
const events = await relayClient.fetchEvents({
|
||||
kinds: PERSONA_SYNC_KINDS,
|
||||
authors: [pubkey],
|
||||
limit: 500,
|
||||
});
|
||||
for (const event of events) {
|
||||
if (onCancelled()) return;
|
||||
if (event.pubkey !== pubkey) continue;
|
||||
await reconcileInboundPersonaEvent(JSON.stringify(event), relayUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the persona/team/agent/deletion sync for `pubkey` on `relayUrl`:
|
||||
// one-shot backfill of existing heads + tombstones, then a live subscription.
|
||||
// Returns a disposer that closes the live subscription. Extracted from the hook
|
||||
@@ -48,15 +65,9 @@ export function startPersonaSync(
|
||||
|
||||
// One-shot backfill of existing heads + tombstones (closes the fresh-start
|
||||
// gap that live-only subscription + reconnect-replay cannot recover).
|
||||
void relayClient
|
||||
.fetchEvents({ kinds: PERSONA_SYNC_KINDS, authors: [pubkey], limit: 500 })
|
||||
.then((events) => {
|
||||
if (onCancelled()) return;
|
||||
for (const event of events) reconcile(event);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn("[usePersonaSync] backfill failed:", error);
|
||||
});
|
||||
void hydratePersonaSync(pubkey, relayUrl, onCancelled).catch((error) => {
|
||||
console.warn("[usePersonaSync] backfill failed:", error);
|
||||
});
|
||||
|
||||
let unsub: (() => Promise<void>) | null = null;
|
||||
void relayClient
|
||||
|
||||
@@ -4,6 +4,7 @@ import test from "node:test";
|
||||
import {
|
||||
activateWelcomeTeamPersonasSequentially,
|
||||
buildWelcomeStarterCreateInput,
|
||||
hydrateWelcomeTeamAgentsBeforeProvision,
|
||||
LEGACY_WELCOME_GUIDE_SYSTEM_PROMPT,
|
||||
pickWelcomeGuideAgent,
|
||||
pickWelcomeGuideAgentForRelay,
|
||||
@@ -15,6 +16,30 @@ import {
|
||||
WELCOME_TEAM_STARTERS,
|
||||
} from "./welcomeGuide.ts";
|
||||
|
||||
test("Welcome provisioning awaits relay hydration before inspecting agents", async () => {
|
||||
const calls = [];
|
||||
await hydrateWelcomeTeamAgentsBeforeProvision(
|
||||
"wss://relay.example///",
|
||||
async () => {
|
||||
calls.push("identity");
|
||||
return { pubkey: PUB_A };
|
||||
},
|
||||
async (pubkey, relayUrl) => {
|
||||
calls.push(`hydrate:${pubkey}:${relayUrl}`);
|
||||
},
|
||||
);
|
||||
assert.deepEqual(calls, ["identity", `hydrate:${PUB_A}:wss://relay.example`]);
|
||||
});
|
||||
|
||||
test("Welcome provisioning skips hydration without a relay scope", async () => {
|
||||
let called = false;
|
||||
await hydrateWelcomeTeamAgentsBeforeProvision(null, async () => {
|
||||
called = true;
|
||||
return { pubkey: PUB_A };
|
||||
});
|
||||
assert.equal(called, false);
|
||||
});
|
||||
|
||||
const PUB_A = "a".repeat(64);
|
||||
const PUB_B = "b".repeat(64);
|
||||
const PUB_C = "c".repeat(64);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { hydratePersonaSync } from "@/features/agents/lib/usePersonaSync";
|
||||
import {
|
||||
buildInstanceInputForDefinition,
|
||||
resolveStartRuntimeForDefinition,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
updateManagedAgent,
|
||||
} from "@/shared/api/tauri";
|
||||
import { getGlobalAgentConfig } from "@/shared/api/tauriGlobalAgentConfig";
|
||||
import { getIdentity } from "@/shared/api/tauriIdentity";
|
||||
import { listPersonas, setPersonaActive } from "@/shared/api/tauriPersonas";
|
||||
import type {
|
||||
AcpRuntime,
|
||||
@@ -53,6 +55,17 @@ function normalizeRelayUrl(relayUrl: string | null | undefined) {
|
||||
return relayUrl?.trim().replace(/\/+$/, "") ?? null;
|
||||
}
|
||||
|
||||
export async function hydrateWelcomeTeamAgentsBeforeProvision(
|
||||
relayUrl: string | null | undefined,
|
||||
resolveIdentity: typeof getIdentity = getIdentity,
|
||||
hydrate: typeof hydratePersonaSync = hydratePersonaSync,
|
||||
): Promise<void> {
|
||||
const normalizedRelay = normalizeRelayUrl(relayUrl);
|
||||
if (!normalizedRelay) return;
|
||||
const identity = await resolveIdentity();
|
||||
await hydrate(identity.pubkey, normalizedRelay);
|
||||
}
|
||||
|
||||
function isAgentScopedToRelay(agent: ManagedAgent, relayUrl?: string | null) {
|
||||
const targetRelayUrl = normalizeRelayUrl(relayUrl);
|
||||
if (!targetRelayUrl) {
|
||||
@@ -269,6 +282,7 @@ async function provisionWelcomeTeam(
|
||||
channelId: string,
|
||||
relayUrl?: string | null,
|
||||
): Promise<WelcomeTeamAgents> {
|
||||
await hydrateWelcomeTeamAgentsBeforeProvision(relayUrl);
|
||||
const existingAgents = await listManagedAgents();
|
||||
await ensureWelcomeTeamPersonasActive();
|
||||
const [personas, runtimeCatalog, globalConfig] = await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user