diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index e96758ef6a6..44e16885e4c 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -719,7 +719,7 @@ export class KiloProvider implements vscode.WebviewViewProvider { providerID?: string, modelID?: string, agent?: string, - files?: Array<{ mime: string; url: string }>, + files?: unknown, ): Promise { if (!this.httpClient) { this.postMessage({ @@ -748,6 +748,13 @@ export class KiloProvider implements vscode.WebviewViewProvider { throw new Error("No session available") } + // Sanitize files input from webview (runtime-untyped) + const sanitized = Array.isArray(files) + ? files.filter( + (f): f is { mime: string; url: string } => f && typeof f.mime === "string" && typeof f.url === "string", + ) + : [] + // Build parts array with file context and user text const parts: Array<{ type: "text"; text: string } | { type: "file"; mime: string; url: string }> = [] @@ -755,17 +762,15 @@ export class KiloProvider implements vscode.WebviewViewProvider { const editor = vscode.window.activeTextEditor if (editor && editor.document.uri.scheme === "file") { const url = editor.document.uri.toString() - const already = files?.some((f) => f.url === url) + const already = sanitized.some((f) => f.url === url) if (!already) { parts.push({ type: "file", mime: "text/plain", url }) } } // Add any explicitly attached files from the webview - if (files) { - for (const f of files) { - parts.push({ type: "file", mime: f.mime, url: f.url }) - } + for (const f of sanitized) { + parts.push({ type: "file", mime: f.mime, url: f.url }) } parts.push({ type: "text", text }) diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index 80d3cc6b8b2..4737d46ee1a 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -33,6 +33,7 @@ import type { ContextUsage, AgentInfo, ExtensionMessage, + FileAttachment, } from "../types/messages" // Store structure for messages and parts @@ -87,12 +88,7 @@ interface SessionContextValue { selectAgent: (name: string) => void // Actions - sendMessage: ( - text: string, - providerID?: string, - modelID?: string, - files?: Array<{ mime: string; url: string }>, - ) => void + sendMessage: (text: string, providerID?: string, modelID?: string, files?: FileAttachment[]) => void abort: () => void compact: () => void respondToPermission: (permissionId: string, response: "once" | "always" | "reject") => void @@ -496,12 +492,7 @@ export const SessionProvider: ParentComponent = (props) => { setSelectedAgentName(name) } - function sendMessage( - text: string, - providerID?: string, - modelID?: string, - files?: Array<{ mime: string; url: string }>, - ) { + function sendMessage(text: string, providerID?: string, modelID?: string, files?: FileAttachment[]) { if (!server.isConnected()) { console.warn("[Kilo New] Cannot send message: not connected") return diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index 3d9bac3285f..3ad3d0637eb 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -519,6 +519,11 @@ export type ExtensionMessage = // Messages FROM webview TO extension // ============================================ +export interface FileAttachment { + mime: string + url: string +} + export interface SendMessageRequest { type: "sendMessage" text: string @@ -526,7 +531,7 @@ export interface SendMessageRequest { providerID?: string modelID?: string agent?: string - files?: Array<{ mime: string; url: string }> + files?: FileAttachment[] } export interface AbortRequest {