From d8c99735e16b638b55104396db0a1c8ea813d85c Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 Jul 2026 02:56:10 -0600 Subject: [PATCH 1/4] feat(cursor): expose 1M context windows in model discovery GetUsableModels carries no context-window field, so discovered 1M models were pinned to the 200k default and OMP compacted long before the real ceiling. Recover the 1M window from the signals Cursor does send: "1M" display-name labels across families, natively 1M families served unlabeled (Kimi K3, GLM 5.2+), and the max-mode flag on Claude/Gemini ids. Bump the Cursor model-cache namespace so windows cached before this fix are refetched. Fixes #4798 --- packages/ai/test/cursor-exec-handlers.test.ts | 34 +++++++ packages/catalog/CHANGELOG.md | 4 + packages/catalog/src/discovery/cursor.ts | 35 +++++++- .../src/provider-models/cache-provider-id.ts | 4 +- .../catalog/test/cursor-discovery.test.ts | 89 +++++++++++++++++++ 5 files changed, 164 insertions(+), 2 deletions(-) diff --git a/packages/ai/test/cursor-exec-handlers.test.ts b/packages/ai/test/cursor-exec-handlers.test.ts index bfdc4fad3..9537183b0 100644 --- a/packages/ai/test/cursor-exec-handlers.test.ts +++ b/packages/ai/test/cursor-exec-handlers.test.ts @@ -458,6 +458,40 @@ describe("Cursor request action encoding", () => { expect(payload.requestedModel?.maxMode).toBe(true); }); + it("sends max-mode metadata with prior history when switching providers mid-conversation", async () => { + const payload = await captureCursorPayload( + { + messages: [ + { role: "user", content: "Summarize this repo.", timestamp: 0 }, + { + role: "assistant", + api: "anthropic-messages", + provider: "anthropic", + model: "claude-sonnet-4.5", + content: [{ type: "text", text: "It is a monorepo." }], + usage: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 0, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }, + stopReason: "stop", + timestamp: 1, + }, + { role: "user", content: "continue", timestamp: 2 }, + ], + }, + cursorMaxModeModel, + ); + + expect(payload.modelDetails?.maxMode).toBe(true); + expect(payload.requestedModel?.maxMode).toBe(true); + // History from the other provider is carried into the fresh Cursor conversation. + expect(payload.conversationState?.turns.length).toBeGreaterThan(0); + }); + it("uses a resume action when a tool result is the final context message", async () => { const payload = await captureCursorPayload(toolResultContext()); diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 2627d6c20..409b8fd8a 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Cursor 1M-context model discovery to expose the 1M-token context window for models Cursor labels with a "1M" display name (Claude and GPT families), natively 1M families Cursor serves unlabeled (Kimi K3, GLM 5.2+), and max-mode Claude/Gemini variants, instead of pinning every discovered model to the 200k default. Bumped the Cursor model-cache namespace so windows cached before this fix are refetched. ([#4798](https://github.com/can1357/oh-my-pi/issues/4798)) + ## [17.2.0] - 2026-07-30 ### Added diff --git a/packages/catalog/src/discovery/cursor.ts b/packages/catalog/src/discovery/cursor.ts index 7f4c1efc6..6146b8ebd 100644 --- a/packages/catalog/src/discovery/cursor.ts +++ b/packages/catalog/src/discovery/cursor.ts @@ -13,6 +13,18 @@ const CURSOR_GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels"; const DEFAULT_CONTEXT_WINDOW = 200_000; const DEFAULT_MAX_TOKENS = 64_000; +/** + * `GetUsableModels` carries no context-window field, so the 1M ceiling is + * recovered from the signals Cursor does send: + * - display-name labels ("Opus 5 1M", "GPT-5.5 1M High") across families, + * - natively 1M families Cursor serves unlabeled (Kimi K3, GLM 5.2+), + * - the max-mode flag on Claude/Gemini ids, whose max-mode ceiling is 1M. + */ +const CURSOR_1M_CONTEXT_WINDOW = 1_000_000; +const CURSOR_1M_NAME_PATTERN = /\b1m\b/i; +const CURSOR_NATIVE_1M_ID_PATTERN = /^(?:kimi-k3|glm-5\.[2-9])/; +const CURSOR_MAX_MODE_1M_ID_PATTERN = /claude|gemini/; + /** * Model-id families whose native catalogs (anthropic, openai/openai-codex, * google) are multimodal. Cursor-only or text-only families (`composer-*`, @@ -291,6 +303,7 @@ function normalizeCursorModel( name, baseUrl: baseUrlOverride ?? reference.baseUrl, reasoning, + contextWindow: resolveCursorContextWindow(details, id, reference.contextWindow), cursorMaxMode: details.maxMode, }; } @@ -303,12 +316,32 @@ function normalizeCursorModel( reasoning, input: inferInputFromCursorId(id), cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: DEFAULT_CONTEXT_WINDOW, + contextWindow: resolveCursorContextWindow(details, id, DEFAULT_CONTEXT_WINDOW), maxTokens: DEFAULT_MAX_TOKENS, cursorMaxMode: details.maxMode, }; } +/** + * Context window for a discovered Cursor model: the 1M ceiling when any 1M + * signal fires (never below a larger bundled reference), else the fallback. + */ +function resolveCursorContextWindow( + model: CursorModelDetailsValue, + id: string, + fallback: number | null, +): number | null { + const labeled1M = + CURSOR_1M_NAME_PATTERN.test(id) || + [model.displayName, model.displayNameShort, model.displayModelId, ...model.aliases].some( + candidate => typeof candidate === "string" && CURSOR_1M_NAME_PATTERN.test(candidate), + ); + if (labeled1M || CURSOR_NATIVE_1M_ID_PATTERN.test(id) || (model.maxMode && CURSOR_MAX_MODE_1M_ID_PATTERN.test(id))) { + return Math.max(fallback ?? 0, CURSOR_1M_CONTEXT_WINDOW); + } + return fallback; +} + function pickModelDisplayName(model: CursorModelDetailsValue, fallbackId: string): string { const candidates = [model.displayName, model.displayNameShort, model.displayModelId, ...model.aliases, fallbackId]; for (const candidate of candidates) { diff --git a/packages/catalog/src/provider-models/cache-provider-id.ts b/packages/catalog/src/provider-models/cache-provider-id.ts index aaa16401d..cff4b2e7a 100644 --- a/packages/catalog/src/provider-models/cache-provider-id.ts +++ b/packages/catalog/src/provider-models/cache-provider-id.ts @@ -22,7 +22,9 @@ export function getDefaultModelDiscoveryBaseUrl(providerId: string): string | un export function resolveModelCacheProviderId(providerId: string, options: ModelCacheProviderIdOptions = {}): string { switch (providerId) { case "cursor": - return "cursor:max-mode-v2"; + // v3: max-mode Claude/Gemini rows cached before the 1M context-window + // discovery fix carry a stale 200k window and must be refetched. + return "cursor:max-mode-v3"; case "litellm": { const baseUrl = options.baseUrl ?? getDefaultModelDiscoveryBaseUrl(providerId)!; return `litellm:rich-v5:${Bun.hash(baseUrl).toString(36)}`; diff --git a/packages/catalog/test/cursor-discovery.test.ts b/packages/catalog/test/cursor-discovery.test.ts index b3d99613d..146d94282 100644 --- a/packages/catalog/test/cursor-discovery.test.ts +++ b/packages/catalog/test/cursor-discovery.test.ts @@ -192,6 +192,95 @@ describe("fetchCursorUsableModels", () => { ]); }); + it("assigns the 1M window from display-name labels across families", async () => { + const response = create(GetUsableModelsResponseSchema, { + models: [ + create(ModelDetailsSchema, { modelId: "claude-opus-5-high", displayName: "Opus 5 1M" }), + create(ModelDetailsSchema, { modelId: "gpt-5.5-high", displayName: "GPT-5.5 1M High" }), + create(ModelDetailsSchema, { modelId: "gpt-5.6-sol-medium", displayName: "GPT-5.6 Sol 1M" }), + ], + }); + const labeledBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: labeledBaseUrl, timeoutMs: 1_000 }); + + expect(models).toEqual([ + expect.objectContaining({ id: "claude-opus-5-high", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "gpt-5.5-high", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "gpt-5.6-sol-medium", contextWindow: 1_000_000 }), + ]); + }); + + it("assigns the 1M window to natively 1M families Cursor serves unlabeled", async () => { + const response = create(GetUsableModelsResponseSchema, { + models: [ + create(ModelDetailsSchema, { modelId: "kimi-k3-max", displayName: "Kimi K3" }), + create(ModelDetailsSchema, { modelId: "glm-5.2-max", displayName: "GLM 5.2 Max" }), + ], + }); + const nativeBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: nativeBaseUrl, timeoutMs: 1_000 }); + + expect(models).toEqual([ + expect.objectContaining({ id: "glm-5.2-max", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "kimi-k3-max", contextWindow: 1_000_000 }), + ]); + }); + + it("assigns the 1M window to unlabeled max-mode Claude models", async () => { + const response = create(GetUsableModelsResponseSchema, { + models: [ + create(ModelDetailsSchema, { + modelId: "claude-opus-4-8-high-fast", + displayName: "Opus 4.8 Fast", + maxMode: true, + }), + ], + }); + const maxModeBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: maxModeBaseUrl, timeoutMs: 1_000 }); + + expect(models).toEqual([ + expect.objectContaining({ id: "claude-opus-4-8-high-fast", cursorMaxMode: true, contextWindow: 1_000_000 }), + ]); + }); + + it("keeps the default window for unlabeled non-max models and max-mode models outside 1M families", async () => { + const response = create(GetUsableModelsResponseSchema, { + models: [ + create(ModelDetailsSchema, { modelId: "cursor-composer-max", maxMode: true }), + create(ModelDetailsSchema, { modelId: "claude-opus-4-8-high", displayName: "Opus 4.8" }), + ], + }); + const defaultBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: defaultBaseUrl, timeoutMs: 1_000 }); + + expect(models).toEqual([ + expect.objectContaining({ id: "claude-opus-4-8-high", cursorMaxMode: false, contextWindow: 200_000 }), + expect.objectContaining({ id: "cursor-composer-max", cursorMaxMode: true, contextWindow: 200_000 }), + ]); + }); + + it("raises a bundled reference window when the reference id is served with a 1M label", async () => { + // `claude-4.5-sonnet` is a bundled cursor reference with a 200k window; + // served with a 1M display name it must expose the 1M ceiling. + const response = create(GetUsableModelsResponseSchema, { + models: [create(ModelDetailsSchema, { modelId: "claude-4.5-sonnet", displayName: "Sonnet 4.5 1M" })], + }); + const referenceBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ + apiKey: "test-token", + baseUrl: referenceBaseUrl, + timeoutMs: 1_000, + }); + + expect(models).toEqual([expect.objectContaining({ id: "claude-4.5-sonnet", contextWindow: 1_000_000 })]); + }); + it("ignores Cursor cache rows written before max-mode metadata was persisted", async () => { const cacheDbPath = await createTempCachePath(); const staleSpec = cursorModelSpec("cursor-composer-max"); From 991cd795f4f80ce5393f0dfd82dc7b2cef2311b5 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 Jul 2026 10:46:25 -0600 Subject: [PATCH 2/4] Apply suggestion from @mmmeff --- packages/catalog/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 409b8fd8a..2627d6c20 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,10 +2,6 @@ ## [Unreleased] -### Fixed - -- Fixed Cursor 1M-context model discovery to expose the 1M-token context window for models Cursor labels with a "1M" display name (Claude and GPT families), natively 1M families Cursor serves unlabeled (Kimi K3, GLM 5.2+), and max-mode Claude/Gemini variants, instead of pinning every discovered model to the 200k default. Bumped the Cursor model-cache namespace so windows cached before this fix are refetched. ([#4798](https://github.com/can1357/oh-my-pi/issues/4798)) - ## [17.2.0] - 2026-07-30 ### Added From 6409cb21e956aba3879177ba66aac12f016dba1b Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 Jul 2026 11:20:20 -0600 Subject: [PATCH 3/4] fix(cursor): broadened native 1M family matching, restored changelog Compose the native-1M gate from the shared family parsers (isKimiK3ModelId, parseGlmModel + semverGte 5.2 floor) so namespaced ids (moonshotai/kimi-k3, z-ai/glm-5.2) and future GLM versions (glm-5.10, glm-6) are covered, addressing review feedback. Restore the Unreleased changelog entry dropped by the applied empty suggestion. --- packages/catalog/CHANGELOG.md | 4 +++ packages/catalog/src/discovery/cursor.ts | 26 +++++++++++++++++-- .../catalog/test/cursor-discovery.test.ts | 23 ++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 2627d6c20..409b8fd8a 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Cursor 1M-context model discovery to expose the 1M-token context window for models Cursor labels with a "1M" display name (Claude and GPT families), natively 1M families Cursor serves unlabeled (Kimi K3, GLM 5.2+), and max-mode Claude/Gemini variants, instead of pinning every discovered model to the 200k default. Bumped the Cursor model-cache namespace so windows cached before this fix are refetched. ([#4798](https://github.com/can1357/oh-my-pi/issues/4798)) + ## [17.2.0] - 2026-07-30 ### Added diff --git a/packages/catalog/src/discovery/cursor.ts b/packages/catalog/src/discovery/cursor.ts index 6146b8ebd..92c91e395 100644 --- a/packages/catalog/src/discovery/cursor.ts +++ b/packages/catalog/src/discovery/cursor.ts @@ -1,6 +1,8 @@ import * as http2 from "node:http2"; import { create, fromBinary, toBinary } from "@bufbuild/protobuf"; import { type } from "arktype"; +import { bareModelId, parseGlmModel, semverGte } from "../identity/classify"; +import { isKimiK3ModelId } from "../identity/family"; import { getBundledModels } from "../models"; import { toModelSpec } from "../provider-models/bundled-references"; import type { Model, ModelSpec } from "../types"; @@ -22,7 +24,6 @@ const DEFAULT_MAX_TOKENS = 64_000; */ const CURSOR_1M_CONTEXT_WINDOW = 1_000_000; const CURSOR_1M_NAME_PATTERN = /\b1m\b/i; -const CURSOR_NATIVE_1M_ID_PATTERN = /^(?:kimi-k3|glm-5\.[2-9])/; const CURSOR_MAX_MODE_1M_ID_PATTERN = /claude|gemini/; /** @@ -336,12 +337,33 @@ function resolveCursorContextWindow( [model.displayName, model.displayNameShort, model.displayModelId, ...model.aliases].some( candidate => typeof candidate === "string" && CURSOR_1M_NAME_PATTERN.test(candidate), ); - if (labeled1M || CURSOR_NATIVE_1M_ID_PATTERN.test(id) || (model.maxMode && CURSOR_MAX_MODE_1M_ID_PATTERN.test(id))) { + if (labeled1M || isCursorNative1MModelId(id) || (model.maxMode && CURSOR_MAX_MODE_1M_ID_PATTERN.test(id))) { return Math.max(fallback ?? 0, CURSOR_1M_CONTEXT_WINDOW); } return fallback; } +/** + * Natively 1M-context families Cursor serves without a "1M" label: Kimi K3 and + * GLM 5.2+ coding SKUs. The shared family parsers cover namespace forms + * (`moonshotai/kimi-k3`, `z-ai/glm-5.2`) and future GLM versions (`glm-5.10`, + * `glm-6`); vision and sub-1M variants stay out via the same gates as + * `isGlm52ReasoningEffortModelId`. + */ +function isCursorNative1MModelId(id: string): boolean { + if (isKimiK3ModelId(id)) { + return true; + } + const glm = parseGlmModel(bareModelId(id)); + if (!glm || glm.vision) { + return false; + } + if (glm.variant !== "base" && glm.variant !== "air" && glm.variant !== "turbo") { + return false; + } + return semverGte(glm.version, "5.2"); +} + function pickModelDisplayName(model: CursorModelDetailsValue, fallbackId: string): string { const candidates = [model.displayName, model.displayNameShort, model.displayModelId, ...model.aliases, fallbackId]; for (const candidate of candidates) { diff --git a/packages/catalog/test/cursor-discovery.test.ts b/packages/catalog/test/cursor-discovery.test.ts index 146d94282..256fda750 100644 --- a/packages/catalog/test/cursor-discovery.test.ts +++ b/packages/catalog/test/cursor-discovery.test.ts @@ -215,7 +215,10 @@ describe("fetchCursorUsableModels", () => { const response = create(GetUsableModelsResponseSchema, { models: [ create(ModelDetailsSchema, { modelId: "kimi-k3-max", displayName: "Kimi K3" }), + create(ModelDetailsSchema, { modelId: "moonshotai/kimi-k3", displayName: "Kimi K3" }), create(ModelDetailsSchema, { modelId: "glm-5.2-max", displayName: "GLM 5.2 Max" }), + create(ModelDetailsSchema, { modelId: "glm-5.10-high", displayName: "GLM 5.10 High" }), + create(ModelDetailsSchema, { modelId: "glm-6-max", displayName: "GLM 6 Max" }), ], }); const nativeBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); @@ -223,8 +226,28 @@ describe("fetchCursorUsableModels", () => { const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: nativeBaseUrl, timeoutMs: 1_000 }); expect(models).toEqual([ + expect.objectContaining({ id: "glm-5.10-high", contextWindow: 1_000_000 }), expect.objectContaining({ id: "glm-5.2-max", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "glm-6-max", contextWindow: 1_000_000 }), expect.objectContaining({ id: "kimi-k3-max", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "moonshotai/kimi-k3", contextWindow: 1_000_000 }), + ]); + }); + + it("keeps the default window below the GLM 5.2 floor and outside the coding variants", async () => { + const response = create(GetUsableModelsResponseSchema, { + models: [ + create(ModelDetailsSchema, { modelId: "glm-5.1-high", displayName: "GLM 5.1 High" }), + create(ModelDetailsSchema, { modelId: "glm-5.2-flash", displayName: "GLM 5.2 Flash" }), + ], + }); + const nativeBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); + + const models = await fetchCursorUsableModels({ apiKey: "test-token", baseUrl: nativeBaseUrl, timeoutMs: 1_000 }); + + expect(models).toEqual([ + expect.objectContaining({ id: "glm-5.1-high", contextWindow: 200_000 }), + expect.objectContaining({ id: "glm-5.2-flash", contextWindow: 200_000 }), ]); }); From 8dca9772b93118631bfa0382e1a898e09e4e0f34 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 Jul 2026 14:50:36 -0600 Subject: [PATCH 4/4] fix(cursor): recognized Kimi's official bare k3 id as 1M Cursor custom ids can arrive as k3 (or kimi/k3), which isKimiK3ModelId does not match; the bundled kimi-code catalog defines k3 as the 1,048,576-token Kimi K3 model while k3-256k is the 256k SKU, so gate the bare form with an exact-match pattern that keeps k3-256k at the default window. --- packages/catalog/src/discovery/cursor.ts | 4 +++- packages/catalog/test/cursor-discovery.test.ts | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/catalog/src/discovery/cursor.ts b/packages/catalog/src/discovery/cursor.ts index 92c91e395..64a61e13d 100644 --- a/packages/catalog/src/discovery/cursor.ts +++ b/packages/catalog/src/discovery/cursor.ts @@ -25,6 +25,8 @@ const DEFAULT_MAX_TOKENS = 64_000; const CURSOR_1M_CONTEXT_WINDOW = 1_000_000; const CURSOR_1M_NAME_PATTERN = /\b1m\b/i; const CURSOR_MAX_MODE_1M_ID_PATTERN = /claude|gemini/; +/** Kimi's official bare K3 id (`k3`, `kimi/k3`); `k3-256k` is the 256k SKU and stays out. */ +const CURSOR_KIMI_K3_BARE_ID_PATTERN = /(^|\/)k3$/i; /** * Model-id families whose native catalogs (anthropic, openai/openai-codex, @@ -351,7 +353,7 @@ function resolveCursorContextWindow( * `isGlm52ReasoningEffortModelId`. */ function isCursorNative1MModelId(id: string): boolean { - if (isKimiK3ModelId(id)) { + if (isKimiK3ModelId(id) || CURSOR_KIMI_K3_BARE_ID_PATTERN.test(id)) { return true; } const glm = parseGlmModel(bareModelId(id)); diff --git a/packages/catalog/test/cursor-discovery.test.ts b/packages/catalog/test/cursor-discovery.test.ts index 256fda750..edcaf86b7 100644 --- a/packages/catalog/test/cursor-discovery.test.ts +++ b/packages/catalog/test/cursor-discovery.test.ts @@ -216,6 +216,8 @@ describe("fetchCursorUsableModels", () => { models: [ create(ModelDetailsSchema, { modelId: "kimi-k3-max", displayName: "Kimi K3" }), create(ModelDetailsSchema, { modelId: "moonshotai/kimi-k3", displayName: "Kimi K3" }), + create(ModelDetailsSchema, { modelId: "k3", displayName: "K3" }), + create(ModelDetailsSchema, { modelId: "kimi/k3", displayName: "K3" }), create(ModelDetailsSchema, { modelId: "glm-5.2-max", displayName: "GLM 5.2 Max" }), create(ModelDetailsSchema, { modelId: "glm-5.10-high", displayName: "GLM 5.10 High" }), create(ModelDetailsSchema, { modelId: "glm-6-max", displayName: "GLM 6 Max" }), @@ -229,7 +231,9 @@ describe("fetchCursorUsableModels", () => { expect.objectContaining({ id: "glm-5.10-high", contextWindow: 1_000_000 }), expect.objectContaining({ id: "glm-5.2-max", contextWindow: 1_000_000 }), expect.objectContaining({ id: "glm-6-max", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "k3", contextWindow: 1_000_000 }), expect.objectContaining({ id: "kimi-k3-max", contextWindow: 1_000_000 }), + expect.objectContaining({ id: "kimi/k3", contextWindow: 1_000_000 }), expect.objectContaining({ id: "moonshotai/kimi-k3", contextWindow: 1_000_000 }), ]); }); @@ -239,6 +243,7 @@ describe("fetchCursorUsableModels", () => { models: [ create(ModelDetailsSchema, { modelId: "glm-5.1-high", displayName: "GLM 5.1 High" }), create(ModelDetailsSchema, { modelId: "glm-5.2-flash", displayName: "GLM 5.2 Flash" }), + create(ModelDetailsSchema, { modelId: "k3-256k", displayName: "K3-256k" }), ], }); const nativeBaseUrl = await startCursorDiscoveryServer(toBinary(GetUsableModelsResponseSchema, response)); @@ -248,6 +253,7 @@ describe("fetchCursorUsableModels", () => { expect(models).toEqual([ expect.objectContaining({ id: "glm-5.1-high", contextWindow: 200_000 }), expect.objectContaining({ id: "glm-5.2-flash", contextWindow: 200_000 }), + expect.objectContaining({ id: "k3-256k", contextWindow: 200_000 }), ]); });