Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs-site/src/content/docs/reference/configuration/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,21 @@ Cursor-specific model parameters:
Explicit variants send Cursor's `default` model with its `optimization` parameter, preserving the
selection on every request. They remain available when live discovery omits `default`.

### Vision

Native Cursor vision uses `SelectedImage` (JPEG soft-cap + `blobIdWithData`) for models that can see
images natively — Claude, Gemini, GPT, Kimi, and Grok among them. Auto, `composer-*`, and GLM
(`glm-5.2`) stay on the curated `noVisionModels` list and use the vision describe sidecar instead.
Trailing `<multi_agent_mode>` developer injections (Codex Desktop collab guidance after `view_image`)
are transparent for SelectedImage promotion so the continuation still carries the image and
promote nudge.

After pulling Cursor vision fixes, run `ocx ensure` so the proxy PID is the workspace `src/cli`
binary rather than a stale install. Stale `providers.cursor.noVisionModels` stamps that list every
Cursor model are healed back to the curated Auto/Composer/GLM set on OAuth reconcile. For
`cursor/grok-4.5`, Codex effort `none`/`minimal` maps to wire tier `medium` (some plans reject
Comment on lines +289 to +292

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the stale-configuration sentence.

Lines 290-291 are grammatically incomplete. The sentence does not clearly identify the stale configuration that OAuth reconciliation replaces.

Proposed fix
- Cursor model are healed back to the curated Auto/Composer/GLM set on OAuth reconcile. For
+ Cursor model are healed back to the curated Auto/Composer/GLM set on OAuth reconcile. For

Replace the full sentence with:

Stale `providers.cursor.noVisionModels` values that mark every Cursor model are healed back to the curated Auto/Composer/GLM set during OAuth reconciliation.

As per path instructions, user-facing documentation must stay synchronized with actual CLI/API behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs-site/src/content/docs/reference/configuration/providers.md` around lines
289 - 292, Replace the incomplete stale-configuration sentence in the
documentation with wording that clearly states stale
`providers.cursor.noVisionModels` values marking every Cursor model are healed
to the curated Auto/Composer/GLM set during OAuth reconciliation.

Source: Path instructions

`-low` with Connect `not_found`); explicit `low` still passes through when the account exposes it.

Cursor server-driven local tools are disabled by default. Codex continues using its own tools such as
`apply_patch` and `exec_command` with its own approval and sandbox policy:

Expand Down
5 changes: 4 additions & 1 deletion src/adapters/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isCursorBenignCancelError, isCursorInvalidArgumentError, safeCursorErro
import { isCursorExternalWireModel } from "./cursor/discovery";
import { createCursorKvStore, type CursorKvStore } from "./cursor/kv-store";
import { mapCursorServerMessage } from "./cursor/message-mapper";
import { cursorIsTrailingToolResultContinuation } from "./cursor/images";
import { createCursorRequest } from "./cursor/request-builder";
import {
createLiveCursorTransport,
Expand Down Expand Up @@ -111,7 +112,9 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
_parsed._cursorConversationId = request.conversationId;
let emittedOutput = false;
let replayUnsafe = false;
const lastRawIsToolResult = _parsed.context.messages.at(-1)?.role === "toolResult";
// Desktop multi_agent developer suffixes trail toolResult; treat those as continuations
// so invalid_argument does not force a fresh conversation mid tool resume.
const lastRawIsToolResult = cursorIsTrailingToolResultContinuation(_parsed.context.messages);

const runOnce = async (activeRequest: ReturnType<typeof createCursorRequest>) => {
await runCursorTurnWithRetry(
Expand Down
17 changes: 17 additions & 0 deletions src/adapters/cursor/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ export const CURSOR_ROUTER_MODEL_IDS = [
...CURSOR_ROUTING_LEVELS.map(level => `${CURSOR_AUTO_MODEL_ID}-${level}`),
] as const;

/**
* Cursor models that cannot see images natively. OpenCodex routes them through the vision
* sidecar (option B: catalog still advertises image so Codex can attach). Evidence:
* - Composer family (`composer-*`): Cursor staff — text-only; "Model does not support images"
* - Auto / router modes: Cursor docs omit Images for Auto Cost; staff — pick Claude/GPT for images
* - glm-5.2: Cursor docs omit Images; Z.ai GLM-5.2 is text-only (vision is GLM-5V)
*
* `composer-*` uses modelInList's trailing-`*` prefix match so a new Composer slug stays sidecar
* until proven multimodal. Everyone else in the static seed (Claude, Gemini, GPT, Kimi, Grok)
* takes SelectedImage. Other live-discovered ids stay unclassified (native path) until curated.
*/
export const CURSOR_NO_VISION_MODELS = [
...CURSOR_ROUTER_MODEL_IDS,
"composer-*",
"glm-5.2",
] as const;

/** Wire id Cursor Connect expects for the auto-router (GetUsableModels returns `default`, not `auto`). */
export const CURSOR_AUTO_WIRE_MODEL_ID = "default";

Expand Down
8 changes: 8 additions & 0 deletions src/adapters/cursor/effort-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,19 @@ function codexEffortRank(reasoning: string | undefined): "low" | "medium" | "hig
/**
* The Cursor effort suffix to use for `baseModelId` given a Codex reasoning effort, or `undefined` when
* the model takes no suffix (bare). Literal model tiers pass through; unknown efforts clamp by rank.
*
* Grok 4.5 special case: Codex `none`/`minimal` map to `medium`, not `low`. Cursor Start fixes Grok
* at medium and live Connect returns `not_found` for `grok-4.5-low` on some plans; explicit `low`
* still passes through for Pro accounts that expose it.
*/
export function cursorEffortSuffix(baseModelId: string, reasoning: string | undefined): string | undefined {
const tiers = CURSOR_MODEL_EFFORT_TIERS[baseModelId];
if (!tiers || tiers.length === 0) return undefined;
const requested = normalizeRequestedEffort(reasoning);
const isGrok45 = baseModelId === "grok-4.5" || baseModelId === "grok-4.5-fast";
if (isGrok45 && (requested === "none" || requested === "minimal") && tiers.includes("medium")) {
return "medium";
}
if (requested && tiers.includes(requested)) return requested;
switch (codexEffortRank(reasoning)) {
case "low":
Expand Down
Loading
Loading