fix(providers): make all models available in kilocode provider (#32352)

* kilocode: dynamic model discovery, kilo/auto default, cooldown exemption

- Replace 9-model hardcoded catalog with dynamic discovery from
  GET /api/gateway/models (Venice-like pattern with static fallback)
- Default model changed from anthropic/claude-opus-4.6 to kilo/auto
  (smart routing model)
- Add createKilocodeWrapper for X-KILOCODE-FEATURE header injection
  and reasoning.effort handling (skip for kilo/auto)
- Add kilocode to cooldown-exempt providers (proxy like OpenRouter)
- Keep sync buildKilocodeProvider for onboarding, add async
  buildKilocodeProviderWithDiscovery for implicit provider resolution
- Per-token gateway pricing converted to per-1M-token for cost fields

* kilocode: skip reasoning injection for x-ai models, harden discovery loop

* fix(kilocode): keep valid discovered duplicates (openclaw#32352, thanks @pandemicsyn)

* refactor(proxy): normalize reasoning payload guards (openclaw#32352, thanks @pandemicsyn)

* chore(changelog): note kilocode hardening (openclaw#32352, thanks @pandemicsyn and @vincentkoc)

* chore(changelog): fix kilocode note format (openclaw#32352, thanks @pandemicsyn and @vincentkoc)

* test(kilocode): support auto-model override cases (openclaw#32352, thanks @pandemicsyn)

* Update CHANGELOG.md

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Florian Hines
2026-03-07 10:14:06 -06:00
committed by GitHub
parent 786ec21b5a
commit 33e7394861
15 changed files with 832 additions and 168 deletions

View File

@@ -56,8 +56,8 @@ function createKilocodeProvider() {
baseUrl: "https://api.kilo.ai/api/gateway/",
api: "openai-completions",
models: [
{ id: "anthropic/claude-opus-4.6", name: "Claude Opus 4.6" },
{ id: "minimax/minimax-m2.5:free", name: "MiniMax M2.5 (Free)" },
{ id: "kilo/auto", name: "Kilo Auto" },
{ id: "anthropic/claude-sonnet-4", name: "Claude Sonnet 4" },
],
};
}
@@ -67,7 +67,7 @@ function createApplyAuthChoiceConfig(includeMinimaxProvider = false) {
config: {
agents: {
defaults: {
model: { primary: "kilocode/anthropic/claude-opus-4.6" },
model: { primary: "kilocode/kilo/auto" },
},
},
models: {
@@ -92,7 +92,7 @@ async function runPromptAuthConfigWithAllowlist(includeMinimaxProvider = false)
mocks.promptAuthChoiceGrouped.mockResolvedValue("kilocode-api-key");
mocks.applyAuthChoice.mockResolvedValue(createApplyAuthChoiceConfig(includeMinimaxProvider));
mocks.promptModelAllowlist.mockResolvedValue({
models: ["kilocode/anthropic/claude-opus-4.6"],
models: ["kilocode/kilo/auto"],
});
return promptAuthConfig({}, makeRuntime(), noopPrompter);
@@ -102,19 +102,17 @@ describe("promptAuthConfig", () => {
it("keeps Kilo provider models while applying allowlist defaults", async () => {
const result = await runPromptAuthConfigWithAllowlist();
expect(result.models?.providers?.kilocode?.models?.map((model) => model.id)).toEqual([
"anthropic/claude-opus-4.6",
"minimax/minimax-m2.5:free",
]);
expect(Object.keys(result.agents?.defaults?.models ?? {})).toEqual([
"kilocode/anthropic/claude-opus-4.6",
"kilo/auto",
"anthropic/claude-sonnet-4",
]);
expect(Object.keys(result.agents?.defaults?.models ?? {})).toEqual(["kilocode/kilo/auto"]);
});
it("does not mutate provider model catalogs when allowlist is set", async () => {
const result = await runPromptAuthConfigWithAllowlist(true);
expect(result.models?.providers?.kilocode?.models?.map((model) => model.id)).toEqual([
"anthropic/claude-opus-4.6",
"minimax/minimax-m2.5:free",
"kilo/auto",
"anthropic/claude-sonnet-4",
]);
expect(result.models?.providers?.minimax?.models?.map((model) => model.id)).toEqual([
"MiniMax-M2.5",

View File

@@ -21,17 +21,7 @@ import {
} from "./onboard-auth.models.js";
const emptyCfg: OpenClawConfig = {};
const KILOCODE_MODEL_IDS = [
"anthropic/claude-opus-4.6",
"z-ai/glm-5:free",
"minimax/minimax-m2.5:free",
"anthropic/claude-sonnet-4.5",
"openai/gpt-5.2",
"google/gemini-3-pro-preview",
"google/gemini-3-flash-preview",
"x-ai/grok-code-fast-1",
"moonshotai/kimi-k2.5",
];
const KILOCODE_MODEL_IDS = ["kilo/auto"];
describe("Kilo Gateway provider config", () => {
describe("constants", () => {
@@ -40,11 +30,11 @@ describe("Kilo Gateway provider config", () => {
});
it("KILOCODE_DEFAULT_MODEL_REF includes provider prefix", () => {
expect(KILOCODE_DEFAULT_MODEL_REF).toBe("kilocode/anthropic/claude-opus-4.6");
expect(KILOCODE_DEFAULT_MODEL_REF).toBe("kilocode/kilo/auto");
});
it("KILOCODE_DEFAULT_MODEL_ID is anthropic/claude-opus-4.6", () => {
expect(KILOCODE_DEFAULT_MODEL_ID).toBe("anthropic/claude-opus-4.6");
it("KILOCODE_DEFAULT_MODEL_ID is kilo/auto", () => {
expect(KILOCODE_DEFAULT_MODEL_ID).toBe("kilo/auto");
});
});
@@ -52,7 +42,7 @@ describe("Kilo Gateway provider config", () => {
it("returns correct model shape", () => {
const model = buildKilocodeModelDefinition();
expect(model.id).toBe(KILOCODE_DEFAULT_MODEL_ID);
expect(model.name).toBe("Claude Opus 4.6");
expect(model.name).toBe("Kilo Auto");
expect(model.reasoning).toBe(true);
expect(model.input).toEqual(["text", "image"]);
expect(model.contextWindow).toBe(KILOCODE_DEFAULT_CONTEXT_WINDOW);