← reports

#2503 · 0.40.0 Cursor picker: only Grok keeps a real reasoning ladder

Bug Priority: High Effort: Low–Medium providers provider-acp open on GitHub 2026-08-27 · base ad79bbb5ec909524f8f281e62d860c588a86f332

Verdict: REPRODUCED · Root-cause confidence: high · Linked open PRs: none

1. TL;DR

bb 0.40.0 shows a complete reasoning list for Cursor Grok 4.6. It shows only Medium for GPT-5.6 Sol and no reliable ladder for Opus or Fable. Cursor ACP still supplies full lists for all three models. bb probes 35 models in sequence, but it stops after five seconds. Grok has priority, so the probe usually does not reach the other primary models. If it reaches Opus or Fable, a second defect can select Thinking and return no mapped levels.

Composer 2.5 does not supply a reasoning option through Cursor ACP. Its single managed Medium entry does not hide a Cursor reasoning list.

2. Claims vs findings

Claim from the issueStatusEvidence
Only Grok keeps a real reasoning list in bb 0.40.0.VerifiedThe live CLI and UI show four Grok levels. The latest run showed Sol, Fable, and Composer at Medium. It showed no mapped Opus level because the probe reached Opus and selected Thinking.
Cursor ACP still supplies Sol levels from none through max.VerifiedThe direct ACP probe returned none, low, medium, high, xhigh, max for gpt-5.6-sol.
The five-second probe does not reach Sol.VerifiedThe code probes Grok 4.6 and Grok 4.5 first. It then probes the full 35-model list. Five direct primary-model changes took 5,802 ms in total.
Opus and Fable supply two thought_level options.VerifiedCursor returned Thinking first and Effort second. The focused test proved that bb selects Thinking and returns no mapped levels.
Composer 2.5 should have a real reasoning list.RefutedCursor ACP returned no thought_level option for Composer. It returned only Fast model control.
bb 0.39.0 used Cursor CLI model families.VerifiedThe desktop-v0.39.0 source used cursor-agent --list-models. It named Sol, Opus, and Fable reasoning families directly. This report did not run the old application.
The CLI reasoning argument works around the picker defect.UnverifiedThis report did not start a paid Cursor turn. The ACP probe only proved that Cursor accepts the model change and advertises the level.

3. Environment

Saved environment record

4. Minimal reproduction

4.1 Live catalog and UI

  1. Check out the base commit. Install the workspace. Build it with Turbo.
    git checkout ad79bbb5ec909524f8f281e62d860c588a86f332
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build
  2. Start the isolated dev app. Set the CLI environment.
    scripts/bb-dev-app current
    eval "$(scripts/bb-dev-app env)"
  3. Run the saved check from the repository root. The local path works before report publication. The raw URL works after publication.
    local_artifact=/tmp/bb-reports/issues/2503/repro/catalog-repro.sh
    if [[ -f "$local_artifact" ]]; then
      cp "$local_artifact" /tmp/bb-2503-catalog-repro.sh
    else
      curl -fsS https://raw.githubusercontent.com/get-bb/reports/main/issues/2503/repro/catalog-repro.sh \
        -o /tmp/bb-2503-catalog-repro.sh
    fi
    bash /tmp/bb-2503-catalog-repro.sh

    Expected: Sol supplies none, low, medium, high, xhigh, max.

    Actual:

    grok-4.6: low,medium,high,xhigh
    gpt-5.6-sol: medium
    claude-opus-5: <none>
    claude-fable-5: medium
    composer-2.5: medium
    FAIL: GPT-5.6 Sol expected ["none","low","medium","high","xhigh","max"] but received ["medium"]

    Files: catalog-repro.sh · catalog-output.txt

  4. Open New Thread. Select Cursor. Compare Grok 4.6 with GPT-5.6 Sol.
The Cursor picker shows four reasoning levels for Grok 4.6
Cursor Grok 4.6 shows Low, Medium, High, and Extra High.
The Cursor picker shows only Medium reasoning for GPT-5.6 Sol
GPT-5.6 Sol shows only Medium in the same picker.

4.2 Direct Cursor ACP control

This probe uses no bb catalog code. It proves that Cursor still supplies the missing data.

local_artifact=/tmp/bb-reports/issues/2503/repro/cursor-acp-probe.mjs
if [[ -f "$local_artifact" ]]; then
  cp "$local_artifact" /tmp/bb-2503-cursor-acp-probe.mjs
