feat(desktop): discover and spawn ACP harnesses installed in WSL on Windows - #3679
feat(desktop): discover and spawn ACP harnesses installed in WSL on Windows#3679spfcraze wants to merge 3 commits into
Conversation
d2afdfa to
0458c49
Compare
|
This PR addresses #3122 — the canonical WSL-harness issue. To map its breakage list onto this change:
Verification is on a real Win11 + WSL2 box with Hermes Agent 0.19.0 (handshake through |
|
I tested this PR end-to-end on Windows 10 + WSL2 against Buzz Desktop For an unknown/custom command, if crate::managed_agents::resolve_command(&effective.effective_command).is_none()
&& crate::managed_agents::wsl::probe_wsl_command(&effective.effective_command).is_none()
{
return vec![Requirement::MissingBinary {
command: effective.effective_command.clone(),
}];
}With only that additional WSL fallback in readiness, I cross-built the Windows candidate and completed a real managed-agent turn:
So this is an independent end-to-end confirmation that the approach works with Codex ACP too, once readiness uses the same WSL resolution as discovery/spawn. One rebase follow-up: PR #6904 has since landed and adds the bounded command runner plus forced-discovery cache invalidation. This PR's I'm happy to submit the small readiness/cache/test changes to this branch if you accept contributions there, or help prepare a rebased successor against current |
…indows When a preset or custom harness command is missing from the Windows PATH, harness discovery now probes the default WSL distribution (~/.local/bin, ~/bin, /usr/local/bin, then the distro PATH) and marks the harness Available with a wsl:// binary path. At spawn, Desktop hands the in-distro path to buzz-acp (BUZZ_ACP_AGENT_WSL_PATH, optional BUZZ_ACP_AGENT_WSL_DISTRO), which wraps the agent launch through wsl.exe -e and forwards all agent-intended env vars across the boundary via a computed WSLENV merge. BUZZ_ACP_AGENT_COMMAND keeps the bare command identity so per-runtime defaults (e.g. HERMES_ACP_SKIP_CONFIGURED_MCP) apply unchanged. This unblocks running Hermes Agent (Linux-first install, hermes-acp) from Buzz Desktop on Windows without a hand-authored custom harness. Docs: docs/windows-wsl-harnesses.md Signed-off-by: Frank Saunders <frank@users.noreply.github.com>
buzz-acp is spawned from ~/.buzz, so session/new carried a Windows drive path (C:\Users\x\.buzz) into the distro, where it does not exist. Agents store the session cwd and use it as the edit-approval root and workspace grounding (block#3122's first failure after initialize). When the agent is WSL-targeted, translate drive-letter paths to /mnt/<drive>/… before the cwd leaves the process; UNC, relative, and POSIX paths pass through untouched. Signed-off-by: Frank Saunders <frank@users.noreply.github.com>
…edback Address al-osokin's review on PR block#3679: 1. Readiness: collect_missing_requirements now also probes the default WSL distro (probe_wsl_command) before reporting a custom/unknown command as MissingBinary, so a discovered-but-wsl-only harness (e.g. hermes-acp in WSL) is no longer shown as Not installed. Mirrors the discovery + spawn path's WSL fallback. 2. Cache invalidation: add wsl::clear_wsl_cache() and call it on the same forced-discovery invalidation seams as clear_resolve_cache (commands/agent_discovery.rs install path + forced_single_flight.rs and the install path), so a harness installed inside the distro after a prior miss is found on the next forced run — fixing 'Check again' not discovering a recently-installed WSL command. 3. Bounded probe: route the WSL probe through the discovery bounded runner (bounded_command::output_with_timeout, re-exported under cfg(windows)) with a 30s wall-clock deadline, so a hung distro boot cannot stall discovery forever, matching the other discovery spawns. Author: spfcraze
e5feb40 to
0bc8cfa
Compare
🔐 Codex Security Review
|
|
Rebased the branch onto current Updates included:
Build + clippy clean; wsl 6/6, readiness 53/53, discovery 125/125 tests pass. Re-running the Codex security review on this exact range would be appreciated. |
Problem
On Windows, Buzz Desktop discovers ACP harnesses by resolving their command
on the Windows PATH. Several supported harnesses are commonly installed
inside WSL, not on the Windows host — Hermes Agent (
hermes-acp) is thecanonical case: it is a Linux-first install, so on a Windows machine running
Hermes in WSL, Doctor reports Not installed and the harness cannot be
started, even though it works perfectly inside the distro.
There was no WSL story anywhere in the harness discovery/spawn path (the only
WSL awareness in the tree is
is_windows_apps_alias, which exists to avoidWindowsApps stub launchers, issue #2328).
What this PR does
Buzz now falls back to the default WSL distribution when a preset or
custom harness command is missing from the Windows PATH. This directly
addresses the WSL breakages catalogued in
#3122 (env severed at the
wsl.exeboundary, untranslated cwd, harness invisible to discovery):Discovery (
managed_agents/wsl.rs, new) — probes the default distrovia
wsl.exe -e sh -c …, checking~/.local/bin,~/bin,/usr/local/bin, then the distro PATH (command -v). Positive resultsmark the harness Available with a
wsl://…binary path in Doctor.Resolutions are cached for the app lifetime (mirrors
resolve_command'scache contract). Only bare command names are ever probed — paths are
rejected up front (no injection surface, no nonsense lookups).
Spawn contract (
runtime.rs→buzz-acp) — when the host PATHresolution failed but the WSL probe succeeded, Desktop sets
BUZZ_ACP_AGENT_WSL_PATH(absolute in-distro path) and optionallyBUZZ_ACP_AGENT_WSL_DISTROon thebuzz-acpchild.BUZZ_ACP_AGENT_COMMANDkeeps the original bare identity(
hermes-acp), so all per-runtime defaults keyed on the command name(
default_agent_env,default_agent_args) apply unchanged.Spawn wrapping (
buzz-acp/src/acp.rs) —AcpClient::spawnreads thecontract and launches
wsl.exe [-d distro] -e <linux_path> <args>insteadof the bare command. WSL interop bridges the stdio pipes; the ACP/NDJSON
channel is byte-transparent. Contract values are validated (absolute POSIX
path required); a set contract on a non-Windows host is ignored with a
warning.
Environment forwarding — wsl.exe children only import Windows-side
variables listed in
WSLENV.buzz-acpbuilds the forward list fromevery agent-intended key (per-runtime defaults like
HERMES_ACP_SKIP_CONFIGURED_MCP=1, persona env likeGOOSE_PROVIDER,merged
CODEX_CONFIG), merging into any pre-existingWSLENVwithoutduplicating flagged entries (
GOPATH/pstyle). Without this, agents inWSL silently lose their env — for Hermes specifically this re-introduces
the [Bug] Hermes model discovery times out in Buzz Desktop v0.5.0 #3355 startup-stall the skip flag exists to prevent.
Session cwd translation —
buzz-acpis spawned from~/.buzz, sosession/newwould otherwise carryC:\Users\<u>\.buzzinto the distro.For WSL-targeted agents,
AcpClient::spawn-adjacentsession_cwd_for_wsltranslates drive-letter paths to
/mnt/<drive>/…before the cwd reachesthe
[Workspace]grounding prompt and the agent's session state (Hermesuses it as the edit-approval root). UNC, relative, and POSIX paths are
left untouched. This is BYOH cannot reach a WSL-hosted harness on Windows: env is severed at the wsl.exe boundary and cwd is never translated #3122's "first failure after initialize".
Known remaining follow-ups from #3122 (deliberately out of scope here, noted
in the issue's item list): skill/nest placement for a distro
$HOMEandteardown that reaps the in-distro process rather than only the
wsl.exeshim.
Verification (real, on a Windows 11 + WSL2 machine)
Environment: Windows 11, WSL2
Ubuntu, Hermes Agent v0.19.0 at/home/rat/.local/bin/hermes-acp, Buzz Desktop installed atC:\Users\ratz\AppData\Local\Buzz\.hermes-acpabsent from Windows PATH → Doctor showsHermes Not installed.
C:\Windows\System32\wsl.exe -e /home/rat/.local/bin/hermes-acpfrom aWindows parent with piped stdio returns a byte-clean ACP
initializeresponse (
agentInfo: hermes-agent 0.19.0,protocolVersion: 1).WSLENV,hermes-acphangs beforeinitialize(the [Bug] Hermes model discovery times out in Buzz Desktop v0.5.0 #3355 MCP-startup stall). WithHERMES_ACP_SKIP_CONFIGURED_MCP=1set Windows-side and forwarded viaWSLENV, initialize answers immediately.documented in
docs/windows-wsl-harnesses.md(same mechanism this PRautomates) loads and works on the current released build.
Tests
buzz-acp: 8 new unit tests (build_wsl_wrapvalidation,prefix_args,merge_wslenvorder/dup/flag handling).cargo test -p buzz-acp: 648passed, 0 failed (full suite, Linux).
cargo clippy -p buzz-acp --all-targets: clean.wsl.rs: 6 unit tests (probe script contract pins~/.local/binbefore PATH, no login shell; stdout parsing; display path;
probeable-command validation incl. metacharacter rejection; negative-result
caching) — pass on Linux. Process-spawning probes are
cfg(windows)-gated;the Windows-only code paths (
resolve_wsl_exe, probe spawn) werecompile-verified for
x86_64-pc-windows-msvc.cargo check(Linux) ondesktop/src-tauri: green, zero warnings.cargo test managed_agents::ondesktop/src-tauri: 906 passed, 0failed (includes the 6 new
wsl.rstests + all pre-existing discovery,custom-harness, spawn-hash, and readiness tests).
x86_64-pc-windows-msvcworkspace check was not runnable in the devcontainer (crypto
-syscrates need a Windows C toolchain); Windows CIcovers it. Every Windows-gated line added by this PR is either
std-only Rust (verified via the standalone msvc-target compile described
above) or mirrors existing patterns in
git_bash.rs.preset_catalog_entry) —unchanged, still green.
Notes for reviewers
install scripts) still assume host-native installs — a follow-up could
extend the same cached-resolution mechanism to their probes.
on both the discovery and spawn paths. WSL-unavailable machines
short-circuit on
wsl.exeresolution.WslCommandResolution.distrois plumbed through the wholecontract but always
Nonetoday (default distro only) — multi-distrodiscovery needs no contract change.
pub(crate)/ private). Docs added:docs/windows-wsl-harnesses.md.