Repository navigation
fix(hooks): exit Windows vendor-replay hook skips without draining stdin - #33470
Conversation
The Windows managed hook routes the Devin-imports-Claude and Grok-replay skips to the `orca_agent_hook_drain_stdin` epilogue, which parks in `more.com` until stdin reaches EOF. When the caller abandons the pipe instead of closing it — the same #11549 class this file already guards against for missing Orca env — the drain child never returns: the hook is stranded until the caller's timeout, and because `more.com` inherits the hook's stdout handle it keeps that pipe open after a timeout-kill, so the agent's read never sees EOF. Exit the skip paths without owning stdin instead. The payload is discarded on those paths anyway, matching the rule the env guards already follow (buildWindowsHookEnvironmentGuardLines), and removing the last in-script jumps to `more.com`. - hook-script.ts: DEVIN skip `goto :drain` -> `exit /b 0` - grok-replay-guard.ts: Windows Grok skip `goto :drain` -> `exit /b 0` - update the lifecycle + grok tests to pin exit-without-drain The shared drain epilogue itself is left intact (the missing-managed-script launcher case and the lifecycle contract still reference it); a bounded Windows drain, mirroring the POSIX deadline reader, is a larger follow-up. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…reading stdin Extends the Devin/Grok skip fix to the two remaining Windows paths that still drain stdin to EOF for vendor-replayed Claude hooks: - the Cursor replay guard (added after the original fix) now exits instead of jumping to the more.com drain; - orca-agent-hook.exe, now the primary Windows Claude hook, treats GROK_HOOK_EVENT / DEVIN_PROJECT_DIR as exit vars instead of draining. Adds a Windows regression that runs the in-pane skip paths with stdin held open, and widens the structural check to reject any goto into the drain from managed hook scripts. Refs #24907 Co-authored-by: inforanger <inforanger@users.noreply.github.com>
|
No actionable comments were generated in the recent review.
|
ELI5
On Windows, when Grok, Devin, or Cursor runs Orca's Claude hook (these agents import Claude's hooks), Orca is supposed to skip quietly. Instead of returning, the skip handed stdin to
more.comand waited for stdin to close. If the caller kept stdin open, the hook just hung: it held the agent's tool call until the hook timeout and left a child process holding the agent's stdout pipe. These skips now return right away without reading stdin, which is already how Orca handles the "not in an Orca pane" guards on Windows.Root cause
.cmdhooks: the Devin skip (src/main/claude/hook-script.ts), the Grok replay guard (grok-replay-guard.ts, used by the Claude and Cursor hooks), and the Cursor replay guard (cursor-replay-guard.ts, added after fix(hooks): exit Windows Devin/Grok skip without draining stdin #24909 was opened) allgoto :orca_agent_hook_drain_stdin, which runs"%SystemRoot%\System32\more.com" >nul. That's a read-to-EOF with no bound.orca-agent-hook.exeis now the primary Windows Claude hook (perf(claude): run the Windows Claude hook as a signed native claude-hook.exe #27719, exec form fix(claude): run the native Windows hook exe for spaced profile paths #33364). It has the same behavior:drain_vars: ["GROK_HOOK_EVENT", "DEVIN_PROJECT_DIR"]→io::copy(stdin, sink).What changed
main(their commit is kept). The Devin and Grok.cmdskips nowexit /b 0.native/windows-agent-hook/src/main.rs: Grok/Devin move fromdrain_varstoexit_vars, and the now-unused drain branch is removed.managed-hook-stdin-lifecycle.test.ts) now runs the in-pane Devin/Grok/Cursor skips with stdin held open.gotointo the drain from managed scripts. It was ORCA_*-only before.Verification
Reproduced on current
mainwith a real Windows 11 host (win-lowspec). I generatedclaude-hook-impl.cmdwith this repo'sgetManagedScripton Windows and ran it throughcmd.exe /d /cwith Orca pane env plus each vendor's variable. The writer sent a 256 KB payload:EOF)Native exe logic, from the same Rust source built for the host with the payload written and stdin left open:
mainwas still running after 3 s for bothGROK_HOOK_EVENTandDEVIN_PROJECT_DIR, and this PR exits 0 in 2–196 ms.cargo checkis clean. The Windows-only exe/host tests run in Windows CI, because the exe build needs a Windows host.I also ran the Windows-only vitest suites in a clean clone of this branch on win-lowspec. With this PR,
managed-hook-stdin-lifecycle.test.tsandgrok-replay-guard.test.tspass, including the new held-open-stdin skip leg. Withmain's three source files restored, the new leg fails withhook did not finish with stdin abandond: cmd.exe /d /c ...\claude-hook-impl.cmd. The one remaining failure on that host is theorca-agent-hook.exebuild, because it has no Rust toolchain. It fails the same way onmain.Also ran
pnpm tc,check:code-quality:changed, and the agent-hooks / claude / cursor / grok / devin suites (296 files) on macOS.Risk
grok-buildcrates/codegen/xai-grok-hooks/src/runner/command.rs:259-266,let _ = stdin.write_all(..)) and kills the hook group on timeout.cursor-agent2026.06.023880.index.jsexecuteCommandScript:${command} <<'CURSOR_HOOK_EOF').{}first and exits 0.:orca_agent_hook_drain_stdinepilogue is left in the Claude script. It's shared with other scripts and with the statusline and missing-script launchers.Credit
This builds on #24909 by @inforanger: their commit is preserved, and the follow-up commit credits them as co-author. It supersedes #24909, which conflicts with current
mainand misses the Cursor guard and the native exe.Fixes #24907