Files
orca/src/main/windows-process-tree-kill.ts
T
Wooseong Kim dc18ba9cda fix(win): taskkill agent PTY descendant trees on stop (#10100)
* fix(win): taskkill agent PTY descendant trees on stop

Windows agent teardown previously degraded to shell-only kill because
descendant snapshot is POSIX-only. Orphaned claude/codex/MCP children
kept worktree cwd handles open and blocked worktree remove.

Use taskkill /T /F on the PTY root in killWithDescendantSweep (daemon +
local agent shutdown) so the ConPTY tree is cleared before teardown.

Fixes #10004

* fix(win): bound taskkill with timeout and windowsHide

Prevent a wedged taskkill from delaying killRoot, and hide the console
flash during agent PTY tree teardown (CodeRabbit on #10100).
2026-07-24 00:27:40 -07:00

36 lines
1.0 KiB
TypeScript

import { execFile } from 'node:child_process'
export type WindowsTreeKiller = (rootPid: number) => Promise<void>
/** Bound hung taskkill so killRoot still runs in killWithDescendantSweep. */
export const WINDOWS_PROCESS_TREE_KILL_TIMEOUT_MS = 5_000
/**
* Force-kill a Windows process and every descendant (`taskkill /T /F`).
* Best-effort: missing/already-dead roots still resolve so callers can finish
* their own handle cleanup via killRoot.
*/
export function terminateWindowsProcessTree(
rootPid: number,
deps: { execFileImpl?: typeof execFile } = {}
): Promise<void> {
if (!Number.isInteger(rootPid) || rootPid <= 0) {
return Promise.resolve()
}
const run = deps.execFileImpl ?? execFile
return new Promise((resolve) => {
run(
'taskkill',
['/pid', String(rootPid), '/T', '/F'],
{
// Why: a wedged taskkill must not block killRoot forever (#10004 review).
timeout: WINDOWS_PROCESS_TREE_KILL_TIMEOUT_MS,
windowsHide: true
},
() => {
resolve()
}
)
})
}