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
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ jobs:
-p buzz-relay \
-p buzz-acp \
-p buzz-agent \
-p buzz-a2a-acp \
-p buzz-dev-mcp \
-p git-credential-nostr \
-p git-sign-nostr
Expand Down Expand Up @@ -939,7 +940,7 @@ jobs:
shell: bash
run: |
mkdir -p desktop/src-tauri/binaries
for bin in buzz-acp buzz-agent buzz-dev-mcp git-credential-nostr buzz; do
for bin in buzz-acp buzz-agent buzz-a2a-acp buzz-dev-mcp git-credential-nostr buzz; do
touch "desktop/src-tauri/binaries/${bin}-${TARGET}.exe"
done
- name: Clippy (workspace)
Expand Down Expand Up @@ -1013,6 +1014,7 @@ jobs:
mkdir -p desktop/src-tauri/binaries
touch "desktop/src-tauri/binaries/buzz-acp-$TARGET"
touch "desktop/src-tauri/binaries/buzz-agent-$TARGET"
touch "desktop/src-tauri/binaries/buzz-a2a-acp-$TARGET"
touch "desktop/src-tauri/binaries/buzz-dev-mcp-$TARGET"
touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
touch "desktop/src-tauri/binaries/buzz-$TARGET"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ jobs:

- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh

- name: Build Linux Tauri app
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ jobs:

- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh

# Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it.
Expand Down Expand Up @@ -353,7 +353,7 @@ jobs:

- name: Build sidecars
run: |
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh "$TARGET"

- name: Build unsigned Tauri app
Expand Down Expand Up @@ -611,7 +611,7 @@ jobs:

- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh

- name: Generate release config
Expand Down Expand Up @@ -773,7 +773,7 @@ jobs:
- name: Build sidecars
shell: bash
run: |
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh "$TARGET"

- name: Build Windows NSIS installer (unsigned)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/signed-macos-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:

- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh

# Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/windows-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:
- name: Build sidecars
shell: bash
run: |
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
./scripts/bundle-sidecars.sh "$TARGET"

- name: Build Windows NSIS installer (unsigned)
Expand Down
18 changes: 18 additions & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/buzz-audit",
"crates/buzz-acp",
"crates/buzz-agent",
"crates/buzz-a2a-acp",
"crates/sprig",
"crates/buzz-test-client",
"crates/buzz-ws-client",
Expand Down
13 changes: 7 additions & 6 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ _ensure-sidecar-stubs:
set -euo pipefail
TARGET=$(rustc -vV | sed -n 's|host: ||p')
mkdir -p desktop/src-tauri/binaries
for bin in buzz-acp buzz-agent buzz-dev-mcp git-credential-nostr buzz; do
for bin in buzz-acp buzz-agent buzz-a2a-acp buzz-dev-mcp git-credential-nostr buzz; do
touch "desktop/src-tauri/binaries/${bin}-${TARGET}"
done

Expand Down Expand Up @@ -236,6 +236,7 @@ desktop-release-build target="aarch64-apple-darwin":
mkdir -p desktop/src-tauri/binaries
touch "desktop/src-tauri/binaries/buzz-acp-$TARGET"
touch "desktop/src-tauri/binaries/buzz-agent-$TARGET"
touch "desktop/src-tauri/binaries/buzz-a2a-acp-$TARGET"
touch "desktop/src-tauri/binaries/buzz-dev-mcp-$TARGET"
touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
touch "desktop/src-tauri/binaries/buzz-$TARGET"
Expand Down Expand Up @@ -428,7 +429,7 @@ dev *ARGS: bootstrap _ensure-sidecar-stubs _ensure-migrations
fi
done
fi
cargo build -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr -p buzz-relay
cargo build -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr -p buzz-relay
if [[ -n "{{mesh}}" ]]; then
export MESH_LLM_NATIVE_RUNTIME_CACHE_DIR="$(./scripts/ensure-mesh-native-runtime.sh)"
fi
Expand Down Expand Up @@ -475,10 +476,10 @@ desktop-standalone *ARGS: _ensure-sidecar-stubs
#!/usr/bin/env bash
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
cargo build -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
cargo build -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
TARGET=$(rustc -vV | sed -n 's|host: ||p')
TARGET_DIR=$(cargo metadata --format-version 1 --no-deps | node -p "JSON.parse(require('fs').readFileSync(0, 'utf8')).target_directory")
for bin in buzz-acp buzz-agent buzz-dev-mcp git-credential-nostr buzz; do
for bin in buzz-acp buzz-agent buzz-a2a-acp buzz-dev-mcp git-credential-nostr buzz; do
cp "${TARGET_DIR}/debug/${bin}" "desktop/src-tauri/binaries/${bin}-${TARGET}"
chmod +x "desktop/src-tauri/binaries/${bin}-${TARGET}"
done
Expand All @@ -504,7 +505,7 @@ staging *ARGS: bootstrap _ensure-sidecar-stubs
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
pnpm install # unconditional: staging must always start with a clean dep tree
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
FEATURES=()
if [[ -n "{{mesh}}" ]]; then
FEATURES=(--features mesh-llm)
Expand All @@ -531,7 +532,7 @@ production *ARGS: bootstrap _ensure-sidecar-stubs
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
pnpm install # unconditional: production must always start with a clean dep tree
cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
cargo build --release -p buzz-acp -p buzz-agent -p buzz-a2a-acp -p buzz-dev-mcp -p buzz-cli -p git-credential-nostr
FEATURES=()
if [[ -n "{{mesh}}" ]]; then
FEATURES=(--features mesh-llm)
Expand Down
32 changes: 32 additions & 0 deletions crates/buzz-a2a-acp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "buzz-a2a-acp"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "ACP adapter for agents advertised by an OASF Agent Record and invoked through A2A"
readme = "README.md"
keywords = ["acp", "a2a", "agntcy", "oasf", "agent"]
categories = ["command-line-utilities", "web-programming"]

