diff --git a/clients/chrome-extension/background/__tests__/worker-host-browser-result.test.ts b/clients/chrome-extension/background/__tests__/worker-host-browser-result.test.ts index 8ee5b840e3b..02d35215b05 100644 --- a/clients/chrome-extension/background/__tests__/worker-host-browser-result.test.ts +++ b/clients/chrome-extension/background/__tests__/worker-host-browser-result.test.ts @@ -17,6 +17,15 @@ * The function lives in `relay-connection.ts` (rather than `worker.ts`) * so the test can import it directly without dragging in the chrome * service worker module surface. + * + * Related: worker.ts's `connect()` re-reads `vellum.relayMode` from + * chrome.storage.local at entry to close a race where the popup toggles + * the mode radio and immediately clicks Connect before the async + * `chrome.storage.onChanged` listener updates the module-level + * `relayMode` variable. That live-read cannot be unit-tested here + * without dragging in the entire service worker module surface + * (chrome.* globals, bootstrap(), native messaging, etc.), but the + * behaviour is verifiable by reading `connect()` in worker.ts. */ import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; diff --git a/clients/chrome-extension/background/worker.ts b/clients/chrome-extension/background/worker.ts index e9c20657d0d..3095cd325bb 100644 --- a/clients/chrome-extension/background/worker.ts +++ b/clients/chrome-extension/background/worker.ts @@ -287,7 +287,18 @@ function missingTokenMessage(kind: RelayModeKind): string { async function connect(): Promise { if (relayConnection && relayConnection.isOpen()) return; - const mode = await buildRelayModeConfig(relayMode); + // Re-read the live relay mode from storage at connect time. The + // module-level `relayMode` variable is only refreshed asynchronously + // via chrome.storage.onChanged, so trusting it races against a popup + // that toggles the radio and immediately clicks Connect. Reading from + // storage here makes the connect flow deterministic. + // + // The module-level `relayMode` is still updated to match so other code + // paths (status queries, disconnect, result routing) stay consistent + // with the mode we're about to connect with. + const liveMode = await loadRelayMode(); + relayMode = liveMode; + const mode = await buildRelayModeConfig(liveMode); if (!mode.token) { const msg = missingTokenMessage(mode.kind); console.warn(`[vellum-relay] ${msg}`);