From 6563d6d2a53b112088cb04947f8b2df03bedb1ae Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:27:19 +0000 Subject: [PATCH 1/4] feat: add native support for Kimi K3 via OpenRouter --- docs/config/models.mdx | 1 + src/common/constants/knownModels.test.ts | 14 ++++++++-- src/common/constants/knownModels.ts | 13 ++++++++- src/common/types/thinking.ts | 9 ++++++ src/common/utils/ai/modelCapabilities.test.ts | 7 +++++ src/common/utils/ai/providerOptions.test.ts | 28 +++++++++++++++++++ src/common/utils/ai/providerOptions.ts | 11 ++++++-- src/common/utils/thinking/policy.test.ts | 11 ++++++++ src/common/utils/thinking/policy.ts | 7 +++++ src/common/utils/tokens/modelStats.test.ts | 9 ++++++ src/common/utils/tokens/models-extra.ts | 16 +++++++++++ .../builtInSkillContent.generated.ts | 1 + 12 files changed, 120 insertions(+), 7 deletions(-) diff --git a/docs/config/models.mdx b/docs/config/models.mdx index 8bc9407d6e..06c2dd1ba5 100644 --- a/docs/config/models.mdx +++ b/docs/config/models.mdx @@ -34,6 +34,7 @@ Mux ships with curated models kept up to date with the frontier. Use any custom | Grok Code Fast 1 | xai:grok-code-fast-1 | `grok-code` | | | DeepSeek V4 Pro | deepseek:deepseek-v4-pro | `deepseek`, `deepseek-pro`, `deepseek-v4`, `deepseek-v4-pro` | | | DeepSeek V4 Flash | deepseek:deepseek-v4-flash | `deepseek-flash`, `deepseek-v4-flash` | | +| Kimi K3 | openrouter:moonshotai/kimi-k3 | `kimi`, `k3`, `kimi-k3` | | {/* END KNOWN_MODELS_TABLE */} diff --git a/src/common/constants/knownModels.test.ts b/src/common/constants/knownModels.test.ts index a753e45077..092a0b3725 100644 --- a/src/common/constants/knownModels.test.ts +++ b/src/common/constants/knownModels.test.ts @@ -14,9 +14,12 @@ describe("Known Models Integration", () => { for (const [key, model] of Object.entries(KNOWN_MODELS)) { const modelId = model.providerModelId; - // xAI models are prefixed with "xai/" in models.json. - const lookupKey = model.provider === "xai" ? `xai/${modelId}` : modelId; - if (!(lookupKey in modelsJson) && !(modelId in modelsExtra)) { + // xAI and OpenRouter models are provider-prefixed in token metadata. + const lookupKey = + model.provider === "xai" || model.provider === "openrouter" + ? `${model.provider}/${modelId}` + : modelId; + if (!(lookupKey in modelsJson) && !(lookupKey in modelsExtra) && !(modelId in modelsExtra)) { missingModels.push(`${key}: ${model.provider}:${modelId}`); } } @@ -43,6 +46,11 @@ describe("Known Models Integration", () => { expect(MODEL_ABBREVIATIONS["gpt-5.5"]).toBeUndefined(); }); + test("kimi aliases resolve to the OpenRouter-scoped Kimi K3 model", () => { + expect(MODEL_ABBREVIATIONS.kimi).toBe("openrouter:moonshotai/kimi-k3"); + expect(MODEL_ABBREVIATIONS.k3).toBe("openrouter:moonshotai/kimi-k3"); + }); + test("known model ids and aliases stay unique across the curated registry", () => { const seenIds = new Set(); const seenAliases = new Set(); diff --git a/src/common/constants/knownModels.ts b/src/common/constants/knownModels.ts index 3d0029fa35..1542a311cf 100644 --- a/src/common/constants/knownModels.ts +++ b/src/common/constants/knownModels.ts @@ -4,7 +4,7 @@ import { formatModelDisplayName } from "../utils/ai/modelDisplay"; -type ModelProvider = "anthropic" | "openai" | "google" | "xai" | "deepseek"; +type ModelProvider = "anthropic" | "openai" | "google" | "xai" | "deepseek" | "openrouter"; interface KnownModelDefinition { /** Provider identifier used by SDK factories */ @@ -211,6 +211,17 @@ const MODEL_DEFINITIONS = { aliases: ["deepseek-flash", "deepseek-v4-flash"], tokenizerOverride: "deepseek/deepseek-v3.1", }, + // Kimi K3 - Moonshot AI's flagship open-weight multimodal reasoning model (released + // July 16, 2026; 1M context, text+image input; $3/M in, $15/M out via OpenRouter). + // Bare `kimi` alias tracks Moonshot's flagship per the shortest-alias convention. + KIMI_K3: { + provider: "openrouter", + providerModelId: "moonshotai/kimi-k3", + aliases: ["kimi", "k3", "kimi-k3"], + // K3's tokenizer isn't published in ai-tokenizer yet; reuse Kimi K2 (the newest + // Moonshot tokenizer available) for approximate counting. + tokenizerOverride: "moonshotai/kimi-k2", + }, } as const satisfies Record; export type KnownModelKey = keyof typeof MODEL_DEFINITIONS; diff --git a/src/common/types/thinking.ts b/src/common/types/thinking.ts index f8139247c4..09d8b57632 100644 --- a/src/common/types/thinking.ts +++ b/src/common/types/thinking.ts @@ -304,6 +304,15 @@ export function openaiSupportsProMode(modelString: string): boolean { return isGpt56FamilyModel(modelString); } +/** + * Kimi K3 (Moonshot AI via OpenRouter) always reasons, and OpenRouter currently + * exposes only its max (default) reasoning effort; the thinking policy and the + * OpenRouter provider-options branch both key off this predicate. + */ +export function isKimiK3Model(modelString: string): boolean { + return stripModelProviderPrefixes(modelString) === "kimi-k3"; +} + /** * Whether the given Anthropic model rejects `thinking: { type: "disabled" }`. * diff --git a/src/common/utils/ai/modelCapabilities.test.ts b/src/common/utils/ai/modelCapabilities.test.ts index ff06345327..d5492e9585 100644 --- a/src/common/utils/ai/modelCapabilities.test.ts +++ b/src/common/utils/ai/modelCapabilities.test.ts @@ -33,6 +33,13 @@ describe("getModelCapabilities", () => { expect(caps).not.toBeNull(); }); + it("reports image support for Kimi K3 via the OpenRouter gateway id", () => { + const caps = getModelCapabilities("openrouter:moonshotai/kimi-k3"); + expect(caps).not.toBeNull(); + expect(caps?.supportsVision).toBe(true); + expect(caps?.supportsPdfInput).toBe(false); + }); + it("infers PDF support for OpenAI vision models when models-extra omits the flag", () => { const caps = getModelCapabilities("openai:gpt-5.5"); expect(caps).not.toBeNull(); diff --git a/src/common/utils/ai/providerOptions.test.ts b/src/common/utils/ai/providerOptions.test.ts index 64cb6ffdf4..dcf9eba0de 100644 --- a/src/common/utils/ai/providerOptions.test.ts +++ b/src/common/utils/ai/providerOptions.test.ts @@ -1564,6 +1564,34 @@ describe("buildProviderOptions - Google", () => { }); }); +describe("buildProviderOptions - OpenRouter", () => { + test("enables reasoning without an effort override for Kimi K3", () => { + // K3 accepts only its default (max) reasoning effort, so no effort value is sent. + expect(buildProviderOptions("openrouter:moonshotai/kimi-k3", "max")).toEqual({ + openrouter: { + reasoning: { + enabled: true, + exclude: false, + }, + }, + }); + }); + + test("maps thinking levels to effort for other OpenRouter reasoning models", () => { + expect(buildProviderOptions("openrouter:z-ai/glm-4.6", "medium")).toEqual({ + openrouter: { + reasoning: { + enabled: true, + effort: "medium", + exclude: false, + }, + }, + }); + + expect(buildProviderOptions("openrouter:z-ai/glm-4.6", "off")).toEqual({}); + }); +}); + describe("buildRequestHeaders", () => { for (const { name, model, options, expected } of [ { diff --git a/src/common/utils/ai/providerOptions.ts b/src/common/utils/ai/providerOptions.ts index 77c7882037..b5cfa3e3f2 100644 --- a/src/common/utils/ai/providerOptions.ts +++ b/src/common/utils/ai/providerOptions.ts @@ -22,6 +22,7 @@ import { ANTHROPIC_THINKING_BUDGETS, GEMINI_THINKING_BUDGETS, getOpenAIReasoningEffort, + isKimiK3Model, openaiSupportsProMode, OPENROUTER_REASONING_EFFORT, } from "@/common/types/thinking"; @@ -474,7 +475,11 @@ export function buildProviderOptions( // Build OpenRouter-specific options if (formatProvider === "openrouter") { - const reasoningEffort = OPENROUTER_REASONING_EFFORT[effectiveThinking]; + // Kimi K3 always reasons and OpenRouter currently accepts only its default (max) + // reasoning effort, so enable reasoning without an effort override instead of + // sending a low/medium/high value the model does not support. + const kimiK3 = isKimiK3Model(capabilityModel); + const reasoningEffort = kimiK3 ? undefined : OPENROUTER_REASONING_EFFORT[effectiveThinking]; log.debug("buildProviderOptions: OpenRouter config", { reasoningEffort, @@ -482,12 +487,12 @@ export function buildProviderOptions( }); // Only add reasoning config if thinking is enabled - if (reasoningEffort) { + if (kimiK3 || reasoningEffort) { const options = { openrouter: { reasoning: { enabled: true, - effort: reasoningEffort, + ...(reasoningEffort ? { effort: reasoningEffort } : {}), // Don't exclude reasoning content - we want to display it in the UI exclude: false, }, diff --git a/src/common/utils/thinking/policy.test.ts b/src/common/utils/thinking/policy.test.ts index 08abefeb3d..7f76f2d763 100644 --- a/src/common/utils/thinking/policy.test.ts +++ b/src/common/utils/thinking/policy.test.ts @@ -608,6 +608,17 @@ describe("getThinkingPolicyForModel", () => { ]); }); + test("returns a fixed max policy for Kimi K3 behind OpenRouter", () => { + expect(getThinkingPolicyForModel("openrouter:moonshotai/kimi-k3")).toEqual(["max"]); + // Variant ids must not inherit the fixed K3 policy. + expect(getThinkingPolicyForModel("openrouter:moonshotai/kimi-k3-turbo")).toEqual([ + "off", + "low", + "medium", + "high", + ]); + }); + test("returns off/low/medium/high for non-preview Gemini 3 Flash IDs", () => { for (const model of ["google:gemini-3-flash", "google:gemini-3-flash-001"]) { expect(getThinkingPolicyForModel(model)).toEqual(["off", "low", "medium", "high"]); diff --git a/src/common/utils/thinking/policy.ts b/src/common/utils/thinking/policy.ts index efe58b78ce..0fd2b2662a 100644 --- a/src/common/utils/thinking/policy.ts +++ b/src/common/utils/thinking/policy.ts @@ -19,6 +19,7 @@ import { THINKING_LEVEL_OFF, anthropicRejectsDisabledThinking, anthropicSupportsNativeXhigh, + isKimiK3Model, openaiSupportsNativeMaxEffort, stripModelProviderPrefixes, type ThinkingLevel, @@ -168,6 +169,12 @@ function getExplicitThinkingPolicy(modelString: string): ThinkingPolicy | null { return ["low", "high"]; } + // Kimi K3 always reasons and OpenRouter currently exposes only the max (default) + // reasoning effort, so the policy is a fixed single level. + if (isKimiK3Model(withoutProviderNamespace)) { + return ["max"]; + } + // No explicit reasoning rule matched. return null; } diff --git a/src/common/utils/tokens/modelStats.test.ts b/src/common/utils/tokens/modelStats.test.ts index c91ae74f95..de4103c64e 100644 --- a/src/common/utils/tokens/modelStats.test.ts +++ b/src/common/utils/tokens/modelStats.test.ts @@ -145,6 +145,15 @@ describe("getModelStats", () => { expect(flash.cache_read_input_token_cost).toBe(0.000000014); }); + test("resolves Kimi K3 pricing and limits via the OpenRouter gateway id", () => { + const stats = expectStats("openrouter:moonshotai/kimi-k3"); + expect(stats.max_input_tokens).toBe(1_048_576); + expect(stats.max_output_tokens).toBe(131_072); + expect(stats.input_cost_per_token).toBe(0.000003); + expect(stats.output_cost_per_token).toBe(0.000015); + expect(stats.cache_read_input_token_cost).toBe(0.0000003); + }); + test("resolves the default image generation model pricing", () => { const stats = expectStats(DEFAULT_IMAGE_MODEL); diff --git a/src/common/utils/tokens/models-extra.ts b/src/common/utils/tokens/models-extra.ts index 59b81b5b16..a54b8c440f 100644 --- a/src/common/utils/tokens/models-extra.ts +++ b/src/common/utils/tokens/models-extra.ts @@ -543,6 +543,22 @@ export const modelsExtra: Record = { supports_response_schema: true, }, + // Kimi K3 via OpenRouter - released July 16, 2026. 1M context, 128K max output, + // text+image input (per the OpenRouter model page). + "openrouter/moonshotai/kimi-k3": { + max_input_tokens: 1048576, + max_output_tokens: 131072, + input_cost_per_token: 0.000003, // $3 per million input tokens + output_cost_per_token: 0.000015, // $15 per million output tokens + cache_read_input_token_cost: 0.0000003, // $0.30 per million cached input tokens + litellm_provider: "openrouter", + mode: "chat", + supports_function_calling: true, + supports_vision: true, + supports_reasoning: true, + supports_response_schema: true, + }, + // GPT-5.1-Codex-Max - Extended reasoning model with xhigh support // Same pricing as gpt-5.1-codex: $1.25/M input, $10/M output // Supports 5 reasoning levels: off, low, medium, high, xhigh diff --git a/src/node/services/agentSkills/builtInSkillContent.generated.ts b/src/node/services/agentSkills/builtInSkillContent.generated.ts index ee9937a53f..cd25b0be45 100644 --- a/src/node/services/agentSkills/builtInSkillContent.generated.ts +++ b/src/node/services/agentSkills/builtInSkillContent.generated.ts @@ -3188,6 +3188,7 @@ export const BUILTIN_SKILL_FILES: Record> = { "| Grok Code Fast 1 | xai:grok-code-fast-1 | `grok-code` | |", "| DeepSeek V4 Pro | deepseek:deepseek-v4-pro | `deepseek`, `deepseek-pro`, `deepseek-v4`, `deepseek-v4-pro` | |", "| DeepSeek V4 Flash | deepseek:deepseek-v4-flash | `deepseek-flash`, `deepseek-v4-flash` | |", + "| Kimi K3 | openrouter:moonshotai/kimi-k3 | `kimi`, `k3`, `kimi-k3` | |", "", "{/* END KNOWN_MODELS_TABLE */}", "", From 8e4a51ad6b813763aea9e9fc245598af791df94a Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:05:18 +0000 Subject: [PATCH 2/4] fix: send explicit max reasoning effort for Kimi K3 (Codex review) --- src/common/types/thinking.ts | 6 +++--- src/common/utils/ai/providerOptions.test.ts | 6 ++++-- src/common/utils/ai/providerOptions.ts | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/common/types/thinking.ts b/src/common/types/thinking.ts index 09d8b57632..79b0772e49 100644 --- a/src/common/types/thinking.ts +++ b/src/common/types/thinking.ts @@ -305,9 +305,9 @@ export function openaiSupportsProMode(modelString: string): boolean { } /** - * Kimi K3 (Moonshot AI via OpenRouter) always reasons, and OpenRouter currently - * exposes only its max (default) reasoning effort; the thinking policy and the - * OpenRouter provider-options branch both key off this predicate. + * Kimi K3 (Moonshot AI via OpenRouter) always reasons and supports only the max + * reasoning effort; the thinking policy and the OpenRouter provider-options + * branch both key off this predicate. */ export function isKimiK3Model(modelString: string): boolean { return stripModelProviderPrefixes(modelString) === "kimi-k3"; diff --git a/src/common/utils/ai/providerOptions.test.ts b/src/common/utils/ai/providerOptions.test.ts index dcf9eba0de..9c4600667d 100644 --- a/src/common/utils/ai/providerOptions.test.ts +++ b/src/common/utils/ai/providerOptions.test.ts @@ -1565,12 +1565,14 @@ describe("buildProviderOptions - Google", () => { }); describe("buildProviderOptions - OpenRouter", () => { - test("enables reasoning without an effort override for Kimi K3", () => { - // K3 accepts only its default (max) reasoning effort, so no effort value is sent. + test("sends the explicit max effort for Kimi K3", () => { + // `enabled: true` alone falls back to OpenRouter's default (medium) effort, + // which K3 does not support, so the effort must be sent explicitly. expect(buildProviderOptions("openrouter:moonshotai/kimi-k3", "max")).toEqual({ openrouter: { reasoning: { enabled: true, + effort: "max", exclude: false, }, }, diff --git a/src/common/utils/ai/providerOptions.ts b/src/common/utils/ai/providerOptions.ts index b5cfa3e3f2..fb09d41b05 100644 --- a/src/common/utils/ai/providerOptions.ts +++ b/src/common/utils/ai/providerOptions.ts @@ -52,7 +52,9 @@ interface OpenRouterReasoningOptions { reasoning?: { enabled?: boolean; exclude?: boolean; - effort?: "low" | "medium" | "high"; + // "max" is not in OpenRouter's generic low/medium/high set; it is the only + // effort Kimi K3 accepts (model metadata supported_efforts: ["max"]). + effort?: "low" | "medium" | "high" | "max"; }; } @@ -475,11 +477,12 @@ export function buildProviderOptions( // Build OpenRouter-specific options if (formatProvider === "openrouter") { - // Kimi K3 always reasons and OpenRouter currently accepts only its default (max) - // reasoning effort, so enable reasoning without an effort override instead of - // sending a low/medium/high value the model does not support. - const kimiK3 = isKimiK3Model(capabilityModel); - const reasoningEffort = kimiK3 ? undefined : OPENROUTER_REASONING_EFFORT[effectiveThinking]; + // Kimi K3 always reasons and supports only the max reasoning effort. Send it + // explicitly: `enabled: true` alone falls back to OpenRouter's default (medium) + // effort, which the model does not support. + const reasoningEffort = isKimiK3Model(capabilityModel) + ? "max" + : OPENROUTER_REASONING_EFFORT[effectiveThinking]; log.debug("buildProviderOptions: OpenRouter config", { reasoningEffort, @@ -487,12 +490,12 @@ export function buildProviderOptions( }); // Only add reasoning config if thinking is enabled - if (kimiK3 || reasoningEffort) { + if (reasoningEffort) { const options = { openrouter: { reasoning: { enabled: true, - ...(reasoningEffort ? { effort: reasoningEffort } : {}), + effort: reasoningEffort, // Don't exclude reasoning content - we want to display it in the UI exclude: false, }, From 165664061c68f5931261e9d17e792fc1bb5e1966 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:34:32 +0000 Subject: [PATCH 3/4] feat: switch Kimi K3 to a first-class Moonshot AI direct provider --- bun.lock | 7 +++++ docs/config/models.mdx | 2 +- docs/config/providers.mdx | 2 ++ package.json | 1 + scripts/gen_docs.ts | 1 + src/browser/assets/icons/moonshotai.svg | 4 +++ .../ProjectSidebar/ProjectSidebar.test.tsx | 1 + .../components/ProviderIcon/ProviderIcon.tsx | 2 ++ .../Settings/Sections/ProvidersSection.tsx | 1 + src/cli/run.ts | 2 +- src/common/config/schemas/providersConfig.ts | 3 +++ src/common/constants/knownModels.test.ts | 10 +++---- src/common/constants/knownModels.ts | 8 +++--- src/common/constants/providers.ts | 10 ++++++- src/common/types/thinking.ts | 6 ++--- src/common/utils/ai/modelCapabilities.test.ts | 12 +++++---- src/common/utils/ai/providerOptions.test.ts | 27 +++++++++++++++++-- src/common/utils/ai/providerOptions.ts | 24 +++++++++++++++++ src/common/utils/thinking/policy.test.ts | 5 ++-- src/common/utils/thinking/policy.ts | 4 +-- src/common/utils/tokens/modelStats.test.ts | 7 +++-- src/common/utils/tokens/models-extra.ts | 8 +++--- .../builtInSkillContent.generated.ts | 4 ++- src/node/utils/providerRequirements.ts | 3 +++ 24 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 src/browser/assets/icons/moonshotai.svg diff --git a/bun.lock b/bun.lock index 72f3cc5683..9306bc5cbb 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ "@ai-sdk/deepseek": "^3.0.7", "@ai-sdk/google": "^4.0.11", "@ai-sdk/mcp": "^2.0.10", + "@ai-sdk/moonshotai": "^3.0.15", "@ai-sdk/openai": "^4.0.11", "@ai-sdk/openai-compatible": "^3.0.7", "@ai-sdk/xai": "^4.0.10", @@ -234,6 +235,8 @@ "@ai-sdk/mcp": ["@ai-sdk/mcp@2.0.10", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.7", "pkce-challenge": "^5.0.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LP47CEk3e6BwCqXamvv4nKbgOWQ5z5T/qS/J2fola5h98KTy7JT7I1yw5LKtB89MZBLviYmEh98Ep4ovHOz/vA=="], + "@ai-sdk/moonshotai": ["@ai-sdk/moonshotai@3.0.15", "", { "dependencies": { "@ai-sdk/openai-compatible": "3.0.12", "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.11" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-R4+UEk1f1+lare2dQ1xK1vOJLXFmKo2wZ/d1wXTCZGOsls2I4KXEgl6oq1rB2PRT6NkvA5DK52fMA9DaeQZoYQ=="], + "@ai-sdk/openai": ["@ai-sdk/openai@4.0.11", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2PLnVPBsdJusgquPVYgPWd3ox4lbUFkp7DVUSmM0lQ4VISQoNxdgo6TvFAHgwQn9wJ/ckVYVRLU7+BvHc7T8ww=="], "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-y8RVpm8BZKNHDxlbNVt/GYmoBBqLx10m4d7bOzOnRD8V2aP3Cr6/g/bfubR4EkbW1ySrnPr62svZIeND2fdp3Q=="], @@ -3788,6 +3791,10 @@ "@ai-sdk/amazon-bedrock/@ai-sdk/openai": ["@ai-sdk/openai@4.0.10", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-K2t3JAshgw3KQ4kf2mEUl2gLCdZVbh1b1+G1lIc6+6JJjrZsp+ErOqqs2a29+hR6UkLI22vU//24bgA61eAJ6g=="], + "@ai-sdk/moonshotai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@3.0.12", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.11" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tN9BUb4jUGjqtbRPUsziLxASfogLNICcq/Qrwr55vpnzKvprV6Ukl8ynED2lZaNrAHU5U4zlGnyU/iuYrjQK6g=="], + + "@ai-sdk/moonshotai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.11", "", { "dependencies": { "@ai-sdk/provider": "4.0.3", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7/96wE+ZsKB35iS9ASyllrE4Ym/EolXEB7AkuJ5FI++fmS85BVTAs77890C+1Z2jwHfBKjBQSBmsliOsAh0iFQ=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], diff --git a/docs/config/models.mdx b/docs/config/models.mdx index 06c2dd1ba5..30a7748217 100644 --- a/docs/config/models.mdx +++ b/docs/config/models.mdx @@ -34,7 +34,7 @@ Mux ships with curated models kept up to date with the frontier. Use any custom | Grok Code Fast 1 | xai:grok-code-fast-1 | `grok-code` | | | DeepSeek V4 Pro | deepseek:deepseek-v4-pro | `deepseek`, `deepseek-pro`, `deepseek-v4`, `deepseek-v4-pro` | | | DeepSeek V4 Flash | deepseek:deepseek-v4-flash | `deepseek-flash`, `deepseek-v4-flash` | | -| Kimi K3 | openrouter:moonshotai/kimi-k3 | `kimi`, `k3`, `kimi-k3` | | +| Kimi K3 | moonshotai:kimi-k3 | `kimi`, `k3`, `kimi-k3` | | {/* END KNOWN_MODELS_TABLE */} diff --git a/docs/config/providers.mdx b/docs/config/providers.mdx index 435e543874..404baf546d 100644 --- a/docs/config/providers.mdx +++ b/docs/config/providers.mdx @@ -23,6 +23,7 @@ Most providers only need an API key. The UI handles validation and shows which p | **Google** | Gemini Pro, Flash | [aistudio.google.com](https://aistudio.google.com/) | | **xAI** | Grok | [console.x.ai](https://console.x.ai/) | | **DeepSeek** | DeepSeek Chat, Reasoner | [platform.deepseek.com](https://platform.deepseek.com/) | +| **Moonshot AI** | Kimi K3 | [platform.moonshot.ai](https://platform.moonshot.ai/) | | **OpenRouter** | 300+ models | [openrouter.ai](https://openrouter.ai/) | | **Ollama** | Local models | [ollama.com](https://ollama.com/) (no key needed) | | **Bedrock** | Claude via AWS | AWS Console | @@ -42,6 +43,7 @@ Providers also read from environment variables as fallback: | xAI | `XAI_API_KEY` | | OpenRouter | `OPENROUTER_API_KEY` | | DeepSeek | `DEEPSEEK_API_KEY` | +| Moonshot AI | `MOONSHOT_API_KEY` | | github-copilot | `GITHUB_COPILOT_TOKEN` | | Bedrock | `AWS_REGION` (credentials via AWS SDK chain) | diff --git a/package.json b/package.json index 16b7102d8d..f4be91c0e8 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@ai-sdk/deepseek": "^3.0.7", "@ai-sdk/google": "^4.0.11", "@ai-sdk/mcp": "^2.0.10", + "@ai-sdk/moonshotai": "^3.0.15", "@ai-sdk/openai": "^4.0.11", "@ai-sdk/openai-compatible": "^3.0.7", "@ai-sdk/xai": "^4.0.10", diff --git a/scripts/gen_docs.ts b/scripts/gen_docs.ts index ed914eb56b..c33024cad5 100644 --- a/scripts/gen_docs.ts +++ b/scripts/gen_docs.ts @@ -369,6 +369,7 @@ const PROVIDER_DISPLAY_NAMES: Record = { google: "Google", xai: "xAI", deepseek: "DeepSeek", + moonshotai: "Moonshot AI", openrouter: "OpenRouter", bedrock: "Bedrock", }; diff --git a/src/browser/assets/icons/moonshotai.svg b/src/browser/assets/icons/moonshotai.svg new file mode 100644 index 0000000000..f57c5bd0d4 --- /dev/null +++ b/src/browser/assets/icons/moonshotai.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/browser/components/ProjectSidebar/ProjectSidebar.test.tsx b/src/browser/components/ProjectSidebar/ProjectSidebar.test.tsx index 3cd5d6c8f7..13a19a952b 100644 --- a/src/browser/components/ProjectSidebar/ProjectSidebar.test.tsx +++ b/src/browser/components/ProjectSidebar/ProjectSidebar.test.tsx @@ -60,6 +60,7 @@ function installProviderIconSvgMocks() { "@/browser/assets/icons/openrouter.svg?react", "@/browser/assets/icons/ollama.svg?react", "@/browser/assets/icons/deepseek.svg?react", + "@/browser/assets/icons/moonshotai.svg?react", "@/browser/assets/icons/aws.svg?react", "@/browser/assets/icons/github.svg?react", ] as const; diff --git a/src/browser/components/ProviderIcon/ProviderIcon.tsx b/src/browser/components/ProviderIcon/ProviderIcon.tsx index 221f0c5de6..12fb039d71 100644 --- a/src/browser/components/ProviderIcon/ProviderIcon.tsx +++ b/src/browser/components/ProviderIcon/ProviderIcon.tsx @@ -7,6 +7,7 @@ import XAIIcon from "@/browser/assets/icons/xai.svg?react"; import OpenRouterIcon from "@/browser/assets/icons/openrouter.svg?react"; import OllamaIcon from "@/browser/assets/icons/ollama.svg?react"; import DeepSeekIcon from "@/browser/assets/icons/deepseek.svg?react"; +import MoonshotAIIcon from "@/browser/assets/icons/moonshotai.svg?react"; import AWSIcon from "@/browser/assets/icons/aws.svg?react"; import GitHubIcon from "@/browser/assets/icons/github.svg?react"; import { GatewayIcon } from "@/browser/components/icons/GatewayIcon/GatewayIcon"; @@ -27,6 +28,7 @@ const PROVIDER_ICONS: Partial> = { google: GoogleIcon, xai: XAIIcon, deepseek: DeepSeekIcon, + moonshotai: MoonshotAIIcon, openrouter: OpenRouterIcon, bedrock: AWSIcon, ollama: OllamaIcon, diff --git a/src/browser/features/Settings/Sections/ProvidersSection.tsx b/src/browser/features/Settings/Sections/ProvidersSection.tsx index 4bf8560d72..008cfd5f7a 100644 --- a/src/browser/features/Settings/Sections/ProvidersSection.tsx +++ b/src/browser/features/Settings/Sections/ProvidersSection.tsx @@ -243,6 +243,7 @@ const PROVIDER_KEY_URLS: Partial> = { google: "https://aistudio.google.com/app/apikey", xai: "https://console.x.ai/team/default/api-keys", deepseek: "https://platform.deepseek.com/api_keys", + moonshotai: "https://platform.moonshot.ai/console/api-keys", openrouter: "https://openrouter.ai/settings/keys", // bedrock: AWS credential chain, no simple key URL // ollama: local service, no key needed diff --git a/src/cli/run.ts b/src/cli/run.ts index a4af0608e2..fa49ccd66b 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -540,7 +540,7 @@ async function main(): Promise { config.saveProvidersConfig(providersFromEnv); } else { throw new Error( - "No provider credentials found. Configure providers.jsonc or set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY." + "No provider credentials found. Configure providers.jsonc or set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY / MOONSHOT_API_KEY." ); } } diff --git a/src/common/config/schemas/providersConfig.ts b/src/common/config/schemas/providersConfig.ts index beed8dfb43..30aaaf3114 100644 --- a/src/common/config/schemas/providersConfig.ts +++ b/src/common/config/schemas/providersConfig.ts @@ -68,6 +68,7 @@ export const MuxGatewayProviderConfigSchema = BaseProviderConfigSchema.extend({ export const GoogleProviderConfigSchema = BaseProviderConfigSchema; export const DeepSeekProviderConfigSchema = BaseProviderConfigSchema; +export const MoonshotAIProviderConfigSchema = BaseProviderConfigSchema; export const OllamaProviderConfigSchema = BaseProviderConfigSchema; export const GitHubCopilotProviderConfigSchema = BaseProviderConfigSchema; @@ -81,6 +82,7 @@ export const ProvidersConfigSchema = z "mux-gateway": MuxGatewayProviderConfigSchema.optional(), google: GoogleProviderConfigSchema.optional(), deepseek: DeepSeekProviderConfigSchema.optional(), + moonshotai: MoonshotAIProviderConfigSchema.optional(), ollama: OllamaProviderConfigSchema.optional(), "github-copilot": GitHubCopilotProviderConfigSchema.optional(), }) @@ -95,6 +97,7 @@ export type XAIProviderConfig = z.infer; export type MuxGatewayProviderConfig = z.infer; export type GoogleProviderConfig = z.infer; export type DeepSeekProviderConfig = z.infer; +export type MoonshotAIProviderConfig = z.infer; export type OllamaProviderConfig = z.infer; export type GitHubCopilotProviderConfig = z.infer; diff --git a/src/common/constants/knownModels.test.ts b/src/common/constants/knownModels.test.ts index 092a0b3725..4f413d2d1b 100644 --- a/src/common/constants/knownModels.test.ts +++ b/src/common/constants/knownModels.test.ts @@ -14,9 +14,9 @@ describe("Known Models Integration", () => { for (const [key, model] of Object.entries(KNOWN_MODELS)) { const modelId = model.providerModelId; - // xAI and OpenRouter models are provider-prefixed in token metadata. + // xAI and Moonshot models are provider-prefixed in token metadata. const lookupKey = - model.provider === "xai" || model.provider === "openrouter" + model.provider === "xai" || model.provider === "moonshotai" ? `${model.provider}/${modelId}` : modelId; if (!(lookupKey in modelsJson) && !(lookupKey in modelsExtra) && !(modelId in modelsExtra)) { @@ -46,9 +46,9 @@ describe("Known Models Integration", () => { expect(MODEL_ABBREVIATIONS["gpt-5.5"]).toBeUndefined(); }); - test("kimi aliases resolve to the OpenRouter-scoped Kimi K3 model", () => { - expect(MODEL_ABBREVIATIONS.kimi).toBe("openrouter:moonshotai/kimi-k3"); - expect(MODEL_ABBREVIATIONS.k3).toBe("openrouter:moonshotai/kimi-k3"); + test("kimi aliases resolve to the direct Moonshot Kimi K3 model", () => { + expect(MODEL_ABBREVIATIONS.kimi).toBe("moonshotai:kimi-k3"); + expect(MODEL_ABBREVIATIONS.k3).toBe("moonshotai:kimi-k3"); }); test("known model ids and aliases stay unique across the curated registry", () => { diff --git a/src/common/constants/knownModels.ts b/src/common/constants/knownModels.ts index 1542a311cf..5d03d45aaf 100644 --- a/src/common/constants/knownModels.ts +++ b/src/common/constants/knownModels.ts @@ -4,7 +4,7 @@ import { formatModelDisplayName } from "../utils/ai/modelDisplay"; -type ModelProvider = "anthropic" | "openai" | "google" | "xai" | "deepseek" | "openrouter"; +type ModelProvider = "anthropic" | "openai" | "google" | "xai" | "deepseek" | "moonshotai"; interface KnownModelDefinition { /** Provider identifier used by SDK factories */ @@ -212,11 +212,11 @@ const MODEL_DEFINITIONS = { tokenizerOverride: "deepseek/deepseek-v3.1", }, // Kimi K3 - Moonshot AI's flagship open-weight multimodal reasoning model (released - // July 16, 2026; 1M context, text+image input; $3/M in, $15/M out via OpenRouter). + // July 16, 2026; 1M context, text+image input; $3/M in, $15/M out). // Bare `kimi` alias tracks Moonshot's flagship per the shortest-alias convention. KIMI_K3: { - provider: "openrouter", - providerModelId: "moonshotai/kimi-k3", + provider: "moonshotai", + providerModelId: "kimi-k3", aliases: ["kimi", "k3", "kimi-k3"], // K3's tokenizer isn't published in ai-tokenizer yet; reuse Kimi K2 (the newest // Moonshot tokenizer available) for approximate counting. diff --git a/src/common/constants/providers.ts b/src/common/constants/providers.ts index 950a6ca204..37a1bbe080 100644 --- a/src/common/constants/providers.ts +++ b/src/common/constants/providers.ts @@ -20,6 +20,7 @@ export type ProviderName = | "google" | "xai" | "deepseek" + | "moonshotai" | "openrouter" | "github-copilot" | "bedrock" @@ -128,13 +129,20 @@ export const PROVIDER_DEFINITIONS = { requiresApiKey: true, kind: "direct", }, + moonshotai: { + displayName: "Moonshot AI", + import: () => import("@ai-sdk/moonshotai"), + factoryName: "createMoonshotAI", + requiresApiKey: true, + kind: "direct", + }, openrouter: { displayName: "OpenRouter", import: () => import("@openrouter/ai-sdk-provider"), factoryName: "createOpenRouter", requiresApiKey: true, kind: "gateway", - routes: ["anthropic", "openai", "google", "xai", "deepseek"], + routes: ["anthropic", "openai", "google", "xai", "deepseek", "moonshotai"], passthrough: false, toGatewayModelId: toSlashSeparatedGatewayModelId, fromGatewayModelId: fromSlashSeparatedGatewayModelId, diff --git a/src/common/types/thinking.ts b/src/common/types/thinking.ts index 79b0772e49..53c08b845e 100644 --- a/src/common/types/thinking.ts +++ b/src/common/types/thinking.ts @@ -305,9 +305,9 @@ export function openaiSupportsProMode(modelString: string): boolean { } /** - * Kimi K3 (Moonshot AI via OpenRouter) always reasons and supports only the max - * reasoning effort; the thinking policy and the OpenRouter provider-options - * branch both key off this predicate. + * Kimi K3 (Moonshot AI) always reasons and supports only the max reasoning + * effort; the thinking policy and the Moonshot/OpenRouter provider-options + * branches key off this predicate. */ export function isKimiK3Model(modelString: string): boolean { return stripModelProviderPrefixes(modelString) === "kimi-k3"; diff --git a/src/common/utils/ai/modelCapabilities.test.ts b/src/common/utils/ai/modelCapabilities.test.ts index d5492e9585..d95811ab72 100644 --- a/src/common/utils/ai/modelCapabilities.test.ts +++ b/src/common/utils/ai/modelCapabilities.test.ts @@ -33,11 +33,13 @@ describe("getModelCapabilities", () => { expect(caps).not.toBeNull(); }); - it("reports image support for Kimi K3 via the OpenRouter gateway id", () => { - const caps = getModelCapabilities("openrouter:moonshotai/kimi-k3"); - expect(caps).not.toBeNull(); - expect(caps?.supportsVision).toBe(true); - expect(caps?.supportsPdfInput).toBe(false); + it("reports image support for Kimi K3 via direct and gateway ids", () => { + for (const model of ["moonshotai:kimi-k3", "openrouter:moonshotai/kimi-k3"]) { + const caps = getModelCapabilities(model); + expect(caps).not.toBeNull(); + expect(caps?.supportsVision).toBe(true); + expect(caps?.supportsPdfInput).toBe(false); + } }); it("infers PDF support for OpenAI vision models when models-extra omits the flag", () => { diff --git a/src/common/utils/ai/providerOptions.test.ts b/src/common/utils/ai/providerOptions.test.ts index 9c4600667d..ec52ac363a 100644 --- a/src/common/utils/ai/providerOptions.test.ts +++ b/src/common/utils/ai/providerOptions.test.ts @@ -1564,11 +1564,34 @@ describe("buildProviderOptions - Google", () => { }); }); +describe("buildProviderOptions - Moonshot", () => { + test("sends the explicit max reasoning effort for direct Kimi K3", () => { + expect(buildProviderOptions("moonshotai:kimi-k3", "max")).toEqual({ + moonshotai: { reasoningEffort: "max" }, + }); + }); + + test("emits no provider options for other Moonshot models", () => { + expect(buildProviderOptions("moonshotai:kimi-k2.5", "medium")).toEqual({}); + }); +}); + describe("buildProviderOptions - OpenRouter", () => { - test("sends the explicit max effort for Kimi K3", () => { + test("sends the explicit max effort for OpenRouter-routed Kimi K3", () => { // `enabled: true` alone falls back to OpenRouter's default (medium) effort, // which K3 does not support, so the effort must be sent explicitly. - expect(buildProviderOptions("openrouter:moonshotai/kimi-k3", "max")).toEqual({ + const result = buildProviderOptions( + "moonshotai:kimi-k3", + "max", + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + "openrouter" + ); + expect(result).toEqual({ openrouter: { reasoning: { enabled: true, diff --git a/src/common/utils/ai/providerOptions.ts b/src/common/utils/ai/providerOptions.ts index fb09d41b05..fc4564e879 100644 --- a/src/common/utils/ai/providerOptions.ts +++ b/src/common/utils/ai/providerOptions.ts @@ -63,6 +63,15 @@ type OpenAICompatibleGatewayProviderOptions = Pick< "reasoningEffort" >; +/** + * Moonshot AI provider options (@ai-sdk/moonshotai). Kimi K3 accepts only the + * literal "max" reasoning effort, matching the SDK's typed options schema. + */ +interface MoonshotAIProviderOptions { + [key: string]: JSONValue | undefined; + reasoningEffort?: "max"; +} + /** * Provider-specific options structure for AI SDK */ @@ -71,6 +80,7 @@ type ProviderOptions = | { openai: OpenAIResponsesProviderOptions } | { google: GoogleGenerativeAIProviderOptions } | { openrouter: OpenRouterReasoningOptions } + | { moonshotai: MoonshotAIProviderOptions } | { xai: XaiProviderOptions } | { "github-copilot": OpenAICompatibleGatewayProviderOptions } | Record; // Empty object for unsupported providers @@ -475,6 +485,20 @@ export function buildProviderOptions( return options; } + // Build Moonshot-specific options + if (formatProvider === "moonshotai") { + // Kimi K3 always reasons and supports only the max reasoning effort. Send it + // explicitly rather than relying on the API default. + if (isKimiK3Model(capabilityModel)) { + const options = { + moonshotai: { reasoningEffort: "max" }, + } satisfies { moonshotai: MoonshotAIProviderOptions }; + log.debug("buildProviderOptions: Returning Moonshot options", options); + return options; + } + return {}; + } + // Build OpenRouter-specific options if (formatProvider === "openrouter") { // Kimi K3 always reasons and supports only the max reasoning effort. Send it diff --git a/src/common/utils/thinking/policy.test.ts b/src/common/utils/thinking/policy.test.ts index 7f76f2d763..42385bf276 100644 --- a/src/common/utils/thinking/policy.test.ts +++ b/src/common/utils/thinking/policy.test.ts @@ -608,10 +608,11 @@ describe("getThinkingPolicyForModel", () => { ]); }); - test("returns a fixed max policy for Kimi K3 behind OpenRouter", () => { + test("returns a fixed max policy for Kimi K3 (direct and via OpenRouter)", () => { + expect(getThinkingPolicyForModel("moonshotai:kimi-k3")).toEqual(["max"]); expect(getThinkingPolicyForModel("openrouter:moonshotai/kimi-k3")).toEqual(["max"]); // Variant ids must not inherit the fixed K3 policy. - expect(getThinkingPolicyForModel("openrouter:moonshotai/kimi-k3-turbo")).toEqual([ + expect(getThinkingPolicyForModel("moonshotai:kimi-k3-turbo")).toEqual([ "off", "low", "medium", diff --git a/src/common/utils/thinking/policy.ts b/src/common/utils/thinking/policy.ts index 0fd2b2662a..e564896c46 100644 --- a/src/common/utils/thinking/policy.ts +++ b/src/common/utils/thinking/policy.ts @@ -169,8 +169,8 @@ function getExplicitThinkingPolicy(modelString: string): ThinkingPolicy | null { return ["low", "high"]; } - // Kimi K3 always reasons and OpenRouter currently exposes only the max (default) - // reasoning effort, so the policy is a fixed single level. + // Kimi K3 always reasons and supports only the max reasoning effort, so the + // policy is a fixed single level. if (isKimiK3Model(withoutProviderNamespace)) { return ["max"]; } diff --git a/src/common/utils/tokens/modelStats.test.ts b/src/common/utils/tokens/modelStats.test.ts index de4103c64e..17e99ccb8b 100644 --- a/src/common/utils/tokens/modelStats.test.ts +++ b/src/common/utils/tokens/modelStats.test.ts @@ -145,13 +145,16 @@ describe("getModelStats", () => { expect(flash.cache_read_input_token_cost).toBe(0.000000014); }); - test("resolves Kimi K3 pricing and limits via the OpenRouter gateway id", () => { - const stats = expectStats("openrouter:moonshotai/kimi-k3"); + test("resolves Kimi K3 pricing and limits via direct and gateway forms", () => { + const stats = expectStats("moonshotai:kimi-k3"); expect(stats.max_input_tokens).toBe(1_048_576); expect(stats.max_output_tokens).toBe(131_072); expect(stats.input_cost_per_token).toBe(0.000003); expect(stats.output_cost_per_token).toBe(0.000015); expect(stats.cache_read_input_token_cost).toBe(0.0000003); + + // The legacy OpenRouter form canonicalizes back to the direct Moonshot entry. + expect(expectStats("openrouter:moonshotai/kimi-k3")).toEqual(stats); }); test("resolves the default image generation model pricing", () => { diff --git a/src/common/utils/tokens/models-extra.ts b/src/common/utils/tokens/models-extra.ts index a54b8c440f..01cb3cf43e 100644 --- a/src/common/utils/tokens/models-extra.ts +++ b/src/common/utils/tokens/models-extra.ts @@ -543,15 +543,15 @@ export const modelsExtra: Record = { supports_response_schema: true, }, - // Kimi K3 via OpenRouter - released July 16, 2026. 1M context, 128K max output, - // text+image input (per the OpenRouter model page). - "openrouter/moonshotai/kimi-k3": { + // Kimi K3 - released July 16, 2026. 1M context, 128K max output, text+image input. + // Keyed on the direct provider; OpenRouter-routed requests canonicalize to this id. + "moonshotai/kimi-k3": { max_input_tokens: 1048576, max_output_tokens: 131072, input_cost_per_token: 0.000003, // $3 per million input tokens output_cost_per_token: 0.000015, // $15 per million output tokens cache_read_input_token_cost: 0.0000003, // $0.30 per million cached input tokens - litellm_provider: "openrouter", + litellm_provider: "moonshotai", mode: "chat", supports_function_calling: true, supports_vision: true, diff --git a/src/node/services/agentSkills/builtInSkillContent.generated.ts b/src/node/services/agentSkills/builtInSkillContent.generated.ts index cd25b0be45..75044179f0 100644 --- a/src/node/services/agentSkills/builtInSkillContent.generated.ts +++ b/src/node/services/agentSkills/builtInSkillContent.generated.ts @@ -3188,7 +3188,7 @@ export const BUILTIN_SKILL_FILES: Record> = { "| Grok Code Fast 1 | xai:grok-code-fast-1 | `grok-code` | |", "| DeepSeek V4 Pro | deepseek:deepseek-v4-pro | `deepseek`, `deepseek-pro`, `deepseek-v4`, `deepseek-v4-pro` | |", "| DeepSeek V4 Flash | deepseek:deepseek-v4-flash | `deepseek-flash`, `deepseek-v4-flash` | |", - "| Kimi K3 | openrouter:moonshotai/kimi-k3 | `kimi`, `k3`, `kimi-k3` | |", + "| Kimi K3 | moonshotai:kimi-k3 | `kimi`, `k3`, `kimi-k3` | |", "", "{/* END KNOWN_MODELS_TABLE */}", "", @@ -3617,6 +3617,7 @@ export const BUILTIN_SKILL_FILES: Record> = { "| **Google** | Gemini Pro, Flash | [aistudio.google.com](https://aistudio.google.com/) |", "| **xAI** | Grok | [console.x.ai](https://console.x.ai/) |", "| **DeepSeek** | DeepSeek Chat, Reasoner | [platform.deepseek.com](https://platform.deepseek.com/) |", + "| **Moonshot AI** | Kimi K3 | [platform.moonshot.ai](https://platform.moonshot.ai/) |", "| **OpenRouter** | 300+ models | [openrouter.ai](https://openrouter.ai/) |", "| **Ollama** | Local models | [ollama.com](https://ollama.com/) (no key needed) |", "| **Bedrock** | Claude via AWS | AWS Console |", @@ -3636,6 +3637,7 @@ export const BUILTIN_SKILL_FILES: Record> = { "| xAI | `XAI_API_KEY` |", "| OpenRouter | `OPENROUTER_API_KEY` |", "| DeepSeek | `DEEPSEEK_API_KEY` |", + "| Moonshot AI | `MOONSHOT_API_KEY` |", "| github-copilot | `GITHUB_COPILOT_TOKEN` |", "| Bedrock | `AWS_REGION` (credentials via AWS SDK chain) |", "", diff --git a/src/node/utils/providerRequirements.ts b/src/node/utils/providerRequirements.ts index 2d74204c40..40d5e19ef9 100644 --- a/src/node/utils/providerRequirements.ts +++ b/src/node/utils/providerRequirements.ts @@ -65,6 +65,9 @@ export const PROVIDER_ENV_VARS: Partial< deepseek: { apiKey: ["DEEPSEEK_API_KEY"], }, + moonshotai: { + apiKey: ["MOONSHOT_API_KEY"], + }, "github-copilot": { apiKey: ["GITHUB_COPILOT_TOKEN"], }, From eef38f90f4fe3b9f97eb229a67b85d51743b82c4 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:45:01 +0000 Subject: [PATCH 4/4] ci: update flake offline cache hash for @ai-sdk/moonshotai --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 7ce2f87a80..3db1e58a37 100644 --- a/flake.nix +++ b/flake.nix @@ -83,7 +83,7 @@ outputHashMode = "recursive"; # Marker used by scripts/update_flake_hash.sh to update this hash in place. - outputHash = "sha256-PKc0nF1lp6yjPlveIhOc/Hp6DwFe3WgtfHyHTDQdUnQ="; # mux-offline-cache-hash + outputHash = "sha256-OzBNhFDq9TWcHNfEGI5SpOVvin9WVzi9SvsG34G267A="; # mux-offline-cache-hash }; configurePhase = ''