From 1cf96c52924548897ada94a0b8e0e1dc7fb1d69d Mon Sep 17 00:00:00 2001 From: Nico <47644445+nicotsx@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:39:55 +0200 Subject: [PATCH] refactor(agents): split local agent bootstrap from controller startup (#847) --- .../agents/__tests__/agents-manager.test.ts | 8 +++---- app/server/modules/agents/agents-manager.ts | 16 +++++++------ app/server/modules/lifecycle/bootstrap.ts | 23 ++++++++++++++----- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/app/server/modules/agents/__tests__/agents-manager.test.ts b/app/server/modules/agents/__tests__/agents-manager.test.ts index c55d222d..eb7f9965 100644 --- a/app/server/modules/agents/__tests__/agents-manager.test.ts +++ b/app/server/modules/agents/__tests__/agents-manager.test.ts @@ -9,7 +9,7 @@ vi.mock("node:child_process", async () => { return { spawn: spawnMock }; }); -let spawnLocalAgent: (typeof import("../agents-manager"))["spawnLocalAgent"]; +let startLocalAgent: (typeof import("../agents-manager"))["startLocalAgent"]; let stopLocalAgent: (typeof import("../agents-manager"))["stopLocalAgent"]; const processWithAgentRuntime = process as ProcessWithAgentRuntime; @@ -50,7 +50,7 @@ const createFakeChild = () => { beforeEach(async () => { vi.resetModules(); setAgentRuntime(); - ({ spawnLocalAgent, stopLocalAgent } = await import("../agents-manager")); + ({ startLocalAgent, stopLocalAgent } = await import("../agents-manager")); }); afterEach(async () => { @@ -68,7 +68,7 @@ test("respawns the local agent after an unexpected exit", async () => { const secondChild = createFakeChild(); spawnMock.mockReturnValueOnce(firstChild).mockReturnValueOnce(secondChild); - await spawnLocalAgent(); + await startLocalAgent(); firstChild.exitCode = 1; firstChild.emit("exit", 1, null); @@ -84,7 +84,7 @@ test("does not respawn the local agent after an intentional stop", async () => { const child = createFakeChild(); spawnMock.mockReturnValue(child); - await spawnLocalAgent(); + await startLocalAgent(); await stopLocalAgent(); await vi.advanceTimersByTimeAsync(1_000); diff --git a/app/server/modules/agents/agents-manager.ts b/app/server/modules/agents/agents-manager.ts index 6986b671..9342b072 100644 --- a/app/server/modules/agents/agents-manager.ts +++ b/app/server/modules/agents/agents-manager.ts @@ -147,7 +147,7 @@ const backupEventHandlers: AgentBackupEventHandlers = { }, }; -export const startAgentRuntime = async () => { +export const startAgentController = async () => { const runtime = getAgentRuntimeState(); if (runtime.agentManager) { @@ -163,6 +163,13 @@ export const startAgentRuntime = async () => { runtime.agentManager = nextAgentManager; }; +export const stopAgentController = async () => { + const runtime = getAgentRuntimeState(); + const agentManagerRuntime = runtime.agentManager; + runtime.agentManager = null; + await agentManagerRuntime?.stop(); +}; + export const agentManager = { runBackup: async (agentId: string, request: AgentRunBackupRequest) => { const runtime = getAgentManagerRuntime(); @@ -213,7 +220,7 @@ export const agentManager = { }, }; -export const spawnLocalAgent = async () => { +export const startLocalAgent = async () => { await spawnLocalAgentProcess(getAgentRuntimeState()); }; @@ -221,8 +228,3 @@ export const spawnLocalAgent = async () => { export const stopLocalAgent = async () => { await stopLocalAgentProcess(getAgentRuntimeState()); }; - -export const stopAgentRuntime = async () => { - await getAgentManagerRuntime()?.stop(); - await stopLocalAgent(); -}; diff --git a/app/server/modules/lifecycle/bootstrap.ts b/app/server/modules/lifecycle/bootstrap.ts index f8a4b519..a1cbb8a2 100644 --- a/app/server/modules/lifecycle/bootstrap.ts +++ b/app/server/modules/lifecycle/bootstrap.ts @@ -1,5 +1,6 @@ import { runDbMigrations } from "../../db/db"; -import { spawnLocalAgent, startAgentRuntime, stopAgentRuntime } from "../agents/agents-manager"; +import { config } from "../../core/config"; +import { startAgentController, startLocalAgent, stopAgentController, stopLocalAgent } from "../agents/agents-manager"; import { runMigrations } from "./migrations"; import { startup } from "./startup"; @@ -8,11 +9,20 @@ let bootstrapPromise: Promise | undefined; const runBootstrap = async () => { await runDbMigrations(); await runMigrations(); - if (process.env.ENABLE_LOCAL_AGENT === "true") { - await startAgentRuntime(); - await spawnLocalAgent(); + + try { + await startAgentController(); + + if (config.flags.enableLocalAgent) { + await startLocalAgent(); + } + + await startup(); + } catch (error) { + await stopLocalAgent(); + await stopAgentController(); + throw error; } - await startup(); }; export const bootstrapApplication = async () => { @@ -30,7 +40,8 @@ export const bootstrapApplication = async () => { export const stopApplicationRuntime = async () => { try { - await stopAgentRuntime(); + await stopLocalAgent(); + await stopAgentController(); } finally { bootstrapPromise = undefined; }