Skip to content

Commit 275345a

Browse files
fix(cursor): add kimi-k3 with low/high/max effort tiers (lidge-jun#646)
* 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. * fix(cursor): keep max as the kimi-k3 catalog default PR lidge-jun#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. --------- Co-authored-by: bitkyc08-arch <bitkyc08@gmail.com>
1 parent bfacc3b commit 275345a

11 files changed

Lines changed: 76 additions & 14 deletions

File tree

docs-site/src/content/docs/guides/providers.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,11 @@ Gateway** needs your account + gateway ids filled into the URL.
274274
Cursor is tracked separately as an experimental adapter. `adapter: "cursor"` appears in `ocx init`
275275
and the dashboard Add Provider picker as an experimental local config entry with Cursor's static
276276
fallback model catalog metadata. When a Cursor access token is configured, opencodex uses Cursor's
277-
live HTTP/2 transport. Its v2.7.1 fallback seed includes `gpt-5.6-sol` / `terra` / `luna` (1M context)
278-
plus `grok-4.5` / `grok-4.5-fast` (500K); live discovery decides which remain visible for the
279-
account. Cursor server-driven native read/write/delete/ls/grep/shell/fetch execution
277+
live HTTP/2 transport. Its v2.7.1 fallback seed includes `gpt-5.6-sol` / `terra` / `luna` (1M context),
278+
`grok-4.5` / `grok-4.5-fast` (500K), and `kimi-k3` (262K); live discovery decides which remain
279+
visible for the account. Cursor serves Kimi K3 only as effort-suffixed wire ids, so
280+
`cursor/kimi-k3` exposes a `low` / `high` / `max` ladder and defaults to `max`, matching the
281+
model's documented API default. Cursor server-driven native read/write/delete/ls/grep/shell/fetch execution
280282
is disabled by default because it bypasses Codex's approval and sandbox path; set
281283
`unsafeAllowNativeLocalExec: true` on the `providers.cursor` object in `~/.opencodex/config.json`
282284
only for trusted local experiments (or via **Providers → Cursor → Edit JSON** in the dashboard).

docs-site/src/content/docs/ja/guides/providers.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,11 @@ Gateway** は URL にアカウント + ゲートウェイ ID を埋める必要
204204
Cursor は別の実験的アダプターとして追跡します。`adapter: "cursor"``ocx init` とダッシュボード Add
205205
Provider ピッカーに実験的 local config 項目として表示され、Cursor の静的フォールバックモデルカタログ
206206
メタデータを保存します。Cursor アクセストークンを設定すると opencodex は Cursor ライブ HTTP/2 トランスポートを
207-
使います。v2.7.1 フォールバックリストには 1M コンテキストの `gpt-5.6-sol` / `terra` / `luna` と 500K コンテキストの
208-
`grok-4.5` / `grok-4.5-fast` が含まれ、ライブ探索結果に基づき現在のアカウントに表示するモデルを
209-
決定します。Cursor サーバーが直接送るネイティブ read/write/delete/ls/grep/shell/fetch 実行は Codex
207+
使います。v2.7.1 フォールバックリストには 1M コンテキストの `gpt-5.6-sol` / `terra` / `luna`、500K コンテキストの
208+
`grok-4.5` / `grok-4.5-fast`、262K コンテキストの `kimi-k3` が含まれ、ライブ探索結果に基づき現在の
209+
アカウントに表示するモデルを決定します。Cursor は Kimi K3 を effort サフィックス付きの wire id
210+
としてのみ提供するため、`cursor/kimi-k3``low` / `high` / `max` のラダーを公開し、既定値はモデル
211+
ドキュメントの API 既定値と同じ `max` です。Cursor サーバーが直接送るネイティブ read/write/delete/ls/grep/shell/fetch 実行は Codex
210212
承認とサンドボックス経路をバイパスするためデフォルトで無効です。信頼できるローカル実験でのみ
211213
`~/.opencodex/config.json``providers.cursor``unsafeAllowNativeLocalExec: true` を設定してください。
212214
ダッシュボードからは **Providers → Cursor → Edit JSON** で設定できます。完全な例は

docs-site/src/content/docs/ko/guides/providers.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,11 @@ Gateway**는 URL에 계정 + 게이트웨이 id를 채워야 합니다.
205205
Cursor는 별도의 실험적 어댑터로 추적합니다. `adapter: "cursor"``ocx init`과 dashboard Add
206206
Provider picker에 실험적 local config 항목으로 표시되며, Cursor의 static fallback model catalog
207207
metadata를 저장합니다. Cursor access token이 설정되면 opencodex는 Cursor live HTTP/2 transport를
208-
사용합니다. v2.7.1 폴백 목록에는 1M 컨텍스트의 `gpt-5.6-sol` / `terra` / `luna`와 500K 컨텍스트의
209-
`grok-4.5` / `grok-4.5-fast`가 들어 있으며, 실시간 탐색 결과에 따라 현재 계정에 표시할 모델을
210-
결정합니다. Cursor 서버가 직접 보내는 native read/write/delete/ls/grep/shell/fetch 실행은 Codex
208+
사용합니다. v2.7.1 폴백 목록에는 1M 컨텍스트의 `gpt-5.6-sol` / `terra` / `luna`, 500K 컨텍스트의
209+
`grok-4.5` / `grok-4.5-fast`, 262K 컨텍스트의 `kimi-k3`가 들어 있으며, 실시간 탐색 결과에 따라
210+
현재 계정에 표시할 모델을 결정합니다. Cursor는 Kimi K3를 effort 접미사가 붙은 wire id로만
211+
제공하므로 `cursor/kimi-k3``low` / `high` / `max` 래더를 노출하고 기본값은 모델 문서의 API
212+
기본값과 같은 `max`입니다. Cursor 서버가 직접 보내는 native read/write/delete/ls/grep/shell/fetch 실행은 Codex
211213
승인 및 sandbox 경로를 우회하므로 기본적으로 비활성화되어 있습니다. 신뢰한 로컬 실험에서만
212214
`~/.opencodex/config.json``providers.cursor``unsafeAllowNativeLocalExec: true`를 설정하세요.
213215
대시보드에서는 **Providers → Cursor → Edit JSON**에서 설정할 수 있습니다. 전체 예시는

docs-site/src/content/docs/ru/guides/providers.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,11 @@ Cursor отслеживается отдельно как эксперимент
219219
`ocx init` и в селекторе Add Provider дашборда как экспериментальная запись локальной конфигурации
220220
с метаданными статического резервного каталога моделей Cursor. Когда настроен токен доступа Cursor,
221221
opencodex использует живой транспорт HTTP/2 Cursor. Его резервный список версии v2.7.1 включает
222-
`gpt-5.6-sol` / `terra` / `luna` (контекст 1M) плюс `grok-4.5` / `grok-4.5-fast` (500K); живое
223-
обнаружение решает, какие из них останутся видимыми для аккаунта. Управляемое сервером Cursor
222+
`gpt-5.6-sol` / `terra` / `luna` (контекст 1M), `grok-4.5` / `grok-4.5-fast` (500K) и `kimi-k3`
223+
(262K); живое обнаружение решает, какие из них останутся видимыми для аккаунта. Cursor отдаёт
224+
Kimi K3 только через wire id с суффиксом усилия, поэтому `cursor/kimi-k3` предоставляет лестницу
225+
`low` / `high` / `max` и по умолчанию использует `max` — как и задокументированное значение по
226+
умолчанию в API модели. Управляемое сервером Cursor
224227
нативное выполнение read/write/delete/ls/grep/shell/fetch по умолчанию отключено, поскольку оно
225228
обходит путь одобрений и песочницу Codex; устанавливайте `unsafeAllowNativeLocalExec: true` в
226229
объекте `providers.cursor` файла `~/.opencodex/config.json` только для доверенных локальных

docs-site/src/content/docs/zh-cn/guides/providers.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ GPT-5.6 Sol/Terra/Luna 会预置在提供商的回退列表中,因此即使实
191191
Cursor 作为单独的实验性 adapter 进行跟踪。`adapter: "cursor"` 会作为实验性本地配置出现在
192192
`ocx init` 和 dashboard Add Provider picker 中,并保存 Cursor 的静态回退模型目录 metadata。配置
193193
Cursor access token 后,opencodex 会使用 Cursor live HTTP/2 transport。v2.7.1 回退列表包含上下文为
194-
1M 的 `gpt-5.6-sol` / `terra` / `luna`,以及上下文为 500K 的
195-
`grok-4.5` / `grok-4.5-fast`;最终显示哪些模型由账号的实时发现结果决定。Cursor 服务器直接发起的
194+
1M 的 `gpt-5.6-sol` / `terra` / `luna`、上下文为 500K 的 `grok-4.5` / `grok-4.5-fast`,以及上下文为
195+
262K 的 `kimi-k3`;最终显示哪些模型由账号的实时发现结果决定。Cursor 只以带 effort 后缀的 wire id
196+
提供 Kimi K3,因此 `cursor/kimi-k3` 暴露 `low` / `high` / `max` 阶梯,默认值为 `max`,与该模型
197+
文档中的 API 默认值一致。Cursor 服务器直接发起的
196198
native read/write/delete/ls/grep/shell/fetch 执行默认禁用,因为它会绕过 Codex 的 approval 和
197199
sandbox 路径;只有在可信本地实验中,才应在 `~/.opencodex/config.json``providers.cursor`
198200
对象上设置 `unsafeAllowNativeLocalExec: true`,也可以在仪表盘的 **Providers → Cursor → Edit JSON**

src/adapters/cursor/discovery.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export const CURSOR_STATIC_MODELS: readonly CursorModelInfo[] = normalizeCursorM
167167
// the request builder appends the per-model suffix (see effort-map.ts) and reasoning models
168168
// advertise effort so Codex exposes the tier picker. `supportsReasoningEffort` tracks whether the
169169
// model has *selectable effort tiers* (CURSOR_MODEL_EFFORT_TIERS), NOT merely whether it reasons:
170-
// gemini/grok/kimi/gpt-5-mini are reasoning models in the SOT but are sent bare (no tier picker).
170+
// gemini/grok/kimi-k2.7/gpt-5-mini are reasoning models in the SOT but are sent bare (no tier picker).
171171
...CURSOR_ROUTER_MODEL_IDS.map(id => ({ id, contextWindow: CONTEXT_200K, supportsReasoningEffort: false })),
172172

173173
{ id: "claude-sonnet-5", contextWindow: CONTEXT_200K, supportsReasoningEffort: true },
@@ -224,6 +224,9 @@ export const CURSOR_STATIC_MODELS: readonly CursorModelInfo[] = normalizeCursorM
224224
// take PR #73's supportsReasoningEffort for glm-5.2 (its effort-map tiers landed with the PR).
225225
{ id: "glm-5.2", contextWindow: CONTEXT_1M, supportsReasoningEffort: true },
226226
{ id: "kimi-k2.7-code", contextWindow: CONTEXT_262K },
227+
// kimi-k3: cursor.com/docs/models/kimi-k3; account-verified via GetUsableModels (2026-07-28) —
228+
// ships only as effort-suffixed kimi-k3-{low,high,max}, so the tier picker is exposed.
229+
{ id: "kimi-k3", contextWindow: CONTEXT_262K, supportsReasoningEffort: true },
227230

228231
{ id: "grok-4.5", contextWindow: 500_000, supportsReasoningEffort: true },
229232
{ id: "grok-4.5-fast", contextWindow: 500_000, supportsReasoningEffort: true },

src/adapters/cursor/effort-map.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ const CURSOR_MODEL_EFFORT_TIERS: Record<string, readonly string[]> = {
2727
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
2828
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
2929
"glm-5.2": ["high", "max"],
30+
// GetUsableModels (2026-07-28) lists kimi-k3 only as effort-suffixed kimi-k3-{low,high,max};
31+
// the bare id returns not_found. Tiers mirror the native Kimi provider's K3 ladder.
32+
"kimi-k3": ["low", "high", "max"],
3033
// GetUsableModels (2026-07-09) lists grok-4.5-{medium,high,xhigh} and grok-4.5-fast-{medium,high,xhigh};
3134
// the bare "grok-4.5-fast" id was removed upstream and now returns not_found.
3235
"grok-4.5": ["medium", "high", "xhigh"],

src/providers/registry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
457457
modelContextWindows: cursorModelContextWindows(CURSOR_STATIC_MODELS),
458458
modelInputModalities: cursorModelInputModalities(CURSOR_STATIC_MODELS),
459459
modelReasoningEfforts: cursorModelReasoningEfforts(CURSOR_STATIC_MODELS),
460+
// Kimi K3 documents `max` as its API default, and its Cursor ladder has no `medium`
461+
// rung — so applyReasoningLevels' medium->high->first fallback would settle the catalog
462+
// default on `high`, the picker would send `high` explicitly, and the request builder's
463+
// no-effort fallback to `kimi-k3-max` would never be reached. Mirrors the other K3
464+
// routes (kimi, kimi-code, opencode-go).
465+
modelDefaultReasoningEfforts: { "kimi-k3": "max" },
460466
// Cursor's wire protocol never forwards image parts (request-builder emits an unsupported-
461467
// content marker), so the vision sidecar covers ALL cursor models regardless of what the
462468
// upstream model could natively do. Live-discovered models outside the static list fall back

tests/codex-catalog.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,34 @@ describe("Google Gemini catalog metadata", () => {
696696
});
697697
});
698698

