From 31c732653fb7fa72545f12bc88f06df58fdb887a Mon Sep 17 00:00:00 2001 From: koopmannleon19977-cmyk Date: Tue, 28 Jul 2026 20:23:20 +0200 Subject: [PATCH 1/2] fix(cursor): add kimi-k3 with low/high/max effort tiers Cursor serves Kimi K3 only as effort-suffixed wire ids (kimi-k3-low/-high/-max, verified via GetUsableModels 2026-07-28); the bare id returns Connect not_found. Add the base model to CURSOR_STATIC_MODELS (tier picker) and CURSOR_MODEL_EFFORT_TIERS so requests resolve to a suffixed variant, mirroring the native Kimi provider's K3 ladder. --- src/adapters/cursor/discovery.ts | 5 ++++- src/adapters/cursor/effort-map.ts | 3 +++ tests/cursor-discovery.test.ts | 1 + tests/cursor-effort-suffix.test.ts | 10 ++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/adapters/cursor/discovery.ts b/src/adapters/cursor/discovery.ts index c8ab41a7cd6..6e95b28216d 100644 --- a/src/adapters/cursor/discovery.ts +++ b/src/adapters/cursor/discovery.ts @@ -167,7 +167,7 @@ export const CURSOR_STATIC_MODELS: readonly CursorModelInfo[] = normalizeCursorM // the request builder appends the per-model suffix (see effort-map.ts) and reasoning models // advertise effort so Codex exposes the tier picker. `supportsReasoningEffort` tracks whether the // model has *selectable effort tiers* (CURSOR_MODEL_EFFORT_TIERS), NOT merely whether it reasons: - // gemini/grok/kimi/gpt-5-mini are reasoning models in the SOT but are sent bare (no tier picker). + // gemini/grok/kimi-k2.7/gpt-5-mini are reasoning models in the SOT but are sent bare (no tier picker). ...CURSOR_ROUTER_MODEL_IDS.map(id => ({ id, contextWindow: CONTEXT_200K, supportsReasoningEffort: false })), { id: "claude-sonnet-5", contextWindow: CONTEXT_200K, supportsReasoningEffort: true }, @@ -224,6 +224,9 @@ export const CURSOR_STATIC_MODELS: readonly CursorModelInfo[] = normalizeCursorM // take PR #73's supportsReasoningEffort for glm-5.2 (its effort-map tiers landed with the PR). { id: "glm-5.2", contextWindow: CONTEXT_1M, supportsReasoningEffort: true }, { id: "kimi-k2.7-code", contextWindow: CONTEXT_262K }, + // 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 }, { 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 ea4edbe5e00..ddb61311418 100644 --- a/src/adapters/cursor/effort-map.ts +++ b/src/adapters/cursor/effort-map.ts @@ -27,6 +27,9 @@ const CURSOR_MODEL_EFFORT_TIERS: Record = { "claude-opus-5": ["low", "medium", "high", "xhigh", "max"], "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], "glm-5.2": ["high", "max"], + // 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"], // GetUsableModels (2026-07-09) lists grok-4.5-{medium,high,xhigh} and grok-4.5-fast-{medium,high,xhigh}; // the bare "grok-4.5-fast" id was removed upstream and now returns not_found. "grok-4.5": ["medium", "high", "xhigh"], diff --git a/tests/cursor-discovery.test.ts b/tests/cursor-discovery.test.ts index 9c6a28f9236..19f88a28533 100644 --- a/tests/cursor-discovery.test.ts +++ b/tests/cursor-discovery.test.ts @@ -36,6 +36,7 @@ describe("Cursor discovery metadata", () => { expect(ids).toContain("gpt-5.5"); expect(ids).toContain("glm-5.2"); expect(ids).toContain("kimi-k2.7-code"); + expect(ids).toContain("kimi-k3"); expect(ids).toContain("claude-opus-4-7-fast"); // 260709 refresh: stale ids dropped from the static seed (cursor.com docs); gpt-5.5-extra // stays — it survives the live GetUsableModels filter (004_live_snapshot.md). diff --git a/tests/cursor-effort-suffix.test.ts b/tests/cursor-effort-suffix.test.ts index b5839d0c274..423cd9919b9 100644 --- a/tests/cursor-effort-suffix.test.ts +++ b/tests/cursor-effort-suffix.test.ts @@ -66,6 +66,16 @@ describe("Cursor per-model reasoning-effort suffix", () => { expect(modelIdFor("cursor/grok-4.5-fast", "xhigh")).toBe("grok-4.5-fast-xhigh"); }); + test("kimi-k3 maps to its live effort-suffixed variants", () => { + expect(modelIdFor("cursor/kimi-k3", "low")).toBe("kimi-k3-low"); + expect(modelIdFor("cursor/kimi-k3", "medium")).toBe("kimi-k3-high"); + expect(modelIdFor("cursor/kimi-k3", "high")).toBe("kimi-k3-high"); + expect(modelIdFor("cursor/kimi-k3", "max")).toBe("kimi-k3-max"); + // Bare id (no requested effort) resolves to the model's top tier; upstream rejects the bare id. + expect(modelIdFor("cursor/kimi-k3")).toBe("kimi-k3-max"); + expect(cursorModelEffortLadder("kimi-k3")).toEqual(["low", "high", "max"]); + }); + test("model ladders are deduped and sorted in canonical Codex order", () => { expect(cursorModelEffortLadder("claude-opus-4-8")).toEqual(["low", "medium", "high", "xhigh", "max"]); expect(cursorModelEffortLadder("glm-5.2")).toEqual(["high", "max"]); From d2201509b62904a1cfc5ce347503a94fc9cf7178 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Thu, 30 Jul 2026 08:51:16 +0900 Subject: [PATCH 2/2] fix(cursor): keep max as the kimi-k3 catalog default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #646 seeds cursor/kimi-k3 with a low/high/max effort ladder, which is the right tier data — Cursor serves K3 only as effort-suffixed wire ids and Kimi documents max as the model's API default. But the Cursor registry entry supplied no modelDefaultReasoningEfforts, and that ladder has no medium rung, so applyReasoningLevels fell through medium -> high and settled the catalog default on high. The picker then sends high explicitly and the request builder's no-effort fallback to kimi-k3-max is never reached. The other K3 routes (kimi, kimi-code, opencode-go) already carry this default, so cursor was also the odd one out. Verified in both directions: on the PR commit alone the assembled catalog row reports default_reasoning_level = high; with this line it reports max; removing the line again fails the new test with Received: "high". The existing effort-suffix tests pass either way, which is why the defect slipped through — they assert the ladder, not the assembled default. Scope check: modelRecordValue matches exact key, colon-family, then case-fold, so { "kimi-k3": "max" } cannot reach kimi-k3-max, kimi-k3-low, or any other cursor model. Confirmed across 8 model ids; only kimi-k3 is affected. Gates: tsc clean; codex-catalog 113 pass; cursor-effort-suffix + cursor-discovery 18 pass; provider-registry-parity + reasoning-effort 70 pass. Docs ship in the same commit because the prose asserts the ladder and default. --- .../src/content/docs/guides/providers.md | 8 ++++-- .../src/content/docs/ja/guides/providers.md | 8 ++++-- .../src/content/docs/ko/guides/providers.md | 8 ++++-- .../src/content/docs/ru/guides/providers.md | 7 +++-- .../content/docs/zh-cn/guides/providers.md | 6 ++-- src/providers/registry.ts | 6 ++++ tests/codex-catalog.test.ts | 28 +++++++++++++++++++ 7 files changed, 58 insertions(+), 13 deletions(-) diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 2e70c4dae36..7fe3b7f4d76 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -271,9 +271,11 @@ Gateway** needs your account + gateway ids filled into the URL. Cursor is tracked separately as an experimental adapter. `adapter: "cursor"` appears in `ocx init` and the dashboard Add Provider picker as an experimental local config entry with Cursor's static fallback model catalog metadata. When a Cursor access token is configured, opencodex uses Cursor's -live HTTP/2 transport. Its v2.7.1 fallback seed includes `gpt-5.6-sol` / `terra` / `luna` (1M context) -plus `grok-4.5` / `grok-4.5-fast` (500K); live discovery decides which remain visible for the -account. Cursor server-driven native read/write/delete/ls/grep/shell/fetch execution +live HTTP/2 transport. Its v2.7.1 fallback seed includes `gpt-5.6-sol` / `terra` / `luna` (1M context), +`grok-4.5` / `grok-4.5-fast` (500K), and `kimi-k3` (262K); live discovery decides which remain +visible for the account. Cursor serves Kimi K3 only as effort-suffixed wire ids, so +`cursor/kimi-k3` exposes a `low` / `high` / `max` ladder and defaults to `max`, matching the +model's documented API default. Cursor server-driven native read/write/delete/ls/grep/shell/fetch execution is disabled by default because it bypasses Codex's approval and sandbox path; set `unsafeAllowNativeLocalExec: true` on the `providers.cursor` object in `~/.opencodex/config.json` only for trusted local experiments (or via **Providers → Cursor → Edit JSON** in the dashboard). diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index ef2dd1d5486..67548d50eb0 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -204,9 +204,11 @@ Gateway** は URL にアカウント + ゲートウェイ ID を埋める必要 Cursor は別の実験的アダプターとして追跡します。`adapter: "cursor"` は `ocx init` とダッシュボード Add Provider ピッカーに実験的 local config 項目として表示され、Cursor の静的フォールバックモデルカタログ メタデータを保存します。Cursor アクセストークンを設定すると opencodex は Cursor ライブ HTTP/2 トランスポートを -使います。v2.7.1 フォールバックリストには 1M コンテキストの `gpt-5.6-sol` / `terra` / `luna` と 500K コンテキストの -`grok-4.5` / `grok-4.5-fast` が含まれ、ライブ探索結果に基づき現在のアカウントに表示するモデルを -決定します。Cursor サーバーが直接送るネイティブ read/write/delete/ls/grep/shell/fetch 実行は Codex +使います。v2.7.1 フォールバックリストには 1M コンテキストの `gpt-5.6-sol` / `terra` / `luna`、500K コンテキストの +`grok-4.5` / `grok-4.5-fast`、262K コンテキストの `kimi-k3` が含まれ、ライブ探索結果に基づき現在の +アカウントに表示するモデルを決定します。Cursor は Kimi K3 を effort サフィックス付きの wire id +としてのみ提供するため、`cursor/kimi-k3` は `low` / `high` / `max` のラダーを公開し、既定値はモデル +ドキュメントの API 既定値と同じ `max` です。Cursor サーバーが直接送るネイティブ read/write/delete/ls/grep/shell/fetch 実行は Codex 承認とサンドボックス経路をバイパスするためデフォルトで無効です。信頼できるローカル実験でのみ `~/.opencodex/config.json` の `providers.cursor` に `unsafeAllowNativeLocalExec: true` を設定してください。 ダッシュボードからは **Providers → Cursor → Edit JSON** で設定できます。完全な例は diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 208839d8107..8cdae100adc 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -205,9 +205,11 @@ Gateway**는 URL에 계정 + 게이트웨이 id를 채워야 합니다. Cursor는 별도의 실험적 어댑터로 추적합니다. `adapter: "cursor"`는 `ocx init`과 dashboard Add Provider picker에 실험적 local config 항목으로 표시되며, Cursor의 static fallback model catalog metadata를 저장합니다. Cursor access token이 설정되면 opencodex는 Cursor live HTTP/2 transport를 -사용합니다. v2.7.1 폴백 목록에는 1M 컨텍스트의 `gpt-5.6-sol` / `terra` / `luna`와 500K 컨텍스트의 -`grok-4.5` / `grok-4.5-fast`가 들어 있으며, 실시간 탐색 결과에 따라 현재 계정에 표시할 모델을 -결정합니다. Cursor 서버가 직접 보내는 native read/write/delete/ls/grep/shell/fetch 실행은 Codex +사용합니다. v2.7.1 폴백 목록에는 1M 컨텍스트의 `gpt-5.6-sol` / `terra` / `luna`, 500K 컨텍스트의 +`grok-4.5` / `grok-4.5-fast`, 262K 컨텍스트의 `kimi-k3`가 들어 있으며, 실시간 탐색 결과에 따라 +현재 계정에 표시할 모델을 결정합니다. Cursor는 Kimi K3를 effort 접미사가 붙은 wire id로만 +제공하므로 `cursor/kimi-k3`는 `low` / `high` / `max` 래더를 노출하고 기본값은 모델 문서의 API +기본값과 같은 `max`입니다. Cursor 서버가 직접 보내는 native read/write/delete/ls/grep/shell/fetch 실행은 Codex 승인 및 sandbox 경로를 우회하므로 기본적으로 비활성화되어 있습니다. 신뢰한 로컬 실험에서만 `~/.opencodex/config.json`의 `providers.cursor`에 `unsafeAllowNativeLocalExec: true`를 설정하세요. 대시보드에서는 **Providers → Cursor → Edit JSON**에서 설정할 수 있습니다. 전체 예시는 diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index d6df82506d2..f23e818d8c0 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -219,8 +219,11 @@ Cursor отслеживается отдельно как эксперимент `ocx init` и в селекторе Add Provider дашборда как экспериментальная запись локальной конфигурации с метаданными статического резервного каталога моделей Cursor. Когда настроен токен доступа Cursor, opencodex использует живой транспорт HTTP/2 Cursor. Его резервный список версии v2.7.1 включает -`gpt-5.6-sol` / `terra` / `luna` (контекст 1M) плюс `grok-4.5` / `grok-4.5-fast` (500K); живое -обнаружение решает, какие из них останутся видимыми для аккаунта. Управляемое сервером Cursor +`gpt-5.6-sol` / `terra` / `luna` (контекст 1M), `grok-4.5` / `grok-4.5-fast` (500K) и `kimi-k3` +(262K); живое обнаружение решает, какие из них останутся видимыми для аккаунта. Cursor отдаёт +Kimi K3 только через wire id с суффиксом усилия, поэтому `cursor/kimi-k3` предоставляет лестницу +`low` / `high` / `max` и по умолчанию использует `max` — как и задокументированное значение по +умолчанию в API модели. Управляемое сервером Cursor нативное выполнение read/write/delete/ls/grep/shell/fetch по умолчанию отключено, поскольку оно обходит путь одобрений и песочницу Codex; устанавливайте `unsafeAllowNativeLocalExec: true` в объекте `providers.cursor` файла `~/.opencodex/config.json` только для доверенных локальных diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index 357ddbcacf2..b749d383746 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -191,8 +191,10 @@ GPT-5.6 Sol/Terra/Luna 会预置在提供商的回退列表中,因此即使实 Cursor 作为单独的实验性 adapter 进行跟踪。`adapter: "cursor"` 会作为实验性本地配置出现在 `ocx init` 和 dashboard Add Provider picker 中,并保存 Cursor 的静态回退模型目录 metadata。配置 Cursor access token 后,opencodex 会使用 Cursor live HTTP/2 transport。v2.7.1 回退列表包含上下文为 -1M 的 `gpt-5.6-sol` / `terra` / `luna`,以及上下文为 500K 的 -`grok-4.5` / `grok-4.5-fast`;最终显示哪些模型由账号的实时发现结果决定。Cursor 服务器直接发起的 +1M 的 `gpt-5.6-sol` / `terra` / `luna`、上下文为 500K 的 `grok-4.5` / `grok-4.5-fast`,以及上下文为 +262K 的 `kimi-k3`;最终显示哪些模型由账号的实时发现结果决定。Cursor 只以带 effort 后缀的 wire id +提供 Kimi K3,因此 `cursor/kimi-k3` 暴露 `low` / `high` / `max` 阶梯,默认值为 `max`,与该模型 +文档中的 API 默认值一致。Cursor 服务器直接发起的 native read/write/delete/ls/grep/shell/fetch 执行默认禁用,因为它会绕过 Codex 的 approval 和 sandbox 路径;只有在可信本地实验中,才应在 `~/.opencodex/config.json` 的 `providers.cursor` 对象上设置 `unsafeAllowNativeLocalExec: true`,也可以在仪表盘的 **Providers → Cursor → Edit JSON** diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 7fea6644f08..a1d86b74a22 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -384,6 +384,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelContextWindows: cursorModelContextWindows(CURSOR_STATIC_MODELS), modelInputModalities: cursorModelInputModalities(CURSOR_STATIC_MODELS), modelReasoningEfforts: cursorModelReasoningEfforts(CURSOR_STATIC_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 + // 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 diff --git a/tests/codex-catalog.test.ts b/tests/codex-catalog.test.ts index 179e8a1bc11..7d501caf48c 100644 --- a/tests/codex-catalog.test.ts +++ b/tests/codex-catalog.test.ts @@ -686,6 +686,34 @@ describe("Google Gemini catalog metadata", () => { }); }); +describe("Cursor Kimi K3 catalog default effort", () => { + // Regression for the effort-tier ladder added with cursor/kimi-k3: the Cursor ladder is + // low/high/max with NO medium rung, so applyReasoningLevels' medium -> high -> first + // preference would settle default_reasoning_level on "high" unless the registry supplies a + // default. Kimi documents `max` as K3's API default, and a "high" default makes the picker + // send high explicitly so the request builder never falls back to kimi-k3-max. + test("keeps max as the catalog default instead of falling back to high", async () => { + const cursor = { + adapter: "cursor", + authMode: "oauth" as const, + liveModels: false, + }; + enrichProviderFromRegistry("cursor", cursor); + const models = await gatherRoutedModels({ + port: 0, + defaultProvider: "cursor", + providers: { cursor }, + }); + const entry = buildCatalogEntries(nativeTemplate(), [], models) + .find(row => row.slug === "cursor/kimi-k3"); + + expect(entry).toBeDefined(); + expect((entry?.supported_reasoning_levels as Array<{ effort: string }>).map(level => level.effort)) + .toEqual(["low", "high", "max", "ultra"]); + expect(entry?.default_reasoning_level).toBe("max"); + }); +}); + describe("configured CatalogModel displayName -> catalog display_name", () => { test("a routed CatalogModel displayName becomes the catalog display_name", () => { const model = { provider: "deepseek", id: "deepseek-v4", displayName: "DeepSeek V4", owned_by: "deepseek" };