diff --git a/apps/web/src/speech/browserVoiceInput.ts b/apps/web/src/speech/browserVoiceInput.ts
index 1d04f96bba0e2..95460cc39877a 100644
--- a/apps/web/src/speech/browserVoiceInput.ts
+++ b/apps/web/src/speech/browserVoiceInput.ts
@@ -11,7 +11,7 @@ import {
import type { PreparedConnection } from "@t3tools/client-runtime/connection";
import type { SpeechTranscriptionOptions, SpeechStreamText } from "@t3tools/contracts";
-import { runtime } from "../lib/runtime";
+import { speechRuntime as runtime } from "./runtime";
import { createBrowserPcmCapture } from "./browserPcmCapture";
export function createBrowserVoiceInputPlatform(input: {
diff --git a/apps/web/src/speech/runtime.browser.test.ts b/apps/web/src/speech/runtime.browser.test.ts
new file mode 100644
index 0000000000000..0e4de55e8bdde
--- /dev/null
+++ b/apps/web/src/speech/runtime.browser.test.ts
@@ -0,0 +1,25 @@
+import { RelayConnectionTarget, type PreparedConnection } from "@t3tools/client-runtime/connection";
+import { getEnvironmentSpeechStatus } from "@t3tools/client-runtime/voice-input";
+import { EnvironmentId } from "@t3tools/contracts";
+import { expect, it } from "vite-plus/test";
+
+import { speechRuntime } from "./runtime";
+
+it("authorizes relay speech requests through the connection session", async () => {
+ const target = new RelayConnectionTarget({
+ environmentId: EnvironmentId.make("speech-relay-test"),
+ label: "Speech destination",
+ });
+ const prepared: PreparedConnection = {
+ target,
+ environmentId: target.environmentId,
+ label: target.label,
+ httpBaseUrl: "https://speech.example.test",
+ socketUrl: "wss://speech.example.test/ws",
+ httpAuthorization: { _tag: "Dpop", accessToken: "expired", expiresAtEpochMs: 0 },
+ };
+ // No account is signed in: renewal must reach the authorizer rather than fail on a missing service.
+ await expect(speechRuntime.runPromise(getEnvironmentSpeechStatus(prepared))).rejects.toThrow(
+ "Could not authorize the environment request.",
+ );
+});
diff --git a/apps/web/src/speech/runtime.ts b/apps/web/src/speech/runtime.ts
new file mode 100644
index 0000000000000..22c338d7138e2
--- /dev/null
+++ b/apps/web/src/speech/runtime.ts
@@ -0,0 +1,17 @@
+import * as Effect from "effect/Effect";
+import type { HttpClient } from "effect/http";
+import { AtomRegistry } from "effect/reactivity";
+
+import { connectionAtomRuntime } from "../connection/runtime";
+import { appAtomRegistry } from "../rpc/atomRegistry";
+
+export const speechRuntime = {
+ runPromise: (
+ effect: Effect.Effect,
+ options?: { signal?: AbortSignal },
+ ) =>
+ Effect.runPromise(
+ AtomRegistry.getResult(appAtomRegistry, connectionAtomRuntime.atom(effect)),
+ options,
+ ),
+};
diff --git a/apps/web/src/speech/useEnvironmentSpeechInput.browser.test.tsx b/apps/web/src/speech/useEnvironmentSpeechInput.browser.test.tsx
index a41f359f68ef4..6d51191e31814 100644
--- a/apps/web/src/speech/useEnvironmentSpeechInput.browser.test.tsx
+++ b/apps/web/src/speech/useEnvironmentSpeechInput.browser.test.tsx
@@ -88,7 +88,7 @@ vi.mock("../hooks/useSettings", () => ({
),
}),
}));
-vi.mock("../lib/runtime", () => ({ runtime: { runPromise: Effect.runPromise } }));
+vi.mock("./runtime", () => ({ speechRuntime: { runPromise: Effect.runPromise } }));
vi.mock("../components/ui/toast", () => ({ toastManager: { add: vi.fn() } }));
vi.mock("@t3tools/client-runtime/voice-input", async (importOriginal) => ({
...(await importOriginal()),
diff --git a/apps/web/src/speech/useEnvironmentSpeechInput.ts b/apps/web/src/speech/useEnvironmentSpeechInput.ts
index 644edb303aef2..353779c5d0919 100644
--- a/apps/web/src/speech/useEnvironmentSpeechInput.ts
+++ b/apps/web/src/speech/useEnvironmentSpeechInput.ts
@@ -33,7 +33,7 @@ import {
useClientSettingsHydrated,
useEnvironmentSettings,
} from "../hooks/useSettings";
-import { runtime } from "../lib/runtime";
+import { speechRuntime as runtime } from "./runtime";
import { useEnvironment } from "../state/environments";
import { useEnvironmentScope, usePreparedConnection } from "../state/session";
import { createBrowserVoiceInputPlatform } from "./browserVoiceInput";