699+
describe("Cursor Kimi K3 catalog default effort", () => {
700+
// Regression for the effort-tier ladder added with cursor/kimi-k3: the Cursor ladder is
701+
// low/high/max with NO medium rung, so applyReasoningLevels' medium -> high -> first
702+
// preference would settle default_reasoning_level on "high" unless the registry supplies a
703+
// default. Kimi documents `max` as K3's API default, and a "high" default makes the picker
704+
// send high explicitly so the request builder never falls back to kimi-k3-max.
705+
test("keeps max as the catalog default instead of falling back to high", async () => {
706+
const cursor = {
707+
adapter: "cursor",
708+
authMode: "oauth" as const,
709+
liveModels: false,
710+
};
711+
enrichProviderFromRegistry("cursor", cursor);
712+
const models = await gatherRoutedModels({
713+
port: 0,
714+
defaultProvider: "cursor",
715+
providers: { cursor },
716+
});
717+
const entry = buildCatalogEntries(nativeTemplate(), [], models)
718+
.find(row => row.slug === "cursor/kimi-k3");
719+
720+
expect(entry).toBeDefined();
721+
expect((entry?.supported_reasoning_levels as Array<{ effort: string }>).map(level => level.effort))
722+
.toEqual(["low", "high", "max", "ultra"]);
723+
expect(entry?.default_reasoning_level).toBe("max");
724+
});
725+
});
726+
699727
describe("configured CatalogModel displayName -> catalog display_name", () => {
700728
test("a routed CatalogModel displayName becomes the catalog display_name", () => {
701729
const model = { provider: "deepseek", id: "deepseek-v4", displayName: "DeepSeek V4", owned_by: "deepseek" };

tests/cursor-discovery.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe("Cursor discovery metadata", () => {
3636
expect(ids).toContain("gpt-5.5");
3737
expect(ids).toContain("glm-5.2");
3838
expect(ids).toContain("kimi-k2.7-code");
39+
expect(ids).toContain("kimi-k3");
3940
expect(ids).toContain("claude-opus-4-7-fast");
4041
// 260709 refresh: stale ids dropped from the static seed (cursor.com docs); gpt-5.5-extra
4142
// stays — it survives the live GetUsableModels filter (004_live_snapshot.md).

0 commit comments

Comments
 (0)