Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions crates/buzz-acp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,65 @@ Each channel has at most one prompt in flight. Multiple channels can be processe

> **Note:** On startup, the harness replays all unprocessed @mentions since the last run. Expect a burst of activity if there are stale events in the channel.

## Bring Your Own Harness (BYOH)

Buzz Desktop supports registering any ACP-speaking agent tool as a selectable runtime without a PR.

### How it works

**Tier-1 — compiled-in runtimes** (Goose, Claude Code, Codex, Buzz Agent): have auto-installers, auth probes, and first-class onboarding. Their IDs (`goose`, `claude`, `codex`, `buzz-agent`) are reserved and cannot be overridden.

**Tier-2 — preset catalog** (Cursor, Oh My Pi, Grok Build, OpenCode, Kimi Code, Amp, Hermes Agent, OpenClaw): static `HarnessDefinition` entries in `desktop/src-tauri/src/managed_agents/discovery.rs` (`PRESET_HARNESSES`). They are always present in the runtime catalog, PATH-probed for availability, not editable or deletable by the user. Displayed with bundled logos; if not installed, a docs link appears instead.

> **Note — OpenClaw:** `openclaw acp` is a Gateway-backed bridge; PATH availability shows "Available" even when the OpenClaw Gateway daemon is not running. This is expected tier-2 semantics (same class as a preset with unconfigured auth). The Gateway URL is configured via `OPENCLAW_GATEWAY_URL` (or the equivalent env var from OpenClaw's docs) — set it in the agent's **env vars** in Edit Agent, not in the definition env (the preset definition carries no env entries). Note that `openclaw acp` executes tools inside the Gateway daemon, not the Desktop process, so Desktop-injected `BUZZ_*` env vars do NOT reach the execution locus unless you also set them on the Gateway's own environment.

**Tier-3 — user custom harnesses**: JSON files in `<app-data>/custom_harnesses/` that the user can create from the Settings UI or drop in directly. Each file describes one harness — no install scripts.

### Custom harness JSON schema

```json
{
"id": "my-agent",
"label": "My Agent",
"command": "my-agent-bin",
"args": ["acp"],
"env": {
"MY_AGENT_MODE": "acp"
},
"installInstructionsUrl": "https://example.com/docs",
"installHint": "Download from example.com"
}
```

Fields:
- `id` — `[a-z0-9_][a-z0-9_-]*` (used as the runtime picker value and file name)
- `label` — human-readable name shown in the UI
- `command` — the executable name or absolute path (must be non-empty)
- `args` — optional default CLI arguments (array); instance-level args override this when non-empty
- `env` — optional environment variables injected at spawn time (definition env is a floor; user/persona/global env overrides it; Buzz-reserved keys like `BUZZ_MANAGED_AGENT` are always stripped and cannot be overridden)
- `installInstructionsUrl` / `installHint` — shown when the binary is not on PATH

Invalid files (bad JSON, unknown id, empty command) are skipped with a warning and do not break discovery for other entries.

### Security guarantees

- No install shell commands in preset or custom definitions — only the user's own PATH is consulted.
- `can_auto_install` is always `false` for preset and custom entries.
- No user-supplied icon URLs — icons are bundled assets keyed by id in `RuntimeIcon.tsx`.
- `BUZZ_MANAGED_AGENT` and other Buzz identity keys cannot be overridden by `env` in a custom definition; they are stripped before merging.

### Adding a preset (contributor guide)

To add a new runtime to the tier-2 gallery:

1. **Verify the ACP entrypoint** from the vendor's own documentation — do not rely on a PR description alone. Test with the actual binary.
2. **Add a `HarnessDefinition` entry** to the `PRESET_HARNESSES` slice in `desktop/src-tauri/src/managed_agents/discovery.rs`. Fill `id`, `label`, `command`, `args`, `install_instructions_url`, `install_hint`. Leave `env` empty unless the harness requires a specific env var to enable ACP mode.
3. **Add the preset id to `BUILTIN_IDS`** in `desktop/src-tauri/src/managed_agents/custom_harnesses.rs` so custom JSON files cannot shadow it.
4. **Add a bundled logo** (64×64 PNG or optimised SVG) to `desktop/public/harness-logos/<id>.png` and add a corresponding entry to `PRESET_LOGOS` in `desktop/src/features/onboarding/ui/RuntimeIcon.tsx`.
5. Run `cargo test --lib` and `just desktop-typecheck` to verify everything compiles.

The built-in `BUILTIN_IDS` set (`goose`, `claude`, `codex`, `buzz-agent`, and all current preset ids) is the reserved namespace; every other id is available for custom harnesses.

## Using Any ACP Agent

The harness works with any agent that implements the [ACP spec](https://agentclientprotocol.com/) over stdio. The requirements are:
Expand Down
Binary file added desktop/public/harness-logos/amp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/public/harness-logos/cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/public/harness-logos/grok.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/public/harness-logos/kimi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/public/harness-logos/omp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions desktop/public/harness-logos/opencode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 70 additions & 7 deletions desktop/scripts/check-file-sizes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,20 @@ const overrides = new Map([
// record_provider param + applies persona_field_with_record_fallback. +5 lines.
// global-agent-config: spawn_agent_child loads global config and merges as
// lowest env layer (+8 lines). Queued to split.
["src-tauri/src/managed_agents/runtime.rs", 2216],
// +2: BYOH orphan-sweep fix — `!belongs && !has_buzz_marker` OR-gate replaces
// the old AND-gate so custom harness processes are not silently leaked on crash.
// +27: BYOH F4 fix — extract shared `buzz_sweep_owns_process` predicate, fix
// Linux AND-gate in sweep + orphan collectors, 4 production predicate tests.
// +12: BYOH F2 — record_agent_command / effective_agent_command check loaded
// harness registry for preset/custom ids after static-builtin lookup.
// +61: BYOH pass-2 — I2 (spawn_agent_child env+args from definition), I3
// (valid_agent_runtime_receipt uses buzz_sweep_owns_process marker-only),
// I6 (cross-platform buzz_sweep_owns_process, drop #[cfg(unix)]), +2 new
// receipt-path collector-decision tests.
// +2: BYOH Phase A — resolve_effective_harness_descriptor single typed resolver;
// C-9 injectable sweep predicates (kill_stale_tracked_processes_with +
// valid_agent_runtime_receipt_with) with 5 discriminating tests.
["src-tauri/src/managed_agents/runtime.rs", 2326],
// config-bridge setup-payload env-boundary fix adds readiness wiring in
// spawn_agent_child; load-bearing security fix, queued to split.
["src-tauri/src/managed_agents/config_bridge/reader.rs", 1016],
Expand Down Expand Up @@ -177,11 +190,15 @@ const overrides = new Map([
// Windows Doctor install fix: cli_install_commands_windows field added to test stubs.
// team-instructions-first-class: ManagedAgentRecord fixture gains the new
// team_id field (+1 line).
["src-tauri/src/managed_agents/readiness.rs", 1765],
["src-tauri/src/managed_agents/readiness.rs", 1863],
// Windows PATH-correctness fix: 3 #[cfg(windows)] test functions covering
// .cmd shim rejection, .bat shim rejection, and .exe acceptance for
// configure_runtime_cli (fix #2397). Test-only growth; queued to split.
["src-tauri/src/managed_agents/runtime/tests.rs", 1041],
// +34: BYOH custom-harness sweep condition unit tests — 3 tests validating
// the OR-gate fix for custom-binary orphan cleanup.
// +26: BYOH pass-2 I3 — 2 collector-decision tests for receipt path
// ownership (valid_agent_runtime_receipt uses buzz_sweep_owns_process).
["src-tauri/src/managed_agents/runtime/tests.rs", 1320],
// applyWorkspace reposDir parameter plus the validateReposDir binding,
// threaded through Tauri invokes for configurable repos_dir, plus the
// harness-persona-sync `harnessOverride` create-input bit — load-bearing
Expand Down Expand Up @@ -231,7 +248,12 @@ const overrides = new Map([
// doc comment) and AgentTeam/CreateTeamInput/UpdateTeamInput.instructions
// (+3) — the new team-id spawn link and the runtime-layered instructions
// field.
["src/shared/api/types.ts", 1047],
// byoh-env-roundtrip: AcpRuntimeCatalogEntry.definitionEnv field + JSDoc
// (+12 lines) so the edit form can read back existing env vars on save.
// Load-bearing correctness fix. Queued to split.
// +2: AcpRuntimeCatalogEntry.requiresExternalCli field added by main
// (#2680) to indicate runtimes that need a separate CLI install.
["src/shared/api/types.ts", 1051],
// readiness-gate: PersonaDialog.tsx threads computeLocalModeGate +
// requiredCredentialEnvKeys + RequiredFieldLabel so the "New agent" dialog
// shows required markers and credential amber rows (parity with
Expand Down Expand Up @@ -296,7 +318,34 @@ const overrides = new Map([
// Buzz-managed Node path helpers and resolution tests moved to
// managed_node_paths.rs and discovery/tests/managed_path_resolution.rs;
// ratcheting 1366 -> 1392 after adding the managed-path probes to discovery.
["src-tauri/src/managed_agents/discovery.rs", 1393],
// +17: BYOH custom harness catalog merge phase-3 — append custom definitions
// from custom_harnesses_dir with PATH-probe availability; source tagging.
// +148: BYOH F2/F3 — PRESET_HARNESSES static data (6 presets), Phase 2.5 in
// discover_acp_runtimes_from (PATH-probe each preset, build catalog entries,
// populate loaded-harness registry), record/effective command resolution now
// checks loaded registry for preset/custom ids. Queued to split presets out.
// +3: BYOH F5 — seen_ids rejects preset/builtin collisions from custom files.
// +79: BYOH pass-2 C1 — 4 registry lifecycle tests (warm→spawn, delete→
// dangling, immediate save+start, edit with rename); try_record_agent_command
// typed error for dangling ids wired into spawn; readiness/spawn_hash now
// include definition env floor.
// +7: BYOH pass-2 I2 env round-trip — definition_env field populated in
// custom catalog entries + 2 discriminating tests (custom env preserved,
// builtin env empty). Load-bearing edit round-trip fix.
// +16: BYOH scope addition — Hermes Agent + OpenClaw preset entries (two
// data-only PresetHarness structs; no new logic or test functions).
// +29: rebase over main (#2680) — discover_acp_runtime_phase1 extracted
// helper + discover_acp_runtime_availability; both load-bearing for
// post-install verification. Semantic composition with BYOH changes.
["src-tauri/src/managed_agents/discovery.rs", 1715],
// BYOH — save_custom_harness_to_dir (backup-swap atomic write) + save_and_warm /
// delete_and_warm (persist-mutex serialization for concurrent-safe registry
// refresh, B-6). Also: id/collision/load/registry tests (from the file base) +
// B-4 real persistence tests (create, same-id edit, rename, backup cleanup) +
// B-3 env validation boundary tests (malformed key, reserved shape, NUL,
// size limit, ownership marker). Load-bearing correctness/security coverage;
// queued to extract helper module once the feature stabilizes.
["src-tauri/src/managed_agents/custom_harnesses.rs", 1045],
// rebase over codex-acp-package-swap: its version-probe tests union with the
// doctor-install-reliability nvm/login-shell/semver tests — each side alone
// stayed under the 1000 default; the union exceeds it.
Expand All @@ -307,7 +356,11 @@ const overrides = new Map([
// None regression, .cmd shim resolution, no-git-bash error hint.
// +32: deterministic .cmd resolver + no-registry + install_shell_from tests.
// Managed-path resolution test split to discovery/tests/managed_path_resolution.rs.
["src-tauri/src/managed_agents/discovery/tests.rs", 1273],
// +227: BYOH pass-2 C1 — 4 registry lifecycle tests (warm→spawn, delete→dangling,
// immediate save+start, edit with rename) added to discovery/tests.rs.
// +64: BYOH pass-2 I2 env round-trip — 2 discriminating tests proving custom
// catalog entries carry definition_env and builtins do not.
["src-tauri/src/managed_agents/discovery/tests.rs", 1576],
// identity-import-keyring: the identity resolution state machine's behavioral
// matrix (46 tests over FakeIdentityStore — probe × marker × file cells,
// adoption / read-back-corruption / marker-failure arms, recovery-mode
Expand Down Expand Up @@ -500,7 +553,17 @@ const overrides = new Map([
// Includes unit tests for detection, routing, and -Command body preservation.
// +16: test_powershell_command_goose_catalog_dequoted proves the \$→$ escape
// fix for the Goose Windows installer (PR #2680 interaction with #2750).
["src-tauri/src/commands/agent_discovery.rs", 1826],
// +126: BYOH — save_custom_harness (validate, atomic write, return entry) +
// delete_custom_harness (id-guard, builtin reject, remove file) commands;
// discover_acp_providers updated to pass AppHandle + custom_harnesses dir.
// +30: BYOH F5 — atomic-write-file dep, original_id rename/delete support.
// +13: BYOH pass-2 C1 — warm_harness_registry_from_dir call in save and
// delete commands now verifies transactional registry refresh.
// +2: BYOH pass-2 I2 env round-trip — definition_env carried through save
// return value so the frontend immediately has the updated env.
// +1: rebase over main (#2680) — requires_external_cli: false added to
// save_custom_harness catalog entry construction (new required field).
["src-tauri/src/commands/agent_discovery.rs", 2038],
// draft-persistence predicate: submit-time `loadDraft` check + inline comment
// + deps-array entry in submitMessage closes the never-persisted-boundary
// defect (Thufir Pass-3 finding). Load-bearing correctness fix; queued to
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
/gen/schemas

# Sidecar binaries (built by scripts/bundle-sidecars.sh)
/binaries/
/binaries
1 change: 1 addition & 0 deletions desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ rubato = "3.0"
audioadapter-buffers = "3.0"
tempfile = "3"
strip-ansi-escapes = "0.2"
tracing = "0.1"

[dev-dependencies]
# `test-util` enables tokio's paused-clock (`start_paused`) so the relay
Expand Down
Loading
Loading