Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 4 additions & 5 deletions crates/buzz-acp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ Buzz Desktop supports registering any ACP-speaking agent tool as a selectable ru

**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.
**Tier-2 — preset catalog** (Cursor, Oh My Pi, Pi, Grok Build, OpenCode, Kimi Code, Amp, Hermes Agent, OpenClaw): static `HarnessDefinition` entries in `desktop/src-tauri/src/managed_agents/discovery/presets.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.

Expand Down Expand Up @@ -327,10 +327,9 @@ Invalid files (bad JSON, unknown id, empty command) are skipped with a warning a
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`. Record the source and license in `desktop/public/harness-logos/CREDITS.md`. Only bundle a mark whose upstream license permits redistribution; skipping this step is caught by `presetLogos.test.mjs`, which asserts every `PRESET_HARNESSES` id has a mapped logo that exists on disk.
5. Run `cargo test --lib` and `just desktop-typecheck` to verify everything compiles.
2. **Add a `PresetHarness` entry** to the `PRESET_HARNESSES` slice in `desktop/src-tauri/src/managed_agents/discovery/presets.rs`. Fill `id`, `label`, `command`, `args`, `install_instructions_url`, `install_hint`, and `underlying_cli` when the command wraps a separately installed CLI. Preset ids are automatically reserved so custom JSON files cannot shadow them.
3. **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`. Record the source and license in `desktop/public/harness-logos/CREDITS.md`. Only bundle a mark whose upstream license permits redistribution; skipping this step is caught by `presetLogos.test.mjs`, which asserts every `PRESET_HARNESSES` id has a mapped logo that exists on disk.
4. 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.

Expand Down
1 change: 1 addition & 0 deletions desktop/public/harness-logos/CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ license permits redistribution.
| `hermes.png` | [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) | `6ad632b` | MIT © 2025 Nous Research | `website/static/img/logo.png` | Cropped the baked-in border frame, padded to square, resized to 64×64, quantised to a 16-colour palette |
| `openclaw.svg` | [openclaw/openclaw](https://github.com/openclaw/openclaw) | `b06f40a` | MIT © 2026 OpenClaw Foundation | `ui/public/favicon.svg` | Removed the SMIL animation elements (renders the upstream rest pose statically — verified pixel-identical to the upstream frame at t=0); minified paths |
| `omp.svg` | [can1357/oh-my-pi](https://github.com/can1357/oh-my-pi) | `667111575ebba136dadfd6989379e7f67e0d40d9` | MIT © 2025 Mario Zechner; © 2025–2026 Can Bölük | `assets/icon.svg` | None |
| `pi.svg` | [earendil-works/pi-website](https://github.com/earendil-works/pi-website) | `2f5e410b97474d0a34ec2500aa1aa58d6c3f992c` | MIT © 2026 Earendil Inc. and contributors | `src/favicon.svg` | None |
| `kimi.png` | [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) | `4a550effdfcb29a25a5d325bf935296cc50cd417` | Apache-2.0; NOTICE: Kimi Code CLI © 2025 Moonshot AI | `web/public/logo.png` | None |
| `grok.svg` | [SpaceXAI brand guidelines](https://x.ai/legal/brand-guidelines) | Retrieved 2026-07-25 | xAI Brand Guidelines: marks may be used to accurately refer to xAI or its services; logos must be used exactly as provided | `SpaceXAI_Grok_Assets.zip` → `Grok_Logomark_Dark.svg` | None |

Expand Down
21 changes: 21 additions & 0 deletions desktop/public/harness-logos/pi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions desktop/src-tauri/src/managed_agents/discovery/presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ pub(super) fn preset_catalog_entry(
}

pub(super) const PRESET_HARNESSES: &[PresetHarness] = &[
PresetHarness {
id: "pi",
label: "Pi",
command: "pi-acp",
args: &[],
install_instructions_url: "https://github.com/svkozak/pi-acp",
install_hint: "Buzz talks to Pi through the pi-acp adapter. Install Pi with `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`, then install the adapter with `npm install -g pi-acp`.",
underlying_cli: Some("pi"),
},
PresetHarness {
id: "devin",
label: "Devin",
Expand Down Expand Up @@ -347,6 +356,48 @@ mod tests {
assert_eq!(entry.source, HarnessSource::Preset);
}

#[test]
fn pi_preset_uses_zero_arg_adapter_and_reports_missing_component() {
let preset = PRESET_HARNESSES
.iter()
.find(|preset| preset.id == "pi")
.expect("Pi preset should be present");

assert_eq!(preset.label, "Pi");
assert_eq!(preset.command, "pi-acp");
assert!(preset.args.is_empty());
assert_eq!(preset.underlying_cli, Some("pi"));

let available = preset_catalog_entry(preset, |command| match command {
"pi-acp" => Some(PathBuf::from("/usr/local/bin/pi-acp")),
"pi" => Some(PathBuf::from("/usr/local/bin/pi")),
_ => None,
});
assert_eq!(available.availability, AcpAvailabilityStatus::Available);
assert_eq!(available.command.as_deref(), Some("pi-acp"));
assert!(available.default_args.is_empty());
assert_eq!(
available.underlying_cli_path.as_deref(),
Some("/usr/local/bin/pi")
);

let adapter_missing = preset_catalog_entry(preset, |command| {
(command == "pi").then(|| PathBuf::from("/usr/local/bin/pi"))
});
assert_eq!(
adapter_missing.availability,
AcpAvailabilityStatus::AdapterMissing
);
assert!(adapter_missing.command.is_none());
assert!(adapter_missing.default_args.is_empty());

let not_installed = preset_catalog_entry(preset, |_| None);
assert_eq!(
not_installed.availability,
AcpAvailabilityStatus::NotInstalled
);
}

#[test]
fn adapter_missing_when_underlying_cli_present() {
let entry = preset_catalog_entry(&ADAPTER_PRESET, |command| {
Expand Down
1 change: 1 addition & 0 deletions desktop/src/features/onboarding/ui/RuntimeIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const RUNTIME_LOGOS: Record<string, string> = {
export const PRESET_LOGOS: Record<string, string> = {
devin: "/harness-logos/devin.svg",
omp: "/harness-logos/omp.svg",
pi: "/harness-logos/pi.svg",
grok: "/harness-logos/grok.svg",
opencode: "/harness-logos/opencode.svg",
kimi: "/harness-logos/kimi.png",
Expand Down
Loading