Skip to content

feat(providers): ACP V1 — Cursor, Hermes, and OpenCode agent providers#87

Open
SawyerHood wants to merge 7 commits into
mainfrom
acp-v1-cursor-hermes-opencode
Open

feat(providers): ACP V1 — Cursor, Hermes, and OpenCode agent providers#87
SawyerHood wants to merge 7 commits into
mainfrom
acp-v1-cursor-hermes-opencode

Conversation

@SawyerHood

Copy link
Copy Markdown
Collaborator

For @jt — review requested; do not merge yet.

Summary

V1 support for connecting BB to Agent Client Protocol agent servers, with three built-in provider profiles:

Provider id Display Launch command
acp-cursor Cursor cursor acp
acp-hermes Hermes hermes acp
acp-opencode OpenCode opencode acp

Architecture — one generic ACP adapter + bridge (packages/agent-runtime/src/acp/), following the existing pi/claude-code bridge pattern:

  • bb-acp-bridge (one process per provider id) acts as the ACP client: spawns one agent subprocess + one ACP session per bb thread, NDJSON JSON-RPC on both sides. The consumed ACP subset is validated with in-repo zod schemas (wire.ts) rather than the pre-1.0 external SDK.
  • Permissions are cooperative, enforced in the bridge: full auto-allows session/request_permission; ask forwards to bb's approval flow (acp/permission/request → pending interaction → user decision → ACP optionId); deny rides the runtime's existing auto-deny. Client fs/write_text_file is path-checked against workspace write roots and surfaced as fileChange events with diffs; readonly refuses writes.
  • Event translation: agent_message_chunk/agent_thought_chunk stream as deltas with accumulation for faithful item/completed; tool calls map by kind to commandExecution / fileChange (diff content) / generic toolCall; ACP planturn/plan/updated.
  • Steer (no ACP equivalent) is emulated by chaining the input as the next session/prompt within the same bb turn. Resume uses session/load when the agent advertises loadSession, else falls back to a fresh session with a visible provider/warning.
  • Models stay agent-owned: model/list serves a synthetic "Agent default" entry; no model/reasoning overrides are forwarded to the agent.
  • No DB migration, no new dependencies, no route changes — --provider acp-opencode flows through existing spawn/tell/stop paths; daemon bundle manifest, packaged-app artifact checks, tarball smoke test, app icon fallback, thread-view display names, and bb-workflows skill typings are wired.

Verification

  • ✅ New tests: 22 ACP adapter unit tests + 14 bridge tests against a scripted fake-ACP-agent subprocess (lifecycle, permissions in all three modes, fs policy inside/outside workspace, steer chaining, cancellation, resume with/without loadSession, agent crash, missing binary)
  • turbo run test for @bb/agent-providers, @bb/agent-runtime, @bb/domain, @bb/thread-view, @bb/server, @bb/host-daemon, @bb/app, @bb/cli, @bb/workflow-runtime — all green
  • turbo run typecheck repo-wide — green except 8 pre-existing @bb/host-daemon errors (Timer/Timeout type mismatch in untouched files; verified identical with this change stashed)
  • turbo run lint — green

