← reports

#1688 · Cursor Grok 4.6 is hidden from the primary model picker

Bug Low Effort: Small providers provider-acp open on GitHub 2026-08-18 base 16ceb3a540f81c1189efaffb27a39b1d9443abf5 (main)

Verdict: REPRODUCED · root-cause confidence: high · linked PRs: #1687 (already CLOSED as obsolete on 2026-08-18; reviewed below anyway)

TL;DR

Plain-language framing. bb does not know Cursor's model list itself; it asks the Cursor CLI (cursor-agent --list-models) at picker time. That list has ~200 raw ids (cursor-grok-4.6-low, cursor-grok-4.6-medium-fast, …) which bb's ACP bridge folds into "families" (one row per model, with a reasoning ladder and a Fast-mode toggle). The picker then shows only a short curated subset of those families up front and pushes the rest behind a "More models" row. Which families are "up front" is not discovered: it is a hard-coded list of family ids (primaryModels) that ships inside bb.

The user sees "Cursor Grok 4.5" in the Cursor tab of the model picker (and in bb provider models acp-cursor) but no "Cursor Grok 4.6", even though Cursor already offers Grok 4.6 (with low/medium/high/xhigh efforts and fast variants). Grok 4.6 is there, but only under "More models"; the CLI omits it unless --selected-model cursor-grok-4.6-medium is passed. The reason is exactly what the issue says: the hard-coded list BUILT_IN_ACP_LAUNCH_SPECS["acp-cursor"].modelCli.primaryModels in packages/agent-runtime/src/acp-launch-specs.ts still names cursor-grok-4.5-medium. Since splitPrimaryModels matches by exact family id, the newer family cannot be primary until someone edits that string. This is a stale-constant bug, not a parsing or discovery bug; the deeper issue is that the "recommended models" policy is pinned to exact version ids and silently rots every time Cursor ships a new family (same thing happened for Grok 4.5 in #933).

Reproduced on 16ceb3a54 with the real Cursor CLI (2026.08.11-e8db854) both via the CLI and in the app, plus a vitest that fails on main and passes with a one-line change. The linked PR #1687 makes the right one-line change but to a file (packages/agent-runtime/src/acp/profiles.ts) that no longer exists on main (removed in #1640), so it cannot merge (GitHub reports CONFLICTING); it was closed as obsolete. Nothing on origin/main after the base commit fixes this.

Claims vs findings

ClaimStatusEvidence
Cursor CLI advertises the Grok 4.6 familyVerifiedcursor-agent --list-models (2026.08.11-e8db854) prints 9 cursor-grok-4.6-* ids (low/medium/high/xhigh, some with -fast). Full output: cursor-agent-list-models.txt.
bb's built-in primary list still names cursor-grok-4.5-mediumVerifiedacp-launch-specs.ts#L30-L37. Note the issue and PR still point at packages/agent-runtime/src/acp/profiles.ts, which was deleted by #1640 (c5b53caab); the list moved, unchanged, to acp-launch-specs.ts.
bb provider models acp-cursor omits Grok 4.6VerifiedOutput below: six rows, Grok 4.5 is second, no 4.6 (cli-provider-models-acp-cursor.txt).
App puts Grok 4.6 under "More models"VerifiedScreenshots 1688-picker-primary.png and 1688-picker-more-models.png: "Cursor Grok 4.6" is the third entry of the More-models submenu.
CLI shows it only with --selected-model cursor-grok-4.6-mediumVerifiedOutput below (cli-provider-models-selected-model.txt); includeSelectedOnlyModel in apps/cli/src/commands/provider.ts#L110-L123 prepends the selected-only match.
Grok 4.6 is "classified as a selected-only model"VerifiedThe bridge's splitPrimaryModels puts every family whose id is not in primaryModels into selectedOnlyModels; the Grok 4.6 family id is cursor-grok-4.6-medium. Vitest below asserts this and fails on main.
Grok 4.5 remains primaryVerifiedCursor still lists cursor-grok-4.5-{low,medium,high}[-fast], so the family id cursor-grok-4.5-medium still matches and stays second in the picker.
Recursive tool-schema failure (#1612) is separateVerified#1612 is a per-turn provider error and was closed by #1613 (c25298f69, on origin/main after the base commit). This issue is purely picker classification.

Environment

Minimal reproduction

A. CLI (needs the Cursor CLI on PATH)

  1. Build and start a dev instance: pnpm install --frozen-lockfile --prefer-offline && pnpm exec turbo run build && scripts/bb-dev-app current. Export the printed env: export BB_SERVER_URL=http://localhost:<server port>.
  2. Confirm Cursor offers Grok 4.6: cursor-agent --list-models | grep grok
    cursor-grok-4.6-high-fast - Cursor Grok 4.6 Fast
    cursor-grok-4.5-high - Cursor Grok 4.5
    cursor-grok-4.5-high-fast - Cursor Grok 4.5 Fast
    cursor-grok-4.6-low - Cursor Grok 4.6 Low
    cursor-grok-4.6-low-fast - Cursor Grok 4.6 Low Fast
    cursor-grok-4.6-medium - Cursor Grok 4.6 Medium
    cursor-grok-4.6-medium-fast - Cursor Grok 4.6 Medium Fast
    cursor-grok-4.6-high - Cursor Grok 4.6
    cursor-grok-4.6-xhigh - Cursor Grok 4.6 Extra High
    cursor-grok-4.6-xhigh-fast - Cursor Grok 4.6 Extra High Fast
    cursor-grok-4.5-low - Cursor Grok 4.5 Low
    cursor-grok-4.5-low-fast - Cursor Grok 4.5 Low Fast
    cursor-grok-4.5-medium - Cursor Grok 4.5 Medium
    cursor-grok-4.5-medium-fast - Cursor Grok 4.5 Medium Fast
  3. Ask bb for the Cursor picker list: node packages/scripts/dist/commands/run-cli.js provider models acp-cursor

Expected: a "Cursor Grok 4.6" row (family id cursor-grok-4.6-medium) in the default list. Actual:

Models for acp-cursor:

Model                           Name             Default
------------------------------  ---------------  -------
auto                            Auto             *
------------------------------  ---------------  -------
cursor-grok-4.5-medium          Cursor Grok 4.5
------------------------------  ---------------  -------
gpt-5.6-sol-medium              GPT-5.6 Sol
------------------------------  ---------------  -------
claude-opus-5-thinking-medium   Claude Opus 5
------------------------------  ---------------  -------
claude-fable-5-thinking-medium  Claude Fable 5
------------------------------  ---------------  -------
composer-2.5                    Composer 2.5

Only when the caller already knows the id does it show up (prepended by the CLI's includeSelectedOnlyModel):

$ node packages/scripts/dist/commands/run-cli.js provider models acp-cursor --selected-model cursor-grok-4.6-medium
Models for acp-cursor:

Model                           Name             Default
------------------------------  ---------------  -------
cursor-grok-4.6-medium          Cursor Grok 4.6
------------------------------  ---------------  -------
auto                            Auto             *
------------------------------  ---------------  -------
cursor-grok-4.5-medium          Cursor Grok 4.5
------------------------------  ---------------  -------
gpt-5.6-sol-medium              GPT-5.6 Sol
------------------------------  ---------------  -------
claude-opus-5-thinking-medium   Claude Opus 5
------------------------------  ---------------  -------
claude-fable-5-thinking-medium  Claude Fable 5
------------------------------  ---------------  -------
composer-2.5                    Composer 2.5

B. App

Open the app, click New thread, click the provider/model button in the composer ("Provider, model and reasoning"), select the Cursor tab.

Cursor tab of the model picker
Cursor tab of the model picker on 16ceb3a54. Primary list is Auto, Cursor Grok 4.5, GPT-5.6 Sol, Claude Opus 5, Claude Fable 5, Composer 2.5, then a "More models" row. No Grok 4.6.
More models submenu with Cursor Grok 4.6
After clicking "More models": "Cursor Grok 4.6" is the third hidden entry (between GPT-5.2 and Gemini 3.7 Flash), i.e. it was discovered and folded correctly but classified as selected-only.

Browser driver scripts used: browser-cursor-picker.js, browser-more-models.js (dev-browser).

C. Unit-level repro (fails on main)

File: plugins/provider-acp/src/bridge/issue-1688-grok-4.6-primary.test.ts, fixture issue-1688-cursor-list-models.txt (the captured cursor-agent --list-models output; copy both into plugins/provider-acp/src/bridge/). It runs the exact pipeline the bridge's model/list handler runs, with the exact built-in policy.

/**
 * Repro for get-bb/bb#1688: Cursor Grok 4.6 is hidden from the primary
 * model picker.
 *
 * Feeds real `cursor-agent --list-models` output (Cursor CLI 2026.08.11)
 * through the same pipeline the ACP bridge uses for `model/list`
 * (parseAgentModelLines -> buildAgentModelCatalog -> splitPrimaryModels)
 * with the built-in acp-cursor primaryModels policy. On main the Grok 4.6
 * family lands in `selectedOnlyModels` because the policy still names the
 * Grok 4.5 family id.
 */
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { BUILT_IN_ACP_LAUNCH_SPECS } from "../../../../packages/agent-runtime/src/acp-launch-specs.js";
import {
  buildAgentModelCatalog,
  parseAgentModelLines,
  splitPrimaryModels,
} from "./model-catalog.js";

// Captured with `cursor-agent --list-models` (2026.08.11-e8db854).
const CURSOR_LIST_MODELS = readFileSync(
  new URL("./issue-1688-cursor-list-models.txt", import.meta.url),
  "utf8",
);

describe("issue #1688: Cursor Grok 4.6 primary placement", () => {
  const primaryModels =
    BUILT_IN_ACP_LAUNCH_SPECS["acp-cursor"].modelCli!.primaryModels;
  const catalog = buildAgentModelCatalog(
    parseAgentModelLines(CURSOR_LIST_MODELS),
  )!;
  const split = splitPrimaryModels(catalog.models, primaryModels);

  it("folds the Grok 4.6 variants into one family keyed by cursor-grok-4.6-medium", () => {
    const grok46 = catalog.models.find(
      (m) => m.displayName === "Cursor Grok 4.6",
    );
    expect(grok46?.id).toBe("cursor-grok-4.6-medium");
    expect(
      grok46?.supportedReasoningEfforts.map((e) => e.reasoningEffort),
    ).toEqual(["low", "medium", "high", "xhigh"]);
  });

  it("puts Cursor Grok 4.6 in the primary (default picker) list", () => {
    const primaryIds = split.models.map((m) => m.id);
    const selectedOnlyIds = split.selectedOnlyModels.map((m) => m.id);
    // Sanity: the older Grok family is primary today.
    expect(primaryIds).toContain("cursor-grok-4.5-medium");
    // BUG on main: Grok 4.6 is selected-only, not primary.
    expect(selectedOnlyIds).not.toContain("cursor-grok-4.6-medium");
    expect(primaryIds).toContain("cursor-grok-4.6-medium");
  });
});

Run from plugins/provider-acp: pnpm exec vitest run src/bridge/issue-1688-grok-4.6-primary.test.ts. The first test passes (proves the family id is cursor-grok-4.6-medium with ladder low/medium/high/xhigh); the second fails because cursor-grok-4.6-medium is in selectedOnlyModels (vitest-main.txt):

 RUN  v4.1.1 /home/sawyer/projects/bb/.claude/worktrees/wf_242c3e11-a10-38/plugins/provider-acp

 ❯  bb-plugin-provider-acp  src/bridge/issue-1688-grok-4.6-primary.test.ts (2 tests | 1 failed) 5ms
     × puts Cursor Grok 4.6 in the primary (default picker) list 3ms

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL   bb-plugin-provider-acp  src/bridge/issue-1688-grok-4.6-primary.test.ts > issue #1688: Cursor Grok 4.6 primary placement > puts Cursor Grok 4.6 in the primary (default picker) list
AssertionError: expected [ 'gpt-5.3-codex', 'gpt-5.2', …(26) ] to not include 'cursor-grok-4.6-medium'
 ❯ src/bridge/issue-1688-grok-4.6-primary.test.ts:51:33
     49|     expect(primaryIds).toContain("cursor-grok-4.5-medium");
     50|     // BUG on main: Grok 4.6 is selected-only, not primary.
     51|     expect(selectedOnlyIds).not.toContain("cursor-grok-4.6-medium");
       |                                 ^
     52|     expect(primaryIds).toContain("cursor-grok-4.6-medium");
     53|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯


 Test Files  1 failed (1)
      Tests  1 failed | 1 passed (2)
   Start at  07:59:32
   Duration  632ms (transform 315ms, setup 0ms, import 536ms, tests 5ms, environment 0ms)

With the one-line change from proposed-fix.diff applied (and the 4.5 sanity assertion dropped, since it is intentionally replaced) both tests pass (vitest-with-fix.txt):

diff --git a/packages/agent-runtime/src/acp-launch-specs.ts b/packages/agent-runtime/src/acp-launch-specs.ts
index c42000eb8..4096a073a 100644
--- a/packages/agent-runtime/src/acp-launch-specs.ts
+++ b/packages/agent-runtime/src/acp-launch-specs.ts
@@ -29,7 +29,7 @@ export const BUILT_IN_ACP_LAUNCH_SPECS: Readonly<
       // catalog folds effort and the `-fast` tail into one entry per family.
       primaryModels: [
         "auto",
-        "cursor-grok-4.5-medium",
+        "cursor-grok-4.6-medium",
         "gpt-5.6-sol-medium",
         "claude-opus-5-thinking-medium",
         "claude-fable-5-thinking-medium",
 RUN  v4.1.1 /home/sawyer/projects/bb/.claude/worktrees/wf_242c3e11-a10-38/plugins/provider-acp


 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  07:59:42
   Duration  646ms (transform 331ms, setup 0ms, import 558ms, tests 3ms, environment 0ms)

Root cause

Where the list comes from. The bundled Cursor provider has no server-side entry; when the runtime packs the ACP bridge's provider statics it falls back to a hard-coded table (provider-registry.ts#L158-L163) whose Cursor entry pins the picker's default families by exact family id (acp-launch-specs.ts#L26-L38):

modelCli: {
  listArgs: ["--list-models"],
  selectFlag: "--model",
  // Family ids (the default variant's raw id), not raw variant ids: the
  // catalog folds effort and the `-fast` tail into one entry per family.
  primaryModels: [
    "auto",
    "cursor-grok-4.5-medium",      // <-- stale: Cursor now ships Grok 4.6
    "gpt-5.6-sol-medium",
    "claude-opus-5-thinking-medium",
    "claude-fable-5-thinking-medium",
    "composer-2.5",
  ],
},

How it is applied. The spec becomes an AcpAgentProfile; buildAcpModelListParams copies primaryModels into the bridge's model/list params (session-params.ts#L212-L232). In the bridge, handleModelList spawns the list command, folds the raw ids into families with buildAgentModelCatalog, then calls splitPrimaryModels(models, params.primaryModels) (bridge.ts#L2124-L2143). That split is a pure exact-id lookup (model-catalog.ts#L570-L590):

const primaryIds = new Set(primaryModels);
const modelsById = new Map(catalogModels.map((model) => [model.id, model]));
const models = primaryModels.flatMap((id) => {
  const model = modelsById.get(id);
  return model ? [model] : [];
});
…
const selectedOnlyModels = catalogModels.filter(
  (model) => !primaryIds.has(model.id),
);

Why the family id is cursor-grok-4.6-medium. buildAgentModelCatalog keys a family by the raw id of its default variant: the non-fast "medium" member when present (model-catalog.ts#L494-L502). Cursor lists cursor-grok-4.6-medium, so the family is cursor-grok-4.6-medium / "Cursor Grok 4.6" with efforts low/medium/high/xhigh (first test above). That id is not in primaryModels, so it lands in selectedOnlyModels. The server passes both arrays through (execution-options.ts#L340-L351), the app renders selectedOnlyModels behind "More models" (ModelReasoningPicker.tsx#L214), and the CLI hides them unless --selected-model names one (provider.ts#L110-L123). The symptom follows directly.

Deeper issue. The "recommended" policy is a version-pinned constant maintained by hand. Cursor released Grok 4.5 → #933 / 9daeffb7f edited the constant; Grok 4.6 → this issue. Nothing detects the rot (an all-miss list falls back to the full picker, a partial miss silently drops the entry), and the pinned ids live in packages/agent-runtime (host-daemon side) even though AGENTS.md says the server owns product defaults such as tool/model lists. Neither is a blocker for the one-line fix, but the fix will rot again on the next Cursor release.

Proposed fix (first principles)

  1. Immediate: in packages/agent-runtime/src/acp-launch-specs.ts replace "cursor-grok-4.5-medium" with "cursor-grok-4.6-medium" (proposed-fix.diff). Grok 4.5 moves to "More models" and stays selectable; existing threads pinned to a 4.5 id are unaffected because splitPrimaryModels only classifies, and the CLI/app re-include a selected model. No HOST_DAEMON_PROTOCOL_VERSION bump: primaryModels is already string[] on the wire, only a value changes (same as 9daeffb7f). No test in the repo pins the 4.5 id (grep -rn cursor-grok-4.5 hits only the spec). Add the vitest above (drop the 4.5 sanity assertion) so the next drift is at least visible when the fixture is refreshed.
  2. Durable (optional, larger): stop pinning versions. Either (a) let primaryModels entries be family-prefix patterns (e.g. cursor-grok-*, gpt-5.6-sol-*) and pick the highest version present per pattern in splitPrimaryModels, or (b) keep exact ids but move the table to the server (product policy) so it can be updated without a daemon release, and add a startup/CI check that logs when a listed id matches nothing in the live catalog. Risk with (a): version comparison across Cursor's ad-hoc naming (gpt-5.6-sol vs gpt-5.6-luna, claude-opus-5-thinking) needs care; a simple numeric-suffix comparison inside one family prefix is enough for the Grok case. Option (a) would also change the meaning of an existing wire field and so needs a HOST_DAEMON_PROTOCOL_VERSION bump.

PR review

#1687 · Promote Cursor Grok 4.6 in the model picker (state: CLOSED 2026-08-18, mergeable: CONFLICTING)

What it changes. One line: packages/agent-runtime/src/acp/profiles.ts "cursor-grok-4.5-medium""cursor-grok-4.6-medium". Head d25224295, based on ba4265453 (before #1640).

Does it address the root cause? The intent is exactly the immediate fix (the value is the correct family id; I verified it against the real catalog in the vitest). But the file it edits was deleted on main by #1640 (c5b53caab): git rename-detects it against plugins/provider-acp/src/profiles.ts, which no longer contains any primaryModels, and the merge conflicts (pr-1687-merge-tree.txt):

2d13e1e036e7e7dc59a87e8950392658d765195c
100644 ced6cd92e7778860f0aae8a142f3a714545999bd 1	plugins/provider-acp/src/profiles.ts
100644 4ec4f1ae2c05dd1ae735e7f5683bccab19f81e99 2	plugins/provider-acp/src/profiles.ts
100644 4752d057849e384005a19cad816a403b28164233 3	plugins/provider-acp/src/profiles.ts

Auto-merging plugins/provider-acp/src/profiles.ts
CONFLICT (content): Merge conflict in plugins/provider-acp/src/profiles.ts

Even if the conflict were resolved by dropping the hunk, the PR would change nothing, because the live policy is now in packages/agent-runtime/src/acp-launch-specs.ts#L32. So on today's main it does not fix the issue.

Findings.

Tests I ran. Fetched pull/1687/head; gh pr view --json mergeableCONFLICTING; git merge-tree --write-tree 16ceb3a54 pr-1687 → content conflict on the renamed file. Applied the equivalent one-line change to acp-launch-specs.ts in my worktree instead: repro vitest goes red → green (section C); reverted afterwards.

Verdict: CLOSE (already closed by the maintainer as obsolete; agree). A fresh PR against acp-launch-specs.ts with the fixture test is a MERGE-quality change.

Related issues

Appendix

Commands run

gh issue view 1688 --json title,body,state,labels,comments
gh pr view 1687; gh pr diff 1687; gh pr view 1687 --json mergeable,mergeStateStatus,headRefOid,comments
git checkout 16ceb3a54 && pnpm install --frozen-lockfile --prefer-offline && pnpm exec turbo run build
git fetch origin main; git log 16ceb3a54..origin/main -- packages/agent-runtime/src/acp-launch-specs.ts plugins/provider-acp/src/bridge/model-catalog.ts   # empty
grep -rn "primaryModels" --include=*.ts apps packages plugins | grep -v node_modules
git log --oneline -S"cursor-grok-4.5-medium"      # c5b53caab (#1640), 9daeffb7f (#937 / #933)
git ls-tree 16ceb3a54 -- packages/agent-runtime/src/acp/profiles.ts   # empty: file gone
cursor-agent --version; cursor-agent --list-models > 1688/repro/cursor-agent-list-models.txt
scripts/bb-dev-app current; export BB_SERVER_URL=http://localhost:20192 BB_HOST_DAEMON_PORT=28192 BB_PROJECT_ID=proj_personal
node packages/scripts/dist/commands/run-cli.js machine list --json
curl -s -X POST $BB_SERVER_URL/api/v1/projects -H 'content-type: application/json' -d '{"name":"qa","source":{"type":"local_path","path":"/tmp/bb-1688-scratch","hostId":"host_ifkswh6bau"}}'
node packages/scripts/dist/commands/run-cli.js provider models acp-cursor
node packages/scripts/dist/commands/run-cli.js provider models acp-cursor --json
node packages/scripts/dist/commands/run-cli.js provider models acp-cursor --selected-model cursor-grok-4.6-medium
dev-browser --browser bb1688 --headless run 1688/repro/browser-cursor-picker.js
dev-browser --browser bb1688 --headless run 1688/repro/browser-more-models.js
cd plugins/provider-acp && pnpm exec vitest run src/bridge/issue-1688-grok-4.6-primary.test.ts   # fails on main
sed -i 's/"cursor-grok-4.5-medium",/"cursor-grok-4.6-medium",/' packages/agent-runtime/src/acp-launch-specs.ts && (re-run vitest: passes) && git checkout packages/agent-runtime/src/acp-launch-specs.ts
git fetch origin pull/1687/head:pr-1687; git merge-tree --write-tree 16ceb3a54 pr-1687
pnpm dev:stop

Family view of the Cursor catalog as returned to the CLI (--json, condensed)

auto                           | Auto           | medium                          | default: medium
cursor-grok-4.5-medium         | Cursor Grok 4.5| low,medium,high                 | default: medium
gpt-5.6-sol-medium             | GPT-5.6 Sol    | none,low,medium,high,xhigh,max  | default: medium
claude-opus-5-thinking-medium  | Claude Opus 5  | none,low,medium,high,xhigh,max  | default: medium
claude-fable-5-thinking-medium | Claude Fable 5 | none,low,medium,high,xhigh,max  | default: medium
composer-2.5                   | Composer 2.5   | medium                          | default: medium

Full JSON: cli-provider-models-acp-cursor.json. Selected-only families seen in the app's More-models submenu (accessibility snapshot): Codex 5.3, GPT-5.2, Cursor Grok 4.6, Gemini 3.7 Flash, Claude Sonnet 5, GPT-5.6 Luna, Claude Opus 4.8, GPT-5.5, GPT-5.6 Terra, …