[lib]
name = "buzz_a2a_acp"
path = "src/lib.rs"

[[bin]]
name = "buzz-a2a-acp"
path = "src/main.rs"

[dependencies]
base64 = "0.22"
clap = { version = "4", features = ["derive", "env"] }
hex = { workspace = true }
reqwest = { workspace = true, features = ["json", "rustls"] }
serde = { workspace = true }
serde_json = { workspace = true, features = ["raw_value"] }
sha2 = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] }
url = { workspace = true }
uuid = { workspace = true }
92 changes: 92 additions & 0 deletions crates/buzz-a2a-acp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# buzz-a2a-acp

`buzz-a2a-acp` is a small BYOH subprocess adapter. It lets Buzz host an agent
that is described by an [AGNTCY/OASF Agent Record](https://docs.agntcy.org/oasf/agent-record-guide/)
and invoked with [A2A](https://a2a-protocol.org/latest/).

The adapter reads the record, resolves the `integration/a2a` module (OASF id
`203`), and exposes the remote agent through Buzz's existing ACP stdio seam:

```
OASF Agent Record -> A2A Agent Card -> A2A JSON-RPC -> ACP stdio -> Buzz
```

The current adapter prefers an A2A JSON-RPC interface declared in
`supportedInterfaces`. It selects `SendMessage`/`GetTask` for A2A 1.x and
`message/send`/`tasks/get` for A2A 0.3. It sends the matching `A2A-Version`
header on every standard A2A request. It also has an explicit vendor
compatibility path for a non-standard card shape (`serviceEndpoint` plus
`agent/sendMessage` and `agent/getTask`). The compatibility path is not an A2A
release contract. Task responses are polled with bounded backoff until a
terminal state or the configured timeout.

## Configure in Buzz

Register the binary as a BYOH ACP runtime with:

```text
command: buzz-a2a-acp
args: --record,/absolute/path/to/agent-record.json
```

The same configuration can be represented by a custom-harness JSON object:

```json
{
"id": "remote-oasf-agent",
"label": "Remote OASF agent",
"command": "buzz-a2a-acp",
"args": ["--record", "/absolute/path/to/agent-record.json"],
"env": {}
}
```

This is an operator-owned configuration example. The adapter is not added to
Buzz's compiled-in runtime gallery and does not auto-import or auto-trust
records.

Or set `BUZZ_A2A_AGENT_RECORD`. The record can be a local file or an HTTP(S)
URL. Use `BUZZ_A2A_BEARER_TOKEN` only when the remote A2A endpoint requires it.
The token is supplied by the operator and is never read from the public record.

The adapter requires the OASF descriptor fields `digest`, `media_type`, and
`size` for every artifact. It validates the SHA-256 digest and exact size, and
it accepts only JSON media types. It accepts the OASF `data.card_data` field
only as an explicit deprecated compatibility fallback because current OASF
schemas prefer an artifact descriptor.

Remote records and card/artifact endpoints must use HTTPS. HTTP is accepted
only for loopback hosts. Redirects are disabled. A bearer token is sent only
when `--bearer-token-endpoint` normalizes to the resolved A2A endpoint. This
keeps operator credentials out of arbitrary endpoints selected by a public
record. The adapter resolves each hostname, applies the address policy, and
pins the request client to the checked address. It does not perform a second
unchecked DNS lookup for the request.

The optional `--agency-ref`, `--space-ref`, and `--agent-ref` flags project
stable host context references into A2A `metadata`. The source record does
not supply commands, environment variables, or credentials. New conversations
use random UUID context identifiers by default. An operator can supply a stable
identifier with `--context-id` or `BUZZ_A2A_CONTEXT_ID` when the host has a
durable conversation reference to preserve intentionally.

## Scope and trust boundary

The adapter projects public discovery metadata and A2A results. It does not
copy an Agency's private prompts, memory, tools, local files, or signing keys
into Buzz. The source runtime remains responsible for authentication,
authorization, execution, and any Nostr or Git signing. Buzz receives the
ACP-visible response or task status.

This is an experimental adapter. It does not implement AGNTCY Directory
registration, OASF custom taxonomy exchange, A2A streaming, push notifications,
or Surface rendering. Those are separate integration layers that can build on
the real invocation seam without inventing a parallel agency protocol.

OASF defines the record schema; it does not define how records are discovered
or transported. The current adapter resolves a reviewed local path or HTTPS
URL. Authenticity is therefore based on the operator's review and, for remote
records, the HTTPS connection. OASF 1.1 records do not carry a general record
signature. Domain-JWKS verification is the next planned trust layer. Optional
AGNTCY Directory resolution can follow when interoperable Directory identity
and verification are required.
Loading