Skip to content
Merged
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
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
11 changes: 11 additions & 0 deletions desktop/src/features/settings/ui/harnessCatalogCopy.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import assert from "node:assert/strict";
import test from "node:test";

import { harnessDescription } from "./harnessCatalogCopy.ts";

test("Pi catalog entry has its curated product description", () => {
assert.equal(
harnessDescription("pi"),
"A minimal terminal coding harness, connected through the pi-acp adapter.",
);
});
2 changes: 2 additions & 0 deletions desktop/src/features/settings/ui/harnessCatalogCopy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const HARNESS_DESCRIPTIONS: Record<string, string> = {
cursor: "Cursor's coding agent, connected to Buzz through its ACP server.",
// Source: https://github.com/can1357/oh-my-pi
omp: "A terminal coding agent with integrated development tools.",
// Sources: https://pi.dev/docs/latest, https://github.com/svkozak/pi-acp
pi: "A minimal terminal coding harness, connected through the pi-acp adapter.",
// Source: https://build.x.ai (docs unavailable during research; kept
// deliberately conservative).
grok: "xAI's coding agent, connected to Buzz through its ACP entrypoint.",
Expand Down
Loading