From afbb2e8bb86d4606e6bc3b75c5a4ae29b43b53e0 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 8 Jun 2026 12:12:40 +0700 Subject: [PATCH] Fix release-blocking CI failures --- .../desktop/src/daemon/daemon-manager.test.ts | 3 ++- .../test-utils/test-opencode-runtime.ts | 4 +-- packages/server/src/server/daemon-worker.ts | 26 +++++++++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/desktop/src/daemon/daemon-manager.test.ts b/packages/desktop/src/daemon/daemon-manager.test.ts index d7a6e78d6..0c5a1ee17 100644 --- a/packages/desktop/src/daemon/daemon-manager.test.ts +++ b/packages/desktop/src/daemon/daemon-manager.test.ts @@ -359,8 +359,9 @@ describe("daemon-manager commands", () => { expect(thrown).toBeInstanceOf(Error); const message = thrown?.message ?? ""; + const recentLogsLabel = message.match(/Recent logs \(([^)]*)\):/)?.[1]; expect(message).toContain("Daemon failed to start: exit code 1"); - expect(message).toContain(`Recent logs (${mocks.paseoHome}/daemon.log):`); + expect(recentLogsLabel?.split(/[\\/]/).at(-1)).toBe("daemon.log"); expect(message).toContain("recent daemon failure"); expect(mocks.spawnProcess).toHaveBeenCalledWith( "node", diff --git a/packages/server/src/server/agent/providers/opencode/test-utils/test-opencode-runtime.ts b/packages/server/src/server/agent/providers/opencode/test-utils/test-opencode-runtime.ts index eb7768f74..e9d1b6234 100644 --- a/packages/server/src/server/agent/providers/opencode/test-utils/test-opencode-runtime.ts +++ b/packages/server/src/server/agent/providers/opencode/test-utils/test-opencode-runtime.ts @@ -1,4 +1,4 @@ -import type { Event as OpenCodeEvent, OpencodeClient } from "@opencode-ai/sdk/v2/client"; +import type { OpencodeClient } from "@opencode-ai/sdk/v2/client"; import type { OpenCodeRuntime, OpenCodeServerAcquisition } from "../runtime.js"; @@ -92,7 +92,7 @@ export class TestOpenCodeClient { sessionMessagesResponse: OpenCodeResponse = { data: [] }; sessionPromptAsyncEvents: unknown[] = [idleEvent()]; sessionPromptAsyncResponse: OpenCodeResponse = {}; - sessionSummarizeEvents: OpenCodeEvent[] = []; + sessionSummarizeEvents: unknown[] = [idleEvent()]; sessionSummarizeResponse: OpenCodeResponse = { data: {} }; sessionUpdateResponse: OpenCodeResponse = {}; private readonly queuedEventStream = createQueuedEventStream(); diff --git a/packages/server/src/server/daemon-worker.ts b/packages/server/src/server/daemon-worker.ts index 34c082552..ff094da23 100644 --- a/packages/server/src/server/daemon-worker.ts +++ b/packages/server/src/server/daemon-worker.ts @@ -173,13 +173,17 @@ async function main() { const supervisorPid = process.ppid; let lastSupervisorHeartbeatAt = Date.now(); - let supervisorShutdownRequested = false; - const requestSupervisorShutdown = (reason: string) => { - if (supervisorShutdownRequested) { + let supervisorExitRequested = false; + const exitAfterSupervisorLoss = () => { + if (supervisorExitRequested) { return; } - supervisorShutdownRequested = true; - beginShutdown(reason); + supervisorExitRequested = true; + + // The supervisor owns the worker's stdout/stderr pipes. Once it is gone, + // logging during graceful shutdown can block on the broken pipe and leave + // the daemon orphaned, so supervisor loss is a hard process boundary. + process.exit(0); }; process.on("message", (message: unknown) => { @@ -192,13 +196,19 @@ async function main() { lastSupervisorHeartbeatAt = Date.now(); } }); - process.on("disconnect", () => requestSupervisorShutdown("supervisor disconnect")); + process.on("disconnect", exitAfterSupervisorLoss); const timer = setInterval(() => { const ipcConnected = typeof process.connected === "boolean" ? process.connected : true; const heartbeatExpired = Date.now() - lastSupervisorHeartbeatAt > 3500; - if (ipcConnected === false || !isPidAlive(supervisorPid) || heartbeatExpired) { - requestSupervisorShutdown("supervisor disconnect"); + const supervisorChanged = process.ppid !== supervisorPid; + if ( + ipcConnected === false || + supervisorChanged || + !isPidAlive(supervisorPid) || + heartbeatExpired + ) { + exitAfterSupervisorLoss(); } }, 1000); timer.unref();