Files
orca/native/windows-agent-hook/Cargo.lock
T
OrcaWinandOrca Worker 7909732283 perf(claude): run the Windows Claude hook as a signed native claude-hook.exe (#27719)
* perf(claude): run the Windows Claude hook as a native orca-hook.exe

Each Claude hook event on Windows started cmd.exe (plus the user's cmd
AutoRun) and then curl.exe on top of the agent's own shell. A small signed
Rust binary now does the same synchronous POST in one process, under Git
Bash, PowerShell and direct CreateProcess alike.

- native/windows-agent-hook: std-only crate, same contract as the .cmd hook
  (prints {}, env guards before stdin, endpoint re-read, 0.5s/1.5s caps,
  waits for the listener reply, always exit 0). Its installed file name
  selects the agent profile, so the registration stays a bare path.
- Packaged as resources/bin/orca-hook.exe (SignPath signs it with the other
  inner PEs); afterPack fails on a missing or wrong-machine binary.
- Claude install copies it verbatim to ~/.orca/agent-hooks/claude-hook.exe
  (temp + rename, running copy moved aside) and registers it; it falls back
  to the .cmd hook when the bundle is missing, unsigned or cannot be copied.
  Only Claude opts in, via windowsNativeHookExecutable.
- The managed-command matcher sweeps .exe entries alongside .cmd/.ps1/.sh.
- The CLI launcher and hook builds share windows-rust-executable-build.mjs,
  which now also checks the PE machine.

Refs #20913

* fix(build): publish native Windows binaries atomically so parallel builds cannot collide

The Windows CI lane runs every orca-hook.exe suite in one vitest
invocation, each building through build-windows-agent-hook.mjs. The build
copied straight onto native/windows-agent-hook/.build/orca-hook.exe, and
CopyFile opens its destination unshared, so a second build (or a scanner
still holding the fresh image) failed with EBUSY.

- windows-rust-executable-build.mjs skips the publish when the output
  already has the built bytes, otherwise stages a per-process temp file and
  renames it over the output, retrying briefly while another handle holds it.
- The test fixture hands each suite a private copy of the build output, so
  no suite reads or starts the shared file while another build replaces it.

Refs #20913

* refactor(claude): simplify the native Windows hook to one Claude binary

Review follow-up on the native hook (#20913): the same behaviour with less
machinery.

- The binary is built, shipped and installed as claude-hook.exe, so the
  installed name matches its PE OriginalFilename. It is Claude-only, so the
  file-name profile table and the per-agent opt-in flag are gone; the
  registration is gated by the existing Claude-only usesWindowsEntry check.
- Install and refresh reuse writeManagedScript / refreshManagedScriptIfPresent
  (now accepting bytes): temp file, rename, skip when identical. A copy whose
  hook is still running cannot be replaced; install then registers the .cmd
  hook and refresh keeps the old build until a later start, instead of the
  rename-aside and *.retired sweep.
- The install-time Authenticode-presence check is dropped. Release CI now
  requires resources\bin\claude-hook.exe to be signed (release-cut and the
  signing rehearsal), next to resources\bin\orca.exe.
- getStatus counts either Windows form through findManagedEventHookForms in
  hook-settings.ts, replacing managed-event-hook-status.ts.
- Rust: plain Claude constants, one endpoint-file lookup (last assignment
  wins, as under `call`), one request write and a deadline-bounded drain.
  Guard order and form encoding are unchanged.
- Build: PR CI builds the hook once before the Windows tests, so the suites
  only read it; the launcher publish goes back to copyFileSync and the retry
  loop and its test are gone. The PE-machine check lives only in afterPack.
  Cargo.lock joins both crates' build fingerprints, and the launcher's alias
  exports are removed.

Refs #20913

* test(ci): run the managed hook stdin-lifecycle suite in the Windows lane

Its claude-hook.exe case (and the other Windows-only cases) are gated per
test, so the lane-registration guard did not require the file and it ran
only on Linux, where those cases skip. Register it in both Windows lists.

Refs #20913

* feat(agent-hooks): ship the native hook as orca-agent-hook.exe <agent>

The native Windows hook (#20913) becomes one general binary that stays
Claude-only in behaviour.

- Named orca-agent-hook.exe everywhere: build output, PE OriginalFilename
  and InternalName, resources\bin, the afterPack check, the installed copy
  in ~/.orca/agent-hooks, and the release signed-target lists.
- It takes the agent as its first argument and Claude registers
  `<path> claude`. The CLAUDE_JOB_DIR exit and the Grok/Devin drain belong
  to that agent; an unknown or missing agent answers {} without reading
  stdin. The managed-command matcher owns `orca-agent-hook.exe claude` and
  still owns the old claude-hook.exe entry, so installs migrate; install
  deletes a leftover claude-hook.exe when nothing runs it.
- A copy still running from the previous build no longer forces the .cmd
  fallback. Identical bytes are not rewritten; otherwise the temp file is
  renamed over the target, and when Windows refuses because the image is
  mapped, the running copy is renamed to orca-agent-hook.exe.old first.
  Install deletes a stale .old; only when that copy is also still running
  does the install fall back to .cmd. writeManagedScript and
  refreshManagedScriptIfPresent take the aside path, sharing one helper.

Refs #20913

* fix(agent-hooks): roll back a failed aside rename and keep an installed hook exe

Design-review follow-up on the native hook (#20913).

- If the running copy was moved to orca-agent-hook.exe.old but the new
  file then could not take its name, the copy is renamed back before the
  error is rethrown, so settings.json never points at a missing exe. The
  rename helper now owns the `.old` name, next to the comment that
  explains it; a mocked-fs unit test covers both the sync and async paths.
- A failed write, or a build with no bundled exe, keeps registering a copy
  that is already installed (every build knows `claude`), so a dev checkout
  and an installed build no longer flip settings.json between forms.
- The claude-hook.exe migration is gone: no release ever shipped that
  name, so the legacy sweep, the `.exe` matcher needle and their test are
  removed.

Refs #20913

* fix(agent-hooks): derive the native hook's agent with win32 path rules

getWindowsNativeHookAgent took the host's basename of the managed script
path, so on a POSIX host (Linux CI) a Windows path was not split and the
registered command became `orca-agent-hook.exe C:\...\claude`. It now takes
the path and applies path.win32.basename itself. The install destination
and the `.old` aside stay on host paths: they name files the host opens.

Refs #20913

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-12 16:09:30 -07:00

158 lines
4.0 KiB
TOML

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "orca-agent-hook"
version = "0.0.0"
dependencies = [
"winresource",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "syn"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml"
version = "1.1.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
[[package]]
name = "unicode-ident"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
[[package]]
name = "winresource"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
dependencies = [
"toml",
"version_check",
]