Publish the ACP bridge as @get-bb/plugin-sdk/provider-bridge/acp - #2228
Closed
SawyerHood wants to merge 1 commit into
Closed
Publish the ACP bridge as @get-bb/plugin-sdk/provider-bridge/acp#2228SawyerHood wants to merge 1 commit into
SawyerHood wants to merge 1 commit into
Conversation
bb runs every ACP agent through one generic bridge: the agent to launch arrives per command in the provider options, so nothing in it is first-party. It lived inside the first-party plugin, where no other plugin could reach it, and a third party wanting to add an ACP agent had to vendor a bridge or ask bb for a config entry. The implementation moves to packages/provider-bridge-acp and the plugin SDK publishes a curated surface at ./provider-bridge/acp: the bridge, the dialect hooks, the launch-profile type, the protocol vocabularies and the model-catalog helpers. The first-party plugin is now registrations, icons and a one-line host entry that re-exports the kit — exactly what a third-party plugin writes — and public-sdk-only.test.ts proves it takes no shortcut: no file in the plugin may import a private @bb/* package. The move also removed the kit's one real privilege, which the G1 ratchet caught: provider maintenance (sign-in, install, account, usage) branched on the bb provider id "acp-cursor". It is now an optional surface on the agent's dialect, so a plugin registering Cursor under any id gets it and a plugin bringing its own agent can supply its own. Co-Authored-By: Claude <noreply@anthropic.com>
Collaborator
Author
Coordinator review — WS2b layer 4 (#2228)PASS against Verified on the branch
Note for stabilization (no action now)
|
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #2216.
What was wrong
bb's ACP bridge is already generic — the agent to launch arrives per command in
providerOptions.acpLaunchSpec, and one implementation serves Cursor, opencode, omp, Grok Build and Hermes Agent. But it lived insideplugins/provider-acp/src/**, where no other plugin could reach it. A third party wanting to add an ACP agent (Amp) had to vendor a bridge or ask bb to add acustomAcpAgentsconfig entry. That is the first-party privilege this workstream exists to delete.What changed
The implementation moves to
packages/provider-bridge-acp(agit mv; the diff is renames plus import rewrites). It no longer imports the plugin SDK — it imports the private packages the SDK re-exports (@bb/provider-bridge-protocol,@bb/provider-bridge-protocol/bridge-kit,@bb/domain,@bb/host-daemon-contract) — because the SDK now depends on it.The SDK publishes
./provider-bridge/acp:packages/plugin-sdk/src/provider-bridge-acp.ts, a hand-curated named-export module (neverexport *), with every value carrying theexperimental_prefix:experimental_acpProviderBridge— the bridge a plugin re-exports from itsbb.hostartifactexperimental_registerAcpDialect/experimental_resolveAcpDialect/experimental_acpDialectIdsand the shipped dialects — the dialect hooksexperimental_acpProfileFromLaunchSpec, typeAcpAgentProfile— the launch profileexperimental_ACP_TOOL_KINDS/_STATUSES/_PROTOCOL_VERSIONand the wire types a dialect readsexperimental_parseAcpAgentModelLines/_buildAcpAgentModelCatalog/_splitAcpPrimaryModels— the model picker from an agent's--list-modelsPlumbing: the
exportsentry,build-runtime.mjs(a new esbuild entry, zod external),build-bundled-dts.mjs(a new bundled.d.ts), the ordered-key assertion inpackage-exports.test.ts, and an entry indocs/api_to_audit.mdnaming what to audit before the prefix comes off.The first-party plugin is now what a third-party plugin is:
server.ts(registrations),icons/, andsrc/host.ts— one line:Its four
@bb/*devDependencies are gone, andpublic-sdk-only.test.ts— the same guard the echo canary carries (#2189) — walks every file in the package and fails on any private import or any specifier outside the allowlist.The kit's one real privilege is gone. The G1 ratchet caught it during the move:
provider-maintenance.tsbranched on the bb provider id"acp-cursor"in five places to decide sign-in, installation, account and usage. That is nowAcpMaintenanceDialect, an optional surface on the agent's dialect:CURSOR_ACP_MAINTENANCEhangs off the cursor dialect, a plugin registering Cursor under any id gets it, and a plugin bringing its own agent supplies its own. The generic path reports only whether the executable exists, as it must.How I verified
typecheck(whole repo)test --filter=@bb/provider-bridge-acptest --filter=bb-plugin-provider-acptest --filter=@get-bb/plugin-sdktest --filter=@bb/provider-parity --forcetest --filter=@bb/plugin-build --forcenode scripts/check-provider-literal-ratchet.mjsTwo guards are new and are the ones worth reading:
builtin-host-artifacts.test.tsnow builds the ACP plugin's host artifact the way the daemon does — inlining the SDK's publisheddist/provider-bridge-acp.jsfrom the plugin's ownnode_modules— and imports the result, asserting it is a valid provider bridge. A kit that only resolved through the workspacesourcecondition would pass every other test here and fail this one.import type { ThreadEvent } from "@bb/domain"tosrc/host.tsand confirmed it fails (src/host.ts imports private packages: expected [ '@bb/domain' ] to deeply equal []), then reverted.On G1:
packages/provider-bridge-acpjoinsplugins/provider-*in the ratchet's carve-out, because it is the same kind of code — a provider implementation, which is allowed to name its own provider — moved underpackages/only so the SDK can re-export it. A fixture test pins that the carve-out is exactly that path and does not leak to its neighbours (packages/provider-bridge-protocolstill counts). The carve-out hides nothing today: after the maintenance fix the kit contains no bb provider id at all — the five remaining regex hits are a filesystem path segment ("cursor"in~/.cursor/auth.json), the synthetic model sentinel"acp-default", the synthetic item id"acp-permission", and the executable name"cursor-agent"in the dialect table.Spike §8, stated plainly: I did not record a fresh
turn-toolscell per installed agent in this layer. Only grok and cursor-agent are installed on this machine — opencode, gemini and hermes are not — and recording needs a dev app, which the machine cannot host right now without disturbing two other resident dev apps and the perf gate. The dialect rules this stack commits to are backed by the live wire logs in #2211/#2216 rather than by fresh cells; a fresh cursor cell would additionally carryacpDialectand let the recorded oracle exercise the dialect, which today it cannot (the committed recordings predate the field).