diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index f09622232..a99dcbb99 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -285,6 +285,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper message.providerID, message.modelID, message.agent, + message.variant, files, ) break @@ -439,6 +440,23 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "telemetry": TelemetryProxy.capture(message.event, message.properties) break + case "persistVariant": { + const stored = + this.extensionContext?.globalState.get>("variantSelections") ?? {} + if (message.value === undefined) { + delete stored[message.key] + } else { + stored[message.key] = message.value + } + await this.extensionContext?.globalState.update("variantSelections", stored) + break + } + case "requestVariants": { + const variants = + this.extensionContext?.globalState.get>("variantSelections") ?? {} + this.postMessage({ type: "variantsLoaded", variants }) + break + } } }) } @@ -1002,6 +1020,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper providerID?: string, modelID?: string, agent?: string, + variant?: string, files?: Array<{ mime: string; url: string }>, ): Promise { if (!this.httpClient) { @@ -1057,6 +1076,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper providerID, modelID, agent, + variant, }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to send message:", error) diff --git a/packages/kilo-vscode/src/services/cli-backend/http-client.ts b/packages/kilo-vscode/src/services/cli-backend/http-client.ts index fcbb2646a..8892525c4 100644 --- a/packages/kilo-vscode/src/services/cli-backend/http-client.ts +++ b/packages/kilo-vscode/src/services/cli-backend/http-client.ts @@ -214,7 +214,7 @@ export class HttpClient { sessionId: string, parts: Array<{ type: "text"; text: string } | { type: "file"; mime: string; url: string }>, directory: string, - options?: { providerID?: string; modelID?: string; agent?: string }, + options?: { providerID?: string; modelID?: string; agent?: string; variant?: string }, ): Promise { const body: Record = { parts } if (options?.providerID && options?.modelID) { @@ -224,6 +224,9 @@ export class HttpClient { if (options?.agent) { body.agent = options.agent } + if (options?.variant) { + body.variant = options.variant + } await this.request("POST", `/session/${sessionId}/message`, body, { directory, allowEmpty: true }) } diff --git a/packages/kilo-vscode/src/services/cli-backend/types.ts b/packages/kilo-vscode/src/services/cli-backend/types.ts index 461082255..bf143c714 100644 --- a/packages/kilo-vscode/src/services/cli-backend/types.ts +++ b/packages/kilo-vscode/src/services/cli-backend/types.ts @@ -139,6 +139,8 @@ export interface ProviderModel { latest?: boolean // Actual shape returned by the server (Provider.Model) limit?: { context: number; input?: number; output: number } + variants?: Record> + capabilities?: { reasoning: boolean } } // Provider definition diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index 64c26abaa..6f089960a 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -314,6 +314,18 @@ export const PromptInput: Component = () => {
+ 0}> + + + +
// sessionID -> todos modelSelections: Record // sessionID -> model agentSelections: Record // sessionID -> agent name + variantSelections: Record // "providerID/modelID" -> variant name } interface SessionContextValue { @@ -105,6 +106,11 @@ interface SessionContextValue { setSessionModel: (sessionID: string, providerID: string, modelID: string) => void setSessionAgent: (sessionID: string, name: string) => void + // Thinking variant for the selected model + variantList: () => string[] + currentVariant: () => string | undefined + cycleVariant: () => void + // Actions sendMessage: (text: string, providerID?: string, modelID?: string, files?: FileAttachment[]) => void abort: () => void @@ -177,6 +183,7 @@ export const SessionProvider: ParentComponent = (props) => { todos: {}, modelSelections: {}, agentSelections: {}, + variantSelections: {}, }) // Keep pending selection in sync with provider default until the user @@ -268,6 +275,56 @@ export const SessionProvider: ParentComponent = (props) => { clearInterval(agentRetryTimer) }) + // Variant (thinking effort) selection — keyed by "providerID/modelID" + const variantKey = (sel: ModelSelection) => `${sel.providerID}/${sel.modelID}` + + const variantList = () => { + const sel = selected() + if (!sel) return [] + const model = provider.findModel(sel) + if (!model?.variants) return [] + return Object.keys(model.variants) + } + + const currentVariant = () => { + const sel = selected() + if (!sel) return undefined + const key = variantKey(sel) + const stored = store.variantSelections[key] + const list = variantList() + if (stored && list.includes(stored)) return stored + return undefined + } + + const cycleVariant = () => { + const sel = selected() + if (!sel) return + const list = variantList() + if (list.length === 0) return + const key = variantKey(sel) + const current = store.variantSelections[key] + const next = (() => { + if (!current || !list.includes(current)) return list[0] + const idx = list.indexOf(current) + if (idx === list.length - 1) return undefined + return list[idx + 1] + })() + setStore("variantSelections", key, next) + vscode.postMessage({ type: "persistVariant", key, value: next }) + } + + // Load persisted variants from extension globalState + const unsubVariants = vscode.onMessage((message: ExtensionMessage) => { + if (message.type !== "variantsLoaded") return + for (const [k, v] of Object.entries(message.variants)) { + setStore("variantSelections", k, v) + } + }) + + vscode.postMessage({ type: "requestVariants" }) + + onCleanup(unsubVariants) + // Handle messages from extension onMount(() => { const unsubscribe = vscode.onMessage((message: ExtensionMessage) => { @@ -631,6 +688,7 @@ export const SessionProvider: ParentComponent = (props) => { providerID, modelID, agent, + variant: currentVariant(), files, }) } @@ -866,6 +924,9 @@ export const SessionProvider: ParentComponent = (props) => { }, allMessages, allParts, + variantList, + currentVariant, + cycleVariant, sendMessage, abort, compact, diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index 8c9f7b003..06320f478 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -215,6 +215,8 @@ export interface ProviderModel { latest?: boolean // Actual shape returned by the server (Provider.Model) limit?: { context: number; input?: number; output: number } + variants?: Record> + capabilities?: { reasoning: boolean } } export interface Provider { @@ -607,6 +609,12 @@ export interface AgentManagerMultiVersionProgressMessage { groupId?: string } +// Stored variant selections loaded from extension globalState (extension → webview) +export interface VariantsLoadedMessage { + type: "variantsLoaded" + variants: Record +} + // Request webview to send initial prompt to a newly created session (extension → webview) export interface AgentManagerSendInitialMessage { type: "agentManager.sendInitialMessage" @@ -663,6 +671,7 @@ export type ExtensionMessage = | AgentManagerSendInitialMessage | SetChatBoxMessage | TriggerTaskMessage + | VariantsLoadedMessage // ============================================ // Messages FROM webview TO extension @@ -680,6 +689,7 @@ export interface SendMessageRequest { providerID?: string modelID?: string agent?: string + variant?: string files?: FileAttachment[] } @@ -947,6 +957,18 @@ export interface SetSessionsCollapsedRequest { collapsed: boolean } +// Variant persistence (webview → extension) +export interface PersistVariantRequest { + type: "persistVariant" + key: string + value: string | undefined +} + +// Request stored variants from extension (webview → extension) +export interface RequestVariantsMessage { + type: "requestVariants" +} + export type WebviewMessage = | SendMessageRequest | AbortRequest @@ -999,6 +1021,8 @@ export type WebviewMessage = | CreateMultiVersionRequest | SetTabOrderRequest | SetSessionsCollapsedRequest + | PersistVariantRequest + | RequestVariantsMessage // ============================================ // VS Code API type