From cb329c6bdb4be807f25206079e915400cae976b6 Mon Sep 17 00:00:00 2001 From: kirillk Date: Tue, 7 Jul 2026 14:13:35 -0400 Subject: [PATCH] chore: drop unrelated review fixes --- .../code-with-ai/agents/custom-models.md | 4 +- .../pages/code-with-ai/platforms/cli.md | 4 +- .../tests/unit/config-scope.test.ts | 15 ------- .../tests/unit/speech-to-text-button.test.ts | 30 ------------- .../src/components/chat/PromptInput.tsx | 2 +- .../speech-to-text/SpeechToTextButton.tsx | 5 ++- .../speech-to-text/speech-cleanup.ts | 5 --- .../src/cli/cmd/tui/routes/session/index.tsx | 5 ++- .../cli/cmd/tui/component/prompt/vim.ts | 2 +- .../tui/routes/session/routed-model-meta.tsx | 4 +- .../src/kilocode/session/routed-model.ts | 7 +-- .../kilocode/cli/cmd/tui/prompt/vim.test.ts | 22 --------- .../test/kilocode/config/config.test.ts | 45 ------------------- .../kilocode/session-routed-model.test.ts | 27 +---------- 14 files changed, 18 insertions(+), 159 deletions(-) delete mode 100644 packages/kilo-vscode/tests/unit/speech-to-text-button.test.ts delete mode 100644 packages/kilo-vscode/webview-ui/src/components/speech-to-text/speech-cleanup.ts diff --git a/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md b/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md index 065672280e6..d489dc63ebc 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md @@ -390,8 +390,8 @@ You can also set options that apply to all models from a provider: | `timeout` | `number \| false` | Request timeout in milliseconds. Defaults to `300000` (5 minutes); set to `false` to disable | | `chunkTimeout` | `number` | Timeout in milliseconds between streamed response chunks. If no chunk arrives within this window, the request is aborted and retried. This catches silent provider dropouts where the TCP connection stays open but SSE streaming stops. Recommended: `15000`–`30000` (15–30 seconds) for providers with unreliable streaming. | -{% callout type="warning" title="{env:} requires trusted config; {file:} is project-confined" %} -`{env:VAR}` references in `apiKey` (or any option) resolve **only** when the config lives in a trusted location: your global config (`~/.config/kilo`), a config passed via `KILO_CONFIG` / `KILO_CONFIG_CONTENT`, or organization/MDM-managed config. A project-level `kilo.json` / `opencode.json` committed to a repository **cannot** resolve `{env:VAR}`; if one appears anywhere in that file, Kilo drops the entire file's config and logs a warning, so unrelated settings in the same file stop applying too. This prevents a malicious repository from exfiltrating your secrets to an attacker-controlled `baseURL` just by being opened. `{file:...}` works in trusted config and still works in project config, but only for files that resolve inside the project root — references that leave it (absolute paths outside the root, `../` traversal, and symlink escapes) are rejected. Keep provider credentials in your global config. +{% callout type="warning" title="{env:} / {file:} only resolve in trusted config" %} +`{env:VAR}` and `{file:...}` references in `apiKey` (or any option) are resolved **only** when the config lives in a trusted location: your global config (`~/.config/kilo`), a config passed via `KILO_CONFIG` / `KILO_CONFIG_CONTENT`, or organization/MDM-managed config. A project-level `kilo.json` / `opencode.json` committed to a repository **cannot** resolve `{env:VAR}` — the reference is ignored and a warning is logged, so a provider configured this way in a repo will not authenticate. This prevents a malicious repository from exfiltrating your secrets to an attacker-controlled `baseURL` just by being opened. `{file:...}` still works in project config, but only for files that resolve inside the project root — references that leave it (absolute paths outside the root, `../` traversal, and symlink escapes) are rejected. Keep provider credentials in your global config. {% /callout %} ## Filtering Available Models diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/cli.md b/packages/kilo-docs/pages/code-with-ai/platforms/cli.md index be2bafc75d4..d785539288e 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/cli.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/cli.md @@ -465,8 +465,8 @@ Use `{env:VARIABLE_NAME}` syntax in config files to reference environment variab } ``` -{% callout type="warning" title="{env:} requires trusted config; {file:} is project-confined" %} -`{env:VAR}` references resolve **only** in trusted config: your global config (`~/.config/kilo`), a config passed via `KILO_CONFIG` / `KILO_CONFIG_CONTENT`, or organization/MDM-managed config. A project-level `kilo.json` / `opencode.json` committed to a repository **cannot** use `{env:VAR}`; if one appears anywhere in that file, Kilo drops the entire file's config and logs a warning, so unrelated settings in the same file stop applying too. This prevents a malicious repository from exfiltrating your secrets to an attacker-controlled `baseURL` simply by being opened. `{file:...}` works in trusted config and still works in project config, but only for files that resolve inside the project root — references that leave it (absolute paths outside the root, `../` traversal, and symlink escapes) are rejected. +{% callout type="warning" title="Only works in trusted config" %} +`{env:VAR}` (and `{file:...}`) references are resolved **only** in trusted config: your global config (`~/.config/kilo`), a config passed via `KILO_CONFIG` / `KILO_CONFIG_CONTENT`, or organization/MDM-managed config. A project-level `kilo.json` / `opencode.json` committed to a repository **cannot** use `{env:VAR}` — the reference is ignored and a warning is logged. This prevents a malicious repository from exfiltrating your secrets to an attacker-controlled `baseURL` simply by being opened. `{file:...}` still works in project config, but only for files that resolve inside the project root — references that leave it (absolute paths outside the root, `../` traversal, and symlink escapes) are rejected. {% /callout %} For full details on all configuration options including compaction, file watchers, plugins, and experimental features, see the [OpenCode Config documentation](https://opencode.ai/docs/config). diff --git a/packages/kilo-vscode/tests/unit/config-scope.test.ts b/packages/kilo-vscode/tests/unit/config-scope.test.ts index aee2c488ce7..eca588a5d1b 100644 --- a/packages/kilo-vscode/tests/unit/config-scope.test.ts +++ b/packages/kilo-vscode/tests/unit/config-scope.test.ts @@ -47,19 +47,4 @@ describe("splitConfigByScope", () => { }) expect(split.project).toEqual({}) }) - - it("writes sandbox writable paths to global config", () => { - const split = splitConfigByScope({ - experimental: { - sandbox_writable_paths: ["/tmp/kilo"], - }, - }) - - expect(split.global).toEqual({ - experimental: { - sandbox_writable_paths: ["/tmp/kilo"], - }, - }) - expect(split.project).toEqual({}) - }) }) diff --git a/packages/kilo-vscode/tests/unit/speech-to-text-button.test.ts b/packages/kilo-vscode/tests/unit/speech-to-text-button.test.ts deleted file mode 100644 index 56db776d55f..00000000000 --- a/packages/kilo-vscode/tests/unit/speech-to-text-button.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { describe, expect, it } from "bun:test" -import { cleanupSpeechCapture } from "../../webview-ui/src/components/speech-to-text/speech-cleanup" - -describe("speech-to-text button", () => { - it("cancels active capture on component cleanup", () => { - let cancels = 0 - - cleanupSpeechCapture({ - active: () => true, - cancel: () => { - cancels += 1 - }, - }) - - expect(cancels).toBe(1) - }) - - it("does not cancel idle capture on component cleanup", () => { - let cancels = 0 - - cleanupSpeechCapture({ - active: () => false, - cancel: () => { - cancels += 1 - }, - }) - - expect(cancels).toBe(0) - }) -}) 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 0298535f8a4..1593dadf0c2 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -1394,7 +1394,7 @@ export const PromptInput: Component = (props) => { - + = (props) => { props.start() } - onCleanup(() => cleanupSpeechCapture(props.speech)) + onCleanup(() => { + if (props.speech.active()) props.speech.cancel() + }) return ( diff --git a/packages/kilo-vscode/webview-ui/src/components/speech-to-text/speech-cleanup.ts b/packages/kilo-vscode/webview-ui/src/components/speech-to-text/speech-cleanup.ts deleted file mode 100644 index fa97233f3e9..00000000000 --- a/packages/kilo-vscode/webview-ui/src/components/speech-to-text/speech-cleanup.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { SpeechToText } from "./useSpeechToText" - -export function cleanupSpeechCapture(speech: Pick) { - if (speech.active()) speech.cancel() -} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index f14577ce029..9b3c279feac 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1738,8 +1738,11 @@ function StepFinishPart(props: { last: boolean; part: StepFinishPart; message: A const { theme } = useTheme() const info = useContext(RoutedModelMeta.Context) const routed = createMemo(() => { + if (props.message.providerID !== "kilo") return undefined + if (!props.message.modelID.startsWith("kilo-auto/")) return undefined const model = props.part.model - if (!RoutedModelMeta.routed(model, props.message)) return undefined + if (!model) return undefined + if (model.providerID === props.message.providerID && model.modelID === props.message.modelID) return undefined return RoutedModelMeta.label(ctx.providers(), model) }) const consumed = createMemo(() => info().consumed.has(props.part.id)) diff --git a/packages/opencode/src/kilocode/cli/cmd/tui/component/prompt/vim.ts b/packages/opencode/src/kilocode/cli/cmd/tui/component/prompt/vim.ts index ad1d0b58d93..2b91606c86c 100644 --- a/packages/opencode/src/kilocode/cli/cmd/tui/component/prompt/vim.ts +++ b/packages/opencode/src/kilocode/cli/cmd/tui/component/prompt/vim.ts @@ -299,7 +299,7 @@ function resolveMotion(text: string, pos: number, key: string, count: number, aw case "$": { let t = pos for (let i = 0; i < reps; i++) t = lineEnd(text, lineEnd(text, t) + (i === 0 ? 0 : 1)) - return { target: Math.max(lineStart(text, t), lineEnd(text, t) - 1), inclusive: true, linewise: false } + return { target: lineEnd(text, t), inclusive: true, linewise: false } } case "G": { let target = 0 diff --git a/packages/opencode/src/kilocode/cli/cmd/tui/routes/session/routed-model-meta.tsx b/packages/opencode/src/kilocode/cli/cmd/tui/routes/session/routed-model-meta.tsx index 12179aba523..61b964b2707 100644 --- a/packages/opencode/src/kilocode/cli/cmd/tui/routes/session/routed-model-meta.tsx +++ b/packages/opencode/src/kilocode/cli/cmd/tui/routes/session/routed-model-meta.tsx @@ -39,10 +39,10 @@ export namespace RoutedModelMeta { return KiloRoutedModel.displayName(text) } - export function routed(model: StepFinishPart["model"], message: Message) { + function routed(model: StepFinishPart["model"], message: Message) { if (!model) return undefined if (message.providerID !== "kilo") return undefined - if (!KiloRoutedModel.routedSelection(message.modelID)) return undefined + if (!message.modelID.startsWith("kilo-auto/")) return undefined if (model.providerID === message.providerID && model.modelID === message.modelID) return undefined return model } diff --git a/packages/opencode/src/kilocode/session/routed-model.ts b/packages/opencode/src/kilocode/session/routed-model.ts index ba474dad40f..eace803049a 100644 --- a/packages/opencode/src/kilocode/session/routed-model.ts +++ b/packages/opencode/src/kilocode/session/routed-model.ts @@ -47,15 +47,10 @@ export namespace KiloRoutedModel { input: { providerID: ProviderID; modelID: string; selected?: string }, ) { if (input.providerID !== ProviderID.kilo) return undefined - if (!routedSelection(input.modelID)) return undefined + if (!input.modelID.startsWith("kilo-auto/") && !input.modelID.includes("fable")) return undefined const model = read(meta, input.providerID) if (!model) return undefined if (model.modelID === input.modelID || model.modelID === input.selected) return undefined return model } - - export function routedSelection(modelID: string) { - const id = modelID.trim() - return id.startsWith("kilo-auto/") || /(?:^|[/.])claude-fable(?:[-.:/]|$)/.test(id) - } } diff --git a/packages/opencode/test/kilocode/cli/cmd/tui/prompt/vim.test.ts b/packages/opencode/test/kilocode/cli/cmd/tui/prompt/vim.test.ts index 878ffed9537..43a2330d618 100644 --- a/packages/opencode/test/kilocode/cli/cmd/tui/prompt/vim.test.ts +++ b/packages/opencode/test/kilocode/cli/cmd/tui/prompt/vim.test.ts @@ -199,28 +199,6 @@ describe("vim edits", () => { expect(doc.text).toBe("bar baz") }) - test("d$ deletes line content without consuming the newline", () => { - const doc = new MockDoc("abc\ndef", 0) - const state = createVimState("normal") - feed(doc, state, "d$") - expect(doc.text).toBe("\ndef") - }) - - test("c$ changes line content without consuming the newline", () => { - const doc = new MockDoc("abc\ndef", 0) - const state = createVimState("normal") - feed(doc, state, "c$") - expect(doc.text).toBe("\ndef") - expect(state.mode).toBe("insert") - }) - - test("y$ yanks line content without the newline", () => { - const doc = new MockDoc("abc\ndef", 0) - const state = createVimState("normal") - feed(doc, state, "y$") - expect(state.register).toEqual({ text: "abc", linewise: false }) - }) - test("dd deletes the current line", () => { const doc = new MockDoc("one\ntwo\nthree", 4) const state = createVimState("normal") diff --git a/packages/opencode/test/kilocode/config/config.test.ts b/packages/opencode/test/kilocode/config/config.test.ts index b8a41edcc6f..467282a3463 100644 --- a/packages/opencode/test/kilocode/config/config.test.ts +++ b/packages/opencode/test/kilocode/config/config.test.ts @@ -243,51 +243,6 @@ describe("kilocode indexing config", () => { }) describe("kilocode sandbox writable paths config", () => { - test("honors sandbox_writable_paths from global config", async () => { - await using globalTmp = await tmpdir() - await using tmp = await tmpdir({ git: true }) - - const prev = Global.Path.config - ;(Global.Path as { config: string }).config = globalTmp.path - await clear() - await disposeAllInstances() - - try { - await writeConfig(globalTmp.path, { - $schema: "https://app.kilo.ai/config.json", - experimental: { sandbox_writable_paths: ["/tmp/global"] }, - }) - - await provideTestInstance({ - directory: tmp.path, - fn: async () => { - const config = await load() - expect(config.experimental?.sandbox_writable_paths).toEqual(["/tmp/global"]) - }, - }) - } finally { - ;(Global.Path as { config: string }).config = prev - await clear() - await disposeAllInstances() - } - }) - - test("drops sandbox_writable_paths from project config", async () => { - await using tmp = await tmpdir({ git: true }) - - await writeConfig(tmp.path, { - experimental: { sandbox_writable_paths: ["/tmp/project"] }, - }) - - await provideTestInstance({ - directory: tmp.path, - fn: async () => { - const config = await load() - expect(config.experimental?.sandbox_writable_paths).toBeUndefined() - }, - }) - }) - test("honors sandbox_writable_paths from global config only, ignoring project config", async () => { await using globalTmp = await tmpdir() await using tmp = await tmpdir({ git: true }) diff --git a/packages/opencode/test/kilocode/session-routed-model.test.ts b/packages/opencode/test/kilocode/session-routed-model.test.ts index de86c5af4bc..64981bad902 100644 --- a/packages/opencode/test/kilocode/session-routed-model.test.ts +++ b/packages/opencode/test/kilocode/session-routed-model.test.ts @@ -103,7 +103,7 @@ describe("session routed model", () => { expect(KiloRoutedModel.displayName("o3")).toBe("o3") }) - test("shows compact labels only for routed Kilo selections", () => { + test("shows compact labels only for Kilo auto selections", () => { const model = { providerID: "openai", modelID: "gpt-5.5" } const parts = [reason, finish(model)] @@ -115,13 +115,6 @@ describe("session routed model", () => { expect(routed.footer).toBe("gpt-5.5") expect(routed.consumed.has("finish")).toBe(true) - const fable = RoutedModelMeta.info(undefined, parts, false, { - providerID: "kilo", - modelID: "anthropic/claude-fable-5", - }) - expect(fable.labels.get("reasoning")).toBe("gpt-5.5") - expect(fable.footer).toBe("gpt-5.5") - const explicit = RoutedModelMeta.info(undefined, parts, false, { providerID: "openai", modelID: "gpt-5.5", @@ -176,7 +169,7 @@ describe("session routed model", () => { expect(routed.consumed.has("last")).toBe(false) }) - test("reads routed model only for selected Kilo routed models", () => { + test("reads routed model only for selected Kilo auto models", () => { const meta = { kilocode: { routedModelID: "openai/gpt-5.5-20260423" } } expect( @@ -189,22 +182,6 @@ describe("session routed model", () => { modelID: ModelID.make("openai/gpt-5.5-20260423"), }) - expect( - KiloRoutedModel.readAuto(meta, { - providerID: ProviderID.kilo, - modelID: "anthropic.claude-fable-5", - }), - ).toEqual({ - providerID: ProviderID.kilo, - modelID: ModelID.make("openai/gpt-5.5-20260423"), - }) - - expect( - KiloRoutedModel.readAuto(meta, { - providerID: ProviderID.kilo, - modelID: "openai/affable-model", - }), - ).toBeUndefined() expect( KiloRoutedModel.readAuto(meta, { providerID: ProviderID.kilo,