With the ACP bridges (@agentclientprotocol/claude-agent-acp,
@agentclientprotocol/codex-acp) bundled with the app at pinned versions
and resolved ahead of user installs, three pieces of machinery are dead
code for those two runtimes:
- The codex 0.16.x version gate: probe_codex_acp_major_version,
codex_adapter_availability / codex_adapter_is_outdated, and the
AdapterOutdated availability status (Rust enum variant plus the
frontend "adapter_outdated" union member and all its UI branches).
The bundled adapter is always the pinned 1.x package, so probing for
the deprecated @zed-industries/codex-acp package has no trigger left.
- The EEXIST uninstall-then-reinstall two-step in plan_adapter_install,
which existed only to swap the deprecated codex package for the new
one. The plan is now the simple missing -> catalog-commands mapping,
and the function drops its runtime_id parameter.
- The in-app `npm install -g` flow for the two bundled bridges: their
catalog entries now carry empty adapter_install_commands and a hint
that the adapter ships with the Buzz desktop app. Goose keeps its
npm install flow untouched — its adapter is not bundled.
cli_login_requirements now classifies availability purely via
classify_runtime (which resolves bundle-first), replacing the codex
version-probe special case.
The configNudge validator rejects the retired "adapter_outdated"
literal, so stale nudge JSON emitted by an older app version cannot
render a card the current UI has no branch for (regression test
inverted accordingly).
This is a Buzz-specific retirement enabled by the bundling series; the
donor series (block/builderbot apps/staged, berd) had no equivalent
version gate to remove. File-size ledger entries for discovery.rs
(1178 -> 1056), discovery/tests.rs (1029 -> 825), and readiness.rs
(1754 -> 1583) are ratcheted down to bank the deletions, per the note
left in the bundled-resolution commit.
Verification:
- cargo test --lib (desktop/src-tauri): 1381 passed, 0 failed
- cargo clippy --lib --tests -D warnings: clean
- cargo fmt --check: clean
- tsc --noEmit: clean; biome check src: no new diagnostics
- pnpm test (desktop): 2744 passed, 0 failed
- node scripts/check-file-sizes.mjs: pass with ratcheted limits
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
The bundled ACP bridges are shell shims that exec node; without a
suitable Node.js on the spawn PATH the first agent session dies with a
bare exit 127. Surface the requirement in the Doctor panel instead:
- New managed_agents::node_runtime module reads the staged
resources/acp/node-runtime.json manifest (one entry per npm-sourced
bridge, each with its own required Node major), resolves node from
the same augmented PATH the agent spawn and CLI auth probes use (so
the check cannot disagree with what the wrapper shims find at spawn
time), probes `node -p process.versions.node` with a 10s timeout,
and reports pass/warn with a per-bridge satisfied/unmet/unknown
verdict list. A missing manifest keeps the check silent (no
npm-sourced bridges bundled); an unreadable one warns instead of
hiding a packaging break.
- New check_acp_node_runtime Tauri command plus a Doctor panel section
(message, node path, per-bridge requirements, Install Node.js fix
link on warn) that the Re-run button refreshes alongside the runtime
rows. The e2e mock bridge grows a nodeRuntimeCheck fixture knob.
- tokio gains the "process" feature for the async node probe.
Readiness gating for bundled bridges needs no code here: Buzz's
classify_runtime already reports adapter_missing when find_command
(which prefers the bundle since the previous commit) cannot resolve
the bridge, and cli_login_requirements resolves the adapter the same
way — so a broken bundle reads not_installed instead of ready. This
is the behaviour squareup/berd 17c5e9e5 had to add explicitly.
Ports block/builderbot apps/staged adea4017 (node-runtime doctor
manifest), itself a port of squareup/berd 07087303.
Verification:
- cargo test --manifest-path desktop/src-tauri/Cargo.toml --lib:
1392 passed, 0 failed, 11 ignored (10 new tests: version parsing,
requirement labels, all four check states, manifest missing/invalid/
loaded including the exact staging-script JSON shape)
- cargo clippy --all-targets: clean; cargo fmt --check: clean
- desktop: tsc --noEmit clean; biome clean; pnpm test 2744 passed
- playwright doctor-states.spec.ts (smoke): 8 passed, including new
06-node-runtime-pass / 07-node-runtime-warn /
08-node-runtime-hidden-when-not-bundled
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Register the staged ACP tools bin dir (BUZZ_ACP_TOOLS_DIR dev override,
else the resources/acp/bin Tauri resource dir) once at app setup, and
consult it everywhere bridge commands are resolved:
- New managed_agents::acp_tools module: OnceLock registration plus
command_in_bundled_dir, which resolves bare command names only —
path-like commands name a specific binary the user picked and are
never redirected into the bundle (Path::join with an absolute path
would replace the bundled dir entirely).
- resolve_command_uncached consults the bundled dir first, so the
discovery sweep, readiness find_command, and spawn-time agent-command
resolution (BUZZ_ACP_AGENT_COMMAND) all prefer the pinned bridges.
Registration runs at the top of .setup() because resolutions are
cached for the app lifetime.
- build_augmented_path gains the bundled dir as its highest-priority
segment, covering both the agent spawn PATH and the CLI auth-probe
PATH, and now joins entries best-effort: an entry embedding the PATH
separator (legal in macOS paths) is dropped with a log line instead
of collapsing the entire augmented PATH to None.
The donor's GOOSE_SEARCH_PATHS pinning is deliberately not ported —
Buzz spawns bridges via its own buzz-acp harness, which takes the
resolved command from BUZZ_ACP_AGENT_COMMAND and the augmented PATH.
The discovery.rs file-size ratchet is bumped 1171 -> 1178 for the new
sweep check; the codex version-gate retirement later in this series
shrinks the file and ratchets it back down.
Ports block/builderbot apps/staged 55eb2772 (resolve bundled ACP tools
at runtime) and afa524e7 (best-effort PATH join), themselves ports of
squareup/berd 2add3727 and bb912c96.
Verification:
- cargo test --manifest-path desktop/src-tauri/Cargo.toml --lib:
1382 passed, 0 failed, 11 ignored (14 new tests cover env-override
precedence, bare-name-only bundled resolution, executable checks,
bundled-dir PATH priority, and un-joinable entry handling)
- cargo clippy --all-targets: clean; cargo fmt --check: clean
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Buzz desktop spawned the claude-agent-acp and codex-acp bridges from
whatever the user had installed globally — an unpinned `npm install -g`
surface with no integrity checking, which produced stale-bridge drift
(the deprecated @zed-industries/codex-acp 0.16.x gate) and
missing-tool failures. Bundle both bridges as app resources instead,
pinned per target:
- desktop/acp-tools.lock.json pins @agentclientprotocol/claude-agent-acp
0.58.1 and @agentclientprotocol/codex-acp 1.1.2 (npm `latest` at time
of commit) for the four supported targets, with integrity hashes and
Block Artifactory tarballs for the package, its claude-agent-sdk /
@openai/codex dependency, and the per-target native package.
- desktop/scripts/update-acp-tools-lock.mjs regenerates the lock from
the registry's `latest` dist-tags, failing loudly on any unresolvable
package — never silently pinning an older version. Ranged
dependencies (codex-acp's ^0.144.0) resolve to the highest matching
version when `npm view` returns an array.
- desktop/scripts/ensure-acp-tools.sh installs the locked tools into a
shared dev cache (~/Library/Caches/buzz-dev/acp-tools), validates
versions + integrity against the lock, and stamps staged binaries
next to the shared bin dir so any lock change — including a revert —
forces a re-stage; binaries no longer in the lock are pruned.
- desktop/scripts/prepare-acp-tools-resource.sh stages the vendored npm
trees + node wrapper shims into desktop/src-tauri/resources/acp,
writes the node-runtime.json manifest for the app's Node.js doctor
check, and ad-hoc signs every nested Mach-O (file(1) scan — the codex
package vendors rg, zsh, and codex-code-mode-host beyond the main
CLIs) so Gatekeeper doesn't kill them in local builds.
- desktop/scripts/lib/acp-node-wrapper.sh is the single wrapper-shim
generator shared by both scripts, so the dev-cache and bundled
wrappers (and the Node major they enforce) cannot drift.
- Wiring: `just dev` / `just staging` stage the resources and export
BUZZ_ACP_TOOLS_DIR; `just desktop-release-build` stages per target
before `tauri build`; `just bump-acp-tools` reruns the lock updater;
tauri.conf.json bundles resources/acp; staged artifacts are
gitignored with a .gitkeep placeholder.
Runtime resolution of the staged dir follows in the next commit. The
sprout-releases internal pipeline will need the same staging step
before its `tauri build`; that lives in a separate repo.
Ports the build-time half of the Staged implementation in
block/builderbot#876 (branch commits 16b115a7 bundle feature, f5c6bba9
re-stage after lock revert, 288fa7eb shared node wrapper lib, adea4017
node-runtime manifest, 5124302a sign all nested Mach-Os, de6a9782
ranged-dependency updater fix), itself ported from squareup/berd
f07df1d2 + 1db993fb + 24d7518b + 07087303 + 737e33a5.
Verified: update-acp-tools-lock.mjs regenerated the lock against the
Block registry (byte-identical pins to the donor lock; both packages
confirmed at npm `latest`); fresh stage installs both tools and the
staged wrappers report 0.58.1 / 1.1.2; no-op re-run performs zero npm
installs; a stamp/lock mismatch forces a re-stage and stray binaries
are pruned; all five staged Mach-Os pass `codesign --verify`; cargo
check on desktop/src-tauri passes with the new resources entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>