diff --git a/packages/core/src/__tests__/session-manager.test.ts b/packages/core/src/__tests__/session-manager.test.ts index 4354ea6f6..bff1bee25 100644 --- a/packages/core/src/__tests__/session-manager.test.ts +++ b/packages/core/src/__tests__/session-manager.test.ts @@ -2690,6 +2690,27 @@ describe("spawnOrchestrator", () => { expect(meta?.["orchestratorSessionReused"]).toBeUndefined(); }); + it("respawns the orchestrator when stale metadata exists but the runtime is dead", async () => { + writeMetadata(sessionsDir, "app-orchestrator", { + worktree: join(tmpDir, "my-app"), + branch: "main", + status: "working", + project: "my-app", + role: "orchestrator", + runtimeHandle: JSON.stringify(makeHandle("rt-stale")), + createdAt: new Date().toISOString(), + }); + + vi.mocked(mockRuntime.isAlive).mockResolvedValueOnce(false); + + const sm = createSessionManager({ config, registry: mockRegistry }); + await sm.spawnOrchestrator({ projectId: "my-app" }); + + expect(mockRuntime.create).toHaveBeenCalledTimes(1); + const meta = readMetadataRaw(sessionsDir, "app-orchestrator"); + expect(meta?.["runtimeHandle"]).toBe(JSON.stringify(makeHandle("rt-1"))); + }); + it("uses orchestratorModel when configured", async () => { const configWithOrchestratorModel: OrchestratorConfig = { ...config, diff --git a/packages/core/src/session-manager.ts b/packages/core/src/session-manager.ts index 6396f50d7..2f875bf3a 100644 --- a/packages/core/src/session-manager.ts +++ b/packages/core/src/session-manager.ts @@ -917,6 +917,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager { writeFileSync(systemPromptFile, orchestratorConfig.systemPrompt, "utf-8"); } + const existingOrchestratorMetadata = readMetadataRaw(sessionsDir, sessionId); const existingOrchestrator = await get(sessionId); if (existingOrchestrator?.runtimeHandle) { const existingAlive = await plugins.runtime @@ -931,7 +932,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager { } } - if (!reserveSessionId(sessionsDir, sessionId)) { + if (!existingOrchestratorMetadata && !reserveSessionId(sessionsDir, sessionId)) { const raceSession = await get(sessionId); if (raceSession?.runtimeHandle) { const raceAlive = await plugins.runtime