fix(cursor): preserve native Claude effort model IDs before executor dispatch (#12838)

This commit is contained in:
Dominatorrr
2026-09-18 17:29:31 +03:00
committed by GitHub
parent 3e7764e57d
commit e20f5f34ea
3 changed files with 67 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
---
title: "Cursor API provider and the Cursor CLI passthrough"
version: 3.8.50
lastUpdated: 2026-08-19
version: 3.8.51
lastUpdated: 2026-09-05
---
# Cursor API provider and the Cursor CLI passthrough
@@ -66,6 +66,18 @@ Notes:
- `POST /api/providers/{id}/refresh-cursor` is for the `cursor` IDE provider
only; `cursor-api` connections have no IDE session to renew.
## Native model IDs and effort
For `cursor` / `cu` and `cursor-api` / `cua`, the shared Claude-effort normalizer
leaves the requested model ID intact. Cursor can advertise a suffix such as
`-low` as part of a real model ID, rather than as an OmniRoute effort alias.
The Cursor executor preserves an exact live-catalog match; when there is no
match, its existing model resolver owns suffix-to-parameter fallback.
This does not change effort normalization for direct Claude, Claude-compatible,
or Vertex routes. Availability still depends on the selected Cursor account's
catalog and entitlement.
## Cursor CLI passthrough
Route: `src/app/api/cursor-cli/[...path]/route.ts`

View File

@@ -38,6 +38,17 @@ export function applyClaudeEffortVariant(opts: {
sourceFormat: string;
}): { effectiveModel: string; log: string | null } {
const { provider, body, sourceFormat } = opts;
// Cursor advertises native effort-suffixed ids. Its executor resolves exact
// live-catalog ids before applying its own suffix fallback; stripping here
// destroys that information before the executor can see it.
if (
provider === "cursor" ||
provider === "cu" ||
provider === "cursor-api" ||
provider === "cua"
) {
return { effectiveModel: opts.effectiveModel, log: null };
}
let effectiveModel = opts.effectiveModel;
let log: string | null = null;

View File

@@ -10,6 +10,48 @@ import { test } from "node:test";
import assert from "node:assert/strict";
import { applyClaudeEffortVariant } from "../../open-sse/handlers/chatCore/claudeEffortVariant.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
import { resolveRequestedModel } from "../../open-sse/utils/cursorAgentProtobuf.ts";
for (const provider of ["cursor", "cu", "cursor-api", "cua"]) {
for (const sourceFormat of [FORMATS.OPENAI, FORMATS.CLAUDE, FORMATS.OPENAI_RESPONSES]) {
test(`${provider}/${sourceFormat}: preserves native Cursor Claude model ids before executor resolution`, () => {
for (const model of [
"claude-fable-5-1-low",
"claude-fable-5-1-thinking-low",
"claude-opus-5-low",
]) {
const body = { model, messages: [] };
const result = applyClaudeEffortVariant({
provider,
effectiveModel: model,
body,
sourceFormat,
});
assert.deepEqual(result, { effectiveModel: model, log: null });
assert.deepEqual(body, { model, messages: [] });
assert.deepEqual(
resolveRequestedModel(result.effectiveModel, { liveCatalogIds: new Set([model]) }),
{ modelId: model, parameters: [] }
);
}
});
}
}
test("Cursor preserves explicit client effort while its encoder owns non-catalog suffix fallback", () => {
const body = { model: "claude-opus-5-low", reasoning_effort: "none", messages: [] };
const result = applyClaudeEffortVariant({
provider: "cursor",
effectiveModel: body.model,
body,
sourceFormat: FORMATS.OPENAI,
});
assert.deepEqual(body, { model: "claude-opus-5-low", reasoning_effort: "none", messages: [] });
assert.deepEqual(resolveRequestedModel(result.effectiveModel, { liveCatalogIds: new Set() }), {
modelId: "claude-opus-5",
parameters: [{ id: "effort", value: "low" }],
});
});
test("claude provider + effort suffix → strips to base, mutates body model + reasoning_effort, returns log", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4-high", messages: [] };