Let the ACP plugin own its agents, including the user's own - #2229
Closed
SawyerHood wants to merge 1 commit into
Closed
Let the ACP plugin own its agents, including the user's own#2229SawyerHood wants to merge 1 commit into
SawyerHood wants to merge 1 commit into
Conversation
The ACP agents were split across three owners: the plugin declared five providers in a hardcoded list, the server composed user-configured agents from a customAcpAgents array in config.json at request time, and the ACP tier supplied capabilities for anything acp-*. A user could add an agent only by hand-editing bb's config file, and only bb could add a known one. Now the plugin owns both. A known agent and a configured agent are the same AcpAgentDefinition and become a declaration the same way. Configured agents live in the plugin's own customAgents setting, so bb plugin config sets them and a change re-registers the providers with no restart. Each agent carries its own fork support, dialect, reasoning ladder and compaction support instead of inheriting one tier's answer for five agents, and every one of them declares family "acp" so nothing has to parse an id prefix. The deprecated customAcpAgents array is still READ, never written: each agent it declares is registered and logged with a deprecation notice naming the setting to move it to, until 0.40. A setting entry wins over a config entry with the same id. Co-Authored-By: Claude <noreply@anthropic.com>
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 #2228.
What was wrong
Three owners shared bb's ACP agents:
plugins/provider-acp/server.tsdeclared five providers in a hardcoded array.customAcpAgentsarray in~/.bb/config.json, at request time, inexecution-options.ts.acp-provider-tier.ts) supplied capabilities for any id starting withacp-.So a user could add an agent only by hand-editing bb's own config file (no CLI surface, needing
bb-app config refresh), only bb could add a known one, and every agent inherited one tier answer:fork: "tip"for all five,supportsManualCompactionfrom a config field the tier read back out.What changed
One shape, one path.
src/agents.tsdefinesAcpAgentDefinition— a launch spec plus the facts a declaration needs — andsrc/declaration.tsturns any definition into aPluginProviderDeclaration.src/known-agents.tsholds the five bb ships. A user-configured agent is the same shape and the same path; nothing about the built-ins is privileged.Configured agents are the plugin's own setting.
customAgentsholds a JSON array, so the CLI already reaches it:settings.onChangedisposes the previous registrations and registers the new set, so an edit applies with no restart and noconfig refresh. Every rejected entry is logged with the reason (malformed, shadows a built-in id, configured twice) — an agent that vanishes silently is a support ticket.Each agent declares its own facts.
fork,dialect,reasoningLevels,supportsManualCompaction,visibility, usage and installation support are per agent instead of per tier. Every registration carriesexperimental_family: "acp", which is what groups the agents once theacp-prefix stops meaning anything (layer 6). Theacp-<slug>id stays exactly as it is: threads persist provider ids, so they can never change.The deprecated config array is read, never written.
src/legacy-config.tsreadscustomAcpAgentsfrom<dataDir>/config.json, registers what it finds, and logs a deprecation notice per agent naming the setting to move it to. A setting entry wins over a config entry with the same id. The module is self-contained and dated: it goes away in 0.40.Deliberate behavior changes
fork: "none"instead of inheriting the tier's"tip". The bridge refusessession/forkfor an agent that never advertised it, but only after bb created the fork thread, so a declaration above what the agent answers is a thread that dies on start (acp-cursor advertises fork support it does not have; forking births an errored thread #1833) — and bb has never seen a configured agent'sinitializereply. The five known agents keep exactly the values they declare today, including the unverified"tip"on opencode/omp/hermes-agent, marked as guesses in the source. Q21's per-instance probe replaces every guess with what the agent answers.logois no longer used. The legacy field pointed at an arbitrary file the server served from a route of its own; a plugin-registered provider's icon is a host glyph or an asset the plugin ships. The field still parses (an old config must not break) and the agent shows the generic tool glyph. This is a real, small loss and I did not want to hide it.signInCommand, the hint says "Sign in to Amp on the machine, then reload." instead of naming a command bb invented.How I verified
pnpm exec turbo run typecheck(whole repo): 75/75.test --filter=bb-plugin-provider-acp: 4 files, 27 tests. New coverage: the parser keeps a well-formed agent and defaults what it omitted; it reports a malformed entry, a shadowed built-in and a duplicate; the deprecatedlogoparses and yields the glyph; the launch spec drops amodelCliwith nothing to list; every known agent declares familyacp; Cursor and grok declarefork: "none"and their dialects while opencode keeps"tip"; grok keeps its own three-level reasoning ladder; a configured agent gets honest copy.legacy-config.test.tscovers data-dir resolution (BB_DATA_DIR,~/…, the production default), reading the array, a missing file and an empty array being normal, and unreadable JSON being reported rather than thrown.apps/serverfirst-party-provider-plugins.test.ts— the test that pins every built-in provider's client-read declaration — passes unchanged, which is the check that this layer preserved the five known agents' behavior.@bb/serversuite: 1895 passed, 2 failed. One is this test before I restored the unverified fork values (now passing). The other isinternal-skill-trees.test.tsexpecting file mode0644and getting0664— a pre-existing local failure on this machine'sumask 0002, unrelated to this change.node scripts/check-provider-literal-ratchet.mjs: OK, 148 across 40 core files — unchanged.Doc surfaces updated in the same change, per AGENTS.md:
docs/configuration.md(rewritten "Custom ACP Agents" with the new CLI command and a dated deprecation subsection),packages/templates/src/templates/bb-guide-providers.md(the in-CLI guide), and thebb-clibuiltin skill.What this layer does not do yet
The server's ACP tier still exists, so a legacy config entry still wins over the plugin's registration for the same id (the server's own precedence rule) and unregistered
acp-*ids still fall back to tier capabilities. Layer 6 deletes the tier, after which the plugin is the only source. Q21's per-instance capability probing is the next layer.