Research: porting block/buzz into ainb (ACP, chat surfaces, federation) #570
Replies: 1 comment
Spike: ACP session resume and steering, per-adapter probe resultsDate: 2026-07-31 MethodEach adapter was installed locally and driven by a purpose-built Node client speaking newline-delimited JSON-RPC 2.0 over piped stdio, with every byte in both directions timestamped into a Support matrix
Secondary capability read
Steering outcomes actually observed
Timeline caveat on the claude-agent-acp steer run: the 6 s mid-turn delay in the driver was too long, so the probe labelled Open questions answeredQ1 (line 229): which adapters support Q2 (line 230): verify claude-agent-acp/codex-acp steering behaviour before promising broadcast-steer semantics. Implications for the ainb chat-bus planResume strategy the daemon must own
Whether steering can be promised, per adapter
The codex-acp
Interop hazard specific to gemini-cli
Known gaps in this spike
Raw evidenceAll paths are absolute. Every claude-agent-acp
Source corroboration (readable JS with doc comments): codex-acp
The bundle is 1.1 MB unminified and greppable ( gemini-cli
Source references: Install notes
Whole codex-acp spike ran about 4 minutes wall clock and roughly 37k tokens of codex usage across 3 prompts. No credential values were read, printed or logged for any adapter. |
Uh oh!
There was an error while loading. Please reload this page.
Research: Porting block/buzz features into ainb, ACP, chat surfaces, federation
Date: 2026-07-31 14:56:19
Repository: agents-in-a-box (worktree agents-in-a-box--f-buzz--19533eb1)
Branch: f/buzz
Commit: 10165ef
Research Type: Comprehensive (Codebase ainb + Codebase buzz@39ce3df + Web)
Buzz snapshot: https://github.com/block/buzz @ 39ce3df, cloned to session scratchpad
Spec: research/2026-07-31_buzz-acp-research-spec.md
Research Question
Comprehensively research block/buzz: what features can ainb port? Would ACP (Agent Client Protocol) work better than what ainb has today? Design options for an ainb chat feature (claude/codex backends) and/or an independent fleet chat with broadcast. Is anything useful for making local-only ainb federated across systems?
Executive Summary
Buzz is not a small ACP chat app, it is a self-hosted, Nostr-relay-based team workspace where humans and AI agents are equal members. Three verdicts came out of the research: (1) ACP: hybrid-adopt: add ACP as a new provider transport at ainb's existing
ProviderAdapterseam for chat-grade agent sessions, keep tmux for interactive terminal sessions, do not touch the hangar plugin or fleet daemon protocols; (2) Chat: build the daemon-level chat bus first: the fleet daemon already has composer→send→broadcast→receipt plumbing proven against two clients, the only missing piece is a persisted message model; (3) Federation: Tailscale/SSH over the existing daemon socket now, adopt buzz's NIP-42-style challenge auth and NIP-OA owner-attestation identity pattern only when a second person's machines join; buzz itself has zero relay-to-relay federation in code today.Key Findings
hangar.sockwith capability gating, revision-log replay and resync (ainb-tui/crates/ainb-hangar-proto/src/fleet.rs:727-742,apps/ainb-fleet-macos/Sources/FleetUI/FleetWindowView.swift:430-481). The gap is a persisted message model, not a wire protocol.tmux send-keyswith a 1000 ms paste settle and 3× Enter retry (ainb-tui/crates/ainb-fleet-core/src/fleet/send/tmux.rs:84-104), observation bycapture-pane+ tailing Claude's own JSONL. ACP providessession/prompt, streamedsession/update(message/thought/tool_call/plan),session/request_permission, cancellation and steering, the structured versions of everything the needs/attention pipeline currently scrapes for. TheProviderAdaptertrait (ainb-tui/crates/ainb-fleet-core/src/fleet/provider.rs:149-166) is already shaped to host it.claude-agent-acp(Claude Code),codex-acp(Codex); Gemini CLI and Copilot CLI native (copilot --acp --portis even TCP). First-party Rust crateagent-client-protocolv1.6.0. Co-governed Zed + JetBrains, Apache 2.0. Weakness: remote transport is spec-level WIP; stdio-only baseline.buzz-relay-meshis pod-to-pod QUIC inside one deployment and fails closed against foreign relay identities (buzz/crates/buzz-relay-mesh/src/membership.rs:90-102). What buzz federates is identity: a portable keypair, NIP-42 challenge auth (~50 transport-agnostic lines), and NIP-OA owner-attestation ("my agents act under my key, bounded and revocable").buzz-dev-mcp(MCP tool server with_Stop/_PostCompactlifecycle hooks),buzz-workflow(YAML trigger/action/cron DSL behind an action-sink seam),buzz-persona(.persona.mdparser/merge), thebuzz-acpAgentPool/queue/circuit-breaker patterns, andbuzz-voice(offline TTS).Detailed Findings
1. What buzz is (architecture in one screen)
Every action is a signed Nostr event with a
kindinteger; new feature = new kind, zero breaking changes. The relay is the single source of truth; agents are members with their own keypairs, channel memberships and audit trails. Minimum self-host footprint is 5 containers (relay, Postgres, Redis, MinIO, minio-init) with no feature flags to drop any of them and no sqlite path (buzz/deploy/compose/compose.yml).2. How buzz uses ACP (buzz-acp deep dive)
buzz/crates/buzz-acp/src/acp.rs:19-21). Deliberately squats protocol v2 ahead of the upstream RFD (acp.rs:126).initialize,authenticate,session/new(withmcpServers+systemPrompt),session/prompt,session/cancel,session/set_config_option/set_model, plus steering extensions (_session/steering, goose-namespaced variants). Inbound:session/update(8 variants),session/request_permission. Unknown agent→client requests get-32601so agents never hang. Nofs/terminalcapability advertised.goose acp,codex-acp,claude-agent-acp,buzz-agent(buzz/crates/buzz-acp/src/config.rs:694-700); npm-installed out of band. Codex needs aCODEX_CONFIGenv deep-merge to open sandbox network access for the MCP child (config.rs:741-775).!rotate, or model switch (buzz/crates/buzz-acp/src/pool.rs:556-596, 2093-2097). Idle timeout 620 s (reset on any stdout line and ontool_call), hard cap 7200 s; per-slot crash circuit breaker with jittered exponential backoff (buzz/crates/buzz-acp/src/lib.rs:1027-1136).buzz/crates/buzz-acp/src/queue.rs:92-136). Mid-turn arrivals: Queue / Steer (default; non-cancelling injection, capability-gated because codex-acp answers unknown methods with{}success) / Interrupt / OwnerInterrupt.session/updatechunks only feed logs and an encrypted owner-only observer feed (kind 24200). The agent posts to chat itself by running thebuzzCLI via an MCP shell tool (instruction baked into the base prompt,buzz/crates/buzz-acp/src/base_prompt.md:5-24). The harness only publishes presence, typing, 👀/💬 reactions, observer frames, token metrics, and failure notices.allow_onceselected by kind), session-level permission mode pushed viasession/set_config_option; never surfaced to the channel for action.session/new(buzz-dev-mcp), which installs a 0700 tempdir PATH shim exposingrg,tree,buzz,git-credential-nostr,git-sign-nostr; the private key is moved from env into a 0600 keyfile so children never see it (buzz/crates/buzz-dev-mcp/src/shim.rs:33-58).--agents Nis a concurrency knob, not N identities. @mention routing isp-tag equality against the agent's pubkey (buzz/crates/buzz-acp/src/lib.rs:2732-2738), plus an inbound author gate (owner-only default / allowlist / anyone).3. What ainb has today (current-state map)
ainb-plugin-protocol, abi v2) and the hangar daemon protocol over~/.agents-in-a-box/hangar.sock(0600, token auth viaauth/hello, constant-time SHA-256 check;ainb-tui/crates/ainb-hangar-daemon/src/rpc/mod.rs:210-222). Daemon surface is ~150 methods and already includes chat-adjacent primitives:hangar/comment_add,comment_mention_preview,inbox_list,squad_fanout,attention/*,fleet/*.tmux send-keys -l --+ paste-settle + Enter-retry loop born from the "ATC heartbeat incident: 8 unsubmitted pastes" (ainb-tui/crates/ainb-fleet-core/src/fleet/send/tmux.rs:12-21,84-104). Observe:capture-panewith box-drawing-character heuristics for AskUserQuestion, plus re-implementing Claude Code's JSONL transcript path slugging (.../fleet/read/jsonl_tail.rs:16-49). Provider enum isClaude | Codex | Unknown, Gemini/Copilot launchable but not observable (.../fleet/types.rs:25-29).fleet/negotiate(versioned),fleet/snapshot+fleet/subscribewith revision-contiguous replay andfleet/resync_required, capability ids per write surface,fleet/broadcast {target_keys, text, idempotency_key}returning receipts withPending|Delivered|Failed|Unknown|Rejected(ainb-tui/crates/ainb-hangar-proto/src/fleet.rs:290-352, 624-663, 727-742). Proven against two independent clients (hangar-tui plugin, Swift app).ainb-web(axum; WS terminal, SSE events, bearer-token auth, refuses non-loopback bind without token;ainb-tui/crates/ainb-web/src/config.rs:97-136) andainb fleet bridge(Telegram/Slack/Discord two-way relay behind aFleetTransporttrait seam;ainb-tui/crates/ainb-core/src/fleet/bridge/relay.rs:1-30).ClaudeChatComponentin core (talks the Anthropic API directly,#![allow(dead_code)];ainb-tui/crates/ainb-core/src/components/claude_chat.rs:14); the Multica exploration explicitly lists standalone chat as an unstarted gap and lands on "coordination is entirely comment-driven; agents are team members" (docs/explorations/multica-reference/README.md:19-26,docs/hangar/proofs/multica-comparison.md:70,202,250).4. ACP ecosystem (web survey, fetched 2026-07-31)
initializenegotiates integerprotocolVersion(stable = 1; v2 schema in alpha). Remote transport (HTTP/WebSocket) explicitly WIP in the spec (agentclientprotocol.com/overview/architecture).agent-client-protocolv1.6.0 (2026-07-21), TS@agentclientprotocol/sdk, plus Python/Kotlin/Java SDKs (crates.io).claude-agent-acp; Codex via officialcodex-acp; Gemini CLI native; Copilot CLI native with TCP (copilot --acp --port 8080, GitHub docs); Goose, OpenCode, OpenHands, Junie native. Gaps: Aider, Crush.session/newso the agent connects to tools directly.5. Buzz features worth porting (full inventory, port calls)
Port call legend: port = lift code/pattern largely as-is; adapt = re-implement the pattern; skip = not worth it for ainb. Effort S/M/L.
buzz-dev-mcpMCP tool server (shell/read_file/str_replace/todo/view_image +_Stop/_PostCompacthooks + PATH shim + keyfile hygiene)crates/buzz-dev-mcpcrates/buzz-acp(pool.rs,queue.rs,lib.rs:1027-1136)agent-client-protocolcrate instead of hand-rollingdesktop/src/features/agents/lib/agentSessionTranscript.tscrates/buzz-workflow(schema.rs,executor.rs,action_sink.rs).persona.mdpersona packs (frontmatter + system prompt, merge/validate, MCP servers, triggers, model)crates/buzz-personadesktop/src/shared/constants/kinds.tseffective(ctx)=max(own,parent), debounced, budgeted)readStateManager.ts, NIP-RSretryable, semantic exit codes incl. conflict=5,delivery_unknownnever-retryable class, stdin-, errors naming the follow-up command)crates/buzz-cli/src/error.rs:87-136ainbCLI JSON mode; add the--follow/--waitmodes buzz lackscrates/buzz-auth/src/nip42.rs:37-86docs/nips/NIP-OA.md,NIP-AA.mdcrates/buzz-core/src/pairing/, NIP-ABagent_workingmodule all surfaces read)agentWorkingSignal.ts:11-45crates/buzz-relay-mesh/src/{endpoint,wire,membership}.rsbuzz-voicelocal offline TTS (April Pocket)crates/buzz-voicecrates/sprig6. ACP verdict: HYBRID-ADOPT
Decision: adopt ACP as a new agent transport for chat/broadcast sessions, at the
ProviderAdapterseam, inside the hangar daemon. Keep tmux for interactive terminal sessions. Do not replace the hangar plugin protocol or the fleet daemon protocol, ACP is not for those layers.Rationale:
normalize_event/execute/observe_fallbackduties ofProviderAdapter(ainb-tui/crates/ainb-fleet-core/src/fleet/provider.rs:149-166).Providerenum growth + fleet protocol version bump (the enum is on the wire atfleet.rs:91).Migration sketch (research-only; not scheduled):
ainb-acpcrate usingagent-client-protocolv1.x: spawn adapter,initialize,session/new(pass ainb's MCP servers),session/prompt, reducesession/updateinto fleet events.AcpProviderAdapterimplementing the existingProviderAdaptertrait; capability bitset advertises structured send/observe/cancel/steer.fleet/message_*methods (see §7); TUI/Fleet/web render from the store.session/request_permissionhandling, retiring scrape heuristics for those sessions.Risks/trade-offs:
ACP sessions are not resumable across daemon restartsCORRECTED 2026-07-31 by spike (research/2026-07-31_acp-resume-steering-spike.md): kill-respawn-load resume PROVEN for claude-agent-acp 0.64.0 and codex-acp 1.1.7, persistence in their own on-disk stores; still optional in spec and unverified for gemini-cli; stdio-only means the agent process must be co-located with the daemon (fine locally; federation handled at the daemon/message layer, not by tunneling ACP); protocol v2 churn upstream (pin v1; buzz squatting v2 is a warning, not a model to copy).7. Chat surfaces: comparison and build order
Build order: (1) daemon chat bus → (2) Fleet chat + broadcast UI → (3) TUI chat tab. All three share the bus; UIs are thin.
hangar/comment_*, inbox, mentions; ainb-hangar-store for persistencefleet/message_send,fleet/message_list,fleet/thread_*; message table in hangar store; AgentPool wiring (ACP) for agent participantsFleetSessionDetailView.swift:87-117), broadcast form with recipient checklist + confirmation (FleetWindowView.swift:430-481), receipts UIhangar-tuiplugin-as-daemon-client template (ainb-plugin-hangar/src/plugin.rs:821);captures_textfor text entry; dormantClaudeChatComponentUI shellPLUGIN_SCREENS, keybinding); message list + composer in wire-buffer cellsDesign notes carried over from buzz (see §5): clean timeline + side transcript; kind partition (content/aux/non-conversational); slash commands in the TUI (buzz has none, terminal-native gap to exploit); optimistic send with carried local key; message grouping/continuations; one-level threads for v1; agents as members with profiles; sticky "addressed agents" audience per channel.
8. Federation: options and pick
Buzz evidence: relay-to-relay federation does not exist in buzz code (mesh = pod-to-pod, fails closed against foreign relays;
buzz/crates/buzz-relay-mesh/src/membership.rs:36-42,90-102). What is portable is identity (keypair, NIP-42 challenge, NIP-OA/NIP-AA owner→agent attestation) and the pairing UX (QR + ECDH + 6-digit SAS + stateless throwaway relay;buzz/crates/buzz-core/src/pairing/). Their compute mesh (iroh QUIC, NIP-51 discovery, dual-signature endpoint binding, fail-closed roster intersection) is the best small-scale authenticated-peer-discovery reference (~300 lines of pattern).Candidate architectures:
Pick: C now. The daemon protocol +
ainb-webalready carry everything; Tailscale supplies identity, enrollment, revocation, NAT traversal and encryption, the exact list A and B rebuild by hand. Threat model check: "Stevie's laptop ↔ Stevie's devbox" has no untrusted party, so buzz's signature layers buy nothing yet. Sequencing: adopt the NIP-42 challenge (50 lines) the moment a second person's machine joins; NIP-OA-style owner→agent attestation when agents get their own keys; reach for B (iroh) only if coordinator-less p2p becomes a requirement. Buzz's own history endorses this ordering, thorough identity layer, still no federation, and both times they went p2p they kept a central bootstrap anyway.Concrete near-term enablers already in-tree:
ainb-webnon-loopback bind + bearer token (docs describe tailnet usage,docs/tui/web.md:1-13);FleetTransportseam for a remote-peer transport as a 4th bridge impl;hangar.sock→ optionally also listen on a tailnet TCP port behind the same token auth (needs a hardening pass).9. Cross-cutting cautions
ainb-core/src/cli/run.rs:417-430: baresend-keys+C-m, no-l, no--, no verify) violates the fleet-core "ONE verified send path" invariant, worth fixing regardless of this research.Providerenum (Claude | Codex | Unknown) is on the wire; growing it is a protocol-version event, batch it with the ACP work.Code References
ainb (branch f/buzz):
ainb-tui/crates/ainb-fleet-core/src/fleet/provider.rs:149-166, ProviderAdapter seam (ACP attachment point)ainb-tui/crates/ainb-fleet-core/src/fleet/send/tmux.rs:44-46,84-104, current send path + paste-retry ceilingainb-tui/crates/ainb-fleet-core/src/fleet/read/jsonl_tail.rs:16-49, JSONL transcript scrapingainb-tui/crates/ainb-hangar-proto/src/fleet.rs:290-352,624-663,727-742, negotiate/replay, receipts, broadcast paramsainb-tui/crates/ainb-hangar-daemon/src/rpc/mod.rs:210-222, socket + authapps/ainb-fleet-macos/Sources/FleetUI/FleetWindowView.swift:430-481, existing broadcast formainb-tui/crates/ainb-plugin-hangar/src/plugin.rs:821, plugin-as-daemon-client templateainb-tui/crates/ainb-web/src/config.rs:97-136, remote bind policyainb-tui/crates/ainb-core/src/fleet/bridge/relay.rs:1-30, FleetTransport seamainb-tui/crates/ainb-core/src/components/claude_chat.rs:14, dormant chat UI shellbuzz @ 39ce3df (session scratchpad clone):
crates/buzz-acp/src/{acp.rs,pool.rs,queue.rs,config.rs}, ACP harness (pool 556-596, queue 92-136, breaker lib.rs:1027-1136)crates/buzz-dev-mcp/src/{lib.rs,shim.rs}, standalone MCP tool server + shim/keyfile hygienecrates/buzz-workflow/src/{schema.rs,executor.rs,action_sink.rs}, liftable workflow enginecrates/buzz-persona/src/persona.rs:101-169, persona pack formatcrates/buzz-auth/src/nip42.rs:37-86, challenge authdocs/nips/NIP-OA.md,docs/nips/NIP-AA.md, owner→agent attestationcrates/buzz-core/src/pairing/, SAS device pairingcrates/buzz-relay-mesh/src/{endpoint.rs,wire.rs,membership.rs}, iroh QUIC mesh (intra-deployment)desktop/src/shared/constants/kinds.ts, kind partition contractdesktop/src/features/agents/lib/agentSessionTranscript.ts, ACP-update transcript reducercrates/buzz-cli/src/error.rs:87-136, agent-ergonomic CLI error contractExternal:
Recommendations
ainb-acpcrate on the upstream Rust SDK, daemon-owned AgentPool adapted from buzz, tmux retained for interactive sessions.fleet/message_*+ message model in hangar store), then Fleet chat/broadcast UI, then the TUI chat tab, one bus, three thin surfaces.buzz-dev-mcppattern as ainb's agent tool server;.persona.mdsupport; buzz-cli error/exit-code contract forainb --format json; fix the unhardenedrun.rssend path.Open Questions
session/loadtoday, and what does chat-session resume look like across daemon restarts? (Spec makes it optional; needs a per-adapter test matrix.)_session/steeringis an extension, verify claude-agent-acp/codex-acp behavior before promising broadcast-steer semantics.ainb-hangar-store(SQLite): adopt buzz's content/aux/non-conversational partition directly, or simplify to messages+receipts for v1?fleet/message_*family in one break.All reactions