From 8c1122f3a75da5c7512d141f20ff85a930302d75 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 24 Aug 2026 15:26:45 +0200 Subject: [PATCH] fix(cli): avoid Windows PTY termination verification --- .changeset/fix-windows-pty-termination.md | 5 +++++ packages/core/src/kilocode/pty/termination.ts | 2 +- .../test/kilocode/pty-termination.test.ts | 20 ++++++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-windows-pty-termination.md diff --git a/.changeset/fix-windows-pty-termination.md b/.changeset/fix-windows-pty-termination.md new file mode 100644 index 0000000000..90fbc641fe --- /dev/null +++ b/.changeset/fix-windows-pty-termination.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Fix PTY cleanup on Windows when POSIX process-tree inspection is unavailable. diff --git a/packages/core/src/kilocode/pty/termination.ts b/packages/core/src/kilocode/pty/termination.ts index 33b29936d2..51aa3d9f5d 100644 --- a/packages/core/src/kilocode/pty/termination.ts +++ b/packages/core/src/kilocode/pty/termination.ts @@ -153,7 +153,7 @@ export async function terminate(proc: Process, input: Runtime = runtime): Promis }) if ((!killed || input.alive(proc.pid)) && !state.exited) direct(proc) if (!state.exited) await input.sleep(GRACE_MS) - await verify(proc, state.exited, input) + if (!state.exited && input.alive(proc.pid)) throw new Error(`PTY process tree is still alive: ${proc.pid}`) return } diff --git a/packages/core/test/kilocode/pty-termination.test.ts b/packages/core/test/kilocode/pty-termination.test.ts index d3de5e6354..e4468efa49 100644 --- a/packages/core/test/kilocode/pty-termination.test.ts +++ b/packages/core/test/kilocode/pty-termination.test.ts @@ -18,6 +18,8 @@ function runtime( taskkillLeavesAlive?: boolean signal?: "throw" tree?: Array<{ pid: number; parent: number }> + treeError?: boolean + treeCalls?: { count: number } } = {}, ) { const tasks: Array<{ @@ -36,7 +38,11 @@ function runtime( if (result && !input.taskkillLeavesAlive) alive = false return result }, - tree: async () => input.tree ?? [], + tree: async () => { + if (input.treeCalls) input.treeCalls.count++ + if (input.treeError) throw new Error("process tree unavailable") + return input.tree ?? [] + }, alive: () => alive, signal: (pid, signal) => { signals.push({ pid, signal }) @@ -83,6 +89,18 @@ describe("pty process-tree termination", () => { expect(input.sleeps).toEqual([200]) }) + test("continues when Windows process-tree inspection is unavailable", async () => { + const treeCalls = { count: 0 } + const input = runtime("win32", { treeError: true, treeCalls }) + const item = fake(42) + + await KiloPtyTermination.terminate(item.proc, input.value) + + expect(item.calls).toEqual([]) + expect(input.sleeps).toEqual([200]) + expect(treeCalls.count).toBe(0) + }) + test("signals POSIX process groups before escalating", async () => { const item = fake(42) const input = runtime("linux")