From 312c200c4ce2c8c57903e48f2c921f9db2a37582 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Fri, 14 Aug 2026 14:20:22 -0600 Subject: [PATCH] fix(server): keep Windows Prime daemon fallback fail-closed --- .../prime/PrimeAgentDaemonManager.test.ts | 8 +++++--- .../provider/prime/PrimeAgentDaemonManager.ts | 18 ++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts b/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts index 43b30d6d6..e2a39f05c 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts @@ -201,6 +201,7 @@ function managerFixture(options?: { readonly restoreConnectionOnSpawn?: boolean; readonly tempDir?: string; readonly platform?: NodeJS.Platform; + readonly injectBridge?: boolean; }) { const commands: CapturedCommand[] = []; const processes: FakeProcess[] = []; @@ -259,7 +260,7 @@ function managerFixture(options?: { readinessRetryDelay: Duration.zero, readinessRetries: 4, shutdownTimeout: Duration.zero, - bridge, + ...(options?.injectBridge === false ? {} : { bridge }), }).pipe(Effect.provide(Layer.merge(NodeServices.layer, spawner))); return { make, @@ -309,11 +310,12 @@ describe("PrimeAgentDaemonManager paths and environment", () => { }); describe("PrimeAgentDaemonManager lifecycle", () => { - it.effect("fails closed on Windows before spawning an unauthenticated named-pipe daemon", () => { - const fixture = managerFixture({ platform: "win32" }); + it.effect("fails closed on Windows before loading Prime or spawning a named-pipe daemon", () => { + const fixture = managerFixture({ platform: "win32", injectBridge: false }); return Effect.gen(function* () { const error = yield* Effect.flip(fixture.make); expect(error).toMatchObject({ + socket: fixture.paths.socket, reason: "transport-security-unavailable", detail: expect.stringContaining("verified per-user ACL or authenticated handshake"), }); diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonManager.ts b/apps/server/src/provider/prime/PrimeAgentDaemonManager.ts index 0485b84ed..2d9eee8b0 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonManager.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonManager.ts @@ -273,23 +273,21 @@ export const makePrimeAgentDaemonManager = Effect.fn("makePrimeAgentDaemonManage const hostPlatform = yield* HostProcessPlatform; const hostEnvironment = yield* HostProcessEnvironment; const platform = input.platform ?? hostPlatform; - const bridge = input.bridge ?? (yield* loadPrimeAgentDaemonBridge(input.executablePath)); const paths = derivePrimeAgentDaemonPaths({ ...input, platform }); - const defaultSocket = bridge.defaultDaemonSocketPath(); - const socket = - paths.socket === defaultSocket - ? platform === "win32" - ? `${paths.socket}-pylon-private` - : paths.socket.replace(/\.sock$/, "-pylon-private.sock") - : paths.socket; - const sessionDir = paths.sessionDir; if (platform === "win32") { return yield* managerError( - socket, + paths.socket, "transport-security-unavailable", "Prime Agent daemon mode is disabled on Windows until its named pipe has a verified per-user ACL or authenticated handshake.", ); } + const bridge = input.bridge ?? (yield* loadPrimeAgentDaemonBridge(input.executablePath)); + const defaultSocket = bridge.defaultDaemonSocketPath(); + const socket = + paths.socket === defaultSocket + ? paths.socket.replace(/\.sock$/, "-pylon-private.sock") + : paths.socket; + const sessionDir = paths.sessionDir; const timeoutMs = input.connectTimeoutMs ?? 10_000; const readinessSchedule = Schedule.max([ Schedule.spaced(input.readinessRetryDelay ?? Duration.millis(50)),