Skip to content
Closed
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Agents are part of the room, not haunted cron jobs.
|---|---|---|
| Relay, channels, threads, DMs, canvases, media, search, audit log | Mobile clients (iOS + Android, Flutter) | Web-of-trust reputation across relays |
| Desktop app (Tauri + React) | Workflow approval gates (infra exists, glue still drying) | Push notifications |
| `buzz-cli` (agent-first, JSON in / JSON out) + ACP harness (Goose, Codex, Claude Code) | Huddle lifecycle events | Culture features |
| `buzz-cli` (agent-first, JSON in / JSON out) + ACP harness (Goose, Codex, Claude Code, Oh My Pi) | Huddle lifecycle events | Culture features |
| YAML workflows: message / reaction / schedule / webhook triggers | | |
| Git events (NIP-34: patches, repo announcements, status) | | |
| Git hosting backend | | |
Expand Down Expand Up @@ -206,7 +206,7 @@ A Rust workspace of focused crates. Single source of truth: the relay. See [ARCH

**Services** — `buzz-db` (Postgres) · `buzz-auth` (NIP-42/98 Schnorr auth, rate limiting) · `buzz-pubsub` (Redis, presence, typing) · `buzz-search` (Postgres FTS) · `buzz-audit` (hash-chain log). Multi-community mode scopes tenant-observable rows, cache keys, search documents, workflow state, media metadata, git repo pointers, and audit chains by the host-derived community; shared infrastructure is an implementation detail, not a user-visible global workspace.

**Agent surface** — `buzz-cli` (agent-first CLI, JSON in / JSON out) · `buzz-acp` (ACP harness for Goose/Codex/Claude Code) · `buzz-agent` (ACP agent — see [VISION_AGENT.md](VISION_AGENT.md)) · `buzz-dev-mcp` (shell + file-edit tools) · `buzz-workflow` (YAML automation) · `buzz-persona` (agent persona packs)
**Agent surface** — `buzz-cli` (agent-first CLI, JSON in / JSON out) · `buzz-acp` (ACP harness for Goose/Codex/Claude Code/Oh My Pi) · `buzz-agent` (ACP agent — see [VISION_AGENT.md](VISION_AGENT.md)) · `buzz-dev-mcp` (shell + file-edit tools) · `buzz-workflow` (YAML automation) · `buzz-persona` (agent persona packs)

**Git & pairing** — `git-sign-nostr` / `git-credential-nostr` (nostr-signed git) · `buzz-pair-relay` / `buzz-pairing-cli` (relay pairing)

Expand Down
23 changes: 21 additions & 2 deletions crates/buzz-acp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Buzz Relay ──WS──→ buzz-acp ──stdio──→ Your Agent
(send_message, etc.)
```

Supports any agent that speaks [ACP](https://agentclientprotocol.com/) over stdio: **goose**, **codex** (via [codex-acp](https://github.com/agentclientprotocol/codex-acp)), and **claude code** (via [claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp)).
Supports any agent that speaks [ACP](https://agentclientprotocol.com/) over stdio: **goose**, **codex** (via [codex-acp](https://github.com/agentclientprotocol/codex-acp)), **claude code** (via [claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp)), and **oh my pi** (via `omp acp` — [omp.sh](https://omp.sh/)).

## Prerequisites

Expand Down Expand Up @@ -98,6 +98,25 @@ buzz-acp
Older installs that still expose `claude-code-acp` are also supported. `buzz-acp`
treats both Claude ACP command names as the same zero-arg runtime.

## Running with Oh My Pi

[Oh My Pi](https://omp.sh/) (`omp`) is a single-binary, multi-provider CLI whose ACP server is the `omp acp` subcommand — no separate adapter. It resolves its own model and credentials from `~/.omp`, so you authenticate omp once (e.g. an Anthropic Max subscription) and Buzz just spawns it.

```bash
# Install omp (see https://omp.sh/)
curl -fsSL https://omp.sh/install.sh | bash

# Authenticate a provider once (interactive), e.g. Anthropic Max:
# omp # then run /login and pick Anthropic

# Run
export BUZZ_ACP_AGENT_COMMAND="omp" # args default to `acp`

buzz-acp
```

Select a model with `--model` in `BUZZ_ACP_AGENT_ARGS` (e.g. `acp,--model,claude-opus-4-5`) or set a default in `~/.omp/agent/config.yml`.

## Configuration

All configuration is via environment variables (or CLI flags — every env var has a matching flag).
Expand Down Expand Up @@ -269,7 +288,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, 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. Oh My Pi is additionally surfaced in the first-run onboarding picker alongside the tier-1 harnesses (`ONBOARDING_RUNTIME_ORDER` in `desktop/src/features/onboarding/ui/onboardingRuntimeSelection.ts`).

> **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
22 changes: 21 additions & 1 deletion crates/buzz-acp/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@ pub(crate) fn normalize_agent_command_identity(command: &str) -> String {

fn default_agent_args(command: &str) -> Option<Vec<String>> {
match normalize_agent_command_identity(command).as_str() {
"goose" => Some(vec!["acp".to_string()]),
// Single-binary agents whose ACP server is a subcommand of the CLI.
"goose" | "omp" | "oh-my-pi" => Some(vec!["acp".to_string()]),
"codex" | "codex-acp" | "claude-agent-acp" | "claude-code-acp" | "claude-code"
| "claudecode" | "buzz-agent" => Some(Vec::new()),
_ => None,
Expand Down Expand Up @@ -1440,6 +1441,19 @@ mod tests {
assert_eq!(normalize_agent_args("goose", vec!["".into()]), vec!["acp"]);
}

#[test]
fn normalizes_omp_args_to_acp() {
// omp exposes its ACP server as `omp acp`, so args default to ["acp"].
assert_eq!(normalize_agent_args("omp", Vec::new()), vec!["acp"]);
assert_eq!(normalize_agent_args("omp", vec!["".into()]), vec!["acp"]);
assert_eq!(normalize_agent_args("omp", vec!["acp".into()]), vec!["acp"]);
assert_eq!(
normalize_agent_args("/usr/local/bin/omp", Vec::new()),
vec!["acp"]
);
assert_eq!(normalize_agent_args("Oh My Pi", Vec::new()), vec!["acp"]);
}

#[test]
fn normalizes_codex_and_claude_args_to_empty() {
assert_eq!(
Expand Down Expand Up @@ -1495,6 +1509,12 @@ mod tests {
#[test]
fn normalize_agent_command_identity_variants() {
assert_eq!(normalize_agent_command_identity("goose"), "goose");
assert_eq!(normalize_agent_command_identity("omp"), "omp");
assert_eq!(
normalize_agent_command_identity("/Users/me/.bun/bin/omp"),
"omp"
);
assert_eq!(normalize_agent_command_identity("Oh My Pi"), "oh-my-pi");
assert_eq!(
normalize_agent_command_identity("C:\\Program Files\\Goose\\goose.exe"),
"goose"
Expand Down
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/managed_agents/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1527,8 +1527,8 @@ const PRESET_HARNESSES: &[PresetHarness] = &[
label: "Oh My Pi",
command: "omp",
args: &["acp"],
install_instructions_url: "https://github.com/can1357/oh-my-pi",
install_hint: "Install Oh My Pi from github.com/can1357/oh-my-pi.",
install_instructions_url: "https://omp.sh/",
install_hint: "Install Oh My Pi from omp.sh (curl -fsSL https://omp.sh/install.sh | bash).",
underlying_cli: None,
},
PresetHarness {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@ test("all bundled harnesses are visible in onboarding", () => {
assert.equal(runtimeIsVisibleInOnboarding("codex"), true);
assert.equal(runtimeIsVisibleInOnboarding("goose"), true);
assert.equal(runtimeIsVisibleInOnboarding("buzz-agent"), true);
assert.equal(runtimeIsVisibleInOnboarding("omp"), true);
assert.equal(runtimeIsVisibleInOnboarding("custom"), false);
});

test("visible onboarding runtimes use the product order", () => {
const runtimes = [
runtime("buzz-agent", "available", "not_applicable"),
runtime("codex", "available", "logged_in"),
runtime("omp", "available", "not_applicable"),
runtime("goose", "available", "not_applicable"),
runtime("claude", "available", "logged_in"),
];

assert.deepEqual(
getVisibleOnboardingRuntimes(runtimes).map(({ id }) => id),
["claude", "codex", "goose", "buzz-agent"],
["claude", "codex", "goose", "buzz-agent", "omp"],
);
});

Expand Down Expand Up @@ -60,12 +62,13 @@ test("ready onboarding runtimes exclude unknown and non-ready harnesses", () =>
runtime("goose", "available", "not_applicable"),
runtime("codex", "available", "logged_out"),
runtime("buzz-agent", "available", "not_applicable"),
runtime("omp", "available", "not_applicable"),
runtime("claude", "available", "logged_in"),
runtime("custom", "available", "not_applicable"),
];

assert.deepEqual(
getReadyOnboardingRuntimes(runtimes).map(({ id }) => id),
["claude", "goose", "buzz-agent"],
["claude", "goose", "buzz-agent", "omp"],
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const ONBOARDING_RUNTIME_ORDER = [
"codex",
"goose",
"buzz-agent",
"omp",
];

const VISIBLE_ONBOARDING_RUNTIME_IDS = new Set<string>(
Expand Down