Diseño de integración del proveedor Kilo Gateway
Visión general
Este documento describe el diseño para integrar “Kilo Gateway” como proveedor de primera clase en OpenClaw, siguiendo el modelo de la implementación existente de OpenRouter. Kilo Gateway utiliza una API de completions compatible con OpenAI con una URL base diferente.
Decisiones de diseño
1. Nombre del proveedor
Recomendación: kilocode
Justificación:
- Coincide con el ejemplo de configuración del usuario (clave de proveedor
kilocode) - Consistente con los patrones de nombres de proveedores existentes (ej.
openrouter,opencode,moonshot) - Corto y fácil de recordar
- Evita confusión con términos genéricos como “kilo” o “gateway”
Alternativa considerada: kilo-gateway — rechazada porque los nombres con guiones son menos comunes en el código y kilocode es más conciso.
2. Referencia de modelo por defecto
Recomendación: kilocode/anthropic/claude-opus-4.6
Justificación:
- Basada en el ejemplo de configuración del usuario
- Claude Opus 4.5 es un modelo por defecto capaz
- La selección explícita de modelo evita depender del enrutamiento automático
3. Configuración de URL base
Recomendación: URL por defecto hardcodeada con posibilidad de sobreescritura en la configuración
- URL base por defecto:
https://api.kilo.ai/api/gateway/ - Configurable: Sí, mediante
models.providers.kilocode.baseUrl
Esto sigue el patrón usado por otros proveedores como Moonshot, Venice y Synthetic.
4. Escaneo de modelos
Recomendación: Sin endpoint dedicado de escaneo de modelos inicialmente
Justificación:
- Kilo Gateway actúa como proxy de OpenRouter, por lo que los modelos son dinámicos
- Los usuarios pueden configurar modelos manualmente en su configuración
- Si Kilo Gateway expone un endpoint
/modelsen el futuro, se puede agregar el escaneo
5. Manejo especial
Recomendación: Heredar el comportamiento de OpenRouter para modelos de Anthropic
Dado que Kilo Gateway actúa como proxy de OpenRouter, debe aplicarse el mismo manejo especial:
- Elegibilidad de TTL de caché para modelos
anthropic/* - Parámetros extra (cacheControlTtl) para modelos
anthropic/* - La política de transcripciones sigue los patrones de OpenRouter
Archivos a modificar
Gestión de credenciales central
1. src/commands/onboard-auth.credentials.ts
Agregar:
export const KILOCODE_DEFAULT_MODEL_REF = "kilocode/anthropic/claude-opus-4.6";
export async function setKilocodeApiKey(key: string, agentDir?: string) {
upsertAuthProfile({
profileId: "kilocode:default",
credential: {
type: "api_key",
provider: "kilocode",
key,
},
agentDir: resolveAuthAgentDir(agentDir),
});
}
2. src/agents/model-auth.ts
Agregar a envMap en resolveEnvApiKey():
const envMap: Record<string, string> = {
// ... entradas existentes
kilocode: "KILOCODE_API_KEY",
};
3. src/config/io.ts
Agregar a SHELL_ENV_EXPECTED_KEYS:
const SHELL_ENV_EXPECTED_KEYS = [
// ... entradas existentes
"KILOCODE_API_KEY",
];
Aplicación de configuración
4. src/commands/onboard-auth.config-core.ts
Agregar nuevas funciones:
export const KILOCODE_BASE_URL = "https://api.kilo.ai/api/gateway/";
export function applyKilocodeProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
const models = { ...cfg.agents?.defaults?.models };
models[KILOCODE_DEFAULT_MODEL_REF] = {
...models[KILOCODE_DEFAULT_MODEL_REF],
alias: models[KILOCODE_DEFAULT_MODEL_REF]?.alias ?? "Kilo Gateway",
};
const providers = { ...cfg.models?.providers };
const existingProvider = providers.kilocode;
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
string,
unknown
> as { apiKey?: string };
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
const normalizedApiKey = resolvedApiKey?.trim();
providers.kilocode = {
...existingProviderRest,
baseUrl: KILOCODE_BASE_URL,
api: "openai-completions",
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
};
return {
...cfg,
agents: {
...cfg.agents,
defaults: {
...cfg.agents?.defaults,
models,
},
},
models: {
mode: cfg.models?.mode ?? "merge",
providers,
},
};
}
export function applyKilocodeConfig(cfg: OpenClawConfig): OpenClawConfig {
const next = applyKilocodeProviderConfig(cfg);
const existingModel = next.agents?.defaults?.model;
return {
...next,
agents: {
...next.agents,
defaults: {
...next.agents?.defaults,
model: {
...(existingModel && "fallbacks" in (existingModel as Record<string, unknown>)
? {
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
}
: undefined),
primary: KILOCODE_DEFAULT_MODEL_REF,
},
},
},
};
}
Sistema de elección de autenticación
5. src/commands/onboard-types.ts
Agregar al tipo AuthChoice:
export type AuthChoice =
// ... opciones existentes
"kilocode-api-key";
// ...
Agregar a OnboardOptions:
export type OnboardOptions = {
// ... opciones existentes
kilocodeApiKey?: string;
// ...
};
6. src/commands/auth-choice-options.ts
Agregar a AuthChoiceGroupId:
export type AuthChoiceGroupId =
// ... grupos existentes
"kilocode";
// ...
Agregar a AUTH_CHOICE_GROUP_DEFS:
{
value: "kilocode",
label: "Kilo Gateway",
hint: "API key (OpenRouter-compatible)",
choices: ["kilocode-api-key"],
},
Agregar a buildAuthChoiceOptions():
options.push({
value: "kilocode-api-key",
label: "Kilo Gateway API key",
hint: "OpenRouter-compatible gateway",
});
7. src/commands/auth-choice.preferred-provider.ts
Agregar mapeo:
const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
// ... mapeos existentes
"kilocode-api-key": "kilocode",
};
Aplicación de elección de autenticación
8. src/commands/auth-choice.apply.api-providers.ts
Agregar import:
import {
// ... imports existentes
applyKilocodeConfig,
applyKilocodeProviderConfig,
KILOCODE_DEFAULT_MODEL_REF,
setKilocodeApiKey,
} from "./onboard-auth.js";
Agregar manejo para kilocode-api-key:
if (authChoice === "kilocode-api-key") {
const store = ensureAuthProfileStore(params.agentDir, {
allowKeychainPrompt: false,
});
const profileOrder = resolveAuthProfileOrder({
cfg: nextConfig,
store,
provider: "kilocode",
});
const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId]));
const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined;
let profileId = "kilocode:default";
let mode: "api_key" | "oauth" | "token" = "api_key";
let hasCredential = false;
if (existingProfileId && existingCred?.type) {
profileId = existingProfileId;
mode =
existingCred.type === "oauth" ? "oauth" : existingCred.type === "token" ? "token" : "api_key";
hasCredential = true;
}
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "kilocode") {
await setKilocodeApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
hasCredential = true;
}
if (!hasCredential) {
const envKey = resolveEnvApiKey("kilocode");
if (envKey) {
const useExisting = await params.prompter.confirm({
message: `Use existing KILOCODE_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
initialValue: true,
});
if (useExisting) {
await setKilocodeApiKey(envKey.apiKey, params.agentDir);
hasCredential = true;
}
}
}
if (!hasCredential) {
const key = await params.prompter.text({
message: "Enter Kilo Gateway API key",
validate: validateApiKeyInput,
});
await setKilocodeApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
hasCredential = true;
}
if (hasCredential) {
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId,
provider: "kilocode",
mode,
});
}
{
const applied = await applyDefaultModelChoice({
config: nextConfig,
setDefaultModel: params.setDefaultModel,
defaultModel: KILOCODE_DEFAULT_MODEL_REF,
applyDefaultConfig: applyKilocodeConfig,
applyProviderConfig: applyKilocodeProviderConfig,
noteDefault: KILOCODE_DEFAULT_MODEL_REF,
noteAgentModel,
prompter: params.prompter,
});
nextConfig = applied.config;
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
}
return { config: nextConfig, agentModelOverride };
}
También agregar el mapeo de tokenProvider al inicio de la función:
if (params.opts.tokenProvider === "kilocode") {
authChoice = "kilocode-api-key";
}
Registro en CLI
9. src/cli/program/register.onboard.ts
Agregar opción CLI:
.option("--kilocode-api-key <key>", "Kilo Gateway API key")
Agregar al manejador de acción:
kilocodeApiKey: opts.kilocodeApiKey as string | undefined,
Actualizar texto de ayuda de auth-choice:
.option(
"--auth-choice <choice>",
"Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|kilocode-api-key|ai-gateway-api-key|...",
)
Onboarding no interactivo
10. src/commands/onboard-non-interactive/local/auth-choice.ts
Agregar manejo para kilocode-api-key:
if (authChoice === "kilocode-api-key") {
const resolved = await resolveNonInteractiveApiKey({
provider: "kilocode",
cfg: baseConfig,
flagValue: opts.kilocodeApiKey,
flagName: "--kilocode-api-key",
envVar: "KILOCODE_API_KEY",
});
await setKilocodeApiKey(resolved.apiKey, agentDir);
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "kilocode:default",
provider: "kilocode",
mode: "api_key",
});
// ... aplicar modelo por defecto
}
Actualización de exports
11. src/commands/onboard-auth.ts
Agregar exports:
export {
// ... exports existentes
applyKilocodeConfig,
applyKilocodeProviderConfig,
KILOCODE_BASE_URL,
} from "./onboard-auth.config-core.js";
export {
// ... exports existentes
KILOCODE_DEFAULT_MODEL_REF,
setKilocodeApiKey,
} from "./onboard-auth.credentials.js";
Manejo especial (Opcional)
12. src/agents/pi-embedded-runner/cache-ttl.ts
Agregar soporte de Kilo Gateway para modelos de Anthropic:
export function isCacheTtlEligibleProvider(provider: string, modelId: string): boolean {
const normalizedProvider = provider.toLowerCase();
const normalizedModelId = modelId.toLowerCase();
if (normalizedProvider === "anthropic") return true;
if (normalizedProvider === "openrouter" && normalizedModelId.startsWith("anthropic/"))
return true;
if (normalizedProvider === "kilocode" && normalizedModelId.startsWith("anthropic/")) return true;
return false;
}
13. src/agents/transcript-policy.ts
Agregar manejo de Kilo Gateway (similar a OpenRouter):
const isKilocodeGemini = provider === "kilocode" && modelId.toLowerCase().includes("gemini");
// Incluir en la verificación de needsNonImageSanitize
const needsNonImageSanitize =
isGoogle || isAnthropic || isMistral || isOpenRouterGemini || isKilocodeGemini;
Estructura de configuración
Ejemplo de configuración del usuario
{
"models": {
"mode": "merge",
"providers": {
"kilocode": {
"baseUrl": "https://api.kilo.ai/api/gateway/",
"apiKey": "xxxxx",
"api": "openai-completions",
"models": [
{
"id": "anthropic/claude-opus-4.6",
"name": "Anthropic: Claude Opus 4.6"
},
{ "id": "minimax/minimax-m2.5:free", "name": "Minimax: Minimax M2.5" }
]
}
}
}
}
Estructura del perfil de autenticación
{
"profiles": {
"kilocode:default": {
"type": "api_key",
"provider": "kilocode",
"key": "xxxxx"
}
}
}
Consideraciones de testing
-
Tests unitarios:
- Verificar que
setKilocodeApiKey()escribe el perfil correcto - Verificar que
applyKilocodeConfig()establece los valores por defecto correctos - Verificar que
resolveEnvApiKey("kilocode")devuelve la variable de entorno correcta
- Verificar que
-
Tests de integración:
- Probar el flujo de onboarding con
--auth-choice kilocode-api-key - Probar el onboarding no interactivo con
--kilocode-api-key - Probar la selección de modelo con prefijo
kilocode/
- Probar el flujo de onboarding con
-
Tests E2E:
- Probar llamadas API reales a través de Kilo Gateway (tests en vivo)
Notas de migración
- No se necesita migración para usuarios existentes
- Los nuevos usuarios pueden usar inmediatamente la opción de auth
kilocode-api-key - La configuración manual existente con proveedor
kilocodeseguirá funcionando
Consideraciones futuras
-
Catálogo de modelos: Si Kilo Gateway expone un endpoint
/models, agregar soporte de escaneo similar ascanOpenRouterModels() -
Soporte OAuth: Si Kilo Gateway agrega OAuth, extender el sistema de autenticación en consecuencia
-
Limitación de tasa: Considerar agregar manejo de limitación de tasa específico para Kilo Gateway si es necesario
-
Documentación: Agregar documentación en
docs/providers/kilocode.mdexplicando la configuración y el uso
Resumen de cambios
| Archivo | Tipo de cambio | Descripción |
|---|---|---|
src/commands/onboard-auth.credentials.ts | Agregar | KILOCODE_DEFAULT_MODEL_REF, setKilocodeApiKey() |
src/agents/model-auth.ts | Modificar | Agregar kilocode a envMap |
src/config/io.ts | Modificar | Agregar KILOCODE_API_KEY a claves de entorno shell |
src/commands/onboard-auth.config-core.ts | Agregar | applyKilocodeProviderConfig(), applyKilocodeConfig() |
src/commands/onboard-types.ts | Modificar | Agregar kilocode-api-key a AuthChoice, agregar kilocodeApiKey a opciones |
src/commands/auth-choice-options.ts | Modificar | Agregar grupo y opción kilocode |
src/commands/auth-choice.preferred-provider.ts | Modificar | Agregar mapeo kilocode-api-key |
src/commands/auth-choice.apply.api-providers.ts | Modificar | Agregar manejo de kilocode-api-key |
src/cli/program/register.onboard.ts | Modificar | Agregar opción --kilocode-api-key |
src/commands/onboard-non-interactive/local/auth-choice.ts | Modificar | Agregar manejo no interactivo |
src/commands/onboard-auth.ts | Modificar | Exportar nuevas funciones |
src/agents/pi-embedded-runner/cache-ttl.ts | Modificar | Agregar soporte para kilocode |
src/agents/transcript-policy.ts | Modificar | Agregar manejo de kilocode con Gemini |