diff --git a/src/resources/extensions/bg-shell/process-manager.ts b/src/resources/extensions/bg-shell/process-manager.ts index 95ee6ccd9..42ff4bb3d 100644 --- a/src/resources/extensions/bg-shell/process-manager.ts +++ b/src/resources/extensions/bg-shell/process-manager.ts @@ -375,6 +375,19 @@ export function cleanupAll(): void { processes.clear(); } +/** + * Kill all alive, non-persistent bg processes. + * Called between auto-mode units to prevent orphaned servers from + * keeping ports bound across task boundaries (#1209). + */ +export function killSessionProcesses(): void { + for (const [id, bg] of processes) { + if (bg.alive && !bg.persistAcrossSessions) { + killProcess(id, "SIGTERM"); + } + } +} + async function waitForProcessExit(bg: BgProcess, timeoutMs: number): Promise { if (!bg.alive) return true; await new Promise((resolve) => { diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index f3ee32961..e8114f3a9 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -202,10 +202,13 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d } } - // Prune dead bg-shell processes + // Prune dead bg-shell processes and kill non-persistent live ones. + // Without killing live processes between units, dev servers spawned during + // one task keep ports bound, causing conflicts in subsequent tasks (#1209). try { - const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js"); + const { pruneDeadProcesses, killSessionProcesses } = await import("../bg-shell/process-manager.js"); pruneDeadProcesses(); + killSessionProcesses(); } catch { // Non-fatal }