else
  curl -fsS https://raw.githubusercontent.com/get-bb/reports/main/issues/2503/repro/cursor-acp-probe.mjs \
    -o /tmp/bb-2503-cursor-acp-probe.mjs
fi
node /tmp/bb-2503-cursor-acp-probe.mjs
model-count: 35
{"model":"grok-4.6","elapsedMs":1189,"thoughtLevel":[{"id":"effort","values":["low","medium","high","xhigh"]}]}
{"model":"gpt-5.6-sol","elapsedMs":1063,"thoughtLevel":[{"id":"reasoning","values":["none","low","medium","high","xhigh","max"]}]}
{"model":"claude-opus-5","elapsedMs":958,"thoughtLevel":[{"id":"thinking","values":["false","true"]},{"id":"effort","values":["low","medium","high","xhigh","max"]}]}
{"model":"claude-fable-5","elapsedMs":1179,"thoughtLevel":[{"id":"thinking","values":["false","true"]},{"id":"effort","values":["low","medium","high","xhigh","max"]}]}
{"model":"composer-2.5","elapsedMs":1413,"thoughtLevel":[]}

Files: cursor-acp-probe.mjs · cursor-acp-probe.log

4.3 Focused failing test

Copy the test into the bridge package. Run the package test through Turbo.

local_artifact=/tmp/bb-reports/issues/2503/repro/issue-2503.repro.test.ts
if [[ -f "$local_artifact" ]]; then
  cp "$local_artifact" /tmp/bb-2503.repro.test.ts
else
  curl -fsS https://raw.githubusercontent.com/get-bb/reports/main/issues/2503/repro/issue-2503.repro.test.ts \
    -o /tmp/bb-2503.repro.test.ts
fi
cp /tmp/bb-2503.repro.test.ts packages/provider-bridge-acp/src/bridge/issue-2503.repro.test.ts
pnpm exec turbo run test --filter=@bb/provider-bridge-acp --force

The test file contains this complete test:

import { describe, expect, it } from "vitest";

import {
  buildAcpNativeReasoningSupport,
  findAcpThoughtLevelConfigOption,
} from "./model-catalog.js";

describe("issue #2503", () => {
  it("uses the effort ladder when Cursor returns Thinking before Effort", () => {
    const thinking = {
      id: "thinking",
      name: "Thinking",
      category: "thought_level",
      type: "select",
      currentValue: "true",
      options: [{ value: "true" }, { value: "false" }],
    };
    const effort = {
      id: "effort",
      name: "Effort",
      category: "thought_level",
      type: "select",
      currentValue: "medium",
      options: [
        { value: "none" },
        { value: "low" },
        { value: "medium" },
        { value: "high" },
        { value: "xhigh" },
        { value: "max" },
      ],
    };

    const selected = findAcpThoughtLevelConfigOption([thinking, effort]);
    const support = buildAcpNativeReasoningSupport(selected);

    expect({
      selectedId: selected?.id,
      levels: support.supportedReasoningEfforts.map(
        (entry) => entry.reasoningEffort,
      ),
    }).toEqual({
      selectedId: "effort",
      levels: ["none", "low", "medium", "high", "xhigh", "max"],
    });
  });
});

Actual Vitest result:

FAIL  src/bridge/issue-2503.repro.test.ts
AssertionError: expected { selectedId: 'thinking', levels: [] } to deeply equal { selectedId: 'effort', …(1) }

Test Files  1 failed | 17 passed (18)
Tests       1 failed | 288 passed (289)

Files: issue-2503.repro.test.ts · unit-test.log

5. Root cause

5.1 The bounded probe gives its budget to the wrong models

Commit 5d3e7dd59 changed Cursor to the parameterized ACP picker. The change kept a five-second shared probe deadline. It also gave Grok 4.6 and Grok 4.5 first priority.

The Cursor definition lists six primary models. It gives probe priority to two Grok models:

primaryModels: [
  "default",
  "grok-4.6",
  "gpt-5.6-sol",
  "claude-opus-5",
  "claude-fable-5",
  "composer-2.5",
],
reasoningProbePriorityModelIds: ["grok-4.6", "grok-4.5"],

Source: known-agents.ts lines 73–86.

The bridge adds priority models first. It then adds every other model. Thus, the primary list does not limit the probe:

for (const value of args.reasoningProbePriorityModelIds) { /* add first */ }
for (const model of modelOptions) { /* add every remaining model */ }

timeout = setTimeout(() => {
  args.connection.kill();
  resolve(supportByModel);
}, ACP_NATIVE_REASONING_DISCOVERY_TIMEOUT_MS);

Sources: probe loop and timeout · five-second constant.

The bridge does not split primary models until after it completes the full discovery call. Therefore, selected-only models use time before Sol, Opus, and Fable can use it. See handleModelList lines 2600–2637.

When the timeout omits a model from the result map, the catalog inserts the dummy managed Medium entry:

const reasoning = reasoningByModel?.get(option.value) ?? {
  supportedReasoningEfforts: ACP_NATIVE_REASONING_EFFORTS,
  defaultReasoningEffort: "medium",
};

Source: model-catalog.ts lines 259–283.

5.2 The first-option rule selects Thinking instead of Effort

Cursor returns two thought_level options for Opus and Fable. The first option uses boolean values. The second option uses the real effort values. bb returns the first option without a value check:

return (configOptions ?? []).find(
  (option) => option.category === "thought_level",
);

Source: model-catalog.ts lines 140–145.

This rule maps Thinking to no bb levels. The same helper controls catalog discovery and live session selection. A probe-order change alone cannot fix Opus and Fable.

5.3 The 0.39.0 catalog read model variants from the Cursor CLI

The desktop-v0.39.0 tag resolves to b33abbff098ac4c857578e7350d492dcaa65d489. Its Cursor launch specification used the CLI model list and named the reasoning variants:

command: "cursor-agent",
modelCli: {
  listArgs: ["--list-models"],
  selectFlag: "--model",
  primaryModels: [
    "gpt-5.6-sol-medium",
    "claude-opus-5-thinking-medium",
    "claude-fable-5-thinking-medium",
  ],
},

Source: desktop-v0.39.0 acp-launch-specs.ts lines 14–39.

5.4 Current main status

origin/main was the base commit during this investigation. The path log after the base commit was empty. Therefore, current main has no later fix for these files.

6. Proposed fix (first principles)

  1. Pass the existing primary model list into reasoning discovery. Probe each primary model before any selected-only model.
  2. Give the primary set enough time. Use a Cursor-specific budget near 12 seconds, or load selected-only reasoning only after selection.
  3. Select the thought_level option with the most values that map to bb reasoning levels. Do not select the first option only.
  4. Use the same selected option for discovery and live session setup. This keeps the picker and the launched session equal.
  5. Add a 35-model delay test. Assert correct lists for Sol, Opus, and Fable. Add the two-option test from this report.
  6. Keep Composer as agent-managed or hide its reasoning row. Do not invent a list that Cursor does not supply.

The code can reuse the current primaryModels field. This change does not require a new wire field. If the fix changes a server-daemon field or its meaning, increment HOST_DAEMON_PROTOCOL_VERSION.

7. Related issues

8. Appendix

Commands run

gh issue view 2503 --comments --json number,title,body,comments,labels,projectItems,state,url
pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build
git fetch origin main
scripts/bb-dev-app current
eval "$(scripts/bb-dev-app env)"
pnpm bb:dev provider models acp-cursor --json
bash /tmp/bb-reports/issues/2503/repro/catalog-repro.sh
node /tmp/bb-reports/issues/2503/repro/cursor-acp-probe.mjs
pnpm exec turbo run test --filter=@bb/provider-bridge-acp --force
git log ad79bbb5ec909524f8f281e62d860c588a86f332..origin/main --oneline -- \
  plugins/provider-acp/src/known-agents.ts \
  packages/provider-bridge-acp/src/bridge/model-catalog.ts \
  packages/provider-bridge-acp/src/bridge/bridge.ts

Artifact inventory

Verification

The independent verifier checked the local catalog, direct ACP probe, focused test, UI screenshots, and base-commit code claims. The initial Pages-only downloads returned HTTP 404 before publication, and the ACP command used a missing repository path. This revision adds an explicit local artifact path with a raw GitHub fallback. It copies all three artifacts to /tmp before use. The revision reran the corrected commands from a detached base-commit worktree. The catalog failed as documented, the ACP probe passed, and the focused test failed with the documented assertion. It also confirmed that origin/main still equals the base commit and added the desktop-v0.39.0 permalink and excerpt.