diff --git a/src/adapters/cursor/discovery.ts b/src/adapters/cursor/discovery.ts
index 718323b6a2..15f529fdb0 100644
--- a/src/adapters/cursor/discovery.ts
+++ b/src/adapters/cursor/discovery.ts
@@ -154,6 +154,25 @@ export function cursorCodexToWireModelId(modelId: string): string {
return cursorWireModelSelection(modelId).modelId;
}
+/**
+ * Synthetic ultra/big-context picker marker (devlog 260826 070). A `cursor/-1m` row is a
+ * picker-only variant: the wire request keeps `` (plus effort suffix) and turns on Cursor
+ * Max Mode instead. Only ids listed here are treated as synthetic — a real upstream wire id that
+ * happens to end in `-1m` never collides because it will not be in this set.
+ */
+export const CURSOR_ULTRA_1M_MODEL_IDS: ReadonlySet = new Set([
+ "kimi-k3-1m",
+]);
+
+const CURSOR_ULTRA_1M_SUFFIX = "-1m";
+
+/** Resolve a synthetic ultra marker id to its wire base, or undefined for ordinary ids. */
+export function cursorUltraBaseModelId(modelId: string): string | undefined {
+ const normalized = modelId.startsWith("cursor/") ? modelId.slice("cursor/".length) : modelId;
+ if (!CURSOR_ULTRA_1M_MODEL_IDS.has(normalized)) return undefined;
+ return normalized.slice(0, -CURSOR_ULTRA_1M_SUFFIX.length);
+}
+
/**
* Cursor-native wire models keep server-side conversation state reliably.
* External models (gpt/claude/gemini/grok families and similar) are more brittle on resumeAction.
@@ -215,7 +234,11 @@ export function filterCursorConfiguredModelsByLiveDiscovery
!CURSOR_KNOWN_UNCALLABLE_MODEL_IDS.has(model.id)
- && (isCursorRouterModelId(model.id) || isCursorModelAvailableForAccount(model.id, liveIds)),
+ && (
+ isCursorRouterModelId(model.id)
+ // Synthetic ultra rows ride their base model's account availability.
+ || isCursorModelAvailableForAccount(cursorUltraBaseModelId(model.id) ?? model.id, liveIds)
+ ),
);
}
@@ -322,6 +345,10 @@ export const CURSOR_STATIC_MODELS: readonly CursorModelInfo[] = normalizeCursorM
// kimi-k3: cursor.com/docs/models/kimi-k3; account-verified via GetUsableModels (2026-07-28) —
// ships only as effort-suffixed kimi-k3-{low,high,max}, so the tier picker is exposed.
{ id: "kimi-k3", contextWindow: CONTEXT_262K, supportsReasoningEffort: true },
+ // kimi-k3-1m: synthetic ultra/Max-Mode picker variant (CURSOR_ULTRA_1M_MODEL_IDS) — wire sends
+ // kimi-k3- with maxMode=true; 1M context user-verified live on the Ultra plan
+ // (devlog 260826_cursor_responses_gap/025). inferCursorContextWindow maps "1m" ids to 1M.
+ { id: "kimi-k3-1m", contextWindow: CONTEXT_1M, supportsReasoningEffort: true },
{ id: "grok-4.5", contextWindow: 500_000, supportsReasoningEffort: true },
{ id: "grok-4.5-fast", contextWindow: 500_000, supportsReasoningEffort: true },
diff --git a/src/adapters/cursor/effort-map.ts b/src/adapters/cursor/effort-map.ts
index 6bc0270f74..29525b4190 100644
--- a/src/adapters/cursor/effort-map.ts
+++ b/src/adapters/cursor/effort-map.ts
@@ -58,6 +58,9 @@ const CURSOR_MODEL_EFFORT_TIERS: Record = {
// GetUsableModels (2026-07-28) lists kimi-k3 only as effort-suffixed kimi-k3-{low,high,max};
// the bare id returns not_found. Tiers mirror the native Kimi provider's K3 ladder.
"kimi-k3": ["low", "high", "max"],
+ // Synthetic ultra picker variant (devlog 260826 070): same tier ladder as kimi-k3; the -1m
+ // marker is stripped before wire-id composition, so these tiers never form a wire suffix.
+ "kimi-k3-1m": ["low", "high", "max"],
// Cursor renamed the Grok 4.5 slugs to cursor-grok-4.5-{low,medium,high} and
// cursor-grok-4.5-{low,medium,high}-fast. The bare Fast id returns not_found.
"grok-4.5": ["low", "medium", "high"],
diff --git a/src/adapters/cursor/live-models.ts b/src/adapters/cursor/live-models.ts
index 32bafe1517..4e4c07a236 100644
--- a/src/adapters/cursor/live-models.ts
+++ b/src/adapters/cursor/live-models.ts
@@ -43,7 +43,7 @@ export interface CursorUsableModelsOptions {
}
export type CursorUsableModelsResult =
- | { ok: true; models: string[] }
+ | { ok: true; models: string[]; maxModeModels?: string[] }
| { ok: false; error: "auth" | "http" | "policy" | "transport" | "timeout" | "decode" | "empty" | "too_large"; detail?: string };
/** Test-only seam for management connectivity probes; production callers retain the HTTP/2 path. */
@@ -120,6 +120,7 @@ function decodeCursorUsableModels(bytes: Uint8Array): CursorUsableModelsResult {
// make stale configured ids such as `composer-2` look activated.
const ids: string[] = [];
const seenIds = new Set();
+ const maxModeIds: string[] = [];
for (const model of response.models ?? []) {
const rawId = (model as { modelId?: string }).modelId;
if (typeof rawId !== "string") continue;
@@ -127,9 +128,13 @@ function decodeCursorUsableModels(bytes: Uint8Array): CursorUsableModelsResult {
if (!isValidModelDiscoveryModelId(id) || seenIds.has(id)) continue;
seenIds.add(id);
ids.push(id);
+ // Preserve Max-Mode capability for ultra/big-context auto-detection (devlog 260826 070).
+ if ((model as { maxMode?: boolean }).maxMode === true) maxModeIds.push(id);
if (ids.length >= CURSOR_MAX_DISCOVERED_MODELS) break;
}
- return ids.length > 0 ? { ok: true, models: ids } : { ok: false, error: "empty" };
+ return ids.length > 0
+ ? { ok: true, models: ids, ...(maxModeIds.length > 0 ? { maxModeModels: maxModeIds } : {}) }
+ : { ok: false, error: "empty" };
} catch {
return { ok: false, error: "decode", detail: "Invalid GetUsableModels protobuf response" };
}
diff --git a/src/adapters/cursor/protobuf-request.ts b/src/adapters/cursor/protobuf-request.ts
index c274cd9d74..5b4e07b426 100644
--- a/src/adapters/cursor/protobuf-request.ts
+++ b/src/adapters/cursor/protobuf-request.ts
@@ -967,12 +967,15 @@ function buildPreparedCursorRunRequest(
displayName: request.modelId,
displayNameShort: request.modelId,
aliases: [],
+ ...(request.maxMode === true ? { maxMode: true } : {}),
}),
} : {}),
- ...(requestedModelParameters.length > 0 ? {
+ ...(requestedModelParameters.length > 0 || request.maxMode === true ? {
requestedModel: create(RequestedModelSchema, {
modelId: request.modelId,
- maxMode: false,
+ // Max Mode must be raised on BOTH RequestedModel and ModelDetails; missing either
+ // can invalid_argument upstream (devlog 260826 070).
+ maxMode: request.maxMode === true,
parameters: requestedModelParameters.map(parameter =>
create(RequestedModel_ModelParameterbytesSchema, parameter)),
}),
diff --git a/src/adapters/cursor/request-builder.ts b/src/adapters/cursor/request-builder.ts
index 542386b8c5..e99791134a 100644
--- a/src/adapters/cursor/request-builder.ts
+++ b/src/adapters/cursor/request-builder.ts
@@ -10,6 +10,7 @@ import type {
import { isAllowedToolChoice, namespacedToolName, toolChoiceAliases, type OcxTool, type OcxToolChoice } from "../../types";
import type { CursorRequestMessage, CursorRequestedModelParameter, CursorRunRequest } from "./types";
import { cursorCheckpointModelAffinityId, cursorWireModelSelection, type CursorRoutingLevel } from "./discovery";
+import { cursorUltraBaseModelId } from "./discovery";
import { decodeCursorCallId } from "./call-id";
import { cursorEffortSuffix, cursorRequestWireModelIdWithEffort } from "./effort-map";
import {
@@ -189,13 +190,19 @@ function normalizeCursorModelId(modelId: string, reasoning?: string): {
modelId: string;
requestedModelParameters?: readonly CursorRequestedModelParameter[];
routingLevel?: CursorRoutingLevel;
+ maxMode?: boolean;
} {
- const selection = cursorWireModelSelection(modelId);
+ // Synthetic ultra (-1m) picker rows resolve to their wire base with Max Mode on
+ // (devlog 260826 070); the marker never reaches the wire.
+ const ultraBase = cursorUltraBaseModelId(modelId);
+ const selection = cursorWireModelSelection(ultraBase ?? modelId);
+ const maxMode = ultraBase !== undefined ? { maxMode: true } : {};
const id = selection.modelId;
const suffix = cursorEffortSuffix(id, reasoning);
if ((id === "grok-4.5-fast" || id === "grok-4.6-fast") && suffix) {
return {
...selection,
+ ...maxMode,
modelId: id.slice(0, -"-fast".length),
requestedModelParameters: [
{ id: "effort", value: suffix },
@@ -203,7 +210,7 @@ function normalizeCursorModelId(modelId: string, reasoning?: string): {
],
};
}
- return { ...selection, modelId: suffix ? cursorRequestWireModelIdWithEffort(id, suffix) : id };
+ return { ...selection, ...maxMode, modelId: suffix ? cursorRequestWireModelIdWithEffort(id, suffix) : id };
}
function contentPartToText(part: OcxContentPart | OcxAssistantContentPart): string | undefined {
@@ -446,6 +453,7 @@ export function createCursorRequest(
modelId: model.modelId,
...(model.requestedModelParameters ? { requestedModelParameters: model.requestedModelParameters } : {}),
...(model.routingLevel ? { routingLevel: model.routingLevel } : {}),
+ ...(model.maxMode ? { maxMode: true } : {}),
conversationId: resolveCursorConversationId(parsed, model.modelId, options),
system: [...(parsed.context.systemPrompt ?? []), ...(limitNote ? [limitNote] : [])],
messages,
diff --git a/src/adapters/cursor/types.ts b/src/adapters/cursor/types.ts
index 94112b4697..1c642e9b78 100644
--- a/src/adapters/cursor/types.ts
+++ b/src/adapters/cursor/types.ts
@@ -15,6 +15,12 @@ export interface CursorRunRequest {
requestedModelParameters?: readonly CursorRequestedModelParameter[];
/** Cursor Router optimization parameter; valid only while modelId is the `default` wire model. */
routingLevel?: CursorRoutingLevel;
+ /**
+ * Cursor Max Mode (ultra/big-context). Set from a synthetic `-1m` picker variant; the wire
+ * keeps the original model id and raises RequestedModel.maxMode + ModelDetails.maxMode
+ * (both fields — missing either can invalid_argument upstream). Devlog 260826 070.
+ */
+ maxMode?: boolean;
/**
* Bare API callers (no caller tools, no Codex thread identity) pay a ~10-15K input-token
* preamble because an absent AgentRunRequest.mcp_tools field makes Cursor inject its default
diff --git a/tests/cursor-ultra-mode.test.ts b/tests/cursor-ultra-mode.test.ts
new file mode 100644
index 0000000000..a6cbf5bb3a
--- /dev/null
+++ b/tests/cursor-ultra-mode.test.ts
@@ -0,0 +1,94 @@
+import { describe, expect, test } from "bun:test";
+import {
+ CURSOR_STATIC_MODELS,
+ CURSOR_ULTRA_1M_MODEL_IDS,
+ cursorUltraBaseModelId,
+ filterCursorConfiguredModelsByLiveDiscovery,
+} from "../src/adapters/cursor/discovery";
+import { createCursorRequest } from "../src/adapters/cursor/request-builder";
+import { encodeCursorRunRequest } from "../src/adapters/cursor/protobuf-request";
+import { fromBinary } from "@bufbuild/protobuf";
+import { AgentClientMessageSchema, type AgentRunRequest } from "../src/adapters/cursor/gen/agent_pb";
+import type { OcxParsedRequest } from "../src/types/request";
+
+function decodeRunRequest(bytes: Uint8Array): AgentRunRequest {
+ const msg = fromBinary(AgentClientMessageSchema, bytes);
+ if (msg.message.case !== "runRequest") throw new Error("expected runRequest");
+ return msg.message.value;
+}
+
+function parsedFor(modelId: string, reasoning?: string): OcxParsedRequest {
+ return {
+ modelId,
+ context: { systemPrompt: [], messages: [{ role: "user", content: "hi" }] },
+ options: reasoning ? { reasoning } : {},
+ } as OcxParsedRequest;
+}
+
+describe("cursor ultra (-1m / Max Mode) toggle (devlog 260826 070)", () => {
+ test("static catalog exposes the kimi-k3-1m picker row with 1M context", () => {
+ const row = CURSOR_STATIC_MODELS.find(model => model.id === "kimi-k3-1m");
+ expect(row).toBeDefined();
+ expect(row?.contextWindow).toBe(1_000_000);
+ expect(row?.supportsReasoningEffort).toBe(true);
+ });
+
+ test("ultra marker resolves to its wire base and never leaks", () => {
+ expect(cursorUltraBaseModelId("cursor/kimi-k3-1m")).toBe("kimi-k3");
+ expect(cursorUltraBaseModelId("kimi-k3-1m")).toBe("kimi-k3");
+ expect(cursorUltraBaseModelId("kimi-k3")).toBeUndefined();
+ expect(cursorUltraBaseModelId("claude-4-sonnet-1m")).toBeUndefined();
+ });
+
+ test("kimi-k3-1m + max resolves to wire kimi-k3-max with maxMode on the request", () => {
+ const request = createCursorRequest(parsedFor("cursor/kimi-k3-1m", "max"));
+ expect(request.modelId).toBe("kimi-k3-max");
+ expect(request.maxMode).toBe(true);
+ });
+
+ test("plain kimi-k3 stays maxMode-off", () => {
+ const request = createCursorRequest(parsedFor("cursor/kimi-k3", "max"));
+ expect(request.modelId).toBe("kimi-k3-max");
+ expect(request.maxMode).toBeUndefined();
+ });
+
+ test("wire raises maxMode on BOTH RequestedModel and ModelDetails", () => {
+ const bytes = encodeCursorRunRequest({
+ modelId: "kimi-k3-max",
+ maxMode: true,
+ conversationId: "c1",
+ system: [],
+ messages: [{ role: "user", content: "hi" }],
+ });
+ const decoded = decodeRunRequest(bytes);
+ expect(decoded.requestedModel?.maxMode).toBe(true);
+ expect(decoded.requestedModel?.modelId).toBe("kimi-k3-max");
+ expect(decoded.modelDetails?.maxMode).toBe(true);
+ });
+
+ test("non-ultra requests keep maxMode=false wire behavior", () => {
+ const bytes = encodeCursorRunRequest({
+ modelId: "kimi-k3-max",
+ conversationId: "c1",
+ system: [],
+ messages: [{ role: "user", content: "hi" }],
+ });
+ const decoded = decodeRunRequest(bytes);
+ expect(decoded.requestedModel).toBeUndefined();
+ // ModelDetails.maxMode is proto-optional; absent (undefined) means off.
+ expect(decoded.modelDetails?.maxMode ?? false).toBe(false);
+ });
+
+ test("account filter admits the synthetic row through its base availability", () => {
+ const configured = [{ id: "kimi-k3-1m" }, { id: "kimi-k3" }];
+ const live = ["kimi-k3-high", "kimi-k3-max"];
+ const filtered = filterCursorConfiguredModelsByLiveDiscovery(configured, live);
+ expect(filtered.map(model => model.id)).toEqual(["kimi-k3-1m", "kimi-k3"]);
+ });
+
+ test("ultra id set stays narrow and every entry has a static row", () => {
+ for (const id of CURSOR_ULTRA_1M_MODEL_IDS) {
+ expect(CURSOR_STATIC_MODELS.some(model => model.id === id)).toBe(true);
+ }
+ });
+});