From db1227da2ca0aa972243467750fffb0affac118d Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:55:44 +0000 Subject: [PATCH] fix(vscode): validate autocomplete model settings --- .../autocomplete/__tests__/settings.spec.ts | 66 +++++++++++++++++++ .../src/services/autocomplete/settings.ts | 12 +++- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts diff --git a/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts b/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts new file mode 100644 index 0000000000..51eccf7f3f --- /dev/null +++ b/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts @@ -0,0 +1,66 @@ +import { beforeEach, describe, expect, it, vi } from "vitest" + +const state = new Map() +const update = vi.fn(async (key: string, value: unknown) => { + state.set(key, value) +}) + +vi.mock("vscode", () => ({ + ConfigurationTarget: { + Global: 1, + }, + workspace: { + getConfiguration: vi.fn(() => ({ + get: vi.fn((key: string, fallback: unknown) => state.get(key) ?? fallback), + update, + })), + onDidChangeConfiguration: vi.fn(), + }, +})) + +describe("autocomplete settings", () => { + beforeEach(() => { + state.clear() + update.mockClear() + }) + + it("includes the configured model in loaded settings", async () => { + state.set("model", "inception/mercury-edit") + const { buildAutocompleteSettingsMessage } = await import("../settings") + + expect(buildAutocompleteSettingsMessage().settings.model).toBe("inception/mercury-edit") + }) + + it("persists supported model updates", async () => { + const post = vi.fn() + const { routeAutocompleteMessage } = await import("../settings") + + await routeAutocompleteMessage( + { type: "updateAutocompleteSetting", key: "model", value: "inception/mercury-edit" }, + post, + ) + + expect(update).toHaveBeenCalledWith("model", "inception/mercury-edit", 1) + expect(post).toHaveBeenCalledWith(expect.objectContaining({ type: "autocompleteSettingsLoaded" })) + }) + + it("rejects unsupported model updates", async () => { + const post = vi.fn() + const { routeAutocompleteMessage } = await import("../settings") + + await routeAutocompleteMessage({ type: "updateAutocompleteSetting", key: "model", value: "other/model" }, post) + + expect(update).not.toHaveBeenCalled() + expect(post).not.toHaveBeenCalled() + }) + + it("rejects non-boolean toggle updates", async () => { + const post = vi.fn() + const { routeAutocompleteMessage } = await import("../settings") + + await routeAutocompleteMessage({ type: "updateAutocompleteSetting", key: "enableAutoTrigger", value: "true" }, post) + + expect(update).not.toHaveBeenCalled() + expect(post).not.toHaveBeenCalled() + }) +}) diff --git a/packages/kilo-vscode/src/services/autocomplete/settings.ts b/packages/kilo-vscode/src/services/autocomplete/settings.ts index 288c4cb7f1..52f3883920 100644 --- a/packages/kilo-vscode/src/services/autocomplete/settings.ts +++ b/packages/kilo-vscode/src/services/autocomplete/settings.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode" -import { DEFAULT_AUTOCOMPLETE_MODEL } from "../../shared/autocomplete-models" +import { AUTOCOMPLETE_MODELS, DEFAULT_AUTOCOMPLETE_MODEL } from "../../shared/autocomplete-models" const keys = new Set(["enableAutoTrigger", "enableSmartInlineTaskKeybinding", "enableChatAutocomplete", "model"]) @@ -52,6 +52,7 @@ export function watchAutocompleteConfig(post: Post): vscode.Disposable { async function update(key: unknown, value: unknown) { if (typeof key !== "string") return false if (!keys.has(key)) return false + if (!valid(key, value)) return false await vscode.workspace .getConfiguration("kilo-code.new.autocomplete") @@ -59,3 +60,12 @@ async function update(key: unknown, value: unknown) { return true } + +function valid(key: string, value: unknown) { + if (key === "model") { + if (typeof value !== "string") return false + return AUTOCOMPLETE_MODELS.some((m) => m.id === value) + } + + return typeof value === "boolean" +}