Known follow-ups / caveats

  1. No manual QA against a real ACP agent yet — needs a smoke test with opencode/hermes/cursor on PATH via scripts/bb-dev-app.
  2. Cooperative permissions: ACP agents execute tools in their own process; bb's readonly/workspace-write governs what bb approves and what bb's fs methods will do, but cannot sandbox an agent that doesn't ask. Not yet surfaced in UI/docs.
  3. Provider availability is static true (like other providers); a missing agent binary fails thread start with a clear error, but a daemon-side PATH check would improve the picker.
  4. ACP providers are accepted as workflow agents (the DSL union tracks the catalog) via the pi-style schema-in-prompt path — mechanically wired but unvalidated.
  5. Deferred: ACP terminal/* client capability, MCP server pass-through on session/new, authenticate flows, image-URL prompt blocks.

🤖 Generated with Claude Code

@SawyerHood SawyerHood force-pushed the acp-v1-cursor-hermes-opencode branch from 861aa5a to d67d7bc Compare June 12, 2026 18:29
SawyerHood and others added 3 commits June 12, 2026 16:14
Add Agent Client Protocol (ACP) support with three built-in provider
profiles: acp-cursor (`cursor acp`), acp-hermes (`hermes acp`), and
acp-opencode (`opencode acp`).

- New generic ACP adapter + bridge in @bb/agent-runtime (src/acp/): the
  bridge acts as the ACP client, spawning one agent subprocess and one
  ACP session per bb thread over newline-delimited JSON-RPC stdio.
- Permission modes enforced cooperatively in the bridge: full
  auto-allows session/request_permission, ask forwards to bb approvals,
  deny rides the runtime auto-deny; client fs writes are path-checked
  against workspace write roots and surfaced as fileChange events.
- Steer is emulated by chaining input onto the active turn; resume uses
  session/load when supported, else a fresh session plus a warning.
- ACP agents own model selection: model/list serves a synthetic
  "Agent default" entry and no model/reasoning overrides are forwarded.
- Catalog/registry/bundling/UI wiring; bb-workflows skill typings
  updated to the grown provider union.

Tests: ACP adapter unit tests (22) and bridge tests against a scripted
fake ACP agent subprocess (14) covering lifecycle, permissions in all
three modes, fs policy, steer chaining, cancellation, resume with and
without loadSession, agent crash, and missing binaries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…election

ACP V1 shipped three untested profiles behind a synthetic "Agent default"
model. This pares the integration down to what's real and makes Cursor a
first-class provider:

- Launch the Cursor CLI agent binary: the CLI installs as `agent`
  (cursor.com/docs/cli) and speaks ACP via `agent acp`; `cursor` is the
  editor's shell launcher and does not.
- Real model list: the bridge runs the profile's `--list-models` command
  and groups the `base[-effort][-fast]` ids into model families with
  reasoning-effort variants (bridge/model-catalog.ts). `extra-high` maps
  onto xhigh; ids outside the grammar stay standalone single-effort models.
- Reasoning selection: the session's (model, reasoningLevel) rides
  thread/start as `modelSelection`; the bridge resolves it to the exact raw
  variant id by catalog lookup — never suffix synthesis, spellings vary —
  and launches `agent --model <id> acp`. Unresolvable combos fall back to
  the family id with an acp/warning. Applied per session: a mid-thread
  change takes effect on the next session spawn, not the next turn.
- Widen the acp-cursor reasoning ladder to low..max so server validation
  admits the levels the variants actually expose.
- Remove the Hermes and OpenCode profiles, provider ids, and catalog
  entries; `modelCli` becomes required on the (now single) profile and
  `model/list` always carries the list command.

Verification: turbo typecheck for agent-providers, agent-runtime,
thread-view, host-daemon, server, app, cli; tests green for
agent-providers (5), agent-runtime (639, incl. new model-catalog and
bridge launch-pin coverage), thread-view (291), host-daemon (316),
server (569).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@SawyerHood SawyerHood force-pushed the acp-v1-cursor-hermes-opencode branch from d67d7bc to 991f039 Compare June 12, 2026 23:30
SawyerHood and others added 4 commits June 12, 2026 16:51
…section

The full Cursor catalog is 53 model families — too many for the default
picker. The cursor profile now declares its primary families (Auto,
Fable 5 + Thinking, Opus 4.8 + Thinking, GPT-5.5, Composer 2.5 Fast);
the bridge serves them in declared order as `models` and everything else
as `selectedOnlyModels`, falling back to everything-primary when no name
matches a future catalog. The default flag is re-anchored onto the
primary list.

selectedOnlyModels graduates from "hidden unless already selected" to a
browsable pool: the model picker renders a collapsed "More models" row
that expands in place (per-open state), for committed and previewed
providers alike — claude-code's legacy entries become reachable the same
way. Execution-override validation now accepts selected-only models as
swap targets so a More-models selection applies mid-thread too.

Verification: turbo typecheck for agent-runtime, server, app; tests
green for agent-runtime (642), server (569), app (419); picker
collapsed/expanded states exercised in a live dev instance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trim the primary Cursor list to Auto, Fable 5 Thinking, Opus 4.8
Thinking, GPT-5.5, and Composer 2.5 Fast — the non-thinking Fable/Opus
variants move under More models.

Cursor bakes the effort word into variant names ("Opus 4.8 1M Medium")
while bb renders the reasoning level separately, so the picker showed
the level twice. Family display names now shed the default variant's
own effort word by token ("Opus 4.8 1M Medium" → "Opus 4.8 1M");
stripping is keyed to the variant's explicit id token, so brand words
that collide with effort spellings ("Codex 5.1 Max") are untouched. Raw
variant names survive as the per-effort descriptions.

Verification: agent-runtime typecheck + tests (644); picker exercised
in a live dev instance — trigger and rows show each level once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The picker rendered efforts in the agent's listing order (Medium, High,
Extra High, Low, Max for the Opus/Fable families). Family members keep
listing order — it anchors the no-medium default — but the
supportedReasoningEfforts projection now sorts by the canonical domain
ladder, so every reasoning menu reads low → max.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Renders the Cursor glyph (monochrome path via Simple Icons) in provider
tabs and the model picker trigger instead of the "C" letter fallback,
following the existing ClaudeIcon/OpenAiIcon/PiIcon pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant