From f10d1392b6367b0f6c999b2c9ae5327b45de47b5 Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:00:20 -0600 Subject: [PATCH 01/12] feat(cursor): wire SelectedImage vision through live transport Resolve active-turn images before prepare/encode, attach selectedContext on the active UserMessage, omit image placeholders from text, and remove Cursor from noVisionModels so native vision replaces the sidecar path. --- src/adapters/cursor/images.ts | 247 ++++++++++++++++++++++++ src/adapters/cursor/live-transport.ts | 5 +- src/adapters/cursor/protobuf-request.ts | 8 +- src/adapters/cursor/request-builder.ts | 2 +- src/adapters/cursor/types.ts | 6 + src/providers/registry.ts | 5 - tests/cursor-blob.test.ts | 32 +++ tests/cursor-request-builder.test.ts | 6 +- 8 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 src/adapters/cursor/images.ts diff --git a/src/adapters/cursor/images.ts b/src/adapters/cursor/images.ts new file mode 100644 index 000000000..757c0e4bd --- /dev/null +++ b/src/adapters/cursor/images.ts @@ -0,0 +1,247 @@ +import { randomUUID } from "node:crypto"; +import { create } from "@bufbuild/protobuf"; +import type { OcxContentPart, OcxImageContent, OcxMessage } from "../../types"; +import { assessUrlDestination, resolvePublicAddresses } from "../../lib/destination-policy"; +import { pinnedHttpsGet } from "../../images/artifacts"; +import type { PinnedAddress } from "../../lib/pinned-http"; +import { + SelectedContextSchema, + SelectedImageSchema, + type SelectedContext, + type SelectedImage, +} from "./gen/agent_pb"; + +/** Matches OmniRoute / composer-api per-image byte cap. */ +export const MAX_CURSOR_IMAGE_BYTES = 1024 * 1024; + +/** Upper bound on images attached to one Cursor turn. */ +export const MAX_CURSOR_IMAGES = 12; + +const IMAGE_FETCH_TIMEOUT_MS = (() => { + const parsed = Number.parseInt(process.env.CURSOR_IMAGE_FETCH_TIMEOUT_MS || "15000", 10); + return Number.isInteger(parsed) && parsed > 0 ? parsed : 15_000; +})(); + +export class CursorImageError extends Error { + readonly status: number; + + constructor(message: string, status = 400) { + super(message); + this.name = "CursorImageError"; + this.status = status; + } +} + +export interface ResolvedCursorImage { + data: Uint8Array; + mimeType: string; + uuid: string; +} + +function isImagePart(part: OcxContentPart): part is OcxImageContent { + return part.type === "image"; +} + +function estimatedBase64DecodedBytes(payload: string): number { + return Math.floor((payload.length * 3) / 4); +} + +function decodeDataUrl(url: string): { data: Uint8Array; mimeType: string } { + const comma = url.indexOf(","); + if (comma < 0) throw new CursorImageError("Image data URL is malformed."); + const header = url.slice(5, comma); + const payload = url.slice(comma + 1); + const isBase64 = /;base64/i.test(header); + const mimeType = (header.split(";")[0] || "").trim().toLowerCase() || "application/octet-stream"; + + if (!mimeType.startsWith("image/")) { + throw new CursorImageError("Image data URL must have an image/* media type."); + } + if (!isBase64) { + throw new CursorImageError("Image data URL must be base64-encoded."); + } + if (payload.length > MAX_CURSOR_IMAGE_BYTES * 2) { + throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + } + + const normalized = payload.replace(/\s/g, ""); + if (estimatedBase64DecodedBytes(normalized) > MAX_CURSOR_IMAGE_BYTES) { + throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + } + + let data: Uint8Array; + try { + data = Buffer.from(normalized, "base64"); + } catch { + throw new CursorImageError("Image data URL contains invalid base64 data."); + } + if (normalized.length > 0 && data.byteLength === 0) { + throw new CursorImageError("Image data URL contains invalid base64 data."); + } + return { data, mimeType }; +} + +function pickPinnedAddress(addresses: PinnedAddress[]): PinnedAddress { + return addresses.find(address => address.family === 4) ?? addresses[0]!; +} + +async function fetchHttpsImageBytes(url: string, signal?: AbortSignal): Promise<{ data: Uint8Array; mimeType: string }> { + let parsed: URL; + try { + parsed = new URL(url); + } catch { + throw new CursorImageError("Image URL is invalid."); + } + if (parsed.protocol !== "https:") { + throw new CursorImageError("Image URL must use HTTPS."); + } + + const assessment = assessUrlDestination(url); + if (assessment && assessment.kind !== "public" && assessment.kind !== "hostname") { + throw new CursorImageError("Image URL points to a blocked address."); + } + + let resolved: Awaited>; + try { + resolved = await resolvePublicAddresses(url, { context: "Cursor image" }); + } catch { + throw new CursorImageError("Image URL host could not be resolved."); + } + + const controller = new AbortController(); + const onAbort = () => controller.abort(); + if (signal) { + if (signal.aborted) controller.abort(); + else signal.addEventListener("abort", onAbort, { once: true }); + } + const timer = setTimeout(() => controller.abort(), IMAGE_FETCH_TIMEOUT_MS); + + try { + const response = await pinnedHttpsGet(url, pickPinnedAddress(resolved.addresses), controller.signal, { + maxBytes: MAX_CURSOR_IMAGE_BYTES, + }); + const contentType = (response.headers.get("content-type") || "").toLowerCase(); + const mimeType = contentType.split(";")[0]?.trim() || ""; + if (!mimeType.startsWith("image/")) { + throw new CursorImageError("Image URL did not return an image content type."); + } + if (!response.body) throw new CursorImageError("Image URL returned no body."); + + const reader = response.body.getReader(); + const chunks: Uint8Array[] = []; + let total = 0; + try { + for (;;) { + const { value, done } = await reader.read(); + if (done) break; + total += value.byteLength; + if (total > MAX_CURSOR_IMAGE_BYTES) { + throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + } + chunks.push(value); + } + } finally { + try { await reader.cancel(); } catch { /* ignore */ } + reader.releaseLock(); + } + + const data = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + data.set(chunk, offset); + offset += chunk.byteLength; + } + return { data, mimeType }; + } catch (error) { + if (error instanceof CursorImageError) throw error; + throw new CursorImageError("Could not fetch the image URL."); + } finally { + clearTimeout(timer); + signal?.removeEventListener("abort", onAbort); + } +} + +/** Collect image URLs from one message's content parts, preserving order. */ +export function extractCursorImageUrls(content: string | readonly OcxContentPart[]): string[] { + if (typeof content === "string" || !Array.isArray(content)) return []; + const urls: string[] = []; + for (const part of content) { + if (isImagePart(part) && typeof part.imageUrl === "string" && part.imageUrl.length > 0) { + urls.push(part.imageUrl); + } + } + return urls; +} + +/** + * Resolve OpenCodex image parts (data: or https:) into bytes for SelectedImage. + * Remote fetches reuse the repo SSRF destination policy + DNS-pinned HTTPS path. + */ +export async function resolveCursorImages( + imageUrls: readonly string[], + signal?: AbortSignal, +): Promise { + if (imageUrls.length > MAX_CURSOR_IMAGES) { + throw new CursorImageError(`Too many images in one request (max ${MAX_CURSOR_IMAGES}).`); + } + + const out: ResolvedCursorImage[] = []; + for (const url of imageUrls) { + if (typeof url !== "string" || url.length === 0) { + throw new CursorImageError("Image URL is missing."); + } + const resolved = url.toLowerCase().startsWith("data:") + ? decodeDataUrl(url) + : await fetchHttpsImageBytes(url, signal); + if (resolved.data.byteLength === 0) { + throw new CursorImageError("Image input is empty."); + } + if (resolved.data.byteLength > MAX_CURSOR_IMAGE_BYTES) { + throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + } + out.push({ + data: resolved.data, + mimeType: resolved.mimeType, + uuid: randomUUID(), + }); + } + return out; +} + +/** Build SelectedImage messages with inline `data` (field 8), matching OmniRoute. */ +export function buildSelectedImages(images: readonly ResolvedCursorImage[]): SelectedImage[] { + return images.map(image => create(SelectedImageSchema, { + uuid: image.uuid, + path: "", + mimeType: image.mimeType, + dataOrBlobId: { case: "data", value: image.data }, + })); +} + +export function buildSelectedContext(images: readonly ResolvedCursorImage[]): SelectedContext | undefined { + if (images.length === 0) return undefined; + return create(SelectedContextSchema, { + selectedImages: buildSelectedImages(images), + }); +} + +/** + * Resolve images for the active user/developer turn of a Cursor request. + * History stays text-only; only the current UserMessageAction carries SelectedImage. + */ +export async function resolveActiveCursorImages( + messages: readonly OcxMessage[] | undefined, + signal?: AbortSignal, +): Promise { + if (!messages?.length) return []; + if (messages.at(-1)?.role === "toolResult") return []; + + for (let i = messages.length - 1; i >= 0; i--) { + const message = messages[i]; + if (!message) continue; + if (message.role === "user" || message.role === "developer") { + return resolveCursorImages(extractCursorImageUrls(message.content), signal); + } + } + return []; +} diff --git a/src/adapters/cursor/live-transport.ts b/src/adapters/cursor/live-transport.ts index eafb2c9b4..86034a6bf 100644 --- a/src/adapters/cursor/live-transport.ts +++ b/src/adapters/cursor/live-transport.ts @@ -13,6 +13,7 @@ import { type TranslatorBudget, } from "../../lib/translator-budget"; import { activePromptText, prepareCursorRunRequest } from "./protobuf-request"; +import { resolveActiveCursorImages } from "./images"; import { createCursorContextUsageTracker, createCursorProtobufEventState, @@ -569,9 +570,11 @@ class LiveCursorTransport implements CursorTransport { clearPrior: request.contextUsageReset === true, storeCheckpoints: request.contextUsageStoreCheckpoints !== false, }); + const selectedImages = await resolveActiveCursorImages(request.rawMessages, signal); + const preparedRequest = { ...request, selectedImages }; // Build the payload once. The estimate is only worth deriving when there is no // carry-forward to fall back on — with a carry present it would never be used (#373). - const prepared = prepareCursorRunRequest(request, { + const prepared = prepareCursorRunRequest(preparedRequest, { estimateInputTokens: contextUsage.carryForwardTokens === undefined, }); this.blobRequestScope = prepared.blobRequestScope; diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts index 4ede0a482..c20108d65 100644 --- a/src/adapters/cursor/protobuf-request.ts +++ b/src/adapters/cursor/protobuf-request.ts @@ -13,6 +13,7 @@ import { storeCursorBlob, type CursorBlobRequestScopeToken, } from "./native-exec"; +import { buildSelectedContext } from "./images"; import { estimateTokens } from "../../lib/token-estimate"; import { AgentClientMessageSchema, @@ -318,7 +319,7 @@ function contentText(message: OcxMessage): string { .map(part => { if (part.type === "text") return part.text; if (part.type === "thinking") return part.thinking; - if (part.type === "image") return `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`; + if (part.type === "image") return undefined; return undefined; }) .filter((value): value is string => typeof value === "string" && value.length > 0) @@ -328,7 +329,8 @@ function contentText(message: OcxMessage): string { function contentToText(content: OcxToolResultMessage["content"]): string { if (typeof content === "string") return content; return content - .map(part => part.type === "text" ? part.text : `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`) + .map(part => part.type === "text" ? part.text : undefined) + .filter((value): value is string => typeof value === "string" && value.length > 0) .join("\n"); } @@ -582,6 +584,7 @@ function buildPreparedCursorRunRequest( const actionCase = !lastRawIsToolResult && text.trim().length > 0 ? "userMessageAction" : "resumeAction"; + const selectedContext = buildSelectedContext(request.selectedImages ?? []); const action = create(ConversationActionSchema, { action: actionCase === "userMessageAction" ? { @@ -590,6 +593,7 @@ function buildPreparedCursorRunRequest( userMessage: create(UserMessageSchema, { text, messageId: crypto.randomUUID(), + ...(selectedContext ? { selectedContext } : {}), }), requestContext: buildRequestContext(), }), diff --git a/src/adapters/cursor/request-builder.ts b/src/adapters/cursor/request-builder.ts index b60f49fff..6f4bf5335 100644 --- a/src/adapters/cursor/request-builder.ts +++ b/src/adapters/cursor/request-builder.ts @@ -161,7 +161,7 @@ function contentPartToText(part: OcxContentPart | OcxAssistantContentPart): stri case "thinking": return part.thinking; case "image": - return `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`; + return undefined; case "toolCall": // Cursor does not accept OpenAI Responses assistant tool-call parts as native history here. // Rendering them as visible "[tool_call]" text leaks synthetic protocol markers back into diff --git a/src/adapters/cursor/types.ts b/src/adapters/cursor/types.ts index b32026a07..a7dc69e1b 100644 --- a/src/adapters/cursor/types.ts +++ b/src/adapters/cursor/types.ts @@ -1,6 +1,7 @@ import type { OcxUsage } from "../../types"; import type { OcxMessage, OcxRequestOptions, OcxTool } from "../../types"; import type { CursorRoutingLevel } from "./discovery"; +import type { ResolvedCursorImage } from "./images"; export interface CursorRequestedModelParameter { id: string; @@ -17,6 +18,11 @@ export interface CursorRunRequest { system: string[]; messages: CursorRequestMessage[]; rawMessages?: OcxMessage[]; + /** + * Images for the active user turn, inlined as SelectedImage.data under + * UserMessage.selected_context. History stays text-only. + */ + selectedImages?: readonly ResolvedCursorImage[]; tools?: OcxTool[]; toolChoice?: OcxRequestOptions["toolChoice"]; parallelToolCalls?: boolean; diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 8da2f91f8..76f93773a 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -871,11 +871,6 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // no-effort fallback to `kimi-k3-max` would never be reached. Mirrors the other K3 // routes (kimi, kimi-code, opencode-go). modelDefaultReasoningEfforts: { "kimi-k3": "max" }, - // Cursor's wire protocol never forwards image parts (request-builder emits an unsupported- - // content marker), so the vision sidecar covers ALL cursor models regardless of what the - // upstream model could natively do. Live-discovered models outside the static list fall back - // to the same marker until they appear here. - noVisionModels: cursorModelIds(CURSOR_STATIC_MODELS), }, { id: "xai", diff --git a/tests/cursor-blob.test.ts b/tests/cursor-blob.test.ts index 8e0adaa28..65e917aac 100644 --- a/tests/cursor-blob.test.ts +++ b/tests/cursor-blob.test.ts @@ -114,6 +114,15 @@ function actionText(bytes: Uint8Array): string | undefined { return action?.case === "userMessageAction" ? action.value.userMessage?.text : undefined; } +function activeSelectedImages(bytes: Uint8Array) { + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + const action = run?.action?.action; + return action?.case === "userMessageAction" + ? action.value.userMessage?.selectedContext?.selectedImages + : undefined; +} + /** The `toolName`s advertised in the top-level AgentRunRequest.mcp_tools channel (undefined when unset). */ function mcpToolNames(bytes: Uint8Array): string[] | undefined { const msg = fromBinary(AgentClientMessageSchema, bytes); @@ -155,6 +164,29 @@ describe("Cursor blob handshake", () => { expect(run?.mcpTools?.mcpTools[0]?.toolName).toBe("mcp__fs__read_file"); }); + test("encodeCursorRunRequest attaches selectedContext with inline image data on the active user turn", () => { + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "claude-4.6-opus-high", + conversationId: "c1", + system: [], + messages: [{ role: "user", content: "see this" }], + selectedImages: [{ + data: imageBytes, + mimeType: "image/png", + uuid: "img-uuid-1", + }], + }); + + expect(actionText(bytes)).toBe("see this"); + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(1); + expect(images?.[0]?.uuid).toBe("img-uuid-1"); + expect(images?.[0]?.mimeType).toBe("image/png"); + expect(images?.[0]?.dataOrBlobId.case).toBe("data"); + expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + }); + test("caps external root replay while preserving system and newest history", () => { const rawMessages = Array.from({ length: 210 }, (_, index) => index % 2 === 0 diff --git a/tests/cursor-request-builder.test.ts b/tests/cursor-request-builder.test.ts index 6db299ce1..174823425 100644 --- a/tests/cursor-request-builder.test.ts +++ b/tests/cursor-request-builder.test.ts @@ -180,7 +180,7 @@ describe("Cursor request builder", () => { ]); }); - test("uses an explicit image placeholder for unsupported image parts", () => { + test("omits image parts from text while preserving other content", () => { const request = createCursorRequest({ ...base, context: { @@ -198,8 +198,8 @@ describe("Cursor request builder", () => { }); expect(request.messages[0]?.content).toContain("see"); - expect(request.messages[0]?.content).toContain("image input unsupported"); - expect(request.messages[0]?.content).toContain("high"); + expect(request.messages[0]?.content).not.toContain("image input unsupported"); + expect(request.messages[0]?.content).not.toContain("data:image/png"); }); test("preserves Responses tools and tool choice for Cursor request context", () => { From 748d1bd000b79fa271871effc3c1d80c96ca2e1b Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:04:55 -0600 Subject: [PATCH 02/12] test(cursor): cover image resolver and history selectedContext gaps Add focused unit tests for images.ts caps, data URL validation, HTTPS/SSRF rejection, and active-turn selection. Extend cursor-blob coverage to assert history/root UserMessages omit selectedContext while the active turn keeps inline SelectedImage data. --- tests/cursor-blob.test.ts | 56 +++++++++++ tests/cursor-images.test.ts | 190 ++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 tests/cursor-images.test.ts diff --git a/tests/cursor-blob.test.ts b/tests/cursor-blob.test.ts index 65e917aac..9e3ad8099 100644 --- a/tests/cursor-blob.test.ts +++ b/tests/cursor-blob.test.ts @@ -36,6 +36,7 @@ import { GetBlobArgsSchema, KvServerMessageSchema, SetBlobArgsSchema, + UserMessageSchema, } from "../src/adapters/cursor/gen/agent_pb"; beforeEach(() => { @@ -123,6 +124,16 @@ function activeSelectedImages(bytes: Uint8Array) { : undefined; } +function nativeTurnUserMessages(bytes: Uint8Array) { + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + return (run?.conversationState?.turns ?? []).map(turnId => { + const turn = fromBinary(ConversationTurnStructureSchema, blobData(turnId)); + if (turn.turn.case !== "agentConversationTurn") return undefined; + return fromBinary(UserMessageSchema, blobData(turn.turn.value.userMessage)); + }).filter((message): message is NonNullable => message !== undefined); +} + /** The `toolName`s advertised in the top-level AgentRunRequest.mcp_tools channel (undefined when unset). */ function mcpToolNames(bytes: Uint8Array): string[] | undefined { const msg = fromBinary(AgentClientMessageSchema, bytes); @@ -187,6 +198,51 @@ describe("Cursor blob handshake", () => { expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); }); + test("encodeCursorRunRequest keeps selectedContext only on the active user turn", () => { + const activeImageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "composer-2.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "user", content: "active turn" }], + rawMessages: [ + { + role: "user", + content: [ + { type: "text", text: "old turn" }, + { type: "image", imageUrl: "data:image/png;base64,old", detail: "auto" }, + ], + timestamp: 1, + }, + { + role: "assistant", + model: "cursor/composer-2.5", + content: [{ type: "text", text: "ack" }], + timestamp: 2, + }, + { role: "user", content: "active turn", timestamp: 3 }, + ], + selectedImages: [{ + data: activeImageBytes, + mimeType: "image/png", + uuid: "active-img", + }], + }); + + const roots = decodeRootMessages(bytes) as Array<{ role?: string; selectedContext?: unknown }>; + expect(roots.some(root => root.selectedContext !== undefined)).toBe(false); + + const historicalUser = nativeTurnUserMessages(bytes)[0]; + expect(historicalUser?.text).toBe("old turn"); + expect(historicalUser?.selectedContext).toBeUndefined(); + + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(1); + expect(images?.[0]?.uuid).toBe("active-img"); + expect(images?.[0]?.dataOrBlobId.case).toBe("data"); + expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(activeImageBytes)); + }); + test("caps external root replay while preserving system and newest history", () => { const rawMessages = Array.from({ length: 210 }, (_, index) => index % 2 === 0 diff --git a/tests/cursor-images.test.ts b/tests/cursor-images.test.ts new file mode 100644 index 000000000..4cee9d086 --- /dev/null +++ b/tests/cursor-images.test.ts @@ -0,0 +1,190 @@ +import { afterEach, describe, expect, spyOn, test } from "bun:test"; +import { + CursorImageError, + MAX_CURSOR_IMAGE_BYTES, + MAX_CURSOR_IMAGES, + resolveActiveCursorImages, + resolveCursorImages, +} from "../src/adapters/cursor/images"; +import * as destinationPolicy from "../src/lib/destination-policy"; +import * as imageArtifacts from "../src/images/artifacts"; + +const PNG_BYTES = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); +const PNG_DATA_URL = `data:image/png;base64,${Buffer.from(PNG_BYTES).toString("base64")}`; + +const spies: Array> = []; + +function mockSpy( + object: T, + method: K, + implementation: T[K], +): ReturnType { + const spy = spyOn(object, method).mockImplementation(implementation as never); + spies.push(spy); + return spy; +} + +afterEach(() => { + while (spies.length > 0) spies.pop()?.mockRestore(); +}); + +function mockPublicHttpsFetch(body = PNG_BYTES, mimeType = "image/png") { + mockSpy(destinationPolicy, "assessUrlDestination", () => ({ kind: "public" as const })); + mockSpy(destinationPolicy, "resolvePublicAddresses", async () => ({ + addresses: [{ address: "93.184.216.34", family: 4 }], + hostname: "example.com", + })); + mockSpy(imageArtifacts, "pinnedHttpsGet", async () => new Response(body, { + status: 200, + headers: { "content-type": mimeType }, + })); +} + +describe("Cursor image resolver", () => { + test("rejects more than MAX_CURSOR_IMAGES in one request", async () => { + const urls = Array.from({ length: MAX_CURSOR_IMAGES + 1 }, () => PNG_DATA_URL); + await expect(resolveCursorImages(urls)).rejects.toMatchObject({ + name: "CursorImageError", + message: `Too many images in one request (max ${MAX_CURSOR_IMAGES}).`, + }); + }); + + test("rejects data URLs above the 1 MiB cap", async () => { + const oversized = "A".repeat(Math.ceil((MAX_CURSOR_IMAGE_BYTES + 1) * 4 / 3)); + await expect(resolveCursorImages([`data:image/png;base64,${oversized}`])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image input is too large (max 1 MiB). Resize and retry.", + }); + }); + + test("decodes valid base64 data URLs", async () => { + const resolved = await resolveCursorImages([PNG_DATA_URL]); + expect(resolved).toHaveLength(1); + expect(resolved[0]?.mimeType).toBe("image/png"); + expect(Array.from(resolved[0]?.data ?? [])).toEqual(Array.from(PNG_BYTES)); + expect(resolved[0]?.uuid.length).toBeGreaterThan(0); + }); + + test("rejects malformed and non-image data URLs", async () => { + await expect(resolveCursorImages(["data:image/png,not-base64"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image data URL must be base64-encoded.", + }); + await expect(resolveCursorImages(["data:text/plain;base64,YQ=="])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image data URL must have an image/* media type.", + }); + await expect(resolveCursorImages(["data:image/png;base64"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image data URL is malformed.", + }); + await expect(resolveCursorImages(["data:image/png;base64,"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image input is empty.", + }); + }); + + test("rejects non-HTTPS remote URLs", async () => { + await expect(resolveCursorImages(["http://example.com/image.png"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image URL must use HTTPS.", + }); + }); + + test("rejects blocked destinations before DNS resolution", async () => { + mockSpy(destinationPolicy, "assessUrlDestination", () => ({ kind: "loopback" as const })); + await expect(resolveCursorImages(["https://127.0.0.1/image.png"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image URL points to a blocked address.", + }); + }); + + test("rejects remote URLs when public DNS resolution fails", async () => { + mockSpy(destinationPolicy, "assessUrlDestination", () => ({ kind: "public" as const })); + mockSpy(destinationPolicy, "resolvePublicAddresses", async () => { + throw new Error("blocked"); + }); + await expect(resolveCursorImages(["https://example.com/image.png"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image URL host could not be resolved.", + }); + }); + + test("fetches HTTPS images through pinned HTTPS with image content-type", async () => { + mockPublicHttpsFetch(); + const resolved = await resolveCursorImages(["https://example.com/image.png"]); + expect(resolved).toHaveLength(1); + expect(resolved[0]?.mimeType).toBe("image/png"); + expect(Array.from(resolved[0]?.data ?? [])).toEqual(Array.from(PNG_BYTES)); + }); + + test("rejects HTTPS responses without an image content-type", async () => { + mockPublicHttpsFetch(PNG_BYTES, "text/plain"); + await expect(resolveCursorImages(["https://example.com/not-image"])).rejects.toMatchObject({ + name: "CursorImageError", + message: "Image URL did not return an image content type.", + }); + }); + + test("resolveActiveCursorImages selects the last user turn and ignores earlier images", async () => { + const resolved = await resolveActiveCursorImages([ + { + role: "user", + content: [{ type: "image", imageUrl: PNG_DATA_URL }], + timestamp: 1, + }, + { + role: "assistant", + model: "cursor/auto", + content: [{ type: "text", text: "seen" }], + timestamp: 2, + }, + { + role: "user", + content: [ + { type: "text", text: "active" }, + { type: "image", imageUrl: PNG_DATA_URL }, + ], + timestamp: 3, + }, + ]); + expect(resolved).toHaveLength(1); + expect(resolved[0]?.mimeType).toBe("image/png"); + }); + + test("resolveActiveCursorImages supports developer turns", async () => { + const resolved = await resolveActiveCursorImages([ + { + role: "developer", + content: [{ type: "image", imageUrl: PNG_DATA_URL }], + timestamp: 1, + }, + ]); + expect(resolved).toHaveLength(1); + }); + + test("resolveActiveCursorImages returns empty for trailing toolResult continuations", async () => { + const resolved = await resolveActiveCursorImages([ + { + role: "user", + content: [{ type: "image", imageUrl: PNG_DATA_URL }], + timestamp: 1, + }, + { + role: "toolResult", + toolCallId: "call-1", + toolName: "read_file", + content: "done", + isError: false, + timestamp: 2, + }, + ]); + expect(resolved).toEqual([]); + }); + + test("CursorImageError carries HTTP status for callers", () => { + const error = new CursorImageError("blocked", 403); + expect(error.status).toBe(403); + expect(error.name).toBe("CursorImageError"); + }); +}); From badede63f9a25f58526282edd5e99c3e238cd9da Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:10:05 -0600 Subject: [PATCH 03/12] test(cursor): verify native vision registry and catalog modalities Assert Cursor is absent from noVisionModels, advertises image through modelInputModalities, keeps catalog hints sidecar-free, and skips the vision sidecar when requests carry images. --- .../catalog-vision-sidecar-modalities.test.ts | 60 ++++++++++++++++++- tests/cursor-static-catalog.test.ts | 7 +++ tests/provider-registry-parity.test.ts | 13 ++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/tests/catalog-vision-sidecar-modalities.test.ts b/tests/catalog-vision-sidecar-modalities.test.ts index 27de9d504..d3d79af63 100644 --- a/tests/catalog-vision-sidecar-modalities.test.ts +++ b/tests/catalog-vision-sidecar-modalities.test.ts @@ -3,9 +3,13 @@ import { applyProviderConfigHints, gatherRoutedModels } from "../src/codex/catal import { clearModelCache } from "../src/codex/model-cache"; import type { OcxProviderConfig } from "../src/types"; import { deriveComboCatalogModel } from "../src/codex/catalog"; +import { CURSOR_STATIC_MODELS } from "../src/adapters/cursor/discovery"; import { PROVIDER_REGISTRY } from "../src/providers/registry"; -import { enrichProviderFromRegistry } from "../src/providers/derive"; +import { enrichProviderFromRegistry, providerConfigSeed } from "../src/providers/derive"; import type { CatalogModel } from "../src/types"; +import { parseRequest } from "../src/responses/parser"; +import { routeModel } from "../src/router"; +import { planVisionSidecar } from "../src/vision"; const base: OcxProviderConfig = { adapter: "openai-chat", @@ -230,3 +234,57 @@ describe("vision-capable provider models feed combo modalities", () => { expect(hinted.inputModalities).toEqual(["text", "image"]); }); }); + +describe("Cursor native vision registry", () => { + test("Cursor models advertise image through modelInputModalities, not noVisionModels", () => { + const cursor = PROVIDER_REGISTRY.find(entry => entry.id === "cursor"); + expect(cursor?.noVisionModels).toBeUndefined(); + for (const model of ["auto", "composer-2.5", "gpt-5.5", "gemini-3-pro"]) { + expect(cursor?.modelInputModalities?.[model]).toEqual(["text", "image"]); + expect(cursor?.noVisionModels ?? []).not.toContain(model); + } + for (const model of CURSOR_STATIC_MODELS) { + expect(cursor?.modelInputModalities?.[model.id]).toEqual(["text", "image"]); + } + }); + + test("Cursor catalog hints do not force sidecar image augmentation", () => { + const cursor = providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!); + const hinted = applyProviderConfigHints("cursor", cursor, { + id: "gpt-5.5", + provider: "cursor", + contextWindow: 272_000, + inputModalities: cursor.modelInputModalities?.["gpt-5.5"], + }); + expect(hinted.inputModalities).toEqual(["text", "image"]); + }); + + test("Cursor image requests skip the vision sidecar", () => { + const config = { + port: 10100, + defaultProvider: "cursor", + providers: { + cursor: providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!), + }, + }; + const route = routeModel(config, "cursor/auto"); + const parsed = parseRequest({ + model: "cursor/auto", + input: [{ + type: "message", + role: "user", + content: [ + { type: "input_text", text: "What is in this screenshot?" }, + { type: "input_image", image_url: "data:image/png;base64,aGVsbG8=" }, + ], + }], + }); + expect(planVisionSidecar(config, route.provider, route.modelId, parsed, { + providerName: "openai", + provider: { adapter: "openai-responses", baseUrl: "https://chatgpt.test/v1", authMode: "forward" }, + accountMode: "direct", + authContext: { kind: "main", accountId: null }, + headers: new Headers({ authorization: "Bearer chatgpt" }), + })).toBeUndefined(); + }); +}); diff --git a/tests/cursor-static-catalog.test.ts b/tests/cursor-static-catalog.test.ts index 4dbd772e8..d085f13aa 100644 --- a/tests/cursor-static-catalog.test.ts +++ b/tests/cursor-static-catalog.test.ts @@ -111,5 +111,12 @@ describe("Cursor static Codex catalog", () => { ]); expect(entries.find(item => item.slug === "cursor/glm-5.2")?.supported_reasoning_levels) .toMatchObject([{ effort: "high" }, { effort: "max" }, { effort: "ultra" }]); + + for (const modelId of ["auto", "composer-2.5", "gpt-5.5", "gemini-3-pro"]) { + expect( + entries.find(item => item.slug === `cursor/${modelId}`)?.input_modalities, + `cursor/${modelId} should advertise native image input`, + ).toEqual(["text", "image"]); + } }); }); diff --git a/tests/provider-registry-parity.test.ts b/tests/provider-registry-parity.test.ts index 5f205e8a3..8e40b01b8 100644 --- a/tests/provider-registry-parity.test.ts +++ b/tests/provider-registry-parity.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; import { buildCatalogEntries } from "../src/codex/catalog"; +import { CURSOR_STATIC_MODELS, cursorModelInputModalities } from "../src/adapters/cursor/discovery"; import { getModelMetadata, resolveMetadataProvider } from "../src/generated/model-metadata"; import { buildInitProviders } from "../src/cli/init"; import { OAUTH_PROVIDERS } from "../src/oauth"; @@ -596,6 +597,18 @@ describe("provider registry parity", () => { expect(seed.modelContextWindows?.["gpt-5.6-luna"]).toBe(1_000_000); expect(seed.modelReasoningEfforts?.["gpt-5.5"]).toEqual(["low", "medium", "high"]); expect(seed.modelReasoningEfforts?.["gpt-5.6-sol"]).toEqual(["low", "medium", "high", "xhigh", "max"]); + expect(cursor?.noVisionModels).toBeUndefined(); + expect(seed.noVisionModels).toBeUndefined(); + expect(seed.modelInputModalities?.auto).toEqual(["text", "image"]); + expect(seed.modelInputModalities?.["composer-2.5"]).toEqual(["text", "image"]); + expect(seed.modelInputModalities?.["gpt-5.5"]).toEqual(["text", "image"]); + expect(seed.modelInputModalities?.["gemini-3-pro"]).toEqual(["text", "image"]); + for (const model of CURSOR_STATIC_MODELS) { + expect( + seed.modelInputModalities?.[model.id], + `Cursor registry seed must advertise native image input for ${model.id}`, + ).toEqual(cursorModelInputModalities(CURSOR_STATIC_MODELS)[model.id]); + } const savedCursor: OcxProviderConfig = { adapter: "cursor", baseUrl: "https://api2.cursor.sh" }; enrichProviderFromCatalog("cursor", savedCursor); From ea53ef65dbd7d330270c4b9048ffbc81322b6649 Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:21:35 -0600 Subject: [PATCH 04/12] fix(cursor): preserve image-only turns for SelectedImage vision Keep image-only user/developer turns in request-builder as empty-string active messages, and choose userMessageAction when selectedImages are present even if prompt text is empty. Add regression tests for first-turn and follow-up image-only encodes. --- .../final-fix-report.md | 42 +++++++++++ src/adapters/cursor/protobuf-request.ts | 5 +- src/adapters/cursor/request-builder.ts | 16 +++- tests/cursor-blob.test.ts | 73 +++++++++++++++++++ tests/cursor-request-builder.test.ts | 51 +++++++++++++ 5 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 .superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md diff --git a/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md b/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md new file mode 100644 index 000000000..6da1b466a --- /dev/null +++ b/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md @@ -0,0 +1,42 @@ +# Final Fix Report: image-only SelectedImage turns + +## Status +DONE + +## Summary +Fixed image-only Cursor turns so resolved `selectedImages` attach on the active `userMessageAction` instead of falling through to `resumeAction`. + +## Root cause +1. `createCursorRequest` dropped active user/developer turns after image parts were omitted from text, so image-only turns never survived into `request.messages`. +2. `buildPreparedCursorRunRequest` chose `resumeAction` whenever `text.trim()` was empty, even when live transport had already resolved `selectedImages`. + +## Fix +- `request-builder.ts` + - Preserve image-only user/developer turns as empty-string messages when raw content contains Cursor image URLs. + - Keep dropping empty assistant tool-call-only messages. + - Stop filtering out empty-string active messages in `createCursorRequest`. +- `protobuf-request.ts` + - Prefer `userMessageAction` when `selectedImages.length > 0`, even if prompt text is empty. + - Reuse the resolved `selectedImages` array for both action selection and `selectedContext` attachment. + +## Tests added +- `tests/cursor-request-builder.test.ts` + - image-only first user turn preserved as `{ role: "user", content: "" }` + - image-only active user turn after assistant reply preserved +- `tests/cursor-blob.test.ts` + - image-only first turn with `selectedImages` encodes `userMessageAction` + inline `selectedContext` + - image-only turn after assistant reply encodes `userMessageAction` + inline `selectedContext` + +## Verification +```bash +bun test tests/cursor-request-builder.test.ts tests/cursor-blob.test.ts +``` +Result: 92 pass, 0 fail. + +## Commit +Pending commit in this wave. + +## Notes +- `prepareCursorRunRequest` / `encodeCursorRunRequest` remain synchronous. +- No changes to `noVisionModels`. +- History remains text-only; `selectedContext` stays on the active turn only. diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts index c20108d65..e107b18ce 100644 --- a/src/adapters/cursor/protobuf-request.ts +++ b/src/adapters/cursor/protobuf-request.ts @@ -581,10 +581,11 @@ function buildPreparedCursorRunRequest( : rawText; // Tool-result-only turns resume the remembered Cursor conversation with results in history. const lastRawIsToolResult = request.rawMessages?.at(-1)?.role === "toolResult"; - const actionCase = !lastRawIsToolResult && text.trim().length > 0 + const selectedImages = request.selectedImages ?? []; + const actionCase = !lastRawIsToolResult && (selectedImages.length > 0 || text.trim().length > 0) ? "userMessageAction" : "resumeAction"; - const selectedContext = buildSelectedContext(request.selectedImages ?? []); + const selectedContext = buildSelectedContext(selectedImages); const action = create(ConversationActionSchema, { action: actionCase === "userMessageAction" ? { diff --git a/src/adapters/cursor/request-builder.ts b/src/adapters/cursor/request-builder.ts index 6f4bf5335..eba5e97cc 100644 --- a/src/adapters/cursor/request-builder.ts +++ b/src/adapters/cursor/request-builder.ts @@ -23,6 +23,7 @@ import { isBareCodexShellBridgeTool, } from "./tool-definitions"; import { lookupCursorThreadConversation } from "./thread-continuity"; +import { extractCursorImageUrls } from "./images"; /** Probe-verified Cursor Connect boundaries, with byte headroom for the enclosing field. */ export const CURSOR_TOOL_COUNT_LIMIT = 330; @@ -194,9 +195,18 @@ function requestMessage(message: OcxMessage): CursorRequestMessage | undefined { switch (message.role) { case "user": case "developer": - return { role: message.role, content: contentToText(message.content) }; + { + const content = contentToText(message.content); + if (content.length === 0 && extractCursorImageUrls(message.content).length === 0) { + return undefined; + } + return { role: message.role, content }; + } case "assistant": - return { role: "assistant", content: contentToText(message.content) }; + { + const content = contentToText(message.content); + return content.length > 0 ? { role: "assistant", content } : undefined; + } case "toolResult": return { role: "tool", @@ -257,7 +267,7 @@ export function createCursorRequest( ): CursorRunRequest { const messages = parsed.context.messages .map(requestMessage) - .filter((message): message is CursorRequestMessage => !!message && message.content.length > 0); + .filter((message): message is CursorRequestMessage => !!message); const activeText = [...messages].reverse().find(message => message.role === "user" || message.role === "developer")?.content ?? ""; const visibleTools = cursorToolsForActivePrompt(parsed.context.tools, activeText, parsed.options.toolChoice); const budget = applyCursorToolBudget(visibleTools, parsed.options.toolChoice); diff --git a/tests/cursor-blob.test.ts b/tests/cursor-blob.test.ts index 9e3ad8099..4cd32da08 100644 --- a/tests/cursor-blob.test.ts +++ b/tests/cursor-blob.test.ts @@ -243,6 +243,79 @@ describe("Cursor blob handshake", () => { expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(activeImageBytes)); }); + test("encodeCursorRunRequest uses userMessageAction for image-only turns with selectedImages", () => { + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "claude-4.6-opus-high", + conversationId: "c1", + system: [], + messages: [{ role: "user", content: "" }], + rawMessages: [{ + role: "user", + content: [{ type: "image", imageUrl: "data:image/png;base64,abc", detail: "auto" }], + timestamp: 1, + }], + selectedImages: [{ + data: imageBytes, + mimeType: "image/png", + uuid: "image-only", + }], + }); + + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + expect(run?.action?.action.case).toBe("userMessageAction"); + expect(actionText(bytes)).toBe(""); + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(1); + expect(images?.[0]?.uuid).toBe("image-only"); + expect(images?.[0]?.dataOrBlobId.case).toBe("data"); + expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + }); + + test("encodeCursorRunRequest uses userMessageAction for image-only turns after assistant reply", () => { + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "composer-2.5", + conversationId: "c1", + system: [], + messages: [ + { role: "user", content: "first" }, + { role: "assistant", content: "ack" }, + { role: "user", content: "" }, + ], + rawMessages: [ + { role: "user", content: "first", timestamp: 1 }, + { + role: "assistant", + model: "cursor/composer-2.5", + content: [{ type: "text", text: "ack" }], + timestamp: 2, + }, + { + role: "user", + content: [{ type: "image", imageUrl: "data:image/png;base64,abc", detail: "auto" }], + timestamp: 3, + }, + ], + selectedImages: [{ + data: imageBytes, + mimeType: "image/png", + uuid: "follow-up-image", + }], + }); + + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + expect(run?.action?.action.case).toBe("userMessageAction"); + expect(actionText(bytes)).toBe(""); + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(1); + expect(images?.[0]?.uuid).toBe("follow-up-image"); + expect(images?.[0]?.dataOrBlobId.case).toBe("data"); + expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + }); + test("caps external root replay while preserving system and newest history", () => { const rawMessages = Array.from({ length: 210 }, (_, index) => index % 2 === 0 diff --git a/tests/cursor-request-builder.test.ts b/tests/cursor-request-builder.test.ts index 174823425..aa85c7614 100644 --- a/tests/cursor-request-builder.test.ts +++ b/tests/cursor-request-builder.test.ts @@ -202,6 +202,57 @@ describe("Cursor request builder", () => { expect(request.messages[0]?.content).not.toContain("data:image/png"); }); + test("preserves image-only user turns as empty-string active messages", () => { + const request = createCursorRequest({ + ...base, + context: { + messages: [ + { + role: "user", + content: [{ type: "image", imageUrl: "data:image/png;base64,abc", detail: "high" }], + timestamp: 1, + }, + ], + }, + }); + + expect(request.messages).toEqual([{ role: "user", content: "" }]); + expect(request.rawMessages?.length).toBe(1); + }); + + test("preserves image-only active user turn after assistant reply", () => { + const request = createCursorRequest({ + ...base, + context: { + messages: [ + { + role: "user", + content: [{ type: "image", imageUrl: "data:image/png;base64,abc", detail: "high" }], + timestamp: 1, + }, + { + role: "assistant", + model: "cursor/composer-2.5", + content: [{ type: "text", text: "ack" }], + timestamp: 2, + }, + { + role: "user", + content: [{ type: "image", imageUrl: "data:image/png;base64,def", detail: "high" }], + timestamp: 3, + }, + ], + }, + }); + + expect(request.messages).toEqual([ + { role: "user", content: "" }, + { role: "assistant", content: "ack" }, + { role: "user", content: "" }, + ]); + expect(request.messages.at(-1)?.role).toBe("user"); + }); + test("preserves Responses tools and tool choice for Cursor request context", () => { const tool = { name: "read_file", From b3a63e15a895f88caf3a2d082d7e8d18f8b9271e Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:26:25 -0600 Subject: [PATCH 05/12] chore(cursor): drop accidental SDD report from vision fix commit --- .../final-fix-report.md | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 .superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md diff --git a/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md b/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md deleted file mode 100644 index 6da1b466a..000000000 --- a/.superpowers/sdd/2026-08-06-cursor-selected-image-vision/final-fix-report.md +++ /dev/null @@ -1,42 +0,0 @@ -# Final Fix Report: image-only SelectedImage turns - -## Status -DONE - -## Summary -Fixed image-only Cursor turns so resolved `selectedImages` attach on the active `userMessageAction` instead of falling through to `resumeAction`. - -## Root cause -1. `createCursorRequest` dropped active user/developer turns after image parts were omitted from text, so image-only turns never survived into `request.messages`. -2. `buildPreparedCursorRunRequest` chose `resumeAction` whenever `text.trim()` was empty, even when live transport had already resolved `selectedImages`. - -## Fix -- `request-builder.ts` - - Preserve image-only user/developer turns as empty-string messages when raw content contains Cursor image URLs. - - Keep dropping empty assistant tool-call-only messages. - - Stop filtering out empty-string active messages in `createCursorRequest`. -- `protobuf-request.ts` - - Prefer `userMessageAction` when `selectedImages.length > 0`, even if prompt text is empty. - - Reuse the resolved `selectedImages` array for both action selection and `selectedContext` attachment. - -## Tests added -- `tests/cursor-request-builder.test.ts` - - image-only first user turn preserved as `{ role: "user", content: "" }` - - image-only active user turn after assistant reply preserved -- `tests/cursor-blob.test.ts` - - image-only first turn with `selectedImages` encodes `userMessageAction` + inline `selectedContext` - - image-only turn after assistant reply encodes `userMessageAction` + inline `selectedContext` - -## Verification -```bash -bun test tests/cursor-request-builder.test.ts tests/cursor-blob.test.ts -``` -Result: 92 pass, 0 fail. - -## Commit -Pending commit in this wave. - -## Notes -- `prepareCursorRunRequest` / `encodeCursorRunRequest` remain synchronous. -- No changes to `noVisionModels`. -- History remains text-only; `selectedContext` stays on the active turn only. From e48613ed41b73e495adf5765e9c2ab8663a1c540 Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Thu, 6 Aug 2026 21:39:28 -0600 Subject: [PATCH 06/12] fix(cursor): keep view_image bytes for external models like grok External Cursor models flattened tool results to text via contentToText(), which dropped image parts from view_image continuations. Route image-bearing tool results through the native mcpToolCall/McpImageContent path while keeping text-only external replay flattened. Adds grok-4.5 wire regression coverage and a reusable vision harness test. --- .../2026-08-06-cursor-grok-view-image-fix.md | 39 ++++ src/adapters/cursor/images.ts | 9 +- src/adapters/cursor/protobuf-request.ts | 91 +++++++++- tests/cursor-blob.test.ts | 170 +++++++++++++++++- tests/cursor-vision-wire-harness.test.ts | 102 +++++++++++ 5 files changed, 394 insertions(+), 17 deletions(-) create mode 100644 docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md create mode 100644 tests/cursor-vision-wire-harness.test.ts diff --git a/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md b/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md new file mode 100644 index 000000000..ab8584b7e --- /dev/null +++ b/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md @@ -0,0 +1,39 @@ +# Cursor Grok view_image External-Model Fix + +## Goal +Stop OpenCodex from discarding `view_image` image bytes for external Cursor models (`grok-4.5`, GPT/Claude/Gemini families) so Cursor actually receives the tool-result image. Add fully automated wire-level verification so we do not need Codex Desktop reinstall/restart/manual testing. + +## Root Cause +`isCursorExternalWireModel("grok-4.5") === true`. In `conversationTurns()`, the external branch flattens tool results to text via `contentToText()`, which drops image parts. Image-only `view_image` therefore becomes an empty `[Tool Result]\n` for Cursor. The existing `McpImageContent` forwarding path only runs on the native (`composer-*`) branch and is unit-tested only with `composer-2.5`. + +## Global Constraints +- Keep prepare/encode synchronous. +- Preserve `resumeAction` for trailing tool-result continuations (do not force `userMessageAction` there). +- Keep history/root blobs text-only for user-attached images; only active SelectedImage / tool-result image content carries bytes. +- Do not put Cursor back into `noVisionModels`. +- Prefer forwarding real image bytes over inventing placeholders. +- Automated verification must prove non-empty image bytes on the Cursor wire for `grok-4.5` without requiring Desktop UI clicks. +- Work only under `/Users/user/Projects/opencodex`. +- Commit on `audit/cursor-dev`. + +## Tasks + +### Task 1: Failing Grok view_image wire test + fix external flatten path +1. TDD: add a failing test in `tests/cursor-blob.test.ts` that encodes a `view_image` tool-result continuation with `modelId: "grok-4.5"` and asserts the conversation turn carries `McpImageContent` (or equivalent non-empty image bytes) for the tool result — not empty text-only `[Tool Result]`. +2. Optionally add a tiny pure helper/script test that builds the same request from a fixture PNG path and asserts byte length > 0 / PNG magic, so agents can re-run without Desktop. +3. Implement the minimal fix in `src/adapters/cursor/protobuf-request.ts`: for external models, when a toolResult has image parts (or when a matched priorCall exists with image content), use the native `toolCallStep(..., message)` / `McpImageContent` path instead of text-flattening away images. Keep text-flatten only for non-image tool results if that remains needed for external replay compatibility. +4. Keep existing composer `view_image` McpImageContent test green. +5. Run focused tests, commit, report. + +### Task 2: Automated vision harness (no Desktop) +1. Add `tool/cursor_vision_wire_check.ts` (or `tests/cursor-vision-wire-harness.test.ts`) that: + - builds a synthetic 8x8 solid-color PNG (or uses a fixture) + - encodes both attach-turn SelectedImage and grok view_image tool-result paths + - prints/asserts pass/fail with concrete byte counts +2. Document one command: `bun test tests/cursor-blob.test.ts` and `bun test tests/cursor-vision-wire-harness.test.ts` (or `bun run tool/cursor_vision_wire_check.ts`). +3. Commit if separate from Task 1; otherwise fold into Task 1 if tiny. + +## Success Criteria +- `bun test tests/cursor-blob.test.ts` proves grok-4.5 view_image tool results carry non-empty image bytes. +- Automated harness/test can be re-run by the agent without restarting Codex Desktop. +- No manual screenshot testing required to claim the wire fix. diff --git a/src/adapters/cursor/images.ts b/src/adapters/cursor/images.ts index 757c0e4bd..f4f08f7ef 100644 --- a/src/adapters/cursor/images.ts +++ b/src/adapters/cursor/images.ts @@ -212,14 +212,17 @@ export async function resolveCursorImages( export function buildSelectedImages(images: readonly ResolvedCursorImage[]): SelectedImage[] { return images.map(image => create(SelectedImageSchema, { uuid: image.uuid, - path: "", mimeType: image.mimeType, dataOrBlobId: { case: "data", value: image.data }, })); } -export function buildSelectedContext(images: readonly ResolvedCursorImage[]): SelectedContext | undefined { - if (images.length === 0) return undefined; +/** + * OmniRoute always sends `UserMessage.selected_context`, even when empty. + * Cursor accepts the request without it, but vision turns are more reliable + * when the placeholder matches cursor-agent's wire format. + */ +export function buildSelectedContext(images: readonly ResolvedCursorImage[] = []): SelectedContext { return create(SelectedContextSchema, { selectedImages: buildSelectedImages(images), }); diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts index e107b18ce..c1f0d5e17 100644 --- a/src/adapters/cursor/protobuf-request.ts +++ b/src/adapters/cursor/protobuf-request.ts @@ -27,6 +27,7 @@ import { McpArgsSchema, McpSuccessSchema, McpTextContentSchema, + McpImageContentSchema, McpToolCallSchema, McpToolResultContentItemSchema, McpToolResultSchema, @@ -334,6 +335,73 @@ function contentToText(content: OcxToolResultMessage["content"]): string { .join("\n"); } +function toolResultHasImage(content: OcxToolResultMessage["content"]): boolean { + if (typeof content === "string") return false; + return content.some(part => part.type === "image" + && typeof part.imageUrl === "string" + && part.imageUrl.length > 0); +} + +function syntheticToolCallFromResult( + message: OcxToolResultMessage, +): Extract { + return { + type: "toolCall", + id: message.toolCallId, + name: message.toolName, + ...(message.toolNamespace ? { namespace: message.toolNamespace } : {}), + arguments: {}, + }; +} + +function toolResultContentItems(content: OcxToolResultMessage["content"]) { + if (typeof content === "string") { + return [create(McpToolResultContentItemSchema, { + content: { case: "text", value: create(McpTextContentSchema, { text: content }) }, + })]; + } + + const items = content.flatMap(part => { + if (part.type === "text" && part.text.length > 0) { + return [create(McpToolResultContentItemSchema, { + content: { case: "text", value: create(McpTextContentSchema, { text: part.text }) }, + })]; + } + if (part.type === "image" && typeof part.imageUrl === "string" && part.imageUrl.length > 0) { + try { + if (!part.imageUrl.toLowerCase().startsWith("data:")) return []; + const comma = part.imageUrl.indexOf(","); + if (comma < 0) return []; + const header = part.imageUrl.slice(5, comma); + const payload = part.imageUrl.slice(comma + 1).replace(/\s/g, ""); + if (!/;base64/i.test(header) || payload.length === 0) return []; + const mimeType = (header.split(";")[0] || "").trim().toLowerCase() || "image/png"; + if (!mimeType.startsWith("image/")) return []; + const data = Buffer.from(payload, "base64"); + if (data.byteLength === 0) return []; + return [create(McpToolResultContentItemSchema, { + content: { + case: "image", + value: create(McpImageContentSchema, { + data, + mimeType, + }), + }, + })]; + } catch { + return []; + } + } + return []; + }); + + return items.length > 0 + ? items + : [create(McpToolResultContentItemSchema, { + content: { case: "text", value: create(McpTextContentSchema, { text: "" }) }, + })]; +} + function toolResultToText(message: OcxToolResultMessage): string { return [ "[tool_result]", @@ -389,9 +457,7 @@ function toolResultPart(message: OcxToolResultMessage) { case: "success", value: create(McpSuccessSchema, { isError: message.isError, - content: [create(McpToolResultContentItemSchema, { - content: { case: "text", value: create(McpTextContentSchema, { text: contentToText(message.content) }) }, - })], + content: toolResultContentItems(message.content), }), }, }); @@ -461,6 +527,10 @@ function conversationTurns( // Working external-model clients replay only assistant text. Native mcpToolCall and // ThinkingMessage structures are Composer state and cause external workers to hydrate // the blobs, reach stepCompleted, then reject the turn with invalid_argument. + if (part.type === "toolCall") { + pendingToolCalls.set(part.id, part); + continue; + } if (part.type === "text" && part.text.length > 0) { current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, { message: { @@ -483,6 +553,12 @@ function conversationTurns( if (message.role === "toolResult") { if (!current) continue; if (externalModel) { + if (toolResultHasImage(message.content)) { + const priorCall = pendingToolCalls.get(message.toolCallId) ?? syntheticToolCallFromResult(message); + current.steps.push(toolCallStep(priorCall, requestScope, message)); + pendingToolCalls.delete(message.toolCallId); + continue; + } const prefix = message.isError ? "[Tool Error]" : "[Tool Result]"; current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, { message: { @@ -490,6 +566,7 @@ function conversationTurns( value: create(AssistantMessageSchema, { text: `${prefix}\n${contentToText(message.content)}` }), }, })), requestScope)); + pendingToolCalls.delete(message.toolCallId); continue; } const priorCall = pendingToolCalls.get(message.toolCallId); @@ -511,6 +588,8 @@ function conversationTurns( userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, { text: contentText(message), messageId: crypto.randomUUID(), + selectedContext: buildSelectedContext(), + mode: 1, })), requestScope), steps: [], }; @@ -594,7 +673,9 @@ function buildPreparedCursorRunRequest( userMessage: create(UserMessageSchema, { text, messageId: crypto.randomUUID(), - ...(selectedContext ? { selectedContext } : {}), + selectedContext, + // OmniRoute / cursor-agent always send mode=1 on UserMessage. + mode: 1, }), requestContext: buildRequestContext(), }), @@ -680,7 +761,7 @@ function buildPreparedCursorRunRequest( // the event-state `clientToolNames` use (live-transport.ts). Advertising the raw `request.tools` // here would let mcp_tools expose a tool that the event state does not recognize for a generic // tool-count prompt, so a call to it would be rejected as an unknown Responses tool. - ...(mcpToolDefs.length > 0 ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) } : {}), + mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }), }); const message = create(AgentClientMessageSchema, { diff --git a/tests/cursor-blob.test.ts b/tests/cursor-blob.test.ts index 4cd32da08..e97689272 100644 --- a/tests/cursor-blob.test.ts +++ b/tests/cursor-blob.test.ts @@ -115,13 +115,25 @@ function actionText(bytes: Uint8Array): string | undefined { return action?.case === "userMessageAction" ? action.value.userMessage?.text : undefined; } -function activeSelectedImages(bytes: Uint8Array) { +function activeUserMessage(bytes: Uint8Array) { const msg = fromBinary(AgentClientMessageSchema, bytes); const run = msg.message.case === "runRequest" ? msg.message.value : undefined; const action = run?.action?.action; - return action?.case === "userMessageAction" - ? action.value.userMessage?.selectedContext?.selectedImages - : undefined; + return action?.case === "userMessageAction" ? action.value.userMessage : undefined; +} + +function activeSelectedImages(bytes: Uint8Array) { + return activeUserMessage(bytes)?.selectedContext?.selectedImages; +} + +function conversationToolCallSteps(bytes: Uint8Array) { + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + return (run?.conversationState?.turns ?? []).flatMap(turnId => { + const turn = fromBinary(ConversationTurnStructureSchema, blobData(turnId)); + if (turn.turn.case !== "agentConversationTurn") return []; + return turn.turn.value.steps.map(stepId => fromBinary(ConversationStepSchema, blobData(stepId))); + }).filter(step => step.message.case === "toolCall"); } function nativeTurnUserMessages(bytes: Uint8Array) { @@ -190,14 +202,33 @@ describe("Cursor blob handshake", () => { }); expect(actionText(bytes)).toBe("see this"); + const userMessage = activeUserMessage(bytes); + expect(userMessage?.mode).toBe(1); + expect(userMessage?.selectedContext).toBeDefined(); const images = activeSelectedImages(bytes); expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("img-uuid-1"); expect(images?.[0]?.mimeType).toBe("image/png"); + expect(images?.[0]?.path).toBe(""); expect(images?.[0]?.dataOrBlobId.case).toBe("data"); expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); }); + test("encodeCursorRunRequest always sends empty selectedContext and mode=1 on text-only turns", () => { + const bytes = encodeCursorRunRequest({ + modelId: "claude-4.6-opus-high", + conversationId: "c1", + system: [], + messages: [{ role: "user", content: "hi" }], + }); + + const userMessage = activeUserMessage(bytes); + expect(userMessage?.text).toBe("hi"); + expect(userMessage?.mode).toBe(1); + expect(userMessage?.selectedContext).toBeDefined(); + expect(userMessage?.selectedContext?.selectedImages.length).toBe(0); + }); + test("encodeCursorRunRequest keeps selectedContext only on the active user turn", () => { const activeImageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); const bytes = encodeCursorRunRequest({ @@ -234,8 +265,12 @@ describe("Cursor blob handshake", () => { const historicalUser = nativeTurnUserMessages(bytes)[0]; expect(historicalUser?.text).toBe("old turn"); - expect(historicalUser?.selectedContext).toBeUndefined(); + expect(historicalUser?.mode).toBe(1); + expect(historicalUser?.selectedContext).toBeDefined(); + expect(historicalUser?.selectedContext?.selectedImages.length).toBe(0); + const activeMessage = activeUserMessage(bytes); + expect(activeMessage?.mode).toBe(1); const images = activeSelectedImages(bytes); expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("active-img"); @@ -780,6 +815,115 @@ describe("Cursor blob handshake", () => { expect(run?.action?.action.case).toBe("resumeAction"); }); + + test("forwards view_image tool-result data URLs as McpImageContent", () => { + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const imageUrl = `data:image/png;base64,${Buffer.from(imageBytes).toString("base64")}`; + const bytes = encodeCursorRunRequest({ + modelId: "composer-2.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]\ncall_id: call_view\nname: view_image\nis_error: false\noutput:" }], + rawMessages: [ + { role: "user", content: "describe the image", timestamp: 1 }, + { + role: "assistant", + model: "cursor/composer-2.5", + timestamp: 2, + content: [{ type: "toolCall", id: "call_view", name: "view_image", arguments: { path: "/tmp/x.png" } }], + }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [ + { type: "text", text: "image loaded" }, + { type: "image", imageUrl, detail: "auto" }, + ], + isError: false, + timestamp: 3, + }, + ], + }); + + const toolCalls = conversationToolCallSteps(bytes); + expect(toolCalls.length).toBe(1); + const tool = toolCalls[0]?.message; + expect(tool?.case).toBe("toolCall"); + if (tool?.case !== "toolCall") throw new Error("expected toolCall"); + expect(tool.value.tool.case).toBe("mcpToolCall"); + if (tool.value.tool.case !== "mcpToolCall") throw new Error("expected mcpToolCall"); + const result = tool.value.tool.value.result; + expect(result?.result.case).toBe("success"); + if (result?.result.case !== "success") throw new Error("expected success"); + const content = result.result.value.content; + expect(content.length).toBe(2); + expect(content[0]?.content.case).toBe("text"); + expect(content[1]?.content.case).toBe("image"); + if (content[1]?.content.case !== "image") throw new Error("expected image"); + expect(content[1].content.value.mimeType).toBe("image/png"); + expect(Array.from(content[1].content.value.data)).toEqual(Array.from(imageBytes)); + }); + + test("forwards grok-4.5 view_image tool-result data URLs as McpImageContent", () => { + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const imageUrl = `data:image/png;base64,${Buffer.from(imageBytes).toString("base64")}`; + const bytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]\ncall_id: call_view\nname: view_image\nis_error: false\noutput:" }], + rawMessages: [ + { role: "user", content: "describe the image", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + timestamp: 2, + content: [{ type: "toolCall", id: "call_view", name: "view_image", arguments: { path: "/tmp/x.png" } }], + }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [ + { type: "text", text: "image loaded" }, + { type: "image", imageUrl, detail: "auto" }, + ], + isError: false, + timestamp: 3, + }, + ], + }); + + const toolCalls = conversationToolCallSteps(bytes); + expect(toolCalls.length).toBe(1); + const tool = toolCalls[0]?.message; + expect(tool?.case).toBe("toolCall"); + if (tool?.case !== "toolCall") throw new Error("expected toolCall"); + expect(tool.value.tool.case).toBe("mcpToolCall"); + if (tool.value.tool.case !== "mcpToolCall") throw new Error("expected mcpToolCall"); + const result = tool.value.tool.value.result; + expect(result?.result.case).toBe("success"); + if (result?.result.case !== "success") throw new Error("expected success"); + const content = result.result.value.content; + expect(content.length).toBe(2); + expect(content[0]?.content.case).toBe("text"); + expect(content[1]?.content.case).toBe("image"); + if (content[1]?.content.case !== "image") throw new Error("expected image"); + expect(content[1].content.value.mimeType).toBe("image/png"); + expect(Array.from(content[1].content.value.data)).toEqual(Array.from(imageBytes)); + + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + const turn = fromBinary(ConversationTurnStructureSchema, blobData(run?.conversationState?.turns[0] ?? new Uint8Array())); + expect(turn.turn.case).toBe("agentConversationTurn"); + const steps = turn.turn.case === "agentConversationTurn" ? turn.turn.value.steps : []; + expect(steps).toHaveLength(1); + const step = fromBinary(ConversationStepSchema, blobData(steps[0] ?? new Uint8Array())); + expect(step.message.case).toBe("toolCall"); + expect(step.message.case === "toolCall" && step.message.value.tool.case).toBe("mcpToolCall"); + expect(run?.action?.action.case).toBe("resumeAction"); + }); }); describe("Cursor AgentRunRequest.mcp_tools channel", () => { @@ -811,7 +955,7 @@ describe("Cursor AgentRunRequest.mcp_tools channel", () => { expect(mcpToolNames(bytes)).toEqual(["exec_command"]); }); - test("leaves mcp_tools unset when tools are empty", () => { + test("sends empty mcp_tools wrapper when tools are empty", () => { const bytes = encodeCursorRunRequest({ modelId: "gpt-5.6-luna-high", conversationId: "c1", @@ -819,10 +963,14 @@ describe("Cursor AgentRunRequest.mcp_tools channel", () => { messages: [{ role: "user", content: "hi" }], tools: [], }); - expect(mcpToolNames(bytes)).toBeUndefined(); + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + expect(run?.mcpTools).toBeDefined(); + expect(run?.mcpTools?.mcpTools.length).toBe(0); + expect(mcpToolNames(bytes)).toEqual([]); }); - test("leaves mcp_tools unset when toolChoice is none", () => { + test("sends empty mcp_tools wrapper when toolChoice is none", () => { const bytes = encodeCursorRunRequest({ modelId: "gpt-5.6-luna-high", conversationId: "c1", @@ -831,7 +979,11 @@ describe("Cursor AgentRunRequest.mcp_tools channel", () => { toolChoice: "none", tools: [{ name: "js", namespace: "mcp__node_repl", description: "Run JS", parameters: {} }], }); - expect(mcpToolNames(bytes)).toBeUndefined(); + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + expect(run?.mcpTools).toBeDefined(); + expect(run?.mcpTools?.mcpTools.length).toBe(0); + expect(mcpToolNames(bytes)).toEqual([]); }); }); diff --git a/tests/cursor-vision-wire-harness.test.ts b/tests/cursor-vision-wire-harness.test.ts new file mode 100644 index 000000000..3fa8223d8 --- /dev/null +++ b/tests/cursor-vision-wire-harness.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, test } from "bun:test"; +import { fromBinary } from "@bufbuild/protobuf"; +import { encodeCursorRunRequest } from "../src/adapters/cursor/protobuf-request"; +import { + AgentClientMessageSchema, + ConversationStepSchema, + ConversationTurnStructureSchema, +} from "../src/adapters/cursor/gen/agent_pb"; +import { handleCursorNativeKv, resetCursorBlobStateForTests } from "../src/adapters/cursor/native-exec"; +import { GetBlobArgsSchema, KvServerMessageSchema } from "../src/adapters/cursor/gen/agent_pb"; +import { create } from "@bufbuild/protobuf"; + +function blobData(blobId: Uint8Array): Uint8Array { + const reply = fromBinary(AgentClientMessageSchema, handleCursorNativeKv(create(KvServerMessageSchema, { + id: 1, + message: { case: "getBlobArgs", value: create(GetBlobArgsSchema, { blobId }) }, + }))); + const kv = reply.message.case === "kvClientMessage" ? reply.message.value : undefined; + const result = kv?.message.case === "getBlobResult" ? kv.message.value.blobData : undefined; + if (!result) throw new Error("missing blob data"); + return result; +} + +function activeSelectedImageBytes(bytes: Uint8Array): Uint8Array | undefined { + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + const action = run?.action?.action; + const image = action?.case === "userMessageAction" + ? action.value.userMessage?.selectedContext?.selectedImages[0] + : undefined; + if (!image || image.dataOrBlobId.case !== "data") return undefined; + return image.dataOrBlobId.value; +} + +function viewImageToolResultBytes(bytes: Uint8Array): Uint8Array | undefined { + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + for (const turnId of run?.conversationState?.turns ?? []) { + const turn = fromBinary(ConversationTurnStructureSchema, blobData(turnId)); + if (turn.turn.case !== "agentConversationTurn") continue; + for (const stepId of turn.turn.value.steps) { + const step = fromBinary(ConversationStepSchema, blobData(stepId)); + if (step.message.case !== "toolCall") continue; + const tool = step.message.value.tool; + if (tool.case !== "mcpToolCall") continue; + const result = tool.value.result?.result; + if (result?.case !== "success") continue; + for (const item of result.value.content) { + if (item.content.case === "image") return item.content.value.data; + } + } + } + return undefined; +} + +describe("Cursor vision wire harness", () => { + test("grok attach + view_image paths keep non-empty PNG bytes on the wire", () => { + resetCursorBlobStateForTests(); + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13]); + const imageUrl = `data:image/png;base64,${Buffer.from(imageBytes).toString("base64")}`; + + const attachBytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: [], + messages: [{ role: "user", content: "see this" }], + selectedImages: [{ uuid: "img-uuid-1", mimeType: "image/png", data: imageBytes }], + }); + const attachWireBytes = activeSelectedImageBytes(attachBytes); + expect(attachWireBytes).toBeDefined(); + expect(attachWireBytes!.byteLength).toBeGreaterThan(0); + expect(Array.from(attachWireBytes!.slice(0, 4))).toEqual([137, 80, 78, 71]); + + const viewBytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]\ncall_id: call_view\nname: view_image\nis_error: false\noutput:" }], + rawMessages: [ + { role: "user", content: "describe the image", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + timestamp: 2, + content: [{ type: "toolCall", id: "call_view", name: "view_image", arguments: { path: "/tmp/x.png" } }], + }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [{ type: "image", imageUrl, detail: "auto" }], + isError: false, + timestamp: 3, + }, + ], + }); + const viewWireBytes = viewImageToolResultBytes(viewBytes); + expect(viewWireBytes).toBeDefined(); + expect(viewWireBytes!.byteLength).toBeGreaterThan(0); + expect(Array.from(viewWireBytes!)).toEqual(Array.from(imageBytes)); + }); +}); From 1eecfa4bd19a02bbfbcd742859ed73cf1b3698cb Mon Sep 17 00:00:00 2001 From: SB Yoon <44089734+yansigit@users.noreply.github.com> Date: Fri, 7 Aug 2026 02:18:09 -0600 Subject: [PATCH 07/12] fix(cursor): harden SelectedImage vision edge cases Prep before the 1 MiB hard cap, promote consecutive view_image tool results with nudge text and MCP dedupe, fail-closed on undecodable MIME, and restore curated Auto/Composer sidecar noVisionModels. Co-authored-by: Cursor --- .../docs/reference/configuration/providers.md | 10 + .../2026-08-06-cursor-grok-view-image-fix.md | 39 -- src/adapters/cursor/discovery.ts | 17 + src/adapters/cursor/images.ts | 449 ++++++++++++++++-- src/adapters/cursor/live-transport.ts | 8 +- src/adapters/cursor/protobuf-request.ts | 59 ++- src/adapters/cursor/types.ts | 7 +- src/providers/registry.ts | 5 + src/types.ts | 12 +- .../catalog-vision-sidecar-modalities.test.ts | 93 ++-- tests/cursor-blob.test.ts | 241 +++++++++- tests/cursor-discovery.test.ts | 20 + tests/cursor-effort-suffix.test.ts | 7 + tests/cursor-images.test.ts | 288 ++++++++++- tests/cursor-static-catalog.test.ts | 2 +- tests/cursor-vision-wire-harness.test.ts | 7 +- tests/helpers/cursor-grumpy-fixture.png | Bin 0 -> 440734 bytes tests/model-in-list.test.ts | 23 + tests/oauth-provider-reconcile.test.ts | 31 +- tests/provider-registry-parity.test.ts | 8 +- 20 files changed, 1183 insertions(+), 143 deletions(-) delete mode 100644 docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md create mode 100644 tests/helpers/cursor-grumpy-fixture.png create mode 100644 tests/model-in-list.test.ts diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 474b8327c..149deb11b 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -277,6 +277,16 @@ 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` and `composer-*` stay on +the curated `noVisionModels` list and use the vision describe sidecar instead. + +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. + 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: diff --git a/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md b/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md deleted file mode 100644 index ab8584b7e..000000000 --- a/docs/superpowers/plans/2026-08-06-cursor-grok-view-image-fix.md +++ /dev/null @@ -1,39 +0,0 @@ -# Cursor Grok view_image External-Model Fix - -## Goal -Stop OpenCodex from discarding `view_image` image bytes for external Cursor models (`grok-4.5`, GPT/Claude/Gemini families) so Cursor actually receives the tool-result image. Add fully automated wire-level verification so we do not need Codex Desktop reinstall/restart/manual testing. - -## Root Cause -`isCursorExternalWireModel("grok-4.5") === true`. In `conversationTurns()`, the external branch flattens tool results to text via `contentToText()`, which drops image parts. Image-only `view_image` therefore becomes an empty `[Tool Result]\n` for Cursor. The existing `McpImageContent` forwarding path only runs on the native (`composer-*`) branch and is unit-tested only with `composer-2.5`. - -## Global Constraints -- Keep prepare/encode synchronous. -- Preserve `resumeAction` for trailing tool-result continuations (do not force `userMessageAction` there). -- Keep history/root blobs text-only for user-attached images; only active SelectedImage / tool-result image content carries bytes. -- Do not put Cursor back into `noVisionModels`. -- Prefer forwarding real image bytes over inventing placeholders. -- Automated verification must prove non-empty image bytes on the Cursor wire for `grok-4.5` without requiring Desktop UI clicks. -- Work only under `/Users/user/Projects/opencodex`. -- Commit on `audit/cursor-dev`. - -## Tasks - -### Task 1: Failing Grok view_image wire test + fix external flatten path -1. TDD: add a failing test in `tests/cursor-blob.test.ts` that encodes a `view_image` tool-result continuation with `modelId: "grok-4.5"` and asserts the conversation turn carries `McpImageContent` (or equivalent non-empty image bytes) for the tool result — not empty text-only `[Tool Result]`. -2. Optionally add a tiny pure helper/script test that builds the same request from a fixture PNG path and asserts byte length > 0 / PNG magic, so agents can re-run without Desktop. -3. Implement the minimal fix in `src/adapters/cursor/protobuf-request.ts`: for external models, when a toolResult has image parts (or when a matched priorCall exists with image content), use the native `toolCallStep(..., message)` / `McpImageContent` path instead of text-flattening away images. Keep text-flatten only for non-image tool results if that remains needed for external replay compatibility. -4. Keep existing composer `view_image` McpImageContent test green. -5. Run focused tests, commit, report. - -### Task 2: Automated vision harness (no Desktop) -1. Add `tool/cursor_vision_wire_check.ts` (or `tests/cursor-vision-wire-harness.test.ts`) that: - - builds a synthetic 8x8 solid-color PNG (or uses a fixture) - - encodes both attach-turn SelectedImage and grok view_image tool-result paths - - prints/asserts pass/fail with concrete byte counts -2. Document one command: `bun test tests/cursor-blob.test.ts` and `bun test tests/cursor-vision-wire-harness.test.ts` (or `bun run tool/cursor_vision_wire_check.ts`). -3. Commit if separate from Task 1; otherwise fold into Task 1 if tiny. - -## Success Criteria -- `bun test tests/cursor-blob.test.ts` proves grok-4.5 view_image tool results carry non-empty image bytes. -- Automated harness/test can be re-run by the agent without restarting Codex Desktop. -- No manual screenshot testing required to claim the wire fix. diff --git a/src/adapters/cursor/discovery.ts b/src/adapters/cursor/discovery.ts index 747fb32d1..e7dd96691 100644 --- a/src/adapters/cursor/discovery.ts +++ b/src/adapters/cursor/discovery.ts @@ -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"; diff --git a/src/adapters/cursor/images.ts b/src/adapters/cursor/images.ts index f4f08f7ef..aca041b24 100644 --- a/src/adapters/cursor/images.ts +++ b/src/adapters/cursor/images.ts @@ -7,16 +7,66 @@ import type { PinnedAddress } from "../../lib/pinned-http"; import { SelectedContextSchema, SelectedImageSchema, + SelectedImage_BlobIdWithDataSchema, + SelectedImage_DimensionSchema, type SelectedContext, type SelectedImage, } from "./gen/agent_pb"; +import { + storeCursorBlob, + type CursorBlobRequestScopeToken, +} from "./native-exec"; -/** Matches OmniRoute / composer-api per-image byte cap. */ +/** Final per-image byte cap after prep (OmniRoute / composer-api style). */ export const MAX_CURSOR_IMAGE_BYTES = 1024 * 1024; +/** + * Inbound decode/fetch bomb ceiling before JPEG prep. Large clipboard PNGs may exceed + * {@link MAX_CURSOR_IMAGE_BYTES} raw but shrink under the wire cap after re-encode. + */ +export const MAX_CURSOR_IMAGE_DECODE_BYTES = 16 * 1024 * 1024; + +/** + * Soft target for Cursor vision hydration. Live A/B: ~430 KiB PNG failed ("gray"/wrong UI) + * while the same visual as ~75 KiB JPEG succeeded. Prefer JPEG at or under this size. + */ +export const CURSOR_VISION_SOFT_MAX_BYTES = 100 * 1024; + +/** Soft target when the client requests `detail: original` or `high`. */ +export const CURSOR_VISION_SOFT_MAX_BYTES_HIGH = 256 * 1024; + +/** Longest edge after Cursor vision prep (Cursor staff guidance: ≤ 2000 px). */ +export const CURSOR_VISION_MAX_EDGE = 2000; + +const CURSOR_VISION_JPEG_QUALITIES_DEFAULT = [85, 70, 55, 40] as const; +const CURSOR_VISION_JPEG_QUALITIES_HIGH = [90, 80, 65, 50] as const; + +const CURSOR_VISION_PASSTHROUGH_MIME = new Set([ + "image/jpeg", + "image/jpg", + "image/png", + "image/gif", + "image/webp", +]); + /** Upper bound on images attached to one Cursor turn. */ export const MAX_CURSOR_IMAGES = 12; +/** + * UserMessage text when promoting view_image tool-result pixels onto SelectedImage. + * Empty text alone is accepted for attach-only turns; tool promotions use a nudge so + * Cursor/Grok do not invent content from file paths. + */ +export const CURSOR_VISION_PROMOTE_NUDGE = + "Describe the image from the tool result. Do not infer content from file paths or names."; + +/** Honest marker when MCP tool-result image bytes are peeled onto SelectedImage instead. */ +export const CURSOR_VISION_MCP_IMAGE_OMITTED = "[image attached via SelectedImage]"; + +/** Marker when an image cannot be prepared for the Cursor vision wire. */ +export const CURSOR_VISION_IMAGE_OMITTED = + "[image omitted: undecodable or unsupported type]"; + const IMAGE_FETCH_TIMEOUT_MS = (() => { const parsed = Number.parseInt(process.env.CURSOR_IMAGE_FETCH_TIMEOUT_MS || "15000", 10); return Number.isInteger(parsed) && parsed > 0 ? parsed : 15_000; @@ -36,8 +86,14 @@ export interface ResolvedCursorImage { data: Uint8Array; mimeType: string; uuid: string; + /** Codex/OpenAI image detail hint; affects JPEG soft-cap tier. */ + detail?: string; } +export type PrepareCursorImageOutcome = + | { status: "ready"; image: ResolvedCursorImage } + | { status: "omitted"; reason: string }; + function isImagePart(part: OcxContentPart): part is OcxImageContent { return part.type === "image"; } @@ -46,6 +102,19 @@ function estimatedBase64DecodedBytes(payload: string): number { return Math.floor((payload.length * 3) / 4); } +function isHighDetail(detail: string | undefined): boolean { + const normalized = (detail ?? "").trim().toLowerCase(); + return normalized === "original" || normalized === "high"; +} + +function softMaxBytesForDetail(detail: string | undefined): number { + return isHighDetail(detail) ? CURSOR_VISION_SOFT_MAX_BYTES_HIGH : CURSOR_VISION_SOFT_MAX_BYTES; +} + +function jpegQualitiesForDetail(detail: string | undefined): readonly number[] { + return isHighDetail(detail) ? CURSOR_VISION_JPEG_QUALITIES_HIGH : CURSOR_VISION_JPEG_QUALITIES_DEFAULT; +} + function decodeDataUrl(url: string): { data: Uint8Array; mimeType: string } { const comma = url.indexOf(","); if (comma < 0) throw new CursorImageError("Image data URL is malformed."); @@ -60,13 +129,13 @@ function decodeDataUrl(url: string): { data: Uint8Array; mimeType: string } { if (!isBase64) { throw new CursorImageError("Image data URL must be base64-encoded."); } - if (payload.length > MAX_CURSOR_IMAGE_BYTES * 2) { - throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + if (payload.length > MAX_CURSOR_IMAGE_DECODE_BYTES * 2) { + throw new CursorImageError("Image input is too large to process safely."); } const normalized = payload.replace(/\s/g, ""); - if (estimatedBase64DecodedBytes(normalized) > MAX_CURSOR_IMAGE_BYTES) { - throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + if (estimatedBase64DecodedBytes(normalized) > MAX_CURSOR_IMAGE_DECODE_BYTES) { + throw new CursorImageError("Image input is too large to process safely."); } let data: Uint8Array; @@ -78,6 +147,9 @@ function decodeDataUrl(url: string): { data: Uint8Array; mimeType: string } { if (normalized.length > 0 && data.byteLength === 0) { throw new CursorImageError("Image data URL contains invalid base64 data."); } + if (data.byteLength > MAX_CURSOR_IMAGE_DECODE_BYTES) { + throw new CursorImageError("Image input is too large to process safely."); + } return { data, mimeType }; } @@ -118,7 +190,7 @@ async function fetchHttpsImageBytes(url: string, signal?: AbortSignal): Promise< try { const response = await pinnedHttpsGet(url, pickPinnedAddress(resolved.addresses), controller.signal, { - maxBytes: MAX_CURSOR_IMAGE_BYTES, + maxBytes: MAX_CURSOR_IMAGE_DECODE_BYTES, }); const contentType = (response.headers.get("content-type") || "").toLowerCase(); const mimeType = contentType.split(";")[0]?.trim() || ""; @@ -135,8 +207,8 @@ async function fetchHttpsImageBytes(url: string, signal?: AbortSignal): Promise< const { value, done } = await reader.read(); if (done) break; total += value.byteLength; - if (total > MAX_CURSOR_IMAGE_BYTES) { - throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + if (total > MAX_CURSOR_IMAGE_DECODE_BYTES) { + throw new CursorImageError("Image input is too large to process safely."); } chunks.push(value); } @@ -163,30 +235,48 @@ async function fetchHttpsImageBytes(url: string, signal?: AbortSignal): Promise< /** Collect image URLs from one message's content parts, preserving order. */ export function extractCursorImageUrls(content: string | readonly OcxContentPart[]): string[] { + return extractCursorImageParts(content).map(part => part.imageUrl); +} + +export interface CursorImagePartRef { + imageUrl: string; + detail?: string; +} + +/** Collect image parts (URL + optional detail) from one message's content. */ +export function extractCursorImageParts( + content: string | readonly OcxContentPart[], +): CursorImagePartRef[] { if (typeof content === "string" || !Array.isArray(content)) return []; - const urls: string[] = []; + const parts: CursorImagePartRef[] = []; for (const part of content) { if (isImagePart(part) && typeof part.imageUrl === "string" && part.imageUrl.length > 0) { - urls.push(part.imageUrl); + parts.push({ + imageUrl: part.imageUrl, + ...(typeof part.detail === "string" && part.detail.length > 0 ? { detail: part.detail } : {}), + }); } } - return urls; + return parts; } /** * Resolve OpenCodex image parts (data: or https:) into bytes for SelectedImage. - * Remote fetches reuse the repo SSRF destination policy + DNS-pinned HTTPS path. + * Prep (JPEG soft-cap) runs before the 1 MiB wire cap so large clipboard PNGs can shrink. + * Unsupported / undecodable images are omitted (fail-closed). */ export async function resolveCursorImages( imageUrls: readonly string[], signal?: AbortSignal, + options?: { details?: readonly (string | undefined)[] }, ): Promise { if (imageUrls.length > MAX_CURSOR_IMAGES) { throw new CursorImageError(`Too many images in one request (max ${MAX_CURSOR_IMAGES}).`); } const out: ResolvedCursorImage[] = []; - for (const url of imageUrls) { + for (let i = 0; i < imageUrls.length; i++) { + const url = imageUrls[i]; if (typeof url !== "string" || url.length === 0) { throw new CursorImageError("Image URL is missing."); } @@ -196,55 +286,348 @@ export async function resolveCursorImages( if (resolved.data.byteLength === 0) { throw new CursorImageError("Image input is empty."); } - if (resolved.data.byteLength > MAX_CURSOR_IMAGE_BYTES) { - throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); - } - out.push({ + const outcome = await prepareCursorImageForWire({ data: resolved.data, mimeType: resolved.mimeType, uuid: randomUUID(), + ...(options?.details?.[i] ? { detail: options.details[i] } : {}), }); + if (outcome.status === "omitted") continue; + if (outcome.image.data.byteLength > MAX_CURSOR_IMAGE_BYTES) { + throw new CursorImageError("Image input is too large (max 1 MiB). Resize and retry."); + } + out.push(outcome.image); } return out; } -/** Build SelectedImage messages with inline `data` (field 8), matching OmniRoute. */ -export function buildSelectedImages(images: readonly ResolvedCursorImage[]): SelectedImage[] { - return images.map(image => create(SelectedImageSchema, { - uuid: image.uuid, - mimeType: image.mimeType, - dataOrBlobId: { case: "data", value: image.data }, - })); +export async function resolveCursorImageParts( + parts: readonly CursorImagePartRef[], + signal?: AbortSignal, +): Promise { + return resolveCursorImages( + parts.map(part => part.imageUrl), + signal, + { details: parts.map(part => part.detail) }, + ); +} + +/** Filename Cursor clients typically put on SelectedImage.path (shunt / agent parity). */ +export function cursorImageAttachmentPath(uuid: string, mimeType: string): string { + const normalized = mimeType.toLowerCase(); + const ext = normalized === "image/jpeg" || normalized === "image/jpg" ? "jpg" + : normalized === "image/gif" ? "gif" + : normalized === "image/webp" ? "webp" + : "png"; + return `attachment-${uuid}.${ext}`; +} + +/** + * Re-encode toward a JPEG under the soft vision cap when Bun can decode the payload. + * Unsupported MIME or undecodable bytes are omitted (fail-closed) except for tiny + * undecodable PNG/JPEG/GIF/WebP stubs used in unit tests (pass-through under soft cap). + */ +export async function prepareCursorImageForWire( + image: ResolvedCursorImage, +): Promise { + const mime = image.mimeType.toLowerCase(); + const softMax = softMaxBytesForDetail(image.detail); + + if (!CURSOR_VISION_PASSTHROUGH_MIME.has(mime)) { + return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }; + } + + try { + // Force a full decode before accepting passthrough / encode (Anthropic-style validate). + // Includes already-small JPEGs — labeled JPEG under the soft cap must still decode. + await new Bun.Image(image.data).resize(1, 1).jpeg({ quality: 1 }).toBuffer(); + + const alreadySmallJpeg = (mime === "image/jpeg" || mime === "image/jpg") + && image.data.byteLength <= softMax; + if (alreadySmallJpeg) return { status: "ready", image }; + + const meta = await new Bun.Image(image.data).metadata(); + const width = typeof meta.width === "number" ? meta.width : 0; + const height = typeof meta.height === "number" ? meta.height : 0; + let targetW = width; + let targetH = height; + if (width > 0 && height > 0 && Math.max(width, height) > CURSOR_VISION_MAX_EDGE) { + const scale = CURSOR_VISION_MAX_EDGE / Math.max(width, height); + targetW = Math.max(1, Math.round(width * scale)); + targetH = Math.max(1, Math.round(height * scale)); + } + + let best: Uint8Array | undefined; + for (const quality of jpegQualitiesForDetail(image.detail)) { + let pipeline = new Bun.Image(image.data); + if (targetW > 0 && targetH > 0 && (targetW !== width || targetH !== height)) { + pipeline = pipeline.resize(targetW, targetH); + } + const encoded = new Uint8Array(await pipeline.jpeg({ quality }).bytes()); + if (!best || encoded.byteLength < best.byteLength) best = encoded; + if (encoded.byteLength <= softMax) { + return { + status: "ready", + image: { ...image, data: encoded, mimeType: "image/jpeg" }, + }; + } + } + if (best && best.byteLength < image.data.byteLength) { + return { + status: "ready", + image: { ...image, data: best, mimeType: "image/jpeg" }, + }; + } + // Decoded but could not shrink under soft max — still under wire hard cap check later. + return { status: "ready", image }; + } catch { + // Tiny undecodable stubs (unit wire tests) may still be referenced as blobIdWithData. + if (image.data.byteLength <= 64 && CURSOR_VISION_PASSTHROUGH_MIME.has(mime)) { + return { status: "ready", image }; + } + return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }; + } +} + +/** + * Sniff PNG/JPEG/GIF dimensions from raw bytes when the header is present. + * Best-effort only — unknown formats return undefined (dimension is optional). + */ +export function sniffCursorImageDimensions( + data: Uint8Array, +): { width: number; height: number } | undefined { + // PNG: signature + IHDR chunk (width/height at bytes 16..23) + if ( + data.byteLength >= 24 + && data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47 + && data[4] === 0x0d && data[5] === 0x0a && data[6] === 0x1a && data[7] === 0x0a + ) { + const width = ((data[16]! << 24) | (data[17]! << 16) | (data[18]! << 8) | data[19]!) >>> 0; + const height = ((data[20]! << 24) | (data[21]! << 16) | (data[22]! << 8) | data[23]!) >>> 0; + if (width > 0 && height > 0) return { width, height }; + } + // GIF: "GIF8" + width/height as little-endian u16 at bytes 6..9 + if ( + data.byteLength >= 10 + && data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x38 + ) { + const width = data[6]! | (data[7]! << 8); + const height = data[8]! | (data[9]! << 8); + if (width > 0 && height > 0) return { width, height }; + } + // JPEG: scan for SOF0/SOF2 marker with dimensions + if (data.byteLength >= 4 && data[0] === 0xff && data[1] === 0xd8) { + let offset = 2; + while (offset + 8 < data.byteLength) { + if (data[offset] !== 0xff) break; + const marker = data[offset + 1]!; + const length = (data[offset + 2]! << 8) | data[offset + 3]!; + if (marker === 0xc0 || marker === 0xc2) { + const height = (data[offset + 5]! << 8) | data[offset + 6]!; + const width = (data[offset + 7]! << 8) | data[offset + 8]!; + if (width > 0 && height > 0) return { width, height }; + break; + } + if (length < 2) break; + offset += 2 + length; + } + } + return undefined; +} + +/** + * Build SelectedImage messages for the AgentService vision path: + * store bytes in the local KV map under sha256(blobId), and encode + * `blobIdWithData` so the server can populate its cache without relying solely + * on getBlobArgs timing. Also set `path` like native/shunt clients. + */ +export function buildSelectedImages( + images: readonly ResolvedCursorImage[], + requestScope?: CursorBlobRequestScopeToken, +): SelectedImage[] { + return images.map(image => { + const blobId = storeCursorBlob(image.data, requestScope); + const dims = sniffCursorImageDimensions(image.data); + return create(SelectedImageSchema, { + uuid: image.uuid, + path: cursorImageAttachmentPath(image.uuid, image.mimeType), + mimeType: image.mimeType, + ...(dims + ? { dimension: create(SelectedImage_DimensionSchema, dims) } + : {}), + dataOrBlobId: { + case: "blobIdWithData", + value: create(SelectedImage_BlobIdWithDataSchema, { + blobId, + data: image.data, + }), + }, + }); + }); } /** - * OmniRoute always sends `UserMessage.selected_context`, even when empty. - * Cursor accepts the request without it, but vision turns are more reliable - * when the placeholder matches cursor-agent's wire format. + * Always send `UserMessage.selected_context`, even when empty — matches cursor-agent. + * When images are present, they are blobIdWithData refs backed by the request-scoped KV store. */ -export function buildSelectedContext(images: readonly ResolvedCursorImage[] = []): SelectedContext { +export function buildSelectedContext( + images: readonly ResolvedCursorImage[] = [], + requestScope?: CursorBlobRequestScopeToken, +): SelectedContext { return create(SelectedContextSchema, { - selectedImages: buildSelectedImages(images), + selectedImages: buildSelectedImages(images, requestScope), }); } /** - * Resolve images for the active user/developer turn of a Cursor request. - * History stays text-only; only the current UserMessageAction carries SelectedImage. + * Trailing toolResult image promotion: parts kept for SelectedImage (newest + * {@link MAX_CURSOR_IMAGES}) and the call ids that own at least one kept part. + * Older overflow parts stay on MCP (not omitted) because they are not promoted. + */ +export function extractTrailingToolResultImagePromotion( + messages: readonly OcxMessage[], +): { parts: CursorImagePartRef[]; omittedOlder: number; promotedCallIds: Set } { + let start = messages.length; + while (start > 0 && messages[start - 1]?.role === "toolResult") start -= 1; + + const entries: Array<{ callId: string; part: CursorImagePartRef }> = []; + for (const message of messages.slice(start)) { + if (message.role !== "toolResult") continue; + for (const part of extractCursorImageParts(message.content)) { + entries.push({ callId: message.toolCallId, part }); + } + } + + const kept = entries.length <= MAX_CURSOR_IMAGES + ? entries + : entries.slice(-MAX_CURSOR_IMAGES); + return { + parts: kept.map(entry => entry.part), + omittedOlder: Math.max(0, entries.length - kept.length), + promotedCallIds: new Set(kept.map(entry => entry.callId)), + }; +} + +/** + * Collect image parts from trailing consecutive toolResult messages, + * oldest→newest within the block, capped at {@link MAX_CURSOR_IMAGES} (keep newest). + */ +export function extractTrailingToolResultImageParts( + messages: readonly OcxMessage[], +): { parts: CursorImagePartRef[]; omittedOlder: number } { + const { parts, omittedOlder } = extractTrailingToolResultImagePromotion(messages); + return { parts, omittedOlder }; +} + +/** + * Resolve images for the active Cursor turn. + * - Trailing user/developer: attach images (SelectedImage). + * - Trailing toolResult run (e.g. Codex `view_image`): same channel — McpImageContent + * alone is not consumed as vision for external Cursor models like grok-4.5. */ export async function resolveActiveCursorImages( messages: readonly OcxMessage[] | undefined, signal?: AbortSignal, ): Promise { if (!messages?.length) return []; - if (messages.at(-1)?.role === "toolResult") return []; + + const last = messages.at(-1); + if (last?.role === "toolResult") { + const { parts } = extractTrailingToolResultImageParts(messages); + if (parts.length === 0) return []; + return resolveCursorImageParts(parts, signal); + } for (let i = messages.length - 1; i >= 0; i--) { const message = messages[i]; if (!message) continue; if (message.role === "user" || message.role === "developer") { - return resolveCursorImages(extractCursorImageUrls(message.content), signal); + return resolveCursorImageParts(extractCursorImageParts(message.content), signal); } } return []; } + +function imageDataUrlFromPrepared(image: ResolvedCursorImage): string { + return `data:${image.mimeType};base64,${Buffer.from(image.data).toString("base64")}`; +} + +/** + * Re-encode a single `data:image/...;base64,...` URL through {@link prepareCursorImageForWire}. + * Omitted images become a text-safe data URL skip (caller replaces the part). + */ +export async function prepareCursorImageDataUrl( + imageUrl: string, + detail?: string, +): Promise<{ status: "ready"; imageUrl: string } | { status: "omitted"; reason: string }> { + if (!imageUrl.toLowerCase().startsWith("data:")) return { status: "ready", imageUrl }; + try { + const decoded = decodeDataUrl(imageUrl); + if (decoded.data.byteLength === 0) { + return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }; + } + const outcome = await prepareCursorImageForWire({ + data: decoded.data, + mimeType: decoded.mimeType, + uuid: randomUUID(), + ...(detail ? { detail } : {}), + }); + if (outcome.status === "omitted") return outcome; + if (outcome.image.data.byteLength > MAX_CURSOR_IMAGE_BYTES) { + return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }; + } + if (outcome.image.data === decoded.data && outcome.image.mimeType === decoded.mimeType) { + return { status: "ready", imageUrl }; + } + return { status: "ready", imageUrl: imageDataUrlFromPrepared(outcome.image) }; + } catch { + return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }; + } +} + +async function prepareCursorContentParts( + content: string | readonly OcxContentPart[], +): Promise { + if (typeof content === "string" || !Array.isArray(content)) return content; + let changed = false; + const next: OcxContentPart[] = []; + for (const part of content) { + if (part.type === "image" && typeof part.imageUrl === "string" && part.imageUrl.length > 0) { + const prepared = await prepareCursorImageDataUrl(part.imageUrl, part.detail); + if (prepared.status === "omitted") { + changed = true; + next.push({ type: "text", text: prepared.reason }); + continue; + } + if (prepared.imageUrl !== part.imageUrl) changed = true; + next.push({ ...part, imageUrl: prepared.imageUrl }); + } else { + next.push(part); + } + } + return changed ? next : content; +} + +/** + * Rewrite image data URLs in user/developer/toolResult messages (including `view_image` + * tool outputs) through the JPEG soft-cap path before protobuf encode. + */ +export async function prepareCursorRawMessages( + messages: readonly OcxMessage[] | undefined, +): Promise { + if (!messages?.length) return messages; + let changed = false; + const out: OcxMessage[] = []; + for (const message of messages) { + if (message.role === "user" || message.role === "developer" || message.role === "toolResult") { + const content = await prepareCursorContentParts(message.content); + if (content !== message.content) { + changed = true; + out.push({ ...message, content } as OcxMessage); + continue; + } + } + out.push(message); + } + return changed ? out : messages; +} diff --git a/src/adapters/cursor/live-transport.ts b/src/adapters/cursor/live-transport.ts index 86034a6bf..21feedc95 100644 --- a/src/adapters/cursor/live-transport.ts +++ b/src/adapters/cursor/live-transport.ts @@ -13,7 +13,7 @@ import { type TranslatorBudget, } from "../../lib/translator-budget"; import { activePromptText, prepareCursorRunRequest } from "./protobuf-request"; -import { resolveActiveCursorImages } from "./images"; +import { prepareCursorRawMessages, resolveActiveCursorImages } from "./images"; import { createCursorContextUsageTracker, createCursorProtobufEventState, @@ -570,8 +570,10 @@ class LiveCursorTransport implements CursorTransport { clearPrior: request.contextUsageReset === true, storeCheckpoints: request.contextUsageStoreCheckpoints !== false, }); - const selectedImages = await resolveActiveCursorImages(request.rawMessages, signal); - const preparedRequest = { ...request, selectedImages }; + // JPEG soft-cap rewrite for attach + view_image tool-result data URLs before encode. + const rawMessages = await prepareCursorRawMessages(request.rawMessages); + const selectedImages = await resolveActiveCursorImages(rawMessages, signal); + const preparedRequest = { ...request, rawMessages, selectedImages }; // Build the payload once. The estimate is only worth deriving when there is no // carry-forward to fall back on — with a carry present it would never be used (#373). const prepared = prepareCursorRunRequest(preparedRequest, { diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts index c1f0d5e17..9e9ff6ea9 100644 --- a/src/adapters/cursor/protobuf-request.ts +++ b/src/adapters/cursor/protobuf-request.ts @@ -13,7 +13,12 @@ import { storeCursorBlob, type CursorBlobRequestScopeToken, } from "./native-exec"; -import { buildSelectedContext } from "./images"; +import { + buildSelectedContext, + CURSOR_VISION_MCP_IMAGE_OMITTED, + CURSOR_VISION_PROMOTE_NUDGE, + extractTrailingToolResultImagePromotion, +} from "./images"; import { estimateTokens } from "../../lib/token-estimate"; import { AgentClientMessageSchema, @@ -354,7 +359,10 @@ function syntheticToolCallFromResult( }; } -function toolResultContentItems(content: OcxToolResultMessage["content"]) { +function toolResultContentItems( + content: OcxToolResultMessage["content"], + options?: { omitImages?: boolean }, +) { if (typeof content === "string") { return [create(McpToolResultContentItemSchema, { content: { case: "text", value: create(McpTextContentSchema, { text: content }) }, @@ -368,6 +376,11 @@ function toolResultContentItems(content: OcxToolResultMessage["content"]) { })]; } if (part.type === "image" && typeof part.imageUrl === "string" && part.imageUrl.length > 0) { + if (options?.omitImages) { + return [create(McpToolResultContentItemSchema, { + content: { case: "text", value: create(McpTextContentSchema, { text: CURSOR_VISION_MCP_IMAGE_OMITTED }) }, + })]; + } try { if (!part.imageUrl.toLowerCase().startsWith("data:")) return []; const comma = part.imageUrl.indexOf(","); @@ -425,6 +438,7 @@ function toolCallStep( part: Extract, requestScope: CursorBlobRequestScopeToken, result?: OcxToolResultMessage, + options?: { omitToolResultImageCallIds?: ReadonlySet }, ): Uint8Array { const args: Record = {}; for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value); @@ -443,7 +457,7 @@ function toolCallStep( providerIdentifier: OCX_RESPONSES_TOOL_PROVIDER, args, }), - ...(result ? { result: toolResultPart(result) } : {}), + ...(result ? { result: toolResultPart(result, options) } : {}), }), }, }), @@ -451,13 +465,18 @@ function toolCallStep( })), requestScope); } -function toolResultPart(message: OcxToolResultMessage) { +function toolResultPart( + message: OcxToolResultMessage, + options?: { omitToolResultImageCallIds?: ReadonlySet }, +) { + const omitImages = options?.omitToolResultImageCallIds?.has(message.toolCallId) === true + && toolResultHasImage(message.content); return create(McpToolResultSchema, { result: { case: "success", value: create(McpSuccessSchema, { isError: message.isError, - content: toolResultContentItems(message.content), + content: toolResultContentItems(message.content, { omitImages }), }), }, }); @@ -496,6 +515,7 @@ function conversationTurns( request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken, historyMessageStart = 0, + options?: { omitToolResultImageCallIds?: ReadonlySet }, ): Uint8Array[] { const messages = request.rawMessages; if (!messages?.length) return []; @@ -508,7 +528,9 @@ function conversationTurns( const pendingToolCalls = new Map>(); const flush = () => { if (!current) return; - for (const part of pendingToolCalls.values()) current.steps.push(toolCallStep(part, requestScope)); + for (const part of pendingToolCalls.values()) { + current.steps.push(toolCallStep(part, requestScope, undefined, options)); + } turns.push(storeCursorBlob(toBinary(ConversationTurnStructureSchema, create(ConversationTurnStructureSchema, { turn: { case: "agentConversationTurn", @@ -555,7 +577,7 @@ function conversationTurns( if (externalModel) { if (toolResultHasImage(message.content)) { const priorCall = pendingToolCalls.get(message.toolCallId) ?? syntheticToolCallFromResult(message); - current.steps.push(toolCallStep(priorCall, requestScope, message)); + current.steps.push(toolCallStep(priorCall, requestScope, message, options)); pendingToolCalls.delete(message.toolCallId); continue; } @@ -571,7 +593,7 @@ function conversationTurns( } const priorCall = pendingToolCalls.get(message.toolCallId); if (priorCall) { - current.steps.push(toolCallStep(priorCall, requestScope, message)); + current.steps.push(toolCallStep(priorCall, requestScope, message, options)); pendingToolCalls.delete(message.toolCallId); } else { current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, { @@ -588,7 +610,7 @@ function conversationTurns( userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, { text: contentText(message), messageId: crypto.randomUUID(), - selectedContext: buildSelectedContext(), + selectedContext: buildSelectedContext([], requestScope), mode: 1, })), requestScope), steps: [], @@ -659,19 +681,26 @@ function buildPreparedCursorRunRequest( ? appendCursorGenericToolUseHint(request.tools, rawText) : rawText; // Tool-result-only turns resume the remembered Cursor conversation with results in history. + // Exception: image-bearing tool results (Codex view_image) must ride SelectedImage on a + // userMessageAction — McpImageContent inline bytes are not hydrated as vision for grok/etc. const lastRawIsToolResult = request.rawMessages?.at(-1)?.role === "toolResult"; const selectedImages = request.selectedImages ?? []; - const actionCase = !lastRawIsToolResult && (selectedImages.length > 0 || text.trim().length > 0) + const promoteToolResultImages = lastRawIsToolResult && selectedImages.length > 0; + const omitToolResultImageCallIds = promoteToolResultImages + ? extractTrailingToolResultImagePromotion(request.rawMessages ?? []).promotedCallIds + : undefined; + const actionCase = selectedImages.length > 0 || (!lastRawIsToolResult && text.trim().length > 0) ? "userMessageAction" : "resumeAction"; - const selectedContext = buildSelectedContext(selectedImages); + const actionText = promoteToolResultImages ? CURSOR_VISION_PROMOTE_NUDGE : text; + const selectedContext = buildSelectedContext(selectedImages, requestScope); const action = create(ConversationActionSchema, { action: actionCase === "userMessageAction" ? { case: "userMessageAction", value: create(UserMessageActionSchema, { userMessage: create(UserMessageSchema, { - text, + text: actionText, messageId: crypto.randomUUID(), selectedContext, // OmniRoute / cursor-agent always send mode=1 on UserMessage. @@ -689,7 +718,9 @@ function buildPreparedCursorRunRequest( }); const rootPromptMessagesState = rootPromptMessages(request, requestScope); const rootPromptMessageIds = rootPromptMessagesState.ids; - const turnIds = conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart); + const turnIds = conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart, { + omitToolResultImageCallIds, + }); // Hoisted out of the mcp_tools spread below so the estimate can read the same // filtered definitions the wire carries. Both helpers are pure. const visibleTools = cursorToolsForActivePrompt(request.tools, rawText, request.toolChoice); @@ -774,7 +805,7 @@ function buildPreparedCursorRunRequest( // tools the payload dropped — the defect that blocked PR #376. const modelVisibleParts = [ ...rootPromptMessagesState.serialized, - ...(actionCase === "userMessageAction" ? [text] : []), + ...(actionCase === "userMessageAction" ? [actionText] : []), ...mcpToolDefs.map(modelVisibleToolText), ]; return { diff --git a/src/adapters/cursor/types.ts b/src/adapters/cursor/types.ts index a7dc69e1b..4de1223b1 100644 --- a/src/adapters/cursor/types.ts +++ b/src/adapters/cursor/types.ts @@ -17,10 +17,11 @@ export interface CursorRunRequest { conversationId: string; system: string[]; messages: CursorRequestMessage[]; - rawMessages?: OcxMessage[]; + rawMessages?: readonly OcxMessage[]; /** - * Images for the active user turn, inlined as SelectedImage.data under - * UserMessage.selected_context. History stays text-only. + * Images for the active user turn. Encoded as SelectedImage blobId refs under + * UserMessage.selected_context (bytes live in the request-scoped KV store for + * getBlobArgs hydration). History stays text-only. */ selectedImages?: readonly ResolvedCursorImage[]; tools?: OcxTool[]; diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 76f93773a..fecdd1dbd 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -8,6 +8,7 @@ import { ALIBABA_CODING_BASE_URL_CHOICES, ALIBABA_CODING_INTL_BASE_URL, } from "./base-url-choices"; import { + CURSOR_NO_VISION_MODELS, CURSOR_STATIC_MODELS, cursorModelContextWindows, cursorModelIds, @@ -865,6 +866,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelContextWindows: cursorModelContextWindows(CURSOR_STATIC_MODELS), modelInputModalities: cursorModelInputModalities(CURSOR_STATIC_MODELS), modelReasoningEfforts: cursorModelReasoningEfforts(CURSOR_STATIC_MODELS), + // Blind Cursor models (Auto routers, Composer, GLM-5.2) go through the vision sidecar; + // multimodal hosts (Claude/Gemini/GPT/Kimi/Grok) take native SelectedImage. Catalog still + // advertises image for noVision members so Codex can attach (sidecar option B). + noVisionModels: [...CURSOR_NO_VISION_MODELS], // Kimi K3 documents `max` as its API default, and its Cursor ladder has no `medium` // rung — so applyReasoningLevels' medium->high->first fallback would settle the catalog // default on `high`, the picker would send `high` explicitly, and the request builder's diff --git a/src/types.ts b/src/types.ts index 9de0b94b0..7288e2e60 100644 --- a/src/types.ts +++ b/src/types.ts @@ -203,13 +203,21 @@ export function resolveToolChoiceWireName(tools: readonly Pick 0 && list.includes(modelId.slice(0, colon)); + if (colon > 0 && list.includes(modelId.slice(0, colon))) return true; + for (const entry of list) { + if (entry.length > 1 && entry.endsWith("*")) { + const prefix = entry.slice(0, -1); + if (prefix.length > 0 && modelId.startsWith(prefix)) return true; + } + } + return false; } export type OcxToolChoice = diff --git a/tests/catalog-vision-sidecar-modalities.test.ts b/tests/catalog-vision-sidecar-modalities.test.ts index d3d79af63..d9a9f8d1e 100644 --- a/tests/catalog-vision-sidecar-modalities.test.ts +++ b/tests/catalog-vision-sidecar-modalities.test.ts @@ -2,8 +2,9 @@ import { afterEach, describe, expect, test } from "bun:test"; import { applyProviderConfigHints, gatherRoutedModels } from "../src/codex/catalog"; import { clearModelCache } from "../src/codex/model-cache"; import type { OcxProviderConfig } from "../src/types"; +import { modelInList } from "../src/types"; import { deriveComboCatalogModel } from "../src/codex/catalog"; -import { CURSOR_STATIC_MODELS } from "../src/adapters/cursor/discovery"; +import { CURSOR_NO_VISION_MODELS, CURSOR_STATIC_MODELS } from "../src/adapters/cursor/discovery"; import { PROVIDER_REGISTRY } from "../src/providers/registry"; import { enrichProviderFromRegistry, providerConfigSeed } from "../src/providers/derive"; import type { CatalogModel } from "../src/types"; @@ -17,6 +18,28 @@ const base: OcxProviderConfig = { noVisionModels: ["glm-5.2"], }; +const openAiSidecarFixture = { + providerName: "openai", + provider: { adapter: "openai-responses" as const, baseUrl: "https://chatgpt.test/v1", authMode: "forward" as const }, + accountMode: "direct" as const, + authContext: { kind: "main" as const, accountId: null }, + headers: new Headers({ authorization: "Bearer chatgpt" }), +}; + +function cursorImageParsed(model: string) { + return parseRequest({ + model, + input: [{ + type: "message", + role: "user", + content: [ + { type: "input_text", text: "What is in this screenshot?" }, + { type: "input_image", image_url: "data:image/png;base64,aGVsbG8=" }, + ], + }], + }); +} + describe("vision-sidecar catalog modalities", () => { test("noVisionModels models advertise image input (sidecar gives them eyes)", () => { const hinted = applyProviderConfigHints("opencode-go", base, { id: "glm-5.2", provider: "opencode-go" }); @@ -235,20 +258,36 @@ describe("vision-capable provider models feed combo modalities", () => { }); }); -describe("Cursor native vision registry", () => { - test("Cursor models advertise image through modelInputModalities, not noVisionModels", () => { +describe("Cursor native vs sidecar vision registry", () => { + test("curates noVisionModels for Auto/Composer/GLM while advertising image for all static ids", () => { const cursor = PROVIDER_REGISTRY.find(entry => entry.id === "cursor"); - expect(cursor?.noVisionModels).toBeUndefined(); - for (const model of ["auto", "composer-2.5", "gpt-5.5", "gemini-3-pro"]) { + expect(cursor?.noVisionModels).toEqual([...CURSOR_NO_VISION_MODELS]); + for (const model of ["auto", "composer-1", "composer-2.5", "composer-2.5-fast", "composer-9-future", "glm-5.2"]) { + expect(modelInList(cursor?.noVisionModels, model), `${model} should match noVision`).toBe(true); + } + for (const model of ["auto", "composer-2.5", "glm-5.2", "gpt-5.5", "gemini-3-pro", "grok-4.5", "kimi-k3"]) { expect(cursor?.modelInputModalities?.[model]).toEqual(["text", "image"]); - expect(cursor?.noVisionModels ?? []).not.toContain(model); + } + for (const model of ["gpt-5.5", "gemini-3-pro", "grok-4.5", "kimi-k3"]) { + expect(modelInList(cursor?.noVisionModels, model)).toBe(false); } for (const model of CURSOR_STATIC_MODELS) { expect(cursor?.modelInputModalities?.[model.id]).toEqual(["text", "image"]); } }); - test("Cursor catalog hints do not force sidecar image augmentation", () => { + test("Cursor catalog hints advertise image for sidecar-covered Composer", () => { + const cursor = providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!); + const hinted = applyProviderConfigHints("cursor", cursor, { + id: "composer-2.5", + provider: "cursor", + contextWindow: 200_000, + inputModalities: ["text"], + }); + expect(hinted.inputModalities).toEqual(["text", "image"]); + }); + + test("Cursor catalog hints keep native image modalities for Grok", () => { const cursor = providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!); const hinted = applyProviderConfigHints("cursor", cursor, { id: "gpt-5.5", @@ -259,7 +298,7 @@ describe("Cursor native vision registry", () => { expect(hinted.inputModalities).toEqual(["text", "image"]); }); - test("Cursor image requests skip the vision sidecar", () => { + test("Cursor Auto/Composer image requests plan the vision sidecar", () => { const config = { port: 10100, defaultProvider: "cursor", @@ -267,24 +306,24 @@ describe("Cursor native vision registry", () => { cursor: providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!), }, }; - const route = routeModel(config, "cursor/auto"); - const parsed = parseRequest({ - model: "cursor/auto", - input: [{ - type: "message", - role: "user", - content: [ - { type: "input_text", text: "What is in this screenshot?" }, - { type: "input_image", image_url: "data:image/png;base64,aGVsbG8=" }, - ], - }], - }); - expect(planVisionSidecar(config, route.provider, route.modelId, parsed, { - providerName: "openai", - provider: { adapter: "openai-responses", baseUrl: "https://chatgpt.test/v1", authMode: "forward" }, - accountMode: "direct", - authContext: { kind: "main", accountId: null }, - headers: new Headers({ authorization: "Bearer chatgpt" }), - })).toBeUndefined(); + for (const model of ["cursor/auto", "cursor/composer-2.5", "cursor/glm-5.2"] as const) { + const route = routeModel(config, model); + const plan = planVisionSidecar(config, route.provider, route.modelId, cursorImageParsed(model), openAiSidecarFixture); + expect(plan, `${model} should plan the vision sidecar`).toBeDefined(); + } + }); + + test("Cursor Grok/GPT image requests skip the vision sidecar", () => { + const config = { + port: 10100, + defaultProvider: "cursor", + providers: { + cursor: providerConfigSeed(PROVIDER_REGISTRY.find(entry => entry.id === "cursor")!), + }, + }; + for (const model of ["cursor/grok-4.5", "cursor/gpt-5.5"] as const) { + const route = routeModel(config, model); + expect(planVisionSidecar(config, route.provider, route.modelId, cursorImageParsed(model), openAiSidecarFixture)).toBeUndefined(); + } }); }); diff --git a/tests/cursor-blob.test.ts b/tests/cursor-blob.test.ts index e97689272..e7867e5a7 100644 --- a/tests/cursor-blob.test.ts +++ b/tests/cursor-blob.test.ts @@ -28,6 +28,10 @@ import { encodeCursorRunRequest, prepareCursorRunRequest, } from "../src/adapters/cursor/protobuf-request"; +import { + CURSOR_VISION_MCP_IMAGE_OMITTED, + CURSOR_VISION_PROMOTE_NUDGE, +} from "../src/adapters/cursor/images"; import { estimateTokens } from "../src/lib/token-estimate"; import { AgentClientMessageSchema, @@ -187,8 +191,11 @@ describe("Cursor blob handshake", () => { expect(run?.mcpTools?.mcpTools[0]?.toolName).toBe("mcp__fs__read_file"); }); - test("encodeCursorRunRequest attaches selectedContext with inline image data on the active user turn", () => { + test("encodeCursorRunRequest attaches selectedContext with blobId image refs on the active user turn", () => { + // Native cursor-agent converts SelectedImage.data → sha256 blobId and serves + // bytes via getBlobArgs. Inline data on the run request is ignored for vision. const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const expectedBlobId = new Uint8Array(createHash("sha256").update(imageBytes).digest()); const bytes = encodeCursorRunRequest({ modelId: "claude-4.6-opus-high", conversationId: "c1", @@ -209,9 +216,12 @@ describe("Cursor blob handshake", () => { expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("img-uuid-1"); expect(images?.[0]?.mimeType).toBe("image/png"); - expect(images?.[0]?.path).toBe(""); - expect(images?.[0]?.dataOrBlobId.case).toBe("data"); - expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + expect(images?.[0]?.path).toBe("attachment-img-uuid-1.png"); + expect(images?.[0]?.dataOrBlobId.case).toBe("blobIdWithData"); + const withData = images?.[0]?.dataOrBlobId.value as { blobId: Uint8Array; data: Uint8Array }; + expect(Array.from(withData.blobId)).toEqual(Array.from(expectedBlobId)); + expect(Array.from(withData.data)).toEqual(Array.from(imageBytes)); + expect(Array.from(blobData(expectedBlobId))).toEqual(Array.from(imageBytes)); }); test("encodeCursorRunRequest always sends empty selectedContext and mode=1 on text-only turns", () => { @@ -274,8 +284,11 @@ describe("Cursor blob handshake", () => { const images = activeSelectedImages(bytes); expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("active-img"); - expect(images?.[0]?.dataOrBlobId.case).toBe("data"); - expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(activeImageBytes)); + expect(images?.[0]?.path).toBe("attachment-active-img.png"); + expect(images?.[0]?.dataOrBlobId.case).toBe("blobIdWithData"); + const activeWithData = images?.[0]?.dataOrBlobId.value as { blobId: Uint8Array; data: Uint8Array }; + expect(Array.from(blobData(activeWithData.blobId))).toEqual(Array.from(activeImageBytes)); + expect(Array.from(activeWithData.data)).toEqual(Array.from(activeImageBytes)); }); test("encodeCursorRunRequest uses userMessageAction for image-only turns with selectedImages", () => { @@ -304,8 +317,11 @@ describe("Cursor blob handshake", () => { const images = activeSelectedImages(bytes); expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("image-only"); - expect(images?.[0]?.dataOrBlobId.case).toBe("data"); - expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + expect(images?.[0]?.path).toBe("attachment-image-only.png"); + expect(images?.[0]?.dataOrBlobId.case).toBe("blobIdWithData"); + const imageOnlyWithData = images?.[0]?.dataOrBlobId.value as { blobId: Uint8Array; data: Uint8Array }; + expect(Array.from(blobData(imageOnlyWithData.blobId))).toEqual(Array.from(imageBytes)); + expect(Array.from(imageOnlyWithData.data)).toEqual(Array.from(imageBytes)); }); test("encodeCursorRunRequest uses userMessageAction for image-only turns after assistant reply", () => { @@ -347,8 +363,11 @@ describe("Cursor blob handshake", () => { const images = activeSelectedImages(bytes); expect(images?.length).toBe(1); expect(images?.[0]?.uuid).toBe("follow-up-image"); - expect(images?.[0]?.dataOrBlobId.case).toBe("data"); - expect(Array.from(images?.[0]?.dataOrBlobId.value ?? [])).toEqual(Array.from(imageBytes)); + expect(images?.[0]?.path).toBe("attachment-follow-up-image.png"); + expect(images?.[0]?.dataOrBlobId.case).toBe("blobIdWithData"); + const followUpWithData = images?.[0]?.dataOrBlobId.value as { blobId: Uint8Array; data: Uint8Array }; + expect(Array.from(blobData(followUpWithData.blobId))).toEqual(Array.from(imageBytes)); + expect(Array.from(followUpWithData.data)).toEqual(Array.from(imageBytes)); }); test("caps external root replay while preserving system and newest history", () => { @@ -924,6 +943,208 @@ describe("Cursor blob handshake", () => { expect(step.message.case === "toolCall" && step.message.value.tool.case).toBe("mcpToolCall"); expect(run?.action?.action.case).toBe("resumeAction"); }); + + test("promotes grok-4.5 view_image images onto userMessageAction SelectedImage", () => { + resetCursorBlobStateForTests(); + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]\ncall_id: call_view\nname: view_image\nis_error: false\noutput:" }], + selectedImages: [{ uuid: "from-view", mimeType: "image/png", data: imageBytes }], + rawMessages: [ + { role: "user", content: "describe the image", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + timestamp: 2, + content: [{ type: "toolCall", id: "call_view", name: "view_image", arguments: { path: "/tmp/x.png" } }], + }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [{ type: "image", imageUrl: `data:image/png;base64,${Buffer.from(imageBytes).toString("base64")}`, detail: "auto" }], + isError: false, + timestamp: 3, + }, + ], + }); + + const msg = fromBinary(AgentClientMessageSchema, bytes); + const run = msg.message.case === "runRequest" ? msg.message.value : undefined; + expect(run?.action?.action.case).toBe("userMessageAction"); + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(1); + expect(images?.[0]?.uuid).toBe("from-view"); + expect(images?.[0]?.dataOrBlobId.case).toBe("blobIdWithData"); + expect(actionText(bytes)).toBe(CURSOR_VISION_PROMOTE_NUDGE); + + const toolCalls = conversationToolCallSteps(bytes); + expect(toolCalls.length).toBe(1); + const tool = toolCalls[0]?.message; + expect(tool?.case).toBe("toolCall"); + if (tool?.case !== "toolCall") throw new Error("expected toolCall"); + expect(tool.value.tool.case).toBe("mcpToolCall"); + if (tool.value.tool.case !== "mcpToolCall") throw new Error("expected mcpToolCall"); + const result = tool.value.tool.value.result; + expect(result?.result.case).toBe("success"); + if (result?.result.case !== "success") throw new Error("expected success"); + const content = result.result.value.content; + expect(content.every(item => item.content.case !== "image")).toBe(true); + expect(content.some(item => + item.content.case === "text" && item.content.value.text === CURSOR_VISION_MCP_IMAGE_OMITTED + )).toBe(true); + }); + + test("image-only user attach keeps empty action text", () => { + resetCursorBlobStateForTests(); + const imageBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const bytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "user", content: "" }], + selectedImages: [{ uuid: "attach", mimeType: "image/png", data: imageBytes }], + rawMessages: [ + { + role: "user", + content: [{ type: "image", imageUrl: `data:image/png;base64,${Buffer.from(imageBytes).toString("base64")}` }], + timestamp: 1, + }, + ], + }); + expect(activeSelectedImages(bytes)?.length).toBe(1); + expect(actionText(bytes)).toBe(""); + }); + + test("promotes two trailing view_image toolResults onto SelectedImage", () => { + resetCursorBlobStateForTests(); + const a = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); + const b = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 11]); + const bytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]" }], + selectedImages: [ + { uuid: "from-a", mimeType: "image/png", data: a }, + { uuid: "from-b", mimeType: "image/png", data: b }, + ], + rawMessages: [ + { role: "user", content: "describe", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + timestamp: 2, + content: [ + { type: "toolCall", id: "call_a", name: "view_image", arguments: { path: "/tmp/a.png" } }, + { type: "toolCall", id: "call_b", name: "view_image", arguments: { path: "/tmp/b.png" } }, + ], + }, + { + role: "toolResult", + toolCallId: "call_a", + toolName: "view_image", + content: [{ type: "image", imageUrl: `data:image/png;base64,${Buffer.from(a).toString("base64")}` }], + isError: false, + timestamp: 3, + }, + { + role: "toolResult", + toolCallId: "call_b", + toolName: "view_image", + content: [{ type: "image", imageUrl: `data:image/png;base64,${Buffer.from(b).toString("base64")}` }], + isError: false, + timestamp: 4, + }, + ], + }); + const images = activeSelectedImages(bytes); + expect(images?.length).toBe(2); + expect(actionText(bytes)).toBe(CURSOR_VISION_PROMOTE_NUDGE); + }); + + test("MCP omit on promote only hits call ids that were SelectedImage-promoted", () => { + resetCursorBlobStateForTests(); + // 13 trailing image toolResults; SelectedImage only carries the newest 12 uuids. + // The oldest call must keep McpImageContent (not text-omitted). + const stub = (n: number) => new Uint8Array([137, 80, 78, 71, 13, 10, 26, n & 0xff]); + const toolCalls = Array.from({ length: 13 }, (_, i) => ({ + type: "toolCall" as const, + id: `call_${i}`, + name: "view_image", + arguments: { path: `/tmp/${i}.png` }, + })); + const toolResults = Array.from({ length: 13 }, (_, i) => ({ + role: "toolResult" as const, + toolCallId: `call_${i}`, + toolName: "view_image", + content: [{ + type: "image" as const, + imageUrl: `data:image/png;base64,${Buffer.from(stub(i)).toString("base64")}`, + }], + isError: false, + timestamp: i + 3, + })); + const selectedImages = Array.from({ length: 12 }, (_, i) => ({ + uuid: `keep-${i + 1}`, + mimeType: "image/png", + data: stub(i + 1), + })); + const bytes = encodeCursorRunRequest({ + modelId: "grok-4.5", + conversationId: "c1", + system: ["You are helpful."], + messages: [{ role: "tool", content: "[tool_result]" }], + selectedImages, + rawMessages: [ + { role: "user", content: "describe", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + timestamp: 2, + content: toolCalls, + }, + ...toolResults, + ], + }); + + expect(activeSelectedImages(bytes)?.length).toBe(12); + const toolSteps = conversationToolCallSteps(bytes); + expect(toolSteps.length).toBe(13); + + const byCallId = new Map[number]>(); + for (const step of toolSteps) { + if (step.message.case !== "toolCall") continue; + if (step.message.value.tool.case !== "mcpToolCall") continue; + byCallId.set(step.message.value.tool.value.args?.toolCallId ?? "", step); + } + + const oldest = byCallId.get("call_0"); + expect(oldest).toBeDefined(); + if (oldest?.message.case !== "toolCall" || oldest.message.value.tool.case !== "mcpToolCall") { + throw new Error("expected mcp toolCall"); + } + const oldestResult = oldest.message.value.tool.value.result; + expect(oldestResult?.result.case).toBe("success"); + if (oldestResult?.result.case !== "success") throw new Error("expected success"); + expect(oldestResult.result.value.content.some(item => item.content.case === "image")).toBe(true); + + const newest = byCallId.get("call_12"); + expect(newest).toBeDefined(); + if (newest?.message.case !== "toolCall" || newest.message.value.tool.case !== "mcpToolCall") { + throw new Error("expected mcp toolCall"); + } + const newestResult = newest.message.value.tool.value.result; + expect(newestResult?.result.case).toBe("success"); + if (newestResult?.result.case !== "success") throw new Error("expected success"); + expect(newestResult.result.value.content.every(item => item.content.case !== "image")).toBe(true); + expect(newestResult.result.value.content.some(item => + item.content.case === "text" && item.content.value.text === CURSOR_VISION_MCP_IMAGE_OMITTED + )).toBe(true); + }); }); describe("Cursor AgentRunRequest.mcp_tools channel", () => { diff --git a/tests/cursor-discovery.test.ts b/tests/cursor-discovery.test.ts index c8343deda..94e1efcfb 100644 --- a/tests/cursor-discovery.test.ts +++ b/tests/cursor-discovery.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test"; import { CURSOR_AUTO_WIRE_MODEL_ID, CURSOR_DEFAULT_CONTEXT_WINDOW, + CURSOR_NO_VISION_MODELS, CURSOR_ROUTER_MODEL_IDS, CURSOR_ROUTING_LEVELS, CURSOR_STATIC_MODELS, @@ -133,6 +134,25 @@ describe("Cursor discovery metadata", () => { expect(inferCursorContextWindow("gpt-5.5")).toBe(272_000); }); + test("no-vision list is a curated subset of the static seed", () => { + const ids = new Set(cursorModelIds(CURSOR_STATIC_MODELS)); + expect([...CURSOR_NO_VISION_MODELS]).toEqual([ + ...CURSOR_ROUTER_MODEL_IDS, + "composer-*", + "glm-5.2", + ]); + for (const id of CURSOR_NO_VISION_MODELS) { + if (id.endsWith("*")) continue; + expect(ids.has(id), `${id} must be in the static Cursor seed`).toBe(true); + } + for (const id of ["composer-1", "composer-2.5", "composer-2.5-fast"]) { + expect(ids.has(id)).toBe(true); + } + for (const id of ["grok-4.5", "grok-4.5-fast", "gpt-5.5", "claude-sonnet-5", "kimi-k3", "gemini-3-pro"]) { + expect(CURSOR_NO_VISION_MODELS as readonly string[]).not.toContain(id); + } + }); + test("input modalities are cloned per model", () => { const modalities = cursorModelInputModalities([{ id: "auto" }]); diff --git a/tests/cursor-effort-suffix.test.ts b/tests/cursor-effort-suffix.test.ts index 2d4c627f0..764ba2da9 100644 --- a/tests/cursor-effort-suffix.test.ts +++ b/tests/cursor-effort-suffix.test.ts @@ -98,6 +98,13 @@ describe("Cursor per-model reasoning-effort suffix", () => { 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"); + // Connect rejects bare grok-4.5; Codex effort "none" must clamp to the lowest tier. + expect(modelIdFor("cursor/grok-4.5", "none")).toBe("cursor-grok-4.5-low"); + expect(modelIdFor("cursor/grok-4.5", "minimal")).toBe("cursor-grok-4.5-low"); + expect(selectionFor("cursor/grok-4.5", "none")).toEqual({ + modelId: "cursor-grok-4.5-low", + parameters: undefined, + }); expect(selectionFor("cursor/grok-4.5", "high")).toEqual({ modelId: "cursor-grok-4.5-high", parameters: undefined, diff --git a/tests/cursor-images.test.ts b/tests/cursor-images.test.ts index 4cee9d086..937d69a6e 100644 --- a/tests/cursor-images.test.ts +++ b/tests/cursor-images.test.ts @@ -1,11 +1,33 @@ import { afterEach, describe, expect, spyOn, test } from "bun:test"; +import { createHash } from "node:crypto"; +import { create, fromBinary } from "@bufbuild/protobuf"; import { CursorImageError, + CURSOR_VISION_IMAGE_OMITTED, + CURSOR_VISION_PROMOTE_NUDGE, + CURSOR_VISION_SOFT_MAX_BYTES, + CURSOR_VISION_SOFT_MAX_BYTES_HIGH, MAX_CURSOR_IMAGE_BYTES, + MAX_CURSOR_IMAGE_DECODE_BYTES, MAX_CURSOR_IMAGES, + buildSelectedImages, + extractTrailingToolResultImageParts, + extractTrailingToolResultImagePromotion, + prepareCursorImageForWire, + prepareCursorRawMessages, resolveActiveCursorImages, resolveCursorImages, + sniffCursorImageDimensions, } from "../src/adapters/cursor/images"; +import { + handleCursorNativeKv, + resetCursorBlobStateForTests, +} from "../src/adapters/cursor/native-exec"; +import { + AgentClientMessageSchema, + GetBlobArgsSchema, + KvServerMessageSchema, +} from "../src/adapters/cursor/gen/agent_pb"; import * as destinationPolicy from "../src/lib/destination-policy"; import * as imageArtifacts from "../src/images/artifacts"; @@ -40,6 +62,12 @@ function mockPublicHttpsFetch(body = PNG_BYTES, mimeType = "image/png") { })); } +async function oversizedDecodablePng(): Promise { + const pngPath = new URL("./helpers/cursor-grumpy-fixture.png", import.meta.url); + const src = new Uint8Array(await Bun.file(pngPath).arrayBuffer()); + return new Uint8Array(await new Bun.Image(src).resize(2400, 2400).png().bytes()); +} + describe("Cursor image resolver", () => { test("rejects more than MAX_CURSOR_IMAGES in one request", async () => { const urls = Array.from({ length: MAX_CURSOR_IMAGES + 1 }, () => PNG_DATA_URL); @@ -49,14 +77,31 @@ describe("Cursor image resolver", () => { }); }); - test("rejects data URLs above the 1 MiB cap", async () => { - const oversized = "A".repeat(Math.ceil((MAX_CURSOR_IMAGE_BYTES + 1) * 4 / 3)); + test("rejects data URLs above the inbound decode bomb ceiling", async () => { + const oversized = "A".repeat(Math.ceil((MAX_CURSOR_IMAGE_DECODE_BYTES + 1) * 4 / 3)); await expect(resolveCursorImages([`data:image/png;base64,${oversized}`])).rejects.toMatchObject({ name: "CursorImageError", - message: "Image input is too large (max 1 MiB). Resize and retry.", + message: "Image input is too large to process safely.", }); }); + test("prep-before-cap accepts PNG over 1 MiB that JPEG-encodes under the wire cap", async () => { + const png = await oversizedDecodablePng(); + expect(png.byteLength).toBeGreaterThan(MAX_CURSOR_IMAGE_BYTES); + const url = `data:image/png;base64,${Buffer.from(png).toString("base64")}`; + const resolved = await resolveCursorImages([url]); + expect(resolved).toHaveLength(1); + expect(resolved[0]?.mimeType).toBe("image/jpeg"); + expect(resolved[0]!.data.byteLength).toBeLessThan(png.byteLength); + expect(resolved[0]!.data.byteLength).toBeLessThanOrEqual(MAX_CURSOR_IMAGE_BYTES); + }); + + test("omits undecodable payloads under the decode ceiling instead of sending them", async () => { + const junk = "A".repeat(Math.ceil((MAX_CURSOR_IMAGE_BYTES + 1) * 4 / 3)); + const resolved = await resolveCursorImages([`data:image/png;base64,${junk}`]); + expect(resolved).toEqual([]); + }); + test("decodes valid base64 data URLs", async () => { const resolved = await resolveCursorImages([PNG_DATA_URL]); expect(resolved).toHaveLength(1); @@ -163,7 +208,7 @@ describe("Cursor image resolver", () => { expect(resolved).toHaveLength(1); }); - test("resolveActiveCursorImages returns empty for trailing toolResult continuations", async () => { + test("resolveActiveCursorImages returns empty for text-only trailing toolResult", async () => { const resolved = await resolveActiveCursorImages([ { role: "user", @@ -182,9 +227,244 @@ describe("Cursor image resolver", () => { expect(resolved).toEqual([]); }); + test("resolveActiveCursorImages promotes trailing view_image toolResult images", async () => { + const resolved = await resolveActiveCursorImages([ + { role: "user", content: "describe", timestamp: 1 }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [{ type: "image", imageUrl: PNG_DATA_URL, detail: "auto" }], + isError: false, + timestamp: 2, + }, + ]); + expect(resolved).toHaveLength(1); + expect(resolved[0]?.data.byteLength).toBeGreaterThan(0); + }); + + test("resolveActiveCursorImages collects consecutive trailing image toolResults", async () => { + const second = `data:image/png;base64,${Buffer.from([...PNG_BYTES, 1]).toString("base64")}`; + const resolved = await resolveActiveCursorImages([ + { role: "user", content: "describe both", timestamp: 1 }, + { + role: "toolResult", + toolCallId: "call_a", + toolName: "view_image", + content: [{ type: "image", imageUrl: PNG_DATA_URL }], + isError: false, + timestamp: 2, + }, + { + role: "toolResult", + toolCallId: "call_b", + toolName: "view_image", + content: [{ type: "image", imageUrl: second }], + isError: false, + timestamp: 3, + }, + ]); + expect(resolved).toHaveLength(2); + }); + + test("extractTrailingToolResultImageParts keeps the newest MAX_CURSOR_IMAGES", () => { + const urls = Array.from({ length: MAX_CURSOR_IMAGES + 2 }, (_, i) => ( + `data:image/png;base64,${Buffer.from([...PNG_BYTES, i]).toString("base64")}` + )); + const messages = urls.map((imageUrl, i) => ({ + role: "toolResult" as const, + toolCallId: `call_${i}`, + toolName: "view_image", + content: [{ type: "image" as const, imageUrl }], + isError: false, + timestamp: i + 1, + })); + const { parts, omittedOlder } = extractTrailingToolResultImageParts(messages); + expect(omittedOlder).toBe(2); + expect(parts).toHaveLength(MAX_CURSOR_IMAGES); + expect(parts[0]?.imageUrl).toBe(urls[2]); + expect(parts.at(-1)?.imageUrl).toBe(urls.at(-1)); + }); + test("CursorImageError carries HTTP status for callers", () => { const error = new CursorImageError("blocked", 403); expect(error.status).toBe(403); expect(error.name).toBe("CursorImageError"); }); + + test("buildSelectedImages uses blobIdWithData + attachment path and keeps KV hydrated", () => { + resetCursorBlobStateForTests(); + // Minimal PNG signature + IHDR claiming 2x3 (not Bun-decodable — stays PNG) + const png = Uint8Array.from([ + 137, 80, 78, 71, 13, 10, 26, 10, + 0, 0, 0, 13, 73, 72, 68, 82, + 0, 0, 0, 2, 0, 0, 0, 3, + 8, 2, 0, 0, 0, 0, 0, 0, 0, + ]); + expect(sniffCursorImageDimensions(png)).toEqual({ width: 2, height: 3 }); + + const [selected] = buildSelectedImages([{ + data: png, + mimeType: "image/png", + uuid: "u-dim", + }]); + expect(selected?.dataOrBlobId.case).toBe("blobIdWithData"); + expect(selected?.path).toBe("attachment-u-dim.png"); + expect(selected?.dimension?.width).toBe(2); + expect(selected?.dimension?.height).toBe(3); + const withData = selected!.dataOrBlobId.value as { blobId: Uint8Array; data: Uint8Array }; + expect(Array.from(withData.blobId)).toEqual(Array.from(createHash("sha256").update(png).digest())); + expect(Array.from(withData.data)).toEqual(Array.from(png)); + + const reply = fromBinary(AgentClientMessageSchema, handleCursorNativeKv(create(KvServerMessageSchema, { + id: 1, + message: { case: "getBlobArgs", value: create(GetBlobArgsSchema, { blobId: withData.blobId }) }, + }))); + const kv = reply.message.case === "kvClientMessage" ? reply.message.value : undefined; + const data = kv?.message.case === "getBlobResult" ? kv.message.value.blobData : undefined; + expect(Array.from(data ?? [])).toEqual(Array.from(png)); + }); + + test("prepareCursorImageForWire re-encodes large PNG as JPEG under the soft cap", async () => { + const pngPath = new URL("./helpers/cursor-grumpy-fixture.png", import.meta.url); + const png = new Uint8Array(await Bun.file(pngPath).arrayBuffer()); + expect(png.byteLength).toBeGreaterThan(CURSOR_VISION_SOFT_MAX_BYTES); + + const prepared = await prepareCursorImageForWire({ + data: png, + mimeType: "image/png", + uuid: "big-png", + }); + expect(prepared.status).toBe("ready"); + if (prepared.status !== "ready") throw new Error("expected ready"); + expect(prepared.image.mimeType).toBe("image/jpeg"); + expect(prepared.image.data.byteLength).toBeLessThan(png.byteLength); + expect(prepared.image.data.byteLength).toBeLessThanOrEqual(CURSOR_VISION_SOFT_MAX_BYTES); + expect(prepared.image.data[0]).toBe(0xff); + expect(prepared.image.data[1]).toBe(0xd8); + }); + + test("detail original/high uses a higher soft tier than auto", async () => { + const pngPath = new URL("./helpers/cursor-grumpy-fixture.png", import.meta.url); + const png = new Uint8Array(await Bun.file(pngPath).arrayBuffer()); + const auto = await prepareCursorImageForWire({ + data: png, + mimeType: "image/png", + uuid: "auto", + detail: "auto", + }); + const original = await prepareCursorImageForWire({ + data: png, + mimeType: "image/png", + uuid: "original", + detail: "original", + }); + expect(auto.status).toBe("ready"); + expect(original.status).toBe("ready"); + if (auto.status !== "ready" || original.status !== "ready") throw new Error("expected ready"); + expect(original.image.data.byteLength).toBeGreaterThan(auto.image.data.byteLength); + expect(auto.image.data.byteLength).toBeLessThanOrEqual(CURSOR_VISION_SOFT_MAX_BYTES); + expect(original.image.data.byteLength).toBeLessThanOrEqual(CURSOR_VISION_SOFT_MAX_BYTES_HIGH); + expect(original.image.data.byteLength).toBeLessThanOrEqual(MAX_CURSOR_IMAGE_BYTES); + }); + + test("exotic MIME and corrupt PNG fail closed", async () => { + const bmp = await prepareCursorImageForWire({ + data: new Uint8Array([0x42, 0x4d, 0, 0, 0, 0]), + mimeType: "image/bmp", + uuid: "bmp", + }); + expect(bmp).toEqual({ status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }); + + const corrupt = await prepareCursorImageForWire({ + data: new Uint8Array(128).fill(0x41), + mimeType: "image/png", + uuid: "corrupt", + }); + expect(corrupt).toEqual({ status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }); + + // Soft-cap-sized labeled JPEG must still decode; junk under the soft max is omitted. + const fakeJpeg = await prepareCursorImageForWire({ + data: new Uint8Array(128).fill(0xff), + mimeType: "image/jpeg", + uuid: "fake-jpeg", + }); + expect(fakeJpeg).toEqual({ status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED }); + }); + + test("extractTrailingToolResultImagePromotion scopes call ids to kept parts", () => { + const urls = Array.from({ length: MAX_CURSOR_IMAGES + 2 }, (_, i) => ( + `data:image/png;base64,${Buffer.from([...PNG_BYTES, i]).toString("base64")}` + )); + const messages = urls.map((imageUrl, i) => ({ + role: "toolResult" as const, + toolCallId: `call_${i}`, + toolName: "view_image", + content: [{ type: "image" as const, imageUrl }], + isError: false, + timestamp: i + 1, + })); + const { parts, omittedOlder, promotedCallIds } = extractTrailingToolResultImagePromotion(messages); + expect(omittedOlder).toBe(2); + expect(parts).toHaveLength(MAX_CURSOR_IMAGES); + expect(promotedCallIds.has("call_0")).toBe(false); + expect(promotedCallIds.has("call_1")).toBe(false); + expect(promotedCallIds.has("call_2")).toBe(true); + expect(promotedCallIds.has(`call_${MAX_CURSOR_IMAGES + 1}`)).toBe(true); + }); + + test("promote nudge warns against path/filename inference", () => { + expect(CURSOR_VISION_PROMOTE_NUDGE).toContain("Describe the image from the tool result."); + expect(CURSOR_VISION_PROMOTE_NUDGE).toContain("Do not infer content from file paths or names."); + }); + + test("prepareCursorRawMessages JPEG-preps view_image tool-result data URLs", async () => { + const pngPath = new URL("./helpers/cursor-grumpy-fixture.png", import.meta.url); + const png = new Uint8Array(await Bun.file(pngPath).arrayBuffer()); + const imageUrl = `data:image/png;base64,${Buffer.from(png).toString("base64")}`; + const prepared = await prepareCursorRawMessages([ + { role: "user", content: "describe", timestamp: 1 }, + { + role: "assistant", + model: "cursor/grok-4.5", + content: [{ type: "toolCall", id: "call_view", name: "view_image", arguments: { path: "/tmp/x.png" } }], + timestamp: 2, + }, + { + role: "toolResult", + toolCallId: "call_view", + toolName: "view_image", + content: [{ type: "image", imageUrl, detail: "auto" }], + isError: false, + timestamp: 3, + }, + ]); + const tool = prepared?.[2]; + expect(tool?.role).toBe("toolResult"); + if (tool?.role !== "toolResult" || typeof tool.content === "string") throw new Error("expected image parts"); + const part = tool.content.find(item => item.type === "image"); + expect(part?.type).toBe("image"); + if (part?.type !== "image") throw new Error("expected image"); + expect(part.imageUrl.startsWith("data:image/jpeg;base64,")).toBe(true); + const payload = part.imageUrl.slice(part.imageUrl.indexOf(",") + 1); + const bytes = Buffer.from(payload, "base64"); + expect(bytes.byteLength).toBeLessThanOrEqual(CURSOR_VISION_SOFT_MAX_BYTES); + expect(bytes[0]).toBe(0xff); + expect(bytes[1]).toBe(0xd8); + }); + + test("prepareCursorRawMessages replaces exotic images with omission text", async () => { + const bmpUrl = `data:image/bmp;base64,${Buffer.from([0x42, 0x4d, 0, 0]).toString("base64")}`; + const prepared = await prepareCursorRawMessages([ + { + role: "user", + content: [{ type: "image", imageUrl: bmpUrl }], + timestamp: 1, + }, + ]); + const user = prepared?.[0]; + expect(user?.role).toBe("user"); + if (user?.role !== "user" || typeof user.content === "string") throw new Error("expected parts"); + expect(user.content).toEqual([{ type: "text", text: CURSOR_VISION_IMAGE_OMITTED }]); + }); }); diff --git a/tests/cursor-static-catalog.test.ts b/tests/cursor-static-catalog.test.ts index d085f13aa..fa8be304f 100644 --- a/tests/cursor-static-catalog.test.ts +++ b/tests/cursor-static-catalog.test.ts @@ -115,7 +115,7 @@ describe("Cursor static Codex catalog", () => { for (const modelId of ["auto", "composer-2.5", "gpt-5.5", "gemini-3-pro"]) { expect( entries.find(item => item.slug === `cursor/${modelId}`)?.input_modalities, - `cursor/${modelId} should advertise native image input`, + `cursor/${modelId} should advertise image input`, ).toEqual(["text", "image"]); } }); diff --git a/tests/cursor-vision-wire-harness.test.ts b/tests/cursor-vision-wire-harness.test.ts index 3fa8223d8..380ab210d 100644 --- a/tests/cursor-vision-wire-harness.test.ts +++ b/tests/cursor-vision-wire-harness.test.ts @@ -28,8 +28,11 @@ function activeSelectedImageBytes(bytes: Uint8Array): Uint8Array | undefined { const image = action?.case === "userMessageAction" ? action.value.userMessage?.selectedContext?.selectedImages[0] : undefined; - if (!image || image.dataOrBlobId.case !== "data") return undefined; - return image.dataOrBlobId.value; + if (!image) return undefined; + if (image.dataOrBlobId.case === "data") return image.dataOrBlobId.value; + if (image.dataOrBlobId.case === "blobId") return blobData(image.dataOrBlobId.value); + if (image.dataOrBlobId.case === "blobIdWithData") return image.dataOrBlobId.value.data; + return undefined; } function viewImageToolResultBytes(bytes: Uint8Array): Uint8Array | undefined { diff --git a/tests/helpers/cursor-grumpy-fixture.png b/tests/helpers/cursor-grumpy-fixture.png new file mode 100644 index 0000000000000000000000000000000000000000..a06cdc84a2491b7289866dd884f5f6fa1034f8c7 GIT binary patch literal 440734 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

hvddan4-@|<@; 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%0DWZrohfWhFLCA2Z6rPx0H~v(h6!B} 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