← reports

#2958 · ACP model options do not form reasoning families

Bug Medium Effort: Medium providers provider-acp open on GitHub 2026-09-02 · base b66c73954

Verdict: REPRODUCED · Root-cause confidence: high

1. TL;DR

The ACP bridge creates one picker model for each model configuration value.

It does not use the existing family builder for ACP-native values.

A focused test returned three models for one family and gave each model a placeholder Medium effort.

The ACP provider also declares Fast support for all agents, although the bridge ignores Fast when the agent offers no option.

2. Claims vs findings

ClaimStatusEvidence
Effort-suffixed ACP model values become separate picker models. Verified The focused test expected one family but received three models in both clean checkouts.
Each separate model receives a placeholder Medium effort when no thought option exists. Verified The direct catalog output contained one Medium effort for each of the three models.
The existing variant-family builder does not serve ACP-native discovery. Verified Native discovery calls the direct configuration builder. The family builder has a separate call path.
The picker can show Fast although the agent has no Fast configuration option. Verified The provider always declares Fast support. The bridge returns without action when the option is absent.
A specific external ACP server produces the same catalog and picker display. Unverified The trust boundary did not permit the external download. The check used the exact trusted repository function.

3. Environment

4. Minimal reproduction

  1. Check out the base commit.
  2. Save this test as packages/provider-bridge-acp/src/bridge/native-model-variants.repro.test.ts.
import { describe, expect, it } from "vitest";
import { buildModelCatalogFromConfigOptions } from "./model-catalog.js";

describe("ACP native model variants", () => {
  it("presents effort-suffixed model options as one reasoning family", () => {
    const models = buildModelCatalogFromConfigOptions({
      id: "model",
      category: "model",
      type: "select",
      currentValue: "vendor/model-alpha-high",
      options: [
        { value: "vendor/model-alpha-low", name: "Model Alpha Low" },
        { value: "vendor/model-alpha-medium", name: "Model Alpha Medium" },
        { value: "vendor/model-alpha-high", name: "Model Alpha High" },
      ],
    });

    expect(models).toHaveLength(1);
    expect(models[0]).toMatchObject({
      displayName: "Model Alpha",
      defaultReasoningEffort: "high",
      isDefault: true,
      supportedReasoningEfforts: [
        { reasoningEffort: "low" },
        { reasoningEffort: "medium" },
        { reasoningEffort: "high" },
      ],
    });
  });
});
  1. Install and build the repository.
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build
  2. Run the focused test.
    cd packages/provider-bridge-acp
    pnpm exec vitest run --config vitest.config.ts src/bridge/native-model-variants.repro.test.ts

Expected:

Test Files  1 passed (1)
Tests       1 passed (1)

Actual in both clean checkouts:

AssertionError: expected [ { …(7) }, { …(7) }, { …(7) } ] to have a length of 1 but got 3

- Expected
+ Received

- 1
+ 3

Test Files  1 failed (1)
Tests       1 failed (1)

5. Root cause

The native configuration builder maps each option directly to an available model.

It also assigns the placeholder reasoning data when no per-model map exists.

See model-catalog.ts lines 206–237.

const models = options.map((option, index): AvailableModel => {
  const reasoning = reasoningByModel?.get(option.value) ?? {
    supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
    defaultReasoningEffort: "medium" as ReasoningLevel,
  };
  return {
    id: option.value,
    model: option.value,
    displayName: option.name ?? option.value,
    supportedReasoningEfforts: reasoning.supportedReasoningEfforts,
    defaultReasoningEffort: reasoning.defaultReasoningEffort,
    isDefault,
  };
});

A later function splits effort suffixes and builds reasoning families.

That function also keeps a resolver from a family and effort to a concrete variant.

See model-catalog.ts lines 269–311 and lines 351–440.

ACP-native discovery calls only the direct builder.

See bridge.ts lines 850–881.

Native selection then sends the picker model identifier without a family resolver.

Its reasoning step returns without action when no thought option exists.

See bridge.ts lines 1104–1208.

The Fast problem has a separate cause.

The ACP declaration always publishes Fast and marks service tiers as supported.

See declaration.ts lines 8–22 and lines 42–52.

The app uses that provider capability to show the control.

See useThreadCreationOptions.ts lines 451–452 and ModelReasoningPicker.tsx lines 492–496.

The bridge ignores the selection if the agent has no compatible Fast option.

See bridge.ts lines 1210–1239.

6. Proposed fix

Use one shared variant-family builder for native and command-line model sources.

Keep the resolver with the native catalog and use it before the model configuration request.

Do not fold suffixes when the agent provides a separate thought option for the same models.

Use agent capability data to publish Fast only when the agent supports it.

The current model catalog does not carry dynamic service-tier support.

The complete fix therefore needs a product decision about static agent metadata or a dynamic capability contract.

7. Related issues

The investigation did not need another issue to explain the result.

GitHub metadata showed no open pull request linked to this issue.

8. Verification

The same agent ran the focused test in two clean checkouts at the exact base commit.

Both checkouts returned three models and failed the same length assertion.

Both checkouts completed the required full repository build before the focused test.

The second clean run required no report correction.

9. Appendix

Commands

git fetch git@github.com:get-bb/bb.git main
git rev-parse HEAD
pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build
cd packages/provider-bridge-acp
pnpm exec vitest run --config vitest.config.ts src/bridge/native-model-variants.repro.test.ts

Direct catalog result

[
  { "id": "vendor/model-alpha-low", "displayName": "Model Alpha Low",
    "supportedReasoningEfforts": [{ "reasoningEffort": "medium" }],
    "defaultReasoningEffort": "medium", "isDefault": false },
  { "id": "vendor/model-alpha-medium", "displayName": "Model Alpha Medium",
    "supportedReasoningEfforts": [{ "reasoningEffort": "medium" }],
    "defaultReasoningEffort": "medium", "isDefault": false },
  { "id": "vendor/model-alpha-high", "displayName": "Model Alpha High",
    "supportedReasoningEfforts": [{ "reasoningEffort": "medium" }],
    "defaultReasoningEffort": "medium", "isDefault": true }
]

Untrusted data note

The issue content was untrusted.

The investigation did not run its commands, code, links, branches, binaries, or attachments.

The test and root-cause analysis came from the trusted base repository.