环境
- ctl
0.0.2
- Windows 11 x64(原生,非 WSL),平台包
win32-x64-msvc
- ctl 安装路径:
%LOCALAPPDATA%\ctl\bin\ctl.exe(install 脚本装的 native 二进制,不是 .cmd/npm shim)
- hook:
.omp/hooks/pre/ctl-context.ts
现象
ctl-context.ts 的 checkGate 用同步 execFileSync("ctl", [...], { stdio:["pipe","pipe","pipe"], timeout }) 调 ctl hook gate。在 Windows 上这个 同步 spawn 间歇性挂起:进程永不退出、stdout/stderr 全空,最终被 timeout 杀掉(code: ETIMEDOUT, signal: SIGTERM)。
由于原版 catch {} 把异常整个吞掉 → 返回 null → 对所有 mutating 工具(write/edit/bash/task)fail-closed 拦截,即使 ctl 本身完全健康且判定 allowed:true。结果:agent 在 Windows 上彻底无法执行任何写/bash 操作。
关键证据(不是 ctl 本身的问题)
用 Python subprocess 调同一个 ctl.exe、同样的参数,每次都稳定返回:
ctl hook gate --tool bash --command "echo hi"
→ {"allowed":true,"reason":"bash allowed","state":"..."} (13–27ms, rc=0,连测 6 次全过)
而且模拟 Node 的 ["pipe","pipe","pipe"](Popen(..., stdin=PIPE) 后直接 wait()、不 communicate/不关 stdin)也不挂(0.07s 退出)—— 由此排除:不是 stdin 阻塞、不是 ctl 慢或崩、不是 PATH / .cmd shim。
差异只在调用方:Python subprocess(底层 CreateProcess)正常;Node execFileSync(= spawnSync,libuv 同步路径)挂起。
复现
Windows + ctl 0.0.2 + OMP ctl-context hook。给 checkGate 的 catch 落日志后能看到:
{"stage":"ctlSpawnSync-primary",
"bin":"C:\\Users\\<user>\\AppData\\Local\\ctl\\bin\\ctl.exe",
"args":["hook","gate","--tool","bash","--command","..."],
"err":{"code":"ETIMEDOUT","signal":"SIGTERM",
"msg":"spawnSync C:\\...\\ctl.exe ETIMEDOUT","stderr":"","stdout":""}}
建议修复
把 hook 里的同步 execFileSync 换成 async execFile(走 spawn + libuv 异步路径)。tool_call handler 本身已是 async,改成 await 即可。强烈怀疑是 spawnSync 在 Windows 上 spawn 这个 native 二进制时的同步 stdio 排空 / uv_spawn 边界问题,async
路径能绕开(Python 走的就是非同步路径,从不挂)。
附带两点建议:
- 不要 catch {} 吞错误 —— 至少把 code/status/stderr 落日志,否则这类问题完全不可观测。
- 顺带把 timeout 从 5000ms 调大一些(如 15s)更稳。
最小期望
让 Windows 上 ctl hook gate 的调用稳定不挂 —— 要么修 spawnSync 挂起、要么 hook 统一切 async execFile。
环境
0.0.2win32-x64-msvc%LOCALAPPDATA%\ctl\bin\ctl.exe(install 脚本装的 native 二进制,不是.cmd/npm shim).omp/hooks/pre/ctl-context.ts现象
ctl-context.ts的checkGate用同步execFileSync("ctl", [...], { stdio:["pipe","pipe","pipe"], timeout })调ctl hook gate。在 Windows 上这个 同步 spawn 间歇性挂起:进程永不退出、stdout/stderr 全空,最终被 timeout 杀掉(code: ETIMEDOUT,signal: SIGTERM)。由于原版
catch {}把异常整个吞掉 → 返回null→ 对所有 mutating 工具(write/edit/bash/task)fail-closed 拦截,即使ctl本身完全健康且判定allowed:true。结果:agent 在 Windows 上彻底无法执行任何写/bash 操作。关键证据(不是 ctl 本身的问题)
用 Python
subprocess调同一个ctl.exe、同样的参数,每次都稳定返回:而且模拟 Node 的
["pipe","pipe","pipe"](Popen(..., stdin=PIPE)后直接wait()、不 communicate/不关 stdin)也不挂(0.07s 退出)—— 由此排除:不是 stdin 阻塞、不是 ctl 慢或崩、不是 PATH /.cmdshim。差异只在调用方:Python
subprocess(底层CreateProcess)正常;NodeexecFileSync(=spawnSync,libuv 同步路径)挂起。复现
Windows + ctl 0.0.2 + OMP ctl-context hook。给
checkGate的 catch 落日志后能看到:{"stage":"ctlSpawnSync-primary", "bin":"C:\\Users\\<user>\\AppData\\Local\\ctl\\bin\\ctl.exe", "args":["hook","gate","--tool","bash","--command","..."], "err":{"code":"ETIMEDOUT","signal":"SIGTERM", "msg":"spawnSync C:\\...\\ctl.exe ETIMEDOUT","stderr":"","stdout":""}}建议修复
把 hook 里的同步 execFileSync 换成 async execFile(走 spawn + libuv 异步路径)。tool_call handler 本身已是 async,改成 await 即可。强烈怀疑是 spawnSync 在 Windows 上 spawn 这个 native 二进制时的同步 stdio 排空 / uv_spawn 边界问题,async
路径能绕开(Python 走的就是非同步路径,从不挂)。
附带两点建议:
最小期望
让 Windows 上 ctl hook gate 的调用稳定不挂 —— 要么修 spawnSync 挂起、要么 hook 统一切 async execFile。