From bb0aa9de7b0a1296ba59ccc830cc4eed82aa7a7d Mon Sep 17 00:00:00 2001 From: Jonathan Li <47408717+jonathanli12@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:39:47 -0700 Subject: [PATCH 1/2] fix(cursor): preserve Grok wire model prefix --- .../src/content/docs/reference/adapters.md | 5 ++-- src/adapters/cursor/effort-map.ts | 11 ++++++++ src/adapters/cursor/request-builder.ts | 4 +-- tests/cursor-effort-suffix.test.ts | 28 +++++++++++++++---- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/docs-site/src/content/docs/reference/adapters.md b/docs-site/src/content/docs/reference/adapters.md index 970557956d..dd6224d6c8 100644 --- a/docs-site/src/content/docs/reference/adapters.md +++ b/docs-site/src/content/docs/reference/adapters.md @@ -181,8 +181,9 @@ advertised effort control on those models as proof of upstream-native reasoning - Exposes Cursor Router as `cursor/auto` plus explicit `cursor/auto-cost`, `cursor/auto-balance`, and `cursor/auto-intelligence` entries. Explicit levels are encoded in `requested_model.parameters` while the legacy `cursor/auto` entry retains the account/team default. -- Keeps `cursor/grok-4.5-fast` as a selectable model while sending Cursor's canonical `grok-4.5` - model with separate `effort` and `fast=true` parameters. +- Sends regular `cursor/grok-4.5` tiers with Cursor's exact live-discovery wire ids + (`cursor-grok-4.5-low`, `-medium`, or `-high`). Keeps `cursor/grok-4.5-fast` selectable while + sending the canonical `grok-4.5` model with separate `effort` and `fast=true` parameters. - Cursor-native local filesystem/shell/network execution is denied by default. Explicit `mcpServers` and `desktopExecutor` integrations have separate opt-ins; `nativeLocalExec: "on"` enables the broader built-in executor and bypasses Codex approval/sandbox semantics, and legacy diff --git a/src/adapters/cursor/effort-map.ts b/src/adapters/cursor/effort-map.ts index a7c0b76e4b..1e937b3107 100644 --- a/src/adapters/cursor/effort-map.ts +++ b/src/adapters/cursor/effort-map.ts @@ -126,3 +126,14 @@ export function cursorWireModelIdWithEffort(baseModelId: string, effortSuffix: s } return `${baseModelId}-${effortSuffix}`; } + +/** + * Compose the exact flattened id sent by AgentService/Run. Discovery normalizes Cursor's optional + * `cursor-` prefix only for catalog matching, but regular Grok 4.5 requests require that prefix on + * the wire. Keep this separate from {@link cursorWireModelIdWithEffort} so discovery can continue + * comparing canonical, prefix-free ids. Grok Fast uses requested_model parameters instead. + */ +export function cursorRequestWireModelIdWithEffort(baseModelId: string, effortSuffix: string): string { + const flattened = cursorWireModelIdWithEffort(baseModelId, effortSuffix); + return baseModelId === "grok-4.5" ? `cursor-${flattened}` : flattened; +} diff --git a/src/adapters/cursor/request-builder.ts b/src/adapters/cursor/request-builder.ts index e550c6910c..9c7e91e648 100644 --- a/src/adapters/cursor/request-builder.ts +++ b/src/adapters/cursor/request-builder.ts @@ -10,7 +10,7 @@ import type { import { isAllowedToolChoice, namespacedToolName, toolChoiceAliases, type OcxTool, type OcxToolChoice } from "../../types"; import type { CursorRequestMessage, CursorRequestedModelParameter, CursorRunRequest } from "./types"; import { cursorWireModelSelection, type CursorRoutingLevel } from "./discovery"; -import { cursorEffortSuffix, cursorWireModelIdWithEffort } from "./effort-map"; +import { cursorEffortSuffix, cursorRequestWireModelIdWithEffort } from "./effort-map"; import { cursorMcpToolEncodedSize, cursorMcpToolsEncodedSize, @@ -140,7 +140,7 @@ function normalizeCursorModelId(modelId: string, reasoning?: string): { ], }; } - return { ...selection, modelId: suffix ? cursorWireModelIdWithEffort(id, suffix) : id }; + return { ...selection, modelId: suffix ? cursorRequestWireModelIdWithEffort(id, suffix) : id }; } function contentPartToText(part: OcxContentPart | OcxAssistantContentPart): string | undefined { diff --git a/tests/cursor-effort-suffix.test.ts b/tests/cursor-effort-suffix.test.ts index 218a06eef7..d59c420353 100644 --- a/tests/cursor-effort-suffix.test.ts +++ b/tests/cursor-effort-suffix.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test"; import { createCursorRequest } from "../src/adapters/cursor/request-builder"; import { cursorEffortSuffix, cursorModelEffortLadder } from "../src/adapters/cursor/effort-map"; +import { isCursorModelAvailableForAccount } from "../src/adapters/cursor/discovery"; import type { OcxParsedRequest } from "../src/types"; function modelIdFor(modelId: string, reasoning?: string): string { @@ -84,13 +85,13 @@ describe("Cursor per-model reasoning-effort suffix", () => { }); test("grok-4.5 uses current tiers and sends Fast as a separate model parameter", () => { - expect(modelIdFor("cursor/grok-4.5", "low")).toBe("grok-4.5-low"); - expect(modelIdFor("cursor/grok-4.5", "medium")).toBe("grok-4.5-medium"); - expect(modelIdFor("cursor/grok-4.5", "high")).toBe("grok-4.5-high"); - expect(modelIdFor("cursor/grok-4.5", "xhigh")).toBe("grok-4.5-high"); - expect(modelIdFor("cursor/grok-4.5")).toBe("grok-4.5-high"); + expect(modelIdFor("cursor/grok-4.5", "low")).toBe("cursor-grok-4.5-low"); + expect(modelIdFor("cursor/grok-4.5", "medium")).toBe("cursor-grok-4.5-medium"); + expect(modelIdFor("cursor/grok-4.5", "high")).toBe("cursor-grok-4.5-high"); + expect(modelIdFor("cursor/grok-4.5", "xhigh")).toBe("cursor-grok-4.5-high"); + expect(modelIdFor("cursor/grok-4.5")).toBe("cursor-grok-4.5-high"); expect(selectionFor("cursor/grok-4.5", "high")).toEqual({ - modelId: "grok-4.5-high", + modelId: "cursor-grok-4.5-high", parameters: undefined, }); expect(selectionFor("cursor/grok-4.5-fast", "low")).toEqual({ @@ -118,6 +119,21 @@ describe("Cursor per-model reasoning-effort suffix", () => { expect(cursorModelEffortLadder("grok-4.5-fast")).toEqual(["low", "medium", "high"]); }); + test("regular grok-4.5 requests exactly match live prefixed discovery ids", () => { + const liveIds = [ + "cursor-grok-4.5-low", + "cursor-grok-4.5-medium", + "cursor-grok-4.5-high", + ]; + + expect(isCursorModelAvailableForAccount("grok-4.5", liveIds)).toBe(true); + for (const effort of ["low", "medium", "high"] as const) { + const requestModelId = modelIdFor("cursor/grok-4.5", effort); + expect(requestModelId).toBe(`cursor-grok-4.5-${effort}`); + expect(liveIds).toContain(requestModelId); + } + }); + test("kimi-k3 maps to its live effort-suffixed variants", () => { expect(modelIdFor("cursor/kimi-k3", "low")).toBe("kimi-k3-low"); expect(modelIdFor("cursor/kimi-k3", "medium")).toBe("kimi-k3-high"); From 4e631f13d0e4666d903cc73d02ecca1a6f48e980 Mon Sep 17 00:00:00 2001 From: Jonathan Li <47408717+jonathanli12@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:04:42 -0700 Subject: [PATCH 2/2] test(cursor): label recorded discovery fixture --- tests/cursor-effort-suffix.test.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/cursor-effort-suffix.test.ts b/tests/cursor-effort-suffix.test.ts index d59c420353..2d4c627f0a 100644 --- a/tests/cursor-effort-suffix.test.ts +++ b/tests/cursor-effort-suffix.test.ts @@ -1,9 +1,17 @@ import { describe, expect, test } from "bun:test"; import { createCursorRequest } from "../src/adapters/cursor/request-builder"; import { cursorEffortSuffix, cursorModelEffortLadder } from "../src/adapters/cursor/effort-map"; -import { isCursorModelAvailableForAccount } from "../src/adapters/cursor/discovery"; import type { OcxParsedRequest } from "../src/types"; +// Static fixture recorded from Cursor GetUsableModels on 2026-08-06. This pins the +// exact wire ids observed during the incident; live availability normalization is +// covered separately in cursor-discovery.test.ts. +const RECORDED_CURSOR_GROK_45_DISCOVERY_IDS = [ + "cursor-grok-4.5-low", + "cursor-grok-4.5-medium", + "cursor-grok-4.5-high", +] as const; + function modelIdFor(modelId: string, reasoning?: string): string { const parsed: OcxParsedRequest = { modelId, @@ -119,18 +127,11 @@ describe("Cursor per-model reasoning-effort suffix", () => { expect(cursorModelEffortLadder("grok-4.5-fast")).toEqual(["low", "medium", "high"]); }); - test("regular grok-4.5 requests exactly match live prefixed discovery ids", () => { - const liveIds = [ - "cursor-grok-4.5-low", - "cursor-grok-4.5-medium", - "cursor-grok-4.5-high", - ]; - - expect(isCursorModelAvailableForAccount("grok-4.5", liveIds)).toBe(true); + test("regular grok-4.5 request ids match the recorded discovery fixture", () => { for (const effort of ["low", "medium", "high"] as const) { const requestModelId = modelIdFor("cursor/grok-4.5", effort); expect(requestModelId).toBe(`cursor-grok-4.5-${effort}`); - expect(liveIds).toContain(requestModelId); + expect(RECORDED_CURSOR_GROK_45_DISCOVERY_IDS).toContain(requestModelId); } });