From f068b7de5c46640a6a51f9e03f763301e48eb68b Mon Sep 17 00:00:00 2001 From: james burke Date: Tue, 16 Dec 2025 00:20:50 -0500 Subject: [PATCH 01/15] fix(ollama): fix model not found error and context window display - Fix race condition where getModel() was called before initialization completed - Add initializationPromise to properly await async initialization - Fix UI context window display to use user's ollamaNumCtx setting - User's explicit context window setting now always takes precedence - Provide fallback model info when Ollama models can't be fetched - Add improved error messages with troubleshooting steps --- src/api/providers/fetchers/ollama.ts | 2 +- src/api/providers/native-ollama.ts | 83 ++++++++++++++----- .../components/ui/hooks/useSelectedModel.ts | 24 ++++-- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/api/providers/fetchers/ollama.ts b/src/api/providers/fetchers/ollama.ts index 8581b71d7b7..49fecd940fc 100644 --- a/src/api/providers/fetchers/ollama.ts +++ b/src/api/providers/fetchers/ollama.ts @@ -127,7 +127,7 @@ export async function getOllamaModels( } else { console.error(`Error parsing Ollama models response: ${JSON.stringify(parsedResponse.error, null, 2)}`) } - } catch (error) { + } catch (error: any) { if (error.code === "ECONNREFUSED") { console.warn(`Failed connecting to Ollama at ${baseUrl}`) } else { diff --git a/src/api/providers/native-ollama.ts b/src/api/providers/native-ollama.ts index b986049c44e..9aea0da02d3 100644 --- a/src/api/providers/native-ollama.ts +++ b/src/api/providers/native-ollama.ts @@ -1,7 +1,12 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import { Message, Ollama, Tool as OllamaTool, type Config as OllamaOptions } from "ollama" -import { ModelInfo, openAiModelInfoSaneDefaults, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types" +import { + ModelInfo, + openAiModelInfoSaneDefaults, + DEEP_SEEK_DEFAULT_TEMPERATURE, + ollamaDefaultModelInfo, +} from "@roo-code/types" import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" import type { ApiHandlerOptions } from "../../shared/api" @@ -162,11 +167,13 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio private client: Ollama | undefined protected models: Record = {} private isInitialized = false // kilocode_change + private modelFetchError: string | null = null // kilocode_change - track fetch errors for better diagnostics + private initializationPromise: Promise | null = null // kilocode_change - prevent race condition constructor(options: ApiHandlerOptions) { super() this.options = options - this.initialize() // kilocode_change + this.initializationPromise = this.initialize() // kilocode_change - store the promise } // kilocode_change start @@ -177,6 +184,12 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio await this.fetchModel() this.isInitialized = true } + + private async ensureInitialized(): Promise { + if (this.initializationPromise) { + await this.initializationPromise + } + } // kilocode_change end private ensureClient(): Ollama { @@ -230,9 +243,7 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio metadata?: ApiHandlerCreateMessageMetadata, ): ApiStream { // kilocode_change start - if (!this.isInitialized) { - await this.initialize() - } + await this.ensureInitialized() // kilocode_change end const client = this.ensureClient() @@ -376,42 +387,76 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio async fetchModel() { // kilocode_change start - this.models = await getOllamaModels( - this.options.ollamaBaseUrl, - this.options.ollamaApiKey, - this.options.ollamaNumCtx, - ) + try { + this.modelFetchError = null + this.models = await getOllamaModels( + this.options.ollamaBaseUrl, + this.options.ollamaApiKey, + this.options.ollamaNumCtx, + ) + if (Object.keys(this.models).length === 0) { + this.modelFetchError = "noModelsReturned" + } + } catch (error: any) { + this.modelFetchError = error.message || "unknownError" + this.models = {} + } return this.models // kilocode_change end } override getModel(): { id: string; info: ModelInfo } { const modelId = this.options.ollamaModelId || "" + const userContextWindow = this.options.ollamaNumCtx // kilocode_change start + // If not yet initialized, return default model info to allow getEnvironmentDetails to work + // The actual model validation will happen in createMessage() after ensureInitialized() + if (!this.isInitialized) { + const contextWindow = userContextWindow || ollamaDefaultModelInfo.contextWindow + return { + id: modelId, + info: { + ...ollamaDefaultModelInfo, + contextWindow: contextWindow, + }, + } + } + const modelInfo = this.models[modelId] if (!modelInfo) { const availableModels = Object.keys(this.models) - const errorMessage = - availableModels.length > 0 - ? `Model ${modelId} not found. Available models: ${availableModels.join(", ")}` - : `Model ${modelId} not found. No models available.` - throw new Error(errorMessage) + if (availableModels.length > 0) { + throw new Error(`Model ${modelId} not found. Available models: ${availableModels.join(", ")}`) + } + + const baseUrl = this.options.ollamaBaseUrl || "http://localhost:11434" + const troubleshooting = [ + `1. Ensure Ollama is running (try: ollama serve)`, + `2. Verify the base URL is correct: ${baseUrl}`, + `3. Check that models are installed (try: ollama list)`, + `4. Pull the model if needed: ollama pull ${modelId}`, + ].join("\n") + + throw new Error( + `Model ${modelId} not found. Could not retrieve models from Ollama.\n\nTroubleshooting:\n${troubleshooting}`, + ) } + + // Override contextWindow with user's setting if provided + const finalModelInfo = userContextWindow ? { ...modelInfo, contextWindow: userContextWindow } : modelInfo // kilocode_change end return { id: modelId, - info: modelInfo, // kilocode_change + info: finalModelInfo, // kilocode_change } } async completePrompt(prompt: string): Promise { try { // kilocode_change start - if (!this.isInitialized) { - await this.initialize() - } + await this.ensureInitialized() // kilocode_change end const client = this.ensureClient() diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index d7a195e5e52..60679915cc4 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -329,22 +329,34 @@ function getSelectedModel({ const info = apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults return { id, info } } + // kilocode_change start - improved context window handling case "ollama": { const id = apiConfiguration.ollamaModelId ?? "" const info = ollamaModels && ollamaModels[apiConfiguration.ollamaModelId!] + const userContextWindow = apiConfiguration?.ollamaNumCtx - const adjustedInfo = - info?.contextWindow && - apiConfiguration?.ollamaNumCtx && - apiConfiguration.ollamaNumCtx < info.contextWindow - ? { ...info, contextWindow: apiConfiguration.ollamaNumCtx } - : info + // If user has set ollamaNumCtx, always use it as the context window (user's explicit setting takes precedence) + // If no user setting, use the fetched model info's context window + // If neither, provide a sensible default so UI doesn't show undefined + let adjustedInfo: ModelInfo | undefined + if (info) { + adjustedInfo = userContextWindow ? { ...info, contextWindow: userContextWindow } : info + } else if (userContextWindow) { + // No fetched model info but user has set context window - provide default model info with user's setting + adjustedInfo = { + maxTokens: userContextWindow, + contextWindow: userContextWindow, + supportsImages: true, + supportsPromptCache: true, + } + } return { id, info: adjustedInfo || undefined, } } + // kilocode_change end case "lmstudio": { const id = apiConfiguration.lmStudioModelId ?? "" const info = lmStudioModels && lmStudioModels[apiConfiguration.lmStudioModelId!] From 19875d009280c39f5e6de9f797ab77799133cbac Mon Sep 17 00:00:00 2001 From: CrazyRabbit Date: Thu, 18 Dec 2025 22:21:51 +0200 Subject: [PATCH 02/15] chore: update Gemini Cli models and metadata - Added gemini-3-flash-preview model configuration. - Updated the default model to gemini-3-flash-preview. - Updated maxThinkingTokens for gemini-3-pro-preview to 32,768. - Reordered model definitions to prioritize newer versions. --- packages/types/src/providers/gemini-cli.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/types/src/providers/gemini-cli.ts b/packages/types/src/providers/gemini-cli.ts index 6ec6ccc07c5..f959dc1dbc0 100644 --- a/packages/types/src/providers/gemini-cli.ts +++ b/packages/types/src/providers/gemini-cli.ts @@ -4,18 +4,28 @@ import type { ModelInfo } from "../model.js" // Gemini CLI models with free tier pricing (all $0) export type GeminiCliModelId = keyof typeof geminiCliModels -export const geminiCliDefaultModelId: GeminiCliModelId = "gemini-2.5-flash" +export const geminiCliDefaultModelId: GeminiCliModelId = "gemini-3-flash-preview" export const geminiCliModels = { - "gemini-2.5-flash": { + "gemini-3-pro-preview": { + maxTokens: 64_000, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + supportsReasoningBudget: true, + maxThinkingTokens: 32_768, + }, + "gemini-3-flash-preview": { maxTokens: 64_000, contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: false, inputPrice: 0, outputPrice: 0, - maxThinkingTokens: 24_576, supportsReasoningBudget: true, + maxThinkingTokens: 32_768, }, "gemini-2.5-pro": { maxTokens: 64_000, @@ -28,14 +38,14 @@ export const geminiCliModels = { supportsReasoningBudget: true, requiredReasoningBudget: true, }, - "gemini-3-pro-preview": { + "gemini-2.5-flash": { maxTokens: 64_000, contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: false, inputPrice: 0, outputPrice: 0, + maxThinkingTokens: 24_576, supportsReasoningBudget: true, - maxThinkingTokens: 64_000, }, } as const satisfies Record From 5bdfe6b9b68acf345e302791c15291c05a043204 Mon Sep 17 00:00:00 2001 From: CrazyRabbit Date: Thu, 18 Dec 2025 22:42:01 +0200 Subject: [PATCH 03/15] Update Gemini CLI models and metadata Updated Gemini CLI models and metadata, added new model configuration and adjusted settings for existing models. --- .changeset/large-jars-train.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/large-jars-train.md diff --git a/.changeset/large-jars-train.md b/.changeset/large-jars-train.md new file mode 100644 index 00000000000..04e61b9aa55 --- /dev/null +++ b/.changeset/large-jars-train.md @@ -0,0 +1,9 @@ +--- +"@roo-code/types": patch +--- + +chore: update Gemini Cli models and metadata +- Added gemini-3-flash-preview model configuration. +- Updated the default model to gemini-3-flash-preview. +- Updated maxThinkingTokens for gemini-3-pro-preview to 32,768. +- Reordered model definitions to prioritize newer versions. From f7c3715b4b7fea9fcd363d12bfb9467e9f169729 Mon Sep 17 00:00:00 2001 From: Kevin van Dijk Date: Fri, 19 Dec 2025 00:21:24 +0100 Subject: [PATCH 04/15] Add changeset --- .changeset/yellow-plants-work.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/yellow-plants-work.md diff --git a/.changeset/yellow-plants-work.md b/.changeset/yellow-plants-work.md new file mode 100644 index 00000000000..9bb67bcd17f --- /dev/null +++ b/.changeset/yellow-plants-work.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +fix(ollama): fix model not found error and context window display From c018a5a6fe7d26d647a5a010889c5de6e96fdc98 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 19 Dec 2025 11:17:49 +0100 Subject: [PATCH 05/15] fix(am): Improve agent manager and agent startup performance (#4567) * tmp * feat(agent-manager): add CliSessionLauncher with pre-warming support - Extract CLI session launching logic into dedicated CliSessionLauncher class - Pre-warm slow lookups (CLI path: 500-2000ms, git URL: 50-100ms) on panel open - Run CLI path, git URL, and API config lookups in parallel during spawn - Add handleSessionRenamed callback for provisional session upgrades - Clear pre-warm state when panel closes or on dispose * Extract and simplify * Extract renameMapKey --- .../agent-manager/AgentManagerProvider.ts | 94 ++++---- .../kilocode/agent-manager/AgentRegistry.ts | 25 ++ .../agent-manager/CliProcessHandler.ts | 97 +++++++- .../agent-manager/CliSessionLauncher.ts | 214 +++++++++++++++++ .../__tests__/AgentManagerProvider.spec.ts | 19 +- .../__tests__/CliProcessHandler.spec.ts | 220 ++++++++++++++++++ .../agent-manager/__tests__/mapUtils.spec.ts | 45 ++++ src/core/kilocode/agent-manager/mapUtils.ts | 14 ++ 8 files changed, 671 insertions(+), 57 deletions(-) create mode 100644 src/core/kilocode/agent-manager/CliSessionLauncher.ts create mode 100644 src/core/kilocode/agent-manager/__tests__/mapUtils.spec.ts create mode 100644 src/core/kilocode/agent-manager/mapUtils.ts diff --git a/src/core/kilocode/agent-manager/AgentManagerProvider.ts b/src/core/kilocode/agent-manager/AgentManagerProvider.ts index fd130245965..00ad935cd61 100644 --- a/src/core/kilocode/agent-manager/AgentManagerProvider.ts +++ b/src/core/kilocode/agent-manager/AgentManagerProvider.ts @@ -3,27 +3,27 @@ import * as fs from "node:fs" import * as path from "node:path" import { t } from "i18next" import { AgentRegistry } from "./AgentRegistry" +import { renameMapKey } from "./mapUtils" import { parseParallelModeBranch, parseParallelModeWorktreePath, isParallelModeCompletionMessage, parseParallelModeCompletionBranch, } from "./parallelModeParser" -import { findKilocodeCli } from "./CliPathResolver" import { canInstallCli, getCliInstallCommand, getLocalCliInstallCommand, getLocalCliBinDir } from "./CliInstaller" import { CliProcessHandler, type CliProcessHandlerCallbacks } from "./CliProcessHandler" import type { StreamEvent, KilocodeStreamEvent, KilocodePayload, WelcomeStreamEvent } from "./CliOutputParser" import { extractRawText, tryParsePayloadJson } from "./askErrorParser" import { RemoteSessionService } from "./RemoteSessionService" import { KilocodeEventProcessor } from "./KilocodeEventProcessor" +import { CliSessionLauncher } from "./CliSessionLauncher" import type { RemoteSession } from "./types" import { getUri } from "../../webview/getUri" import { getNonce } from "../../webview/getNonce" import { getViteDevServerConfig } from "../../webview/getViteDevServerConfig" import { getRemoteUrl } from "../../../services/code-index/managed/git-utils" import { normalizeGitUrl } from "./normalizeGitUrl" -import type { ClineMessage } from "@roo-code/types" -import type { ProviderSettings } from "@roo-code/types" +import type { ClineMessage, ProviderSettings } from "@roo-code/types" import { captureAgentManagerOpened, captureAgentManagerSessionStarted, @@ -53,6 +53,7 @@ export class AgentManagerProvider implements vscode.Disposable { private remoteSessionService: RemoteSessionService private processHandler: CliProcessHandler private eventProcessor: KilocodeEventProcessor + private sessionLauncher: CliSessionLauncher private sessionMessages: Map = new Map() // Track first api_req_started per session to filter user-input echoes private firstApiReqStarted: Map = new Map() @@ -72,6 +73,12 @@ export class AgentManagerProvider implements vscode.Disposable { this.registry = new AgentRegistry() this.remoteSessionService = new RemoteSessionService({ outputChannel }) + // Initialize session launcher with pre-warming + // Pre-warming starts slow lookups (CLI: 500-2000ms, git: 50-100ms) immediately + // so they complete before the user clicks "Start" to reduce time-to-first-token + this.sessionLauncher = new CliSessionLauncher(outputChannel, () => this.getApiConfigurationForCli()) + this.sessionLauncher.startPrewarm() + // Initialize currentGitUrl from workspace void this.initializeCurrentGitUrl() @@ -142,6 +149,7 @@ export class AgentManagerProvider implements vscode.Disposable { }) }, onPaymentRequiredPrompt: (payload) => this.showPaymentRequiredPrompt(payload), + onSessionRenamed: (oldId, newId) => this.handleSessionRenamed(oldId, newId), } this.processHandler = new CliProcessHandler(this.registry, callbacks) @@ -196,6 +204,8 @@ export class AgentManagerProvider implements vscode.Disposable { () => { this.panel = undefined this.stopAllAgents() + // Clear pre-warm state when panel closes + this.sessionLauncher.clearPrewarm() }, null, this.disposables, @@ -207,6 +217,21 @@ export class AgentManagerProvider implements vscode.Disposable { captureAgentManagerOpened() } + /** Rename session key in all session-keyed maps. */ + private handleSessionRenamed(oldId: string, newId: string): void { + this.outputChannel.appendLine(`[AgentManager] Renaming session: ${oldId} -> ${newId}`) + + renameMapKey(this.sessionMessages, oldId, newId) + renameMapKey(this.firstApiReqStarted, oldId, newId) + renameMapKey(this.processStartTimes, oldId, newId) + renameMapKey(this.sendingMessageMap, oldId, newId) + + const messages = this.sessionMessages.get(newId) + if (messages) { + this.postMessage({ type: "agentManager.chatMessages", sessionId: newId, messages }) + } + } + private handleMessage(message: { type: string; [key: string]: unknown }): void { this.outputChannel.appendLine(`Agent Manager received message: ${JSON.stringify(message)}`) @@ -419,23 +444,10 @@ export class AgentManagerProvider implements vscode.Disposable { return } - // Get workspace folder early to fetch git URL before spawning // Note: we intentionally allow starting parallel mode from within an existing git worktree. // Git worktrees share a common .git dir, so `git worktree add/remove` still works from a worktree root. const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath - // Get git URL for the workspace (used for filtering sessions) - let gitUrl: string | undefined - if (workspaceFolder) { - try { - gitUrl = normalizeGitUrl(await getRemoteUrl(workspaceFolder)) - } catch (error) { - this.outputChannel.appendLine( - `[AgentManager] Could not get git URL: ${error instanceof Error ? error.message : String(error)}`, - ) - } - } - const onSetupFailed = () => { if (!workspaceFolder) { void vscode.window.showErrorMessage("Please open a folder before starting an agent.") @@ -443,12 +455,12 @@ export class AgentManagerProvider implements vscode.Disposable { this.postMessage({ type: "agentManager.startSessionFailed" }) } + // Git URL lookup is now handled by spawnCliWithCommonSetup using pre-warmed promise await this.spawnCliWithCommonSetup( prompt, { parallelMode: options?.parallelMode, label: options?.labelOverride, - gitUrl, existingBranch: options?.existingBranch, }, onSetupFailed, @@ -462,7 +474,7 @@ export class AgentManagerProvider implements vscode.Disposable { /** * Common helper to spawn a CLI process with standard setup. - * Handles CLI path lookup, workspace folder validation, API config, and event callback wiring. + * Delegates to CliSessionLauncher for pre-warming and spawning. * @returns true if process was spawned, false if setup failed */ private async spawnCliWithCommonSetup( @@ -476,47 +488,23 @@ export class AgentManagerProvider implements vscode.Disposable { }, onSetupFailed?: () => void, ): Promise { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath - if (!workspaceFolder) { - this.outputChannel.appendLine("ERROR: No workspace folder open") - onSetupFailed?.() - return false - } - - const cliPath = await findKilocodeCli((msg) => this.outputChannel.appendLine(`[AgentManager] ${msg}`)) - if (!cliPath) { - this.outputChannel.appendLine("ERROR: kilocode CLI not found") - this.showCliNotFoundError() - onSetupFailed?.() - return false - } - - const processStartTime = Date.now() - let apiConfiguration: ProviderSettings | undefined - try { - apiConfiguration = await this.getApiConfigurationForCli() - } catch (error) { - this.outputChannel.appendLine( - `[AgentManager] Failed to read provider settings for CLI: ${ - error instanceof Error ? error.message : String(error) - }`, - ) - } - - this.processHandler.spawnProcess( - cliPath, - workspaceFolder, + const result = await this.sessionLauncher.spawn( prompt, - { ...options, apiConfiguration }, + options, + this.processHandler, (sid, event) => { - if (!this.processStartTimes.has(sid)) { - this.processStartTimes.set(sid, processStartTime) + if (result.processStartTime && !this.processStartTimes.has(sid)) { + this.processStartTimes.set(sid, result.processStartTime) } this.handleCliEvent(sid, event) }, + () => { + this.showCliNotFoundError() + onSetupFailed?.() + }, ) - return true + return result.success } /** @@ -1150,7 +1138,7 @@ export class AgentManagerProvider implements vscode.Disposable { this.processHandler.dispose() this.sessionMessages.clear() this.firstApiReqStarted.clear() - + this.sessionLauncher.clearPrewarm() this.panel?.dispose() this.disposables.forEach((d) => d.dispose()) } diff --git a/src/core/kilocode/agent-manager/AgentRegistry.ts b/src/core/kilocode/agent-manager/AgentRegistry.ts index 7266e84aa9c..83568a947b9 100644 --- a/src/core/kilocode/agent-manager/AgentRegistry.ts +++ b/src/core/kilocode/agent-manager/AgentRegistry.ts @@ -209,6 +209,31 @@ export class AgentRegistry { return this.sessions.delete(sessionId) } + /** + * Rename a session from one ID to another. + * Used when upgrading a provisional session to a real session ID. + */ + public renameSession(oldId: string, newId: string): boolean { + const session = this.sessions.get(oldId) + if (!session) { + return false + } + + // Update the session's internal ID + session.sessionId = newId + + // Move in the map + this.sessions.delete(oldId) + this.sessions.set(newId, session) + + // Update selectedId if it was pointing to the old ID + if (this._selectedId === oldId) { + this._selectedId = newId + } + + return true + } + private pruneOldSessions(): void { const sessions = this.getSessions() const overflow = sessions.length - MAX_SESSIONS diff --git a/src/core/kilocode/agent-manager/CliProcessHandler.ts b/src/core/kilocode/agent-manager/CliProcessHandler.ts index 7f498a6b3db..003274f1c5c 100644 --- a/src/core/kilocode/agent-manager/CliProcessHandler.ts +++ b/src/core/kilocode/agent-manager/CliProcessHandler.ts @@ -37,6 +37,7 @@ interface PendingProcessInfo { gitUrl?: string stderrBuffer: string[] // Capture stderr for error detection timeoutId?: NodeJS.Timeout // Timer for auto-failing stuck pending sessions + provisionalSessionId?: string // Temporary session ID created when api_req_started arrives (before session_created) } interface ActiveProcessInfo { @@ -60,6 +61,7 @@ export interface CliProcessHandlerCallbacks { onSessionCreated: (sawApiReqStarted: boolean) => void onPaymentRequiredPrompt?: (payload: KilocodePayload) => void onSessionCompleted?: (sessionId: string, exitCode: number | null) => void // Called when process exits successfully + onSessionRenamed?: (oldId: string, newId: string) => void // Called when provisional session is upgraded to real session ID } export class CliProcessHandler { @@ -365,10 +367,11 @@ export class CliProcessHandler { } return } - // Track api_req_started that arrives before session_created - // This is needed so KilocodeEventProcessor knows the user echo has already happened + // Handle kilocode events during pending state if (event.streamEventType === "kilocode") { const payload = (event as KilocodeStreamEvent).payload + + // Handle error cases that should abort session creation if (payload?.ask === "payment_required_prompt") { this.handlePaymentRequiredDuringPending(payload) return @@ -377,11 +380,34 @@ export class CliProcessHandler { this.handleApiReqFailedDuringPending(payload) return } + + // Track api_req_started for KilocodeEventProcessor if (payload?.say === "api_req_started") { this.pendingProcess.sawApiReqStarted = true this.debugLog(`Captured api_req_started before session_created`) } + + // Create provisional session on first content event (text, api_req_started, etc.) + // This ensures we don't lose the user's prompt echo or other early events + if (!this.pendingProcess.provisionalSessionId) { + this.createProvisionalSession(proc) + } + + // Forward the event to the provisional session + if (this.pendingProcess?.provisionalSessionId) { + onCliEvent(this.pendingProcess.provisionalSessionId, event) + this.callbacks.onStateChanged() + } + return + } + + // If we have a provisional session, forward non-kilocode events to it + if (this.pendingProcess?.provisionalSessionId) { + onCliEvent(this.pendingProcess.provisionalSessionId, event) + this.callbacks.onStateChanged() + return } + // Events before session_created are typically status messages if (event.streamEventType === "status") { this.debugLog(`Pending session status: ${event.message}`) @@ -397,6 +423,64 @@ export class CliProcessHandler { } } + /** Create a provisional session to show streaming content before session_created arrives. */ + private createProvisionalSession(proc: ChildProcess): void { + if (!this.pendingProcess || this.pendingProcess.provisionalSessionId) { + return + } + + const provisionalId = `provisional-${Date.now()}` + this.pendingProcess.provisionalSessionId = provisionalId + + const { prompt, startTime, parallelMode, desiredLabel, gitUrl, parser } = this.pendingProcess + + this.registry.createSession(provisionalId, prompt, startTime, { + parallelMode, + labelOverride: desiredLabel, + gitUrl, + }) + + this.activeSessions.set(provisionalId, { process: proc, parser }) + + if (proc.pid) { + this.registry.setSessionPid(provisionalId, proc.pid) + } + + this.registry.clearPendingSession() + this.callbacks.onPendingSessionChanged(null) + this.callbacks.onSessionCreated(this.pendingProcess?.sawApiReqStarted ?? false) + + this.debugLog(`Created provisional session: ${provisionalId}`) + this.callbacks.onStateChanged() + } + + /** Upgrade provisional session to real session ID when session_created arrives. */ + private upgradeProvisionalSession( + provisionalSessionId: string, + realSessionId: string, + worktreeBranch: string | undefined, + parallelMode: boolean | undefined, + ): void { + this.debugLog(`Upgrading provisional session ${provisionalSessionId} -> ${realSessionId}`) + + this.registry.renameSession(provisionalSessionId, realSessionId) + + const activeInfo = this.activeSessions.get(provisionalSessionId) + if (activeInfo) { + this.activeSessions.delete(provisionalSessionId) + this.activeSessions.set(realSessionId, activeInfo) + } + + this.callbacks.onSessionRenamed?.(provisionalSessionId, realSessionId) + + if (worktreeBranch && parallelMode) { + this.registry.updateParallelModeInfo(realSessionId, { branch: worktreeBranch }) + } + + this.pendingProcess = null + this.callbacks.onStateChanged() + } + private handlePendingTimeout(): void { if (!this.pendingProcess) { return @@ -445,10 +529,19 @@ export class CliProcessHandler { desiredLabel, sawApiReqStarted, gitUrl, + provisionalSessionId, } = this.pendingProcess // Use desired sessionId when provided (resuming) to keep UI continuity const sessionId = desiredSessionId ?? event.sessionId + + // Handle provisional session upgrade if one exists + if (provisionalSessionId) { + this.upgradeProvisionalSession(provisionalSessionId, sessionId, worktreeBranch, parallelMode) + return + } + + // Normal path: no provisional session, create the session now const existing = this.registry.getSession(sessionId) let session: ReturnType diff --git a/src/core/kilocode/agent-manager/CliSessionLauncher.ts b/src/core/kilocode/agent-manager/CliSessionLauncher.ts new file mode 100644 index 00000000000..4fbfa3a1422 --- /dev/null +++ b/src/core/kilocode/agent-manager/CliSessionLauncher.ts @@ -0,0 +1,214 @@ +import * as vscode from "vscode" +import { findKilocodeCli } from "./CliPathResolver" +import { CliProcessHandler } from "./CliProcessHandler" +import type { StreamEvent } from "./CliOutputParser" +import { getRemoteUrl } from "../../../services/code-index/managed/git-utils" +import { normalizeGitUrl } from "./normalizeGitUrl" +import { buildProviderEnvOverrides } from "./providerEnvMapper" +import type { ProviderSettings } from "@roo-code/types" + +/** + * Options for spawning a CLI session + */ +export interface SpawnOptions { + parallelMode?: boolean + label?: string + gitUrl?: string + existingBranch?: string + sessionId?: string +} + +/** + * Result of a spawn attempt + */ +export interface SpawnResult { + success: boolean + processStartTime?: number +} + +/** + * Callback to get API configuration for CLI environment variables + */ +export type GetApiConfigurationFn = () => Promise + +/** + * CliSessionLauncher + * + * Encapsulates CLI session launching logic including: + * - Pre-warming of slow lookups (CLI path, git URL) + * - Environment variable setup for provider settings + * - Process spawning coordination + * + * This class is responsible for the "how to launch a CLI session" concern, + * separating it from the AgentManagerProvider's orchestration responsibilities. + */ +export class CliSessionLauncher { + // Pre-warm promises for CLI path and git URL lookups + private cliPathPromise: Promise | null = null + private gitUrlPromise: Promise | null = null + + constructor( + private readonly outputChannel: vscode.OutputChannel, + private readonly getApiConfiguration: GetApiConfigurationFn, + ) {} + + /** + * Start pre-warming slow lookups (CLI path and git URL) in parallel. + * Call this early (e.g., from constructor or panel open) so these complete + * well before the user clicks "Start" to reduce time-to-first-token. + * + * Pre-warming is idempotent - calling multiple times won't restart lookups. + */ + public startPrewarm(): void { + // Pre-warm CLI path lookup (500-2000ms typically) + this.cliPathPromise ??= findKilocodeCli((msg) => this.log(msg)) + + // Pre-warm git URL lookup (50-100ms typically) + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath + if (workspaceFolder) { + this.gitUrlPromise ??= getRemoteUrl(workspaceFolder) + .then(normalizeGitUrl) + .catch(() => undefined) + } + } + + /** + * Clear pre-warm promises. + * Call this when the panel closes or when you want to force fresh lookups. + */ + public clearPrewarm(): void { + this.cliPathPromise = null + this.gitUrlPromise = null + } + + /** + * Get the pre-warmed git URL, or undefined if not available. + * This is useful for filtering sessions by git URL. + */ + public async getPrewarmedGitUrl(): Promise { + return this.gitUrlPromise ?? undefined + } + + /** + * Spawn a CLI process with all the standard setup. + * Handles CLI path lookup, git URL resolution, API config, and event callback wiring. + * Uses pre-warmed promises when available. + * + * @param prompt - The task prompt for the agent + * @param options - Spawn options (parallelMode, label, gitUrl, existingBranch, sessionId) + * @param processHandler - The CliProcessHandler to spawn the process with + * @param onCliEvent - Callback for CLI events + * @param onSetupFailed - Optional callback when setup fails (e.g., no workspace, CLI not found) + * @returns SpawnResult indicating success and process start time + */ + public async spawn( + prompt: string, + options: SpawnOptions, + processHandler: CliProcessHandler, + onCliEvent: (sessionId: string, event: StreamEvent) => void, + onSetupFailed?: () => void, + ): Promise { + const spawnStart = Date.now() + + // Validate workspace + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath + if (!workspaceFolder) { + this.log("ERROR: No workspace folder open") + onSetupFailed?.() + return { success: false } + } + + // Use pre-warmed CLI path promise if available, otherwise start fresh lookup + const cliPathPromise = this.cliPathPromise ?? findKilocodeCli((msg) => this.log(msg)) + + // Use pre-warmed git URL promise if available and no gitUrl provided in options + const gitUrlPromise = this.resolveGitUrlPromise(options.gitUrl, workspaceFolder) + + // Run CLI path lookup, git URL lookup, and API config fetch in parallel + const [cliPath, resolvedGitUrl, apiConfiguration] = await Promise.all([ + cliPathPromise, + gitUrlPromise, + this.getApiConfiguration().catch((error) => { + this.log( + `Failed to read provider settings for CLI: ${error instanceof Error ? error.message : String(error)}`, + ) + return undefined + }), + ]) + + this.log( + `Parallel lookups completed in ${Date.now() - spawnStart}ms (CLI: ${cliPath ? "found" : "not found"}, gitUrl: ${resolvedGitUrl ?? "none"})`, + ) + + // Clear pre-warm promises after use (they're single-use per panel open) + this.clearPrewarm() + + if (!cliPath) { + this.log("ERROR: kilocode CLI not found") + onSetupFailed?.() + return { success: false } + } + + const processStartTime = Date.now() + + processHandler.spawnProcess( + cliPath, + workspaceFolder, + prompt, + { ...options, gitUrl: resolvedGitUrl, apiConfiguration }, + onCliEvent, + ) + + return { success: true, processStartTime } + } + + /** + * Build environment variables with API configuration overrides. + * Useful for testing or custom spawn scenarios. + */ + public buildEnvWithApiConfiguration(apiConfiguration?: ProviderSettings): NodeJS.ProcessEnv { + const baseEnv = { ...process.env } + + const overrides = buildProviderEnvOverrides( + apiConfiguration, + baseEnv, + (message) => this.log(message), + (message) => this.log(message), // Debug log same as regular log for launcher + ) + + return { + ...baseEnv, + ...overrides, + NO_COLOR: "1", + FORCE_COLOR: "0", + KILO_PLATFORM: "agent-manager", + } + } + + /** + * Resolve the git URL promise based on options and pre-warming state. + */ + private resolveGitUrlPromise( + optionsGitUrl: string | undefined, + workspaceFolder: string, + ): Promise { + // If gitUrl is explicitly provided in options, use it + if (optionsGitUrl) { + return Promise.resolve(optionsGitUrl) + } + + // Use pre-warmed promise if available + if (this.gitUrlPromise) { + return this.gitUrlPromise + } + + // Fall back to fresh lookup + return getRemoteUrl(workspaceFolder) + .then(normalizeGitUrl) + .catch(() => undefined) + } + + private log(message: string): void { + this.outputChannel.appendLine(`[AgentManager] ${message}`) + } +} diff --git a/src/core/kilocode/agent-manager/__tests__/AgentManagerProvider.spec.ts b/src/core/kilocode/agent-manager/__tests__/AgentManagerProvider.spec.ts index 2ae8d7c7cb7..ea7778f290a 100644 --- a/src/core/kilocode/agent-manager/__tests__/AgentManagerProvider.spec.ts +++ b/src/core/kilocode/agent-manager/__tests__/AgentManagerProvider.spec.ts @@ -611,10 +611,23 @@ describe("AgentManagerProvider gitUrl filtering", () => { }) it("handles git URL retrieval errors gracefully", async () => { + // Need to recreate provider with the mock rejection set up BEFORE construction + // because pre-warming happens in the constructor + provider.dispose() mockGetRemoteUrl.mockRejectedValue(new Error("No remote configured")) - const spawnProcessSpy = vi.spyOn((provider as any).processHandler, "spawnProcess") - await (provider as any).startAgentSession("test prompt") + const mockContext = { extensionUri: {}, extensionPath: "", extensionMode: 1 } as any + const mockOutputChannel = { appendLine: vi.fn() } as any + const mockClineProvider = { + getState: vi.fn().mockResolvedValue({ apiConfiguration: { apiProvider: "kilocode" } }), + } + + const module = await import("../AgentManagerProvider") + const newProvider = new module.AgentManagerProvider(mockContext, mockOutputChannel, mockClineProvider as any) + + const spawnProcessSpy = vi.spyOn((newProvider as any).processHandler, "spawnProcess") + + await (newProvider as any).startAgentSession("test prompt") // Should still spawn process without gitUrl expect(spawnProcessSpy).toHaveBeenCalledWith( @@ -624,6 +637,8 @@ describe("AgentManagerProvider gitUrl filtering", () => { expect.objectContaining({ gitUrl: undefined }), expect.any(Function), ) + + newProvider.dispose() }) it("stores gitUrl on created session", async () => { diff --git a/src/core/kilocode/agent-manager/__tests__/CliProcessHandler.spec.ts b/src/core/kilocode/agent-manager/__tests__/CliProcessHandler.spec.ts index 204c1e3ed82..2e15fe30a47 100644 --- a/src/core/kilocode/agent-manager/__tests__/CliProcessHandler.spec.ts +++ b/src/core/kilocode/agent-manager/__tests__/CliProcessHandler.spec.ts @@ -42,6 +42,7 @@ function createMockCallbacks(): CliProcessHandlerCallbacks & { onChatMessages: ReturnType onSessionCreated: ReturnType onPaymentRequiredPrompt: ReturnType + onSessionRenamed: ReturnType } { return { onLog: vi.fn(), @@ -53,6 +54,7 @@ function createMockCallbacks(): CliProcessHandlerCallbacks & { onChatMessages: vi.fn(), onSessionCreated: vi.fn(), onPaymentRequiredPrompt: vi.fn(), + onSessionRenamed: vi.fn(), } } @@ -1231,4 +1233,222 @@ describe("CliProcessHandler", () => { ) }) }) + + describe("provisional session handling", () => { + it("creates provisional session when kilocode event arrives before session_created", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created (e.g., user_feedback with the prompt) + const kilocodeEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent + "\n")) + + // A provisional session should be created + const sessions = registry.getSessions() + expect(sessions).toHaveLength(1) + expect(sessions[0].sessionId).toMatch(/^provisional-/) + expect(sessions[0].status).toBe("running") + }) + + it("forwards events to provisional session before session_created", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created + const kilocodeEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent + "\n")) + + // Event should be forwarded with provisional session ID + expect(onCliEvent).toHaveBeenCalledWith( + expect.stringMatching(/^provisional-/), + expect.objectContaining({ + streamEventType: "kilocode", + payload: expect.objectContaining({ + type: "say", + say: "user_feedback", + }), + }), + ) + }) + + it("renames provisional session to real session ID when session_created arrives", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created to create provisional session + const kilocodeEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent + "\n")) + + // Get the provisional session ID + const provisionalId = registry.getSessions()[0].sessionId + expect(provisionalId).toMatch(/^provisional-/) + + // Now emit session_created with real session ID + mockProcess.stdout.emit("data", Buffer.from('{"event":"session_created","sessionId":"real-session-123"}\n')) + + // Session should be renamed + const sessions = registry.getSessions() + expect(sessions).toHaveLength(1) + expect(sessions[0].sessionId).toBe("real-session-123") + expect(registry.getSession(provisionalId)).toBeUndefined() + expect(registry.getSession("real-session-123")).toBeDefined() + }) + + it("calls onSessionRenamed callback when provisional session is upgraded", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created to create provisional session + const kilocodeEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent + "\n")) + + // Get the provisional session ID + const provisionalId = registry.getSessions()[0].sessionId + + // Now emit session_created with real session ID + mockProcess.stdout.emit("data", Buffer.from('{"event":"session_created","sessionId":"real-session-123"}\n')) + + // onSessionRenamed should be called with old and new IDs + expect(callbacks.onSessionRenamed).toHaveBeenCalledWith(provisionalId, "real-session-123") + }) + + it("continues forwarding events to new session ID after rename", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created to create provisional session + const kilocodeEvent1 = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent1 + "\n")) + + // Now emit session_created with real session ID + mockProcess.stdout.emit("data", Buffer.from('{"event":"session_created","sessionId":"real-session-123"}\n')) + + // Clear previous calls + onCliEvent.mockClear() + + // Emit another kilocode event after session_created + const kilocodeEvent2 = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "text", content: "Hello world" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent2 + "\n")) + + // Event should be forwarded with the real session ID + expect(onCliEvent).toHaveBeenCalledWith( + "real-session-123", + expect.objectContaining({ + streamEventType: "kilocode", + payload: expect.objectContaining({ + type: "say", + say: "text", + content: "Hello world", + }), + }), + ) + }) + + it("does not call onSessionRenamed when no provisional session exists", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit session_created directly without any kilocode events first + mockProcess.stdout.emit("data", Buffer.from('{"event":"session_created","sessionId":"session-1"}\n')) + + // onSessionRenamed should NOT be called + expect(callbacks.onSessionRenamed).not.toHaveBeenCalled() + }) + + it("creates provisional session for api_req_started events", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit api_req_started before session_created - this SHOULD create provisional session + const apiStartedEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { say: "api_req_started" }, + }) + mockProcess.stdout.emit("data", Buffer.from(apiStartedEvent + "\n")) + + // Provisional session should be created + const sessions = registry.getSessions() + expect(sessions).toHaveLength(1) + expect(sessions[0].sessionId).toMatch(/^provisional-/) + }) + + it("creates provisional session for user_feedback events (user prompt echo)", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit user_feedback event - this SHOULD create provisional session + const userFeedbackEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(userFeedbackEvent + "\n")) + + // Provisional session should be created + const sessions = registry.getSessions() + expect(sessions).toHaveLength(1) + expect(sessions[0].sessionId).toMatch(/^provisional-/) + }) + + it("creates provisional session for text events (streaming content)", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit text event - this SHOULD create provisional session + const textEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "text", content: "Hello" }, + }) + mockProcess.stdout.emit("data", Buffer.from(textEvent + "\n")) + + // Provisional session should be created + const sessions = registry.getSessions() + expect(sessions).toHaveLength(1) + expect(sessions[0].sessionId).toMatch(/^provisional-/) + }) + + it("preserves session data when renaming provisional session", () => { + const onCliEvent = vi.fn() + handler.spawnProcess("/path/to/kilocode", "/workspace", "test prompt", undefined, onCliEvent) + + // Emit a kilocode event before session_created to create provisional session + const kilocodeEvent = JSON.stringify({ + streamEventType: "kilocode", + payload: { type: "say", say: "user_feedback", content: "test prompt" }, + }) + mockProcess.stdout.emit("data", Buffer.from(kilocodeEvent + "\n")) + + // Get the provisional session + const provisionalSession = registry.getSessions()[0] + expect(provisionalSession.prompt).toBe("test prompt") + expect(provisionalSession.status).toBe("running") + + // Now emit session_created with real session ID + mockProcess.stdout.emit("data", Buffer.from('{"event":"session_created","sessionId":"real-session-123"}\n')) + + // Session should be renamed but preserve data + const renamedSession = registry.getSession("real-session-123") + expect(renamedSession).toBeDefined() + expect(renamedSession?.prompt).toBe("test prompt") + expect(renamedSession?.status).toBe("running") + expect(renamedSession?.pid).toBe(12345) + }) + }) }) diff --git a/src/core/kilocode/agent-manager/__tests__/mapUtils.spec.ts b/src/core/kilocode/agent-manager/__tests__/mapUtils.spec.ts new file mode 100644 index 00000000000..491705a8aa0 --- /dev/null +++ b/src/core/kilocode/agent-manager/__tests__/mapUtils.spec.ts @@ -0,0 +1,45 @@ +import { describe, it, expect } from "vitest" +import { renameMapKey } from "../mapUtils" + +describe("renameMapKey", () => { + it("moves value from old key to new key", () => { + const map = new Map([["a", 1]]) + + const result = renameMapKey(map, "a", "b") + + expect(result).toBe(true) + expect(map.get("a")).toBeUndefined() + expect(map.get("b")).toBe(1) + }) + + it("returns false when old key does not exist", () => { + const map = new Map() + + const result = renameMapKey(map, "a", "b") + + expect(result).toBe(false) + expect(map.size).toBe(0) + }) + + it("overwrites existing value at new key", () => { + const map = new Map([ + ["a", 1], + ["b", 2], + ]) + + const result = renameMapKey(map, "a", "b") + + expect(result).toBe(true) + expect(map.get("a")).toBeUndefined() + expect(map.get("b")).toBe(1) + expect(map.size).toBe(1) + }) + + it("works with complex value types", () => { + const map = new Map([["old", { name: "test" }]]) + + renameMapKey(map, "old", "new") + + expect(map.get("new")).toEqual({ name: "test" }) + }) +}) diff --git a/src/core/kilocode/agent-manager/mapUtils.ts b/src/core/kilocode/agent-manager/mapUtils.ts new file mode 100644 index 00000000000..489c7e979d1 --- /dev/null +++ b/src/core/kilocode/agent-manager/mapUtils.ts @@ -0,0 +1,14 @@ +/** + * Move a value from one key to another in a Map. + * If the old key exists, deletes it and sets the new key. + * @returns true if the key was renamed, false if old key didn't exist + */ +export function renameMapKey(map: Map, oldKey: K, newKey: K): boolean { + const value = map.get(oldKey) + if (value === undefined) { + return false + } + map.delete(oldKey) + map.set(newKey, value) + return true +} From b1702cd1c3119a89c96edf23c388b84135b8cbd3 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 19 Dec 2025 11:32:00 +0100 Subject: [PATCH 06/15] refactor(agent-manager): remove redundant header buttons (#4568) * refactor(agent-manager): remove redundant header buttons Remove the 'New Agent' (Plus) and 'Refresh messages' (RefreshCw) buttons from the session detail header. These actions are already available in the sidebar, making the header buttons redundant. Changes: - Remove am-header-actions div with both buttons - Remove unused imports: Plus, RefreshCw, selectedSessionIdAtom, useSetAtom - Remove handleRefresh and handleNew functions * Update remove-agent-manager-header-buttons.md --- .../remove-agent-manager-header-buttons.md | 5 ++ .../components/SessionDetail.tsx | 47 +------------------ 2 files changed, 7 insertions(+), 45 deletions(-) create mode 100644 .changeset/remove-agent-manager-header-buttons.md diff --git a/.changeset/remove-agent-manager-header-buttons.md b/.changeset/remove-agent-manager-header-buttons.md new file mode 100644 index 00000000000..c67255529d5 --- /dev/null +++ b/.changeset/remove-agent-manager-header-buttons.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Remove redundant "New Agent" and "Refresh messages" buttons from agent manager session detail header. diff --git a/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx b/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx index 62e9ff3c764..a8fc91e3678 100644 --- a/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx +++ b/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx @@ -1,9 +1,8 @@ import React, { useState, useEffect, useMemo, useRef } from "react" -import { useAtom, useAtomValue, useSetAtom } from "jotai" +import { useAtom, useAtomValue } from "jotai" import { useTranslation } from "react-i18next" import { selectedSessionAtom, - selectedSessionIdAtom, startSessionFailedCounterAtom, pendingSessionAtom, preferredRunModeAtom, @@ -19,19 +18,7 @@ import { ChatInput } from "./ChatInput" import { BranchPicker } from "./BranchPicker" import { vscode } from "../utils/vscode" import { formatRelativeTime, createRelativeTimeLabels } from "../utils/timeUtils" -import { - Loader2, - SendHorizontal, - RefreshCw, - GitBranch, - Folder, - ChevronDown, - AlertCircle, - Zap, - Layers, - X, - Plus, -} from "lucide-react" +import { Loader2, SendHorizontal, GitBranch, Folder, ChevronDown, AlertCircle, Zap, Layers, X } from "lucide-react" import DynamicTextArea from "react-textarea-autosize" import { cn } from "../../../lib/utils" import { StandardTooltip } from "../../../components/ui" @@ -43,7 +30,6 @@ export function SessionDetail() { const pendingSession = useAtomValue(pendingSessionAtom) const machineUiState = useAtomValue(sessionMachineUiStateAtom) const selectedSessionState = useAtomValue(selectedSessionMachineStateAtom) - const setSelectedSessionId = useSetAtom(selectedSessionIdAtom) const prevSessionStateRef = useRef<{ id: string; status: string } | undefined>(undefined) // Hooks must be called unconditionally before any early returns @@ -79,14 +65,6 @@ export function SessionDetail() { return } - const handleRefresh = () => { - vscode.postMessage({ type: "agentManager.refreshSessionMessages", sessionId: selectedSession.sessionId }) - } - - const handleNew = () => { - setSelectedSessionId(null) - } - // Use state machine UI state as the single source of truth for activity/spinner const sessionUiState = machineUiState[selectedSession.sessionId] const isActive = sessionUiState?.isActive ?? false @@ -136,27 +114,6 @@ export function SessionDetail() { )} - -
- {!showSpinner && ( - - )} - {!isActive && ( - - )} -
{selectedSession.status === "error" && selectedSession.error && ( From e8c4323f3d55dbac33dfcc01ba28cfffa66f74c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 10:34:20 +0000 Subject: [PATCH 07/15] changeset version bump --- .changeset/all-things-cough.md | 5 -- .changeset/every-knives-dig.md | 5 -- .changeset/loud-lights-build.md | 5 -- .changeset/polite-games-arrive.md | 59 ---------------- .../remove-agent-manager-header-buttons.md | 5 -- .changeset/wacky-lions-kiss.md | 5 -- CHANGELOG.md | 70 +++++++++++++++++++ cli/CHANGELOG.md | 6 ++ cli/package.dist.json | 2 +- cli/package.json | 2 +- src/package.json | 2 +- 11 files changed, 79 insertions(+), 87 deletions(-) delete mode 100644 .changeset/all-things-cough.md delete mode 100644 .changeset/every-knives-dig.md delete mode 100644 .changeset/loud-lights-build.md delete mode 100644 .changeset/polite-games-arrive.md delete mode 100644 .changeset/remove-agent-manager-header-buttons.md delete mode 100644 .changeset/wacky-lions-kiss.md diff --git a/.changeset/all-things-cough.md b/.changeset/all-things-cough.md deleted file mode 100644 index 269427d0ab5..00000000000 --- a/.changeset/all-things-cough.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Add GLM-4.6V model support for z.ai provider diff --git a/.changeset/every-knives-dig.md b/.changeset/every-knives-dig.md deleted file mode 100644 index 6f718835233..00000000000 --- a/.changeset/every-knives-dig.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Default read permissions now require approval for read operations outside the workspace diff --git a/.changeset/loud-lights-build.md b/.changeset/loud-lights-build.md deleted file mode 100644 index 4d06f9cafc0..00000000000 --- a/.changeset/loud-lights-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Added gemini-3-flash-preview model diff --git a/.changeset/polite-games-arrive.md b/.changeset/polite-games-arrive.md deleted file mode 100644 index 03c192fc55a..00000000000 --- a/.changeset/polite-games-arrive.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -"kilo-code": patch ---- - -Include changes from Roo Code v3.36.6 - -- Add tool alias support for model-specific tool customization, allowing users to configure how tools are presented to different AI models (PR #9989 by @daniel-lxs) -- Sanitize MCP server and tool names for API compatibility, ensuring special characters don't cause issues with API calls (PR #10054 by @daniel-lxs) -- Improve auto-approve timer visibility in follow-up suggestions for better user awareness of pending actions (PR #10048 by @brunobergher) -- Fix: Cancel auto-approval timeout when user starts typing, preventing accidental auto-approvals during user interaction (PR #9937 by @roomote) -- Add WorkspaceTaskVisibility type for organization cloud settings to support team visibility controls (PR #10020 by @roomote) -- Fix: Extract raw error message from OpenRouter metadata for clearer error reporting (PR #10039 by @daniel-lxs) -- Fix: Show tool protocol dropdown for LiteLLM provider, restoring missing configuration option (PR #10053 by @daniel-lxs) -- Add: GPT-5.2 model to openai-native provider (PR #10024 by @hannesrudolph) -- Fix: Handle empty Gemini responses and reasoning loops to prevent infinite retries (PR #10007 by @hannesrudolph) -- Fix: Add missing tool_result blocks to prevent API errors when tool results are expected (PR #10015 by @daniel-lxs) -- Fix: Filter orphaned tool_results when more results than tool_uses to prevent message validation errors (PR #10027 by @daniel-lxs) -- Fix: Add general API endpoints for Z.ai provider (#9879 by @richtong, PR #9894 by @roomote) -- Remove: Deprecated list_code_definition_names tool (PR #10005 by @hannesrudolph) -- Add error details modal with on-demand display for improved error visibility when debugging issues (PR #9985 by @roomote) -- Fix: Prevent premature rawChunkTracker clearing for MCP tools, improving reliability of MCP tool streaming (PR #9993 by @daniel-lxs) -- Fix: Filter out 429 rate limit errors from API error telemetry for cleaner metrics (PR #9987 by @daniel-lxs) -- Fix: Correct TODO list display order in chat view to show items in proper sequence (PR #9991 by @roomote) -- Refactor: Unified context-management architecture with improved UX for better context control (PR #9795 by @hannesrudolph) -- Add new `search_replace` native tool for single-replacement operations with improved editing precision (PR #9918 by @hannesrudolph) -- Streaming tool stats and token usage throttling for better real-time feedback during generation (PR #9926 by @hannesrudolph) -- Add versioned settings support with minPluginVersion gating for Roo provider (PR #9934 by @hannesrudolph) -- Make Architect mode save plans to `/plans` directory and gitignore it (PR #9944 by @brunobergher) -- Add ability to save screenshots from the browser tool (PR #9963 by @mrubens) -- Refactor: Decouple tools from system prompt for cleaner architecture (PR #9784 by @daniel-lxs) -- Update DeepSeek models to V3.2 with new pricing (PR #9962 by @hannesrudolph) -- Add minimal and medium reasoning effort levels for Gemini models (PR #9973 by @hannesrudolph) -- Update xAI models catalog with latest model options (PR #9872 by @hannesrudolph) -- Add DeepSeek V3-2 support for Baseten provider (PR #9861 by @AlexKer) -- Tweaks to Baseten model definitions for better defaults (PR #9866 by @mrubens) -- Fix: Add xhigh reasoning effort support for gpt-5.1-codex-max (#9891 by @andrewginns, PR #9900 by @andrewginns) -- Fix: Add Kimi, MiniMax, and Qwen model configurations for Bedrock (#9902 by @jbearak, PR #9905 by @app/roomote) -- Configure tool preferences for xAI models (PR #9923 by @hannesrudolph) -- Default to using native tools when supported on OpenRouter (PR #9878 by @mrubens) -- Fix: Exclude apply_diff from native tools when diffEnabled is false (#9919 by @denis-kudelin, PR #9920 by @app/roomote) -- Fix: Always show tool protocol selector for openai-compatible provider (#9965 by @bozoweed, PR #9966 by @hannesrudolph) -- Fix: Respect explicit supportsReasoningEffort array values for proper model configuration (PR #9970 by @hannesrudolph) -- Add timeout configuration to OpenAI Compatible Provider Client (PR #9898 by @dcbartlett) -- Revert default tool protocol change from xml to native for stability (PR #9956 by @mrubens) -- Improve OpenAI error messages to be more useful for debugging (PR #9639 by @mrubens) -- Better error logs for parseToolCall exceptions (PR #9857 by @cte) -- Improve cloud job error logging for RCC provider errors (PR #9924 by @cte) -- Fix: Display actual API error message instead of generic text on retry (PR #9954 by @hannesrudolph) -- Add API error telemetry to OpenRouter provider for better diagnostics (PR #9953 by @daniel-lxs) -- Fix: Sanitize removed/invalid API providers to prevent infinite loop (PR #9869 by @hannesrudolph) -- Fix: Use foreground color for context-management icons (PR #9912 by @hannesrudolph) -- Fix: Suppress 'ask promise was ignored' error in handleError (PR #9914 by @daniel-lxs) -- Fix: Process finish_reason to emit tool_call_end events properly (PR #9927 by @daniel-lxs) -- Fix: Add finish_reason processing to xai.ts provider (PR #9929 by @daniel-lxs) -- Fix: Validate and fix tool_result IDs before API requests (PR #9952 by @daniel-lxs) -- Fix: Return undefined instead of 0 for disabled API timeout (PR #9960 by @hannesrudolph) -- Stop making unnecessary count_tokens requests for better performance (PR #9884 by @mrubens) -- Refactor: Consolidate ThinkingBudget components and fix disable handling (PR #9930 by @hannesrudolph) -- Forbid time estimates in architect mode for more focused planning (PR #9931 by @app/roomote diff --git a/.changeset/remove-agent-manager-header-buttons.md b/.changeset/remove-agent-manager-header-buttons.md deleted file mode 100644 index c67255529d5..00000000000 --- a/.changeset/remove-agent-manager-header-buttons.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Remove redundant "New Agent" and "Refresh messages" buttons from agent manager session detail header. diff --git a/.changeset/wacky-lions-kiss.md b/.changeset/wacky-lions-kiss.md deleted file mode 100644 index be58208d287..00000000000 --- a/.changeset/wacky-lions-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Change the default value of auto-approval for reading outside workspace to false diff --git a/CHANGELOG.md b/CHANGELOG.md index 640da968382..556ba2db9df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # kilo-code +## 4.140.0 + +### Minor Changes + +- [#4538](https://github.com/Kilo-Org/kilocode/pull/4538) [`459b95c`](https://github.com/Kilo-Org/kilocode/commit/459b95cbf78de10fce597e3467120e52020d1114) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Added gemini-3-flash-preview model + +### Patch Changes + +- [#4530](https://github.com/Kilo-Org/kilocode/pull/4530) [`782347e`](https://github.com/Kilo-Org/kilocode/commit/782347e9ed6cbaf42c88285cb8576801cd178d96) Thanks [@alvinward](https://github.com/alvinward)! - Add GLM-4.6V model support for z.ai provider + +- [#4509](https://github.com/Kilo-Org/kilocode/pull/4509) [`8a9fddd`](https://github.com/Kilo-Org/kilocode/commit/8a9fddd8311633c3085516ab6255bb027aff81d6) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Include changes from Roo Code v3.36.6 + + - Add tool alias support for model-specific tool customization, allowing users to configure how tools are presented to different AI models (PR #9989 by @daniel-lxs) + - Sanitize MCP server and tool names for API compatibility, ensuring special characters don't cause issues with API calls (PR #10054 by @daniel-lxs) + - Improve auto-approve timer visibility in follow-up suggestions for better user awareness of pending actions (PR #10048 by @brunobergher) + - Fix: Cancel auto-approval timeout when user starts typing, preventing accidental auto-approvals during user interaction (PR #9937 by @roomote) + - Add WorkspaceTaskVisibility type for organization cloud settings to support team visibility controls (PR #10020 by @roomote) + - Fix: Extract raw error message from OpenRouter metadata for clearer error reporting (PR #10039 by @daniel-lxs) + - Fix: Show tool protocol dropdown for LiteLLM provider, restoring missing configuration option (PR #10053 by @daniel-lxs) + - Add: GPT-5.2 model to openai-native provider (PR #10024 by @hannesrudolph) + - Fix: Handle empty Gemini responses and reasoning loops to prevent infinite retries (PR #10007 by @hannesrudolph) + - Fix: Add missing tool_result blocks to prevent API errors when tool results are expected (PR #10015 by @daniel-lxs) + - Fix: Filter orphaned tool_results when more results than tool_uses to prevent message validation errors (PR #10027 by @daniel-lxs) + - Fix: Add general API endpoints for Z.ai provider (#9879 by @richtong, PR #9894 by @roomote) + - Remove: Deprecated list_code_definition_names tool (PR #10005 by @hannesrudolph) + - Add error details modal with on-demand display for improved error visibility when debugging issues (PR #9985 by @roomote) + - Fix: Prevent premature rawChunkTracker clearing for MCP tools, improving reliability of MCP tool streaming (PR #9993 by @daniel-lxs) + - Fix: Filter out 429 rate limit errors from API error telemetry for cleaner metrics (PR #9987 by @daniel-lxs) + - Fix: Correct TODO list display order in chat view to show items in proper sequence (PR #9991 by @roomote) + - Refactor: Unified context-management architecture with improved UX for better context control (PR #9795 by @hannesrudolph) + - Add new `search_replace` native tool for single-replacement operations with improved editing precision (PR #9918 by @hannesrudolph) + - Streaming tool stats and token usage throttling for better real-time feedback during generation (PR #9926 by @hannesrudolph) + - Add versioned settings support with minPluginVersion gating for Roo provider (PR #9934 by @hannesrudolph) + - Make Architect mode save plans to `/plans` directory and gitignore it (PR #9944 by @brunobergher) + - Add ability to save screenshots from the browser tool (PR #9963 by @mrubens) + - Refactor: Decouple tools from system prompt for cleaner architecture (PR #9784 by @daniel-lxs) + - Update DeepSeek models to V3.2 with new pricing (PR #9962 by @hannesrudolph) + - Add minimal and medium reasoning effort levels for Gemini models (PR #9973 by @hannesrudolph) + - Update xAI models catalog with latest model options (PR #9872 by @hannesrudolph) + - Add DeepSeek V3-2 support for Baseten provider (PR #9861 by @AlexKer) + - Tweaks to Baseten model definitions for better defaults (PR #9866 by @mrubens) + - Fix: Add xhigh reasoning effort support for gpt-5.1-codex-max (#9891 by @andrewginns, PR #9900 by @andrewginns) + - Fix: Add Kimi, MiniMax, and Qwen model configurations for Bedrock (#9902 by @jbearak, PR #9905 by @app/roomote) + - Configure tool preferences for xAI models (PR #9923 by @hannesrudolph) + - Default to using native tools when supported on OpenRouter (PR #9878 by @mrubens) + - Fix: Exclude apply_diff from native tools when diffEnabled is false (#9919 by @denis-kudelin, PR #9920 by @app/roomote) + - Fix: Always show tool protocol selector for openai-compatible provider (#9965 by @bozoweed, PR #9966 by @hannesrudolph) + - Fix: Respect explicit supportsReasoningEffort array values for proper model configuration (PR #9970 by @hannesrudolph) + - Add timeout configuration to OpenAI Compatible Provider Client (PR #9898 by @dcbartlett) + - Revert default tool protocol change from xml to native for stability (PR #9956 by @mrubens) + - Improve OpenAI error messages to be more useful for debugging (PR #9639 by @mrubens) + - Better error logs for parseToolCall exceptions (PR #9857 by @cte) + - Improve cloud job error logging for RCC provider errors (PR #9924 by @cte) + - Fix: Display actual API error message instead of generic text on retry (PR #9954 by @hannesrudolph) + - Add API error telemetry to OpenRouter provider for better diagnostics (PR #9953 by @daniel-lxs) + - Fix: Sanitize removed/invalid API providers to prevent infinite loop (PR #9869 by @hannesrudolph) + - Fix: Use foreground color for context-management icons (PR #9912 by @hannesrudolph) + - Fix: Suppress 'ask promise was ignored' error in handleError (PR #9914 by @daniel-lxs) + - Fix: Process finish_reason to emit tool_call_end events properly (PR #9927 by @daniel-lxs) + - Fix: Add finish_reason processing to xai.ts provider (PR #9929 by @daniel-lxs) + - Fix: Validate and fix tool_result IDs before API requests (PR #9952 by @daniel-lxs) + - Fix: Return undefined instead of 0 for disabled API timeout (PR #9960 by @hannesrudolph) + - Stop making unnecessary count_tokens requests for better performance (PR #9884 by @mrubens) + - Refactor: Consolidate ThinkingBudget components and fix disable handling (PR #9930 by @hannesrudolph) + - Forbid time estimates in architect mode for more focused planning (PR #9931 by @app/roomote + +- [#4568](https://github.com/Kilo-Org/kilocode/pull/4568) [`b1702cd`](https://github.com/Kilo-Org/kilocode/commit/b1702cd1c3119a89c96edf23c388b84135b8cbd3) Thanks [@marius-kilocode](https://github.com/marius-kilocode)! - Remove redundant "New Agent" and "Refresh messages" buttons from agent manager session detail header. + +- [#4228](https://github.com/Kilo-Org/kilocode/pull/4228) [`a128228`](https://github.com/Kilo-Org/kilocode/commit/a128228b3649924ad1fd88d040a79c6963a250bd) Thanks [@lambertjosh](https://github.com/lambertjosh)! - Change the default value of auto-approval for reading outside workspace to false + ## 4.139.0 ### Minor Changes diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index d48da7f015e..023d3228ab7 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @kilocode/cli +## 0.17.1 + +### Patch Changes + +- [#4186](https://github.com/Kilo-Org/kilocode/pull/4186) [`6078a9c`](https://github.com/Kilo-Org/kilocode/commit/6078a9ce77512faaebcda54ea9d2e909cf6b340c) Thanks [@lambertjosh](https://github.com/lambertjosh)! - Default read permissions now require approval for read operations outside the workspace + ## 0.17.0 ### Minor Changes diff --git a/cli/package.dist.json b/cli/package.dist.json index 8cb2c3e0f70..7a40a7aedfe 100644 --- a/cli/package.dist.json +++ b/cli/package.dist.json @@ -1,6 +1,6 @@ { "name": "@kilocode/cli", - "version": "0.17.0", + "version": "0.17.1", "description": "Terminal User Interface for Kilo Code", "type": "module", "main": "index.js", diff --git a/cli/package.json b/cli/package.json index 282300f34db..6495195c67a 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/cli", - "version": "0.17.0", + "version": "0.17.1", "description": "Terminal User Interface for Kilo Code", "type": "module", "main": "dist/index.js", diff --git a/src/package.json b/src/package.json index fc7db3ff748..00c42d1dbfa 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "kilocode", - "version": "4.139.0", + "version": "4.140.0", "icon": "assets/icons/logo-outline-black.png", "galleryBanner": { "color": "#FFFFFF", From e40379f7b295f6330c859ce861e4507e04481574 Mon Sep 17 00:00:00 2001 From: Kevin van Dijk Date: Fri, 19 Dec 2025 14:23:11 +0100 Subject: [PATCH 08/15] Add small correction to changeset --- .changeset/large-jars-train.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/large-jars-train.md b/.changeset/large-jars-train.md index 04e61b9aa55..eccc5f35ceb 100644 --- a/.changeset/large-jars-train.md +++ b/.changeset/large-jars-train.md @@ -1,8 +1,9 @@ --- -"@roo-code/types": patch +"kilo-code": patch --- chore: update Gemini Cli models and metadata + - Added gemini-3-flash-preview model configuration. - Updated the default model to gemini-3-flash-preview. - Updated maxThinkingTokens for gemini-3-pro-preview to 32,768. From 7542d7972b970aa331387ffcd4926ad8346eeee2 Mon Sep 17 00:00:00 2001 From: Kevin van Dijk Date: Fri, 19 Dec 2025 14:25:34 +0100 Subject: [PATCH 09/15] Set default for gemini-cli to 2.5-flash --- .changeset/large-jars-train.md | 1 - packages/types/src/providers/gemini-cli.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/large-jars-train.md b/.changeset/large-jars-train.md index eccc5f35ceb..0343c1a39cc 100644 --- a/.changeset/large-jars-train.md +++ b/.changeset/large-jars-train.md @@ -5,6 +5,5 @@ chore: update Gemini Cli models and metadata - Added gemini-3-flash-preview model configuration. -- Updated the default model to gemini-3-flash-preview. - Updated maxThinkingTokens for gemini-3-pro-preview to 32,768. - Reordered model definitions to prioritize newer versions. diff --git a/packages/types/src/providers/gemini-cli.ts b/packages/types/src/providers/gemini-cli.ts index f959dc1dbc0..73951e2ee36 100644 --- a/packages/types/src/providers/gemini-cli.ts +++ b/packages/types/src/providers/gemini-cli.ts @@ -4,7 +4,7 @@ import type { ModelInfo } from "../model.js" // Gemini CLI models with free tier pricing (all $0) export type GeminiCliModelId = keyof typeof geminiCliModels -export const geminiCliDefaultModelId: GeminiCliModelId = "gemini-3-flash-preview" +export const geminiCliDefaultModelId: GeminiCliModelId = "gemini-2.5-flash" export const geminiCliModels = { "gemini-3-pro-preview": { From e81e7fa011bffab844758dbe892429ac8584ffe4 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:30:07 +0000 Subject: [PATCH 10/15] docs: add Kilo Slackbot integration documentation - Add new slackbot.md documentation covering: - Overview and capabilities - Prerequisites (Kilo account, GitHub integration, Slack installation) - How to interact via DMs and channel mentions - Use cases (repo questions, implementing fixes, debugging, code reviews) - Cost information and tips for best results - Troubleshooting guide - Add slackbot entry to sidebars.ts in Advanced Usage section --- .../docs/advanced-usage/slackbot.md | 154 ++++++++++++++++++ apps/kilocode-docs/sidebars.ts | 1 + 2 files changed, 155 insertions(+) create mode 100644 apps/kilocode-docs/docs/advanced-usage/slackbot.md diff --git a/apps/kilocode-docs/docs/advanced-usage/slackbot.md b/apps/kilocode-docs/docs/advanced-usage/slackbot.md new file mode 100644 index 00000000000..b861574036e --- /dev/null +++ b/apps/kilocode-docs/docs/advanced-usage/slackbot.md @@ -0,0 +1,154 @@ +--- +title: Kilo Slackbot +sidebar_label: Kilo Slackbot +--- + +# Kilo Slackbot + +The Kilo Slackbot brings the power of Kilo Code directly into your Slack workspace. Ask questions about your repositories, request code implementations, or get help with issues—all without leaving Slack. + +--- + +## What You Can Do With the Slackbot + +- **Ask questions about your repositories** — Get explanations about code, architecture, or implementation details +- **Request code implementations** — Tell the bot to implement fixes or features suggested in Slack threads +- **Get help with debugging** — Share error messages or issues and get AI-powered assistance +- **Collaborate with your team** — Mention the bot in any channel to get help in context + +--- + +## Prerequisites + +Before using the Kilo Slackbot: + +- You must have a **Kilo Code account** with available credits +- Your **GitHub Integration must be configured** via the [Integrations tab](https://app.kilo.ai/integrations) so the Slackbot can access your repositories +- The Kilo Slackbot must be **installed in your Slack workspace** by a workspace admin + +--- + +## How to Interact with the Slackbot + +### Direct Messages + +You can message the Kilo Slackbot directly through Slack DMs for private conversations: + +1. Find **Kilo** in your Slack workspace's app list +2. Start a direct message conversation +3. Ask your question or describe what you need + +This is ideal for: +- Private questions about your code +- Sensitive debugging sessions +- Personal productivity tasks + +### Channel Mentions + +Mention the bot in any channel where it's been added: + +``` +@Kilo can you explain how the authentication flow works in our backend? +``` + +This is great for: +- Team discussions where AI assistance would help +- Collaborative debugging sessions +- Getting quick answers during code reviews + +--- + +## Use Cases + +### Ask Questions About Your Repositories + +Get instant answers about your codebase without switching contexts: + +``` +@Kilo what does the UserService class do in our main backend repo? +``` + +``` +@Kilo how is error handling implemented in the payment processing module? +``` + +### Implement Fixes from Slack Discussions + +When your team identifies an issue or improvement in a Slack thread, ask the bot to implement it: + +``` +@Kilo based on this thread, can you implement the fix for the null pointer exception in the order processing service? +``` + +The bot can: +- Read the context from the thread +- Understand the proposed solution +- Create a branch with the implementation +- Push the changes to your repository + +### Debug Issues + +Share error messages or stack traces and get help: + +``` +@Kilo I'm seeing this error in production: +[paste error message] +Can you help me understand what's causing it? +``` + +### Code Reviews and Explanations + +Get AI-powered insights during code reviews: + +``` +@Kilo can you review the changes in PR #123 and suggest any improvements? +``` + +--- + +## How It Works + +1. **Message the bot** — Either through DMs or by mentioning it in a channel +2. **Bot processes your request** — The Slackbot uses your connected GitHub repositories to understand context +3. **AI generates a response** — Kilo Code's AI analyzes your request and provides helpful responses +4. **Code changes (if requested)** — For implementation requests, the bot can create branches and push code + +--- + +## Cost + +- **Kilo Code credits are used** when the Slackbot performs work (model usage, operations, etc.) +- Credit usage is similar to using Kilo Code through other interfaces + +--- + +## Tips for Best Results + +- **Be specific** — The more context you provide, the better the response +- **Reference specific files or functions** — Help the bot understand exactly what you're asking about +- **Use threads** — Keep related conversations in threads for better context +- **Specify the repository** — If you have multiple repos connected, mention which one you're asking about + +--- + +## Limitations + +- The Slackbot can only access repositories you've connected through the [Integrations](https://app.kilo.ai/integrations) page +- Complex multi-step implementations may require follow-up messages +- Response times may vary based on the complexity of your request + +--- + +## Troubleshooting + +**"The bot isn't responding."** +Ensure the Kilo Slackbot is installed in your workspace and has been added to the channel you're using. + +**"The bot can't access my repository."** +Verify your GitHub integration is configured correctly in the [Integrations tab](https://app.kilo.ai/integrations). + +**"I'm getting incomplete responses."** +Try breaking your request into smaller, more specific questions. + +**"The bot doesn't understand my codebase."** +Make sure the repository you're asking about is connected and accessible through your GitHub integration. diff --git a/apps/kilocode-docs/sidebars.ts b/apps/kilocode-docs/sidebars.ts index 0e3ae210ca8..1b40ad47756 100644 --- a/apps/kilocode-docs/sidebars.ts +++ b/apps/kilocode-docs/sidebars.ts @@ -157,6 +157,7 @@ const sidebars: SidebarsConfig = { "features/auto-launch-configuration", "advanced-usage/auto-cleanup", "advanced-usage/integrations", + "advanced-usage/slackbot", "advanced-usage/appbuilder", "advanced-usage/cloud-agent", "advanced-usage/code-reviews", From 638256190e32208c92389b22f94c69b7fc348cd6 Mon Sep 17 00:00:00 2001 From: Remon Oldenbeuving Date: Fri, 19 Dec 2025 14:34:32 +0100 Subject: [PATCH 11/15] Apply suggestions from code review --- apps/kilocode-docs/docs/advanced-usage/slackbot.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/kilocode-docs/docs/advanced-usage/slackbot.md b/apps/kilocode-docs/docs/advanced-usage/slackbot.md index b861574036e..2a8470b879d 100644 --- a/apps/kilocode-docs/docs/advanced-usage/slackbot.md +++ b/apps/kilocode-docs/docs/advanced-usage/slackbot.md @@ -23,8 +23,9 @@ The Kilo Slackbot brings the power of Kilo Code directly into your Slack workspa Before using the Kilo Slackbot: - You must have a **Kilo Code account** with available credits -- Your **GitHub Integration must be configured** via the [Integrations tab](https://app.kilo.ai/integrations) so the Slackbot can access your repositories -- The Kilo Slackbot must be **installed in your Slack workspace** by a workspace admin +- Your **GitHub Integration must be configured** via the [Integrations tab](https://app.kilo.ai/integrations) so the Slackbot can access your repositories + +To install the Kilo Slackbot, simply go to the integrations menu in the sidebar on https://app.kilo.ai and set up the Slack integration. --- From 3f81b6a795144366e696f5e7cfdd74d789aed693 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Fri, 19 Dec 2025 15:15:30 +0100 Subject: [PATCH 12/15] Apply suggestion from @markijbema --- apps/kilocode-docs/docs/advanced-usage/slackbot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/kilocode-docs/docs/advanced-usage/slackbot.md b/apps/kilocode-docs/docs/advanced-usage/slackbot.md index 2a8470b879d..858d652852e 100644 --- a/apps/kilocode-docs/docs/advanced-usage/slackbot.md +++ b/apps/kilocode-docs/docs/advanced-usage/slackbot.md @@ -112,7 +112,7 @@ Get AI-powered insights during code reviews: 1. **Message the bot** — Either through DMs or by mentioning it in a channel 2. **Bot processes your request** — The Slackbot uses your connected GitHub repositories to understand context 3. **AI generates a response** — Kilo Code's AI analyzes your request and provides helpful responses -4. **Code changes (if requested)** — For implementation requests, the bot can create branches and push code +4. **Code changes (if requested)** — For implementation requests, the bot can create pull requests --- From 74c81b7269bc870e81c6f9586a102cba2ef49769 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Fri, 19 Dec 2025 15:16:25 +0100 Subject: [PATCH 13/15] Remove code reviews and explanations section Removed section on AI-powered insights during code reviews from the Slackbot documentation. --- apps/kilocode-docs/docs/advanced-usage/slackbot.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/apps/kilocode-docs/docs/advanced-usage/slackbot.md b/apps/kilocode-docs/docs/advanced-usage/slackbot.md index 858d652852e..3c9bf22f1dc 100644 --- a/apps/kilocode-docs/docs/advanced-usage/slackbot.md +++ b/apps/kilocode-docs/docs/advanced-usage/slackbot.md @@ -97,14 +97,6 @@ Share error messages or stack traces and get help: Can you help me understand what's causing it? ``` -### Code Reviews and Explanations - -Get AI-powered insights during code reviews: - -``` -@Kilo can you review the changes in PR #123 and suggest any improvements? -``` - --- ## How It Works From e259b04037c71a9bdd9e53c174b70a975e772833 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Fri, 19 Dec 2025 15:24:32 +0100 Subject: [PATCH 14/15] feat: add telemetry for chat textarea autocomplete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds telemetry tracking for the chat textarea autocomplete feature by reusing the existing `AutocompleteTelemetry` class with a new autocomplete type discriminator. ## Changes ### Extended AutocompleteTelemetry Class - Added `AutocompleteType` discriminator (`"inline"` | `"chat-textarea"`) to distinguish between editor autocomplete and chat textarea autocomplete - All telemetry events now include `autocompleteType` property for filtering in analytics - Made token/cost properties optional in `captureLlmRequestCompleted` (not available for chat completions) - Added optional `suggestionLength` parameter to `captureAcceptSuggestion` ### Chat Textarea Integration - Updated `ChatTextAreaAutocomplete.ts` to track: - Suggestion requests (`captureSuggestionRequested`) - LLM request completion with latency (`captureLlmRequestCompleted`) - LLM request failures with error details (`captureLlmRequestFailed`) - Suggestion filtering with reasons (`captureSuggestionFiltered`) - Successful suggestion returns (`captureLlmSuggestionReturned`) ### Acceptance Tracking - Added new `chatCompletionAccepted` webview message type - Created `handleChatCompletionAccepted.ts` handler for acceptance events - Updated `useChatGhostText.ts` to send telemetry when user accepts suggestions via Tab or ArrowRight - Tracks suggestion length for accepted completions ### Files Changed - `src/services/ghost/classic-auto-complete/AutocompleteTelemetry.ts` - Extended with type discriminator - `src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts` - Added telemetry calls - `src/services/ghost/chat-autocomplete/handleChatCompletionAccepted.ts` - New handler - `src/core/webview/webviewMessageHandler.ts` - Added message handler - `src/shared/WebviewMessage.ts` - Added new message type - `webview-ui/src/components/chat/hooks/useChatGhostText.ts` - Send acceptance events ## Design Decisions - **Reused existing telemetry class** rather than creating a separate one, using a type discriminator for filtering - **Same event names** as inline autocomplete for consistent analytics, differentiated by `autocompleteType` - **Acceptance tracked from webview** since that's where user interaction happens - **Error handling** wraps LLM calls to capture failures with latency and error details ## Testing - ✅ All tests pass - ✅ Type checking passes - ✅ Linting passes --- .changeset/small-towns-march.md | 5 ++ src/core/webview/webviewMessageHandler.ts | 7 ++ .../ChatTextAreaAutocomplete.ts | 90 ++++++++++++++----- .../handleChatCompletionAccepted.ts | 25 ++++++ .../AutocompleteTelemetry.ts | 43 ++++++--- src/shared/WebviewMessage.ts | 2 + .../components/chat/hooks/useChatGhostText.ts | 10 +++ 7 files changed, 148 insertions(+), 34 deletions(-) create mode 100644 .changeset/small-towns-march.md create mode 100644 src/services/ghost/chat-autocomplete/handleChatCompletionAccepted.ts diff --git a/.changeset/small-towns-march.md b/.changeset/small-towns-march.md new file mode 100644 index 00000000000..8be9da81341 --- /dev/null +++ b/.changeset/small-towns-march.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Add chat autocomplete telemetry diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index f2cf40bc916..720170b7725 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -3717,6 +3717,13 @@ export const webviewMessageHandler = async ( ) break } + case "chatCompletionAccepted": { + const { handleChatCompletionAccepted } = await import( + "../../services/ghost/chat-autocomplete/handleChatCompletionAccepted" + ) + handleChatCompletionAccepted(message as WebviewMessage & { type: "chatCompletionAccepted" }) + break + } // kilocode_change end: Chat text area FIM autocomplete case "openCommandFile": { try { diff --git a/src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts b/src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts index 3d7ad42d540..cc153a5733d 100644 --- a/src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts +++ b/src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts @@ -1,9 +1,10 @@ import * as vscode from "vscode" import { GhostModel } from "../GhostModel" import { ProviderSettingsManager } from "../../../core/config/ProviderSettingsManager" -import { VisibleCodeContext } from "../types" +import { AutocompleteContext, VisibleCodeContext } from "../types" import { ApiStreamChunk } from "../../../api/transform/stream" import { removePrefixOverlap } from "../../continuedev/core/autocomplete/postprocessing/removePrefixOverlap.js" +import { AutocompleteTelemetry } from "../classic-auto-complete/AutocompleteTelemetry" /** * Service for providing FIM-based autocomplete suggestions in ChatTextArea @@ -11,10 +12,12 @@ import { removePrefixOverlap } from "../../continuedev/core/autocomplete/postpro export class ChatTextAreaAutocomplete { private model: GhostModel private providerSettingsManager: ProviderSettingsManager + private telemetry: AutocompleteTelemetry constructor(providerSettingsManager: ProviderSettingsManager) { this.model = new GhostModel() this.providerSettingsManager = providerSettingsManager + this.telemetry = new AutocompleteTelemetry("chat-textarea") } async initialize(): Promise { @@ -30,6 +33,18 @@ export class ChatTextAreaAutocomplete { } async getCompletion(userText: string, visibleCodeContext?: VisibleCodeContext): Promise<{ suggestion: string }> { + const startTime = Date.now() + + // Build context for telemetry + const context: AutocompleteContext = { + languageId: "chat", // Chat textarea doesn't have a language ID + modelId: this.model.getModelName(), + provider: this.model.getProviderDisplayName(), + } + + // Capture suggestion requested + this.telemetry.captureSuggestionRequested(context) + if (!this.model.loaded) { const loaded = await this.initialize() if (!loaded) { @@ -47,29 +62,60 @@ export class ChatTextAreaAutocomplete { let response = "" - // Use FIM if supported, otherwise fall back to chat-based completion - if (this.model.supportsFim()) { - await this.model.generateFimResponse(prefix, suffix, (chunk) => { - response += chunk - }) - } else { - // Fall back to chat-based completion for models without FIM support - const systemPrompt = this.getChatSystemPrompt() - const userPrompt = this.getChatUserPrompt(prefix) - - await this.model.generateResponse(systemPrompt, userPrompt, (chunk) => { - if (chunk.type === "text") { - response += chunk.text - } - }) - } + try { + // Use FIM if supported, otherwise fall back to chat-based completion + if (this.model.supportsFim()) { + await this.model.generateFimResponse(prefix, suffix, (chunk) => { + response += chunk + }) + } else { + // Fall back to chat-based completion for models without FIM support + const systemPrompt = this.getChatSystemPrompt() + const userPrompt = this.getChatUserPrompt(prefix) + + await this.model.generateResponse(systemPrompt, userPrompt, (chunk) => { + if (chunk.type === "text") { + response += chunk.text + } + }) + } - const cleanedSuggestion = this.cleanSuggestion(response, userText) - console.log( - `[ChatAutocomplete] prefix: ${JSON.stringify(userText)} | response: ${JSON.stringify(response)} | cleanedSuggestion: ${JSON.stringify(cleanedSuggestion)}`, - ) + const latencyMs = Date.now() - startTime + + // Capture successful LLM request + this.telemetry.captureLlmRequestCompleted( + { + latencyMs, + // Token counts not available from current API + }, + context, + ) + + const cleanedSuggestion = this.cleanSuggestion(response, userText) + + // Track if suggestion was filtered or returned + if (!cleanedSuggestion) { + if (!response.trim()) { + this.telemetry.captureSuggestionFiltered("empty_response", context) + } else { + this.telemetry.captureSuggestionFiltered("filtered_by_postprocessing", context) + } + } else { + this.telemetry.captureLlmSuggestionReturned(context, cleanedSuggestion.length) + } - return { suggestion: cleanedSuggestion } + return { suggestion: cleanedSuggestion } + } catch (error) { + const latencyMs = Date.now() - startTime + this.telemetry.captureLlmRequestFailed( + { + latencyMs, + error: error instanceof Error ? error.message : String(error), + }, + context, + ) + return { suggestion: "" } + } } /** diff --git a/src/services/ghost/chat-autocomplete/handleChatCompletionAccepted.ts b/src/services/ghost/chat-autocomplete/handleChatCompletionAccepted.ts new file mode 100644 index 00000000000..ec00ba82aa7 --- /dev/null +++ b/src/services/ghost/chat-autocomplete/handleChatCompletionAccepted.ts @@ -0,0 +1,25 @@ +import { WebviewMessage } from "../../../shared/WebviewMessage" +import { AutocompleteTelemetry } from "../classic-auto-complete/AutocompleteTelemetry" + +// Singleton telemetry instance for chat-textarea autocomplete +// This ensures we use the same instance across requests and acceptance events +let telemetryInstance: AutocompleteTelemetry | null = null + +/** + * Get or create the telemetry instance for chat-textarea autocomplete + */ +export function getChatAutocompleteTelemetry(): AutocompleteTelemetry { + if (!telemetryInstance) { + telemetryInstance = new AutocompleteTelemetry("chat-textarea") + } + return telemetryInstance +} + +/** + * Handles a chat completion accepted event from the webview. + * Captures telemetry when the user accepts a suggestion via Tab or ArrowRight. + */ +export function handleChatCompletionAccepted(message: WebviewMessage & { type: "chatCompletionAccepted" }): void { + const telemetry = getChatAutocompleteTelemetry() + telemetry.captureAcceptSuggestion(message.suggestionLength) +} diff --git a/src/services/ghost/classic-auto-complete/AutocompleteTelemetry.ts b/src/services/ghost/classic-auto-complete/AutocompleteTelemetry.ts index 24a92f03692..604dd54e968 100644 --- a/src/services/ghost/classic-auto-complete/AutocompleteTelemetry.ts +++ b/src/services/ghost/classic-auto-complete/AutocompleteTelemetry.ts @@ -4,23 +4,38 @@ import type { AutocompleteContext, CacheMatchType } from "../types" export type { AutocompleteContext, CacheMatchType } +/** + * Type of autocomplete being used + * - "inline": Classic inline code completion in the editor + * - "chat-textarea": Autocomplete in the chat input textarea + */ +export type AutocompleteType = "inline" | "chat-textarea" + /** * Telemetry service for autocomplete events. * Can be initialized without parameters and injected into components that need telemetry tracking. + * Supports different autocomplete types via the `autocompleteType` property. */ export class AutocompleteTelemetry { - constructor() {} + private readonly autocompleteType: AutocompleteType + + /** + * Create a new AutocompleteTelemetry instance + * @param autocompleteType - The type of autocomplete (defaults to "inline" for backward compatibility) + */ + constructor(autocompleteType: AutocompleteType = "inline") { + this.autocompleteType = autocompleteType + } private captureEvent(event: TelemetryEventName, properties?: Record): void { // also log to console: if (TelemetryService.hasInstance()) { - if (properties !== undefined) { - TelemetryService.instance.captureEvent(event, properties) - console.log(`Autocomplete Telemetry event: ${event}`, properties) - } else { - TelemetryService.instance.captureEvent(event) - console.log(`Autocomplete Telemetry event: ${event}`) + const propsWithType = { + ...properties, + autocompleteType: this.autocompleteType, } + TelemetryService.instance.captureEvent(event, propsWithType) + console.log(`Autocomplete Telemetry event: ${event}`, propsWithType) } } @@ -98,9 +113,9 @@ export class AutocompleteTelemetry { public captureLlmRequestCompleted( properties: { latencyMs: number - cost: number - inputTokens: number - outputTokens: number + cost?: number + inputTokens?: number + outputTokens?: number }, context: AutocompleteContext, ): void { @@ -132,8 +147,12 @@ export class AutocompleteTelemetry { * There are two ways to analyze what percentage was accepted: * 1. Sum of this event divided by the sum of the suggestion returned event * 2. Sum of this event divided by the sum of the suggestion returned + cache hit events + * + * @param suggestionLength - Optional length of the accepted suggestion */ - public captureAcceptSuggestion(): void { - this.captureEvent(TelemetryEventName.AUTOCOMPLETE_ACCEPT_SUGGESTION) + public captureAcceptSuggestion(suggestionLength?: number): void { + this.captureEvent(TelemetryEventName.AUTOCOMPLETE_ACCEPT_SUGGESTION, { + ...(suggestionLength !== undefined && { suggestionLength }), + }) } } diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 07ff1ddbed4..5bfedc5cfd2 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -287,7 +287,9 @@ export interface WebviewMessage { | "cancelDeviceAuth" // kilocode_change: Cancel device auth flow | "deviceAuthCompleteWithProfile" // kilocode_change: Device auth complete with specific profile | "requestChatCompletion" // kilocode_change: Request FIM completion for chat text area + | "chatCompletionAccepted" // kilocode_change: User accepted a chat completion suggestion text?: string + suggestionLength?: number // kilocode_change: Length of accepted suggestion for telemetry completionRequestId?: string // kilocode_change shareId?: string // kilocode_change - for sessionFork sessionId?: string // kilocode_change - for sessionSelect diff --git a/webview-ui/src/components/chat/hooks/useChatGhostText.ts b/webview-ui/src/components/chat/hooks/useChatGhostText.ts index 72073f04571..a31824c53ba 100644 --- a/webview-ui/src/components/chat/hooks/useChatGhostText.ts +++ b/webview-ui/src/components/chat/hooks/useChatGhostText.ts @@ -65,6 +65,11 @@ export function useChatGhostText({ event.preventDefault() skipNextCompletionRef.current = true insertTextAtCursor(textArea, ghostText) + // Send telemetry event for accepted suggestion + vscode.postMessage({ + type: "chatCompletionAccepted", + suggestionLength: ghostText.length, + }) setGhostText("") return true } @@ -81,6 +86,11 @@ export function useChatGhostText({ skipNextCompletionRef.current = true const { word, remainder } = extractNextWord(ghostText) insertTextAtCursor(textArea, word) + // Send telemetry event for accepted word + vscode.postMessage({ + type: "chatCompletionAccepted", + suggestionLength: word.length, + }) setGhostText(remainder) return true } From 1df99797a8c1d9add29da2a7755e4213c7d698fe Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Fri, 19 Dec 2025 15:55:20 +0100 Subject: [PATCH 15/15] import statically --- src/core/webview/webviewMessageHandler.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 720170b7725..99deac1e3cb 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -88,6 +88,8 @@ import { deviceAuthMessageHandler, } from "../kilocode/webview/webviewMessageHandlerUtils" import { GhostServiceManager } from "../../services/ghost/GhostServiceManager" +import { handleChatCompletionRequest } from "../../services/ghost/chat-autocomplete/handleChatCompletionRequest" +import { handleChatCompletionAccepted } from "../../services/ghost/chat-autocomplete/handleChatCompletionAccepted" // kilocode_change end const ALLOWED_VSCODE_SETTINGS = new Set(["terminal.integrated.inheritEnv"]) @@ -3707,9 +3709,6 @@ export const webviewMessageHandler = async ( break } // kilocode_change start: Chat text area FIM autocomplete case "requestChatCompletion": { - const { handleChatCompletionRequest } = await import( - "../../services/ghost/chat-autocomplete/handleChatCompletionRequest" - ) await handleChatCompletionRequest( message as WebviewMessage & { type: "requestChatCompletion" }, provider, @@ -3718,9 +3717,6 @@ export const webviewMessageHandler = async ( break } case "chatCompletionAccepted": { - const { handleChatCompletionAccepted } = await import( - "../../services/ghost/chat-autocomplete/handleChatCompletionAccepted" - ) handleChatCompletionAccepted(message as WebviewMessage & { type: "chatCompletionAccepted" }) break }