diff --git a/desktop/src/features/local-archive/archiveSyncManager.test.mjs b/desktop/src/features/local-archive/archiveSyncManager.test.mjs index a7feaac8e..17d1a8155 100644 --- a/desktop/src/features/local-archive/archiveSyncManager.test.mjs +++ b/desktop/src/features/local-archive/archiveSyncManager.test.mjs @@ -425,6 +425,43 @@ test("manager_builds_correct_filter_for_owner_p", async () => { mgr.destroy(); }); +test("manager_metric_kill_switch_filters_only_kind_44200", async () => { + const relay = makeFakeRelayClient(); + const archive = makeFakeArchive(); + archive.setSubs([ + { + scopeType: "owner_p", + scopeValue: "mixed", + kinds: [24200, 44200, 9], + identityPubkey: "pk", + relayUrl: "wss://r", + createdAt: 0, + }, + { + scopeType: "owner_p", + scopeValue: "metric-only", + kinds: [44200], + identityPubkey: "pk", + relayUrl: "wss://r", + createdAt: 0, + }, + ]); + + const mgr = makeManager(relay, archive, { + disableAgentMetricArchive: true, + }); + await mgr.start(); + await tick(); + + const activeFilters = [...relay.subs.values()] + .filter((entry) => !entry.unsubbed) + .map((entry) => entry.filter); + assert.deepEqual(activeFilters, [ + { kinds: [24200, 9], limit: 0, "#p": ["mixed"] }, + ]); + mgr.destroy(); +}); + test("manager_forwards_events_to_archive_events_on_flush", async () => { const relay = makeFakeRelayClient(); const archive = makeFakeArchive(); diff --git a/desktop/src/features/local-archive/archiveSyncManager.ts b/desktop/src/features/local-archive/archiveSyncManager.ts index 3672989a7..3684afb76 100644 --- a/desktop/src/features/local-archive/archiveSyncManager.ts +++ b/desktop/src/features/local-archive/archiveSyncManager.ts @@ -1,6 +1,7 @@ import { relayClient as defaultRelayClient } from "@/shared/api/relayClient"; import type { RelaySubscriptionFilter } from "@/shared/api/relayClientShared"; import type { RelayEvent } from "@/shared/api/types"; +import { KIND_AGENT_TURN_METRIC } from "@/shared/constants/kinds"; import { archiveEvents as defaultArchiveEvents, listSaveSubscriptions as defaultListSaveSubscriptions, @@ -15,6 +16,8 @@ import { const FLUSH_BATCH_SIZE = 25; const FLUSH_IDLE_MS = 2_000; +const DISABLE_AGENT_METRIC_ARCHIVE = + import.meta.env?.VITE_BUZZ_DISABLE_AGENT_METRIC_ARCHIVE === "1"; // ── Types ───────────────────────────────────────────────────────────────────── @@ -34,6 +37,7 @@ export interface ArchiveSyncDeps { }>, ) => Promise; onSubscriptionChange: (listener: () => void) => () => void; + disableAgentMetricArchive?: boolean; flushBatchSize?: number; flushIdleMs?: number; } @@ -81,8 +85,12 @@ function scopeKey(scopeType: ScopeType, scopeValue: string): string { */ export class ArchiveSyncManager { private readonly deps: Required< - Omit + Omit< + ArchiveSyncDeps, + "disableAgentMetricArchive" | "flushBatchSize" | "flushIdleMs" + > >; + private readonly disableAgentMetricArchive: boolean; private readonly flushBatchSize: number; private readonly flushIdleMs: number; @@ -112,6 +120,8 @@ export class ArchiveSyncManager { onSubscriptionChange: deps?.onSubscriptionChange ?? defaultOnSubscriptionChange, }; + this.disableAgentMetricArchive = + deps?.disableAgentMetricArchive ?? DISABLE_AGENT_METRIC_ARCHIVE; this.flushBatchSize = deps?.flushBatchSize ?? FLUSH_BATCH_SIZE; this.flushIdleMs = deps?.flushIdleMs ?? FLUSH_IDLE_MS; } @@ -202,6 +212,15 @@ export class ArchiveSyncManager { if (this.destroyed) return; + if (this.disableAgentMetricArchive) { + subs = subs.flatMap((sub) => { + const kinds = sub.kinds.filter( + (kind) => kind !== KIND_AGENT_TURN_METRIC, + ); + return kinds.length > 0 ? [{ ...sub, kinds }] : []; + }); + } + // Full keys (scope+kinds) for the current subscription list. const wanted = new Set( subs.map((s) => subKey(s.scopeType, s.scopeValue, s.kinds)), diff --git a/desktop/src/features/local-archive/useAgentMetricArchiveSeed.test.mjs b/desktop/src/features/local-archive/useAgentMetricArchiveSeed.test.mjs index e19b80a7f..f83761ba9 100644 --- a/desktop/src/features/local-archive/useAgentMetricArchiveSeed.test.mjs +++ b/desktop/src/features/local-archive/useAgentMetricArchiveSeed.test.mjs @@ -10,11 +10,16 @@ import test from "node:test"; // ── Fake deps factory ──────────────────────────────────────────────────────── -function makeDeps({ hasExplicitChoice = false, mergeShouldFail = false } = {}) { +function makeDeps({ + hasExplicitChoice = false, + mergeShouldFail = false, + disableAgentMetricArchive = false, +} = {}) { const calls = { mergeSaveSubscriptionKinds: [], setExplicitChoice: [] }; return { calls, + disableAgentMetricArchive, mergeSaveSubscriptionKinds: async (kind) => { if (mergeShouldFail) throw new Error("merge failed"); calls.mergeSaveSubscriptionKinds.push({ kind }); @@ -31,7 +36,7 @@ function makeDeps({ hasExplicitChoice = false, mergeShouldFail = false } = {}) { const KIND_AGENT_TURN_METRIC = 44200; async function runSeed(pubkey, deps) { - if (!pubkey) return; + if (!pubkey || deps.disableAgentMetricArchive) return; if (deps.hasExplicitChoice(pubkey)) return; try { @@ -71,6 +76,14 @@ test("test_default_enabled_persists_explicit_choice_after_seed", async () => { assert.equal(deps.calls.setExplicitChoice[0].enabled, true); }); +test("test_kill_switch_does_not_seed_or_persist_choice", async () => { + const deps = makeDeps({ disableAgentMetricArchive: true }); + await runSeed("pubkey123", deps); + + assert.equal(deps.calls.mergeSaveSubscriptionKinds.length, 0); + assert.equal(deps.calls.setExplicitChoice.length, 0); +}); + test("test_explicit_choice_set_does_not_reseed", async () => { const deps = makeDeps({ hasExplicitChoice: true }); await runSeed("pubkey123", deps); diff --git a/desktop/src/features/local-archive/useAgentMetricArchiveSeed.ts b/desktop/src/features/local-archive/useAgentMetricArchiveSeed.ts index 2cadaa260..41cf81d29 100644 --- a/desktop/src/features/local-archive/useAgentMetricArchiveSeed.ts +++ b/desktop/src/features/local-archive/useAgentMetricArchiveSeed.ts @@ -30,12 +30,15 @@ export interface AgentMetricArchiveSeedDeps { mergeSaveSubscriptionKinds: (kind: number) => Promise; hasExplicitChoice: (pubkey: string) => boolean; setExplicitChoice: (pubkey: string, enabled: boolean) => void; + disableAgentMetricArchive: boolean; } const defaultDeps: AgentMetricArchiveSeedDeps = { mergeSaveSubscriptionKinds, hasExplicitChoice: hasExplicitAgentMetricArchiveChoice, setExplicitChoice: setExplicitAgentMetricArchiveChoice, + disableAgentMetricArchive: + import.meta.env?.VITE_BUZZ_DISABLE_AGENT_METRIC_ARCHIVE === "1", }; /** @@ -51,7 +54,7 @@ export function useAgentMetricArchiveSeed( deps: AgentMetricArchiveSeedDeps = defaultDeps, ): void { React.useEffect(() => { - if (!pubkey) return; + if (!pubkey || deps.disableAgentMetricArchive) return; // Already made an explicit choice for this identity — never re-seed. if (deps.hasExplicitChoice(pubkey)) return;