Merge branch 'main' into fix/cost-fallback-price-unit

This commit is contained in:
Christiaan Arnoldus
2026-03-25 11:28:48 +01:00
committed by GitHub
7 changed files with 106 additions and 5 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ export function createKilo(options: KiloProviderOptions = {}): KiloProvider {
const openrouter = createOpenRouter(sdkOptions)
const anthropic = createAnthropic(sdkOptions)
const openai = createOpenAI(sdkOptions)
const openaiCompatible = createOpenAICompatible({ ...sdkOptions, name: "kilo.openai-compatible" })
const openaiCompatible = createOpenAICompatible({ ...sdkOptions, name: "openaiCompatible" })
return {
languageModel(modelId) {
+2 -2
View File
@@ -2471,7 +2471,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
const uri = tab.input.uri
if (uri.scheme === "file") {
const rel = path.relative(dir, uri.fsPath)
if (!rel.startsWith("..") && controller.validateAccess(uri.fsPath)) {
if (!rel.startsWith("..") && !path.isAbsolute(rel) && controller.validateAccess(uri.fsPath)) {
result.add(rel.replaceAll("\\", "/"))
}
}
@@ -2505,7 +2505,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
return undefined
}
const relative = path.relative(workspaceDir, fsPath)
if (relative.startsWith("..")) {
if (relative.startsWith("..") || path.isAbsolute(relative)) {
return undefined
}
return relative
@@ -10,6 +10,11 @@ const GITIGNORE = ".gitignore"
*/
const SENSITIVE_PATTERNS = [".env", ".env.*"]
// Matches Windows drive-letter absolute paths (e.g. "C:/" or "c:\").
// path.isAbsolute() on POSIX does not recognise these, so we check explicitly
// to avoid passing them to the `ignore` package which throws a RangeError.
const WINDOWS_DRIVE = /^[a-zA-Z]:[/\\]/
function toPosix(filePath: string): string {
return filePath.replace(/\\/g, "/")
}
@@ -87,7 +92,7 @@ export class FileIgnoreController {
}
const relative = path.relative(this.workspacePath, resolved)
if (!relative || relative.startsWith("..")) {
if (!relative || relative.startsWith("..") || path.isAbsolute(relative) || WINDOWS_DRIVE.test(relative)) {
return null
}
@@ -2,8 +2,16 @@ import { afterEach, describe, expect, it } from "bun:test"
import os from "node:os"
import path from "node:path"
import fs from "node:fs/promises"
import ignore from "ignore"
import { FileIgnoreController } from "../../src/services/autocomplete/shims/FileIgnoreController"
// Activate Windows drive-letter detection in the `ignore` package.
// On actual Windows this runs automatically (process.platform === 'win32');
// here we enable it explicitly so the test reproduces the Windows-only
// RangeError on any platform.
const setup = (ignore as any)[Symbol.for("setupWindows")]
if (typeof setup === "function") setup()
const tempDirs: string[] = []
afterEach(async () => {
@@ -102,6 +110,55 @@ describe("FileIgnoreController", () => {
})
})
describe("Windows cross-drive paths", () => {
it("does not throw for a Windows-style absolute path from another drive", async () => {
const workspace = await createTempWorkspace()
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
const controller = new FileIgnoreController(workspace)
await controller.initialize()
// Simulates a VS Code tab open on a file from a different Windows drive.
// On Windows, path.relative("D:\\project", "C:\\Users\\file") returns
// "C:\\Users\\file" (absolute), which the `ignore` package rejects via
// RangeError: path should be a `path.relative()`d string.
//
// On macOS, path.resolve joins "c:/..." relative to the workspace,
// producing "c:/Users/..." as the relative portion — still detected as
// a Windows drive letter by ignore's setupWindows() regex.
const cross =
"c:/Users/User/AppData/Roaming/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json"
expect(() => controller.validateAccess(cross)).not.toThrow()
expect(controller.validateAccess(cross)).toBe(false)
})
it("does not throw for file:// URIs with Windows drive letters", async () => {
const workspace = await createTempWorkspace()
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
const controller = new FileIgnoreController(workspace)
await controller.initialize()
const uri =
"file:///c:/Users/User/AppData/Roaming/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json"
expect(() => controller.validateAccess(uri)).not.toThrow()
expect(controller.validateAccess(uri)).toBe(false)
})
it("still allows workspace files after cross-drive check", async () => {
const workspace = await createTempWorkspace()
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
const controller = new FileIgnoreController(workspace)
await controller.initialize()
expect(controller.validateAccess(path.join(workspace, "src", "main.ts"))).toBe(true)
expect(controller.validateAccess(path.join(workspace, "node_modules", "foo.js"))).toBe(false)
})
})
describe("when constructed with empty workspace path", () => {
it("denies all access", async () => {
const controller = new FileIgnoreController("")
+4 -1
View File
@@ -596,12 +596,15 @@ export namespace File {
const fullPath = path.join(resolved, entry.name)
const relativePath = path.relative(Instance.directory, fullPath)
const type = entry.isDirectory() ? "directory" : "file"
// On Windows, path.relative() across drives returns an absolute path;
// skip the gitignore check in that case to avoid a RangeError from `ignore`.
const canIgnore = !path.isAbsolute(relativePath)
nodes.push({
name: entry.name,
path: relativePath,
absolute: fullPath,
type,
ignored: ignored(type === "directory" ? relativePath + "/" : relativePath),
ignored: canIgnore && ignored(type === "directory" ? relativePath + "/" : relativePath),
})
}
return nodes.sort((a, b) => {
@@ -0,0 +1,29 @@
import type { AnthropicProviderOptions } from "@ai-sdk/anthropic"
import type { OpenAIResponsesProviderOptions } from "@ai-sdk/openai"
import type { OpenAICompatibleProviderOptions } from "@ai-sdk/openai-compatible"
import type { OpenRouterProviderOptions } from "@openrouter/ai-sdk-provider"
export function kiloProviderOptions(options: { [x: string]: any }) {
const result: Record<string, any> = {}
const openrouter = options as OpenRouterProviderOptions & {
verbosity?: "high" | "medium" | "low"
}
result.openrouter = openrouter
result.openai = {
reasoningEffort:
openrouter.reasoning && "effort" in openrouter.reasoning ? openrouter.reasoning?.effort : undefined,
textVerbosity: openrouter.verbosity,
store: false,
//forceReasoning: openrouter.reasoning?.enabled, // ai sdk v6
} satisfies OpenAIResponsesProviderOptions
result.anthropic = {
thinking: { type: openrouter.reasoning?.enabled ? "adaptive" : "disabled" },
effort: openrouter.verbosity,
} satisfies AnthropicProviderOptions
result.openaiCompatible = {
reasoningEffort:
openrouter.reasoning && "effort" in openrouter.reasoning ? openrouter.reasoning?.effort : undefined,
textVerbosity: openrouter.verbosity,
} satisfies OpenAICompatibleProviderOptions
return result
}
@@ -6,6 +6,7 @@ import type { Provider } from "./provider"
import type { ModelsDev } from "./models"
import { iife } from "@/util/iife"
import { Flag } from "@/flag/flag"
import { kiloProviderOptions } from "@/kilocode/provider-options"
type Modality = NonNullable<ModelsDev.Model["modalities"]>["input"][number]
@@ -936,6 +937,12 @@ export namespace ProviderTransform {
return result
}
// kilocode_change start
if (model.api.npm === "@kilocode/kilo-gateway") {
return kiloProviderOptions(options)
}
// kilocode_change end
const key = sdkKey(model.api.npm) ?? model.providerID
return { [key]: options }
}