You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
background-terminals: force-kill falls back to TerminateProcess(shell) when taskkill exceeds its 400ms budget, orphaning the process tree (Windows) #408
On Windows machines where launching taskkill.exe is slow (≳400ms — e.g. under real-time antivirus scanning), the background-terminal kill path silently orphans the process tree. Graceful taskkill /pid X /T (no /F) never works on console processes (no window to close), and force taskkill /pid X /T /F exceeds its 400ms budget, so the code SIGKILLs the taskkill helper and falls back to child.kill() = TerminateProcess on the cmd.exe shell only — the tree's real children survive. The manager settles as killed anyway. Impact: bg_kill/timeout/teardown reports success while the actual process (e.g. a dev server) keeps running and holding its port; leaked grandchildren keep stdio handles open so the test process never exits. Reproducible: manager.test.ts passes all tests alone yet never exits, leaving ~20 orphaned setInterval stub processes on every run.
一句话(对用户的影响)
OpenPI 在部分 Windows 机器上会谎报军情:界面与 agent 被告知"进程已终止",而真正干活的进程(如 dev server)还活着——占端口、锁文件、吃内存,agent 的世界模型被污染后会在错误方向上自信排查。
对使用者的影响(真实下午)
用户让 agent 后台起 dev server → 中途喊停 → 管理器报"✅ 已终止"、外壳 cmd.exe 死了,node dev server 本体成为孤儿继续占端口 → agent 在新端口重启失败、查半天查不出"旧进程其实还活着" → 一天下来任务管理器躺一排僵尸 node,dist/ 删除报"文件被占用"(Windows 经典 EPERM)。这类 bug 不会当场报错,而是在半小时后让人陷入莫名其妙的泥潭。
环境
Windows 11 Home China 10.0.26200
Node v24.11.1、Bun 1.3.13(仓库 pin 1.3.14)
Pi 0.85.0 / OpenPI main @ 75018b5(package.json v0.5.0)
English TL;DR
一句话(对用户的影响)
OpenPI 在部分 Windows 机器上会谎报军情:界面与 agent 被告知"进程已终止",而真正干活的进程(如 dev server)还活着——占端口、锁文件、吃内存,agent 的世界模型被污染后会在错误方向上自信排查。
对使用者的影响(真实下午)
用户让 agent 后台起 dev server → 中途喊停 → 管理器报"✅ 已终止"、外壳 cmd.exe 死了,node dev server 本体成为孤儿继续占端口 → agent 在新端口重启失败、查半天查不出"旧进程其实还活着" → 一天下来任务管理器躺一排僵尸 node,
dist/删除报"文件被占用"(Windows 经典 EPERM)。这类 bug 不会当场报错,而是在半小时后让人陷入莫名其妙的泥潭。环境
10.0.2620075018b5(package.json v0.5.0)taskkill.exe启动到结束实测 ~880ms(>400ms 预算,详见"复现"的自检测试)本机实测症状
FORCE_KILL_AFTER_MS=2000/FORCE_CLOSE_WAIT_MS=500/SETTLE_GRACE_MS=1000)manager.test.ts单跑:全部测试通过,但进程永不退出(5 分钟看门狗后强杀),每次运行遗留 ~20 个孤儿node -e "eval(Buffer.from('c2V0SW50ZXJ2YWwoKCkgPT4ge30sIDEwMDAp',...))"(=setInterval(()=>{},1000))桩进程(两次独立运行各清出 20 个)bun run test:跑到该文件后冻结;test: eliminate Windows background-terminal process-test flakes in the full suite #304 报告的 flaky 与"进程停止输出、需中断"和本次观察同源根因链(行号均指
extensions/background-terminals/src/manager.ts)taskkill /pid X /T(不带/F)本质是发WM_CLOSE,只对有窗口的进程有效;控制台进程(cmd/node)无窗口可关 → 温和杀必然失败,每次都升级(265-271;本机实测退出码 128、目标存活)FORCE_CLOSE_WAIT_MS 500 − TASKKILL_HELPER_CLOSE_WAIT_MS 100,49-52 / 278-280)SIGKILL(319-335);helper 在 +100ms 内关闭 →helperClosed=true→ 结果不是unresolved→ 落入兜底directSignal(398-415)=child.kill()=TerminateProcess,只杀 cmd.exe 外壳——代码注释自己写明:这样会使后代孤儿、原进程树句柄不可用(405-407)close事件永不触发 → 结算靠 1s 宽限强制落地 → 管理器报"已终止",但 OS 层根本没死node --test运行器无限等待 → 挂死核心矛盾:
killed/settle 的判定依据是 taskkill 退出码与结算事件,不验证后代进程的真实存活——所以测试可以"通过"而进程照样活着。复现
注意:依赖机器条件——taskkill 启动 <100ms 的快机器不泄漏。这正是 CI(及多数 Windows)见不到的原因,也让 #304 更容易把现象误判为时序噪音。
与 #304 的关系
#304 将现象归为"测试基础设施时序噪音",提议对真进程测试做串行隔离。隔离能让套件变绿,但会掩盖本 bug:本机实测 manager.test.ts 即使单跑(无任何并发竞争)也泄漏 ~20 孤儿——病根不在调度,在杀进程的兜底路径。建议 #304 的验收标准补一条:"套件结束后无存活的后台后代进程",否则验收会以"绿但漏杀"收场。
建议修复方向(供讨论)
directSignal兜底——TerminateProcess只杀外壳等于亲手制造孤儿;应标记unresolved,或重试一次taskkill /T /FKILL_ON_JOB_CLOSE把每个后台终端装进独立 Job——进程树物理上逃不出去,任何兜底都不会漏