From 8e0731f47aead7c279b622597d9c98ce9e119845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Mon, 25 May 2026 21:19:56 +0300 Subject: [PATCH] revert: revert KiloCode provider support This reverts commit cc10485f0cc2cf5b0ff672d7614931c35ec67ee4. --- .../kilocode-model-catalog/contracts/dto.ts | 11 -- .../kilocode-model-catalog/contracts/index.ts | 6 - .../domain/kilocodeModelCatalogFallback.ts | 32 --- src/features/kilocode-model-catalog/index.ts | 8 - .../createKilocodeModelCatalogFeature.ts | 186 ------------------ .../kilocode-model-catalog/main/index.ts | 5 - .../InMemoryKilocodeModelCatalogCache.ts | 37 ---- .../infrastructure/KilocodeGatewayClient.ts | 101 ---------- .../member-work-sync/contracts/types.ts | 2 +- .../renderer/ui/providerBrandIcons.tsx | 1 - .../core/domain/WorkspaceTrustTypes.ts | 8 +- src/main/index.ts | 12 -- src/main/ipc/cliInstaller.ts | 7 +- .../infrastructure/CliInstallerService.ts | 98 +-------- .../runtime/ClaudeMultimodelBridgeService.ts | 19 +- .../runtime/ProviderConnectionService.ts | 148 +------------- .../services/runtime/providerRuntimeEnv.ts | 12 +- .../services/team/TeamProvisioningService.ts | 13 -- .../team/runtime/TeamRuntimeAdapter.ts | 8 +- .../components/common/ProviderBrandLogo.tsx | 2 - .../components/dashboard/CliStatusBanner.tsx | 2 - .../dashboard/providerDashboardRateLimits.ts | 1 - .../extensions/ExtensionStoreView.tsx | 2 +- .../runtime/ProviderRuntimeSettingsDialog.tsx | 38 +--- .../settings/sections/CliStatusSection.tsx | 2 - .../sidebar/DateGroupedSessions.tsx | 1 - .../dialogs/teammateRuntimeCompatibility.tsx | 1 - .../store/slices/cliInstallerSlice.ts | 32 +-- src/renderer/utils/teamModelCatalog.ts | 8 - src/shared/types/cliInstaller.ts | 2 +- src/shared/types/team.ts | 2 +- src/shared/utils/teamProvider.ts | 17 +- .../CliInstallerService.test.ts | 17 +- .../runtime/ProviderConnectionService.test.ts | 128 ------------ .../cli/CliStatusVisibility.test.ts | 2 +- .../extensions/skills/SkillsPanel.test.ts | 5 +- test/setup.ts | 43 ---- 37 files changed, 55 insertions(+), 964 deletions(-) delete mode 100644 src/features/kilocode-model-catalog/contracts/dto.ts delete mode 100644 src/features/kilocode-model-catalog/contracts/index.ts delete mode 100644 src/features/kilocode-model-catalog/core/domain/kilocodeModelCatalogFallback.ts delete mode 100644 src/features/kilocode-model-catalog/index.ts delete mode 100644 src/features/kilocode-model-catalog/main/composition/createKilocodeModelCatalogFeature.ts delete mode 100644 src/features/kilocode-model-catalog/main/index.ts delete mode 100644 src/features/kilocode-model-catalog/main/infrastructure/InMemoryKilocodeModelCatalogCache.ts delete mode 100644 src/features/kilocode-model-catalog/main/infrastructure/KilocodeGatewayClient.ts diff --git a/src/features/kilocode-model-catalog/contracts/dto.ts b/src/features/kilocode-model-catalog/contracts/dto.ts deleted file mode 100644 index 66f8f7f8..00000000 --- a/src/features/kilocode-model-catalog/contracts/dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { - CliProviderModelCatalog, - CliProviderModelCatalogItem, - CliProviderModelCatalogSource, - CliProviderModelCatalogStatus, -} from '@shared/types'; - -export type KilocodeModelCatalogDto = CliProviderModelCatalog; -export type KilocodeModelCatalogItemDto = CliProviderModelCatalogItem; -export type KilocodeModelCatalogSourceDto = CliProviderModelCatalogSource; -export type KilocodeModelCatalogStatusDto = CliProviderModelCatalogStatus; diff --git a/src/features/kilocode-model-catalog/contracts/index.ts b/src/features/kilocode-model-catalog/contracts/index.ts deleted file mode 100644 index 8d12ad9f..00000000 --- a/src/features/kilocode-model-catalog/contracts/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type { - KilocodeModelCatalogDto, - KilocodeModelCatalogItemDto, - KilocodeModelCatalogSourceDto, - KilocodeModelCatalogStatusDto, -} from './dto'; diff --git a/src/features/kilocode-model-catalog/core/domain/kilocodeModelCatalogFallback.ts b/src/features/kilocode-model-catalog/core/domain/kilocodeModelCatalogFallback.ts deleted file mode 100644 index d5b35f60..00000000 --- a/src/features/kilocode-model-catalog/core/domain/kilocodeModelCatalogFallback.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { KilocodeModelCatalogItemDto } from '../../contracts'; - -export function createStaticKilocodeModelCatalogModels(): KilocodeModelCatalogItemDto[] { - return [ - { - id: 'claude-sonnet-4-5', - launchModel: 'claude-sonnet-4-5', - displayName: 'Claude Sonnet 4.5', - hidden: false, - supportedReasoningEfforts: [], - defaultReasoningEffort: null, - inputModalities: ['text'], - supportsPersonality: false, - isDefault: true, - upgrade: false, - source: 'static-fallback', - }, - { - id: 'claude-opus-4-5', - launchModel: 'claude-opus-4-5', - displayName: 'Claude Opus 4.5', - hidden: false, - supportedReasoningEfforts: [], - defaultReasoningEffort: null, - inputModalities: ['text'], - supportsPersonality: false, - isDefault: false, - upgrade: false, - source: 'static-fallback', - }, - ]; -} diff --git a/src/features/kilocode-model-catalog/index.ts b/src/features/kilocode-model-catalog/index.ts deleted file mode 100644 index 0cc3e229..00000000 --- a/src/features/kilocode-model-catalog/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type { - KilocodeModelCatalogDto, - KilocodeModelCatalogItemDto, - KilocodeModelCatalogSourceDto, - KilocodeModelCatalogStatusDto, -} from './contracts'; -export type { KilocodeModelCatalogFeatureFacade, KilocodeModelCatalogRequest } from './main'; -export { createKilocodeModelCatalogFeature } from './main'; diff --git a/src/features/kilocode-model-catalog/main/composition/createKilocodeModelCatalogFeature.ts b/src/features/kilocode-model-catalog/main/composition/createKilocodeModelCatalogFeature.ts deleted file mode 100644 index d4a68a96..00000000 --- a/src/features/kilocode-model-catalog/main/composition/createKilocodeModelCatalogFeature.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { createHash } from 'node:crypto'; - -import { createStaticKilocodeModelCatalogModels } from '../../core/domain/kilocodeModelCatalogFallback'; -import { InMemoryKilocodeModelCatalogCache } from '../infrastructure/InMemoryKilocodeModelCatalogCache'; -import { KilocodeGatewayClient } from '../infrastructure/KilocodeGatewayClient'; - -import type { KilocodeModelCatalogDto, KilocodeModelCatalogItemDto } from '../../contracts'; -import type { Logger } from '@shared/utils/logger'; - -type LoggerPort = Pick; - -const CATALOG_CACHE_TTL_MS = 10 * 60_000; -const CATALOG_STALE_TTL_MS = 24 * 60 * 60_000; - -export interface KilocodeModelCatalogRequest { - apiKey?: string | null; - forceRefresh?: boolean; -} - -export interface KilocodeModelCatalogFeatureFacade { - getCatalog(options?: KilocodeModelCatalogRequest): Promise; - invalidate(): void; -} - -function nowIso(): string { - return new Date().toISOString(); -} - -function staleAtIso(): string { - return new Date(Date.now() + CATALOG_CACHE_TTL_MS).toISOString(); -} - -function buildCacheKey(apiKey: string): string { - return `kilocode:${createHash('sha256').update(apiKey).digest('hex')}`; -} - -function normalizeGatewayModels( - models: { id: string; displayName: string }[] -): KilocodeModelCatalogItemDto[] { - return models.map((model, index) => ({ - id: model.id, - launchModel: model.id, - displayName: model.displayName, - hidden: false, - supportedReasoningEfforts: [], - defaultReasoningEffort: null, - inputModalities: ['text'], - supportsPersonality: false, - isDefault: index === 0, - upgrade: false, - source: 'app-server' as const, - })); -} - -function createFallbackCatalog(options: { - message: string; - status?: KilocodeModelCatalogDto['status']; - appServerState: KilocodeModelCatalogDto['diagnostics']['appServerState']; -}): KilocodeModelCatalogDto { - const models = createStaticKilocodeModelCatalogModels(); - const defaultModel = models.find((m) => m.isDefault) ?? models[0] ?? null; - return { - schemaVersion: 1, - providerId: 'kilocode', - source: 'static-fallback', - status: options.status ?? 'degraded', - fetchedAt: nowIso(), - staleAt: staleAtIso(), - defaultModelId: defaultModel?.id ?? null, - defaultLaunchModel: defaultModel?.launchModel ?? null, - models, - diagnostics: { - configReadState: 'skipped', - appServerState: options.appServerState, - message: options.message, - code: null, - }, - }; -} - -export function createKilocodeModelCatalogFeature(options: { - logger: LoggerPort; -}): KilocodeModelCatalogFeatureFacade { - const cache = new InMemoryKilocodeModelCatalogCache(); - const inFlightRefreshes = new Map>(); - const client = new KilocodeGatewayClient(); - - async function getCatalog( - request: KilocodeModelCatalogRequest = {} - ): Promise { - const apiKey = request.apiKey?.trim() || process.env.KILO_API_KEY?.trim() || null; - - if (!apiKey) { - return createFallbackCatalog({ - message: 'No KiloCode API key configured. Set KILO_API_KEY or configure an API key.', - appServerState: 'runtime-missing', - status: 'unavailable', - }); - } - - const cacheKey = buildCacheKey(apiKey); - - if (request.forceRefresh !== true) { - const cached = cache.get(cacheKey, CATALOG_CACHE_TTL_MS); - if (cached) { - return cached; - } - } - - const existing = inFlightRefreshes.get(cacheKey); - if (existing) { - return existing; - } - - const refreshPromise = (async (): Promise => { - try { - const gatewayModels = await client.listModels(apiKey); - const models = normalizeGatewayModels(gatewayModels); - - if (models.length === 0) { - throw new Error('KiloCode gateway returned no models.'); - } - - const defaultModel = models[0] ?? null; - const catalog: KilocodeModelCatalogDto = { - schemaVersion: 1, - providerId: 'kilocode', - source: 'app-server', - status: 'ready', - fetchedAt: nowIso(), - staleAt: staleAtIso(), - defaultModelId: defaultModel?.id ?? null, - defaultLaunchModel: defaultModel?.launchModel ?? null, - models, - diagnostics: { - configReadState: 'skipped', - appServerState: 'healthy', - message: null, - code: null, - }, - }; - - cache.set(cacheKey, catalog); - return catalog; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - const stale = cache.getLatest(cacheKey); - if (stale && Date.parse(stale.fetchedAt) + CATALOG_STALE_TTL_MS > Date.now()) { - return { - ...stale, - status: 'stale', - diagnostics: { - configReadState: 'skipped', - appServerState: 'degraded', - message, - code: null, - }, - }; - } - - options.logger.warn('KiloCode model catalog refresh failed', { error: message }); - return createFallbackCatalog({ - message, - appServerState: 'degraded', - }); - } - })(); - - inFlightRefreshes.set(cacheKey, refreshPromise); - try { - return await refreshPromise; - } finally { - if (inFlightRefreshes.get(cacheKey) === refreshPromise) { - inFlightRefreshes.delete(cacheKey); - } - } - } - - return { - getCatalog, - invalidate: () => { - cache.clear(); - inFlightRefreshes.clear(); - }, - }; -} diff --git a/src/features/kilocode-model-catalog/main/index.ts b/src/features/kilocode-model-catalog/main/index.ts deleted file mode 100644 index 81881177..00000000 --- a/src/features/kilocode-model-catalog/main/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type { - KilocodeModelCatalogFeatureFacade, - KilocodeModelCatalogRequest, -} from './composition/createKilocodeModelCatalogFeature'; -export { createKilocodeModelCatalogFeature } from './composition/createKilocodeModelCatalogFeature'; diff --git a/src/features/kilocode-model-catalog/main/infrastructure/InMemoryKilocodeModelCatalogCache.ts b/src/features/kilocode-model-catalog/main/infrastructure/InMemoryKilocodeModelCatalogCache.ts deleted file mode 100644 index 1fe75a23..00000000 --- a/src/features/kilocode-model-catalog/main/infrastructure/InMemoryKilocodeModelCatalogCache.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { KilocodeModelCatalogDto } from '../../contracts'; - -interface CacheEntry { - value: KilocodeModelCatalogDto; - observedAt: number; -} - -export class InMemoryKilocodeModelCatalogCache { - private readonly entries = new Map(); - - get(key: string, maxAgeMs: number): KilocodeModelCatalogDto | null { - const entry = this.entries.get(key); - if (!entry) { - return null; - } - if (Date.now() - entry.observedAt > maxAgeMs) { - return null; - } - return structuredClone(entry.value); - } - - getLatest(key: string): KilocodeModelCatalogDto | null { - const entry = this.entries.get(key); - return entry ? structuredClone(entry.value) : null; - } - - set(key: string, value: KilocodeModelCatalogDto): void { - this.entries.set(key, { - value: structuredClone(value), - observedAt: Date.now(), - }); - } - - clear(): void { - this.entries.clear(); - } -} diff --git a/src/features/kilocode-model-catalog/main/infrastructure/KilocodeGatewayClient.ts b/src/features/kilocode-model-catalog/main/infrastructure/KilocodeGatewayClient.ts deleted file mode 100644 index cbda81d6..00000000 --- a/src/features/kilocode-model-catalog/main/infrastructure/KilocodeGatewayClient.ts +++ /dev/null @@ -1,101 +0,0 @@ -import https from 'node:https'; - -const GATEWAY_BASE_URL = 'https://api.kilo.ai'; -// KiloCode gateway endpoint: https://kilo.ai/docs/gateway/models-and-providers -const MODELS_PATH = '/api/gateway/models'; -const REQUEST_TIMEOUT_MS = 8_000; -const ERROR_BODY_PREVIEW_LIMIT = 500; - -interface GatewayModelObject { - id?: string; - object?: string; - created?: number; - owned_by?: string; - display_name?: string; -} - -interface GatewayModelsResponse { - object?: string; - data?: GatewayModelObject[]; -} - -export interface KilocodeGatewayModel { - id: string; - displayName: string; -} - -function sanitizeErrorBody(body: string): string { - const sanitized = body - .trim() - .replace(/Bearer\s+[A-Za-z0-9._~-]+/gi, 'Bearer [redacted]') - .replace(/sk-[A-Za-z0-9_-]+/g, '[redacted-api-key]'); - if (!sanitized) { - return 'empty response body'; - } - return sanitized.length > ERROR_BODY_PREVIEW_LIMIT - ? `${sanitized.slice(0, ERROR_BODY_PREVIEW_LIMIT)}...` - : sanitized; -} - -export class KilocodeGatewayClient { - async listModels(apiKey: string): Promise { - const raw = await this.fetchModels(apiKey); - const items = raw.data ?? []; - return items - .filter( - (item): item is GatewayModelObject & { id: string } => - typeof item.id === 'string' && item.id.trim().length > 0 - ) - .map((item) => ({ - id: item.id.trim(), - displayName: (item.display_name ?? item.id).trim(), - })); - } - - private fetchModels(apiKey: string): Promise { - return new Promise((resolve, reject) => { - const url = new URL(MODELS_PATH, GATEWAY_BASE_URL); - const options: https.RequestOptions = { - hostname: url.hostname, - port: url.port || 443, - path: url.pathname + url.search, - method: 'GET', - headers: { - Authorization: `Bearer ${apiKey}`, - Accept: 'application/json', - }, - }; - - const req = https.request(options, (res) => { - const chunks: Buffer[] = []; - res.on('data', (chunk: Buffer) => chunks.push(chunk)); - res.on('end', () => { - const body = Buffer.concat(chunks).toString('utf8'); - if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) { - reject( - new Error( - `KiloCode gateway responded with HTTP ${res.statusCode}: ${sanitizeErrorBody(body)}` - ) - ); - return; - } - try { - resolve(JSON.parse(body) as GatewayModelsResponse); - } catch { - reject( - new Error(`KiloCode gateway returned non-JSON response: ${sanitizeErrorBody(body)}`) - ); - } - }); - res.on('error', reject); - }); - - req.setTimeout(REQUEST_TIMEOUT_MS, () => { - req.destroy(new Error(`KiloCode gateway request timed out after ${REQUEST_TIMEOUT_MS}ms`)); - }); - - req.on('error', reject); - req.end(); - }); - } -} diff --git a/src/features/member-work-sync/contracts/types.ts b/src/features/member-work-sync/contracts/types.ts index 4d6ade15..d2597252 100644 --- a/src/features/member-work-sync/contracts/types.ts +++ b/src/features/member-work-sync/contracts/types.ts @@ -20,7 +20,7 @@ export type MemberWorkSyncActionableWorkPriority = | 'blocked' | 'needs_clarification'; -export type MemberWorkSyncProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode' | 'kilocode'; +export type MemberWorkSyncProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode'; export type MemberWorkSyncReviewObligation = 'review_pickup_required' | 'review_in_progress'; diff --git a/src/features/runtime-provider-management/renderer/ui/providerBrandIcons.tsx b/src/features/runtime-provider-management/renderer/ui/providerBrandIcons.tsx index 30fe686e..3bbda2e2 100644 --- a/src/features/runtime-provider-management/renderer/ui/providerBrandIcons.tsx +++ b/src/features/runtime-provider-management/renderer/ui/providerBrandIcons.tsx @@ -384,7 +384,6 @@ const BRAND_ALIASES: Record = { 'gitlab-duo': 'gitlab-duo', 'google-vertex': 'google-vertex', 'hugging-face': 'huggingface', - kilocode: 'kilo', 'mistral-ai': 'mistral', 'ollama-cloud': 'ollama-cloud', 'opencode-zen': 'opencode', diff --git a/src/features/workspace-trust/core/domain/WorkspaceTrustTypes.ts b/src/features/workspace-trust/core/domain/WorkspaceTrustTypes.ts index 8c506986..b93561cc 100644 --- a/src/features/workspace-trust/core/domain/WorkspaceTrustTypes.ts +++ b/src/features/workspace-trust/core/domain/WorkspaceTrustTypes.ts @@ -1,10 +1,4 @@ -export type WorkspaceTrustProvider = - | 'claude' - | 'anthropic' - | 'codex' - | 'gemini' - | 'opencode' - | 'kilocode'; +export type WorkspaceTrustProvider = 'claude' | 'anthropic' | 'codex' | 'gemini' | 'opencode'; export type WorkspaceTrustWorkspaceSource = | 'team-root' diff --git a/src/main/index.ts b/src/main/index.ts index 54efaac7..746a21c2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -32,10 +32,6 @@ import { type CodexModelCatalogFeatureFacade, createCodexModelCatalogFeature, } from '@features/codex-model-catalog/main'; -import { - type KilocodeModelCatalogFeatureFacade, - createKilocodeModelCatalogFeature, -} from '@features/kilocode-model-catalog/main'; import { createMemberLogStreamFeature, registerMemberLogStreamIpc, @@ -903,7 +899,6 @@ let updaterService: UpdaterService; let sshConnectionManager: SshConnectionManager; let codexAccountFeature: CodexAccountFeatureFacade | null = null; let codexModelCatalogFeature: CodexModelCatalogFeatureFacade | null = null; -let kilocodeModelCatalogFeature: KilocodeModelCatalogFeatureFacade | null = null; let recentProjectsFeature: RecentProjectsFeatureFacade; let runtimeProviderManagementFeature: RuntimeProviderManagementFeatureFacade; let memberWorkSyncFeature: MemberWorkSyncFeatureFacade | null = null; @@ -2108,10 +2103,6 @@ async function initializeServices(): Promise { codexAccountFeature, }); providerConnectionService.setCodexModelCatalogFeature(codexModelCatalogFeature); - kilocodeModelCatalogFeature = createKilocodeModelCatalogFeature({ - logger: createLogger('Feature:KilocodeModelCatalog'), - }); - providerConnectionService.setKilocodeModelCatalogFeature(kilocodeModelCatalogFeature); // startProcessHealthPolling() is deferred to after window creation // (did-finish-load handler) to avoid thread pool contention at startup. @@ -2364,13 +2355,10 @@ async function shutdownServices(): Promise { await runShutdownStep('skills watcher stop', () => skillsWatcherService?.stopAll()); await runShutdownStep('provider connection feature detach', () => { providerConnectionService.setCodexModelCatalogFeature(null); - providerConnectionService.setKilocodeModelCatalogFeature(null); providerConnectionService.setCodexAccountFeature(null); }); await runShutdownStep('Codex model catalog dispose', () => codexModelCatalogFeature?.dispose()); codexModelCatalogFeature = null; - kilocodeModelCatalogFeature?.invalidate(); - kilocodeModelCatalogFeature = null; await runShutdownStep('Codex account dispose', () => codexAccountFeature?.dispose()); codexAccountFeature = null; await runShutdownStep('member work sync dispose', () => memberWorkSyncFeature?.dispose()); diff --git a/src/main/ipc/cliInstaller.ts b/src/main/ipc/cliInstaller.ts index 991d60c7..16d618f3 100644 --- a/src/main/ipc/cliInstaller.ts +++ b/src/main/ipc/cliInstaller.ts @@ -44,12 +44,7 @@ const cachedStatus = new Map< >(); let statusCacheGeneration = 0; const STATUS_CACHE_TTL_MS = 5_000; -const FRONTEND_MULTIMODEL_PROVIDER_IDS = new Set([ - 'anthropic', - 'codex', - 'opencode', - 'kilocode', -]); +const FRONTEND_MULTIMODEL_PROVIDER_IDS = new Set(['anthropic', 'codex', 'opencode']); function isFrontendMultimodelProviderId(providerId: CliProviderId): boolean { return FRONTEND_MULTIMODEL_PROVIDER_IDS.has(providerId); diff --git a/src/main/services/infrastructure/CliInstallerService.ts b/src/main/services/infrastructure/CliInstallerService.ts index f1403213..1bbf7d43 100644 --- a/src/main/services/infrastructure/CliInstallerService.ts +++ b/src/main/services/infrastructure/CliInstallerService.ts @@ -45,7 +45,6 @@ import { type ProviderModelAvailabilityContext, type ProviderModelAvailabilitySnapshot, } from '../runtime/CliProviderModelAvailabilityService'; -import { providerConnectionService } from '../runtime/ProviderConnectionService'; import { ClaudeBinaryResolver } from '../team/ClaudeBinaryResolver'; import { getCliFlavorUiOptions, getConfiguredCliFlavor } from '../team/cliFlavor'; @@ -72,12 +71,7 @@ const GCS_BASE = 'https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases'; const CLI_INSTALLER_PROGRESS_CHANNEL = 'cliInstaller:progress'; -const FRONTEND_MULTIMODEL_PROVIDER_IDS: CliProviderId[] = [ - 'anthropic', - 'codex', - 'opencode', - 'kilocode', -]; +const FRONTEND_MULTIMODEL_PROVIDER_IDS: CliProviderId[] = ['anthropic', 'codex', 'opencode']; const FRONTEND_MULTIMODEL_PROVIDER_ID_SET = new Set( FRONTEND_MULTIMODEL_PROVIDER_IDS ); @@ -92,8 +86,6 @@ function getProviderDisplayName(providerId: CliProviderId): string { return 'Gemini'; case 'opencode': return 'OpenCode (200+ models)'; - case 'kilocode': - return 'KiloCode'; } } @@ -927,10 +919,6 @@ export class CliInstallerService { background: false, }); - if (providerId === 'kilocode') { - return this.resolveKilocodeProviderStatus(); - } - const binaryPath = await ClaudeBinaryResolver.resolve(); if (!binaryPath) { return null; @@ -971,10 +959,6 @@ export class CliInstallerService { background: false, }); - if (providerId === 'kilocode') { - return this.resolveKilocodeProviderStatus(); - } - const binaryPath = await ClaudeBinaryResolver.resolve(); if (!binaryPath) { return null; @@ -1220,53 +1204,6 @@ export class CliInstallerService { result.authMethod = null; } - private async resolveKilocodeProviderStatus(): Promise { - const baseStatus: CliProviderStatus = { - providerId: 'kilocode', - displayName: 'KiloCode', - supported: false, - authenticated: false, - authMethod: null, - verificationState: 'verified', - modelVerificationState: 'idle', - statusMessage: null, - detailMessage: null, - models: [], - modelAvailability: [], - canLoginFromUi: true, - capabilities: { - teamLaunch: false, - oneShot: false, - extensions: createDefaultCliExtensionCapabilities(), - }, - selectedBackendId: null, - resolvedBackendId: null, - availableBackends: [], - externalRuntimeDiagnostics: [], - backend: null, - connection: null, - modelCatalog: null, - runtimeCapabilities: null, - subscriptionRateLimits: null, - }; - // enrichProviderStatus checks both the app key store and process.env for KILO_API_KEY - const enriched = await providerConnectionService.enrichProviderStatus(baseStatus); - const hasApiKey = Boolean(enriched.connection?.apiKeyConfigured); - const status: CliProviderStatus = { - ...enriched, - supported: hasApiKey, - authenticated: hasApiKey, - authMethod: hasApiKey ? 'api_key' : null, - statusMessage: hasApiKey ? null : 'Configure KILO_API_KEY to use KiloCode.', - capabilities: { - ...enriched.capabilities, - teamLaunch: hasApiKey, - }, - }; - this.updateLatestProviderStatus(status); - return status; - } - private markProvidersDeferred(result: CliInstallationStatus): void { if (result.flavor !== 'agent_teams_orchestrator') { return; @@ -1304,38 +1241,13 @@ export class CliInstallerService { if (result.flavor === 'agent_teams_orchestrator') { result.authStatusChecking = true; let statusTarget = result; - const buildFrontendProviders = async ( - providersSnapshot: CliProviderStatus[] - ): Promise => { - const providersById = new Map(providersSnapshot.map((provider) => [provider.providerId, provider])); - const frontendProviders: CliProviderStatus[] = []; - for (const providerId of FRONTEND_MULTIMODEL_PROVIDER_IDS) { - if (providerId === 'kilocode') { - frontendProviders.push(await this.resolveKilocodeProviderStatus()); - continue; - } - const provider = providersById.get(providerId); - if (provider) { - frontendProviders.push(provider); - } - } - return frontendProviders; - }; - const applyProviders = async ( - providersSnapshot: CliProviderStatus[], - final: boolean - ): Promise => { + const applyProviders = (providersSnapshot: CliProviderStatus[], final: boolean): void => { if (generation !== this.statusGatherGeneration) { return; } const target = statusTarget; - const frontendProviders = await buildFrontendProviders( - filterFrontendMultimodelProviders(providersSnapshot) - ); - if (generation !== this.statusGatherGeneration) { - return; - } + const frontendProviders = filterFrontendMultimodelProviders(providersSnapshot); target.providers = frontendProviders; target.authLoggedIn = hasFrontendAuthenticatedProvider(frontendProviders); target.authMethod = getFrontendAuthenticatedProvider(frontendProviders)?.authMethod ?? null; @@ -1348,10 +1260,10 @@ export class CliInstallerService { const completion = this.multimodelBridgeService .getProviderStatuses(binaryPath, (providersSnapshot) => { - void applyProviders(providersSnapshot, false); + applyProviders(providersSnapshot, false); }) .then((providers) => { - return applyProviders(providers, true); + applyProviders(providers, true); }) .catch((error) => { if (generation !== this.statusGatherGeneration) { diff --git a/src/main/services/runtime/ClaudeMultimodelBridgeService.ts b/src/main/services/runtime/ClaudeMultimodelBridgeService.ts index d799ac8f..448519cc 100644 --- a/src/main/services/runtime/ClaudeMultimodelBridgeService.ts +++ b/src/main/services/runtime/ClaudeMultimodelBridgeService.ts @@ -335,8 +335,6 @@ function getProviderDisplayName(providerId: CliProviderId): string { return 'Gemini'; case 'opencode': return 'OpenCode (200+ models)'; - case 'kilocode': - return 'KiloCode'; } } @@ -423,14 +421,15 @@ function mapRuntimeExtensionCapabilities( const defaults = capabilities ? createDefaultCliExtensionCapabilities() : createLegacyRuntimeFallbackCliExtensionCapabilities(); - const isExternalRuntime = providerId === 'opencode' || providerId === 'kilocode'; - const pluginStatus = isExternalRuntime - ? 'unsupported' - : (capabilities?.plugins?.status ?? defaults.plugins.status); - const pluginReason = isExternalRuntime - ? (capabilities?.plugins?.reason ?? - `${getProviderDisplayName(providerId)} does not support plugin management from Agent Teams.`) - : (capabilities?.plugins?.reason ?? defaults.plugins.reason); + const pluginStatus = + providerId === 'opencode' + ? 'unsupported' + : (capabilities?.plugins?.status ?? defaults.plugins.status); + const pluginReason = + providerId === 'opencode' + ? (capabilities?.plugins?.reason ?? + 'OpenCode does not support plugin management from Agent Teams.') + : (capabilities?.plugins?.reason ?? defaults.plugins.reason); return { plugins: { diff --git a/src/main/services/runtime/ProviderConnectionService.ts b/src/main/services/runtime/ProviderConnectionService.ts index 155bab03..1a89b335 100644 --- a/src/main/services/runtime/ProviderConnectionService.ts +++ b/src/main/services/runtime/ProviderConnectionService.ts @@ -23,7 +23,6 @@ import type { CodexModelCatalogFeatureFacade, CodexModelCatalogRequest, } from '@features/codex-model-catalog/main'; -import type { KilocodeModelCatalogFeatureFacade } from '@features/kilocode-model-catalog/main'; import type { CliProviderAuthMode, CliProviderConnectionInfo, @@ -66,18 +65,12 @@ const PROVIDER_CAPABILITIES: Record< supportsApiKey: false, configurableAuthModes: [], }, - kilocode: { - supportsOAuth: false, - supportsApiKey: true, - configurableAuthModes: ['api_key'], - }, }; const PROVIDER_API_KEY_ENV_VARS: Partial> = { anthropic: 'ANTHROPIC_API_KEY', codex: 'OPENAI_API_KEY', gemini: 'GEMINI_API_KEY', - kilocode: 'KILO_API_KEY', }; const ANTHROPIC_BASE_URL_ENV_VAR = 'ANTHROPIC_BASE_URL'; @@ -371,10 +364,6 @@ export class ProviderConnectionService { private codexAccountFeature: CodexAccountSnapshotReader | null = null; private codexModelCatalogFeature: Pick | null = null; - private kilocodeModelCatalogFeature: Pick< - KilocodeModelCatalogFeatureFacade, - 'getCatalog' - > | null = null; private readonly anthropicApiKeyVerificationCache = new Map< string, { result: AnthropicApiKeyVerificationResult; at: number } @@ -402,12 +391,6 @@ export class ProviderConnectionService { this.codexModelCatalogFeature = feature; } - setKilocodeModelCatalogFeature( - feature: Pick | null - ): void { - this.kilocodeModelCatalogFeature = feature; - } - async getCodexModelCatalog( request: CodexModelCatalogRequest = {} ): Promise { @@ -435,10 +418,6 @@ export class ProviderConnectionService { return this.configManager.getConfig().providerConnections.codex.preferredAuthMode; } - if (providerId === 'kilocode') { - return 'api_key'; - } - return null; } @@ -620,16 +599,6 @@ export class ProviderConnectionService { return env; } - if (providerId === 'kilocode') { - const apiKey = await this.resolveProviderApiKeyForEnv(env, 'kilocode', options); - if (apiKey) { - env.KILO_API_KEY = apiKey; - } else if (typeof env.KILO_API_KEY === 'string' && !env.KILO_API_KEY.trim()) { - delete env.KILO_API_KEY; - } - return env; - } - if (providerId !== 'codex') { return env; } @@ -676,7 +645,7 @@ export class ProviderConnectionService { options?: StoredApiKeyAccessOptions ): Promise { let nextEnv = env; - for (const providerId of ['anthropic', 'codex', 'gemini', 'opencode', 'kilocode'] as const) { + for (const providerId of ['anthropic', 'codex', 'gemini', 'opencode'] as const) { nextEnv = await this.applyConfiguredConnectionEnv(nextEnv, providerId, undefined, options); } return nextEnv; @@ -712,14 +681,6 @@ export class ProviderConnectionService { return env; } - if (providerId === 'kilocode') { - const apiKey = await this.resolveProviderApiKeyForEnv(env, 'kilocode', options); - if (apiKey) { - env.KILO_API_KEY = apiKey; - } - return env; - } - if (providerId !== 'codex') { return env; } @@ -761,7 +722,7 @@ export class ProviderConnectionService { options?: StoredApiKeyAccessOptions ): Promise { let nextEnv = env; - for (const providerId of ['anthropic', 'codex', 'gemini', 'opencode', 'kilocode'] as const) { + for (const providerId of ['anthropic', 'codex', 'gemini', 'opencode'] as const) { nextEnv = await this.augmentConfiguredConnectionEnv(nextEnv, providerId, undefined, options); } return nextEnv; @@ -804,22 +765,6 @@ export class ProviderConnectionService { ); } - if (providerId === 'kilocode') { - if (typeof env.KILO_API_KEY === 'string' && env.KILO_API_KEY.trim()) { - return null; - } - - if (await this.hasStoredApiKey('KILO_API_KEY')) { - return null; - } - - if (this.getExternalCredential('kilocode')?.value.trim()) { - return null; - } - - return 'KiloCode API key is not configured. Set KILO_API_KEY or add it in Provider Settings.'; - } - if (providerId !== 'codex') { return null; } @@ -902,13 +847,7 @@ export class ProviderConnectionService { async getConfiguredConnectionIssues( env: NodeJS.ProcessEnv, - providerIds: readonly CliProviderId[] = [ - 'anthropic', - 'codex', - 'gemini', - 'opencode', - 'kilocode', - ], + providerIds: readonly CliProviderId[] = ['anthropic', 'codex', 'gemini', 'opencode'], runtimeBackendOverrides?: Partial> ): Promise>> { const issues: Partial> = {}; @@ -977,10 +916,6 @@ export class ProviderConnectionService { return this.enrichAnthropicProviderStatus(withConnection); } - if (provider.providerId === 'kilocode') { - return this.enrichKilocodeProviderStatus(withConnection); - } - if (provider.providerId !== 'codex') { return withConnection; } @@ -1045,33 +980,6 @@ export class ProviderConnectionService { } } - private async enrichKilocodeProviderStatus( - provider: CliProviderStatus - ): Promise { - if (!this.kilocodeModelCatalogFeature || !provider.connection?.apiKeyConfigured) { - return provider; - } - try { - const catalog = await this.kilocodeModelCatalogFeature.getCatalog({ - apiKey: await this.resolveStoredOrExternalProviderApiKey('kilocode'), - }); - if (catalog.status === 'unavailable' || catalog.models.length === 0) { - return provider; - } - const models = catalog.models - .filter((m) => !m.hidden) - .map((m) => m.launchModel.trim()) - .filter(Boolean); - return { - ...provider, - models: models.length > 0 ? models : provider.models, - modelCatalog: catalog, - }; - } catch { - return provider; - } - } - private async enrichAnthropicProviderStatus( provider: CliProviderStatus ): Promise { @@ -1288,46 +1196,6 @@ export class ProviderConnectionService { return this.apiKeyService.lookupPreferred(envVarName); } - private async resolveStoredOrExternalProviderApiKey( - providerId: CliProviderId, - options?: StoredApiKeyAccessOptions - ): Promise { - const envVarName = PROVIDER_API_KEY_ENV_VARS[providerId]; - if (!envVarName) { - return null; - } - - const storedKey = await this.lookupStoredApiKeyValue(envVarName, options); - if (storedKey?.value.trim()) { - return storedKey.value.trim(); - } - - return this.getExternalCredential(providerId)?.value.trim() || null; - } - - private async resolveProviderApiKeyForEnv( - env: NodeJS.ProcessEnv, - providerId: CliProviderId, - options?: StoredApiKeyAccessOptions - ): Promise { - const envVarName = PROVIDER_API_KEY_ENV_VARS[providerId]; - if (!envVarName) { - return null; - } - - const storedKey = await this.lookupStoredApiKeyValue(envVarName, options); - if (storedKey?.value.trim()) { - return storedKey.value.trim(); - } - - const existingValue = env[envVarName]; - if (typeof existingValue === 'string' && existingValue.trim()) { - return existingValue.trim(); - } - - return this.getExternalCredential(providerId)?.value.trim() || null; - } - private getConfiguredCodexRuntimeBackend(runtimeBackendOverride?: string | null): 'codex-native' { if (runtimeBackendOverride === CODEX_NATIVE_BACKEND_ID) { return runtimeBackendOverride; @@ -1509,16 +1377,6 @@ export class ProviderConnectionService { } } - if (providerId === 'kilocode') { - const apiKey = this.getExternalEnvValue('KILO_API_KEY'); - if (apiKey) { - return { - label: 'Detected from KILO_API_KEY', - value: apiKey, - }; - } - } - return null; } diff --git a/src/main/services/runtime/providerRuntimeEnv.ts b/src/main/services/runtime/providerRuntimeEnv.ts index 63c23497..d2db5be6 100644 --- a/src/main/services/runtime/providerRuntimeEnv.ts +++ b/src/main/services/runtime/providerRuntimeEnv.ts @@ -103,12 +103,7 @@ export function applyProviderRuntimeEnv( export function resolveRuntimeProviderId( providerId: RuntimeEnvProviderId | undefined ): CliProviderId { - if ( - providerId === 'codex' || - providerId === 'gemini' || - providerId === 'opencode' || - providerId === 'kilocode' - ) { + if (providerId === 'codex' || providerId === 'gemini' || providerId === 'opencode') { return providerId; } @@ -116,10 +111,7 @@ export function resolveRuntimeProviderId( } export function resolveTeamProviderId(providerId: TeamProviderId | undefined): TeamProviderId { - return providerId === 'codex' || - providerId === 'gemini' || - providerId === 'opencode' || - providerId === 'kilocode' + return providerId === 'codex' || providerId === 'gemini' || providerId === 'opencode' ? providerId : 'anthropic'; } diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index e9088c87..9da14501 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -1311,8 +1311,6 @@ function getProviderRuntimeFailureLabel(providerId: TeamProviderId): string { return 'Gemini runtime'; case 'opencode': return 'OpenCode runtime'; - case 'kilocode': - return 'KiloCode runtime'; } } @@ -16571,17 +16569,6 @@ export class TeamProvisioningService { ? Array.from(new Set(providerModelChecks.map((check) => check.modelId))) : selectedModelIds; - if (providerId === 'kilocode') { - const kilocodeConnection = - await this.providerConnectionService.getConnectionInfo('kilocode'); - if (!kilocodeConnection.apiKeyConfigured) { - blockingMessages.push( - 'KiloCode: API key not configured. Set KILO_API_KEY or add it in Provider Settings.' - ); - } - continue; - } - if (providerId === 'opencode') { const adapter = this.getOpenCodeRuntimeAdapter(); if (!adapter) { diff --git a/src/main/services/team/runtime/TeamRuntimeAdapter.ts b/src/main/services/team/runtime/TeamRuntimeAdapter.ts index cb732674..22a2d909 100644 --- a/src/main/services/team/runtime/TeamRuntimeAdapter.ts +++ b/src/main/services/team/runtime/TeamRuntimeAdapter.ts @@ -14,13 +14,7 @@ import type { TeamProvisioningSupportDiagnostic, } from '@shared/types'; -export const TEAM_RUNTIME_PROVIDER_IDS = [ - 'anthropic', - 'codex', - 'gemini', - 'opencode', - 'kilocode', -] as const; +export const TEAM_RUNTIME_PROVIDER_IDS = ['anthropic', 'codex', 'gemini', 'opencode'] as const; export type TeamRuntimeProviderId = (typeof TEAM_RUNTIME_PROVIDER_IDS)[number]; diff --git a/src/renderer/components/common/ProviderBrandLogo.tsx b/src/renderer/components/common/ProviderBrandLogo.tsx index ddd5e9bd..f0cfbf73 100644 --- a/src/renderer/components/common/ProviderBrandLogo.tsx +++ b/src/renderer/components/common/ProviderBrandLogo.tsx @@ -202,7 +202,5 @@ export const ProviderBrandLogo = ({ return ; case 'opencode': return ; - case 'kilocode': - return ; } }; diff --git a/src/renderer/components/dashboard/CliStatusBanner.tsx b/src/renderer/components/dashboard/CliStatusBanner.tsx index d330cc1a..acd87928 100644 --- a/src/renderer/components/dashboard/CliStatusBanner.tsx +++ b/src/renderer/components/dashboard/CliStatusBanner.tsx @@ -443,8 +443,6 @@ function getProviderLabel(providerId: CliProviderId): string { return 'Gemini'; case 'opencode': return 'OpenCode (200+ models)'; - case 'kilocode': - return 'KiloCode'; } } diff --git a/src/renderer/components/dashboard/providerDashboardRateLimits.ts b/src/renderer/components/dashboard/providerDashboardRateLimits.ts index e0d09212..80d18b03 100644 --- a/src/renderer/components/dashboard/providerDashboardRateLimits.ts +++ b/src/renderer/components/dashboard/providerDashboardRateLimits.ts @@ -288,7 +288,6 @@ export function getDashboardRateLimitsForProvider( return getAnthropicDashboardRateLimits(provider); case 'gemini': case 'opencode': - case 'kilocode': return null; } } diff --git a/src/renderer/components/extensions/ExtensionStoreView.tsx b/src/renderer/components/extensions/ExtensionStoreView.tsx index 9ebba874..df7e310c 100644 --- a/src/renderer/components/extensions/ExtensionStoreView.tsx +++ b/src/renderer/components/extensions/ExtensionStoreView.tsx @@ -62,7 +62,7 @@ const ProviderCapabilityCardSkeleton = ({ providerId, displayName, }: { - providerId: 'anthropic' | 'codex' | 'gemini' | 'opencode' | 'kilocode'; + providerId: 'anthropic' | 'codex' | 'gemini' | 'opencode'; displayName: string; }): React.JSX.Element => { const { t } = useAppTranslation('extensions'); diff --git a/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx b/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx index ff6d89f5..d494e068 100644 --- a/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +++ b/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx @@ -68,7 +68,7 @@ import type { CodexRuntimeStatus } from '@features/codex-runtime-installer/contr import type { CliProviderAuthMode, CliProviderId, CliProviderStatus } from '@shared/types'; import type { ApiKeyEntry } from '@shared/types/extensions'; -type ApiKeyProviderId = 'anthropic' | 'codex' | 'gemini' | 'kilocode'; +type ApiKeyProviderId = 'anthropic' | 'codex' | 'gemini'; type PendingConnectionAction = 'auto' | 'oauth' | 'chatgpt' | 'api_key' | 'compatible' | null; interface ConnectionMethodCardOption { @@ -98,7 +98,7 @@ interface Props { const API_KEY_PROVIDER_CONFIG: Record< ApiKeyProviderId, { - envVarName: 'ANTHROPIC_API_KEY' | 'OPENAI_API_KEY' | 'GEMINI_API_KEY' | 'KILO_API_KEY'; + envVarName: 'ANTHROPIC_API_KEY' | 'OPENAI_API_KEY' | 'GEMINI_API_KEY'; name: string; title: string; description: string; @@ -129,14 +129,6 @@ const API_KEY_PROVIDER_CONFIG: Record< 'Use `GEMINI_API_KEY` for the Gemini API backend. CLI SDK and ADC do not require it.', placeholder: 'AIza...', }, - kilocode: { - envVarName: 'KILO_API_KEY', - name: 'KiloCode API Key', - title: 'API key', - description: - 'Use your KiloCode API key to authenticate with the KiloCode gateway and load available models.', - placeholder: 'kc-...', - }, }; const API_KEY_PROVIDER_TRANSLATION_KEYS = { @@ -159,7 +151,7 @@ const API_KEY_PROVIDER_TRANSLATION_KEYS = { placeholder: 'providerRuntime.apiKey.providers.gemini.placeholder', }, } as const satisfies Record< - Exclude, + ApiKeyProviderId, { name: string; title: string; @@ -173,12 +165,7 @@ const ANTHROPIC_COMPATIBLE_AUTH_TOKEN_NAME = 'Anthropic-compatible Auth Token'; const FIRST_PARTY_ANTHROPIC_HOSTS = new Set(['api.anthropic.com', 'api-staging.anthropic.com']); function isApiKeyProviderId(providerId: CliProviderId): providerId is ApiKeyProviderId { - return ( - providerId === 'anthropic' || - providerId === 'codex' || - providerId === 'gemini' || - providerId === 'kilocode' - ); + return providerId === 'anthropic' || providerId === 'codex' || providerId === 'gemini'; } function isCodexRuntimeInstalling( @@ -256,8 +243,6 @@ function getConnectionDescription( return t('providerRuntime.connection.descriptions.gemini'); case 'opencode': return t('providerRuntime.connection.descriptions.opencode'); - case 'kilocode': - return 'KiloCode uses an API key for authentication with the KiloCode gateway.'; } } @@ -274,8 +259,6 @@ function getRuntimeDescription( return t('providerRuntime.runtime.descriptions.gemini'); case 'opencode': return t('providerRuntime.runtime.descriptions.opencode'); - case 'kilocode': - return 'KiloCode uses its own managed runtime host. Configure an API key to use the KiloCode gateway.'; } } @@ -308,10 +291,6 @@ function getAuthModeDescription( } } - if (providerId === 'kilocode' && authMode === 'api_key') { - return 'Use a KiloCode API key for gateway access.'; - } - return ''; } @@ -1047,10 +1026,9 @@ export const ProviderRuntimeSettingsDialog = ({ ? selectedProvider.providerId : null; const apiKeyConfig = apiKeyProviderId ? API_KEY_PROVIDER_CONFIG[apiKeyProviderId] : null; - const apiKeyTranslationKeys = - apiKeyProviderId && apiKeyProviderId !== 'kilocode' - ? API_KEY_PROVIDER_TRANSLATION_KEYS[apiKeyProviderId] - : null; + const apiKeyTranslationKeys = apiKeyProviderId + ? API_KEY_PROVIDER_TRANSLATION_KEYS[apiKeyProviderId] + : null; const apiKeyDisplayConfig = apiKeyTranslationKeys ? { title: t(apiKeyTranslationKeys.title), @@ -1058,7 +1036,7 @@ export const ProviderRuntimeSettingsDialog = ({ name: t(apiKeyTranslationKeys.name), placeholder: t(apiKeyTranslationKeys.placeholder), } - : apiKeyConfig; + : null; const showApiKeyForm = selectedProvider && isApiKeyProviderId(selectedProvider.providerId) && diff --git a/src/renderer/components/settings/sections/CliStatusSection.tsx b/src/renderer/components/settings/sections/CliStatusSection.tsx index 84c9a644..c6e4c986 100644 --- a/src/renderer/components/settings/sections/CliStatusSection.tsx +++ b/src/renderer/components/settings/sections/CliStatusSection.tsx @@ -124,8 +124,6 @@ function getProviderLabel(providerId: CliProviderId): string { return 'Gemini'; case 'opencode': return 'OpenCode (200+ models)'; - case 'kilocode': - return 'KiloCode'; } } diff --git a/src/renderer/components/sidebar/DateGroupedSessions.tsx b/src/renderer/components/sidebar/DateGroupedSessions.tsx index f02e984e..6baefabb 100644 --- a/src/renderer/components/sidebar/DateGroupedSessions.tsx +++ b/src/renderer/components/sidebar/DateGroupedSessions.tsx @@ -411,7 +411,6 @@ export const DateGroupedSessions = memo((): React.JSX.Element => { codex: 0, gemini: 0, opencode: 0, - kilocode: 0, }; for (const session of searchedSessions) { diff --git a/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx b/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx index cf194b60..3fcf651d 100644 --- a/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +++ b/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx @@ -65,7 +65,6 @@ const PROVIDER_LABELS: Record = { codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', - kilocode: 'KiloCode', }; function getProviderLabel(providerId: TeamProviderId): string { diff --git a/src/renderer/store/slices/cliInstallerSlice.ts b/src/renderer/store/slices/cliInstallerSlice.ts index 9c1bd901..e4410c3c 100644 --- a/src/renderer/store/slices/cliInstallerSlice.ts +++ b/src/renderer/store/slices/cliInstallerSlice.ts @@ -28,24 +28,14 @@ const CODEX_PROVIDER_INSTALL_REFRESH_ATTEMPTS = 3; const CODEX_PROVIDER_INSTALL_REFRESH_RETRY_DELAY_MS = 700; export const MULTIMODEL_PROVIDER_IDS: CliProviderId[] = isGeminiUiFrozen() - ? ['anthropic', 'codex', 'opencode', 'kilocode'] - : ['anthropic', 'codex', 'gemini', 'opencode', 'kilocode']; -const MULTIMODEL_PROVIDER_HYDRATION_IDS: CliProviderId[] = isGeminiUiFrozen() ? ['anthropic', 'codex', 'opencode'] : ['anthropic', 'codex', 'gemini', 'opencode']; const MULTIMODEL_PROVIDER_ID_SET = new Set(MULTIMODEL_PROVIDER_IDS); -const MULTIMODEL_PROVIDER_HYDRATION_ID_SET = new Set( - MULTIMODEL_PROVIDER_HYDRATION_IDS -); function isActiveMultimodelProviderId(providerId: CliProviderId): boolean { return MULTIMODEL_PROVIDER_ID_SET.has(providerId); } -function isHydratableMultimodelProviderId(providerId: CliProviderId): boolean { - return MULTIMODEL_PROVIDER_HYDRATION_ID_SET.has(providerId); -} - export function createLoadingMultimodelCliStatus(): CliInstallationStatus { const providers: CliProviderStatus[] = MULTIMODEL_PROVIDER_IDS.map((providerId) => ({ providerId, @@ -269,7 +259,7 @@ export function getIncompleteMultimodelProviderIds( return status.providers .filter( (provider) => - isHydratableMultimodelProviderId(provider.providerId) && + isActiveMultimodelProviderId(provider.providerId) && !isHydratedMultimodelProviderStatus(provider) ) .map((provider) => provider.providerId); @@ -285,7 +275,7 @@ export function getModelOnlyFallbackProviderIds( return status.providers .filter( (provider) => - isHydratableMultimodelProviderId(provider.providerId) && + isActiveMultimodelProviderId(provider.providerId) && isModelOnlyFallbackProviderStatus(provider) ) .map((provider) => provider.providerId); @@ -303,7 +293,7 @@ export function reconcileMultimodelProviderLoading( const providersById = new Map( status.providers.map((provider) => [provider.providerId, provider]) ); - return MULTIMODEL_PROVIDER_HYDRATION_IDS.reduce>>( + return MULTIMODEL_PROVIDER_IDS.reduce>>( (nextLoading, providerId) => { const provider = providersById.get(providerId); return { @@ -575,9 +565,7 @@ function isMultimodelCliStatus( function hasActiveProviderStatusLoading( providerLoading: Partial> ): boolean { - return MULTIMODEL_PROVIDER_HYDRATION_IDS.some( - (providerId) => providerLoading[providerId] === true - ); + return MULTIMODEL_PROVIDER_IDS.some((providerId) => providerLoading[providerId] === true); } function getAuthenticatedProvider(providers: CliProviderStatus[]): CliProviderStatus | null { @@ -616,8 +604,6 @@ function getProviderDisplayName(providerId: CliProviderId): string { return 'Gemini'; case 'opencode': return 'OpenCode (200+ models)'; - case 'kilocode': - return 'KiloCode'; } } @@ -771,7 +757,7 @@ export const createCliInstallerSlice: StateCreator [ + MULTIMODEL_PROVIDER_IDS.map((providerId) => [ providerId, shouldMarkIncompleteProvidersLoading && initialStatus.installed && @@ -822,14 +808,14 @@ export const createCliInstallerSlice: StateCreator [ + MULTIMODEL_PROVIDER_IDS.map((providerId) => [ providerId, !isHydratedMultimodelProviderStatus( nextCliStatus.providers.find((provider) => provider.providerId === providerId) ), ]) ) as Partial>; - pendingProviderIds = MULTIMODEL_PROVIDER_HYDRATION_IDS.filter( + pendingProviderIds = MULTIMODEL_PROVIDER_IDS.filter( (providerId) => nextProviderLoading[providerId] === true ); const nextAuthState = isMultimodelCliStatus(nextCliStatus) @@ -881,7 +867,7 @@ export const createCliInstallerSlice: StateCreator + MULTIMODEL_PROVIDER_IDS.map((providerId) => get().fetchCliProviderStatus(providerId, { silent: false, epoch, @@ -925,7 +911,7 @@ export const createCliInstallerSlice: StateCreator = { codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', - kilocode: 'KiloCode', }; const ANTHROPIC_ALIAS_LABELS = { @@ -141,7 +140,6 @@ const TEAM_PROVIDER_MODEL_OPTIONS: Record> = { @@ -151,9 +149,6 @@ const TEAM_PROVIDER_MODEL_ORDER: Record opencode: new Map( TEAM_PROVIDER_MODEL_OPTIONS.opencode.map((option, index) => [option.value, index]) ), - kilocode: new Map( - TEAM_PROVIDER_MODEL_OPTIONS.kilocode.map((option, index) => [option.value, index]) - ), }; function getKnownTeamProviderModelOption( @@ -342,9 +337,6 @@ export function getTeamModelBadgeLabel( if (providerId === 'opencode') { return getTeamModelLabel(trimmed) ?? trimmed; } - if (providerId === 'kilocode') { - return getTeamModelLabel(trimmed) ?? trimmed; - } return trimmed; } diff --git a/src/shared/types/cliInstaller.ts b/src/shared/types/cliInstaller.ts index 08a16268..391f51e7 100644 --- a/src/shared/types/cliInstaller.ts +++ b/src/shared/types/cliInstaller.ts @@ -33,7 +33,7 @@ export type CliPlatform = export type CliFlavor = 'claude' | 'agent_teams_orchestrator'; -export type CliProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode' | 'kilocode'; +export type CliProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode'; export type CliProviderAuthMode = 'auto' | 'oauth' | 'chatgpt' | 'api_key'; export const CLI_PROVIDER_STATUS_DEFERRED_MESSAGE = 'Provider status will refresh when needed.'; diff --git a/src/shared/types/team.ts b/src/shared/types/team.ts index aa5dc98c..cff49c08 100644 --- a/src/shared/types/team.ts +++ b/src/shared/types/team.ts @@ -966,7 +966,7 @@ export interface TeamViewSnapshot { } export type EffortLevel = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'; -export type TeamProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode' | 'kilocode'; +export type TeamProviderId = 'anthropic' | 'codex' | 'gemini' | 'opencode'; export type TeamProviderBackendId = | 'auto' | 'adapter' diff --git a/src/shared/utils/teamProvider.ts b/src/shared/utils/teamProvider.ts index e93d35b2..c0857f31 100644 --- a/src/shared/utils/teamProvider.ts +++ b/src/shared/utils/teamProvider.ts @@ -3,13 +3,7 @@ import { parseOpenCodeQualifiedModelRef } from './opencodeModelRef'; import type { TeamProviderId } from '@shared/types'; export function isTeamProviderId(value: unknown): value is TeamProviderId { - return ( - value === 'anthropic' || - value === 'codex' || - value === 'gemini' || - value === 'opencode' || - value === 'kilocode' - ); + return value === 'anthropic' || value === 'codex' || value === 'gemini' || value === 'opencode'; } export function normalizeOptionalTeamProviderId(value: unknown): TeamProviderId | undefined { @@ -39,15 +33,6 @@ export function inferTeamProviderIdFromModel( return 'opencode'; } - if ( - normalized.startsWith('kilocode/') || - normalizedWithoutExtendedContextSuffix.startsWith('kilocode/') || - normalized.startsWith('kilo/') || - normalizedWithoutExtendedContextSuffix.startsWith('kilo/') - ) { - return 'kilocode'; - } - if ( normalized.startsWith('gpt-') || normalized.startsWith('codex') || diff --git a/test/main/services/infrastructure/CliInstallerService.test.ts b/test/main/services/infrastructure/CliInstallerService.test.ts index 05df5ac2..4ba73b75 100644 --- a/test/main/services/infrastructure/CliInstallerService.test.ts +++ b/test/main/services/infrastructure/CliInstallerService.test.ts @@ -239,7 +239,6 @@ describe('CliInstallerService', () => { 'anthropic', 'codex', 'opencode', - 'kilocode', ]); expect(openCodeStatus).toMatchObject({ displayName: 'OpenCode (200+ models)', @@ -336,7 +335,6 @@ describe('CliInstallerService', () => { 'anthropic', 'codex', 'opencode', - 'kilocode', ]); expect(status.authLoggedIn).toBe(false); expect(status.authMethod).toBeNull(); @@ -382,7 +380,7 @@ describe('CliInstallerService', () => { expect(resolveInteractiveShellEnvBestEffortMock).not.toHaveBeenCalled(); expect(status.authStatusChecking).toBe(false); expect(status.authLoggedIn).toBe(false); - expect(status.providers).toHaveLength(4); + expect(status.providers).toHaveLength(3); expect( status.providers.every( (provider) => provider.statusMessage === 'Provider status will refresh when needed.' @@ -1215,12 +1213,13 @@ describe('CliInstallerService', () => { createTestProviderStatus('codex', false, null), createTestProviderStatus('opencode', false, null), ]); - await vi.waitFor(() => { - const latest = service.getLatestStatusSnapshot(); - expect(latest?.authStatusChecking).toBe(false); - expect(latest?.authLoggedIn).toBe(true); - expect(latest?.authMethod).toBe('oauth_token'); - }); + await Promise.resolve(); + await Promise.resolve(); + + const latest = service.getLatestStatusSnapshot(); + expect(latest?.authStatusChecking).toBe(false); + expect(latest?.authLoggedIn).toBe(true); + expect(latest?.authMethod).toBe('oauth_token'); expect(status.authStatusChecking).toBe(true); expect(status.authLoggedIn).toBe(false); expect(status.providers.every((provider) => provider.statusMessage === 'Checking...')).toBe( diff --git a/test/main/services/runtime/ProviderConnectionService.test.ts b/test/main/services/runtime/ProviderConnectionService.test.ts index 372acb07..2edded8e 100644 --- a/test/main/services/runtime/ProviderConnectionService.test.ts +++ b/test/main/services/runtime/ProviderConnectionService.test.ts @@ -40,7 +40,6 @@ describe('ProviderConnectionService', () => { const originalAnthropicApiKey = process.env.ANTHROPIC_API_KEY; const originalAnthropicAuthToken = process.env.ANTHROPIC_AUTH_TOKEN; const originalAnthropicBaseUrl = process.env.ANTHROPIC_BASE_URL; - const originalKiloApiKey = process.env.KILO_API_KEY; function createConfig( authMode: 'auto' | 'oauth' | 'api_key' = 'auto', @@ -137,7 +136,6 @@ describe('ProviderConnectionService', () => { delete process.env.ANTHROPIC_API_KEY; delete process.env.ANTHROPIC_AUTH_TOKEN; delete process.env.ANTHROPIC_BASE_URL; - delete process.env.KILO_API_KEY; }); afterEach(() => { @@ -170,12 +168,6 @@ describe('ProviderConnectionService', () => { } else { process.env.ANTHROPIC_BASE_URL = originalAnthropicBaseUrl; } - - if (originalKiloApiKey === undefined) { - delete process.env.KILO_API_KEY; - } else { - process.env.KILO_API_KEY = originalKiloApiKey; - } }); it('removes Anthropic environment credentials when OAuth mode is selected', async () => { @@ -647,126 +639,6 @@ describe('ProviderConnectionService', () => { expect(result.GEMINI_API_KEY).toBe('gemini-stored-key'); }); - it('injects stored KiloCode API keys for runtime launches', async () => { - const lookupPreferred = vi.fn().mockResolvedValue({ - envVarName: 'KILO_API_KEY', - value: 'kilo-stored-key', - }); - const { ProviderConnectionService } = - await import('@main/services/runtime/ProviderConnectionService'); - - const service = new ProviderConnectionService( - { - lookupPreferred, - } as never, - { - getConfig: () => createConfig('auto'), - } as never - ); - - const result = await service.applyConfiguredConnectionEnv({}, 'kilocode'); - - expect(lookupPreferred).toHaveBeenCalledWith('KILO_API_KEY'); - expect(result.KILO_API_KEY).toBe('kilo-stored-key'); - await expect(service.getConfiguredConnectionIssue(result, 'kilocode')).resolves.toBeNull(); - }); - - it('reports a missing KiloCode API key before runtime launches', async () => { - const { ProviderConnectionService } = - await import('@main/services/runtime/ProviderConnectionService'); - - const service = new ProviderConnectionService( - { - lookupPreferred: vi.fn().mockResolvedValue(null), - } as never, - { - getConfig: () => createConfig('auto'), - } as never - ); - - const issue = await service.getConfiguredConnectionIssue({}, 'kilocode'); - - expect(issue).toContain('KiloCode API key is not configured'); - }); - - it('passes stored KiloCode API keys to catalog hydration', async () => { - const lookupPreferred = vi.fn().mockResolvedValue({ - envVarName: 'KILO_API_KEY', - value: 'kilo-stored-key', - }); - const getCatalog = vi.fn().mockResolvedValue({ - schemaVersion: 1, - providerId: 'kilocode', - source: 'app-server', - status: 'ready', - fetchedAt: '2026-05-25T00:00:00.000Z', - staleAt: '2026-05-25T00:10:00.000Z', - defaultModelId: 'kilo/test', - defaultLaunchModel: 'kilo/test', - models: [ - { - id: 'kilo/test', - launchModel: 'kilo/test', - displayName: 'Kilo Test', - hidden: false, - supportedReasoningEfforts: [], - defaultReasoningEffort: null, - inputModalities: ['text'], - supportsPersonality: false, - isDefault: true, - upgrade: false, - source: 'app-server', - }, - ], - diagnostics: { - configReadState: 'skipped', - appServerState: 'healthy', - message: null, - code: null, - }, - }); - const { ProviderConnectionService } = - await import('@main/services/runtime/ProviderConnectionService'); - - const service = new ProviderConnectionService( - { - lookupPreferred, - } as never, - { - getConfig: () => createConfig('auto'), - } as never - ); - service.setKilocodeModelCatalogFeature({ getCatalog }); - - const enriched = await service.enrichProviderStatus({ - providerId: 'kilocode', - displayName: 'KiloCode', - supported: true, - authenticated: true, - authMethod: 'api_key', - verificationState: 'verified', - modelVerificationState: 'idle', - statusMessage: null, - models: [], - modelAvailability: [], - canLoginFromUi: true, - capabilities: { - teamLaunch: true, - oneShot: false, - extensions: { - plugins: { supported: false, status: 'unsupported' }, - mcp: { supported: false, status: 'unsupported' }, - skills: { supported: false, status: 'unsupported' }, - apiKeys: { supported: true, status: 'supported' }, - }, - }, - backend: null, - } as never); - - expect(getCatalog).toHaveBeenCalledWith({ apiKey: 'kilo-stored-key' }); - expect(enriched.models).toEqual(['kilo/test']); - }); - it('reports a missing Anthropic API key when api_key mode is selected', async () => { const { ProviderConnectionService } = await import('@main/services/runtime/ProviderConnectionService'); diff --git a/test/renderer/components/cli/CliStatusVisibility.test.ts b/test/renderer/components/cli/CliStatusVisibility.test.ts index f9053661..b6168105 100644 --- a/test/renderer/components/cli/CliStatusVisibility.test.ts +++ b/test/renderer/components/cli/CliStatusVisibility.test.ts @@ -2696,7 +2696,7 @@ describe('CLI status visibility during completed install state', () => { await Promise.resolve(); }); - expect(host.textContent).toContain('Providers: 1/4 connected'); + expect(host.textContent).toContain('Providers: 1/3 connected'); expect(host.textContent).toContain('5h left'); expect(host.textContent).toContain('1w left'); expect(host.textContent).toContain('resets'); diff --git a/test/renderer/components/extensions/skills/SkillsPanel.test.ts b/test/renderer/components/extensions/skills/SkillsPanel.test.ts index cfab8053..b71bae74 100644 --- a/test/renderer/components/extensions/skills/SkillsPanel.test.ts +++ b/test/renderer/components/extensions/skills/SkillsPanel.test.ts @@ -1,12 +1,11 @@ import React, { act } from 'react'; import { createRoot } from 'react-dom/client'; - -import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { CodexAccountSnapshotDto } from '@features/codex-account/contracts'; import type { CliInstallationStatus } from '@shared/types'; import type { SkillCatalogItem } from '@shared/types/extensions'; +import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities'; interface StoreState { fetchSkillsCatalog: ReturnType; @@ -560,7 +559,7 @@ describe('SkillsPanel', () => { }); expect(host.textContent).toContain( - 'Shared skills in `.claude`, `.cursor`, and `.agents` are available to Anthropic, Codex, OpenCode (200+ models), and KiloCode.' + 'Shared skills in `.claude`, `.cursor`, and `.agents` are available to Anthropic, Codex, and OpenCode (200+ models).' ); expect(host.textContent).toContain('Codex only'); diff --git a/test/setup.ts b/test/setup.ts index 0ebb1f8f..04627612 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -62,49 +62,6 @@ vi.mock('@sentry/electron/main', () => sentryNoOp); vi.mock('@sentry/electron/renderer', () => sentryNoOp); vi.mock('@sentry/react', () => sentryNoOp); -function createInMemoryStorage(): Storage { - const values = new Map(); - - return { - get length() { - return values.size; - }, - clear() { - values.clear(); - }, - getItem(key: string) { - return values.get(key) ?? null; - }, - key(index: number) { - return Array.from(values.keys())[index] ?? null; - }, - removeItem(key: string) { - values.delete(key); - }, - setItem(key: string, value: string) { - values.set(key, String(value)); - }, - }; -} - -function hasStorageApi(value: unknown): value is Storage { - return ( - typeof value === 'object' && - value !== null && - typeof (value as Storage).getItem === 'function' && - typeof (value as Storage).setItem === 'function' && - typeof (value as Storage).removeItem === 'function' && - typeof (value as Storage).clear === 'function' - ); -} - -if (!hasStorageApi(globalThis.localStorage)) { - Object.defineProperty(globalThis, 'localStorage', { - configurable: true, - value: createInMemoryStorage(), - }); -} - // Mock HOME for tests that need a predictable home path. It must be writable: // some services persist state in best-effort background writes after a test has // already reset path overrides.