From bddce1a49dd78c9673e971cdedd3665758378ac6 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 6 Aug 2026 12:28:30 +0200 Subject: [PATCH 01/39] fix: infer custom provider reasoning efforts --- .changeset/broad-custom-provider-efforts.md | 6 + .../kilo-vscode/src/shared/custom-provider.ts | 9 +- .../custom-provider-dialog-validate.test.ts | 28 ++ .../tests/unit/custom-provider.test.ts | 29 ++ .../tests/unit/provider-actions-save.test.ts | 21 ++ .../settings/CustomProviderDialog.tsx | 38 +-- .../settings/CustomProviderModelCard.tsx | 306 +----------------- .../settings/CustomProviderValidation.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/ar.ts | 38 --- .../kilo-vscode/webview-ui/src/i18n/br.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/bs.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/da.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/de.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/en.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/es.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/fa.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/fr.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/it.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/ja.ts | 38 --- .../kilo-vscode/webview-ui/src/i18n/ko.ts | 38 --- .../kilo-vscode/webview-ui/src/i18n/nl.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/no.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/pl.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/ru.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/th.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/tr.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/uk.ts | 39 --- .../kilo-vscode/webview-ui/src/i18n/zh.ts | 38 --- .../kilo-vscode/webview-ui/src/i18n/zht.ts | 38 --- .../src/kilocode/provider/provider.ts | 33 ++ packages/opencode/src/provider/provider.ts | 10 +- .../kilocode/custom-provider-variants.test.ts | 46 +++ .../provider-reasoning-options.test.ts | 53 +++ 33 files changed, 230 insertions(+), 1164 deletions(-) create mode 100644 .changeset/broad-custom-provider-efforts.md create mode 100644 packages/opencode/test/kilocode/custom-provider-variants.test.ts diff --git a/.changeset/broad-custom-provider-efforts.md b/.changeset/broad-custom-provider-efforts.md new file mode 100644 index 00000000000..2b275555aa1 --- /dev/null +++ b/.changeset/broad-custom-provider-efforts.md @@ -0,0 +1,6 @@ +--- +"@kilocode/cli": patch +"kilo-code": patch +--- + +Automatically expose broad reasoning effort options for custom provider models instead of requiring manual variant configuration. diff --git a/packages/kilo-vscode/src/shared/custom-provider.ts b/packages/kilo-vscode/src/shared/custom-provider.ts index 757897d1c16..3ac8ddb917b 100644 --- a/packages/kilo-vscode/src/shared/custom-provider.ts +++ b/packages/kilo-vscode/src/shared/custom-provider.ts @@ -12,14 +12,7 @@ export const EnvSchema = z .trim() .regex(/^[A-Z_][A-Z0-9_]*$/, INVALID_ENV) -const VariantConfigSchema = z.object({ - enable_thinking: z.boolean().optional(), - thinking: z.object({ type: z.enum(["enabled", "disabled", "adaptive"]) }).optional(), - reasoning_split: z.boolean().optional(), - reasoningEffort: z.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).optional(), - effort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(), - chat_template_args: z.object({ enable_thinking: z.boolean() }).optional(), -}) +const VariantConfigSchema = z.record(z.string(), z.unknown()) export type VariantConfig = z.infer diff --git a/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts b/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts index be1f69c0504..ff405481c06 100644 --- a/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts +++ b/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts @@ -206,6 +206,34 @@ describe("validateCustomProvider – variant name validation", () => { }) }) + it("preserves opaque variant options after the editor controls are removed", () => { + const form = base() + const raw = { + thinking: { type: "adaptive", display: "summarized" }, + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + customOption: { enabled: true }, + } + form.models[0].reasoning = true + form.models[0].variants = [ + { + name: "high", + raw, + enableThinking: undefined, + thinking: "adaptive", + splitReasoning: undefined, + outputEffort: undefined, + reasoningEffort: undefined, + chatTemplateArgs: undefined, + }, + ] + + const out = validateCustomProvider(args(form)) + expect(out.result).toBeDefined() + const saved = out.result!.config.models["model-1"] as Record + expect(saved.variants).toEqual({ high: raw }) + }) + it("serializes image modality when supportsImages is set", () => { const form = base() form.models[0].supportsImages = true diff --git a/packages/kilo-vscode/tests/unit/custom-provider.test.ts b/packages/kilo-vscode/tests/unit/custom-provider.test.ts index b4869706e43..30b420abb95 100644 --- a/packages/kilo-vscode/tests/unit/custom-provider.test.ts +++ b/packages/kilo-vscode/tests/unit/custom-provider.test.ts @@ -162,6 +162,35 @@ describe("sanitizeCustomProviderConfig", () => { }) }) + it("preserves opaque options on existing variants", () => { + const variant = { + thinking: { type: "adaptive", display: "summarized" }, + reasoningEffort: "max", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + customOption: { enabled: true }, + } + const result = sanitizeCustomProviderConfig({ + name: "Thinking Provider", + options: { baseURL: "https://example.com/v1" }, + models: { + "model-1": { + name: "Model One", + variants: { high: variant }, + }, + }, + }) + + expect(result).toEqual({ + value: { + npm: "@ai-sdk/openai-compatible", + name: "Thinking Provider", + options: { baseURL: "https://example.com/v1" }, + models: { "model-1": { name: "Model One", variants: { high: variant } } }, + }, + }) + }) + it("preserves core custom model modalities", () => { const result = sanitizeCustomProviderConfig({ name: "Media Provider", diff --git a/packages/kilo-vscode/tests/unit/provider-actions-save.test.ts b/packages/kilo-vscode/tests/unit/provider-actions-save.test.ts index a6499bab1c6..8f0e10b60a8 100644 --- a/packages/kilo-vscode/tests/unit/provider-actions-save.test.ts +++ b/packages/kilo-vscode/tests/unit/provider-actions-save.test.ts @@ -206,6 +206,27 @@ describe("saveCustomProvider", () => { expect(calls.set).toEqual([{ providerID: "myprovider", auth: { type: "api", key: "sk-test" } }]) }) + it("preserves opaque existing variant options through the save boundary", async () => { + const variant = { + thinking: { type: "adaptive", display: "summarized" }, + reasoningEffort: "custom", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + customOption: { enabled: true }, + } + const saved = { + ...createSavedProvider(), + models: { "model-1": { name: "Model One", reasoning: true, variants: { high: variant } } }, + } + const existing = { disabled_providers: [], provider: { myprovider: saved } } + const { ctx, calls, setCachedConfig } = createCtx(existing) + + await saveCustomProvider(ctx, "req", "myprovider", saved, undefined, false, null, setCachedConfig) + + const provider = (calls.config[0]?.config.provider as Record).myprovider + expect(provider.models["model-1"].variants.high).toEqual(variant) + }) + // Regression tests for https://github.com/Kilo-Org/kilocode/issues/9186 // // The CLI's config.update endpoint deep-merges its payload with the existing diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx index 1041664e41c..e81910eadf7 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx @@ -89,6 +89,7 @@ function modes(raw: unknown): Modalities { function parseVariant([name, cfg]: [string, Record]): VariantEntry { return { name, + raw: cfg, enableThinking: typeof cfg.enable_thinking === "boolean" ? cfg.enable_thinking : undefined, thinking: typeof cfg.thinking === "object" && cfg.thinking !== null @@ -460,25 +461,6 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => { setErrors("headers", (v) => v.filter((_, i) => i !== index)) } - function addVariant(mi: number) { - const blank: VariantEntry = { - name: "", - enableThinking: undefined, - thinking: undefined, - splitReasoning: undefined, - reasoningEffort: undefined, - outputEffort: undefined, - chatTemplateArgs: undefined, - } - setForm("models", mi, "variants", (v) => [...v, blank]) - setErrors("models", mi, "variants", (v) => [...(v ?? []), {}]) - } - - function removeVariant(mi: number, vi: number) { - setForm("models", mi, "variants", (v) => v.filter((_, i) => i !== vi)) - setErrors("models", mi, "variants", (v) => (v ?? []).filter((_, i) => i !== vi)) - } - function validate() { const output = validateCustomProvider({ form, @@ -673,7 +655,6 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => { {(m, i) => ( 1} @@ -682,23 +663,6 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => { onChangeReasoning={(v) => setForm("models", i(), "reasoning", v)} onChangeSupportsImages={(v) => setForm("models", i(), "supportsImages", v)} onRemove={() => removeModel(i())} - onAddVariant={() => addVariant(i())} - onRemoveVariant={(vi) => removeVariant(i(), vi)} - onChangeVariantName={(vi, val) => setForm("models", i(), "variants", vi, "name", val)} - onChangeVariantEnableThinking={(vi, val) => - setForm("models", i(), "variants", vi, "enableThinking", val) - } - onChangeVariantThinking={(vi, val) => setForm("models", i(), "variants", vi, "thinking", val)} - onChangeVariantSplitReasoning={(vi, val) => - setForm("models", i(), "variants", vi, "splitReasoning", val) - } - onChangeVariantReasoningEffort={(vi, val) => - setForm("models", i(), "variants", vi, "reasoningEffort", val) - } - onChangeVariantOutputEffort={(vi, val) => setForm("models", i(), "variants", vi, "outputEffort", val)} - onChangeVariantChatTemplateArgs={(vi, val) => - setForm("models", i(), "variants", vi, "chatTemplateArgs", val) - } /> )} diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx index d1b4ea13f3d..fa440816f31 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx @@ -1,8 +1,5 @@ -import { Button } from "@kilocode/kilo-ui/button" import { IconButton } from "@kilocode/kilo-ui/icon-button" -import { Select } from "@kilocode/kilo-ui/select" import { TextField } from "@kilocode/kilo-ui/text-field" -import { For, Show } from "solid-js" import { useLanguage } from "../../context/language" export type Translator = ReturnType["t"] @@ -23,6 +20,7 @@ export type Modalities = { export type VariantEntry = { name: string + raw?: Record enableThinking: EnableThinkingValue thinking: ThinkingTypeValue splitReasoning: SplitReasoningValue @@ -40,265 +38,9 @@ export type ModelEntry = { variants: VariantEntry[] } -type SelectOption = { value: T; labelKey: string } - -const ENABLE_THINKING_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: true, labelKey: "provider.custom.models.variants.enableThinking.true" }, - { value: false, labelKey: "provider.custom.models.variants.enableThinking.false" }, -] - -const THINKING_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: "enabled", labelKey: "provider.custom.models.variants.thinking.enabled" }, - { value: "disabled", labelKey: "provider.custom.models.variants.thinking.disabled" }, - { value: "adaptive", labelKey: "provider.custom.models.variants.thinking.adaptive" }, -] - -const SPLIT_REASONING_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: true, labelKey: "provider.custom.models.variants.splitReasoning.true" }, - { value: false, labelKey: "provider.custom.models.variants.splitReasoning.false" }, -] - -const CHAT_TEMPLATE_ARGS_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: true, labelKey: "provider.custom.models.variants.chatTemplateArgs.true" }, - { value: false, labelKey: "provider.custom.models.variants.chatTemplateArgs.false" }, -] - -const REASONING_EFFORT_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: "none", labelKey: "provider.custom.models.variants.reasoningEffort.none" }, - { value: "minimal", labelKey: "provider.custom.models.variants.reasoningEffort.minimal" }, - { value: "low", labelKey: "provider.custom.models.variants.reasoningEffort.low" }, - { value: "medium", labelKey: "provider.custom.models.variants.reasoningEffort.medium" }, - { value: "high", labelKey: "provider.custom.models.variants.reasoningEffort.high" }, - { value: "xhigh", labelKey: "provider.custom.models.variants.reasoningEffort.xhigh" }, -] - -const OUTPUT_EFFORT_OPTIONS: SelectOption[] = [ - { value: undefined, labelKey: "provider.custom.models.variants.option.unset" }, - { value: "low", labelKey: "provider.custom.models.variants.outputEffort.low" }, - { value: "medium", labelKey: "provider.custom.models.variants.outputEffort.medium" }, - { value: "high", labelKey: "provider.custom.models.variants.outputEffort.high" }, - { value: "xhigh", labelKey: "provider.custom.models.variants.outputEffort.xhigh" }, - { value: "max", labelKey: "provider.custom.models.variants.outputEffort.max" }, -] - -type VariantRowProps = { - v: VariantEntry - vi: () => number - isFirst: () => boolean - error: { name?: string } | undefined - t: Translator - onChangeName: (val: string) => void - onChangeEnableThinking: (val: EnableThinkingValue) => void - onChangeThinking: (val: ThinkingTypeValue) => void - onChangeSplitReasoning: (val: SplitReasoningValue) => void - onChangeReasoningEffort: (val: ReasoningEffortValue) => void - onChangeOutputEffort: (val: OutputEffortValue) => void - onChangeChatTemplateArgs: (val: ChatTemplateArgsValue) => void - onRemove: () => void -} - -function VariantRow(props: VariantRowProps) { - return ( -
- -
- -
-
- -
-
- - o.value === props.v.thinking)} - value={(o) => String(o.value)} - label={(o) => props.t(o.labelKey)} - onSelect={(o) => props.onChangeThinking(o?.value)} - placeholder={props.t("provider.custom.models.variants.thinking.placeholder")} - variant="secondary" - size="small" - triggerVariant="settings" - /> -
-
- - o.value === props.v.reasoningEffort)} - value={(o) => String(o.value)} - label={(o) => props.t(o.labelKey)} - onSelect={(o) => props.onChangeReasoningEffort(o?.value)} - placeholder={props.t("provider.custom.models.variants.reasoningEffort.placeholder")} - variant="secondary" - size="small" - triggerVariant="settings" - /> -
-
- - o.value === props.v.chatTemplateArgs)} - value={(o) => String(o.value)} - label={(o) => props.t(o.labelKey)} - onSelect={(o) => props.onChangeChatTemplateArgs(o?.value)} - placeholder={props.t("provider.custom.models.variants.chatTemplateArgs.placeholder")} - variant="secondary" - size="small" - triggerVariant="settings" - /> -
- -
-
- ) -} - type ModelCardProps = { m: ModelEntry - i: () => number - errors: { id?: string; name?: string; variants?: Array<{ name?: string }> } + errors: { id?: string; name?: string } t: Translator canRemove: boolean onChangeId: (val: string) => void @@ -306,15 +48,6 @@ type ModelCardProps = { onChangeReasoning: (val: boolean) => void onChangeSupportsImages: (val: boolean) => void onRemove: () => void - onAddVariant: () => void - onRemoveVariant: (vi: number) => void - onChangeVariantName: (vi: number, val: string) => void - onChangeVariantEnableThinking: (vi: number, val: EnableThinkingValue) => void - onChangeVariantThinking: (vi: number, val: ThinkingTypeValue) => void - onChangeVariantSplitReasoning: (vi: number, val: SplitReasoningValue) => void - onChangeVariantReasoningEffort: (vi: number, val: ReasoningEffortValue) => void - onChangeVariantOutputEffort: (vi: number, val: OutputEffortValue) => void - onChangeVariantChatTemplateArgs: (vi: number, val: ChatTemplateArgsValue) => void } export function ModelCard(props: ModelCardProps) { @@ -398,41 +131,6 @@ export function ModelCard(props: ModelCardProps) { /> {props.t("provider.custom.models.modalities.image")} - - {/* Variants — only available when reasoning is enabled */} - - 0}> -
- - - {(v, vi) => ( - vi() === 0} - error={props.errors.variants?.[vi()]} - t={props.t} - onChangeName={(val) => props.onChangeVariantName(vi(), val)} - onChangeEnableThinking={(val) => props.onChangeVariantEnableThinking(vi(), val)} - onChangeThinking={(val) => props.onChangeVariantThinking(vi(), val)} - onChangeSplitReasoning={(val) => props.onChangeVariantSplitReasoning(vi(), val)} - onChangeReasoningEffort={(val) => props.onChangeVariantReasoningEffort(vi(), val)} - onChangeOutputEffort={(val) => props.onChangeVariantOutputEffort(vi(), val)} - onChangeChatTemplateArgs={(val) => props.onChangeVariantChatTemplateArgs(vi(), val)} - onRemove={() => props.onRemoveVariant(vi())} - /> - )} - -
-
- -
) } diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts index 2331d295435..1032e6cf91d 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts @@ -105,6 +105,7 @@ function checkProviderID(id: string, editing: boolean, disabled: string[], exist } function serializeVariant(v: VariantEntry): [string, Record] { + if (v.raw) return [v.name.trim(), v.raw] const cfg: Record = {} if (v.enableThinking !== undefined) cfg.enable_thinking = v.enableThinking if (v.thinking !== undefined) cfg.thinking = { type: v.thinking } diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 6c425940a8f..be67beb1093 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -433,44 +433,6 @@ export const dict = { "provider.custom.models.name.placeholder": "الاسم المعروض", "provider.custom.models.reasoning.label": "الاستدلال", "provider.custom.models.modalities.image": "صورة", - "provider.custom.models.variants.label": "المتغيرات", - "provider.custom.models.variants.add": "إضافة متغير", - "provider.custom.models.variants.remove": "إزالة المتغير", - "provider.custom.models.variants.name.label": "الاسم", - "provider.custom.models.variants.name.placeholder": "على سبيل المثال: thinking", - "provider.custom.models.variants.option.unset": "(غير محدد)", - "provider.custom.models.variants.enableThinking.label": "تمكين التفكير (مثل Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "نوع التفكير (مثل Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": "تفعيل التفكير عبر وسائط قالب الدردشة (مثل Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "جهد الاستدلال", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "إزالة النموذج", "provider.custom.models.add": "إضافة نموذج", "provider.custom.models.fetch.authError": "فشلت المصادقة. تحقق من مفتاح API أعلاه وحاول مرة أخرى.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 1a7d9ae17ba..3a066b96b81 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -444,45 +444,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Nome de Exibição", "provider.custom.models.reasoning.label": "Raciocínio", "provider.custom.models.modalities.image": "Imagem", - "provider.custom.models.variants.label": "Variantes", - "provider.custom.models.variants.add": "Adicionar variante", - "provider.custom.models.variants.remove": "Remover variante", - "provider.custom.models.variants.name.label": "Nome", - "provider.custom.models.variants.name.placeholder": "ex. thinking", - "provider.custom.models.variants.option.unset": "(não definido)", - "provider.custom.models.variants.enableThinking.label": "Ativar pensamento (ex. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Tipo de pensamento (ex. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Ativar pensamento via args do template de chat (ex. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Esforço de raciocínio", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Remover modelo", "provider.custom.models.add": "Adicionar modelo", "provider.custom.models.fetch.authError": "Falha na autenticação. Verifique a chave de API acima e tente novamente.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index 509f0265201..e478af80f20 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -486,45 +486,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Naziv za prikaz", "provider.custom.models.reasoning.label": "Zaključivanje", "provider.custom.models.modalities.image": "Slika", - "provider.custom.models.variants.label": "Varijante", - "provider.custom.models.variants.add": "Dodaj varijantu", - "provider.custom.models.variants.remove": "Ukloni varijantu", - "provider.custom.models.variants.name.label": "Ime", - "provider.custom.models.variants.name.placeholder": "npr. thinking", - "provider.custom.models.variants.option.unset": "(nije postavljeno)", - "provider.custom.models.variants.enableThinking.label": "Omogući razmišljanje (npr. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Vrsta razmišljanja (npr. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Omogući razmišljanje preko argumenata chat predloška (npr. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Napor zaključivanja", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Ukloni model", "provider.custom.models.add": "Dodaj model", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index 4f1d6c9034c..7ef37266d21 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -484,45 +484,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Visningsnavn", "provider.custom.models.reasoning.label": "Ræsonnement", "provider.custom.models.modalities.image": "Billede", - "provider.custom.models.variants.label": "Varianter", - "provider.custom.models.variants.add": "Tilføj variant", - "provider.custom.models.variants.remove": "Fjern variant", - "provider.custom.models.variants.name.label": "Navn", - "provider.custom.models.variants.name.placeholder": "f.eks. thinking", - "provider.custom.models.variants.option.unset": "(ikke angivet)", - "provider.custom.models.variants.enableThinking.label": "Aktivér tænkning (f.eks. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Tænkningstype (f.eks. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Aktivér tænkning via chat-skabelonargs (f.eks. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Ræsonnementsindsats", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Fjern model", "provider.custom.models.add": "Tilføj model", "provider.custom.models.fetch.authError": "Godkendelse mislykkedes. Kontrollér API-nøglen ovenfor, og prøv igen.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 7c1366c7261..8eaedf2be46 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -494,45 +494,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Anzeigename", "provider.custom.models.reasoning.label": "Schlussfolgerung", "provider.custom.models.modalities.image": "Bild", - "provider.custom.models.variants.label": "Varianten", - "provider.custom.models.variants.add": "Variante hinzufügen", - "provider.custom.models.variants.remove": "Variante entfernen", - "provider.custom.models.variants.name.label": "Name", - "provider.custom.models.variants.name.placeholder": "z.B. thinking", - "provider.custom.models.variants.option.unset": "(nicht festgelegt)", - "provider.custom.models.variants.enableThinking.label": "Nachdenken aktivieren (z.B. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Art des Nachdenkens (z.B. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Nachdenken über Chat-Vorlagenargumente aktivieren (z.B. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Reasoning-Aufwand", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Modell entfernen", "provider.custom.models.add": "Modell hinzufügen", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 5752f6b2a8c..35ab3e7e9fb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -406,45 +406,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Display Name", "provider.custom.models.reasoning.label": "Reasoning", "provider.custom.models.modalities.image": "Image", - "provider.custom.models.variants.label": "Variants", - "provider.custom.models.variants.add": "Add variant", - "provider.custom.models.variants.remove": "Remove variant", - "provider.custom.models.variants.name.label": "Name", - "provider.custom.models.variants.name.placeholder": "e.g. thinking", - "provider.custom.models.variants.option.unset": "(not set)", - "provider.custom.models.variants.enableThinking.label": "Enable thinking (e.g. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Thinking type (e.g. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Enable thinking via chat template args (e.g. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Reasoning effort", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Remove model", "provider.custom.models.add": "Add model", "provider.custom.models.fetch.authError": "Authentication failed. Check the API key above and try again.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 2b7ed694b0b..db2015b6543 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -487,45 +487,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Nombre para mostrar", "provider.custom.models.reasoning.label": "Razonamiento", "provider.custom.models.modalities.image": "Imagen", - "provider.custom.models.variants.label": "Variantes", - "provider.custom.models.variants.add": "Añadir variante", - "provider.custom.models.variants.remove": "Eliminar variante", - "provider.custom.models.variants.name.label": "Nombre", - "provider.custom.models.variants.name.placeholder": "p. ej. thinking", - "provider.custom.models.variants.option.unset": "(no establecido)", - "provider.custom.models.variants.enableThinking.label": "Habilitar pensamiento (p. ej. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Tipo de pensamiento (p. ej. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Activar pensamiento mediante args de plantilla de chat (ej. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Esfuerzo de razonamiento", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Eliminar modelo", "provider.custom.models.add": "Añadir modelo", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts index 71b97a2d3ff..85eb22ea3ba 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts @@ -407,45 +407,6 @@ export const dict = { "provider.custom.models.name.placeholder": "نام نمایشی", "provider.custom.models.reasoning.label": "استدلال", "provider.custom.models.modalities.image": "تصویر", - "provider.custom.models.variants.label": "نسخه‌های متغیر", - "provider.custom.models.variants.add": "افزودن نسخه متغیر", - "provider.custom.models.variants.remove": "حذف نسخه متغیر", - "provider.custom.models.variants.name.label": "نام", - "provider.custom.models.variants.name.placeholder": "مثلاً thinking", - "provider.custom.models.variants.option.unset": "(تنظیم نشده)", - "provider.custom.models.variants.enableThinking.label": "فعال‌سازی تفکر (مثلاً Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "نوع تفکر (مثلاً Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "فعال", - "provider.custom.models.variants.thinking.disabled": "غیرفعال", - "provider.custom.models.variants.thinking.adaptive": "تطبیقی", - "provider.custom.models.variants.splitReasoning.label": "تقسیم استدلال (لازم برای مثلاً MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "فعال‌سازی تفکر از طریق آرگومان‌های قالب چت (مثلاً Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "سطح استدلال", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "هیچ", - "provider.custom.models.variants.reasoningEffort.minimal": "حداقل", - "provider.custom.models.variants.reasoningEffort.low": "کم", - "provider.custom.models.variants.reasoningEffort.medium": "متوسط", - "provider.custom.models.variants.reasoningEffort.high": "زیاد", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "تلاش خروجی (مثلاً Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "تلاش", - "provider.custom.models.variants.outputEffort.low": "کم", - "provider.custom.models.variants.outputEffort.medium": "متوسط", - "provider.custom.models.variants.outputEffort.high": "زیاد", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "حداکثر", "provider.custom.models.remove": "حذف مدل", "provider.custom.models.add": "افزودن مدل", "provider.custom.models.fetch.authError": "احراز هویت ناموفق بود. کلید API بالا را بررسی کرده و دوباره امتحان کنید.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 3fd9956d9de..cec3e454773 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -488,45 +488,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Nom d'affichage", "provider.custom.models.reasoning.label": "Raisonnement", "provider.custom.models.modalities.image": "Image", - "provider.custom.models.variants.label": "Variantes", - "provider.custom.models.variants.add": "Ajouter une variante", - "provider.custom.models.variants.remove": "Supprimer la variante", - "provider.custom.models.variants.name.label": "Nom", - "provider.custom.models.variants.name.placeholder": "ex. thinking", - "provider.custom.models.variants.option.unset": "(non défini)", - "provider.custom.models.variants.enableThinking.label": "Activer la réflexion (ex. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Type de réflexion (ex. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Activer la réflexion via les args du modèle de chat (ex. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Effort de raisonnement", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Supprimer le modèle", "provider.custom.models.add": "Ajouter un modèle", "provider.custom.models.fetch.authError": "Échec de l'authentification. Vérifiez la clé API ci-dessus et réessayez.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts index b8ec52156f4..4270684cd22 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts @@ -316,45 +316,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Nome visualizzato", "provider.custom.models.reasoning.label": "Reasoning", "provider.custom.models.modalities.image": "Immagine", - "provider.custom.models.variants.label": "Variants", - "provider.custom.models.variants.add": "Aggiungi variante", - "provider.custom.models.variants.remove": "Rimuovi variante", - "provider.custom.models.variants.name.label": "Nome", - "provider.custom.models.variants.name.placeholder": "es. thinking", - "provider.custom.models.variants.option.unset": "(non impostato)", - "provider.custom.models.variants.enableThinking.label": "Abilita thinking (es. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Tipo thinking (es. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Enable thinking via chat template args (e.g. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Sforzo di ragionamento", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Rimuovi modello", "provider.custom.models.add": "Aggiungi modello", "provider.custom.models.fetch.authError": "Autenticazione non riuscita. Controlla l'API key sopra e riprova.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index fa1eda6f6b0..fe0e5b26a80 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -480,44 +480,6 @@ export const dict = { "provider.custom.models.name.placeholder": "表示名", "provider.custom.models.reasoning.label": "推論", "provider.custom.models.modalities.image": "画像", - "provider.custom.models.variants.label": "バリアント", - "provider.custom.models.variants.add": "バリアントを追加", - "provider.custom.models.variants.remove": "バリアントを削除", - "provider.custom.models.variants.name.label": "名前", - "provider.custom.models.variants.name.placeholder": "例: thinking", - "provider.custom.models.variants.option.unset": "(未設定)", - "provider.custom.models.variants.enableThinking.label": "思考を有効にする (例: Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "思考タイプ (例: Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": "チャットテンプレート引数で思考を有効化 (例: Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "推論エフォート", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "モデルを削除", "provider.custom.models.add": "モデルを追加", "provider.custom.models.fetch.authError": "認証に失敗しました。上記のAPIキーを確認して再試行してください。", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 360ed69cb00..5d4a35c614e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -441,44 +441,6 @@ export const dict = { "provider.custom.models.name.placeholder": "표시 이름", "provider.custom.models.reasoning.label": "추론", "provider.custom.models.modalities.image": "이미지", - "provider.custom.models.variants.label": "변형", - "provider.custom.models.variants.add": "변형 추가", - "provider.custom.models.variants.remove": "변형 제거", - "provider.custom.models.variants.name.label": "이름", - "provider.custom.models.variants.name.placeholder": "예: thinking", - "provider.custom.models.variants.option.unset": "(설정되지 않음)", - "provider.custom.models.variants.enableThinking.label": "사고 활성화 (예: Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "사고 유형 (예: Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": "채팅 템플릿 인수로 사고 활성화 (예: Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "추론 노력", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "모델 제거", "provider.custom.models.add": "모델 추가", "provider.custom.models.fetch.authError": "인증에 실패했습니다. 위의 API 키를 확인하고 다시 시도하세요.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index 64f70685b4c..c3d0b3d28e0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -437,45 +437,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Weergavenaam", "provider.custom.models.reasoning.label": "Redeneren", "provider.custom.models.modalities.image": "Afbeelding", - "provider.custom.models.variants.label": "Varianten", - "provider.custom.models.variants.add": "Variant toevoegen", - "provider.custom.models.variants.remove": "Variant verwijderen", - "provider.custom.models.variants.name.label": "Naam", - "provider.custom.models.variants.name.placeholder": "bijv. thinking", - "provider.custom.models.variants.option.unset": "(niet ingesteld)", - "provider.custom.models.variants.enableThinking.label": "Denken inschakelen (bijv. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Denktype (bijv. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Nadenken inschakelen via chat template args (bijv. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Redeneerinspanning", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Model verwijderen", "provider.custom.models.add": "Model toevoegen", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 103de2edff6..27a1abb7719 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -447,45 +447,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Visningsnavn", "provider.custom.models.reasoning.label": "Resonnering", "provider.custom.models.modalities.image": "Bilde", - "provider.custom.models.variants.label": "Varianter", - "provider.custom.models.variants.add": "Legg til variant", - "provider.custom.models.variants.remove": "Fjern variant", - "provider.custom.models.variants.name.label": "Navn", - "provider.custom.models.variants.name.placeholder": "f.eks. thinking", - "provider.custom.models.variants.option.unset": "(ikke angitt)", - "provider.custom.models.variants.enableThinking.label": "Aktiver tenkning (f.eks. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Tenkningstype (f.eks. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Aktiver tenkning via chat-malargumenter (f.eks. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Resonneringsinnsats", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Fjern modell", "provider.custom.models.add": "Legg til modell", "provider.custom.models.fetch.authError": "Autentisering mislyktes. Sjekk API-nøkkelen ovenfor og prøv igjen.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 18830cde9be..d869a869989 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -442,45 +442,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Nazwa wyświetlana", "provider.custom.models.reasoning.label": "Rozumowanie", "provider.custom.models.modalities.image": "Obraz", - "provider.custom.models.variants.label": "Warianty", - "provider.custom.models.variants.add": "Dodaj wariant", - "provider.custom.models.variants.remove": "Usuń wariant", - "provider.custom.models.variants.name.label": "Nazwa", - "provider.custom.models.variants.name.placeholder": "np. thinking", - "provider.custom.models.variants.option.unset": "(nie ustawiono)", - "provider.custom.models.variants.enableThinking.label": "Włącz myślenie (np. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Typ myślenia (np. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Włącz myślenie przez argumenty szablonu czatu (np. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Wysiłek rozumowania", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Usuń model", "provider.custom.models.add": "Dodaj model", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 5eca69def5d..e0efa575acd 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -481,45 +481,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Отображаемое имя", "provider.custom.models.reasoning.label": "Рассуждение", "provider.custom.models.modalities.image": "Изображение", - "provider.custom.models.variants.label": "Варианты", - "provider.custom.models.variants.add": "Добавить вариант", - "provider.custom.models.variants.remove": "Удалить вариант", - "provider.custom.models.variants.name.label": "Имя", - "provider.custom.models.variants.name.placeholder": "напр. thinking", - "provider.custom.models.variants.option.unset": "(не задано)", - "provider.custom.models.variants.enableThinking.label": "Включить мышление (напр. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Тип мышления (напр. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Включить размышление через аргументы шаблона чата (напр. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Усилие рассуждения", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Удалить модель", "provider.custom.models.add": "Добавить модель", "provider.custom.models.fetch.authError": "Ошибка аутентификации. Проверьте API-ключ выше и попробуйте снова.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index c4dbc68bcf5..2393f6c3eed 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -478,45 +478,6 @@ export const dict = { "provider.custom.models.name.placeholder": "ชื่อที่แสดง", "provider.custom.models.reasoning.label": "การใช้เหตุผล", "provider.custom.models.modalities.image": "รูปภาพ", - "provider.custom.models.variants.label": "รูปแบบ", - "provider.custom.models.variants.add": "เพิ่มรูปแบบ", - "provider.custom.models.variants.remove": "ลบรูปแบบ", - "provider.custom.models.variants.name.label": "ชื่อ", - "provider.custom.models.variants.name.placeholder": "เช่น thinking", - "provider.custom.models.variants.option.unset": "(ไม่ได้ตั้งค่า)", - "provider.custom.models.variants.enableThinking.label": "เปิดใช้งานการคิด (เช่น Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "ประเภทการคิด (เช่น Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "เปิดใช้งานการคิดผ่านอาร์กิวเมนต์เทมเพลตแชท (เช่น Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "ระดับการใช้เหตุผล", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "ลบโมเดล", "provider.custom.models.add": "เพิ่มโมเดล", "provider.custom.models.fetch.authError": "การยืนยันตัวตนล้มเหลว ตรวจสอบคีย์ API ด้านบนแล้วลองอีกครั้ง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index 0761b27b94a..4f76d9c8778 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -432,45 +432,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Görünen Ad", "provider.custom.models.reasoning.label": "Akıl Yürütme", "provider.custom.models.modalities.image": "Görüntü", - "provider.custom.models.variants.label": "Varyantlar", - "provider.custom.models.variants.add": "Varyant ekle", - "provider.custom.models.variants.remove": "Varyantı kaldır", - "provider.custom.models.variants.name.label": "Ad", - "provider.custom.models.variants.name.placeholder": "örn. thinking", - "provider.custom.models.variants.option.unset": "(ayarlanmadı)", - "provider.custom.models.variants.enableThinking.label": "Düşünmeyi etkinleştir (örn. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Düşünme türü (örn. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Sohbet şablonu argümanları ile düşünmeyi etkinleştir (ör. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Akıl yürütme çabası", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Modeli kaldır", "provider.custom.models.add": "Model ekle", "provider.custom.models.fetch.authError": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index b4b8c529d24..9b47e1a3a24 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -436,45 +436,6 @@ export const dict = { "provider.custom.models.name.placeholder": "Відображувана назва", "provider.custom.models.reasoning.label": "Міркування", "provider.custom.models.modalities.image": "Зображення", - "provider.custom.models.variants.label": "Варіанти", - "provider.custom.models.variants.add": "Додати варіант", - "provider.custom.models.variants.remove": "Видалити варіант", - "provider.custom.models.variants.name.label": "Ім'я", - "provider.custom.models.variants.name.placeholder": "напр. thinking", - "provider.custom.models.variants.option.unset": "(не встановлено)", - "provider.custom.models.variants.enableThinking.label": "Увімкнути мислення (напр. Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "Тип мислення (напр. Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": - "Увімкнути мислення через аргументи шаблону чату (напр. Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "Зусилля міркування", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "Видалити модель", "provider.custom.models.add": "Додати модель", "provider.custom.models.fetch.authError": "Автентифікація не вдалася. Перевірте API-ключ вище і спробуйте ще раз.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index 041c72d116b..eadac1499ae 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -463,44 +463,6 @@ export const dict = { "provider.custom.models.name.placeholder": "显示名称", "provider.custom.models.reasoning.label": "推理", "provider.custom.models.modalities.image": "图片", - "provider.custom.models.variants.label": "变体", - "provider.custom.models.variants.add": "添加变体", - "provider.custom.models.variants.remove": "移除变体", - "provider.custom.models.variants.name.label": "名称", - "provider.custom.models.variants.name.placeholder": "例如 thinking", - "provider.custom.models.variants.option.unset": "(未设置)", - "provider.custom.models.variants.enableThinking.label": "启用思考 (例如 Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "思考类型 (例如 Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": "通过聊天模板参数启用思考(如 Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "推理强度", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "移除模型", "provider.custom.models.add": "添加模型", "provider.custom.models.fetch.authError": "认证失败。请检查上方的 API 密钥后重试。", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index 52ecca5fa9e..8cec3278a7a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -423,44 +423,6 @@ export const dict = { "provider.custom.models.name.placeholder": "顯示名稱", "provider.custom.models.reasoning.label": "推理", "provider.custom.models.modalities.image": "圖片", - "provider.custom.models.variants.label": "變體", - "provider.custom.models.variants.add": "新增變體", - "provider.custom.models.variants.remove": "移除變體", - "provider.custom.models.variants.name.label": "名稱", - "provider.custom.models.variants.name.placeholder": "例如 thinking", - "provider.custom.models.variants.option.unset": "(未設定)", - "provider.custom.models.variants.enableThinking.label": "啟用思考 (例如 Alibaba)", - "provider.custom.models.variants.enableThinking.placeholder": "enable_thinking", - "provider.custom.models.variants.enableThinking.true": "true", - "provider.custom.models.variants.enableThinking.false": "false", - "provider.custom.models.variants.thinking.label": "思考類型 (例如 Z.ai)", - "provider.custom.models.variants.thinking.placeholder": "thinking", - "provider.custom.models.variants.thinking.enabled": "enabled", - "provider.custom.models.variants.thinking.disabled": "disabled", - "provider.custom.models.variants.thinking.adaptive": "adaptive", - "provider.custom.models.variants.splitReasoning.label": "Split reasoning (required for e.g. MiniMax)", - "provider.custom.models.variants.splitReasoning.placeholder": "reasoning_split", - "provider.custom.models.variants.splitReasoning.true": "true", - "provider.custom.models.variants.splitReasoning.false": "false", - "provider.custom.models.variants.chatTemplateArgs.label": "透過聊天範本參數啟用思考(如 Hugging Face)", - "provider.custom.models.variants.chatTemplateArgs.placeholder": "chat_template_args", - "provider.custom.models.variants.chatTemplateArgs.true": "true", - "provider.custom.models.variants.chatTemplateArgs.false": "false", - "provider.custom.models.variants.reasoningEffort.label": "推理強度", - "provider.custom.models.variants.reasoningEffort.placeholder": "reasoningEffort", - "provider.custom.models.variants.reasoningEffort.none": "none", - "provider.custom.models.variants.reasoningEffort.minimal": "minimal", - "provider.custom.models.variants.reasoningEffort.low": "low", - "provider.custom.models.variants.reasoningEffort.medium": "medium", - "provider.custom.models.variants.reasoningEffort.high": "high", - "provider.custom.models.variants.reasoningEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.label": "Output effort (e.g. Anthropic)", - "provider.custom.models.variants.outputEffort.placeholder": "effort", - "provider.custom.models.variants.outputEffort.low": "low", - "provider.custom.models.variants.outputEffort.medium": "medium", - "provider.custom.models.variants.outputEffort.high": "high", - "provider.custom.models.variants.outputEffort.xhigh": "xhigh", - "provider.custom.models.variants.outputEffort.max": "max", "provider.custom.models.remove": "移除模型", "provider.custom.models.add": "新增模型", "provider.custom.models.fetch.authError": "驗證失敗。請檢查上方的 API 金鑰後重試。", diff --git a/packages/opencode/src/kilocode/provider/provider.ts b/packages/opencode/src/kilocode/provider/provider.ts index cc8a452fc96..13ec8bdddd2 100644 --- a/packages/opencode/src/kilocode/provider/provider.ts +++ b/packages/opencode/src/kilocode/provider/provider.ts @@ -15,6 +15,8 @@ import { ProviderError } from "@/provider/error" import { Effect, Schema } from "effect" import type { LanguageModelV3 } from "@ai-sdk/provider" import { mapValues, omit, pickBy } from "remeda" +import { reasoningSummary } from "./reasoning-summary" +import type { Provider } from "@/provider/provider" /** Default timeout (ms) for provider HTTP requests (connection phase). */ export const REQUEST_TIMEOUT_MS = 300_000 // 5 minutes @@ -95,6 +97,37 @@ export function patchConfigModel(cfg: any, existing: any) { } } +const CUSTOM_PROVIDER_PACKAGES = new Set(["@ai-sdk/openai-compatible", "@ai-sdk/openai", "@ai-sdk/anthropic"]) +const FALLBACK_EFFORTS = ["none", "low", "medium", "high", "xhigh", "max"] +type Variants = NonNullable +type Generate = (model: Provider.Model) => Variants + +export function customProviderVariants(model: Provider.Model, npm: unknown, generate: Generate): Variants { + const supported = typeof npm === "string" && CUSTOM_PROVIDER_PACKAGES.has(npm) && model.api.npm === npm + const variants = generate(supported ? { ...model, variants: {} } : model) + if (Object.keys(variants).length > 0) return variants + if (!model.capabilities.reasoning || !supported) return variants + + return Object.fromEntries( + FALLBACK_EFFORTS.map((effort) => { + if (npm === "@ai-sdk/anthropic") { + return [effort, effort === "none" ? { thinking: { type: "disabled" } } : { effort }] + } + if (npm === "@ai-sdk/openai") { + return [ + effort, + { + reasoningEffort: effort, + reasoningSummary: reasoningSummary(model), + include: ["reasoning.encrypted_content"], + }, + ] + } + return [effort, { reasoningEffort: effort }] + }), + ) +} + // --------------------------------------------------------------------------- // Custom loaders (new or fully-replaced loaders) // --------------------------------------------------------------------------- diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 897e8a6880f..e8f134f2871 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -37,6 +37,7 @@ import { KILO_MODEL_SCHEMA_EXTENSIONS, patchModelsDevModel as patchKiloModel, patchConfigModel as patchKiloConfigModel, + customProviderVariants, patchCustomLoaderResult, patchKiloProviderPrivacy, kiloSmallModelPriority, @@ -1526,7 +1527,14 @@ const layer = Layer.effect( // variants: {}, // kilocode_change, moved into patchKiloConfigModel ...patchKiloConfigModel(model, existingModel), // kilocode_change } - const merged = mergeDeep(ProviderTransform.variants(parsedModel), model.variants ?? {}) + // kilocode_change start + const generated = customProviderVariants( + parsedModel, + model.provider?.npm ?? provider.npm, + ProviderTransform.variants, + ) + const merged = mergeDeep(generated, model.variants ?? {}) + // kilocode_change end parsedModel.variants = mapValues( pickBy(merged, (v): v is NonNullable => !!v && !v.disabled), // kilocode_change - drop null delete sentinels (v) => omit(v, ["disabled"]), diff --git a/packages/opencode/test/kilocode/custom-provider-variants.test.ts b/packages/opencode/test/kilocode/custom-provider-variants.test.ts new file mode 100644 index 00000000000..e3a6e6be179 --- /dev/null +++ b/packages/opencode/test/kilocode/custom-provider-variants.test.ts @@ -0,0 +1,46 @@ +import { expect } from "bun:test" +import { LayerNode } from "@opencode-ai/core/effect/layer-node" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { Effect } from "effect" +import { Env } from "@/env" +import { Plugin } from "@/plugin" +import { Provider } from "@/provider/provider" +import { testEffect } from "../lib/effect" + +const it = testEffect(LayerNode.compile(LayerNode.group([Provider.node, Env.node, Plugin.node]))) + +it.instance( + "adds fallback efforts to configured reasoning models and merges explicit variants", + () => + Effect.gen(function* () { + const providers = yield* Provider.use.list() + const model = providers[ProviderV2.ID.make("custom")]?.models["qwen-custom"] + + expect(Object.keys(model?.variants ?? {})).toEqual(["none", "low", "medium", "xhigh", "max", "custom"]) + expect(model?.variants?.none).toEqual({ reasoningEffort: "none" }) + expect(model?.variants?.high).toBeUndefined() + expect(model?.variants?.custom).toEqual({ reasoningEffort: "custom" }) + }), + { + config: { + provider: { + custom: { + name: "Custom", + npm: "@ai-sdk/openai-compatible", + options: { apiKey: "test" }, + models: { + "qwen-custom": { + name: "Qwen Custom", + reasoning: true, + limit: { context: 128_000, output: 16_000 }, + variants: { + high: { disabled: true }, + custom: { reasoningEffort: "custom" }, + }, + }, + }, + }, + }, + }, + }, +) diff --git a/packages/opencode/test/kilocode/provider-reasoning-options.test.ts b/packages/opencode/test/kilocode/provider-reasoning-options.test.ts index 711820ede3b..268d27f4399 100644 --- a/packages/opencode/test/kilocode/provider-reasoning-options.test.ts +++ b/packages/opencode/test/kilocode/provider-reasoning-options.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test" import { ProviderTransform } from "../../src/provider/transform" import { Provider } from "../../src/provider/provider" +import { customProviderVariants } from "../../src/kilocode/provider/provider" import type * as ModelsDev from "@opencode-ai/core/models-dev" function mockModel(overrides: Partial = {}): any { @@ -138,3 +139,55 @@ describe("ProviderTransform.reasoningVariants - models.dev reasoning_options", ( expect(Object.keys(gpt5.variants ?? {})).toEqual(["minimal", "low", "medium", "high"]) }) }) + +describe("custom provider fallback reasoning efforts", () => { + const efforts = ["none", "low", "medium", "high", "xhigh", "max"] + + for (const npm of ["@ai-sdk/openai-compatible", "@ai-sdk/openai", "@ai-sdk/anthropic"]) { + test(`${npm} exposes broad efforts after heuristics fail`, () => { + const model = mockModel({ id: "qwen-custom", api: { id: "qwen-custom", url: "https://api.test.com", npm } }) + const generated = ProviderTransform.variants({ ...model, variants: {} }) + expect(generated).toEqual({}) + + const result = customProviderVariants(model, npm, ProviderTransform.variants) + + expect(Object.keys(result)).toEqual(efforts) + if (npm === "@ai-sdk/anthropic") { + expect(result.none).toEqual({ thinking: { type: "disabled" } }) + expect(result.max).toEqual({ effort: "max" }) + return + } + expect(result.none?.reasoningEffort).toBe("none") + expect(result.max?.reasoningEffort).toBe("max") + }) + } + + test("preserves successful heuristics", () => { + const model = mockModel({ api: { id: "custom", url: "https://api.test.com", npm: "@ai-sdk/openai-compatible" } }) + const generated = { low: { reasoningEffort: "low" }, high: { reasoningEffort: "high" } } + expect(customProviderVariants(model, model.api.npm, () => generated)).toBe(generated) + }) + + test("requires a reasoning model with an explicitly configured supported package", () => { + const npm = "@ai-sdk/openai-compatible" + const plain = mockModel({ + api: { id: "custom", url: "https://api.test.com", npm }, + capabilities: { ...mockModel().capabilities, reasoning: false }, + }) + expect(customProviderVariants(plain, npm, () => ({}))).toEqual({}) + expect( + customProviderVariants( + mockModel({ api: { id: "custom", url: "https://api.test.com", npm } }), + undefined, + () => ({}), + ), + ).toEqual({}) + expect( + customProviderVariants( + mockModel({ api: { id: "custom", url: "https://api.test.com", npm: "unrelated-provider" } }), + "unrelated-provider", + () => ({}), + ), + ).toEqual({}) + }) +}) From 71a1c6bc3d571aa464376117080680dcaf5588f2 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 6 Aug 2026 13:37:59 +0200 Subject: [PATCH 02/39] fix(vscode): surface custom variant validation --- .../settings/CustomProviderModelCard.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx index fa440816f31..a33984738c3 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderModelCard.tsx @@ -1,5 +1,6 @@ import { IconButton } from "@kilocode/kilo-ui/icon-button" import { TextField } from "@kilocode/kilo-ui/text-field" +import { Show } from "solid-js" import { useLanguage } from "../../context/language" export type Translator = ReturnType["t"] @@ -40,7 +41,7 @@ export type ModelEntry = { type ModelCardProps = { m: ModelEntry - errors: { id?: string; name?: string } + errors: { id?: string; name?: string; variants?: Array<{ name?: string }> } t: Translator canRemove: boolean onChangeId: (val: string) => void @@ -51,6 +52,8 @@ type ModelCardProps = { } export function ModelCard(props: ModelCardProps) { + const issue = () => props.errors.variants?.find((error) => error.name)?.name + return (
{props.t("provider.custom.models.modalities.image")} + + + {(error) => ( + + {error()} + + )} +
) } From 2dc24c1cfdd6271e253e1a7623639b043f33fd6f Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 6 Aug 2026 14:03:12 +0200 Subject: [PATCH 03/39] fix(vscode): identify invalid custom variants --- .../tests/unit/custom-provider-dialog-validate.test.ts | 6 +++--- .../src/components/settings/CustomProviderValidation.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts b/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts index ff405481c06..814b2147e0b 100644 --- a/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts +++ b/packages/kilo-vscode/tests/unit/custom-provider-dialog-validate.test.ts @@ -93,7 +93,7 @@ describe("validateCustomProvider – variant name validation", () => { ] const out = validateCustomProvider(args(form)) expect(out.result).toBeUndefined() - expect(out.errors.models[0].variants?.[0]?.name).toBe("provider.custom.error.required") + expect(out.errors.models[0].variants?.[0]?.name).toBe('variants[""]: provider.custom.error.required') }) it("blocks submit and reports error when reasoning is enabled with a whitespace-only variant name", () => { @@ -112,7 +112,7 @@ describe("validateCustomProvider – variant name validation", () => { ] const out = validateCustomProvider(args(form)) expect(out.result).toBeUndefined() - expect(out.errors.models[0].variants?.[0]?.name).toBe("provider.custom.error.required") + expect(out.errors.models[0].variants?.[0]?.name).toBe('variants[" "]: provider.custom.error.required') }) it("blocks submit and reports duplicate error for two variants with the same name", () => { @@ -140,7 +140,7 @@ describe("validateCustomProvider – variant name validation", () => { ] const out = validateCustomProvider(args(form)) expect(out.result).toBeUndefined() - expect(out.errors.models[0].variants?.[1]?.name).toBe("provider.custom.error.duplicate") + expect(out.errors.models[0].variants?.[1]?.name).toBe('variants["fast"]: provider.custom.error.duplicate') }) it("ignores variants entirely when reasoning is disabled, even if they have empty names", () => { diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts index 1032e6cf91d..2300e9fe910 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderValidation.ts @@ -57,8 +57,9 @@ const PROVIDER_ID = /^[a-z0-9][a-z0-9-_]*$/ function checkVariant(v: VariantEntry, seen: Set, t: Translator) { const n = v.name.trim() - if (!n) return { name: t("provider.custom.error.required") } - if (seen.has(n)) return { name: t("provider.custom.error.duplicate") } + const path = `variants[${JSON.stringify(v.name)}]` + if (!n) return { name: `${path}: ${t("provider.custom.error.required")}` } + if (seen.has(n)) return { name: `${path}: ${t("provider.custom.error.duplicate")}` } seen.add(n) return { name: undefined } } From 6efe6475991e54cb987ad408c87028dd9f08d583 Mon Sep 17 00:00:00 2001 From: Rakshith N Date: Thu, 6 Aug 2026 23:28:48 +0530 Subject: [PATCH 04/39] feat: avoid long skill paths/URLs tooltip to overflow --- .../src/components/settings/AgentBehaviourTab.tsx | 4 ++-- packages/kilo-vscode/webview-ui/src/styles/settings.css | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx index 3a39b7da5dc..e97b1cff6df 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx @@ -903,7 +903,7 @@ const AgentBehaviourTab: Component = () => { "border-bottom": index() < skillPaths().length - 1 ? "1px solid var(--border-weak-base)" : "none", }} > - + { "border-bottom": index() < skillUrls().length - 1 ? "1px solid var(--border-weak-base)" : "none", }} > - + Date: Fri, 7 Aug 2026 00:31:01 +0530 Subject: [PATCH 05/39] test: add viewport bound checks --- .../tests/skills-settings-responsive.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts index 5665ea93a3a..6403880ced9 100644 --- a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts +++ b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts @@ -73,6 +73,12 @@ test.describe("skills settings responsive layout", () => { await trigger.hover() const content = page.locator('[data-component="tooltip"]').filter({ hasText: seeded }) await expect(content, `Kilo Tooltip exposes full path on hover: ${seeded}`).toBeVisible() + const tipBox = await content.boundingBox() + const viewport = page.viewportSize()! + expect(tipBox, "path tooltip bounding box").not.toBeNull() + expect(tipBox!.x + tipBox!.width, `path tooltip stays inside viewport: ${seeded}`).toBeLessThanOrEqual( + viewport.width + 1, + ) } for (const seeded of [SEEDED_URL, SEEDED_URL_2]) { @@ -99,6 +105,12 @@ test.describe("skills settings responsive layout", () => { await trigger.hover() const content = page.locator('[data-component="tooltip"]').filter({ hasText: seeded }) await expect(content, `Kilo Tooltip exposes full URL on hover: ${seeded}`).toBeVisible() + const tipBox = await content.boundingBox() + const viewport = page.viewportSize()! + expect(tipBox, "URL tooltip bounding box").not.toBeNull() + expect(tipBox!.x + tipBox!.width, `URL tooltip stays inside viewport: ${seeded}`).toBeLessThanOrEqual( + viewport.width + 1, + ) } for (const [label, card] of [ From e2db06453915fe25a42122a64348285aee2c8f60 Mon Sep 17 00:00:00 2001 From: Rakshith N Date: Fri, 7 Aug 2026 11:24:43 +0530 Subject: [PATCH 06/39] (chore): add user readable changeset --- .changeset/vscode-skills-tooltip-wrap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/vscode-skills-tooltip-wrap.md diff --git a/.changeset/vscode-skills-tooltip-wrap.md b/.changeset/vscode-skills-tooltip-wrap.md new file mode 100644 index 00000000000..a4a5ecec038 --- /dev/null +++ b/.changeset/vscode-skills-tooltip-wrap.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Long skill folder paths and URLs shown in the tooltip on the Skills settings page now wrap inside the viewport instead of overflowing on a single line. From d70ebd319174b795412d1afcec97b32113801fc0 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Fri, 7 Aug 2026 09:47:38 +0200 Subject: [PATCH 07/39] fix(cli): prefer configured custom variants --- packages/opencode/src/kilocode/provider/provider.ts | 4 +++- packages/opencode/src/provider/provider.ts | 8 +++----- .../test/kilocode/custom-provider-variants.test.ts | 5 ++--- .../test/kilocode/provider-reasoning-options.test.ts | 12 ++++++++++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/kilocode/provider/provider.ts b/packages/opencode/src/kilocode/provider/provider.ts index 13ec8bdddd2..21345881ee9 100644 --- a/packages/opencode/src/kilocode/provider/provider.ts +++ b/packages/opencode/src/kilocode/provider/provider.ts @@ -103,8 +103,10 @@ type Variants = NonNullable type Generate = (model: Provider.Model) => Variants export function customProviderVariants(model: Provider.Model, npm: unknown, generate: Generate): Variants { + if (model.variants && Object.keys(model.variants).length > 0) return model.variants + const supported = typeof npm === "string" && CUSTOM_PROVIDER_PACKAGES.has(npm) && model.api.npm === npm - const variants = generate(supported ? { ...model, variants: {} } : model) + const variants = generate(model) if (Object.keys(variants).length > 0) return variants if (!model.capabilities.reasoning || !supported) return variants diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index e8f134f2871..c830ad8bb78 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1528,11 +1528,9 @@ const layer = Layer.effect( ...patchKiloConfigModel(model, existingModel), // kilocode_change } // kilocode_change start - const generated = customProviderVariants( - parsedModel, - model.provider?.npm ?? provider.npm, - ProviderTransform.variants, - ) + const generated = Object.keys(model.variants ?? {}).length + ? {} + : customProviderVariants(parsedModel, model.provider?.npm ?? provider.npm, ProviderTransform.variants) const merged = mergeDeep(generated, model.variants ?? {}) // kilocode_change end parsedModel.variants = mapValues( diff --git a/packages/opencode/test/kilocode/custom-provider-variants.test.ts b/packages/opencode/test/kilocode/custom-provider-variants.test.ts index e3a6e6be179..7de3036fd7a 100644 --- a/packages/opencode/test/kilocode/custom-provider-variants.test.ts +++ b/packages/opencode/test/kilocode/custom-provider-variants.test.ts @@ -10,14 +10,13 @@ import { testEffect } from "../lib/effect" const it = testEffect(LayerNode.compile(LayerNode.group([Provider.node, Env.node, Plugin.node]))) it.instance( - "adds fallback efforts to configured reasoning models and merges explicit variants", + "uses configured variants instead of inferred reasoning efforts", () => Effect.gen(function* () { const providers = yield* Provider.use.list() const model = providers[ProviderV2.ID.make("custom")]?.models["qwen-custom"] - expect(Object.keys(model?.variants ?? {})).toEqual(["none", "low", "medium", "xhigh", "max", "custom"]) - expect(model?.variants?.none).toEqual({ reasoningEffort: "none" }) + expect(Object.keys(model?.variants ?? {})).toEqual(["custom"]) expect(model?.variants?.high).toBeUndefined() expect(model?.variants?.custom).toEqual({ reasoningEffort: "custom" }) }), diff --git a/packages/opencode/test/kilocode/provider-reasoning-options.test.ts b/packages/opencode/test/kilocode/provider-reasoning-options.test.ts index 268d27f4399..5987819421d 100644 --- a/packages/opencode/test/kilocode/provider-reasoning-options.test.ts +++ b/packages/opencode/test/kilocode/provider-reasoning-options.test.ts @@ -168,6 +168,18 @@ describe("custom provider fallback reasoning efforts", () => { expect(customProviderVariants(model, model.api.npm, () => generated)).toBe(generated) }) + test("prefers configured variants to inference", () => { + const variants = { custom: { reasoningEffort: "custom" } } + for (const npm of ["@ai-sdk/openai-compatible", "@ai-sdk/openai", "@ai-sdk/anthropic"]) { + const model = mockModel({ api: { id: "custom", url: "https://api.test.com", npm }, variants }) + expect( + customProviderVariants(model, npm, () => { + throw new Error("inference should not run") + }), + ).toBe(variants) + } + }) + test("requires a reasoning model with an explicitly configured supported package", () => { const npm = "@ai-sdk/openai-compatible" const plain = mockModel({ From ed63f985d92f36f7e196d42136103875b71339c0 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Fri, 7 Aug 2026 09:58:35 +0200 Subject: [PATCH 08/39] feat(vscode): link custom providers to config --- .changeset/broad-custom-provider-efforts.md | 2 +- .../tests/unit/open-config-message.test.ts | 14 ++++++++ .../settings/CustomProviderDialog.tsx | 14 ++++++++ .../src/components/settings/Settings.tsx | 30 ++--------------- .../kilo-vscode/webview-ui/src/i18n/ar.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/br.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/bs.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/da.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/de.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/en.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/es.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/fa.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/fr.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/it.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/ja.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/ko.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/nl.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/no.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/pl.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/ru.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/th.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/tr.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/uk.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/zh.ts | 1 + .../kilo-vscode/webview-ui/src/i18n/zht.ts | 1 + .../webview-ui/src/utils/open-config.ts | 32 +++++++++++++++++++ 26 files changed, 84 insertions(+), 29 deletions(-) create mode 100644 packages/kilo-vscode/tests/unit/open-config-message.test.ts create mode 100644 packages/kilo-vscode/webview-ui/src/utils/open-config.ts diff --git a/.changeset/broad-custom-provider-efforts.md b/.changeset/broad-custom-provider-efforts.md index 2b275555aa1..c0746956176 100644 --- a/.changeset/broad-custom-provider-efforts.md +++ b/.changeset/broad-custom-provider-efforts.md @@ -3,4 +3,4 @@ "kilo-code": patch --- -Automatically expose broad reasoning effort options for custom provider models instead of requiring manual variant configuration. +Automatically expose broad reasoning effort options for custom provider models and link saved providers to advanced JSON configuration. diff --git a/packages/kilo-vscode/tests/unit/open-config-message.test.ts b/packages/kilo-vscode/tests/unit/open-config-message.test.ts new file mode 100644 index 00000000000..f16f0ee458d --- /dev/null +++ b/packages/kilo-vscode/tests/unit/open-config-message.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from "bun:test" +import { configMessage } from "../../webview-ui/src/utils/open-config" + +describe("configMessage", () => { + it("builds a global config request with localized labels", () => { + const message = configMessage("global", (key, params) => `${key}:${params?.scope ?? ""}`) + + expect(message.type).toBe("openConfigFile") + expect(message.scope).toBe("global") + expect(message.labels.scope).toBe("settings.config.scope.global:") + expect(message.labels.title).toBe("settings.config.title:settings.config.scope.global:") + expect(message.labels.openFailed).toBe("settings.config.openFailed:settings.config.scope.global:") + }) +}) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx index e81910eadf7..c548b06c76d 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CustomProviderDialog.tsx @@ -15,6 +15,7 @@ import { useProvider } from "../../context/provider" import { useVSCode } from "../../context/vscode" import type { ExtensionMessage, ProviderAuthState, ProviderConfig } from "../../types/messages" import { createProviderAction } from "../../utils/provider-action" +import { configMessage } from "../../utils/open-config" import { MASKED_CUSTOM_PROVIDER_KEY, resolveCustomProviderKey } from "../../../../src/shared/custom-provider" import { CUSTOM_PROVIDER_PACKAGE, @@ -563,6 +564,19 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => { {language.t("provider.custom.description.link")} {language.t("provider.custom.description.suffix")} + + +
diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx index 8bd39f14a44..ab8a5e99fba 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx @@ -28,6 +28,7 @@ import SandboxingTab from "./SandboxingTab" import * as Sandboxing from "./sandboxing" import { useServer } from "../../context/server" import type { MigrationSource } from "../../types/messages" +import { configMessage } from "../../utils/open-config" export interface SettingsProps { tab?: string @@ -66,34 +67,7 @@ const Settings: Component = (props) => { } const open = (scope: "local" | "global") => { - const label = - scope === "global" ? language.t("settings.config.scope.global") : language.t("settings.config.scope.local") - vscode.postMessage({ - type: "openConfigFile", - scope, - labels: { - scope: label, - statusLoaded: language.t("settings.config.status.loaded"), - statusLoadedLegacy: language.t("settings.config.status.loadedLegacy"), - statusNotLoaded: language.t("settings.config.status.notLoaded"), - statusCreate: language.t("settings.config.status.create"), - title: language.t("settings.config.title", { scope: label }), - placeholder: language.t("settings.config.placeholder"), - noWorkspace: language.t("settings.config.noWorkspace"), - openFailed: language.t("settings.config.openFailed", { scope: label, message: "{{message}}" }), - sourceXdg: language.t("settings.config.source.xdg"), - sourceHomeKilo: language.t("settings.config.source.homeKilo"), - sourceHomeKilocode: language.t("settings.config.source.homeKilocode"), - sourceHomeOpencode: language.t("settings.config.source.homeOpencode"), - sourceEnvFile: language.t("settings.config.source.envFile"), - sourceEnvDir: language.t("settings.config.source.envDir"), - sourceEnvContent: language.t("settings.config.source.envContent"), - sourceProjectKilo: language.t("settings.config.source.projectKilo"), - sourceProjectRoot: language.t("settings.config.source.projectRoot"), - sourceProjectKilocode: language.t("settings.config.source.projectKilocode"), - sourceProjectOpencode: language.t("settings.config.source.projectOpencode"), - }, - }) + vscode.postMessage(configMessage(scope, language.t)) } // Sync when the parent changes the tab prop (e.g. via navigate message) diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 71ac3559bce..42dde00f517 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -455,6 +455,7 @@ export const dict = { "provider.custom.models.fetch.search": "البحث في النماذج\u2026", "provider.custom.models.fetch.add": "إضافة {{count}} نموذج(نماذج)", "provider.custom.edit.title": "تعديل المزود", + "provider.custom.edit.advanced": "تحرير الإعدادات المتقدمة في ملف إعداد JSON", "provider.custom.headers.label": "الرؤوس (اختياري)", "provider.custom.headers.key.label": "الرأس", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 589a8b38714..42334d5c232 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -466,6 +466,7 @@ export const dict = { "provider.custom.models.fetch.search": "Pesquisar modelos\u2026", "provider.custom.models.fetch.add": "Adicionar {{count}} modelo(s)", "provider.custom.edit.title": "Editar provedor", + "provider.custom.edit.advanced": "Editar configurações avançadas no arquivo de configuração JSON", "provider.custom.headers.label": "Headers (opcional)", "provider.custom.headers.key.label": "Cabeçalho", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index d737c969906..a8f4e547c09 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -509,6 +509,7 @@ export const dict = { "provider.custom.models.fetch.search": "Pretraži modele\u2026", "provider.custom.models.fetch.add": "Dodaj {{count}} model(a)", "provider.custom.edit.title": "Uredi provajdera", + "provider.custom.edit.advanced": "Uredite napredne postavke u JSON konfiguracijskoj datoteci", "provider.custom.headers.label": "Zaglavlja (opcionalno)", "provider.custom.headers.key.label": "Zaglavlje", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index bf71f652cf9..66d4104056d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -506,6 +506,7 @@ export const dict = { "provider.custom.models.fetch.search": "Søg modeller\u2026", "provider.custom.models.fetch.add": "Tilføj {{count}} model(ler)", "provider.custom.edit.title": "Rediger udbyder", + "provider.custom.edit.advanced": "Rediger avancerede indstillinger i JSON-konfigurationsfilen", "provider.custom.headers.label": "Headers (valgfrit)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 12d30679fac..221b11d3e14 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -517,6 +517,7 @@ export const dict = { "provider.custom.models.fetch.search": "Modelle suchen\u2026", "provider.custom.models.fetch.add": "{{count}} Modell(e) hinzufügen", "provider.custom.edit.title": "Anbieter bearbeiten", + "provider.custom.edit.advanced": "Erweiterte Einstellungen in der JSON-Konfigurationsdatei bearbeiten", "provider.custom.headers.label": "Header (optional)", "provider.custom.headers.key.label": "Kopfzeile", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index c5f19bdb6b0..cbcd4ea4223 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -420,6 +420,7 @@ export const dict = { "provider.custom.models.fetch.search": "Search models\u2026", "provider.custom.models.fetch.add": "Add {{count}} model(s)", "provider.custom.edit.title": "Edit provider", + "provider.custom.edit.advanced": "Edit advanced settings in the JSON config file", "provider.custom.headers.label": "Headers (optional)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index bee7bb09d13..929494b6b87 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -510,6 +510,7 @@ export const dict = { "provider.custom.models.fetch.search": "Buscar modelos\u2026", "provider.custom.models.fetch.add": "Añadir {{count}} modelo(s)", "provider.custom.edit.title": "Editar proveedor", + "provider.custom.edit.advanced": "Editar la configuración avanzada en el archivo de configuración JSON", "provider.custom.headers.label": "Headers (opcional)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts index 1d18abd83dc..b18b51b7ae1 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts @@ -421,6 +421,7 @@ export const dict = { "provider.custom.models.fetch.search": "جستجوی مدل‌ها…", "provider.custom.models.fetch.add": "افزودن {{count}} مدل", "provider.custom.edit.title": "ویرایش ارائه‌دهنده", + "provider.custom.edit.advanced": "ویرایش تنظیمات پیشرفته در فایل پیکربندی JSON", "provider.custom.headers.label": "هدرها (اختیاری)", "provider.custom.headers.key.label": "هدر", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index dcf4a43dc37..f63a277f928 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -510,6 +510,7 @@ export const dict = { "provider.custom.models.fetch.search": "Rechercher des modèles\u2026", "provider.custom.models.fetch.add": "Ajouter {{count}} modèle(s)", "provider.custom.edit.title": "Modifier le fournisseur", + "provider.custom.edit.advanced": "Modifier les paramètres avancés dans le fichier de configuration JSON", "provider.custom.headers.label": "En-têtes (optionnel)", "provider.custom.headers.key.label": "En-tête", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts index 84a1a7f4be0..01fa7e835ff 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts @@ -330,6 +330,7 @@ export const dict = { "provider.custom.models.fetch.search": "Cerca modelli...", "provider.custom.models.fetch.add": "Aggiungi {{count}} modelli", "provider.custom.edit.title": "Modifica provider", + "provider.custom.edit.advanced": "Modifica le impostazioni avanzate nel file di configurazione JSON", "provider.custom.headers.label": "Header (opzionali)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Nome-Header", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 96f9f032d6d..dffecedbd80 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -502,6 +502,7 @@ export const dict = { "provider.custom.models.fetch.search": "モデルを検索\u2026", "provider.custom.models.fetch.add": "{{count}}個のモデルを追加", "provider.custom.edit.title": "プロバイダーを編集", + "provider.custom.edit.advanced": "JSON 設定ファイルで詳細設定を編集", "provider.custom.headers.label": "ヘッダー(任意)", "provider.custom.headers.key.label": "ヘッダー", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 69cb64653ff..fac9f03cea6 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -463,6 +463,7 @@ export const dict = { "provider.custom.models.fetch.search": "모델 검색\u2026", "provider.custom.models.fetch.add": "{{count}}개 모델 추가", "provider.custom.edit.title": "공급자 편집", + "provider.custom.edit.advanced": "JSON 구성 파일에서 고급 설정 편집", "provider.custom.headers.label": "헤더 (선택사항)", "provider.custom.headers.key.label": "헤더", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index ae0a6b22bfe..c1485ff655b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -460,6 +460,7 @@ export const dict = { "provider.custom.models.fetch.search": "Modellen zoeken\u2026", "provider.custom.models.fetch.add": "{{count}} model(len) toevoegen", "provider.custom.edit.title": "Provider bewerken", + "provider.custom.edit.advanced": "Geavanceerde instellingen bewerken in het JSON-configuratiebestand", "provider.custom.headers.label": "Headers (optioneel)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Header-Naam", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 6cb0b4bb955..dd6da3fe63e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -469,6 +469,7 @@ export const dict = { "provider.custom.models.fetch.search": "Søk etter modeller\u2026", "provider.custom.models.fetch.add": "Legg til {{count}} modell(er)", "provider.custom.edit.title": "Rediger leverandør", + "provider.custom.edit.advanced": "Rediger avanserte innstillinger i JSON-konfigurasjonsfilen", "provider.custom.headers.label": "Headere (valgfritt)", "provider.custom.headers.key.label": "Header", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 8e6744d6aa8..2114eb158f6 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -465,6 +465,7 @@ export const dict = { "provider.custom.models.fetch.search": "Szukaj modeli\u2026", "provider.custom.models.fetch.add": "Dodaj {{count}} model(i)", "provider.custom.edit.title": "Edytuj dostawcę", + "provider.custom.edit.advanced": "Edytuj ustawienia zaawansowane w pliku konfiguracji JSON", "provider.custom.headers.label": "Nagłówki (opcjonalnie)", "provider.custom.headers.key.label": "Nagłówek", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 91b22b70430..4e20af74782 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -503,6 +503,7 @@ export const dict = { "provider.custom.models.fetch.search": "Поиск моделей\u2026", "provider.custom.models.fetch.add": "Добавить {{count}} модель(ей)", "provider.custom.edit.title": "Редактировать провайдера", + "provider.custom.edit.advanced": "Изменить расширенные настройки в файле конфигурации JSON", "provider.custom.headers.label": "Заголовки (необязательно)", "provider.custom.headers.key.label": "Заголовок", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 764eef584ee..66924828bcb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -500,6 +500,7 @@ export const dict = { "provider.custom.models.fetch.search": "ค้นหาโมเดล\u2026", "provider.custom.models.fetch.add": "เพิ่ม {{count}} โมเดล", "provider.custom.edit.title": "แก้ไขผู้ให้บริการ", + "provider.custom.edit.advanced": "แก้ไขการตั้งค่าขั้นสูงในไฟล์การกำหนดค่า JSON", "provider.custom.headers.label": "ส่วนหัว (ไม่จำเป็น)", "provider.custom.headers.key.label": "ส่วนหัว", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index 9aa92a001be..8ee41b0e18f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -455,6 +455,7 @@ export const dict = { "provider.custom.models.fetch.search": "Model ara\u2026", "provider.custom.models.fetch.add": "{{count}} model ekle", "provider.custom.edit.title": "Sağlayıcıyı düzenle", + "provider.custom.edit.advanced": "JSON yapılandırma dosyasında gelişmiş ayarları düzenle", "provider.custom.headers.label": "Başlıklar (isteğe bağlı)", "provider.custom.headers.key.label": "Başlık", "provider.custom.headers.key.placeholder": "Başlık-Adı", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index 9acb616a6e4..12944f68a4b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -458,6 +458,7 @@ export const dict = { "provider.custom.models.fetch.search": "Пошук моделей\u2026", "provider.custom.models.fetch.add": "Додати {{count}} моделей", "provider.custom.edit.title": "Редагувати провайдера", + "provider.custom.edit.advanced": "Редагувати розширені налаштування у файлі конфігурації JSON", "provider.custom.headers.label": "Заголовки (необов'язково)", "provider.custom.headers.key.label": "Заголовок", "provider.custom.headers.key.placeholder": "Назва-заголовка", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index 1d48e7777b4..78d21c59126 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -485,6 +485,7 @@ export const dict = { "provider.custom.models.fetch.search": "搜索模型\u2026", "provider.custom.models.fetch.add": "添加 {{count}} 个模型", "provider.custom.edit.title": "编辑提供商", + "provider.custom.edit.advanced": "在 JSON 配置文件中编辑高级设置", "provider.custom.headers.label": "请求头(可选)", "provider.custom.headers.key.label": "请求头", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index 97cdf54edf0..1414c7f47d5 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -445,6 +445,7 @@ export const dict = { "provider.custom.models.fetch.search": "搜尋模型\u2026", "provider.custom.models.fetch.add": "新增 {{count}} 個模型", "provider.custom.edit.title": "編輯供應商", + "provider.custom.edit.advanced": "在 JSON 設定檔中編輯進階設定", "provider.custom.headers.label": "標頭(選填)", "provider.custom.headers.key.label": "標頭", "provider.custom.headers.key.placeholder": "Header-Name", diff --git a/packages/kilo-vscode/webview-ui/src/utils/open-config.ts b/packages/kilo-vscode/webview-ui/src/utils/open-config.ts new file mode 100644 index 00000000000..9ae1ed38393 --- /dev/null +++ b/packages/kilo-vscode/webview-ui/src/utils/open-config.ts @@ -0,0 +1,32 @@ +import type { LanguageContextValue } from "../context/language" +import type { OpenConfigFileRequest } from "../types/messages" + +export function configMessage(scope: "local" | "global", t: LanguageContextValue["t"]): OpenConfigFileRequest { + const label = t(scope === "global" ? "settings.config.scope.global" : "settings.config.scope.local") + return { + type: "openConfigFile", + scope, + labels: { + scope: label, + statusLoaded: t("settings.config.status.loaded"), + statusLoadedLegacy: t("settings.config.status.loadedLegacy"), + statusNotLoaded: t("settings.config.status.notLoaded"), + statusCreate: t("settings.config.status.create"), + title: t("settings.config.title", { scope: label }), + placeholder: t("settings.config.placeholder"), + noWorkspace: t("settings.config.noWorkspace"), + openFailed: t("settings.config.openFailed", { scope: label, message: "{{message}}" }), + sourceXdg: t("settings.config.source.xdg"), + sourceHomeKilo: t("settings.config.source.homeKilo"), + sourceHomeKilocode: t("settings.config.source.homeKilocode"), + sourceHomeOpencode: t("settings.config.source.homeOpencode"), + sourceEnvFile: t("settings.config.source.envFile"), + sourceEnvDir: t("settings.config.source.envDir"), + sourceEnvContent: t("settings.config.source.envContent"), + sourceProjectKilo: t("settings.config.source.projectKilo"), + sourceProjectRoot: t("settings.config.source.projectRoot"), + sourceProjectKilocode: t("settings.config.source.projectKilocode"), + sourceProjectOpencode: t("settings.config.source.projectOpencode"), + }, + } +} From dec5bef7ccd72728fd4810235293fe11f5c0805b Mon Sep 17 00:00:00 2001 From: Rakshith N Date: Fri, 7 Aug 2026 13:37:37 +0530 Subject: [PATCH 09/39] (chore): address bot review comments --- .../tests/skills-settings-responsive.spec.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts index 6403880ced9..5d0330e207e 100644 --- a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts +++ b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts @@ -32,6 +32,16 @@ async function assertRowContained(row: Locator, card: Locator, label: string) { ) } +async function assertTooltipFitsViewport(content: Locator, label: string, page: Page) { + const tipBox = await content.boundingBox() + const viewport = page.viewportSize()! + expect(tipBox, `${label}: tooltip bounding box`).not.toBeNull() + expect(tipBox!.x, `${label}: tooltip left edge inside viewport`).toBeGreaterThanOrEqual(-1) + expect(tipBox!.x + tipBox!.width, `${label}: tooltip right edge inside viewport`).toBeLessThanOrEqual( + viewport.width + 1, + ) +} + test.describe("skills settings responsive layout", () => { test("folder-path and URL rows stay contained and the × button remains visible at a narrow viewport", async ({ page, @@ -73,12 +83,7 @@ test.describe("skills settings responsive layout", () => { await trigger.hover() const content = page.locator('[data-component="tooltip"]').filter({ hasText: seeded }) await expect(content, `Kilo Tooltip exposes full path on hover: ${seeded}`).toBeVisible() - const tipBox = await content.boundingBox() - const viewport = page.viewportSize()! - expect(tipBox, "path tooltip bounding box").not.toBeNull() - expect(tipBox!.x + tipBox!.width, `path tooltip stays inside viewport: ${seeded}`).toBeLessThanOrEqual( - viewport.width + 1, - ) + await assertTooltipFitsViewport(content, `path tooltip "${seeded}"`, page) } for (const seeded of [SEEDED_URL, SEEDED_URL_2]) { @@ -105,12 +110,7 @@ test.describe("skills settings responsive layout", () => { await trigger.hover() const content = page.locator('[data-component="tooltip"]').filter({ hasText: seeded }) await expect(content, `Kilo Tooltip exposes full URL on hover: ${seeded}`).toBeVisible() - const tipBox = await content.boundingBox() - const viewport = page.viewportSize()! - expect(tipBox, "URL tooltip bounding box").not.toBeNull() - expect(tipBox!.x + tipBox!.width, `URL tooltip stays inside viewport: ${seeded}`).toBeLessThanOrEqual( - viewport.width + 1, - ) + await assertTooltipFitsViewport(content, `URL tooltip "${seeded}"`, page) } for (const [label, card] of [ From cccc9a52c647d904b37acff4ec5adbacb8269a7f Mon Sep 17 00:00:00 2001 From: Rakshith N Date: Fri, 7 Aug 2026 14:28:35 +0530 Subject: [PATCH 10/39] fix(vscode): allow 8px viewport slack for Kobalte tooltip overflowPadding --- .../kilo-vscode/tests/skills-settings-responsive.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts index 5d0330e207e..4c66eb9fd6e 100644 --- a/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts +++ b/packages/kilo-vscode/tests/skills-settings-responsive.spec.ts @@ -36,9 +36,12 @@ async function assertTooltipFitsViewport(content: Locator, label: string, page: const tipBox = await content.boundingBox() const viewport = page.viewportSize()! expect(tipBox, `${label}: tooltip bounding box`).not.toBeNull() - expect(tipBox!.x, `${label}: tooltip left edge inside viewport`).toBeGreaterThanOrEqual(-1) + // Kobalte's PopperRoot defaults to overflowPadding: 8, so the floating + // tooltip is allowed to extend up to 8px past each viewport edge before + // the shift middleware stops nudging it. + expect(tipBox!.x, `${label}: tooltip left edge inside viewport`).toBeGreaterThanOrEqual(-9) expect(tipBox!.x + tipBox!.width, `${label}: tooltip right edge inside viewport`).toBeLessThanOrEqual( - viewport.width + 1, + viewport.width + 9, ) } From d047224302041a5388f9ad1a04ce3a7f2e652581 Mon Sep 17 00:00:00 2001 From: Bruno Agatao Date: Fri, 7 Aug 2026 13:56:04 +0200 Subject: [PATCH 11/39] fix(vscode): show approval reason for outside-workspace reads and writes --- .../show-outside-workspace-approval-reason.md | 5 +++ .../kilo-ui/src/components/message-part.tsx | 7 ++-- .../src/components/tool-approval.test.ts | 12 +++++++ .../kilo-ui/src/components/tool-approval.tsx | 7 ++++ .../kilo-vscode/webview-ui/src/i18n/en.ts | 1 + .../src/kilocode/permission/provenance.ts | 20 +++++++++-- packages/opencode/src/session/tools.ts | 23 +++++++----- .../kilocode/permission/provenance.test.ts | 36 +++++++++++++++++++ 8 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 .changeset/show-outside-workspace-approval-reason.md diff --git a/.changeset/show-outside-workspace-approval-reason.md b/.changeset/show-outside-workspace-approval-reason.md new file mode 100644 index 00000000000..1475ae34cdf --- /dev/null +++ b/.changeset/show-outside-workspace-approval-reason.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show the permission approval reason for reads and writes outside the workspace, matching other tools diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index 4281174d125..b22fed5b0a6 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -48,7 +48,7 @@ import { checksum } from "@opencode-ai/core/util/encode" import { Tooltip } from "./tooltip" import { IconButton } from "./icon-button" import { TextShimmer } from "@opencode-ai/ui/text-shimmer" -import { ToolApprovalProvider, resolveToolApproval } from "./tool-approval" +import { ToolApprovalProvider, resolveToolApproval, useToolApproval } from "./tool-approval" export { ToolApprovalProvider, resolveToolApproval } from "./tool-approval" import { GrowBox } from "./grow-box" import { COLLAPSIBLE_SPRING } from "./motion" @@ -1888,10 +1888,13 @@ ToolRegistry.register({ const pending = createMemo(() => busy(props.status)) const images = createMemo(() => (props.attachments ?? []).filter((f) => f.mime.startsWith("image/") && f.url)) const preview = (url: string, alt?: string) => dialog.show(() => ) + // Read is high-frequency and low-risk, so details stay hidden unless the target was outside + // the workspace, in which case the approval reason explains what looks like an "agent escape". + const approval = useToolApproval() return ( <> { expect(out?.source).toBe("ui.approval.source.agent(agent=code)") expect(out?.rule).toBeUndefined() }) + + test("adds the outsideWorkspace text when the approval was marked outside the workspace", () => { + const approval = { source: "agent" as const, agent: "code", outsideWorkspace: true } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBe("ui.approval.outsideWorkspace") + }) + + test("omits the outsideWorkspace text for an ordinary in-workspace approval", () => { + const approval = { source: "agent" as const, agent: "code" } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBeUndefined() + }) }) diff --git a/packages/kilo-ui/src/components/tool-approval.tsx b/packages/kilo-ui/src/components/tool-approval.tsx index d555e105df3..1bb2b9f7598 100644 --- a/packages/kilo-ui/src/components/tool-approval.tsx +++ b/packages/kilo-ui/src/components/tool-approval.tsx @@ -11,6 +11,8 @@ export type ToolApproval = { source: "agent" | "global" | "project" | "yolo" | "session" | "manual" | "default" agent?: string rule?: { permission: string; pattern: string; action: string } + /** True when the tool call's target path was outside the workspace/worktree. */ + outsideWorkspace?: boolean } /** Pre-resolved, localized text plus the raw approval, supplied by the caller. */ @@ -19,6 +21,7 @@ export type ToolApprovalDisplay = { decision: string source?: string rule?: string + outsideWorkspace?: string } const SOURCE_KEYS = ["agent", "global", "project", "yolo", "session", "manual", "default"] as const @@ -71,6 +74,7 @@ export function resolveToolApproval( decision: approval.source === "manual" ? t("ui.approval.manual") : t("ui.approval.auto"), source: sourceText(), rule: ruleText, + outsideWorkspace: approval.outsideWorkspace ? t("ui.approval.outsideWorkspace") : undefined, } } @@ -84,6 +88,9 @@ export function ToolApprovalLine(props: { display: ToolApprovalDisplay }) { {(text) => {text()}} {(text) => {text()}} + + {(text) => {text()}} +
) } diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 6a53383af8f..7ba0c65aa8a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -302,6 +302,7 @@ export const dict = { "ui.approval.source.yolo": "by auto-approve (YOLO) mode", "ui.approval.source.session": "by a session auto-approve rule", "ui.approval.source.default": "by default", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Review", "session.review.filesChanged": "{{count}} Files Changed", diff --git a/packages/opencode/src/kilocode/permission/provenance.ts b/packages/opencode/src/kilocode/permission/provenance.ts index 73f7fec4e0f..896db47f1f9 100644 --- a/packages/opencode/src/kilocode/permission/provenance.ts +++ b/packages/opencode/src/kilocode/permission/provenance.ts @@ -27,6 +27,13 @@ export namespace PermissionProvenance { agent?: string /** The winning rule, omitted for manual replies and the ask fallback. */ rule?: { permission: string; pattern: string; action: Permission.Action } + /** True when the ask's target path was outside the workspace/worktree (an `external_directory` ask). */ + outsideWorkspace?: boolean + } + + /** Tag an approval as outside-workspace when it answers an `external_directory` ask. */ + export function tagOutsideWorkspace(approval: Approval, permission: string): Approval { + return permission === "external_directory" ? { ...approval, outsideWorkspace: true } : approval } export type Scope = "global" | "local" @@ -71,13 +78,22 @@ export namespace PermissionProvenance { * The approval is written once during `ask()`, but tools freely overwrite `state.metadata` * during execution and on completion. Carry the prior `approval` onto the replacement unless * the replacement sets its own. + * + * A file tool that crosses the workspace boundary issues *two* asks for one call: the generic + * `external_directory` ask first, then its own `read`/`write`/`edit` ask. Both write `approval` + * metadata, so the second ask's `outsideWorkspace` marker would otherwise clobber the first's + * even though `"approval" in next` is true. Merge that marker forward so it survives. */ export function carryApproval( prev: Record | undefined, next: Record | undefined, ) { - if (!next || !prev?.approval || "approval" in next) return next - return { ...next, approval: prev.approval } + if (!next) return next + const prior = prev?.approval as Approval | undefined + if (!("approval" in next)) return prior ? { ...next, approval: prior } : next + const current = next.approval as Approval | undefined + if (!prior?.outsideWorkspace || !current || current.outsideWorkspace) return next + return { ...next, approval: { ...current, outsideWorkspace: true } } } /** diff --git a/packages/opencode/src/session/tools.ts b/packages/opencode/src/session/tools.ts index 2d7f45fbf2a..fa2a827ebd8 100644 --- a/packages/opencode/src/session/tools.ts +++ b/packages/opencode/src/session/tools.ts @@ -97,18 +97,25 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { }, }).pipe( // record why the call was allowed onto the tool part, then discard the outcome for the tool-facing ask - Effect.tap((approval) => input.processor.metadata(options.toolCallId, { metadata: { approval } })), + Effect.tap((approval) => + input.processor.metadata(options.toolCallId, { + metadata: { approval: PermissionProvenance.tagOutsideWorkspace(approval, req.permission) }, + }), + ), // record why the call was denied too, so JSON exports and clients can explain the denial Effect.tapErrorTag("PermissionDeniedError", (err) => input.processor.metadata(options.toolCallId, { metadata: { - approval: PermissionProvenance.classifyDenial({ - ruleset: err.ruleset, - permission: req.permission, - patterns: req.patterns, - agent: input.agent.name, - origins: permissionOrigins, - }), + approval: PermissionProvenance.tagOutsideWorkspace( + PermissionProvenance.classifyDenial({ + ruleset: err.ruleset, + permission: req.permission, + patterns: req.patterns, + agent: input.agent.name, + origins: permissionOrigins, + }), + req.permission, + ), }, }), ), diff --git a/packages/opencode/test/kilocode/permission/provenance.test.ts b/packages/opencode/test/kilocode/permission/provenance.test.ts index ad179e7e719..816d577d995 100644 --- a/packages/opencode/test/kilocode/permission/provenance.test.ts +++ b/packages/opencode/test/kilocode/permission/provenance.test.ts @@ -121,6 +121,42 @@ describe("PermissionProvenance.carryApproval", () => { test("returns the replacement as-is when it is undefined", () => { expect(PermissionProvenance.carryApproval({ approval }, undefined)).toBeUndefined() }) + + test("merges outsideWorkspace onto a replacement's own approval instead of clobbering it", () => { + // A file tool crossing the workspace boundary asks twice: external_directory first, then its + // own read/write/edit ask. The second ask's approval must not lose the outsideWorkspace marker. + const outside = { source: "manual" as const, outsideWorkspace: true } + const next = { approval: { source: "agent" as const, agent: "build" } } + expect(PermissionProvenance.carryApproval({ approval: outside }, next)).toEqual({ + approval: { source: "agent", agent: "build", outsideWorkspace: true }, + }) + }) + + test("does not add outsideWorkspace when the prior approval was not outside the workspace", () => { + const next = { approval: { source: "agent" as const, agent: "build" } } + expect(PermissionProvenance.carryApproval({ approval }, next)).toBe(next) + }) + + test("leaves a replacement's own outsideWorkspace marker untouched", () => { + const outside = { source: "manual" as const, outsideWorkspace: true } + const next = { approval: { source: "agent" as const, agent: "build", outsideWorkspace: true } } + expect(PermissionProvenance.carryApproval({ approval: outside }, next)).toBe(next) + }) +}) + +describe("PermissionProvenance.tagOutsideWorkspace", () => { + test("marks an external_directory approval as outsideWorkspace", () => { + const approval = { source: "manual" as const } + expect(PermissionProvenance.tagOutsideWorkspace(approval, "external_directory")).toEqual({ + source: "manual", + outsideWorkspace: true, + }) + }) + + test("leaves other permissions' approvals untouched", () => { + const approval = { source: "manual" as const } + expect(PermissionProvenance.tagOutsideWorkspace(approval, "read")).toBe(approval) + }) }) describe("askPermission returns provenance", () => { From 0c9b640331d05012bad4b98527bcbae86b3d80f2 Mon Sep 17 00:00:00 2001 From: Bruno Agatao Date: Fri, 7 Aug 2026 14:53:39 +0200 Subject: [PATCH 12/39] fix(vscode): add missing outsideWorkspace i18n key to all locales --- packages/kilo-vscode/webview-ui/src/i18n/ar.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/br.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/bs.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/da.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/de.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/es.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/fa.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/fr.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/it.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/ja.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/ko.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/nl.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/no.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/pl.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/ru.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/th.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/tr.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/uk.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/zh.ts | 1 + packages/kilo-vscode/webview-ui/src/i18n/zht.ts | 1 + 20 files changed, 20 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 86f2eef2790..f7c71435f13 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -297,6 +297,7 @@ export const dict = { "ui.approval.source.yolo": "بواسطة وضع الموافقة التلقائية (YOLO)", "ui.approval.source.session": "بواسطة قاعدة موافقة تلقائية للجلسة", "ui.approval.source.default": "افتراضيًا", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "مراجعة", "session.review.filesChanged": "تم تغيير {{count}} ملفات", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index d29e57f2ce9..d8c60157218 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -307,6 +307,7 @@ export const dict = { "ui.approval.source.yolo": "pelo modo de aprovação automática (YOLO)", "ui.approval.source.session": "por uma regra de aprovação automática da sessão", "ui.approval.source.default": "por padrão", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Revisão", "session.review.filesChanged": "{{count}} Arquivos Alterados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index cf67d75e527..f7e39f6bfa0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -305,6 +305,7 @@ export const dict = { "ui.approval.source.yolo": "režimom automatskog odobravanja (YOLO)", "ui.approval.source.session": "pravilom automatskog odobravanja sesije", "ui.approval.source.default": "podrazumevano", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Pregled", "session.review.filesChanged": "Izmijenjeno {{count}} datoteka", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index e432d839775..97f238fcf0d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -304,6 +304,7 @@ export const dict = { "ui.approval.source.yolo": "af automatisk godkendelse (YOLO)", "ui.approval.source.session": "af en session-autogodkendelsesregel", "ui.approval.source.default": "som standard", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Gennemgang", "session.review.filesChanged": "{{count}} Filer ændret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 3d7058a7ad5..dd334cb8b6a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -313,6 +313,7 @@ export const dict = { "ui.approval.source.yolo": "durch den Auto-Genehmigungsmodus (YOLO)", "ui.approval.source.session": "durch eine Sitzungs-Auto-Genehmigungsregel", "ui.approval.source.default": "standardmäßig", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Überprüfung", "session.review.filesChanged": "{{count}} Dateien geändert", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 3af575f0c5e..556e4d950bb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -308,6 +308,7 @@ export const dict = { "ui.approval.source.yolo": "por el modo de aprobación automática (YOLO)", "ui.approval.source.session": "por una regla de aprobación automática de sesión", "ui.approval.source.default": "de forma predeterminada", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Revisión", "session.review.filesChanged": "{{count}} Archivos Cambiados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts index 97f0b6b457a..6e0b77e3c3f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts @@ -302,6 +302,7 @@ export const dict = { "ui.approval.source.yolo": "توسط حالت تأیید خودکار (YOLO)", "ui.approval.source.session": "توسط قانون تأیید خودکار جلسه", "ui.approval.source.default": "به‌طور پیش‌فرض", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "بررسی", "session.review.filesChanged": "{{count}} فایل تغییر یافته", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index be9791bc667..3349663f683 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -307,6 +307,7 @@ export const dict = { "ui.approval.source.yolo": "par le mode d'approbation automatique (YOLO)", "ui.approval.source.session": "par une règle d'approbation automatique de session", "ui.approval.source.default": "par défaut", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Revue", "session.review.filesChanged": "{{count}} fichiers modifiés", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts index f1d977e1883..5600b4b533f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts @@ -219,6 +219,7 @@ export const dict = { "ui.approval.source.yolo": "dalla modalità di approvazione automatica (YOLO)", "ui.approval.source.session": "da una regola di approvazione automatica della sessione", "ui.approval.source.default": "per impostazione predefinita", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Revisione", "session.review.filesChanged": "{{count}} file modificati", "session.review.loadingChanges": "Caricamento modifiche...", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 279600efc2b..7f38cab2ecb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -304,6 +304,7 @@ export const dict = { "ui.approval.source.yolo": "自動承認(YOLO)モードによって", "ui.approval.source.session": "セッションの自動承認ルールによって", "ui.approval.source.default": "デフォルトで", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "レビュー", "session.review.filesChanged": "{{count}} ファイル変更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index bfc1ae50aeb..6c88e3d17ac 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -305,6 +305,7 @@ export const dict = { "ui.approval.source.yolo": "자동 승인(YOLO) 모드에 의해", "ui.approval.source.session": "세션 자동 승인 규칙에 의해", "ui.approval.source.default": "기본값으로", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "검토", "session.review.filesChanged": "{{count}}개 파일 변경됨", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index c3ad428f564..52ebf8e8787 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -308,6 +308,7 @@ export const dict = { "ui.approval.source.yolo": "door de automatische goedkeuringsmodus (YOLO)", "ui.approval.source.session": "door een sessie-automatische-goedkeuringsregel", "ui.approval.source.default": "standaard", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Beoordelen", "session.review.filesChanged": "{{count}} bestanden gewijzigd", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 519b8ebc2cd..a0e4809dcd7 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -311,6 +311,7 @@ export const dict = { "ui.approval.source.yolo": "av automatisk godkjenning (YOLO)", "ui.approval.source.session": "av en økt-autogodkjenningsregel", "ui.approval.source.default": "som standard", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Gjennomgang", "session.review.filesChanged": "{{count}} filer endret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index dc7f924e6ac..8f655adac46 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -305,6 +305,7 @@ export const dict = { "ui.approval.source.yolo": "przez tryb automatycznego zatwierdzania (YOLO)", "ui.approval.source.session": "przez regułę automatycznego zatwierdzania sesji", "ui.approval.source.default": "domyślnie", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Przegląd", "session.review.filesChanged": "Zmieniono {{count}} plików", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 0ecea516048..9731d5a1523 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -303,6 +303,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автоодобрения (YOLO)", "ui.approval.source.session": "правилом автоодобрения сессии", "ui.approval.source.default": "по умолчанию", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Обзор", "session.review.filesChanged": "{{count}} файлов изменено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 1f2cc4ac422..2d6f0cac2ba 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -302,6 +302,7 @@ export const dict = { "ui.approval.source.yolo": "โดยโหมดอนุมัติอัตโนมัติ (YOLO)", "ui.approval.source.session": "โดยกฎอนุมัติอัตโนมัติของเซสชัน", "ui.approval.source.default": "ตามค่าเริ่มต้น", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "ตรวจสอบ", "session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index e5748071b2e..0671e4a0fa8 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -303,6 +303,7 @@ export const dict = { "ui.approval.source.yolo": "otomatik onay (YOLO) modu tarafından", "ui.approval.source.session": "bir oturum otomatik onay kuralı tarafından", "ui.approval.source.default": "varsayılan olarak", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "İnceleme", "session.review.filesChanged": "{{count}} Dosya Değişti", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index 183912721fa..a763b3b8d32 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -307,6 +307,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автосхвалення (YOLO)", "ui.approval.source.session": "правилом автосхвалення сесії", "ui.approval.source.default": "за замовчуванням", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "Огляд", "session.review.filesChanged": "{{count}} файлів змінено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index dd35db0e2d6..f2037d94047 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -292,6 +292,7 @@ export const dict = { "ui.approval.source.yolo": "由自动批准(YOLO)模式", "ui.approval.source.session": "由会话自动批准规则", "ui.approval.source.default": "默认", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "审查", "session.review.filesChanged": "{{count}} 个文件变更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index aabed9a3e8d..d78c0885301 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -290,6 +290,7 @@ export const dict = { "ui.approval.source.yolo": "由自動核准(YOLO)模式", "ui.approval.source.session": "由工作階段自動核准規則", "ui.approval.source.default": "預設", + "ui.approval.outsideWorkspace": "path is outside your workspace", "session.tab.review": "審查", "session.review.filesChanged": "{{count}} 個檔案變更", From d1630fcbded568a60c5abc956381edb7ae410c17 Mon Sep 17 00:00:00 2001 From: Bruno Agatao Date: Fri, 7 Aug 2026 14:58:16 +0200 Subject: [PATCH 13/39] fix(vscode): translate outsideWorkspace approval string for all locales --- packages/kilo-vscode/webview-ui/src/i18n/ar.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/br.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/bs.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/da.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/de.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/es.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/fa.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/fr.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/it.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/ja.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/ko.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/nl.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/no.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/pl.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/ru.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/th.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/tr.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/uk.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/zh.ts | 2 +- packages/kilo-vscode/webview-ui/src/i18n/zht.ts | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index f7c71435f13..9d59467f9ed 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -297,7 +297,7 @@ export const dict = { "ui.approval.source.yolo": "بواسطة وضع الموافقة التلقائية (YOLO)", "ui.approval.source.session": "بواسطة قاعدة موافقة تلقائية للجلسة", "ui.approval.source.default": "افتراضيًا", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "المسار خارج مساحة العمل الخاصة بك", "session.tab.review": "مراجعة", "session.review.filesChanged": "تم تغيير {{count}} ملفات", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index d8c60157218..4ea354d7d1b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "pelo modo de aprovação automática (YOLO)", "ui.approval.source.session": "por uma regra de aprovação automática da sessão", "ui.approval.source.default": "por padrão", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "o caminho está fora do seu espaço de trabalho", "session.tab.review": "Revisão", "session.review.filesChanged": "{{count}} Arquivos Alterados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index f7e39f6bfa0..953b1378320 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "režimom automatskog odobravanja (YOLO)", "ui.approval.source.session": "pravilom automatskog odobravanja sesije", "ui.approval.source.default": "podrazumevano", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "putanja je izvan vašeg radnog prostora", "session.tab.review": "Pregled", "session.review.filesChanged": "Izmijenjeno {{count}} datoteka", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index 97f238fcf0d..d0433fdff8e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -304,7 +304,7 @@ export const dict = { "ui.approval.source.yolo": "af automatisk godkendelse (YOLO)", "ui.approval.source.session": "af en session-autogodkendelsesregel", "ui.approval.source.default": "som standard", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "stien er uden for dit arbejdsområde", "session.tab.review": "Gennemgang", "session.review.filesChanged": "{{count}} Filer ændret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index dd334cb8b6a..67ca6ef3bcb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -313,7 +313,7 @@ export const dict = { "ui.approval.source.yolo": "durch den Auto-Genehmigungsmodus (YOLO)", "ui.approval.source.session": "durch eine Sitzungs-Auto-Genehmigungsregel", "ui.approval.source.default": "standardmäßig", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "der Pfad liegt außerhalb deines Arbeitsbereichs", "session.tab.review": "Überprüfung", "session.review.filesChanged": "{{count}} Dateien geändert", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 556e4d950bb..a8de2c15dab 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -308,7 +308,7 @@ export const dict = { "ui.approval.source.yolo": "por el modo de aprobación automática (YOLO)", "ui.approval.source.session": "por una regla de aprobación automática de sesión", "ui.approval.source.default": "de forma predeterminada", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "la ruta está fuera de tu espacio de trabajo", "session.tab.review": "Revisión", "session.review.filesChanged": "{{count}} Archivos Cambiados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts index 6e0b77e3c3f..45974a628f5 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts @@ -302,7 +302,7 @@ export const dict = { "ui.approval.source.yolo": "توسط حالت تأیید خودکار (YOLO)", "ui.approval.source.session": "توسط قانون تأیید خودکار جلسه", "ui.approval.source.default": "به‌طور پیش‌فرض", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "مسیر خارج از فضای کاری شما است", "session.tab.review": "بررسی", "session.review.filesChanged": "{{count}} فایل تغییر یافته", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 3349663f683..f911da4bf0d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "par le mode d'approbation automatique (YOLO)", "ui.approval.source.session": "par une règle d'approbation automatique de session", "ui.approval.source.default": "par défaut", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "le chemin se trouve en dehors de votre espace de travail", "session.tab.review": "Revue", "session.review.filesChanged": "{{count}} fichiers modifiés", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts index 5600b4b533f..fa24e22e0ad 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts @@ -219,7 +219,7 @@ export const dict = { "ui.approval.source.yolo": "dalla modalità di approvazione automatica (YOLO)", "ui.approval.source.session": "da una regola di approvazione automatica della sessione", "ui.approval.source.default": "per impostazione predefinita", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "il percorso si trova fuori dall'area di lavoro", "session.tab.review": "Revisione", "session.review.filesChanged": "{{count}} file modificati", "session.review.loadingChanges": "Caricamento modifiche...", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 7f38cab2ecb..14337d35c98 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -304,7 +304,7 @@ export const dict = { "ui.approval.source.yolo": "自動承認(YOLO)モードによって", "ui.approval.source.session": "セッションの自動承認ルールによって", "ui.approval.source.default": "デフォルトで", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "パスがワークスペースの外にあります", "session.tab.review": "レビュー", "session.review.filesChanged": "{{count}} ファイル変更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 6c88e3d17ac..e9cc284d1d0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "자동 승인(YOLO) 모드에 의해", "ui.approval.source.session": "세션 자동 승인 규칙에 의해", "ui.approval.source.default": "기본값으로", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "경로가 작업 영역 외부에 있습니다", "session.tab.review": "검토", "session.review.filesChanged": "{{count}}개 파일 변경됨", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index 52ebf8e8787..682a87ec9be 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -308,7 +308,7 @@ export const dict = { "ui.approval.source.yolo": "door de automatische goedkeuringsmodus (YOLO)", "ui.approval.source.session": "door een sessie-automatische-goedkeuringsregel", "ui.approval.source.default": "standaard", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "het pad ligt buiten je werkruimte", "session.tab.review": "Beoordelen", "session.review.filesChanged": "{{count}} bestanden gewijzigd", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index a0e4809dcd7..1e2cc66adc8 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -311,7 +311,7 @@ export const dict = { "ui.approval.source.yolo": "av automatisk godkjenning (YOLO)", "ui.approval.source.session": "av en økt-autogodkjenningsregel", "ui.approval.source.default": "som standard", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "stien er utenfor arbeidsområdet ditt", "session.tab.review": "Gjennomgang", "session.review.filesChanged": "{{count}} filer endret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 8f655adac46..0e0beb91eaa 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "przez tryb automatycznego zatwierdzania (YOLO)", "ui.approval.source.session": "przez regułę automatycznego zatwierdzania sesji", "ui.approval.source.default": "domyślnie", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "ścieżka znajduje się poza obszarem roboczym", "session.tab.review": "Przegląd", "session.review.filesChanged": "Zmieniono {{count}} plików", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 9731d5a1523..f95a4330dda 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -303,7 +303,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автоодобрения (YOLO)", "ui.approval.source.session": "правилом автоодобрения сессии", "ui.approval.source.default": "по умолчанию", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "путь находится за пределами вашей рабочей области", "session.tab.review": "Обзор", "session.review.filesChanged": "{{count}} файлов изменено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 2d6f0cac2ba..b53e8e257e3 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -302,7 +302,7 @@ export const dict = { "ui.approval.source.yolo": "โดยโหมดอนุมัติอัตโนมัติ (YOLO)", "ui.approval.source.session": "โดยกฎอนุมัติอัตโนมัติของเซสชัน", "ui.approval.source.default": "ตามค่าเริ่มต้น", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "พาธอยู่นอกพื้นที่ทำงานของคุณ", "session.tab.review": "ตรวจสอบ", "session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index 0671e4a0fa8..fb724d75589 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -303,7 +303,7 @@ export const dict = { "ui.approval.source.yolo": "otomatik onay (YOLO) modu tarafından", "ui.approval.source.session": "bir oturum otomatik onay kuralı tarafından", "ui.approval.source.default": "varsayılan olarak", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "yol çalışma alanınızın dışında", "session.tab.review": "İnceleme", "session.review.filesChanged": "{{count}} Dosya Değişti", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index a763b3b8d32..57cd805aee4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автосхвалення (YOLO)", "ui.approval.source.session": "правилом автосхвалення сесії", "ui.approval.source.default": "за замовчуванням", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "шлях знаходиться за межами вашого робочого простору", "session.tab.review": "Огляд", "session.review.filesChanged": "{{count}} файлів змінено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index f2037d94047..a6c0236772b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -292,7 +292,7 @@ export const dict = { "ui.approval.source.yolo": "由自动批准(YOLO)模式", "ui.approval.source.session": "由会话自动批准规则", "ui.approval.source.default": "默认", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "路径在你的工作区之外", "session.tab.review": "审查", "session.review.filesChanged": "{{count}} 个文件变更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index d78c0885301..b1ac1b008a9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -290,7 +290,7 @@ export const dict = { "ui.approval.source.yolo": "由自動核准(YOLO)模式", "ui.approval.source.session": "由工作階段自動核准規則", "ui.approval.source.default": "預設", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "路徑在你的工作區之外", "session.tab.review": "審查", "session.review.filesChanged": "{{count}} 個檔案變更", From 9c36685c0f7ff218f8733f0ada1807ba5149b7d5 Mon Sep 17 00:00:00 2001 From: Bruno Agatao Date: Fri, 7 Aug 2026 15:10:28 +0200 Subject: [PATCH 14/39] fix(vscode): show filename inline in outside-workspace approval note --- .../src/components/tool-approval.test.ts | 18 +++++++-- .../kilo-ui/src/components/tool-approval.tsx | 9 ++++- .../kilo-vscode/webview-ui/src/i18n/ar.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/br.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/bs.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/da.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/de.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/en.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/es.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/fa.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/fr.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/it.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/ja.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/ko.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/nl.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/no.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/pl.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/ru.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/th.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/tr.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/uk.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/zh.ts | 2 +- .../kilo-vscode/webview-ui/src/i18n/zht.ts | 2 +- .../src/kilocode/permission/provenance.ts | 21 ++++++++-- packages/opencode/src/session/tools.ts | 9 ++++- .../kilocode/permission/provenance.test.ts | 38 +++++++++++++++++++ 26 files changed, 108 insertions(+), 29 deletions(-) diff --git a/packages/kilo-ui/src/components/tool-approval.test.ts b/packages/kilo-ui/src/components/tool-approval.test.ts index 414e18ee34c..f61b7c5c8cd 100644 --- a/packages/kilo-ui/src/components/tool-approval.test.ts +++ b/packages/kilo-ui/src/components/tool-approval.test.ts @@ -55,10 +55,15 @@ describe("resolveToolApproval", () => { expect(out?.rule).toBeUndefined() }) - test("adds the outsideWorkspace text when the approval was marked outside the workspace", () => { - const approval = { source: "agent" as const, agent: "code", outsideWorkspace: true } + test("adds the outsideWorkspace text with just the filename when a path is known", () => { + const approval = { + source: "agent" as const, + agent: "code", + outsideWorkspace: true, + outsideWorkspacePath: "/etc/secrets/hello.txt", + } const out = resolveToolApproval({ approval }, t) - expect(out?.outsideWorkspace).toBe("ui.approval.outsideWorkspace") + expect(out?.outsideWorkspace).toBe("ui.approval.outsideWorkspace(file=hello.txt)") }) test("omits the outsideWorkspace text for an ordinary in-workspace approval", () => { @@ -66,4 +71,11 @@ describe("resolveToolApproval", () => { const out = resolveToolApproval({ approval }, t) expect(out?.outsideWorkspace).toBeUndefined() }) + + test("omits the outsideWorkspace text when outsideWorkspace is set but no path is known", () => { + // e.g. a bash command scanning multiple external directories has no single filepath to show. + const approval = { source: "agent" as const, agent: "code", outsideWorkspace: true } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBeUndefined() + }) }) diff --git a/packages/kilo-ui/src/components/tool-approval.tsx b/packages/kilo-ui/src/components/tool-approval.tsx index a245e145b94..aa9d6c322ae 100644 --- a/packages/kilo-ui/src/components/tool-approval.tsx +++ b/packages/kilo-ui/src/components/tool-approval.tsx @@ -1,4 +1,5 @@ import { createContext, useContext, Show, type Accessor, type ParentProps } from "solid-js" +import { getFilename } from "@opencode-ai/core/util/path" import { Icon } from "./icon" /** @@ -14,6 +15,8 @@ export type ToolApproval = { rule?: { permission: string; pattern: string; action: string } /** True when the tool call's target path was outside the workspace/worktree. */ outsideWorkspace?: boolean + /** The target file path, when known, for display as a filename next to the note above. */ + outsideWorkspacePath?: string } /** Pre-resolved, localized text plus the raw approval, supplied by the caller. */ @@ -85,12 +88,16 @@ export function resolveToolApproval( rule && !(rule.permission === "*" && rule.pattern === "*") ? t("ui.approval.rule", { permission: rule.permission, pattern: rule.pattern }) : undefined + // Only worth calling out when we know which file it was; a bare "outside your workspace" note + // without a filename (e.g. a bash command touching several directories) isn't actionable. + const filename = approval.outsideWorkspacePath ? getFilename(approval.outsideWorkspacePath) : undefined return { approval, decision: approval.source === "manual" ? t("ui.approval.manual") : t("ui.approval.auto"), source: sourceText(), rule: ruleText, - outsideWorkspace: approval.outsideWorkspace ? t("ui.approval.outsideWorkspace") : undefined, + outsideWorkspace: + approval.outsideWorkspace && filename ? t("ui.approval.outsideWorkspace", { file: filename }) : undefined, } } diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 9d59467f9ed..9a3310b6555 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -297,7 +297,7 @@ export const dict = { "ui.approval.source.yolo": "بواسطة وضع الموافقة التلقائية (YOLO)", "ui.approval.source.session": "بواسطة قاعدة موافقة تلقائية للجلسة", "ui.approval.source.default": "افتراضيًا", - "ui.approval.outsideWorkspace": "المسار خارج مساحة العمل الخاصة بك", + "ui.approval.outsideWorkspace": "(خارج مساحة العمل: {{file}})", "session.tab.review": "مراجعة", "session.review.filesChanged": "تم تغيير {{count}} ملفات", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 4ea354d7d1b..0df91b651c9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "pelo modo de aprovação automática (YOLO)", "ui.approval.source.session": "por uma regra de aprovação automática da sessão", "ui.approval.source.default": "por padrão", - "ui.approval.outsideWorkspace": "o caminho está fora do seu espaço de trabalho", + "ui.approval.outsideWorkspace": "(fora do seu espaço de trabalho: {{file}})", "session.tab.review": "Revisão", "session.review.filesChanged": "{{count}} Arquivos Alterados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index 953b1378320..db0f4590145 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "režimom automatskog odobravanja (YOLO)", "ui.approval.source.session": "pravilom automatskog odobravanja sesije", "ui.approval.source.default": "podrazumevano", - "ui.approval.outsideWorkspace": "putanja je izvan vašeg radnog prostora", + "ui.approval.outsideWorkspace": "(izvan vašeg radnog prostora: {{file}})", "session.tab.review": "Pregled", "session.review.filesChanged": "Izmijenjeno {{count}} datoteka", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index d0433fdff8e..6f36adc3d33 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -304,7 +304,7 @@ export const dict = { "ui.approval.source.yolo": "af automatisk godkendelse (YOLO)", "ui.approval.source.session": "af en session-autogodkendelsesregel", "ui.approval.source.default": "som standard", - "ui.approval.outsideWorkspace": "stien er uden for dit arbejdsområde", + "ui.approval.outsideWorkspace": "(uden for dit arbejdsområde: {{file}})", "session.tab.review": "Gennemgang", "session.review.filesChanged": "{{count}} Filer ændret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 67ca6ef3bcb..4066d9396d9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -313,7 +313,7 @@ export const dict = { "ui.approval.source.yolo": "durch den Auto-Genehmigungsmodus (YOLO)", "ui.approval.source.session": "durch eine Sitzungs-Auto-Genehmigungsregel", "ui.approval.source.default": "standardmäßig", - "ui.approval.outsideWorkspace": "der Pfad liegt außerhalb deines Arbeitsbereichs", + "ui.approval.outsideWorkspace": "(außerhalb deines Arbeitsbereichs: {{file}})", "session.tab.review": "Überprüfung", "session.review.filesChanged": "{{count}} Dateien geändert", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index af4d5972c9c..80f804d969d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -302,7 +302,7 @@ export const dict = { "ui.approval.source.yolo": "by auto-approve (YOLO) mode", "ui.approval.source.session": "by a session auto-approve rule", "ui.approval.source.default": "by default", - "ui.approval.outsideWorkspace": "path is outside your workspace", + "ui.approval.outsideWorkspace": "(outside your workspace: {{file}})", "session.tab.review": "Review", "session.review.filesChanged": "{{count}} Files Changed", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index a8de2c15dab..558eb4a26ae 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -308,7 +308,7 @@ export const dict = { "ui.approval.source.yolo": "por el modo de aprobación automática (YOLO)", "ui.approval.source.session": "por una regla de aprobación automática de sesión", "ui.approval.source.default": "de forma predeterminada", - "ui.approval.outsideWorkspace": "la ruta está fuera de tu espacio de trabajo", + "ui.approval.outsideWorkspace": "(fuera de tu espacio de trabajo: {{file}})", "session.tab.review": "Revisión", "session.review.filesChanged": "{{count}} Archivos Cambiados", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts index 45974a628f5..cda714862bc 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fa.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fa.ts @@ -302,7 +302,7 @@ export const dict = { "ui.approval.source.yolo": "توسط حالت تأیید خودکار (YOLO)", "ui.approval.source.session": "توسط قانون تأیید خودکار جلسه", "ui.approval.source.default": "به‌طور پیش‌فرض", - "ui.approval.outsideWorkspace": "مسیر خارج از فضای کاری شما است", + "ui.approval.outsideWorkspace": "(خارج از فضای کاری شما: {{file}})", "session.tab.review": "بررسی", "session.review.filesChanged": "{{count}} فایل تغییر یافته", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index f911da4bf0d..0292d8767a9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "par le mode d'approbation automatique (YOLO)", "ui.approval.source.session": "par une règle d'approbation automatique de session", "ui.approval.source.default": "par défaut", - "ui.approval.outsideWorkspace": "le chemin se trouve en dehors de votre espace de travail", + "ui.approval.outsideWorkspace": "(hors de votre espace de travail : {{file}})", "session.tab.review": "Revue", "session.review.filesChanged": "{{count}} fichiers modifiés", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/it.ts b/packages/kilo-vscode/webview-ui/src/i18n/it.ts index fa24e22e0ad..10ef2fd2dcb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/it.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/it.ts @@ -219,7 +219,7 @@ export const dict = { "ui.approval.source.yolo": "dalla modalità di approvazione automatica (YOLO)", "ui.approval.source.session": "da una regola di approvazione automatica della sessione", "ui.approval.source.default": "per impostazione predefinita", - "ui.approval.outsideWorkspace": "il percorso si trova fuori dall'area di lavoro", + "ui.approval.outsideWorkspace": "(fuori dall'area di lavoro: {{file}})", "session.tab.review": "Revisione", "session.review.filesChanged": "{{count}} file modificati", "session.review.loadingChanges": "Caricamento modifiche...", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 14337d35c98..67c60731ef5 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -304,7 +304,7 @@ export const dict = { "ui.approval.source.yolo": "自動承認(YOLO)モードによって", "ui.approval.source.session": "セッションの自動承認ルールによって", "ui.approval.source.default": "デフォルトで", - "ui.approval.outsideWorkspace": "パスがワークスペースの外にあります", + "ui.approval.outsideWorkspace": "(ワークスペース外:{{file}})", "session.tab.review": "レビュー", "session.review.filesChanged": "{{count}} ファイル変更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index e9cc284d1d0..e7d0664e7b7 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "자동 승인(YOLO) 모드에 의해", "ui.approval.source.session": "세션 자동 승인 규칙에 의해", "ui.approval.source.default": "기본값으로", - "ui.approval.outsideWorkspace": "경로가 작업 영역 외부에 있습니다", + "ui.approval.outsideWorkspace": "(작업 영역 외부: {{file}})", "session.tab.review": "검토", "session.review.filesChanged": "{{count}}개 파일 변경됨", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index 682a87ec9be..40169d8672d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -308,7 +308,7 @@ export const dict = { "ui.approval.source.yolo": "door de automatische goedkeuringsmodus (YOLO)", "ui.approval.source.session": "door een sessie-automatische-goedkeuringsregel", "ui.approval.source.default": "standaard", - "ui.approval.outsideWorkspace": "het pad ligt buiten je werkruimte", + "ui.approval.outsideWorkspace": "(buiten je werkruimte: {{file}})", "session.tab.review": "Beoordelen", "session.review.filesChanged": "{{count}} bestanden gewijzigd", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 1e2cc66adc8..bea79dee720 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -311,7 +311,7 @@ export const dict = { "ui.approval.source.yolo": "av automatisk godkjenning (YOLO)", "ui.approval.source.session": "av en økt-autogodkjenningsregel", "ui.approval.source.default": "som standard", - "ui.approval.outsideWorkspace": "stien er utenfor arbeidsområdet ditt", + "ui.approval.outsideWorkspace": "(utenfor arbeidsområdet ditt: {{file}})", "session.tab.review": "Gjennomgang", "session.review.filesChanged": "{{count}} filer endret", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 0e0beb91eaa..701c94122f9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -305,7 +305,7 @@ export const dict = { "ui.approval.source.yolo": "przez tryb automatycznego zatwierdzania (YOLO)", "ui.approval.source.session": "przez regułę automatycznego zatwierdzania sesji", "ui.approval.source.default": "domyślnie", - "ui.approval.outsideWorkspace": "ścieżka znajduje się poza obszarem roboczym", + "ui.approval.outsideWorkspace": "(poza obszarem roboczym: {{file}})", "session.tab.review": "Przegląd", "session.review.filesChanged": "Zmieniono {{count}} plików", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index f95a4330dda..9f64e84a176 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -303,7 +303,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автоодобрения (YOLO)", "ui.approval.source.session": "правилом автоодобрения сессии", "ui.approval.source.default": "по умолчанию", - "ui.approval.outsideWorkspace": "путь находится за пределами вашей рабочей области", + "ui.approval.outsideWorkspace": "(за пределами вашей рабочей области: {{file}})", "session.tab.review": "Обзор", "session.review.filesChanged": "{{count}} файлов изменено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index b53e8e257e3..5b0e38ddf14 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -302,7 +302,7 @@ export const dict = { "ui.approval.source.yolo": "โดยโหมดอนุมัติอัตโนมัติ (YOLO)", "ui.approval.source.session": "โดยกฎอนุมัติอัตโนมัติของเซสชัน", "ui.approval.source.default": "ตามค่าเริ่มต้น", - "ui.approval.outsideWorkspace": "พาธอยู่นอกพื้นที่ทำงานของคุณ", + "ui.approval.outsideWorkspace": "(นอกพื้นที่ทำงานของคุณ: {{file}})", "session.tab.review": "ตรวจสอบ", "session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index fb724d75589..83a9d8f16ce 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -303,7 +303,7 @@ export const dict = { "ui.approval.source.yolo": "otomatik onay (YOLO) modu tarafından", "ui.approval.source.session": "bir oturum otomatik onay kuralı tarafından", "ui.approval.source.default": "varsayılan olarak", - "ui.approval.outsideWorkspace": "yol çalışma alanınızın dışında", + "ui.approval.outsideWorkspace": "(çalışma alanınızın dışında: {{file}})", "session.tab.review": "İnceleme", "session.review.filesChanged": "{{count}} Dosya Değişti", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index 57cd805aee4..e773ee2d54a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -307,7 +307,7 @@ export const dict = { "ui.approval.source.yolo": "режимом автосхвалення (YOLO)", "ui.approval.source.session": "правилом автосхвалення сесії", "ui.approval.source.default": "за замовчуванням", - "ui.approval.outsideWorkspace": "шлях знаходиться за межами вашого робочого простору", + "ui.approval.outsideWorkspace": "(за межами вашого робочого простору: {{file}})", "session.tab.review": "Огляд", "session.review.filesChanged": "{{count}} файлів змінено", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index a6c0236772b..249a332bd1b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -292,7 +292,7 @@ export const dict = { "ui.approval.source.yolo": "由自动批准(YOLO)模式", "ui.approval.source.session": "由会话自动批准规则", "ui.approval.source.default": "默认", - "ui.approval.outsideWorkspace": "路径在你的工作区之外", + "ui.approval.outsideWorkspace": "(工作区之外:{{file}})", "session.tab.review": "审查", "session.review.filesChanged": "{{count}} 个文件变更", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index b1ac1b008a9..93746129e74 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -290,7 +290,7 @@ export const dict = { "ui.approval.source.yolo": "由自動核准(YOLO)模式", "ui.approval.source.session": "由工作階段自動核准規則", "ui.approval.source.default": "預設", - "ui.approval.outsideWorkspace": "路徑在你的工作區之外", + "ui.approval.outsideWorkspace": "(工作區之外:{{file}})", "session.tab.review": "審查", "session.review.filesChanged": "{{count}} 個檔案變更", diff --git a/packages/opencode/src/kilocode/permission/provenance.ts b/packages/opencode/src/kilocode/permission/provenance.ts index 896db47f1f9..38b244448ec 100644 --- a/packages/opencode/src/kilocode/permission/provenance.ts +++ b/packages/opencode/src/kilocode/permission/provenance.ts @@ -29,11 +29,19 @@ export namespace PermissionProvenance { rule?: { permission: string; pattern: string; action: Permission.Action } /** True when the ask's target path was outside the workspace/worktree (an `external_directory` ask). */ outsideWorkspace?: boolean + /** The target file path, when the `external_directory` ask carried one, for display as a filename. */ + outsideWorkspacePath?: string + } + + /** The `filepath` an `external_directory` ask's metadata carries, if any (see `Tool.assertExternalDirectory`). */ + export function filepathOf(metadata: Record | undefined): string | undefined { + return typeof metadata?.filepath === "string" ? metadata.filepath : undefined } /** Tag an approval as outside-workspace when it answers an `external_directory` ask. */ - export function tagOutsideWorkspace(approval: Approval, permission: string): Approval { - return permission === "external_directory" ? { ...approval, outsideWorkspace: true } : approval + export function tagOutsideWorkspace(approval: Approval, permission: string, path?: string): Approval { + if (permission !== "external_directory") return approval + return { ...approval, outsideWorkspace: true, ...(path ? { outsideWorkspacePath: path } : {}) } } export type Scope = "global" | "local" @@ -93,7 +101,14 @@ export namespace PermissionProvenance { if (!("approval" in next)) return prior ? { ...next, approval: prior } : next const current = next.approval as Approval | undefined if (!prior?.outsideWorkspace || !current || current.outsideWorkspace) return next - return { ...next, approval: { ...current, outsideWorkspace: true } } + return { + ...next, + approval: { + ...current, + outsideWorkspace: true, + ...(prior.outsideWorkspacePath ? { outsideWorkspacePath: prior.outsideWorkspacePath } : {}), + }, + } } /** diff --git a/packages/opencode/src/session/tools.ts b/packages/opencode/src/session/tools.ts index fa2a827ebd8..9b4907512a6 100644 --- a/packages/opencode/src/session/tools.ts +++ b/packages/opencode/src/session/tools.ts @@ -99,7 +99,13 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { // record why the call was allowed onto the tool part, then discard the outcome for the tool-facing ask Effect.tap((approval) => input.processor.metadata(options.toolCallId, { - metadata: { approval: PermissionProvenance.tagOutsideWorkspace(approval, req.permission) }, + metadata: { + approval: PermissionProvenance.tagOutsideWorkspace( + approval, + req.permission, + PermissionProvenance.filepathOf(req.metadata), + ), + }, }), ), // record why the call was denied too, so JSON exports and clients can explain the denial @@ -115,6 +121,7 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { origins: permissionOrigins, }), req.permission, + PermissionProvenance.filepathOf(req.metadata), ), }, }), diff --git a/packages/opencode/test/kilocode/permission/provenance.test.ts b/packages/opencode/test/kilocode/permission/provenance.test.ts index 816d577d995..b2ffae3ecdd 100644 --- a/packages/opencode/test/kilocode/permission/provenance.test.ts +++ b/packages/opencode/test/kilocode/permission/provenance.test.ts @@ -132,6 +132,14 @@ describe("PermissionProvenance.carryApproval", () => { }) }) + test("also carries the outsideWorkspacePath forward alongside the marker", () => { + const outside = { source: "manual" as const, outsideWorkspace: true, outsideWorkspacePath: "/tmp/secret.txt" } + const next = { approval: { source: "agent" as const, agent: "build" } } + expect(PermissionProvenance.carryApproval({ approval: outside }, next)).toEqual({ + approval: { source: "agent", agent: "build", outsideWorkspace: true, outsideWorkspacePath: "/tmp/secret.txt" }, + }) + }) + test("does not add outsideWorkspace when the prior approval was not outside the workspace", () => { const next = { approval: { source: "agent" as const, agent: "build" } } expect(PermissionProvenance.carryApproval({ approval }, next)).toBe(next) @@ -157,6 +165,36 @@ describe("PermissionProvenance.tagOutsideWorkspace", () => { const approval = { source: "manual" as const } expect(PermissionProvenance.tagOutsideWorkspace(approval, "read")).toBe(approval) }) + + test("carries the target path when one is given", () => { + const approval = { source: "manual" as const } + expect(PermissionProvenance.tagOutsideWorkspace(approval, "external_directory", "/tmp/secret.txt")).toEqual({ + source: "manual", + outsideWorkspace: true, + outsideWorkspacePath: "/tmp/secret.txt", + }) + }) + + test("omits outsideWorkspacePath when no path is given", () => { + const approval = { source: "manual" as const } + expect(PermissionProvenance.tagOutsideWorkspace(approval, "external_directory")).toEqual({ + source: "manual", + outsideWorkspace: true, + }) + }) +}) + +describe("PermissionProvenance.filepathOf", () => { + test("reads the filepath an external_directory ask's metadata carries", () => { + expect(PermissionProvenance.filepathOf({ filepath: "/tmp/secret.txt", parentDir: "/tmp" })).toBe( + "/tmp/secret.txt", + ) + }) + + test("returns undefined when there is no filepath, e.g. a bash directory scan", () => { + expect(PermissionProvenance.filepathOf({ command: "cat /tmp/secret.txt", access: "read" })).toBeUndefined() + expect(PermissionProvenance.filepathOf(undefined)).toBeUndefined() + }) }) describe("askPermission returns provenance", () => { From 910f0f24d2b38ff04b43dee694f6968608f54eb1 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Fri, 7 Aug 2026 16:45:50 +0200 Subject: [PATCH 15/39] fix(cli): disable suggest tool in non-interactive runs --- ...disable-suggest-in-non-interactive-runs.md | 5 +++ .../opencode/src/cli/cmd/github.handler.ts | 7 ++++ packages/opencode/src/cli/cmd/run.ts | 17 +++++++- packages/opencode/src/kilocode/tool/task.ts | 1 + packages/opencode/test/cli/auto-mode.test.ts | 15 ++++++- .../opencode/test/cli/run/run-process.test.ts | 41 +++++++++++++++++++ .../test/kilocode/task-nesting.test.ts | 5 +++ 7 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 .changeset/disable-suggest-in-non-interactive-runs.md diff --git a/.changeset/disable-suggest-in-non-interactive-runs.md b/.changeset/disable-suggest-in-non-interactive-runs.md new file mode 100644 index 00000000000..d75f2b540ee --- /dev/null +++ b/.changeset/disable-suggest-in-non-interactive-runs.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Disable the suggest tool and auto-dismiss pending suggestions in non-interactive CLI runs to prevent hanging on benchmarks and automated pipelines. diff --git a/packages/opencode/src/cli/cmd/github.handler.ts b/packages/opencode/src/cli/cmd/github.handler.ts index 9727a76c2a0..1a94e86ebb1 100644 --- a/packages/opencode/src/cli/cmd/github.handler.ts +++ b/packages/opencode/src/cli/cmd/github.handler.ts @@ -516,6 +516,13 @@ export const githubRun = Effect.fn("Cli.github.run")(function* (args: { event?: action: "deny", pattern: "*", }, + // kilocode_change start + { + permission: "suggest", + action: "deny", + pattern: "*", + }, + // kilocode_change end ], }), ) diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 1ae73d53c62..36bcabfba86 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -472,7 +472,12 @@ export const RunCommand = effectCmd({ action: "deny", pattern: "*", }, - // kilocode_change start - non-interactive runs cannot take over a terminal + // kilocode_change start - non-interactive runs cannot answer suggestions or take over a terminal + { + permission: "suggest", + action: "deny", + pattern: "*", + }, { permission: "interactive_terminal", action: "deny", @@ -904,6 +909,16 @@ export const RunCommand = effectCmd({ break } + // kilocode_change start - non-interactive runs dismiss suggestions so they don't block + if (event.type === "suggestion.shown") { + const suggestion = event.properties + if (suggestion.sessionID === sessionID || KiloRunAuto.allowed(tracked, suggestion.sessionID)) { + await client.suggestion.dismiss({ requestID: suggestion.id }).catch(() => {}) + } + continue + } + // kilocode_change end + if (event.type === "permission.asked") { const permission = event.properties // kilocode_change start - skill shell batches need an interactive human decision. The server ignores diff --git a/packages/opencode/src/kilocode/tool/task.ts b/packages/opencode/src/kilocode/tool/task.ts index f5d094df7bc..3337423c8d9 100644 --- a/packages/opencode/src/kilocode/tool/task.ts +++ b/packages/opencode/src/kilocode/tool/task.ts @@ -80,6 +80,7 @@ export namespace KiloTask { return [ { permission: "task", pattern: "*", action: "deny" }, { permission: "question", pattern: "*", action: "deny" }, + { permission: "suggest", pattern: "*", action: "deny" }, { permission: "interactive_terminal", pattern: "*", action: "deny" }, ...rules, ] diff --git a/packages/opencode/test/cli/auto-mode.test.ts b/packages/opencode/test/cli/auto-mode.test.ts index eef67fcc56c..598d4017859 100644 --- a/packages/opencode/test/cli/auto-mode.test.ts +++ b/packages/opencode/test/cli/auto-mode.test.ts @@ -18,6 +18,11 @@ describe("Auto mode flag", () => { action: "deny" as const, pattern: "*", }, + { + permission: "suggest", + action: "deny" as const, + pattern: "*", + }, { permission: "interactive_terminal", action: "deny" as const, @@ -25,7 +30,7 @@ describe("Auto mode flag", () => { }, ] - expect(autoPermissions).toHaveLength(3) + expect(autoPermissions).toHaveLength(4) // First rule: allow all expect(autoPermissions[0].permission).toBe("*") @@ -36,9 +41,12 @@ describe("Auto mode flag", () => { expect(autoPermissions[1].permission).toBe("question") expect(autoPermissions[1].action).toBe("deny") expect(autoPermissions[1].pattern).toBe("*") - expect(autoPermissions[2].permission).toBe("interactive_terminal") + expect(autoPermissions[2].permission).toBe("suggest") expect(autoPermissions[2].action).toBe("deny") expect(autoPermissions[2].pattern).toBe("*") + expect(autoPermissions[3].permission).toBe("interactive_terminal") + expect(autoPermissions[3].action).toBe("deny") + expect(autoPermissions[3].pattern).toBe("*") }) test("non-auto mode should not set allow-all permissions", () => { @@ -55,6 +63,7 @@ describe("Auto mode flag", () => { const autoPermissions = [ { permission: "*", action: "allow" as const, pattern: "*" }, { permission: "question", action: "deny" as const, pattern: "*" }, + { permission: "suggest", action: "deny" as const, pattern: "*" }, { permission: "interactive_terminal", action: "deny" as const, pattern: "*" }, ] @@ -71,6 +80,8 @@ describe("Auto mode flag", () => { // Test that "question" permission resolves to "deny" const questionRule = findLastMatch("question") expect(questionRule?.action).toBe("deny") + const suggestRule = findLastMatch("suggest") + expect(suggestRule?.action).toBe("deny") const terminalRule = findLastMatch("interactive_terminal") expect(terminalRule?.action).toBe("deny") diff --git a/packages/opencode/test/cli/run/run-process.test.ts b/packages/opencode/test/cli/run/run-process.test.ts index 2dce9e8cb21..70fcbe5bea8 100644 --- a/packages/opencode/test/cli/run/run-process.test.ts +++ b/packages/opencode/test/cli/run/run-process.test.ts @@ -412,4 +412,45 @@ describe("opencode run (non-interactive subprocess)", () => { }), 60_000, // kilocode_change ) + + // kilocode_change start - non-interactive runs exclude human-driven tools like suggest + cliIt.concurrent( + "kilo run --auto excludes suggest tool from LLM request", + ({ llm, opencode }) => + Effect.gen(function* () { + yield* llm.text("done") + const result = yield* opencode.run("do work", { extraArgs: ["--auto"] }) + opencode.expectExit(result, 0) + expect(result.stdout).toBe("done\n") + const inputs = yield* llm.inputs + const tools = (inputs[0]?.body as any)?.tools as Array<{ function?: { name?: string } }> | undefined + const toolNames = tools?.map((t) => t.function?.name).filter(Boolean) ?? [] + expect(toolNames).not.toContain("suggest") + expect(toolNames).not.toContain("question") + expect(toolNames).not.toContain("interactive_terminal") + }), + 60_000, + ) + + cliIt.concurrent( + "kilo run auto-dismisses suggestion and exits cleanly if suggest tool is invoked", + ({ llm, opencode }) => + Effect.gen(function* () { + yield* llm.push( + reply().tool("suggest", { + suggest: "Run checks?", + actions: [{ label: "Run checks", prompt: "Run checks" }], + }), + ) + yield* llm.text("completed after dismissal") + const result = yield* opencode.run("do work", { + permission: { suggest: "allow" }, + extraArgs: ["--auto"], + }) + opencode.expectExit(result, 0) + expect(result.stdout).toContain("completed after dismissal") + }), + 60_000, + ) + // kilocode_change end }) diff --git a/packages/opencode/test/kilocode/task-nesting.test.ts b/packages/opencode/test/kilocode/task-nesting.test.ts index 4d566155130..a57b682905c 100644 --- a/packages/opencode/test/kilocode/task-nesting.test.ts +++ b/packages/opencode/test/kilocode/task-nesting.test.ts @@ -279,6 +279,11 @@ describe("Kilo task nesting", () => { pattern: "*", action: "deny", }, + { + permission: "suggest", + pattern: "*", + action: "deny", + }, { permission: "interactive_terminal", pattern: "*", From b234d25337142417b07d83a715e7ad4c3391dca4 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 7 Aug 2026 16:51:44 +0200 Subject: [PATCH 16/39] fix(vscode): surface exit signals and spawn errors in server startup diagnostics --- .../surface-backend-exit-diagnostics.md | 5 +++ .../src/MarketplacePanelProvider.ts | 12 +++++-- .../cli-backend/connection-service.test.ts | 22 ++++++++++++ .../cli-backend/connection-service.ts | 12 +++---- .../src/services/cli-backend/i18n/ar.ts | 6 ++-- .../src/services/cli-backend/i18n/br.ts | 6 ++-- .../src/services/cli-backend/i18n/bs.ts | 6 ++-- .../src/services/cli-backend/i18n/da.ts | 6 ++-- .../src/services/cli-backend/i18n/de.ts | 3 ++ .../src/services/cli-backend/i18n/en.ts | 2 ++ .../src/services/cli-backend/i18n/es.ts | 6 ++-- .../src/services/cli-backend/i18n/fa.ts | 6 ++-- .../src/services/cli-backend/i18n/fr.ts | 10 +++--- .../src/services/cli-backend/i18n/it.ts | 8 +++-- .../src/services/cli-backend/i18n/ja.ts | 2 ++ .../src/services/cli-backend/i18n/ko.ts | 6 ++-- .../src/services/cli-backend/i18n/nl.ts | 2 ++ .../src/services/cli-backend/i18n/no.ts | 2 ++ .../src/services/cli-backend/i18n/pl.ts | 4 ++- .../src/services/cli-backend/i18n/ru.ts | 2 ++ .../src/services/cli-backend/i18n/th.ts | 6 ++-- .../src/services/cli-backend/i18n/tr.ts | 4 ++- .../src/services/cli-backend/i18n/uk.ts | 4 ++- .../src/services/cli-backend/i18n/zh.ts | 6 ++-- .../src/services/cli-backend/i18n/zht.ts | 6 ++-- .../services/cli-backend/server-manager.ts | 35 ++++++++++++------- .../tests/unit/server-manager-utils.test.ts | 28 +++++++++++++++ 27 files changed, 166 insertions(+), 51 deletions(-) create mode 100644 .changeset/surface-backend-exit-diagnostics.md diff --git a/.changeset/surface-backend-exit-diagnostics.md b/.changeset/surface-backend-exit-diagnostics.md new file mode 100644 index 00000000000..146a00c7424 --- /dev/null +++ b/.changeset/surface-backend-exit-diagnostics.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Surface process exit signals and structured spawn failure details in server startup diagnostics diff --git a/packages/kilo-vscode/src/MarketplacePanelProvider.ts b/packages/kilo-vscode/src/MarketplacePanelProvider.ts index 0c704ac5805..fc62bf46b9b 100644 --- a/packages/kilo-vscode/src/MarketplacePanelProvider.ts +++ b/packages/kilo-vscode/src/MarketplacePanelProvider.ts @@ -6,7 +6,7 @@ import { watchFontSizeConfig } from "./kilo-provider/font-size" import { mapSSEEventToWebviewMessage } from "./kilo-provider-utils" import { resolvePanelProjectDirectory } from "./project-directory" import { seedSessionStatuses } from "./session-status" -import type { KiloConnectionService } from "./services/cli-backend" +import { type KiloConnectionService, ServerStartupError } from "./services/cli-backend" import { MarketplaceService } from "./services/marketplace" import { fetchMarketplaceData, @@ -155,7 +155,15 @@ export class MarketplacePanelProvider implements vscode.Disposable { await this.connection.connect(this.directory()) await this.sync(this.statuses.size === 0) } catch (err) { - this.post({ type: "connectionState", state: "error", error: err instanceof Error ? err.message : String(err) }) + this.post({ + type: "connectionState", + state: "error", + error: err instanceof Error ? err.message : String(err), + ...(err instanceof ServerStartupError && { + userMessage: err.userMessage, + userDetails: err.userDetails, + }), + }) } } diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts index 17420f7bc6a..262b2d64bf8 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts @@ -282,3 +282,25 @@ describe("KiloConnectionService drainPendingPrompts", () => { expect(cleared).toBe(0) }) }) + +describe("KiloConnectionService server exit handling", () => { + test("reports signal name when process is killed by signal", () => { + const service = new KiloConnectionService({} as any) + let stateErr: Error | undefined + service.onStateChange((state, err) => { + if (state === "error") stateErr = err + }) + ;(service as any).handleServerExit(null, "SIGSEGV") + expect(stateErr?.message).toBe("CLI background process exited with signal SIGSEGV. Retry to reconnect.") + }) + + test("reports exit code when process exits normally with code", () => { + const service = new KiloConnectionService({} as any) + let stateErr: Error | undefined + service.onStateChange((state, err) => { + if (state === "error") stateErr = err + }) + ;(service as any).handleServerExit(1, null) + expect(stateErr?.message).toBe("CLI background process exited with code 1. Retry to reconnect.") + }) +}) diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts index 7a7e1209659..d202eeedba9 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts @@ -138,7 +138,7 @@ export class KiloConnectionService { update: async () => undefined, } satisfies Pick) this.sandboxPreference = new SandboxPreference(state) - this.serverManager = new ServerManager(context, (code) => this.handleServerExit(code)) + this.serverManager = new ServerManager(context, (code, signal) => this.handleServerExit(code, signal)) this.active = vscode.window.state.focused this.windowStateDisposable = vscode.window.onDidChangeWindowState((ws) => { this.active = ws.focused @@ -790,13 +790,11 @@ export class KiloConnectionService { this.questionRevision += 1 } - private handleServerExit(code: number | null): void { - console.warn("[Kilo New] ConnectionService: CLI background process exited:", code) + private handleServerExit(code: number | null, signal: NodeJS.Signals | null): void { + const reason = signal ? `signal ${signal}` : `code ${code ?? "unknown"}` + console.warn(`[Kilo New] ConnectionService: CLI background process exited with ${reason}`) this.resetConnection() - this.setState( - "error", - new Error(`CLI background process exited with code ${code ?? "unknown"}. Retry to reconnect.`), - ) + this.setState("error", new Error(`CLI background process exited with ${reason}. Retry to reconnect.`)) } private async doConnect(workspaceDir: string): Promise { diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts index 1221fb0de36..c938afc5e41 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "انتهت عملية CLI بالرمز {{code}} قبل بدء الخادم", - "server.startupTimeout": "انتهت مهلة بدء تشغيل الخادم بعد {{seconds}} ثانية", + "server.processExited": "تم إنهاء عملية CLI بالرمز {{code}} قبل بدء الخادم", + "server.processSignaled": "تم إنهاء عملية CLI بواسطة الإشارة {{signal}} قبل بدء الخادم", + "server.spawnFailed": "فشل في تشغيل الملف الثنائي لـ CLI ({{code}})", + "server.startupTimeout": "انتهت مهلة بدء تشغيل الخادم بعد {{seconds}} ثوانٍ", "remote.connected": "Kilo Remote: متصل", "remote.connecting": "Kilo Remote: جارٍ الاتصال\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts index c2cb93e6ef1..31c166b9c29 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "O processo da CLI foi encerrado com o código {{code}} antes que o servidor fosse iniciado", - "server.startupTimeout": "Tempo limite de inicialização do servidor esgotado após {{seconds}} segundos", + "server.processExited": "O processo da CLI foi encerrado com o código {{code}} antes da inicialização do servidor", + "server.processSignaled": "O processo da CLI foi finalizado pelo sinal {{signal}} antes da inicialização do servidor", + "server.spawnFailed": "Falha ao gerar o binário da CLI ({{code}})", + "server.startupTimeout": "Tempo limite de inicialização do servidor atingido após {{seconds}} segundos", "remote.connected": "Kilo Remote: Conectado", "remote.connecting": "Kilo Remote: Conectando\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts index 87de30b377c..6cf1847da96 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "CLI proces je izašao sa kodom {{code}} prije nego što se server pokrenuo", - "server.startupTimeout": "Vrijeme pokretanja servera je isteklo nakon {{seconds}} sekundi", + "server.processExited": "CLI proces je izašao s kodom {{code}} prije nego što je server pokrenut", + "server.processSignaled": "CLI proces je prekinut signalom {{signal}} prije pokretanja servera", + "server.spawnFailed": "Neuspjelo pokretanje CLI binarne datoteke ({{code}})", + "server.startupTimeout": "Isteklo je vrijeme za pokretanje servera nakon {{seconds}} sekundi", "remote.connected": "Kilo Remote: Povezano", "remote.connecting": "Kilo Remote: Povezivanje\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts index 59881a81fec..9bd6d68e93d 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "CLI-processen afsluttede med kode {{code}} før serveren startede", - "server.startupTimeout": "Serverens opstartstid udløb efter {{seconds}} sekunder", + "server.processExited": "CLI-processen afsluttedes med kode {{code}}, før serveren startede", + "server.processSignaled": "CLI-processen blev afbrudt af signal {{signal}}, før serveren startede", + "server.spawnFailed": "Kunne ikke starte CLI-binærfil ({{code}})", + "server.startupTimeout": "Timeout for serverstart efter {{seconds}} sekunder", "remote.connected": "Kilo Remote: Forbundet", "remote.connecting": "Kilo Remote: Forbinder\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts index df45e47ea27..fc3d7b8d5e0 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts @@ -1,5 +1,8 @@ export const dict = { "server.processExited": "Der CLI-Prozess wurde mit dem Code {{code}} beendet, bevor der Server gestartet wurde", + "server.processSignaled": + "Der CLI-Prozess wurde durch das Signal {{signal}} beendet, bevor der Server gestartet wurde", + "server.spawnFailed": "Fehler beim Starten der CLI-Binärdatei ({{code}})", "server.startupTimeout": "Zeitüberschreitung beim Serverstart nach {{seconds}} Sekunden", "remote.connected": "Kilo Remote: Verbunden", "remote.connecting": "Kilo Remote: Verbindung wird hergestellt\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts index 0b59af3f34f..ccecde0d245 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI process exited with code {{code}} before server started", + "server.processSignaled": "CLI process terminated by signal {{signal}} before server started", + "server.spawnFailed": "Failed to spawn CLI binary ({{code}})", "server.startupTimeout": "Server startup timeout after {{seconds}} seconds", "remote.connected": "Kilo Remote: Connected", "remote.connecting": "Kilo Remote: Connecting\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts index a744fe33455..6491eed1e8e 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "El proceso de la CLI finalizó con el código {{code}} antes de que se iniciara el servidor", - "server.startupTimeout": "Tiempo de espera de inicio del servidor agotado después de {{seconds}} segundos", + "server.processExited": "El proceso CLI finalizó con el código {{code}} antes de que se iniciara el servidor", + "server.processSignaled": "El proceso CLI fue terminado por la señal {{signal}} antes de que se iniciara el servidor", + "server.spawnFailed": "Error al generar el binario CLI ({{code}})", + "server.startupTimeout": "Tiempo de espera agotado para el inicio del servidor después de {{seconds}} segundos", "remote.connected": "Kilo Remote: Conectado", "remote.connecting": "Kilo Remote: Conectando\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts index 3aa4dc74830..19729aa5c71 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "فرآیند CLI با کد {{code}} قبل از راه‌اندازی سرور خاتمه یافت", + "server.processExited": "فرایند CLI قبل از شروع سرور با کد {{code}} خارج شد", + "server.processSignaled": "فرایند CLI با سیگنال {{signal}} قبل از شروع سرور متوقف شد", + "server.spawnFailed": "اجرای فایل باینری CLI ناموفق بود ({{code}})", "server.startupTimeout": "زمان راه‌اندازی سرور پس از {{seconds}} ثانیه به پایان رسید", "remote.connected": "Kilo Remote: متصل شد", - "remote.connecting": "Kilo Remote: در حال اتصال…", + "remote.connecting": "Kilo Remote: در حال اتصال\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts index 03f7a3024e9..dafcad4e76d 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "Le processus CLI s'est terminé avec le code {{code}} avant le démarrage du serveur", - "server.startupTimeout": "Délai de démarrage du serveur dépassé après {{seconds}} secondes", - "remote.connected": "Kilo Remote\u00a0: Connecté", - "remote.connecting": "Kilo Remote\u00a0: Connexion\u2026", + "server.processExited": "Le processus CLI a quitté avec le code {{code}} avant le démarrage du serveur", + "server.processSignaled": "Le processus CLI a été arrêté par le signal {{signal}} avant le démarrage du serveur", + "server.spawnFailed": "Échec du lancement du binaire CLI ({{code}})", + "server.startupTimeout": "Délai d'attente du démarrage du serveur dépassé après {{seconds}} secondes", + "remote.connected": "Kilo Remote : Connecté", + "remote.connecting": "Kilo Remote : Connexion en cours\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts index f032870a431..dc4b1d05ffe 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "Il processo CLI è uscito con codice {{code}} prima dell'avvio del server", + "server.processExited": "Il processo CLI è terminato con il codice {{code}} prima dell'avvio del server", + "server.processSignaled": "Il processo CLI è stato terminato dal segnale {{signal}} prima dell'avvio del server", + "server.spawnFailed": "Impossibile avviare il binario CLI ({{code}})", "server.startupTimeout": "Timeout di avvio del server dopo {{seconds}} secondi", - "remote.connected": "Kilo Remote: connesso", - "remote.connecting": "Kilo Remote: connessione...", + "remote.connected": "Kilo Remote: Connesso", + "remote.connecting": "Kilo Remote: Connessione in corso\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts index 58e9c3ee9bd..966c46fc3ca 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "サーバーが起動する前に、CLI プロセスがコード {{code}} で終了しました", + "server.processSignaled": "サーバーが起動する前に、CLI プロセスがシグナル {{signal}} で終了しました", + "server.spawnFailed": "CLI バイナリの起動に失敗しました ({{code}})", "server.startupTimeout": "サーバーの起動が {{seconds}} 秒後にタイムアウトしました", "remote.connected": "Kilo Remote: 接続済み", "remote.connecting": "Kilo Remote: 接続中\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts index c48eed062e9..61ca3601dde 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "서버가 시작되기 전에 CLI 프로세스가 코드 {{code}}로 종료되었습니다", - "server.startupTimeout": "{{seconds}}초 후 서버 시작 시간이 초과되었습니다", + "server.processExited": "서버가 시작되기 전에 CLI 프로세스가 코드 {{code}}(으)로 종료되었습니다", + "server.processSignaled": "서버가 시작되기 전에 CLI 프로세스가 신호 {{signal}}(으)로 종료되었습니다", + "server.spawnFailed": "CLI 바이너리를 생성하지 못했습니다 ({{code}})", + "server.startupTimeout": "{{seconds}}초 후 서버 시작 시간 초과", "remote.connected": "Kilo Remote: 연결됨", "remote.connecting": "Kilo Remote: 연결 중\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts index c9cfc6a96e2..87336c1c3ba 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI-proces is afgesloten met code {{code}} voordat de server is gestart", + "server.processSignaled": "CLI-proces is beëindigd door signaal {{signal}} voordat de server is gestart", + "server.spawnFailed": "Kan CLI-binair bestand niet starten ({{code}})", "server.startupTimeout": "Time-out bij opstarten van server na {{seconds}} seconden", "remote.connected": "Kilo Remote: Verbonden", "remote.connecting": "Kilo Remote: Verbinden\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts index a24895f79e4..e1148e928e4 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI-prosessen avsluttet med kode {{code}} før serveren startet", + "server.processSignaled": "CLI-prosessen ble avsluttet av signal {{signal}} før serveren startet", + "server.spawnFailed": "Kunne ikke starte CLI-binærfil ({{code}})", "server.startupTimeout": "Tidsavbrudd for serveroppstart etter {{seconds}} sekunder", "remote.connected": "Kilo Remote: Tilkoblet", "remote.connecting": "Kilo Remote: Kobler til\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts index 7f7415c1378..7f5c54e96f5 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts @@ -1,5 +1,7 @@ export const dict = { - "server.processExited": "Proces CLI zakończył się z kodem {{code}} przed uruchomieniem serwera", + "server.processExited": "Proces CLI zakończył działanie z kodem {{code}} przed uruchomieniem serwera", + "server.processSignaled": "Proces CLI został przerwany przez sygnał {{signal}} przed uruchomieniem serwera", + "server.spawnFailed": "Nie udało się uruchomić pliku binarnego CLI ({{code}})", "server.startupTimeout": "Przekroczono limit czasu uruchamiania serwera po {{seconds}} sekundach", "remote.connected": "Kilo Remote: Połączono", "remote.connecting": "Kilo Remote: Łączenie\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts index b9e721e60bf..287091788be 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "Процесс CLI завершился с кодом {{code}} до запуска сервера", + "server.processSignaled": "Процесс CLI был завершен сигналом {{signal}} до запуска сервера", + "server.spawnFailed": "Не удалось запустить исполняемый файл CLI ({{code}})", "server.startupTimeout": "Время ожидания запуска сервера истекло через {{seconds}} секунд", "remote.connected": "Kilo Remote: Подключено", "remote.connecting": "Kilo Remote: Подключение\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts index aff4ea8f378..05be10cd554 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "กระบวนการ CLI ออกด้วยรหัส {{code}} ก่อนที่เซิร์ฟเวอร์จะเริ่มทำงาน", - "server.startupTimeout": "หมดเวลาการเริ่มต้นเซิร์ฟเวอร์หลังจาก {{seconds}} วินาที", + "server.processExited": "กระบวนการ CLI ออกด้วยรหัส {{code}} ก่อนที่เซิร์ฟเวอร์จะเริ่มต้น", + "server.processSignaled": "กระบวนการ CLI ถูกยุติโดยสัญญาณ {{signal}} ก่อนที่เซิร์ฟเวอร์จะเริ่มต้น", + "server.spawnFailed": "ไม่สามารถเริ่มต้นไบนารี CLI ({{code}})", + "server.startupTimeout": "หมดเวลาเริ่มต้นเซิร์ฟเวอร์หลังจาก {{seconds}} วินาที", "remote.connected": "Kilo Remote: เชื่อมต่อแล้ว", "remote.connecting": "Kilo Remote: กำลังเชื่อมต่อ\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts index 6be397e7a36..4530412f19a 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts @@ -1,5 +1,7 @@ export const dict = { - "server.processExited": "CLI işlemi sunucu başlamadan önce {{code}} koduyla çıktı", + "server.processExited": "CLI işlemi sunucu başlatılmadan önce {{code}} koduyla çıktı", + "server.processSignaled": "CLI işlemi sunucu başlatılmadan önce {{signal}} sinyali ile sonlandırıldı", + "server.spawnFailed": "CLI ikili dosyası başlatılamadı ({{code}})", "server.startupTimeout": "{{seconds}} saniye sonra sunucu başlatma zaman aşımı", "remote.connected": "Kilo Remote: Bağlandı", "remote.connecting": "Kilo Remote: Bağlanıyor\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts index 315d12fa96d..5b89093555e 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts @@ -1,6 +1,8 @@ export const dict = { "server.processExited": "Процес CLI завершився з кодом {{code}} до запуску сервера", - "server.startupTimeout": "Час очікування запуску сервера вичерпано після {{seconds}} секунд", + "server.processSignaled": "Процес CLI був завершений сигналом {{signal}} до запуску сервера", + "server.spawnFailed": "Не вдалося запустити двійковий файл CLI ({{code}})", + "server.startupTimeout": "Час очікування запуску сервера минув через {{seconds}} секунд", "remote.connected": "Kilo Remote: Підключено", "remote.connecting": "Kilo Remote: Підключення\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts index 72160d167fe..b050faebf43 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "在服务器启动之前,CLI 进程已退出,代码为 {{code}}", - "server.startupTimeout": "服务器启动在 {{seconds}} 秒后超时", + "server.processExited": "CLI 进程在服务器启动前以代码 {{code}} 退出", + "server.processSignaled": "CLI 进程在服务器启动前被信号 {{signal}} 终止", + "server.spawnFailed": "生成 CLI 二进制文件失败 ({{code}})", + "server.startupTimeout": "{{seconds}} 秒后服务器启动超时", "remote.connected": "Kilo Remote: 已连接", "remote.connecting": "Kilo Remote: 正在连接\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts index 61baae8020f..799bb17a8f6 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "在伺服器啟動之前,CLI 處理程序已退出,代碼為 {{code}}", - "server.startupTimeout": "伺服器啟動在 {{seconds}} 秒後逾時", + "server.processExited": "CLI 程序在伺服器啟動前以代碼 {{code}} 退出", + "server.processSignaled": "CLI 程序在伺服器啟動前被信號 {{signal}} 終止", + "server.spawnFailed": "生成 CLI 二進位檔案失敗 ({{code}})", + "server.startupTimeout": "{{seconds}} 秒後伺服器啟動逾時", "remote.connected": "Kilo Remote: 已連線", "remote.connecting": "Kilo Remote: 正在連線\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index 76cb5125719..df6f8121953 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -17,7 +17,7 @@ export interface ServerInstance { const STARTUP_TIMEOUT_SECONDS = 30 type WorkspaceFolderLike = { uri: { fsPath: string } } -type ServerExitListener = (code: number | null) => void +type ServerExitListener = (code: number | null, signal: NodeJS.Signals | null) => void export function resolveServerCwd(folders: readonly WorkspaceFolderLike[] | undefined, storage: string): string { return folders?.[0]?.uri.fsPath ?? storage @@ -182,25 +182,36 @@ export class ServerManager { stderrLines.push(errorOutput) }) - serverProcess.on("error", (error) => { - console.error("[Kilo New] ServerManager: ❌ Process error:", error) + serverProcess.on("error", (err: NodeJS.ErrnoException) => { + console.error("[Kilo New] ServerManager: ❌ Process error:", err) if (!resolved) { - reject(error) + const spawnErr = err as NodeJS.ErrnoException & { spawnargs?: string[] } + const code = err.code || err.name || "UNKNOWN" + const header = t("server.spawnFailed", { code }) + const lines = [ + `Error: ${err.message}`, + ...(err.code ? [`Code: ${err.code}`] : []), + ...(err.errno !== undefined ? [`Errno: ${err.errno}`] : []), + ...(err.syscall ? [`Syscall: ${err.syscall}`] : []), + ...(err.path ? [`Path: ${err.path}`] : []), + ...(Array.isArray(spawnErr.spawnargs) ? [`Spawn args: ${JSON.stringify(spawnErr.spawnargs)}`] : []), + ] + const { userMessage, userDetails } = toErrorMessage(header, [...lines, ...stderrLines], cliPath) + reject(new ServerStartupError(userMessage, userDetails)) } }) - serverProcess.on("exit", (code) => { - console.log("[Kilo New] ServerManager: 🛑 Process exited with code:", code) + serverProcess.on("exit", (code, signal) => { + console.log("[Kilo New] ServerManager: 🛑 Process exited:", { code, signal }) if (this.instance?.process === serverProcess) { this.instance = null - this.onExit?.(code) + this.onExit?.(code, signal) } if (!resolved) { - const { userMessage, userDetails } = toErrorMessage( - t("server.processExited", { code: code ?? "null" }), - stderrLines, - cliPath, - ) + const msg = signal + ? t("server.processSignaled", { signal }) + : t("server.processExited", { code: code ?? "unknown" }) + const { userMessage, userDetails } = toErrorMessage(msg, stderrLines, cliPath) reject(new ServerStartupError(userMessage, userDetails)) } }) diff --git a/packages/kilo-vscode/tests/unit/server-manager-utils.test.ts b/packages/kilo-vscode/tests/unit/server-manager-utils.test.ts index ac811c0532b..c62e3b51eb5 100644 --- a/packages/kilo-vscode/tests/unit/server-manager-utils.test.ts +++ b/packages/kilo-vscode/tests/unit/server-manager-utils.test.ts @@ -295,6 +295,34 @@ describe("toErrorMessage", () => { const result = toErrorMessage("startup failed", ["some output"]) expect(result.error).toBe("startup failed") }) + + it("formats structured spawn error details with syscall, errno, and args", () => { + const spawnLines = [ + "Error: spawn UNKNOWN", + "Code: UNKNOWN", + "Errno: -86", + "Syscall: spawn", + "Path: /path/to/bin/kilo", + 'Spawn args: ["serve","--port","0"]', + ] + const result = toErrorMessage("Failed to spawn CLI binary (UNKNOWN)", spawnLines, "/path/to/bin/kilo") + expect(result.userMessage).toBe("spawn UNKNOWN") + expect(result.userDetails).toContain("CLI path: /path/to/bin/kilo") + expect(result.userDetails).toContain("Failed to spawn CLI binary (UNKNOWN)") + expect(result.userDetails).toContain("Syscall: spawn") + expect(result.userDetails).toContain("Errno: -86") + }) + + it("handles signal termination without stderr lines cleanly", () => { + const result = toErrorMessage( + "CLI process terminated by signal SIGSEGV before server started", + [], + "/path/to/bin/kilo", + ) + expect(result.userMessage).toBe("CLI process terminated by signal SIGSEGV before server started") + expect(result.userDetails).toContain("CLI path: /path/to/bin/kilo") + expect(result.userDetails).toContain("CLI process terminated by signal SIGSEGV before server started") + }) }) describe("server workspace helpers", () => { From c514c08749dbb72fe4db64c12c75218997b769ff Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 7 Aug 2026 16:53:58 +0200 Subject: [PATCH 17/39] fix(ui): prevent snap-to-bottom and flickering during upward session scroll --- .changeset/fix-session-scroll-flicker.md | 6 ++ .../src/hooks/create-auto-scroll.test.tsx | 46 +++++++++++ .../kilo-ui/src/hooks/create-auto-scroll.tsx | 79 +++++++++++-------- .../kilo-ui/src/hooks/scroll-user-activity.ts | 7 +- 4 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 .changeset/fix-session-scroll-flicker.md diff --git a/.changeset/fix-session-scroll-flicker.md b/.changeset/fix-session-scroll-flicker.md new file mode 100644 index 00000000000..7fe8232c1e5 --- /dev/null +++ b/.changeset/fix-session-scroll-flicker.md @@ -0,0 +1,6 @@ +--- +"@kilocode/kilo-ui": patch +"kilo-code": patch +--- + +Fix flickering and sticky scrolling when scrolling up in Agent Manager and chat sessions. diff --git a/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx b/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx index 08df06b7fce..6f1ec9e3542 100644 --- a/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx +++ b/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx @@ -216,4 +216,50 @@ describe("createAutoScroll non-scrollable layouts", () => { expect(ctx.el.scrollTop).toBe(300) ctx.dispose() }) + + test("does not snap to bottom on content resize after user scrolls up while idle", () => { + const ctx = setup({ working: false }) + ctx.el.scrollHeight = 1000 + ctx.el.clientHeight = 200 + ctx.el.scrollTop = 800 // at bottom + + // User wheels up + const event = new FakeWheelEvent(-50, ctx.el) + ctx.el.fire("wheel", event as unknown as Event) + ctx.el.scrollTop = 750 + ctx.scroll.handleScroll() + + expect(ctx.scroll.userScrolled()).toBe(true) + + // Virtual list re-measures / resizes content + ctx.el.scrollHeight = 1100 + ctx.resize() + + // Must NOT snap to bottom (1100), must remain at user scroll position (750) + expect(ctx.scroll.userScrolled()).toBe(true) + expect(ctx.el.scrollTop).toBe(750) + ctx.dispose() + }) + + test("does not snap to bottom when dragging scrollbar up while idle", () => { + const ctx = setup({ working: false }) + ctx.el.scrollHeight = 1000 + ctx.el.clientHeight = 200 + ctx.el.scrollTop = 800 // at bottom + + // User presses pointerdown on scrollbar and drags up + ctx.el.fire("pointerdown", new Event("pointerdown")) + ctx.el.scrollTop = 600 + ctx.scroll.handleScroll() + + expect(ctx.scroll.userScrolled()).toBe(true) + + // Content resize during drag + ctx.el.scrollHeight = 1050 + ctx.resize() + + expect(ctx.scroll.userScrolled()).toBe(true) + expect(ctx.el.scrollTop).toBe(600) + ctx.dispose() + }) }) diff --git a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx index a84b246f572..8bfca603076 100644 --- a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx +++ b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx @@ -14,6 +14,7 @@ export interface AutoScrollOptions { working: () => boolean onUserInteracted?: () => void bottomThreshold?: number + overflowAnchor?: "none" | "auto" | "dynamic" } export function createAutoScroll(options: AutoScrollOptions) { @@ -111,51 +112,39 @@ export function createAutoScroll(options: AutoScrollOptions) { } if (!store.userScrolled && !input) { - // Only explicit user input can pause following. Treat unclassified - // scroll events from virtualization or layout changes as programmatic. if (userActivity.isRecent()) { stop() - } else { - bottom() + return } + if (stopTimer) clearTimeout(stopTimer) + stopTimer = setTimeout(() => { + stopTimer = undefined + if (!scroll) return + if (distanceFromBottom(scroll) < threshold()) return + stop() + }, DEBOUNCE_MS) return } - // Debounce to avoid layout-induced scroll shifts (e.g. images loading, - // virtual-list reflows) from incorrectly breaking auto-follow. - if (stopTimer) clearTimeout(stopTimer) - stopTimer = setTimeout(() => { - stopTimer = undefined - if (!scroll) return - if (distanceFromBottom(scroll) < threshold()) return - stop() - }, DEBOUNCE_MS) + stop() } const onContentResize = () => { - if (scroll && !canScroll(scroll)) return - if (!active()) { - if (!store.userScrolled && scroll && distanceFromBottom(scroll) > threshold()) { - bottom() - return - } - return - } - if (store.userScrolled) { - return - } - // Virtualized lists (virtua) re-measure items during user scroll, firing - // resize events that race ahead of handleScroll's DEBOUNCE_MS window. - // If the user just interacted with the scroller and is no longer near - // the bottom, treat the resize as a layout reflow on top of their - // scroll — pause auto-follow instead of snapping back to the bottom. - if (scroll && userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { + if (!scroll || !canScroll(scroll)) return + if (store.userScrolled) return + + if (userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { stop() return } - // ResizeObserver fires after layout, before paint. - // Keep the bottom locked in the same frame to avoid visible - // "jump up then catch up" artifacts while streaming content. + + if (!active()) { + if (!userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { + bottom() + } + return + } + follow() } @@ -173,6 +162,15 @@ export function createAutoScroll(options: AutoScrollOptions) { createResizeObserver(() => store.contentRef, onContentResize) createResizeObserver(() => store.scrollRef, onViewportResize) + createEffect( + on( + () => store.userScrolled, + () => { + if (scroll) updateOverflowAnchor(scroll) + }, + ), + ) + createEffect( on(options.working, (working: boolean) => { settling = false @@ -195,6 +193,19 @@ export function createAutoScroll(options: AutoScrollOptions) { // Lifecycle // --------------------------------------------------------------------------- + const updateOverflowAnchor = (el: HTMLElement) => { + const mode = options.overflowAnchor ?? "none" + if (mode === "none") { + el.style.overflowAnchor = "none" + return + } + if (mode === "auto") { + el.style.overflowAnchor = "auto" + return + } + el.style.overflowAnchor = store.userScrolled ? "auto" : "none" + } + const setScroll = (el: HTMLElement | undefined) => { if (cleanup) { cleanup() @@ -206,7 +217,7 @@ export function createAutoScroll(options: AutoScrollOptions) { if (!el) return - el.style.overflowAnchor = "auto" + updateOverflowAnchor(el) cleanup = userActivity.listen(el) } diff --git a/packages/kilo-ui/src/hooks/scroll-user-activity.ts b/packages/kilo-ui/src/hooks/scroll-user-activity.ts index a0b640c32d0..a8a33fc8a66 100644 --- a/packages/kilo-ui/src/hooks/scroll-user-activity.ts +++ b/packages/kilo-ui/src/hooks/scroll-user-activity.ts @@ -18,13 +18,16 @@ export const createUserActivity = (options: UserActivityOptions) => { // do not get mistaken for the user leaving auto-follow mode. const mark = (event: Event) => { if (!isPotentialScrollInput(event)) return + if (scroll && scroll.scrollHeight - scroll.clientHeight <= 1) return marked = true time = performance.now() } const handleWheel = (event: WheelEvent) => { - if (event.deltaY >= 0 || !scroll || scroll.scrollTop <= 0) return - time = performance.now() + if (!isPotentialScrollInput(event)) return + if (!scroll || scroll.scrollHeight - scroll.clientHeight <= 1) return + mark(event) + if (event.deltaY >= 0 || scroll.scrollTop <= 0) return options.onWheelUp() } From 1541da3deb3c8df7e658938de64c87bb709d2564 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 7 Aug 2026 16:58:02 +0200 Subject: [PATCH 18/39] fix(vscode): handle ServerStartupError in MarketplacePanelProvider state change and warn on exit --- packages/kilo-vscode/src/MarketplacePanelProvider.ts | 10 +++++++++- .../src/services/cli-backend/server-manager.ts | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/src/MarketplacePanelProvider.ts b/packages/kilo-vscode/src/MarketplacePanelProvider.ts index fc62bf46b9b..32fd768b236 100644 --- a/packages/kilo-vscode/src/MarketplacePanelProvider.ts +++ b/packages/kilo-vscode/src/MarketplacePanelProvider.ts @@ -123,7 +123,15 @@ export class MarketplacePanelProvider implements vscode.Disposable { ) this.subscriptions.push( this.connection.onStateChange((state, err) => { - this.post({ type: "connectionState", state, ...(err ? { error: err.message } : {}) }) + this.post({ + type: "connectionState", + state, + ...(err ? { error: err.message } : {}), + ...(err instanceof ServerStartupError && { + userMessage: err.userMessage, + userDetails: err.userDetails, + }), + }) if (state === "connected") void this.sync(false) }), this.connection.onLanguageChanged((locale) => this.post({ type: "languageChanged", locale })), diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index df6f8121953..9edea2476c8 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -202,7 +202,7 @@ export class ServerManager { }) serverProcess.on("exit", (code, signal) => { - console.log("[Kilo New] ServerManager: 🛑 Process exited:", { code, signal }) + console.warn("[Kilo New] ServerManager: 🛑 Process exited:", { code, signal }) if (this.instance?.process === serverProcess) { this.instance = null this.onExit?.(code, signal) From 82be537f18985223d23612477fa3f4ce30ed57aa Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Fri, 7 Aug 2026 17:07:31 +0200 Subject: [PATCH 19/39] test(cli): verify suggestion auto-dismissal in attached run session --- .../opencode/test/cli/run/run-process.test.ts | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/opencode/test/cli/run/run-process.test.ts b/packages/opencode/test/cli/run/run-process.test.ts index 70fcbe5bea8..5841afa6043 100644 --- a/packages/opencode/test/cli/run/run-process.test.ts +++ b/packages/opencode/test/cli/run/run-process.test.ts @@ -5,6 +5,7 @@ // `KILO_CONFIG_CONTENT` providing the test provider config inline. import { describe, expect } from "bun:test" import { Effect } from "effect" +import { createKiloClient } from "@kilocode/sdk/v2" // kilocode_change import { reply } from "../../lib/llm-server" import { cliIt } from "../../lib/cli-process" @@ -432,10 +433,20 @@ describe("opencode run (non-interactive subprocess)", () => { 60_000, ) - cliIt.concurrent( - "kilo run auto-dismisses suggestion and exits cleanly if suggest tool is invoked", + cliIt.live( + "kilo run auto-dismisses suggestion and exits cleanly if suggest tool is invoked in attached session", ({ llm, opencode }) => Effect.gen(function* () { + const server = yield* opencode.serve() + const client = createKiloClient({ baseUrl: server.url }) + const session = yield* Effect.promise(() => + client.session.create({ + permission: [{ permission: "suggest", action: "allow", pattern: "*" }], + }), + ) + const sessionID = session.data?.id + expect(sessionID).toBeDefined() + yield* llm.push( reply().tool("suggest", { suggest: "Run checks?", @@ -443,12 +454,17 @@ describe("opencode run (non-interactive subprocess)", () => { }), ) yield* llm.text("completed after dismissal") + const result = yield* opencode.run("do work", { - permission: { suggest: "allow" }, - extraArgs: ["--auto"], + extraArgs: ["--attach", server.url, "--session", sessionID!, "--auto"], }) opencode.expectExit(result, 0) - expect(result.stdout).toContain("completed after dismissal") + + const messages = yield* Effect.promise(() => client.session.messages({ sessionID: sessionID! })) + const assistant = messages.data?.findLast((m) => m.info.role === "assistant") + const toolPart = assistant?.parts.find((p) => p.type === "tool" && p.tool === "suggest") + expect(toolPart).toBeDefined() + expect((toolPart as any)?.state?.metadata?.dismissed).toBe(true) }), 60_000, ) From f1e5ed30e48aec807e675a8a46dc304106aebfc4 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 7 Aug 2026 17:17:35 +0200 Subject: [PATCH 20/39] refactor(ui): eliminate stop debounce timer in favor of synchronous scroll state --- .../kilo-ui/src/hooks/create-auto-scroll.tsx | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx index 8bfca603076..09ab785423c 100644 --- a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx +++ b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx @@ -4,7 +4,6 @@ import { createResizeObserver } from "@solid-primitives/resize-observer" import { canScroll, distanceFromBottom } from "./auto-scroll" import { createUserActivity } from "./scroll-user-activity" -const DEBOUNCE_MS = 100 // Grace window after a real pointer/key/touch interaction during which a // ResizeObserver or non-user scroll event must not snap the view back to the // bottom. Upward wheel intent pauses immediately in its capture handler. @@ -25,7 +24,6 @@ export function createAutoScroll(options: AutoScrollOptions) { let scroll: HTMLElement | undefined let settling = false let settleTimer: ReturnType | undefined - let stopTimer: ReturnType | undefined let cleanup: (() => void) | undefined const [store, setStore] = createStore({ @@ -101,7 +99,7 @@ export function createAutoScroll(options: AutoScrollOptions) { const handleScroll = () => { if (!scroll) return - const input = userActivity.consumeScroll() + userActivity.consumeScroll() const distance = distanceFromBottom(scroll) if (!canScroll(scroll)) return @@ -111,21 +109,6 @@ export function createAutoScroll(options: AutoScrollOptions) { return } - if (!store.userScrolled && !input) { - if (userActivity.isRecent()) { - stop() - return - } - if (stopTimer) clearTimeout(stopTimer) - stopTimer = setTimeout(() => { - stopTimer = undefined - if (!scroll) return - if (distanceFromBottom(scroll) < threshold()) return - stop() - }, DEBOUNCE_MS) - return - } - stop() } @@ -223,7 +206,6 @@ export function createAutoScroll(options: AutoScrollOptions) { onCleanup(() => { if (settleTimer) clearTimeout(settleTimer) - if (stopTimer) clearTimeout(stopTimer) if (cleanup) cleanup() }) From 13ec28674f445c0e59515c293ac9173223b4d9a8 Mon Sep 17 00:00:00 2001 From: Remon Oldenbeuving Date: Fri, 7 Aug 2026 17:49:15 +0200 Subject: [PATCH 21/39] feat(docs): Add detailed instructions on how to install the Slack bot --- .../pages/code-with-ai/platforms/slack.md | 14 +- packages/kilo-docs/pnpm-lock.yaml | 2786 +++++++++++++++++ packages/kilo-docs/pnpm-workspace.yaml | 2 + .../slack/slackbot-integrations-2.webp | Bin 0 -> 100894 bytes .../connect/slack/slackbot-integrations.webp | Bin 0 -> 142656 bytes .../slack/slackbot-slack-authorize.webp | Bin 0 -> 109408 bytes 6 files changed, 2801 insertions(+), 1 deletion(-) create mode 100644 packages/kilo-docs/pnpm-lock.yaml create mode 100644 packages/kilo-docs/pnpm-workspace.yaml create mode 100644 packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp create mode 100644 packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp create mode 100644 packages/kilo-docs/public/img/connect/slack/slackbot-slack-authorize.webp diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/slack.md b/packages/kilo-docs/pages/code-with-ai/platforms/slack.md index 20691d8c381..469b135b7fd 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/slack.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/slack.md @@ -110,7 +110,19 @@ Before using Kilo for Slack: ## Setup -To install Kilo for Slack, go to the Integrations menu in the sidebar at [app.kilo.ai](https://app.kilo.ai) and set up the Slack integration. +To install Kilo for Slack, go to the Integrations menu in the sidebar at [app.kilo.ai](https://app.kilo.ai) and set up the Slack integration: + +{% image src="/docs/img/connect/slack/slackbot-integrations.webp" alt="Kilo Integrations page where you install the Slack integration" width="800" /%} + +Then hit the Configure button, which will take you to this page: +{% image src="/docs/img/connect/slack/slackbot-integrations-2.webp" alt="Kilo Integrations page where you install the Slack integration, step 2" width="800" /%} + +Once you press "Connect with Slack", the Slack OAuth authorization page appears: +{% image src="/docs/img/connect/slack/slackbot-slack-authorize.webp" alt="Slack OAuth authorization page" width="800" /%} + +Once this is completed you're good to go! + +To make proper use of all of the Kilo Slack bot's capabilities, it's important that you also connect your source control provider with Kilo: | Platform | Integration Type | Details | |---|---|---| diff --git a/packages/kilo-docs/pnpm-lock.yaml b/packages/kilo-docs/pnpm-lock.yaml new file mode 100644 index 00000000000..6ca1dd11e8d --- /dev/null +++ b/packages/kilo-docs/pnpm-lock.yaml @@ -0,0 +1,2786 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@docsearch/css': + specifier: ^4 + version: 4.7.0 + '@docsearch/js': + specifier: ^4 + version: 4.7.0 + '@markdoc/markdoc': + specifier: ^0.5.4 + version: 0.5.9(@types/react@19.2.18)(react@19.2.8) + '@markdoc/next.js': + specifier: ^0.5.0 + version: 0.5.0(@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8))(next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + '@vscode/codicons': + specifier: ^0.0.44 + version: 0.0.44 + '@xyflow/react': + specifier: 12.10.2 + version: 12.10.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + js-yaml: + specifier: ^4.1.0 + version: 4.3.1 + mermaid: + specifier: 11.15.0 + version: 11.15.0 + next: + specifier: ^16.1.5 + version: 16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + posthog-js: + specifier: ^1.335.3 + version: 1.413.3 + prismjs: + specifier: ^1.30.0 + version: 1.30.0 + react: + specifier: ^19.2.4 + version: 19.2.8 + react-dom: + specifier: ^19.2.4 + version: 19.2.8(react@19.2.8) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.3.3 + '@types/node': + specifier: ^25.0.10 + version: 25.9.5 + '@types/react': + specifier: ^19.2.9 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.4(@types/react@19.2.18) + autoprefixer: + specifier: ^10.4.23 + version: 10.5.4(postcss@8.5.26) + postcss: + specifier: ^8.5.6 + version: 8.5.26 + tailwindcss: + specifier: ^4.1.18 + version: 4.3.3 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: 4.1.0 + version: 4.1.0(@types/node@25.9.5)(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + + '@docsearch/css@4.7.0': + resolution: {integrity: sha512-Sk5xkdRFeE7PeWjG9l4AfTwdvMfr9wHiwNNCpHXT4v4SNyNMKdHGvEILc31BgaVFGDDNbv5u/a73tofRiwbEZw==} + + '@docsearch/js@4.7.0': + resolution: {integrity: sha512-x5lCqu1tetgsJFkjQ6VSocbHldsRkGEgwg5N98Vx21sq/V5wcmj4u226PY9k+TEpIgQ772zlYbPLTPicWyGnpA==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.4': + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@markdoc/markdoc@0.5.9': + resolution: {integrity: sha512-req3Wpi1xXIs7C+n2nsKv7oGeC0xREkpd+D+JRGZ/pIMXEuQSlT+s/I760fqdmiysvkx10EC3MGlScokRRcaSQ==} + engines: {node: '>=14.7.0'} + peerDependencies: + '@types/react': '*' + react: '*' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + '@markdoc/next.js@0.5.0': + resolution: {integrity: sha512-gzWvRc2dlxArQn1mDS4SHed46vkAoKBbXkELihQRYv1eVSA0u9eoNEdJXNuBL4Ajn9j2dPpwZp6nAVjN4nzZlA==} + peerDependencies: + '@markdoc/markdoc': '*' + next: '*' + react: '*' + + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + + '@next/env@16.3.0': + resolution: {integrity: sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==} + + '@next/swc-darwin-arm64@16.3.0': + resolution: {integrity: sha512-55hpqq18bEVAlxedlTt3tFqZmKg2nUXT1kn1G/BGEy0R13h3LwtwHPVzzjG6P4LLeOHE32PFDQUVaJEWvBEZBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.0': + resolution: {integrity: sha512-SOi96kSaF5T+0wW4koiM1bWzSPwjzTesC1p3df+FjdOi5LIQkBK/blxh7HdoKnNuI4PURF1OO7TZqtfnbWDSgw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.0': + resolution: {integrity: sha512-P0gZAoPMF4dyTRzhmkV4PrqVzSOB6t4mC1oI3c4dqijJ+OVEVx5clIXAKR4/uQpsqw2KKM/0D5tVumcR2r5blg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.3.0': + resolution: {integrity: sha512-tXXGKJw0m37O0eKJARVTX/TheKPhz0QFVtVVZXmOig+9YKLQOSP6hvf2pxv5DO7CLEJyTHx3Pg043CDQkv1G4Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.3.0': + resolution: {integrity: sha512-pjGxK5EY7yWml78ALejFkWmgHsU7wbFQrISiugpH6FbUJhgEvw3xFZ/EBAtLl7QtL0WdQKiG9eWJ3mOKGTukHw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.3.0': + resolution: {integrity: sha512-sjo++Xx+lomlPs3HRsHWhVDyGG6ms1kGW5EtHLERdII8AyG1i+f6aq68xHREO6AEMlhjTNEWBSmfJfqm9orf7g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.3.0': + resolution: {integrity: sha512-C5JSgiO54wURdaxdEUIXqkz04uMqC9UmPX1gtDrV/5Tf1UowdWYI8uA5hfFbPolTlp0q4KZ60xlHePNibf0VIw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.0': + resolution: {integrity: sha512-fDOggsweNb5SSw0ZKVk6U+gxSyGFFlIBY/LBc1r8GUj4u/6t6oArL+Pmkg0MBnsgR+KkdsURilVH4F3GXUGepA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.143.0': + resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} + + '@posthog/browser-common@0.4.0': + resolution: {integrity: sha512-W9DCGVks15docUMPvJ2nd8NS16Gn74bsGWuaeg31beEKFSjdW8wvnQ1ETY6WSql5pYxZb3GdJmEUZVVstKSrBQ==} + + '@posthog/core@1.46.9': + resolution: {integrity: sha512-EXO6y5ih+jBkTCpUCuYgQmajuDZuvy6vMvflkub6pLQyi0GPlCPWVSvZZkOeQw9e2MxoD5GteeGCt9R8+UJ/yQ==} + + '@posthog/types@1.402.2': + resolution: {integrity: sha512-ZZTiS4dLwF4/D0YTzS3gGSLFnhuNOY5yu4d9VGS9trGe5GW6FjIXo20p18K5BPW2RZSYSld8sdnSAY3AUXleUQ==} + + '@rolldown/binding-android-arm64@1.2.3': + resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.3': + resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.3': + resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.3': + resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.3': + resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.3': + resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.3': + resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.3': + resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.3': + resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.3': + resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.3': + resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.3': + resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.1': + resolution: {integrity: sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.4': + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/linkify-it@3.0.5': + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + + '@types/markdown-it@12.2.3': + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} + + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + + '@vitest/expect@4.1.0': + resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + + '@vitest/mocker@4.1.0': + resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.0': + resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + + '@vitest/runner@4.1.0': + resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + + '@vitest/snapshot@4.1.0': + resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + + '@vitest/spy@4.1.0': + resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + + '@vitest/utils@4.1.0': + resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + + '@vscode/codicons@0.0.44': + resolution: {integrity: sha512-F7qPRumUK3EHjNdopfICLGRf3iNPoZQt+McTHAn4AlOWPB3W2kL4H0S7uqEqbyZ6rCxaeDjpAn3MCUnwTu/VJQ==} + + '@xyflow/react@12.10.2': + resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@xyflow/system@0.0.76': + resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + autoprefixer@10.5.4: + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + baseline-browser-mapping@2.11.12: + resolution: {integrity: sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001807: + resolution: {integrity: sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js@3.50.0: + resolution: {integrity: sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==} + + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.34.0: + resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + + electron-to-chromium@1.5.402: + resolution: {integrity: sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.4.9: + resolution: {integrity: sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + + mermaid@11.15.0: + resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} + + nanoid@3.3.17: + resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next@16.3.0: + resolution: {integrity: sha512-NEdGOzH+08eTXMUp9UYkA99Nhi5N6Thrhc1jgFOQgfgnGK/dA2hRwBpXep+exdFQrnwlRf/3Wixyp8lLBUpE2A==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + posthog-js@1.413.3: + resolution: {integrity: sha512-UoAymtZYVr84Ldp0ITXmlK4Q3GE8vU7GW8wC+aCput5RrKVJ8wkAPc12n3ElmKRlvLL+oD+Fotjc7n3wYG0RGQ==} + + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + + rolldown@1.2.3: + resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + + vite@8.2.1: + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.0: + resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.0 + '@vitest/browser-preview': 4.1.0 + '@vitest/browser-webdriverio': 4.1.0 + '@vitest/ui': 4.1.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + web-vitals@5.3.0: + resolution: {integrity: sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==} + + web-vitals@6.0.0: + resolution: {integrity: sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.0 + + '@braintree/sanitize-url@7.1.2': {} + + '@chevrotain/types@11.1.2': {} + + '@docsearch/css@4.7.0': {} + + '@docsearch/js@4.7.0': {} + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.4': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + import-meta-resolve: 4.2.0 + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8)': + optionalDependencies: + '@types/linkify-it': 3.0.5 + '@types/markdown-it': 12.2.3 + '@types/react': 19.2.18 + react: 19.2.8 + + '@markdoc/next.js@0.5.0(@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8))(next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)': + dependencies: + '@markdoc/markdoc': 0.5.9(@types/react@19.2.18)(react@19.2.8) + js-yaml: 4.3.1 + next: 16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + + '@mermaid-js/parser@1.2.0': + dependencies: + '@chevrotain/types': 11.1.2 + + '@next/env@16.3.0': {} + + '@next/swc-darwin-arm64@16.3.0': + optional: true + + '@next/swc-darwin-x64@16.3.0': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.0': + optional: true + + '@next/swc-linux-arm64-musl@16.3.0': + optional: true + + '@next/swc-linux-x64-gnu@16.3.0': + optional: true + + '@next/swc-linux-x64-musl@16.3.0': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.0': + optional: true + + '@next/swc-win32-x64-msvc@16.3.0': + optional: true + + '@oxc-project/types@0.143.0': {} + + '@posthog/browser-common@0.4.0': + dependencies: + '@posthog/core': 1.46.9 + '@posthog/types': 1.402.2 + + '@posthog/core@1.46.9': + dependencies: + '@posthog/types': 1.402.2 + + '@posthog/types@1.402.2': {} + + '@rolldown/binding-android-arm64@1.2.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.3': + optional: true + + '@rolldown/binding-darwin-x64@1.2.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.3': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.26 + tailwindcss: 4.3.3 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.1': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.4': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.1 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/geojson@7946.0.16': {} + + '@types/linkify-it@3.0.5': + optional: true + + '@types/markdown-it@12.2.3': + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 2.0.0 + optional: true + + '@types/mdurl@2.0.0': + optional: true + + '@types/node@25.9.5': + dependencies: + undici-types: 7.24.6 + + '@types/react-dom@19.2.4(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': + optional: true + + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + '@vitest/expect@4.1.0': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.0(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0))': + dependencies: + '@vitest/spy': 4.1.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.1(@types/node@25.9.5)(jiti@2.7.0) + + '@vitest/pretty-format@4.1.0': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.0': + dependencies: + '@vitest/utils': 4.1.0 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + '@vitest/utils': 4.1.0 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.0': {} + + '@vitest/utils@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@vscode/codicons@0.0.44': {} + + '@xyflow/react@12.10.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@xyflow/system': 0.0.76 + classcat: 5.0.5 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + zustand: 4.5.7(@types/react@19.2.18)(react@19.2.8) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.76': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + autoprefixer@10.5.4(postcss@8.5.26): + dependencies: + browserslist: 4.28.7 + caniuse-lite: 1.0.30001807 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + baseline-browser-mapping@2.11.12: {} + + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.12 + caniuse-lite: 1.0.30001807 + electron-to-chromium: 1.5.402 + node-releases: 2.0.53 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + + caniuse-lite@1.0.30001807: {} + + chai@6.2.2: {} + + classcat@5.0.5: {} + + client-only@0.0.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + convert-source-map@2.0.0: {} + + core-js@3.50.0: {} + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + csstype@3.2.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.34.0 + + cytoscape-fcose@2.2.0(cytoscape@3.34.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.34.0 + + cytoscape@3.34.0: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + + dayjs@1.11.21: {} + + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + + detect-libc@2.1.2: {} + + dompurify@3.4.13: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + electron-to-chromium@1.5.402: {} + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-module-lexer@2.3.1: {} + + es-toolkit@1.50.0: {} + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fflate@0.4.9: {} + + fraction.js@5.3.4: {} + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + hachure-fill@0.5.2: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + import-meta-resolve@4.2.0: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + jiti@2.7.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + katex@0.16.47: + dependencies: + commander: 8.3.0 + + khroma@2.1.0: {} + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lodash-es@4.18.1: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + marked@16.4.2: {} + + mermaid@11.15.0: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.4 + '@mermaid-js/parser': 1.2.0 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.34.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0) + cytoscape-fcose: 2.2.0(cytoscape@3.34.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.21 + dompurify: 3.4.13 + es-toolkit: 1.50.0 + katex: 0.16.47 + khroma: 2.1.0 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.3.0 + uuid: 14.0.1 + + nanoid@3.3.17: {} + + next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@next/env': 16.3.0 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.11.12 + caniuse-lite: 1.0.30001807 + postcss: 8.5.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + styled-jsx: 5.1.6(react@19.2.8) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.0 + '@next/swc-darwin-x64': 16.3.0 + '@next/swc-linux-arm64-gnu': 16.3.0 + '@next/swc-linux-arm64-musl': 16.3.0 + '@next/swc-linux-x64-gnu': 16.3.0 + '@next/swc-linux-x64-musl': 16.3.0 + '@next/swc-win32-arm64-msvc': 16.3.0 + '@next/swc-win32-x64-msvc': 16.3.0 + sharp: 0.35.3(@types/node@25.9.5) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + node-releases@2.0.53: {} + + obug@2.1.4: {} + + package-manager-detector@1.8.0: {} + + path-data-parser@0.1.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.23: + dependencies: + nanoid: 3.3.17 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.26: + dependencies: + nanoid: 3.3.17 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + posthog-js@1.413.3: + dependencies: + '@posthog/browser-common': 0.4.0 + '@posthog/core': 1.46.9 + '@posthog/types': 1.402.2 + core-js: 3.50.0 + dompurify: 3.4.13 + fflate: 0.4.9 + preact: 10.29.8 + query-selector-shadow-dom: 1.0.1 + web-vitals: 5.3.0 + web-vitals-soft-navs: web-vitals@6.0.0 + transitivePeerDependencies: + - preact-render-to-string + + preact@10.29.8: {} + + prismjs@1.30.0: {} + + query-selector-shadow-dom@1.0.1: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react@19.2.8: {} + + robust-predicates@3.0.3: {} + + rolldown@1.2.3: + dependencies: + '@oxc-project/types': 0.143.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.3 + '@rolldown/binding-darwin-arm64': 1.2.3 + '@rolldown/binding-darwin-x64': 1.2.3 + '@rolldown/binding-freebsd-x64': 1.2.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.3 + '@rolldown/binding-linux-arm64-gnu': 1.2.3 + '@rolldown/binding-linux-arm64-musl': 1.2.3 + '@rolldown/binding-linux-ppc64-gnu': 1.2.3 + '@rolldown/binding-linux-s390x-gnu': 1.2.3 + '@rolldown/binding-linux-x64-gnu': 1.2.3 + '@rolldown/binding-linux-x64-musl': 1.2.3 + '@rolldown/binding-openharmony-arm64': 1.2.3 + '@rolldown/binding-win32-arm64-msvc': 1.2.3 + '@rolldown/binding-win32-x64-msvc': 1.2.3 + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + rw@1.3.3: {} + + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + semver@7.8.5: + optional: true + + sharp@0.35.3(@types/node@25.9.5): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 25.9.5 + optional: true + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + styled-jsx@5.1.6(react@19.2.8): + dependencies: + client-only: 0.0.1 + react: 19.2.8 + + stylis@4.4.0: {} + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.1: {} + + ts-dedent@2.3.0: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@7.24.6: {} + + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + uuid@14.0.1: {} + + vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.9.5 + fsevents: 2.3.3 + jiti: 2.7.0 + + vitest@4.1.0(@types/node@25.9.5)(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)): + dependencies: + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.1(@types/node@25.9.5)(jiti@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.9.5 + transitivePeerDependencies: + - msw + + web-vitals@5.3.0: {} + + web-vitals@6.0.0: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + zustand@4.5.7(@types/react@19.2.18)(react@19.2.8): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.8 diff --git a/packages/kilo-docs/pnpm-workspace.yaml b/packages/kilo-docs/pnpm-workspace.yaml new file mode 100644 index 00000000000..55f54edf1dd --- /dev/null +++ b/packages/kilo-docs/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + core-js: true diff --git a/packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp new file mode 100644 index 0000000000000000000000000000000000000000..6c4d4b96c00ba69c6b55c547150dcc1b6adc5d51 GIT binary patch literal 100894 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�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 literal 0 HcmV?d00001 diff --git a/packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp new file mode 100644 index 0000000000000000000000000000000000000000..25eabb2d312a9c3c5772544e7b3dc26080177bad GIT binary patch literal 142656 zcmdSBWmuJ6yEaO9w{&+(gVHVCA)V5Qq?AZ^cPk~0bazXGNJw{g!#4r-5ufK>Ywh=4 z-`>ae$9>G{ob$d1V_f5k^E|I1FD@d&K?ee)A}k=QEXzSi3wX_FkDLrlO^O@^!xJu& zBANN&b8*`6M1VC4^efBTm{mmT`tjR=IRC4mUVROqNk;|! z+WjZSF0}P4-RdIfH~d z&ke~9=J~);V;L_ILDcQjTe9ojQ|&8;UX>~L-don2+gs7|B0|^3>)UIF{n-uIX4k}v z*(UchlSW8=8aO)y7laOZSnR z+Y5yog&WLUhND>^_mRd<_q|)~o6Xar^~TH4>)9>$>6^{V_ZOA7sTTvMsYJI!jkULP zw+uH$C!4o`fAyiaw;qfOj&tx4phQR=2m;L@;Zi`TijUV^cC4+u6tVBNw%= zt~JYU3MbuFEO_%OQvYB4z#XaiJU$1MOyXcSTdg+vVMOjoL*rj~#5e>k{Vvp{?p|cX zp8W5R@`{lf3h-C&$5kEWvAs*HJ9(IvH9WM&A9Fw(I4lEYF^p$Evh?1I?LV3|I>LGQ z8j?+7eczuG{<#_-Y3ifGkTGX~@MR@Nj2?%(l9)KurB*392VfxtoV5URAQu0huh1)F z--rr1Unq+q0yR|Jp9}oS4o5B-`8Yg6Of)k21&u+Nwa*OlsmB0>^v`eVlp*e0@GyUf z5QH2T>O}mvZKe9s;rI{xl1)+*B`tF`G;W+0jc`9g2+)35c`w_;tRD+~o31~3_5If2 z-+t^*qjtklenral`_yZ##9q};#y!li| zu4`-m3)^C$E~H8t^Vu92ZzWHfs zb9kAxKNsWYvi%$8ka{@j7#Myy-@vKcUb*yE!*0O%(e^(-%z%}q&2(=Ix%H`5ZlH?l zWlwSE;7hQ4lKMX|E%gc3mu92-)5S(Vo<~W>ljf2ywKHD`E%Ch2<4ehIgUt{?`LWyy!bpWNljiXtiZkU^JIPM-nXqW#{3`;L1-Mr`Rr9c+bqNgt;Ry zz3$!9UnBp2?mCrmU`C67d4)GVzWN5y`?)*jl`7dKHS@NQJhNw&ZvpNe8Qx z2E9lv(l+p31Rb2p;Q6sh?xMw&pr{5b=9Jx)cU&MdMob!DITuhHzD?1;Aqi5B`cJ3H zN711*v$f=%1a8z1(}TZ?OgpU@Nk41E+6JDOvtC6!p#{TV+DdnJdt@E9Cm-XgX2+&v zlSiYbe0WOv`GBU<{r15rY$5ng`d)^lPyX7FkJkNv92MPgq*x%Hg<>kox>&>$(k}re zR^mK_)m_MGof2m;)QwlX8|{aWV@EPLB^5r>bk%f;p~cC_`C)n0$y zV6|5Nre*&OkWL?8(^9h+1_h{Q@l}_>HlY;+7z-*tIB>RilyNT_znxR& z6%S~gUp8nza^r~r2ufVv0@m&~r4peWV06C6UhW6XR^s-X#5)E5Sscu%Xpi$ryM4v) zLA%tLj!(6Zv94|q!7lgU9#}f6S3(Me#-m5Db%xQ3Rpkwd-Z7K@#cw`Q&l5 zlO7m!fWug@eSPs|h}g&0lwMYkZ70wR;HTs`_~oa8@IVuO+vYzb?*Dw14;RUW=DdiM z#s;PJO=b*4v3#yMg}`oN)^_i6BXZ&wq6cNcn7mo5Kr1#8@$<8+xxB#n-Y{)HHc%iq z?`ltQ9__NVYbngKf1z2zO0}MQmA}W0_*BDZ5wAz*BVNSZEt3=fk6kE2!JKu`zb3_8opqk+<)IW{ueR#rtkX)Y16NO zq^{7G|1hmL+4;qj)ZS{^hge_T1(tK|!yr2dpUq!}g(Ag|sY2B=@m!U9w;F+(4gvS{ z8J%pID0K5|`6r-Mj^{((SYuD|^>Sf=KW^Y+kv}5} z-OgDpxd; zn$!^o+nEq!i23~1_~Z}O4YmC@S%lxP(DMRryL<@1!?=OOS>xVkZK9H*BnJoj+&kQ+S$7Ez8l%#xx|KkpOc92|Cv()ZoMqxKSZy*dIG1Nt zMOl7<$q)l6xPJpY#oavjy=;~cM`cj1XKkfD?FHyG5n96kjq2!~AKu`{!6$`Z^EPd} zYB};cuf;DPlI&H}o~?|PaTrY6>7g*j5zbLvjhgs1hf;;-2q#hz@oRdT^XFd$>-ate z(MTSe^tNG6{47=;va8m`bP-=e!Ps^|MZhXRpHGdOgpQ)jkg7M zV^vEX_&LsRcTjE)lzT?^RB*l!x*zgJ?{~t~=1nOaVWf{Nslr zMgP?oO@PSaMk49{dd}a!ZU6)fTl*|oX?c<`c7;(DtGK1)A+8%k;p+{fIevIXb(OxP z6yGgbN_sK!2@g1N_>b9%@Rn%lcYEnfG5`&}_e?LV8;~JsXR*L+`vi8xayP*ejUisMTkcH6h3m$U&QnRwj3n=@ml^W*b;Bl(7vk;Zm8j=c;K}c zBHGWG;X!t^Jz3L?c)(hIIQiTUHo6vGN%elFA*GQb-N!4JJ{0#yp*iWtE2iT3Ytq{;~Uvdy7|UK-i}*zAZu;EpCd$&gBm zkKnv^*){Lf7^e@zC1piU6_>ia!kQmt;e;C)uP#<}ik9c}j3K{r7pTAS%chMv!)(JX zNRu#OQ=Hn*ecmlJp`s%IHZOXPVtmuDyc>=;|@cTLVG@pyIU~lYXSUZ{unx z@yN8*Zu_HQqunn=*|ZhELFRM`KEz?-&-n0+XX=w_>9r z8_vJvfN_Klg9vm)VoU&ga5SGUZf002kBzrqi==&100Vog4D#QDd z)5Mm(NSmw11;}ML{HjfZ5n+wZ5{cQotdrFT>Kn~B9GDf{QUsOHNK54xP$n+bNeT=? z1v$8rD2b(|A1E`UH?fkcKSVfY6M;C>Ve^vBjS+@yw#m*as*O*G%R&%xf^may zEwV5VvegF^8r69<2-lAMmP>of3~3zXz7$)uPFyns^gh zJDc_B=VK+KtN*MOAAlBz8Wf6Shi8M2m(yK?GP=G?kN`4SJ(Hv2vsPl;nkccgbgn_Q z;Ts&wDIPM3{vs*t?3)Fr{tSF9sBEE}OpcP+MRj&V zJs%q^5O?fVF8uO=CFbxE(D~V!U;~wU>Oe-VN%&)J39Ptp21X>J5FI(bpE`jE776xA zeUaJZKU~`Opa1g*|Mcbm@BXh&2K7IDcs`|GH-jPSFpuA}AgRe8*w1}c+Ap^Gdpg7q zfX)7|LCJd-Z8MDJw1w*Gb!kpor!6Un}P zzV;f)yZT4i`+>Rri=FZZF!?{L{jYGH*>Lb@b_oLIIIHct+XCkN$%8O#f44<3lR4|> znrOVo_KDjLj`;kZtXuhwYDycI zP>{v8xzzaQmE6q5x5MO}JWp=JDr1;x)eP+>OL(m1csn(VTqMGP}5*e#YjJ6w4NKpT=iZk_^ zgG62K0+zGjjoCyUXj`_SkKSR0A-lbSqKk*$jnzq6oTEun3;m52L;2awcVg~R{M+Z3 zt5zaULP~t)Y6YJitm7Etp#@E812?I{r8#uWEou@?ct})M1BFmCCk@qsqWW2p#UnFM+q&{1IM(=o&+J%vr@EhJG%z$U5I#1HK5e4gG z^;}d%7TPvDXuE+Tb0lthZ6Z-n2)?H?jX{USoLMD8L{kH^)>ii}A_v$nS;i2fHayP# z{>3Q%i<0vne<5}2O5o-aw4c7q4^Dj1;mqnMdjA09wBpJw;n zC87(->HnO;{^OYbXM5?d^rS0nTCq4JxNB#%;*Va`oex z0g>=`7pO2J(>}jSGo<2`ulJRHNjxzz^Lqen9Q(L3`Fq9iZ&f0{)jEIN(HPyixdm#w z*X|-R$}Zsai)&iOcd#GyRo7J>5pg600=Ug9-3=oOUyb&IhcNpmFbMPzwwI6}u^T^pqZ8apdF7m@dlNj> zf9LYKi&Pt}jiEXT(yS#8howcWs8_n=OhV-jEY=yup&>95&bwUh*fJE(z&N$`go!(v z^5>N8{lVANL;BELO=9C3MN8t!fPHQ=5pYFrvPdrO}V$660mi6PGfZ#7ULyV+uJn(cqb}Rx#=E6B!xe z3CJ`qL~fchH~R3#WZS}T*pz$`4#+p6I0rN0Orts+8xtcA*Fk&ccrD_3+eONo5JgR|MfvvS+impx{qP_xHH|`|>2-jKx%j+KqR*x+a2Go8Iu%l~cOx zKMA-u-q$Z0t9V-jI+d3(HeYN0hA&vR(VLy`4*rpwgd#9?_H$v%u6vJ7s->&I+opwL zT-o-FO~9TXJi+>$K{YK&G%EPJBuc@@V6_iZrU+^vhw7_n<%Q%zu7|4sQr4)Wg2|r2nZw3wD%D zdr)=bP#Yg+Z5r0s5s{w2D-^UcsJQY$F6TW5c8(;S&Rb zX?X?-*cTV>zIjJc|4219p;iW@uu{Egi9~(w%*8Lci(YLRGtsbfw@%V=DE4|Zg)m?vsd_g{`!+6`R}C;PEt!@&-y51`pPe9 z{)$5XN+of%i#h$p)9dS?a76j16nh4O6&_e3)fYTGq2_gA*1mFB%8i)#JU%oWI`ls& z_-AiEL6LncdjCR(>>r%>e-GwmG)dNa@i`v{gw|wq{;U1-o0Oxv-nO(8JbAyczq=O1 zs!^9IyACP@QvS*Xgg?@nD3%&P5FMj%+jwr|)v%&AZ$j z?SXx(mS}p2Jv2J=o3pd=+S%9A@cV-TK1?=bdqB{C8`fVsiyxIz#&v{_t;!dC(CaX= zHfJCc_u9mX93+Jw3Y6a}iHS!5QQ^0SQ0kcinqW92*KdAk_J@;;Oa5!L6p3X?{*Z7K z5*cE+`?)UBIK@t11_3_$0U=iZiu%~jzuIOCUS;=0SJ)+EZ<|NZNTaWT1EHIBbmQ`= zssLr3*(90L8~!Y~v)5eIp>+%H{e}bgnT>^M{LQ-^P^KOl#PaxedPy%?CSvkoOEIA>-VbVWuQt49Poddv5q02LZR&T-ydGi&mT+gsrD&4iaiyLobrfY&Y&SH@u_@R<(^h7pg11VP%uGFy9 zO}boY?^;_}xugJ<810^0kv|1Z*3+UDcyDH2VRn5#SMX5FOhQj{${s-xHAsP!1)c(K zZjS{wp_1O&F*a@So6y5BCC(yX*cXD_gU8QNZjw)lcZoiVsw9U%X3O5B<KZM`@Ntu)BY~KN$IG2I(15mrc^y#0gZsfps?+I^E&G(`NAbGvO76>-@3>uSn#}P9 z3h003*I}A%)$FJG3d2HNhPHF%nSzGFVA#)d57;%4<2ll$kNWE;J92U z%!wGO=O$&L_m-D2<3kBG6~Hz1Kn<{D1gAZJ`#XXA#ADsPLHMvb4!o2L#Ve2IsQ86IUISAe>z;qEm|xaH zQ2^G`P<$3J6PT7*Ze8%&7U|5~gKy1|;JYI#g!`;xq?eJQ4+Wy#ZTj)kivm8(NHtQx zU%ie{@u+;rp;=Pkh~>>7$8da^Z0)pE*#DNpqdxt}_Uef$H8* zk=52?uEmlQOaQD`-ZU>%%2t1Jn18aHL<(*#^kZPXO-bq1Fep$HBj6HqYMM14OK|_w z5gYRi86iTV)&;`#lF4nSdH5VVA1%KNv5Zm~c^)zPv06V}g(gUSZ9wGRte*3~$g452 zU)PDgMf9N*JQI-%%0*fiykBpqScDJ@o(=80w*ep9;|fSVclC*J zfgXH;P-nFJV?e8+I0le{{q`Q!wqLQkm; zzIGOzN*=}Z*kVkoE#TkuGY zul;~T^^K_P5GsXwK_`Q#iek;5h%BI`Q8fH0SI$k*3B9VLwxDV(tPF9I-@iz9=wS?1;c7k45L&L54wZ8( zH+=>mR^8fPbwpx50;19$D_(b>IFoFgoPC&26?K%`=vK!hZjk@flDLsr6nu2hC3A?4 zr|1D37$93HXP}D>UE~`rPh1?X^I7`?K}Aa}jIFn|BTNx%14p<^d8hy&$^DO0n6=^gDxmUTS;jXiIkV7RAq!qNH*92|l~< z0t%#)rQ}>1kF~<9j&#IBV%Ros`j?T-81AahEx$sBEX=Y=!Q`lI7t~ZnN&fC;Tf}|P zM7zNAAGs{mBjRA>V#h9=seTl(C}bYB#p3PW0I~WTMV9F$xYmFr5nduof#T89K-cFb zixw(g)Z~b$Jlv)*`@+CHU@Y*4hH=!&8}c;@O7^@Q*&m+9msYoH|8L5vG6qK`)Xc%9-RE`-p170s-hg z$yFwkF*y)CJ+5Tzjw7g@O`N2RULXF_8Qtao$;-8@$t)Ozowm*Acctw$SUEk5Q6GC1 zM++xtxagRjJPVU18Ic>!x11-3tNCLLhn;;juGN^mge!O?ySr_~f=0W{p#u(X;pX86 zj*#LZJj)LJ1jHh5U3;}cpq`nPIR=4n5td=}upo6l1iRr!_|v5Ohckd`=B=hQ8zjjT znTrfg`Z|_L%01tii>K#iW!k+0zIEE^8nCD@Jue1AeLil$Gq41 zDC)(!s>ZNnjSe$M!UA%MeY2Ud^{E_KdD~0Im?sn?Dj(**UlFA~gO5X6YZPuvf#(12 z)rVh<((JydDuz2&owOI~_bzBSZWE4`kawuT+7G0Wib{_W*2Pq^Nly#Oy2K82f5ose zQ)435As`z0gMsthcnBcklh!MOGa@fMw*on8r_h?G@lDKMgVpQu9v_I2Xrg7v#gNhh zp0J_oh&tex7d&AdaNqX|aOPlD%>`T77T{#*G!|iwafFnXs8bgJXVM&vnXFsM{7^_K z-!AH!BzGyr4zAj32{$v$^s>&z`sz{#H}h$Cj`3E1dsk1qujG4_m~2i@B?Y2%%Cy*x zULZ!OqsC4Gg*n&K1;LxM_rt~tpyfKT@f7??C#@|Fpbd}?BZxRbb>yfgq=H`BnyAmq zZtQ3g@Zfi@ZKIJ0hecsZXU>?Ol~Q@$c-l^f3P3Ujzi=lNp(Tmn4;5h_sqVCq_hCw1 zpb{SpBN-uMga#SKPsBYt;K3PSgj1E}-z$6Z%9(_f$w)->;~xE){i|^ zRgM$sc1f<&$EJSWcn>*UH2OV~rC2By0Loci;CWP@+F#?3U;KL zj8G$y#+ULy0uulS64~UfsQ|q6+jZ`&FRQV8pe+!&x{y!}HRNvr2Bbi-aUDbF$-xj9Wk4IC7(K^P*{TmOUNJ#h z>(_uLhPByI8yIusR<*tG-u@abJ5v>eg{XhtW^svjOojke=WE5^bCb;KGiSZ*Mfw2+LIx-P-cj_o1RR3HopY<6iVyRPI?os{UsXIEVf|+& z;5zn9;_53W2o-VDZz-(&GSA=OvAkF)$clp6*k?+hah3(6Cp@lvA^$ZF$j|#)^BHJd z(**fE3~=d~4P}C;0I=fF%GXy0i+FMI3RQc4Sh}z1qXGRJ4BQTRGU5s7i>vtz#eAU% zqXcRwt4b%h7~KIR_OwF>y`(UmUp_n!@+S;9+|vv{$L3K(+jvh)CfSQY2X*9NOp{9h zG9=1dV=_&=ApxQqxeWULp>O7T-bUSBg=1f8KzP{gO^YmT);j4$X`}G$%nstgh-iU2 zVslIMsU@@d^yO1+VXnnj98G;7Wb`$9)*7=~u?owFpY};wsrC=Kn<9B$fNoaxb6C0r zrEyFHl#Y|0Rgcq^RRhr1EN)G}{c%4ChO=k1^Y~)qbEv%Q#@DafNAy3ChKlP-NwepB ziefnRa1Km;(;~gQ(_J9?1O0Xgs=t$@z6GNT1KVeUjS@~De@+4*CH?UPmaWL3M7es^ zqeNkn4W$5_)Cq}uwM(V-dhY}7c}=iNc-Q?i#U;Q6Ga4VZ=p zsOA?wb?V*z>-8_p@8%DU-0Z0*tv*vaPyt>PFzN73xa7O3mH{<*QApvgl z&swh^kQ|^%86FifKaei=VWXM9gO7i6lbSF9p{CaPsTo?4>o0m#Mx<|Y3*ohw@BsR; zJ%kMG3rVI`c7FQD0Bn%p48di~LKZf(!=GZ$c{~_SY_4yAwf+DQ#l)L!KnG57DIDbw zu=eBi{O^F-HRx|_8o-hrdwX_8Uxv-glL&ohk=jPZ?`98-z@-*4iQCsB@rJy6Elg|9 zAWPrc{E6mb#KRp2a{F%sSp>xt^rM>U(8pRpb1-_Q>Nn57u;dF9j!pg#ucKz^zfzGU z|IO`9U3g9di6Tzsm|&rGqu{I;{EjaQ5Cm9NkRfv{N`l7v8?Sl0v7BZBy5=c{wr+MW09OlW zOLQ=a$iKr6=yk6Qz^KLMm{i-DCAVP~K!w@!lVsZYw5K>xSa_EaLE9P07xcJH3YHA> z5^PW8Lk3dsgaDX2RaL#yJAg?w|CT?LOmxy~k;MA(c39X0qMURbuYV-4GM?lh-ZxHf z(2Nw6k?C2uU-O7dc8(i-&99FsAP28IST_4EwGau*F+wHQAM%Vsg7}TUlY>0l-&Qp%3c)I42 z5$_lrYm~6>8)+4{0UqkdR=ekzUT$SnRUl2(w0H@^{)n8z*i1E!(rmDs?1~_AEo}*K z=WONMGfES`0Oqzo$@f!N_4R%V)_WX&-UX%h)m>I(W@-_}5Ict`BvY-__2mby+4(l- zfr8G|ri*@V1twF>q$!GbPZkO$zT~(MAhDXM(WTXY_Y}Uu4BPEPA~*MsnUZOqw@NAT zkHC<*_4_35g`mCACDVI$^Owr&ll||jTy|5NEDV>*GyZp#w;H_Dr_#4usYbzXDK7r* z9Oh1vZMXwF-k4WF2*@{x|G8%`OkVFU>LC5nuiSg+oQ#D?nTTgEB9DJaT*F4|{D1Zp z0>+>F4>jjQ$K@|KTDA%YBKaN&_Ah1358r=vIMUk#mG+CtzmK8-X~ZDZ@Qwgq@nSrAjVBi;awnIXSx0r4a>os85IJl9w%C^gBh~1b1MYC{hq?RA z*HCMB9F4{!8BO58r8qU(*v}5xhxKAF(zYGLm9j`Vb9Gh|Nu`XFa)ju;3)hErTHir{ z!=2CvR>g+5?aDH5@bR_V$lgoW3P|XBb)FkIp16P`dcAD6*4j8n-9xYVt|TpM_I;GW zvHL!X`_Vi$;(z9Yb#eb76dz~85033OiuK1v|JLGP(Y`^ug0Bp#GIpV1!1=DqY=8uO z-vF2Eh3MR!NkiZ4h1p>leNX?euZ( zqwnHg+Tx;}*1a4t?k(H|fCZCQmo2~dP5y~dk5Y*F-N^kJ+wU7WtXpfUj@z8yN4>ml zSpRbe-bt&7PaM6nctIGYBk0OjbIKFE}=G zTD-(75ZXPgSdyvAuy|2xOe)v*Y5y1l*rR3FIBwe+Q3`xm17(8FGujB#XtLqRVXTSJaq@Z$4PztFHri&QX8GAwWKI;%OCgt41B*rzA4j_ z1TV|E-<1#>+gS&sEcF?{VR*eMi-hU61M=2I2USx31JHuHGin&)5k`DH}qUAA-evvz_AH@)wwM_r=GdBJ*u z(T`o#*$r{VVju?v&0BI=)9Ps9RX2?hnC;Oh@~#@}6l4%~!4?GSAt-hKNz?$3i!Vdv zc^IaBbQDxoa3W*g-=%vYKUxmBGR2*=wZDKoKw&+BkmSPc_yjZB50}RIo+{SL3GH@{?M(1NLtn~9pk;yAMSN*Sc z1!4%kmeoLXO7zQ|e4*V`gj~`3Ea%tnn)TlXj*B4$Zl-@{aYSt&dkbc=nrORWVikeF zI$~#E>Pp3-72BGMmp?1nXX?rzEJb9fvo)<6FmXfqLC0M2UT2; zAfpv&leJNRF+PA3sPhK<0+#Zqv+4*O;)BzUu15Q}ZB$mLU1inKhQV96iykkG+xDF~ zd8j;|KxC$m$yZ?~)G)D*+%2R7DM5J|xrK_~!V1hM6Emc=JPjh|I`wdx3l!Q_$%zjw z7Gy(3Qs@rJr8%XrnLWU|0iJa4VKY4RU;>=7qO?r*p|KqaTc(oWTm5e0x+vNk;O=hn zj3Hf>z|a9eZqtvMq3VG8>S`Ca_IL5a)#H;dOfxNkNgQD&{ZOrX$1H$ILvOJ@7cM_N z2umqH{NTYoI&~YX<%@jX_w7p2Hr4-Nl=1o8zywt7v~>rZe6Qyb2kh?@<3vx?5jmnJ%-AC_24U@r<3+P4n%&z`)@Uno+GnohhyIbuyXmD zRhymctaW46P|mtxf>A6_sZ9&?Q&D~>(_J<&TQzP%+_Z{3d&^clvx0mD`K~%fNKd&S zO~Y)1WZ4fAlxfx|Ggv)=O(zv!i|m00F$#NZv{fxIVB_Pq zFI5wXk=TQ?*E9>fPS*uJGDmT1=K?T3%9vi;d?x zQ<)eF{1|2(m{_B#i+}U3@~PqY>yB@AdDs3gaT{Ow`&0@tJX=P%+;YV7=z#R_Q9)2? zTBq!Aiu=wHp)#f;<92P)^aFJ%d=vza?H9wWAqI^jCunCJ4N1Hzc^8#R#ZA#qGkb#y z%oPul9@>|`q%ZJcZrn$hElRB+jF0Q|j)Z7nFKhf!ZiXx~oyplh8@~$5j*T}}BXW(A zqkVy@K5u`HLwtYS$*1VA+@UBf{ZVsf0yZqtT#;LS9Xw+4=MG-VGdC8sYLgXm`aA_N zPxt^CnP9;WdD4$))_5C1Hl9DgvR`m4NcJ+HkkBZ#l3TLYwidNzP&xH0p$YDLW>H(d z`Ph_lCrss*ms*>rG&%#W?}%XRCqVpnqlapJ-j8ZJ3{ZFx!l7tNwdF>-%#gx9kG2WH z8K&SAIQp3${SC*11h&pTr$#zPHJPRyrW%8*r*y9Cf>Qcd2-pwmj- z@=H~%U(iq{V8`GQGb9i5{iC=#g3$Z? zs}-@M8e8Rwg?^IY(vMT;ueNzi3_+r&$Qs`$6_>m9@ZjWLIPO?&c1Bzu65=ANedwx1B!kgff1*UMN_h?guC3au^t9gwDiT4ZE#%eJb1~sX%mb zgHS_Isv6Op*X+zdX?=Vv^E*Z6?{q zJ#rv8E+b1`u5BYYF|OYPc|FOM&_C|U`OA*j2~euMrbj+_+R0L@;t*D4$~cSFm%{yY zm0+Pr?c)o`J_D{G3G*eaizHxS1SCeikI~9bEvljIQg4s(E#JvMF<7=BtWYI;hJb)7 zM)qWpoup}#)@hq>zHo%X=3QbB-1~Q|3}1qwPePh8^&uUPD9j`kE*i|@`{t7*R=W5yK+CIR|%>6DASYFjv5KVqDi&vwU`?iABhaP&W@O7N`QB?2;E>^|Ga6yc3 z9{Ph}k{`JBkrl-v7$k_w&)*Dzr!+|f)jM~JC_9J0^LrL%YX(!ojNQq>mm>0k^7S;m zhY}y!6B*`m31SfE7*pN}Z!U*cMnU@29Xo|xeW$l92XG!gl z9bH|OdHoPmmR1z)k=7m|T4Ph3q^zg$j2mM?I&k0t43Y0h+%8p_I$#f9y$}8r^6f>$ ze_5hw&s@}+E_rhAAGtgBmxXJegpQ2T>U`l^WYpmm{p6R_XE9~ZtRDtvj^Ho!t%jXr zOmMd_{+WoVLWZs;b+JX8r+Fg1aPq{guq>5v*Q<7X1@ZU$Jh=5{WjnF%sYnfaqnVz( z5rjZwt}Y>fT>pW+#DwF>sy5n7+v^-G2|M%h;Z&(P&hl^0QFOK(ueRj8AvZDQk-^>% z?;?dj10`MZJ?erVi4N!L$S&p=Kpyj|zOsMDjx@L2*qbl;nkv9l{zaaVlUh745ZoNd zv$Sf#8znZm?G`*L5`b=^wr&dOWCj1ANzkiO`?dQ~J{j zVhG^2BzWQgjrOf4qW+8x*_knN*j`d|Eh(?EK1qU}T6m=t**68TBQp|H+Qw|Ceeb-r zh>PJ~;5!quU3$aRHD%?q{Q*1FU=nGKU?CV*=L+OOO7k;0QbJqOysq&%o@n}>f?dH? zs21;$K*p1xk`INEes>pIufvYy|?_IB>ymG_hGn*9I})iP}*$t&eKt5&8Q(nRsoPqAt^p;Ceo-kr;} zS_A9R+B-_6wD5TO;1F-j9lngFb=@B5ER4QmSwedz1{g6{G=QXT(h~tWBe^b;ZSpAi zvH;9qnP0LrE2nz`O0S2ZRF@hWw{H-Se8rsMi6_Daa;dvfBc&aOgpq>*&0DzYNeQ61 z7-^WsXO+%vkt%M5(8T7ND%EW*Qv41%LLd6 zY-}fR$1N%NSCN8Q#=}&MdigYZUtv0)KM^D!?@{a<@s_6P@X&pl?}Ts(oXZmI^Nf{E zykY}sraIvEYZrc2E(dw1?v>|m4r$;8E|}O#MQ^oCcF=c718HRqVt!QxVj#&t9m&m+ zvL){=!3(SRn%G~Ikz4MvEP`UT79Rza6@i}5zo?^cxBq(O@nkX4to)mmL=8s=17hr}-zZXP`vKs+&BkxgoE>%+#HlSgDE+;EVGseunh*lWL*se`icoB`J#R4 zJ(9|Th&6RD&kGP=AbwN@+$?0eW*DHq>!tRL3MHu*QM~k=4~y~3&8%ih@{zKnd7w!fU2u{0njPldrv`S99-Uf3|I#ORhb7i>~9TH z+!Wl;&+2GQ@&ZzBs;X-{m_Ea(Z8B_s1BqdC4)mdm-Kq7BoOwFfnj~rUkBCMQM+8Pkpnif&m5k{+0M(2 zMT2#Qn4@olpphCqf!X@R3{qoppk9p`tpV(u$;Er>9Hm|p0MEhzSof0677@7ilFxQK zQ{KD8JuR5W$i1yfpQ;cIjU9M8eD|hVX-*ea}=I%Vo@|FW^h;<67S^87A;y+n90IIf@FEd$)iZf(~T|*HwSUekz!qaa3u`G zd|#xK3!I`YD(dp;@IANk!*N>2(|f{?k%PNlms;rGAZY+R$ce18r2^?e|!p@Ekgy)lgZdBDA9Ux+!?&C6|4bWi8M5-aaz|e zp3{)wLuVFY=n6s&1@+^^mp=%}a`}pSK;aLg*%a&c8Rg_bLn|i1%K%t8Ca4-{?H(M^G@nvX%d(AFMks^O+LQT(iXaBWM}( z29QNaZZX`mUcZ>!?d(~1C_rKcIKT$Il6bo@uOj_`$1pY6%yh0nR_lz!JweIW~IU3JK$Q;gOp3 z>;(u@^i&W)^%*L~RbBGFc1hYgAVz`jD6j#0bnHNVv3W((bUHcFsQ}2$UT8fNaw@7_ z2CT-v>pAKBo6lC-s~r6CmU)RdJf3Tr$%=0szo{(aG!R=czzhWG?Nr>Xr$=8_q<+;E zIORSAkQvSbP#A_$l3LH#$K72<0_g=B`NzOI^<^8o8efVeWAt(Dn0N#b+Ro|d>EQ31 z06)>5Ij;1_7}bZFavE4a+fi}I7%=n$gL|us6yis(CLb$=?_uvh=Y{QSr;WBaZUrE2i(YA;nsJ`VjM0MK!gz>l9FXSejeM>T#NF zA)?z1Q~A$|~u| zk#lVi0A6~|bYGmj@t+lt3QqDaO;O_q*Rl&41!TOrnzqAX2f=qwsBwtK&CE5;^u zHz|QM3`jpr6z#JR{;l3DcQu#eo%r;$*7!qc(r76>SuL9QQLivPd?jF?q4-}ls;M$8a}5a^ zc%IwI^jqG23Hj6g=~6TffL)EH#L8&!j`^dTxx=ss#M_^TPkk>?f4E0ul@Z?3#yRZK zP}lF88&@Z<;YlbsL3d&PI+rCGmbV==!kz3-h&XOEQ+u*N0Y_Ik9}Dpqcybmv-+|+P zx{|dj1t7l79-OYltyaC6g)Ovoi${I(3;&0?w+!zpS+nK zL)_h6iIIdjad&rjk~hfA05j*zoOAE}KQEuc&-UKk)zz!2)~a3y8-?67hLm@hJg2&= zQ}{LozYu0*HI*yG{#+`XyM0O|{h=rRUWNPuNk$j`T!Wdc`6<`=-ge$3W8@VMKMR)_ zYl|#KwCD22FJ+lN?z=!kx-V&ADEqZ!6}Uf4`>7S~Z}HjJ;*p&c|J}zkhA(FDgTiz7 zASwIB+sI4RE<*U9UjDK|ZcamB^@>OE2M`^9Z{zxVA*%Ha(!Fl?lUn0jx<6qRYF
dZBvyzvV4}5{wIn5-dmM$yCRwS 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
    ^!0!GR#*#)z;kyYN#j_%${M)Ud- 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 z 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 Date: Fri, 7 Aug 2026 19:19:07 +0200 Subject: [PATCH 22/39] fix(ui): drop italic styling and highlight decision text in tool approval line --- packages/kilo-ui/src/components/basic-tool.css | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/kilo-ui/src/components/basic-tool.css b/packages/kilo-ui/src/components/basic-tool.css index 9d7ff5acc76..7f54737247e 100644 --- a/packages/kilo-ui/src/components/basic-tool.css +++ b/packages/kilo-ui/src/components/basic-tool.css @@ -508,8 +508,8 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty } /* "why was this allowed" line inside a tool's expanded body. Styled like - [data-component="tool-hint"] (muted, italic) so it reads as ambient - context rather than a call to action, and recedes the way reasoning text does. */ + [data-component="tool-hint"] (muted) so it reads as ambient context rather + than a call to action, and recedes the way reasoning text does. */ [data-slot="tool-approval-line"] { display: flex; flex-wrap: wrap; @@ -518,7 +518,6 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty padding: 4px 0 6px; font-family: var(--font-family-sans); font-size: var(--font-size-small); - font-style: italic; line-height: var(--line-height-normal); color: var(--text-weak); opacity: 0.9; @@ -528,6 +527,11 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty color: var(--text-weak); } + [data-slot="tool-approval-decision"] { + font-weight: var(--font-weight-medium); + color: var(--text-strong); + } + [data-slot="tool-approval-rule"] { font-family: var(--font-family-mono); } From 4feb0c903d9e193dace32f7a1361135ce2b2c4a1 Mon Sep 17 00:00:00 2001 From: Bruno Agatao Date: Fri, 7 Aug 2026 19:29:13 +0200 Subject: [PATCH 23/39] fix(vscode): preserve approval metadata when slimming tool parts for the webview --- .../src/kilo-provider/slim-metadata.ts | 4 ++++ .../tests/unit/slim-metadata.test.ts | 20 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts b/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts index df8d576e030..9175ea673dc 100644 --- a/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts +++ b/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts @@ -73,6 +73,7 @@ function slimEdit(state: Record): Record { } } if (meta.diagnostics) result.diagnostics = meta.diagnostics + if (meta.approval) result.approval = meta.approval next.metadata = result return next } @@ -84,6 +85,7 @@ function slimPatch(state: Record): Record { if (isObj(meta)) { const slim: Record = {} if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval if (Array.isArray(meta.files)) { slim.files = (meta.files as Record[]).map((f) => { const diff = patch(f.patch) ?? patch(f.diff) @@ -115,6 +117,7 @@ function slimMultiedit(state: Record): Record if (isObj(meta)) { const slim: Record = {} if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval if (Array.isArray(meta.results)) { slim.results = (meta.results as Record[]).map((r) => { const rs: Record = {} @@ -149,6 +152,7 @@ function slimWrite(state: Record): Record { if (meta.filepath) slim.filepath = meta.filepath if (meta.exists !== undefined) slim.exists = meta.exists if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval const fd = meta.filediff if (isObj(fd)) { slim.filediff = { diff --git a/packages/kilo-vscode/tests/unit/slim-metadata.test.ts b/packages/kilo-vscode/tests/unit/slim-metadata.test.ts index 21c62f4722c..96ee681858a 100644 --- a/packages/kilo-vscode/tests/unit/slim-metadata.test.ts +++ b/packages/kilo-vscode/tests/unit/slim-metadata.test.ts @@ -30,6 +30,10 @@ const BIG = "x".repeat(200_000) // 200 KB — typical file content size const DIAG = [ { range: { start: { line: 1, character: 0 }, end: { line: 1, character: 5 } }, message: "err", severity: 1 }, ] +// Regression for #13001: slimmers used to rebuild `metadata` from an explicit allowlist that +// didn't include `approval`, silently dropping the auto-approval reason (and the +// outside-workspace note) before it ever reached the webview. +const APPROVAL = { source: "agent", agent: "code", outsideWorkspace: true, outsideWorkspacePath: "/tmp/a.ts" } // --------------------------------------------------------------------------- // Tests @@ -96,6 +100,7 @@ describe("slimPart", () => { diff: BIG, filediff: { file: "/a.ts", patch: PATCH, before: BIG, after: BIG, additions: 3, deletions: 1 }, diagnostics: { "/a.ts": DIAG }, + approval: APPROVAL, }, }) @@ -103,7 +108,7 @@ describe("slimPart", () => { expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES) }) - it("keeps filediff counts and diagnostics", () => { + it("keeps filediff counts, diagnostics, and approval", () => { const slim = slimPart(heavy) as Record const meta = slim.state.metadata expect(meta.filediff.file).toBe("/a.ts") @@ -111,6 +116,7 @@ describe("slimPart", () => { expect(meta.filediff.additions).toBe(3) expect(meta.filediff.deletions).toBe(1) expect(meta.diagnostics).toEqual({ "/a.ts": DIAG }) + expect(meta.approval).toEqual(APPROVAL) }) it("keeps output and input intact", () => { @@ -176,6 +182,7 @@ describe("slimPart", () => { }, ], diagnostics: { "/a.ts": DIAG }, + approval: APPROVAL, }, }) @@ -183,7 +190,7 @@ describe("slimPart", () => { expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES) }) - it("keeps file summary fields and diagnostics", () => { + it("keeps file summary fields, diagnostics, and approval", () => { const slim = slimPart(heavy) as Record const meta = slim.state.metadata expect(meta.files[0].filePath).toBe("/a.ts") @@ -193,6 +200,7 @@ describe("slimPart", () => { expect(meta.files[0].additions).toBe(5) expect(meta.files[1].type).toBe("add") expect(meta.diagnostics).toEqual({ "/a.ts": DIAG }) + expect(meta.approval).toEqual(APPROVAL) }) it("drops unknown heavy metadata fields", () => { @@ -266,6 +274,7 @@ describe("slimPart", () => { }, { filediff: { file: "/b.ts", before: BIG, after: BIG, additions: 2, deletions: 0 }, diagnostics: {} }, ], + approval: APPROVAL, }, }) @@ -273,7 +282,7 @@ describe("slimPart", () => { expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES) }) - it("keeps filediff counts and per-result diagnostics", () => { + it("keeps filediff counts, per-result diagnostics, and approval", () => { const slim = slimPart(heavy) as Record const meta = slim.state.metadata expect(meta.results[0].filediff.file).toBe("/a.ts") @@ -282,6 +291,7 @@ describe("slimPart", () => { expect(meta.results[0].diagnostics).toEqual({ "/a.ts": DIAG }) expect(meta.results[1].filediff.file).toBe("/b.ts") expect(meta.diagnostics).toEqual({ "/a.ts": DIAG }) + expect(meta.approval).toEqual(APPROVAL) }) it("drops unknown heavy metadata fields", () => { @@ -318,6 +328,7 @@ describe("slimPart", () => { diff: BIG, filediff: { file: "/a.ts", patch: PATCH, before: BIG, after: BIG, additions: 100, deletions: 0 }, diagnostics: { "/a.ts": DIAG }, + approval: APPROVAL, }, }) @@ -325,7 +336,7 @@ describe("slimPart", () => { expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES) }) - it("keeps filepath, exists, filediff counts, diagnostics", () => { + it("keeps filepath, exists, filediff counts, diagnostics, and approval", () => { const slim = slimPart(heavy) as Record const meta = slim.state.metadata expect(meta.filepath).toBe("/a.ts") @@ -335,6 +346,7 @@ describe("slimPart", () => { expect(meta.filediff.additions).toBe(100) expect(meta.filediff.deletions).toBe(0) expect(meta.diagnostics).toEqual({ "/a.ts": DIAG }) + expect(meta.approval).toEqual(APPROVAL) }) it("drops unknown heavy metadata fields", () => { From 9dbf276adec8a6b88e451eb13422272e16435cbe Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 17:26:07 +0000 Subject: [PATCH 24/39] feat(llm): set explicit prompt cache breakpoints on stable prefix for GPT-5.6+ --- ...penai-explicit-prompt-cache-breakpoints.md | 5 ++ packages/llm/src/cache-policy.ts | 14 ++- packages/llm/src/protocols/openai-chat.ts | 87 ++++++++++++++++--- .../llm/src/protocols/openai-responses.ts | 69 +++++++++++++-- packages/llm/test/cache-policy.test.ts | 84 +++++++++++++++++- packages/opencode/src/provider/transform.ts | 35 +++++++- .../opencode/test/provider/transform.test.ts | 64 ++++++++++++++ 7 files changed, 335 insertions(+), 23 deletions(-) create mode 100644 .changeset/openai-explicit-prompt-cache-breakpoints.md diff --git a/.changeset/openai-explicit-prompt-cache-breakpoints.md b/.changeset/openai-explicit-prompt-cache-breakpoints.md new file mode 100644 index 00000000000..4ba3d97b450 --- /dev/null +++ b/.changeset/openai-explicit-prompt-cache-breakpoints.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Set explicit prompt cache breakpoints on stable prefixes for OpenAI GPT-5.6+ models. diff --git a/packages/llm/src/cache-policy.ts b/packages/llm/src/cache-policy.ts index 60f96dc69aa..3387c3a67d0 100644 --- a/packages/llm/src/cache-policy.ts +++ b/packages/llm/src/cache-policy.ts @@ -36,10 +36,16 @@ const resolve = (policy: CachePolicy | undefined): CachePolicyObject => { return policy } -// Protocols whose wire format ignores inline cache markers (OpenAI's implicit -// prefix caching, Gemini's implicit + out-of-band CachedContent). Skip the -// whole policy pass for these — emitting hints would be harmless but pointless. -const RESPECTS_INLINE_HINTS = new Set(["anthropic-messages", "bedrock-converse"]) +// kilocode_change start - Protocols whose wire format supports inline cache markers / explicit breakpoints. +// Gemini uses out-of-band CachedContent. +const RESPECTS_INLINE_HINTS = new Set([ + "anthropic-messages", + "bedrock-converse", + "openai-responses", + "openai-chat", + "openai-compatible-chat", +]) +// kilocode_change end const makeHint = (ttlSeconds: number | undefined): CacheHint => ttlSeconds !== undefined ? new CacheHint({ type: "ephemeral", ttlSeconds }) : new CacheHint({ type: "ephemeral" }) diff --git a/packages/llm/src/protocols/openai-chat.ts b/packages/llm/src/protocols/openai-chat.ts index c6ac16c7b3b..7d672563e5a 100644 --- a/packages/llm/src/protocols/openai-chat.ts +++ b/packages/llm/src/protocols/openai-chat.ts @@ -28,6 +28,24 @@ const IMAGE_MIMES = new Set(ProviderShared.IMAGE_MIMES) export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/chat/completions" +// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ +const supportsBreakpoint = (modelId: string) => { + const match = modelId.match(/gpt-(\d+)\.(\d+)/) + if (match) { + const major = Number(match[1]) + const minor = Number(match[2]) + if (major > 5 || (major === 5 && minor >= 6)) return true + } + const majorMatch = modelId.match(/gpt-(\d+)/) + if (majorMatch && Number(majorMatch[1]) >= 6) return true + return false +} + +const OpenAIChatPromptCacheBreakpoint = Schema.Struct({ + mode: Schema.Literal("explicit"), +}) +// kilocode_change end + // ============================================================================= // Request Body Schema // ============================================================================= @@ -57,15 +75,29 @@ const OpenAIChatAssistantToolCall = Schema.Struct({ type OpenAIChatAssistantToolCall = Schema.Schema.Type const OpenAIChatUserContent = Schema.Union([ - Schema.Struct({ type: Schema.Literal("text"), text: Schema.String }), + Schema.Struct({ + type: Schema.Literal("text"), + text: Schema.String, + prompt_cache_breakpoint: Schema.optional(OpenAIChatPromptCacheBreakpoint), // kilocode_change + }), Schema.Struct({ type: Schema.Literal("image_url"), image_url: Schema.Struct({ url: Schema.String }), + prompt_cache_breakpoint: Schema.optional(OpenAIChatPromptCacheBreakpoint), // kilocode_change }), ]) const OpenAIChatMessage = Schema.Union([ - Schema.Struct({ role: Schema.Literal("system"), content: Schema.String }), + // kilocode_change start - support content block array for system/developer messages + Schema.Struct({ + role: Schema.Literal("system"), + content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), + }), + Schema.Struct({ + role: Schema.Literal("developer"), + content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), + }), + // kilocode_change end Schema.Struct({ role: Schema.Literal("user"), content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), @@ -210,21 +242,33 @@ const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part: MediaPart const openAICompatibleReasoningContent = (native: unknown) => isRecord(native) && typeof native.reasoning_content === "string" ? native.reasoning_content : undefined -const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (message: OpenAIChatRequestMessage) { +const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* ( + message: OpenAIChatRequestMessage, + modelId: string, // kilocode_change +) { const content: Array> = [] for (const part of message.content) { + // kilocode_change start + const breakpoint = + "cache" in part && part.cache && supportsBreakpoint(modelId) + ? { prompt_cache_breakpoint: { mode: "explicit" as const } } + : {} if (part.type === "text") { - content.push({ type: "text", text: part.text }) + content.push({ type: "text", text: part.text, ...breakpoint }) continue } if (part.type === "media") { - content.push(yield* lowerMedia(part)) + content.push({ ...(yield* lowerMedia(part)), ...breakpoint }) continue } + // kilocode_change end return yield* ProviderShared.unsupportedContent("OpenAI Chat", "user", ["text", "media"]) } - if (content.every((part) => part.type === "text")) - return { role: "user" as const, content: content.map((part) => part.text).join("\n") } // kilocode_change + // kilocode_change start + const hasBreakpoint = content.some((part) => "prompt_cache_breakpoint" in part && part.prompt_cache_breakpoint) + if (!hasBreakpoint && content.every((part) => part.type === "text")) + return { role: "user" as const, content: content.map((part) => (part as { text: string }).text).join("\n") } + // kilocode_change end return { role: "user" as const, content } }) @@ -284,15 +328,36 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m return { messages, images } }) -const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message: OpenAIChatRequestMessage) { - if (message.role === "user") return [yield* lowerUserMessage(message)] +const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* ( + message: OpenAIChatRequestMessage, + modelId: string, // kilocode_change +) { + if (message.role === "user") return [yield* lowerUserMessage(message, modelId)] // kilocode_change if (message.role === "assistant") return [yield* lowerAssistantMessage(message)] return (yield* lowerToolMessages(message)).messages }) const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: LLMRequest) { + // kilocode_change start + const hasSystemCache = request.system.some((part) => part.cache) && supportsBreakpoint(request.model.id) const system: OpenAIChatMessage[] = - request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }] + request.system.length === 0 + ? [] + : hasSystemCache + ? [ + { + role: "system", + content: [ + { + type: "text", + text: ProviderShared.joinText(request.system), + prompt_cache_breakpoint: { mode: "explicit" }, + }, + ], + }, + ] + : [{ role: "system", content: ProviderShared.joinText(request.system) }] + // kilocode_change end const messages = [...system] const pendingImages: Array> = [] const flushImages = () => { @@ -324,7 +389,7 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: continue } flushImages() - messages.push(...(yield* lowerMessage(message))) + messages.push(...(yield* lowerMessage(message, request.model.id))) // kilocode_change } flushImages() return messages diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index 2e6b67aec72..cdbb27965f7 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -29,16 +29,36 @@ const ADAPTER = "openai-responses" export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/responses" +// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ +const supportsBreakpoint = (modelId: string) => { + const match = modelId.match(/gpt-(\d+)\.(\d+)/) + if (match) { + const major = Number(match[1]) + const minor = Number(match[2]) + if (major > 5 || (major === 5 && minor >= 6)) return true + } + const majorMatch = modelId.match(/gpt-(\d+)/) + if (majorMatch && Number(majorMatch[1]) >= 6) return true + return false +} + +const OpenAIResponsesPromptCacheBreakpoint = Schema.Struct({ + mode: Schema.Literal("explicit"), +}) +// kilocode_change end + // ============================================================================= // Request Body Schema // ============================================================================= const OpenAIResponsesInputText = Schema.Struct({ type: Schema.tag("input_text"), text: Schema.String, + prompt_cache_breakpoint: Schema.optional(OpenAIResponsesPromptCacheBreakpoint), // kilocode_change }) const OpenAIResponsesInputImage = Schema.Struct({ type: Schema.tag("input_image"), image_url: Schema.String, + prompt_cache_breakpoint: Schema.optional(OpenAIResponsesPromptCacheBreakpoint), // kilocode_change }) const OpenAIResponsesInputContent = Schema.Union([OpenAIResponsesInputText, OpenAIResponsesInputImage]) type OpenAIResponsesInputContent = Schema.Schema.Type @@ -76,7 +96,16 @@ const OpenAIResponsesFunctionCallOutput = Schema.Union([ ]) const OpenAIResponsesInputItem = Schema.Union([ - Schema.Struct({ role: Schema.tag("system"), content: Schema.String }), + // kilocode_change start - support content block array for system/developer messages + Schema.Struct({ + role: Schema.Literal("system"), + content: Schema.Union([Schema.String, Schema.Array(OpenAIResponsesInputContent)]), + }), + Schema.Struct({ + role: Schema.Literal("developer"), + content: Schema.Union([Schema.String, Schema.Array(OpenAIResponsesInputContent)]), + }), + // kilocode_change end Schema.Struct({ role: Schema.tag("user"), content: Schema.Array(OpenAIResponsesInputContent) }), Schema.Struct({ role: Schema.tag("assistant"), content: Schema.Array(OpenAIResponsesOutputText) }), OpenAIResponsesReasoningItem, @@ -307,16 +336,23 @@ const hostedToolItemID = (part: ToolResultPart) => { const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* ( part: LLMRequest["messages"][number]["content"][number], + modelId: string, // kilocode_change ) { - if (part.type === "text") return { type: "input_text" as const, text: part.text } + // kilocode_change start + const breakpoint = + "cache" in part && part.cache && supportsBreakpoint(modelId) + ? { prompt_cache_breakpoint: { mode: "explicit" as const } } + : {} + if (part.type === "text") return { type: "input_text" as const, text: part.text, ...breakpoint } if (part.type === "media") { const media = yield* ProviderShared.validateMedia( "OpenAI Responses", part, new Set(ProviderShared.IMAGE_MIMES), ) - return { type: "input_image" as const, image_url: media.dataUrl } + return { type: "input_image" as const, image_url: media.dataUrl, ...breakpoint } } + // kilocode_change end return yield* ProviderShared.unsupportedContent("OpenAI Responses", "user", ["text", "media"]) }) @@ -344,8 +380,26 @@ const lowerToolResultOutput = Effect.fn("OpenAIResponses.lowerToolResultOutput") }) const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (request: LLMRequest) { + // kilocode_change start + const hasSystemCache = request.system.some((part) => part.cache) && supportsBreakpoint(request.model.id) const system: OpenAIResponsesInputItem[] = - request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }] + request.system.length === 0 + ? [] + : hasSystemCache + ? [ + { + role: "system", + content: [ + { + type: "input_text", + text: ProviderShared.joinText(request.system), + prompt_cache_breakpoint: { mode: "explicit" }, + }, + ], + }, + ] + : [{ role: "system", content: ProviderShared.joinText(request.system) }] + // kilocode_change end const input: OpenAIResponsesInputItem[] = [...system] const store = OpenAIOptions.store(request) @@ -363,7 +417,12 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ } if (message.role === "user") { - input.push({ role: "user", content: yield* Effect.forEach(message.content, lowerUserContent) }) + // kilocode_change start + input.push({ + role: "user", + content: yield* Effect.forEach(message.content, (part) => lowerUserContent(part, request.model.id)), + }) + // kilocode_change end continue } diff --git a/packages/llm/test/cache-policy.test.ts b/packages/llm/test/cache-policy.test.ts index a126d9502c5..91a6712d108 100644 --- a/packages/llm/test/cache-policy.test.ts +++ b/packages/llm/test/cache-policy.test.ts @@ -6,6 +6,9 @@ import { AmazonBedrock } from "../src/providers" import * as AnthropicMessages from "../src/protocols/anthropic-messages" import * as Gemini from "../src/protocols/gemini" import * as OpenAIChat from "../src/protocols/openai-chat" +// kilocode_change start +import * as OpenAIResponses from "../src/protocols/openai-responses" +// kilocode_change end import { applyCachePolicy } from "../src/cache-policy" import { it } from "./lib/effect" @@ -21,6 +24,16 @@ const openaiModel = OpenAIChat.route .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) .model({ id: "gpt-4o-mini" }) +// kilocode_change start +const openaiGpt56ResponsesModel = OpenAIResponses.route + .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) + .model({ id: "gpt-5.6" }) + +const openaiGpt56ChatModel = OpenAIChat.route + .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) + .model({ id: "gpt-5.6" }) +// kilocode_change end + const geminiModel = Gemini.route .with({ endpoint: { baseURL: "https://generativelanguage.test/v1beta/" }, @@ -79,7 +92,8 @@ describe("applyCachePolicy", () => { }), ) - it.effect("'auto' is a no-op on OpenAI (implicit caching protocol)", () => + // kilocode_change start + it.effect("'auto' does not emit explicit breakpoints on pre-5.6 OpenAI models", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( LLM.request({ @@ -91,13 +105,79 @@ describe("applyCachePolicy", () => { ) const body = prepared.body as { messages: Array<{ content: unknown }> } - // OpenAI doesn't accept cache_control on messages — policy must skip. + // Older OpenAI models reject prompt_cache_breakpoint — policy must skip. const flat = JSON.stringify(body) + expect(flat).not.toContain("prompt_cache_breakpoint") expect(flat).not.toContain("cache_control") expect(flat).not.toContain("cachePoint") }), ) + it.effect("'auto' emits prompt_cache_breakpoint on stable system prefix and latest user on GPT-5.6 Responses", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: openaiGpt56ResponsesModel, + system: "System instructions", + messages: [ + Message.user("first question"), + Message.assistant("assistant reply"), + Message.user("latest question"), + ], + cache: "auto", + }), + ) + + expect(prepared.body).toMatchObject({ + input: [ + { + role: "system", + content: [{ type: "input_text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], + }, + { role: "user", content: [{ type: "input_text", text: "first question" }] }, + { role: "assistant", content: [{ type: "output_text", text: "assistant reply" }] }, + { + role: "user", + content: [{ type: "input_text", text: "latest question", prompt_cache_breakpoint: { mode: "explicit" } }], + }, + ], + }) + }), + ) + + it.effect("'auto' emits prompt_cache_breakpoint on stable system prefix and latest user on GPT-5.6 Chat", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: openaiGpt56ChatModel, + system: "System instructions", + messages: [ + Message.user("first question"), + Message.assistant("assistant reply"), + Message.user("latest question"), + ], + cache: "auto", + }), + ) + + expect(prepared.body).toMatchObject({ + messages: [ + { + role: "system", + content: [{ type: "text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], + }, + { role: "user", content: "first question" }, + { role: "assistant", content: "assistant reply" }, + { + role: "user", + content: [{ type: "text", text: "latest question", prompt_cache_breakpoint: { mode: "explicit" } }], + }, + ], + }) + }), + ) + // kilocode_change end + it.effect("'auto' is a no-op on Gemini (out-of-band caching protocol)", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 8112996020f..4b1c28e6f5f 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -328,6 +328,20 @@ function normalizeMessages( return msgs } +// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ +function supportsOpenAICacheBreakpoint(modelId: string): boolean { + const match = modelId.match(/gpt-(\d+)\.(\d+)/) + if (match) { + const major = Number(match[1]) + const minor = Number(match[2]) + if (major > 5 || (major === 5 && minor >= 6)) return true + } + const majorMatch = modelId.match(/gpt-(\d+)/) + if (majorMatch && Number(majorMatch[1]) >= 6) return true + return false +} +// kilocode_change end + function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] { const system = msgs.filter((msg) => msg.role === "system").slice(0, 2) const final = msgs.filter((msg) => msg.role !== "system").slice(-2) @@ -351,6 +365,18 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage alibaba: { cacheControl: { type: "ephemeral" }, }, + // kilocode_change start + ...(supportsOpenAICacheBreakpoint(model.api.id) + ? { + openai: { + promptCacheBreakpoint: { mode: "explicit" }, + }, + azure: { + promptCacheBreakpoint: { mode: "explicit" }, + }, + } + : {}), + // kilocode_change end } for (const msg of unique([...system, ...final])) { @@ -438,6 +464,7 @@ function mapProviderOptions( export function message(msgs: ModelMessage[], model: Provider.Model, options: Record) { msgs = unsupportedParts(msgs, model) msgs = normalizeMessages(msgs, model, options) + // kilocode_change start - apply caching for anthropic, alibaba, and GPT-5.6+ openai/azure if ( (model.providerID === "anthropic" || model.providerID === "google-vertex-anthropic" || @@ -446,11 +473,17 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.id.includes("anthropic") || model.id.includes("claude") || model.api.npm === "@ai-sdk/anthropic" || - model.api.npm === "@ai-sdk/alibaba") && + model.api.npm === "@ai-sdk/alibaba" || + ((model.api.npm === "@ai-sdk/openai" || + model.api.npm === "@ai-sdk/azure" || + model.providerID === "openai" || + model.providerID === "azure") && + supportsOpenAICacheBreakpoint(model.api.id))) && model.api.npm !== "@ai-sdk/gateway" ) { msgs = applyCaching(msgs, model) } + // kilocode_change end // Remap providerOptions keys from stored providerID to expected SDK key const key = sdkKey(model.api.npm) diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index dfa901724b1..14542cdaa41 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3029,6 +3029,70 @@ describe("ProviderTransform.message - cache control on gateway", () => { }, }) }) + + // kilocode_change start + test("openai gpt-5.6 applies promptCacheBreakpoint", () => { + const model = createModel({ + providerID: "openai", + api: { + id: "gpt-5.6", + url: "https://api.openai.com/v1", + npm: "@ai-sdk/openai", + }, + id: "gpt-5.6", + }) + const msgs = [ + { + role: "system", + content: "You are a helpful assistant", + }, + { + role: "user", + content: "Hello", + }, + ] as any[] + + const result = ProviderTransform.message(msgs, model, {}) as any[] + + expect(result[0].providerOptions.openai).toEqual({ + promptCacheBreakpoint: { + mode: "explicit", + }, + }) + expect(result[1].providerOptions.openai).toEqual({ + promptCacheBreakpoint: { + mode: "explicit", + }, + }) + }) + + test("openai pre-5.6 does not apply promptCacheBreakpoint", () => { + const model = createModel({ + providerID: "openai", + api: { + id: "gpt-4o", + url: "https://api.openai.com/v1", + npm: "@ai-sdk/openai", + }, + id: "gpt-4o", + }) + const msgs = [ + { + role: "system", + content: "You are a helpful assistant", + }, + { + role: "user", + content: "Hello", + }, + ] as any[] + + const result = ProviderTransform.message(msgs, model, {}) as any[] + + expect(result[0].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() + expect(result[1].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() + }) + // kilocode_change end }) describe("ProviderTransform.temperature - Cohere North", () => { From 435c029f8cad49af70be2dd1857a85636d4f406b Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 17:39:22 +0000 Subject: [PATCH 25/39] refactor(llm): extract shared supportsPromptCacheBreakpoint and restrict inline hints to native openai routes Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/llm/src/cache-policy.ts | 1 - packages/llm/src/index.ts | 1 + packages/llm/src/protocols/openai-chat.ts | 16 ++-------------- .../llm/src/protocols/openai-responses.ts | 16 ++-------------- .../llm/src/protocols/utils/openai-options.ts | 14 ++++++++++++++ packages/opencode/src/provider/transform.ts | 19 +++---------------- 6 files changed, 22 insertions(+), 45 deletions(-) diff --git a/packages/llm/src/cache-policy.ts b/packages/llm/src/cache-policy.ts index 3387c3a67d0..e1caf0d67e5 100644 --- a/packages/llm/src/cache-policy.ts +++ b/packages/llm/src/cache-policy.ts @@ -43,7 +43,6 @@ const RESPECTS_INLINE_HINTS = new Set([ "bedrock-converse", "openai-responses", "openai-chat", - "openai-compatible-chat", ]) // kilocode_change end diff --git a/packages/llm/src/index.ts b/packages/llm/src/index.ts index 735520ff77c..1a9fd1fd3de 100644 --- a/packages/llm/src/index.ts +++ b/packages/llm/src/index.ts @@ -26,6 +26,7 @@ export type { ToolToModelOutput, } from "./tool" export * as LLM from "./llm" +export { supportsPromptCacheBreakpoint } from "./protocols/utils/openai-options" // kilocode_change export type { Definition as ProviderDefinition, ModelFactory as ProviderModelFactory, diff --git a/packages/llm/src/protocols/openai-chat.ts b/packages/llm/src/protocols/openai-chat.ts index 7d672563e5a..9db95915669 100644 --- a/packages/llm/src/protocols/openai-chat.ts +++ b/packages/llm/src/protocols/openai-chat.ts @@ -29,18 +29,6 @@ export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/chat/completions" // kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -const supportsBreakpoint = (modelId: string) => { - const match = modelId.match(/gpt-(\d+)\.(\d+)/) - if (match) { - const major = Number(match[1]) - const minor = Number(match[2]) - if (major > 5 || (major === 5 && minor >= 6)) return true - } - const majorMatch = modelId.match(/gpt-(\d+)/) - if (majorMatch && Number(majorMatch[1]) >= 6) return true - return false -} - const OpenAIChatPromptCacheBreakpoint = Schema.Struct({ mode: Schema.Literal("explicit"), }) @@ -250,7 +238,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* ( for (const part of message.content) { // kilocode_change start const breakpoint = - "cache" in part && part.cache && supportsBreakpoint(modelId) + "cache" in part && part.cache && OpenAIOptions.supportsPromptCacheBreakpoint(modelId) ? { prompt_cache_breakpoint: { mode: "explicit" as const } } : {} if (part.type === "text") { @@ -339,7 +327,7 @@ const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* ( const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: LLMRequest) { // kilocode_change start - const hasSystemCache = request.system.some((part) => part.cache) && supportsBreakpoint(request.model.id) + const hasSystemCache = request.system.some((part) => part.cache) && OpenAIOptions.supportsPromptCacheBreakpoint(request.model.id) const system: OpenAIChatMessage[] = request.system.length === 0 ? [] diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index cdbb27965f7..74b4524518d 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -30,18 +30,6 @@ export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/responses" // kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -const supportsBreakpoint = (modelId: string) => { - const match = modelId.match(/gpt-(\d+)\.(\d+)/) - if (match) { - const major = Number(match[1]) - const minor = Number(match[2]) - if (major > 5 || (major === 5 && minor >= 6)) return true - } - const majorMatch = modelId.match(/gpt-(\d+)/) - if (majorMatch && Number(majorMatch[1]) >= 6) return true - return false -} - const OpenAIResponsesPromptCacheBreakpoint = Schema.Struct({ mode: Schema.Literal("explicit"), }) @@ -340,7 +328,7 @@ const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* ) { // kilocode_change start const breakpoint = - "cache" in part && part.cache && supportsBreakpoint(modelId) + "cache" in part && part.cache && OpenAIOptions.supportsPromptCacheBreakpoint(modelId) ? { prompt_cache_breakpoint: { mode: "explicit" as const } } : {} if (part.type === "text") return { type: "input_text" as const, text: part.text, ...breakpoint } @@ -381,7 +369,7 @@ const lowerToolResultOutput = Effect.fn("OpenAIResponses.lowerToolResultOutput") const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (request: LLMRequest) { // kilocode_change start - const hasSystemCache = request.system.some((part) => part.cache) && supportsBreakpoint(request.model.id) + const hasSystemCache = request.system.some((part) => part.cache) && OpenAIOptions.supportsPromptCacheBreakpoint(request.model.id) const system: OpenAIResponsesInputItem[] = request.system.length === 0 ? [] diff --git a/packages/llm/src/protocols/utils/openai-options.ts b/packages/llm/src/protocols/utils/openai-options.ts index 59ac7abc874..708dc07d28c 100644 --- a/packages/llm/src/protocols/utils/openai-options.ts +++ b/packages/llm/src/protocols/utils/openai-options.ts @@ -101,4 +101,18 @@ export const instructions = (request: LLMRequest) => { return typeof value === "string" ? value : undefined } +// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ +export const supportsPromptCacheBreakpoint = (modelId: string): boolean => { + const match = modelId.match(/gpt-(\d+)\.(\d+)/) + if (match) { + const major = Number(match[1]) + const minor = Number(match[2]) + if (major > 5 || (major === 5 && minor >= 6)) return true + } + const majorMatch = modelId.match(/gpt-(\d+)/) + if (majorMatch && Number(majorMatch[1]) >= 6) return true + return false +} +// kilocode_change end + export * as OpenAIOptions from "./openai-options" diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 4b1c28e6f5f..dbe3f58da25 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1,6 +1,7 @@ import type { ModelMessage, ToolResultPart } from "ai" import { mergeDeep, unique } from "remeda" import type { JSONSchema7 } from "@ai-sdk/provider" +import { supportsPromptCacheBreakpoint } from "@opencode-ai/llm" // kilocode_change import type * as Provider from "./provider" import type * as ModelsDev from "@opencode-ai/core/models-dev" import { iife } from "@/util/iife" @@ -328,20 +329,6 @@ function normalizeMessages( return msgs } -// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -function supportsOpenAICacheBreakpoint(modelId: string): boolean { - const match = modelId.match(/gpt-(\d+)\.(\d+)/) - if (match) { - const major = Number(match[1]) - const minor = Number(match[2]) - if (major > 5 || (major === 5 && minor >= 6)) return true - } - const majorMatch = modelId.match(/gpt-(\d+)/) - if (majorMatch && Number(majorMatch[1]) >= 6) return true - return false -} -// kilocode_change end - function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] { const system = msgs.filter((msg) => msg.role === "system").slice(0, 2) const final = msgs.filter((msg) => msg.role !== "system").slice(-2) @@ -366,7 +353,7 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage cacheControl: { type: "ephemeral" }, }, // kilocode_change start - ...(supportsOpenAICacheBreakpoint(model.api.id) + ...(supportsPromptCacheBreakpoint(model.api.id) ? { openai: { promptCacheBreakpoint: { mode: "explicit" }, @@ -478,7 +465,7 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.api.npm === "@ai-sdk/azure" || model.providerID === "openai" || model.providerID === "azure") && - supportsOpenAICacheBreakpoint(model.api.id))) && + supportsPromptCacheBreakpoint(model.api.id))) && model.api.npm !== "@ai-sdk/gateway" ) { msgs = applyCaching(msgs, model) From 944ab18304ff4b26574daa546ef745b2c6337efe Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:17:49 +0000 Subject: [PATCH 26/39] fix(llm): place prompt cache breakpoint before trailing environment details Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/llm/src/cache-policy.ts | 21 +++++++-- packages/llm/test/cache-policy.test.ts | 46 +++++++++++++++++++ packages/opencode/src/provider/transform.ts | 28 ++++++++--- .../opencode/test/provider/transform.test.ts | 35 ++++++++++++++ 4 files changed, 120 insertions(+), 10 deletions(-) diff --git a/packages/llm/src/cache-policy.ts b/packages/llm/src/cache-policy.ts index e1caf0d67e5..96cd7b38bdb 100644 --- a/packages/llm/src/cache-policy.ts +++ b/packages/llm/src/cache-policy.ts @@ -66,15 +66,28 @@ const markLastSystem = (system: LLMRequest["system"], hint: CacheHint): LLMReque const lastIndexOfRole = (messages: ReadonlyArray, role: Message["role"]): number => messages.findLastIndex((m) => m.role === role) -// Mark the last text part of `messages[index]`. If no text part exists, mark -// the last content part regardless of type — that's the breakpoint position -// in tool-result-only messages too. +// kilocode_change start - mark the last stable text part of `messages[index]` (skipping trailing ephemeral details) +const isEphemeralPart = (part: ContentPart): boolean => { + if (part.type === "text" && (part.text.startsWith("") || part.text.includes(""))) + return true + if ("metadata" in part && part.metadata && Boolean((part.metadata as Record).synthetic)) + return true + return false +} + const markMessageAt = (messages: ReadonlyArray, index: number, hint: CacheHint): ReadonlyArray => { if (index < 0 || index >= messages.length) return messages const target = messages[index]! if (target.content.length === 0) return messages + const lastStableTextIndex = target.content.findLastIndex((part) => part.type === "text" && !isEphemeralPart(part)) const lastTextIndex = target.content.findLastIndex((part) => part.type === "text") - const markAt = lastTextIndex >= 0 ? lastTextIndex : target.content.length - 1 + const markAt = + lastStableTextIndex >= 0 + ? lastStableTextIndex + : lastTextIndex >= 0 + ? lastTextIndex + : target.content.length - 1 + // kilocode_change end const existing = target.content[markAt]! if ("cache" in existing && existing.cache) return messages const nextContent = target.content.map((part, i) => (i === markAt ? ({ ...part, cache: hint } as ContentPart) : part)) diff --git a/packages/llm/test/cache-policy.test.ts b/packages/llm/test/cache-policy.test.ts index 91a6712d108..b0f3256e988 100644 --- a/packages/llm/test/cache-policy.test.ts +++ b/packages/llm/test/cache-policy.test.ts @@ -176,6 +176,52 @@ describe("applyCachePolicy", () => { }) }), ) + + it.effect("places prompt_cache_breakpoint BEFORE trailing part", () => + Effect.gen(function* () { + const envBlock = "\nCurrent time: 2026-08-08T18:00:00+00:00\n" + const prepared = yield* LLMClient.prepare( + LLM.request({ + model: openaiGpt56ResponsesModel, + system: "System instructions", + messages: [ + Message.user([ + { type: "text", text: "Please inspect the codebase" }, + { type: "text", text: envBlock }, + ]), + ], + cache: "auto", + }), + ) + + expect(prepared.body).toMatchObject({ + input: [ + { + role: "system", + content: [{ type: "input_text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], + }, + { + role: "user", + content: [ + { + type: "input_text", + text: "Please inspect the codebase", + prompt_cache_breakpoint: { mode: "explicit" }, + }, + { + type: "input_text", + text: envBlock, + }, + ], + }, + ], + }) + + const userContent = (prepared.body as any).input[1].content + expect(userContent[0].prompt_cache_breakpoint).toEqual({ mode: "explicit" }) + expect(userContent[1].prompt_cache_breakpoint).toBeUndefined() + }), + ) // kilocode_change end it.effect("'auto' is a no-op on Gemini (out-of-band caching protocol)", () => diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index dbe3f58da25..f0173b50303 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -371,20 +371,36 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage model.providerID === "anthropic" || model.providerID.includes("bedrock") || model.api.npm === "@ai-sdk/amazon-bedrock" + // kilocode_change start - place caching breakpoint on stable content before trailing const shouldUseContentOptions = !useMessageLevelOptions && Array.isArray(msg.content) && msg.content.length > 0 if (shouldUseContentOptions) { - const lastContent = msg.content[msg.content.length - 1] + const parts = msg.content as any[] + const targetIndex = parts.findLastIndex( + (part) => + part && + typeof part === "object" && + part.type !== "tool-approval-request" && + part.type !== "tool-approval-response" && + !( + part.type === "text" && + typeof part.text === "string" && + (part.text.startsWith("") || part.text.includes("")) + ) && + !part.synthetic, + ) + const targetContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1] if ( - lastContent && - typeof lastContent === "object" && - lastContent.type !== "tool-approval-request" && - lastContent.type !== "tool-approval-response" + targetContent && + typeof targetContent === "object" && + targetContent.type !== "tool-approval-request" && + targetContent.type !== "tool-approval-response" ) { - lastContent.providerOptions = mergeDeep(lastContent.providerOptions ?? {}, providerOptions) + targetContent.providerOptions = mergeDeep(targetContent.providerOptions ?? {}, providerOptions) continue } } + // kilocode_change end msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions) } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 14542cdaa41..2f8c7d23b18 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3092,6 +3092,41 @@ describe("ProviderTransform.message - cache control on gateway", () => { expect(result[0].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() expect(result[1].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() }) + + test("openai gpt-5.6 places promptCacheBreakpoint before trailing environment_details", () => { + const model = createModel({ + providerID: "openai", + api: { + id: "gpt-5.6", + url: "https://api.openai.com/v1", + npm: "@ai-sdk/openai", + }, + id: "gpt-5.6", + }) + const envBlock = "\nCurrent time: 2026-08-08T18:00:00+00:00\n" + const msgs = [ + { + role: "system", + content: "You are a helpful assistant", + }, + { + role: "user", + content: [ + { type: "text", text: "Please review the changes" }, + { type: "text", text: envBlock }, + ], + }, + ] as any[] + + const result = ProviderTransform.message(msgs, model, {}) as any[] + + expect(result[1].content[0].providerOptions.openai).toEqual({ + promptCacheBreakpoint: { + mode: "explicit", + }, + }) + expect(result[1].content[1].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() + }) // kilocode_change end }) From 2f72a8b72dc16b226f937384c8fd11a140262e1e Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:22:35 +0000 Subject: [PATCH 27/39] refactor(cli): limit explicit prompt cache breakpoint changes to Vercel AI SDK path Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/llm/src/cache-policy.ts | 34 ++--- packages/llm/src/index.ts | 1 - packages/llm/src/protocols/openai-chat.ts | 75 ++-------- .../llm/src/protocols/openai-responses.ts | 57 +------- .../llm/src/protocols/utils/openai-options.ts | 14 -- packages/llm/test/cache-policy.test.ts | 130 +----------------- packages/opencode/src/provider/transform.ts | 15 +- 7 files changed, 40 insertions(+), 286 deletions(-) diff --git a/packages/llm/src/cache-policy.ts b/packages/llm/src/cache-policy.ts index 96cd7b38bdb..60f96dc69aa 100644 --- a/packages/llm/src/cache-policy.ts +++ b/packages/llm/src/cache-policy.ts @@ -36,15 +36,10 @@ const resolve = (policy: CachePolicy | undefined): CachePolicyObject => { return policy } -// kilocode_change start - Protocols whose wire format supports inline cache markers / explicit breakpoints. -// Gemini uses out-of-band CachedContent. -const RESPECTS_INLINE_HINTS = new Set([ - "anthropic-messages", - "bedrock-converse", - "openai-responses", - "openai-chat", -]) -// kilocode_change end +// Protocols whose wire format ignores inline cache markers (OpenAI's implicit +// prefix caching, Gemini's implicit + out-of-band CachedContent). Skip the +// whole policy pass for these — emitting hints would be harmless but pointless. +const RESPECTS_INLINE_HINTS = new Set(["anthropic-messages", "bedrock-converse"]) const makeHint = (ttlSeconds: number | undefined): CacheHint => ttlSeconds !== undefined ? new CacheHint({ type: "ephemeral", ttlSeconds }) : new CacheHint({ type: "ephemeral" }) @@ -66,28 +61,15 @@ const markLastSystem = (system: LLMRequest["system"], hint: CacheHint): LLMReque const lastIndexOfRole = (messages: ReadonlyArray, role: Message["role"]): number => messages.findLastIndex((m) => m.role === role) -// kilocode_change start - mark the last stable text part of `messages[index]` (skipping trailing ephemeral details) -const isEphemeralPart = (part: ContentPart): boolean => { - if (part.type === "text" && (part.text.startsWith("") || part.text.includes(""))) - return true - if ("metadata" in part && part.metadata && Boolean((part.metadata as Record).synthetic)) - return true - return false -} - +// Mark the last text part of `messages[index]`. If no text part exists, mark +// the last content part regardless of type — that's the breakpoint position +// in tool-result-only messages too. const markMessageAt = (messages: ReadonlyArray, index: number, hint: CacheHint): ReadonlyArray => { if (index < 0 || index >= messages.length) return messages const target = messages[index]! if (target.content.length === 0) return messages - const lastStableTextIndex = target.content.findLastIndex((part) => part.type === "text" && !isEphemeralPart(part)) const lastTextIndex = target.content.findLastIndex((part) => part.type === "text") - const markAt = - lastStableTextIndex >= 0 - ? lastStableTextIndex - : lastTextIndex >= 0 - ? lastTextIndex - : target.content.length - 1 - // kilocode_change end + const markAt = lastTextIndex >= 0 ? lastTextIndex : target.content.length - 1 const existing = target.content[markAt]! if ("cache" in existing && existing.cache) return messages const nextContent = target.content.map((part, i) => (i === markAt ? ({ ...part, cache: hint } as ContentPart) : part)) diff --git a/packages/llm/src/index.ts b/packages/llm/src/index.ts index 1a9fd1fd3de..735520ff77c 100644 --- a/packages/llm/src/index.ts +++ b/packages/llm/src/index.ts @@ -26,7 +26,6 @@ export type { ToolToModelOutput, } from "./tool" export * as LLM from "./llm" -export { supportsPromptCacheBreakpoint } from "./protocols/utils/openai-options" // kilocode_change export type { Definition as ProviderDefinition, ModelFactory as ProviderModelFactory, diff --git a/packages/llm/src/protocols/openai-chat.ts b/packages/llm/src/protocols/openai-chat.ts index 9db95915669..c6ac16c7b3b 100644 --- a/packages/llm/src/protocols/openai-chat.ts +++ b/packages/llm/src/protocols/openai-chat.ts @@ -28,12 +28,6 @@ const IMAGE_MIMES = new Set(ProviderShared.IMAGE_MIMES) export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/chat/completions" -// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -const OpenAIChatPromptCacheBreakpoint = Schema.Struct({ - mode: Schema.Literal("explicit"), -}) -// kilocode_change end - // ============================================================================= // Request Body Schema // ============================================================================= @@ -63,29 +57,15 @@ const OpenAIChatAssistantToolCall = Schema.Struct({ type OpenAIChatAssistantToolCall = Schema.Schema.Type const OpenAIChatUserContent = Schema.Union([ - Schema.Struct({ - type: Schema.Literal("text"), - text: Schema.String, - prompt_cache_breakpoint: Schema.optional(OpenAIChatPromptCacheBreakpoint), // kilocode_change - }), + Schema.Struct({ type: Schema.Literal("text"), text: Schema.String }), Schema.Struct({ type: Schema.Literal("image_url"), image_url: Schema.Struct({ url: Schema.String }), - prompt_cache_breakpoint: Schema.optional(OpenAIChatPromptCacheBreakpoint), // kilocode_change }), ]) const OpenAIChatMessage = Schema.Union([ - // kilocode_change start - support content block array for system/developer messages - Schema.Struct({ - role: Schema.Literal("system"), - content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), - }), - Schema.Struct({ - role: Schema.Literal("developer"), - content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), - }), - // kilocode_change end + Schema.Struct({ role: Schema.Literal("system"), content: Schema.String }), Schema.Struct({ role: Schema.Literal("user"), content: Schema.Union([Schema.String, Schema.Array(OpenAIChatUserContent)]), @@ -230,33 +210,21 @@ const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part: MediaPart const openAICompatibleReasoningContent = (native: unknown) => isRecord(native) && typeof native.reasoning_content === "string" ? native.reasoning_content : undefined -const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* ( - message: OpenAIChatRequestMessage, - modelId: string, // kilocode_change -) { +const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (message: OpenAIChatRequestMessage) { const content: Array> = [] for (const part of message.content) { - // kilocode_change start - const breakpoint = - "cache" in part && part.cache && OpenAIOptions.supportsPromptCacheBreakpoint(modelId) - ? { prompt_cache_breakpoint: { mode: "explicit" as const } } - : {} if (part.type === "text") { - content.push({ type: "text", text: part.text, ...breakpoint }) + content.push({ type: "text", text: part.text }) continue } if (part.type === "media") { - content.push({ ...(yield* lowerMedia(part)), ...breakpoint }) + content.push(yield* lowerMedia(part)) continue } - // kilocode_change end return yield* ProviderShared.unsupportedContent("OpenAI Chat", "user", ["text", "media"]) } - // kilocode_change start - const hasBreakpoint = content.some((part) => "prompt_cache_breakpoint" in part && part.prompt_cache_breakpoint) - if (!hasBreakpoint && content.every((part) => part.type === "text")) - return { role: "user" as const, content: content.map((part) => (part as { text: string }).text).join("\n") } - // kilocode_change end + if (content.every((part) => part.type === "text")) + return { role: "user" as const, content: content.map((part) => part.text).join("\n") } // kilocode_change return { role: "user" as const, content } }) @@ -316,36 +284,15 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m return { messages, images } }) -const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* ( - message: OpenAIChatRequestMessage, - modelId: string, // kilocode_change -) { - if (message.role === "user") return [yield* lowerUserMessage(message, modelId)] // kilocode_change +const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message: OpenAIChatRequestMessage) { + if (message.role === "user") return [yield* lowerUserMessage(message)] if (message.role === "assistant") return [yield* lowerAssistantMessage(message)] return (yield* lowerToolMessages(message)).messages }) const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: LLMRequest) { - // kilocode_change start - const hasSystemCache = request.system.some((part) => part.cache) && OpenAIOptions.supportsPromptCacheBreakpoint(request.model.id) const system: OpenAIChatMessage[] = - request.system.length === 0 - ? [] - : hasSystemCache - ? [ - { - role: "system", - content: [ - { - type: "text", - text: ProviderShared.joinText(request.system), - prompt_cache_breakpoint: { mode: "explicit" }, - }, - ], - }, - ] - : [{ role: "system", content: ProviderShared.joinText(request.system) }] - // kilocode_change end + request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }] const messages = [...system] const pendingImages: Array> = [] const flushImages = () => { @@ -377,7 +324,7 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: continue } flushImages() - messages.push(...(yield* lowerMessage(message, request.model.id))) // kilocode_change + messages.push(...(yield* lowerMessage(message))) } flushImages() return messages diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index 74b4524518d..2e6b67aec72 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -29,24 +29,16 @@ const ADAPTER = "openai-responses" export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const PATH = "/responses" -// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -const OpenAIResponsesPromptCacheBreakpoint = Schema.Struct({ - mode: Schema.Literal("explicit"), -}) -// kilocode_change end - // ============================================================================= // Request Body Schema // ============================================================================= const OpenAIResponsesInputText = Schema.Struct({ type: Schema.tag("input_text"), text: Schema.String, - prompt_cache_breakpoint: Schema.optional(OpenAIResponsesPromptCacheBreakpoint), // kilocode_change }) const OpenAIResponsesInputImage = Schema.Struct({ type: Schema.tag("input_image"), image_url: Schema.String, - prompt_cache_breakpoint: Schema.optional(OpenAIResponsesPromptCacheBreakpoint), // kilocode_change }) const OpenAIResponsesInputContent = Schema.Union([OpenAIResponsesInputText, OpenAIResponsesInputImage]) type OpenAIResponsesInputContent = Schema.Schema.Type @@ -84,16 +76,7 @@ const OpenAIResponsesFunctionCallOutput = Schema.Union([ ]) const OpenAIResponsesInputItem = Schema.Union([ - // kilocode_change start - support content block array for system/developer messages - Schema.Struct({ - role: Schema.Literal("system"), - content: Schema.Union([Schema.String, Schema.Array(OpenAIResponsesInputContent)]), - }), - Schema.Struct({ - role: Schema.Literal("developer"), - content: Schema.Union([Schema.String, Schema.Array(OpenAIResponsesInputContent)]), - }), - // kilocode_change end + Schema.Struct({ role: Schema.tag("system"), content: Schema.String }), Schema.Struct({ role: Schema.tag("user"), content: Schema.Array(OpenAIResponsesInputContent) }), Schema.Struct({ role: Schema.tag("assistant"), content: Schema.Array(OpenAIResponsesOutputText) }), OpenAIResponsesReasoningItem, @@ -324,23 +307,16 @@ const hostedToolItemID = (part: ToolResultPart) => { const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* ( part: LLMRequest["messages"][number]["content"][number], - modelId: string, // kilocode_change ) { - // kilocode_change start - const breakpoint = - "cache" in part && part.cache && OpenAIOptions.supportsPromptCacheBreakpoint(modelId) - ? { prompt_cache_breakpoint: { mode: "explicit" as const } } - : {} - if (part.type === "text") return { type: "input_text" as const, text: part.text, ...breakpoint } + if (part.type === "text") return { type: "input_text" as const, text: part.text } if (part.type === "media") { const media = yield* ProviderShared.validateMedia( "OpenAI Responses", part, new Set(ProviderShared.IMAGE_MIMES), ) - return { type: "input_image" as const, image_url: media.dataUrl, ...breakpoint } + return { type: "input_image" as const, image_url: media.dataUrl } } - // kilocode_change end return yield* ProviderShared.unsupportedContent("OpenAI Responses", "user", ["text", "media"]) }) @@ -368,26 +344,8 @@ const lowerToolResultOutput = Effect.fn("OpenAIResponses.lowerToolResultOutput") }) const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (request: LLMRequest) { - // kilocode_change start - const hasSystemCache = request.system.some((part) => part.cache) && OpenAIOptions.supportsPromptCacheBreakpoint(request.model.id) const system: OpenAIResponsesInputItem[] = - request.system.length === 0 - ? [] - : hasSystemCache - ? [ - { - role: "system", - content: [ - { - type: "input_text", - text: ProviderShared.joinText(request.system), - prompt_cache_breakpoint: { mode: "explicit" }, - }, - ], - }, - ] - : [{ role: "system", content: ProviderShared.joinText(request.system) }] - // kilocode_change end + request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }] const input: OpenAIResponsesInputItem[] = [...system] const store = OpenAIOptions.store(request) @@ -405,12 +363,7 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ } if (message.role === "user") { - // kilocode_change start - input.push({ - role: "user", - content: yield* Effect.forEach(message.content, (part) => lowerUserContent(part, request.model.id)), - }) - // kilocode_change end + input.push({ role: "user", content: yield* Effect.forEach(message.content, lowerUserContent) }) continue } diff --git a/packages/llm/src/protocols/utils/openai-options.ts b/packages/llm/src/protocols/utils/openai-options.ts index 708dc07d28c..59ac7abc874 100644 --- a/packages/llm/src/protocols/utils/openai-options.ts +++ b/packages/llm/src/protocols/utils/openai-options.ts @@ -101,18 +101,4 @@ export const instructions = (request: LLMRequest) => { return typeof value === "string" ? value : undefined } -// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ -export const supportsPromptCacheBreakpoint = (modelId: string): boolean => { - const match = modelId.match(/gpt-(\d+)\.(\d+)/) - if (match) { - const major = Number(match[1]) - const minor = Number(match[2]) - if (major > 5 || (major === 5 && minor >= 6)) return true - } - const majorMatch = modelId.match(/gpt-(\d+)/) - if (majorMatch && Number(majorMatch[1]) >= 6) return true - return false -} -// kilocode_change end - export * as OpenAIOptions from "./openai-options" diff --git a/packages/llm/test/cache-policy.test.ts b/packages/llm/test/cache-policy.test.ts index b0f3256e988..a126d9502c5 100644 --- a/packages/llm/test/cache-policy.test.ts +++ b/packages/llm/test/cache-policy.test.ts @@ -6,9 +6,6 @@ import { AmazonBedrock } from "../src/providers" import * as AnthropicMessages from "../src/protocols/anthropic-messages" import * as Gemini from "../src/protocols/gemini" import * as OpenAIChat from "../src/protocols/openai-chat" -// kilocode_change start -import * as OpenAIResponses from "../src/protocols/openai-responses" -// kilocode_change end import { applyCachePolicy } from "../src/cache-policy" import { it } from "./lib/effect" @@ -24,16 +21,6 @@ const openaiModel = OpenAIChat.route .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) .model({ id: "gpt-4o-mini" }) -// kilocode_change start -const openaiGpt56ResponsesModel = OpenAIResponses.route - .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) - .model({ id: "gpt-5.6" }) - -const openaiGpt56ChatModel = OpenAIChat.route - .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) - .model({ id: "gpt-5.6" }) -// kilocode_change end - const geminiModel = Gemini.route .with({ endpoint: { baseURL: "https://generativelanguage.test/v1beta/" }, @@ -92,8 +79,7 @@ describe("applyCachePolicy", () => { }), ) - // kilocode_change start - it.effect("'auto' does not emit explicit breakpoints on pre-5.6 OpenAI models", () => + it.effect("'auto' is a no-op on OpenAI (implicit caching protocol)", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( LLM.request({ @@ -105,125 +91,13 @@ describe("applyCachePolicy", () => { ) const body = prepared.body as { messages: Array<{ content: unknown }> } - // Older OpenAI models reject prompt_cache_breakpoint — policy must skip. + // OpenAI doesn't accept cache_control on messages — policy must skip. const flat = JSON.stringify(body) - expect(flat).not.toContain("prompt_cache_breakpoint") expect(flat).not.toContain("cache_control") expect(flat).not.toContain("cachePoint") }), ) - it.effect("'auto' emits prompt_cache_breakpoint on stable system prefix and latest user on GPT-5.6 Responses", () => - Effect.gen(function* () { - const prepared = yield* LLMClient.prepare( - LLM.request({ - model: openaiGpt56ResponsesModel, - system: "System instructions", - messages: [ - Message.user("first question"), - Message.assistant("assistant reply"), - Message.user("latest question"), - ], - cache: "auto", - }), - ) - - expect(prepared.body).toMatchObject({ - input: [ - { - role: "system", - content: [{ type: "input_text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], - }, - { role: "user", content: [{ type: "input_text", text: "first question" }] }, - { role: "assistant", content: [{ type: "output_text", text: "assistant reply" }] }, - { - role: "user", - content: [{ type: "input_text", text: "latest question", prompt_cache_breakpoint: { mode: "explicit" } }], - }, - ], - }) - }), - ) - - it.effect("'auto' emits prompt_cache_breakpoint on stable system prefix and latest user on GPT-5.6 Chat", () => - Effect.gen(function* () { - const prepared = yield* LLMClient.prepare( - LLM.request({ - model: openaiGpt56ChatModel, - system: "System instructions", - messages: [ - Message.user("first question"), - Message.assistant("assistant reply"), - Message.user("latest question"), - ], - cache: "auto", - }), - ) - - expect(prepared.body).toMatchObject({ - messages: [ - { - role: "system", - content: [{ type: "text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], - }, - { role: "user", content: "first question" }, - { role: "assistant", content: "assistant reply" }, - { - role: "user", - content: [{ type: "text", text: "latest question", prompt_cache_breakpoint: { mode: "explicit" } }], - }, - ], - }) - }), - ) - - it.effect("places prompt_cache_breakpoint BEFORE trailing part", () => - Effect.gen(function* () { - const envBlock = "\nCurrent time: 2026-08-08T18:00:00+00:00\n" - const prepared = yield* LLMClient.prepare( - LLM.request({ - model: openaiGpt56ResponsesModel, - system: "System instructions", - messages: [ - Message.user([ - { type: "text", text: "Please inspect the codebase" }, - { type: "text", text: envBlock }, - ]), - ], - cache: "auto", - }), - ) - - expect(prepared.body).toMatchObject({ - input: [ - { - role: "system", - content: [{ type: "input_text", text: "System instructions", prompt_cache_breakpoint: { mode: "explicit" } }], - }, - { - role: "user", - content: [ - { - type: "input_text", - text: "Please inspect the codebase", - prompt_cache_breakpoint: { mode: "explicit" }, - }, - { - type: "input_text", - text: envBlock, - }, - ], - }, - ], - }) - - const userContent = (prepared.body as any).input[1].content - expect(userContent[0].prompt_cache_breakpoint).toEqual({ mode: "explicit" }) - expect(userContent[1].prompt_cache_breakpoint).toBeUndefined() - }), - ) - // kilocode_change end - it.effect("'auto' is a no-op on Gemini (out-of-band caching protocol)", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index f0173b50303..6009891d05e 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1,7 +1,6 @@ import type { ModelMessage, ToolResultPart } from "ai" import { mergeDeep, unique } from "remeda" import type { JSONSchema7 } from "@ai-sdk/provider" -import { supportsPromptCacheBreakpoint } from "@opencode-ai/llm" // kilocode_change import type * as Provider from "./provider" import type * as ModelsDev from "@opencode-ai/core/models-dev" import { iife } from "@/util/iife" @@ -329,6 +328,20 @@ function normalizeMessages( return msgs } +// kilocode_change start - explicit prompt cache breakpoints for GPT-5.6+ +function supportsPromptCacheBreakpoint(modelId: string): boolean { + const match = modelId.match(/gpt-(\d+)\.(\d+)/) + if (match) { + const major = Number(match[1]) + const minor = Number(match[2]) + if (major > 5 || (major === 5 && minor >= 6)) return true + } + const majorMatch = modelId.match(/gpt-(\d+)/) + if (majorMatch && Number(majorMatch[1]) >= 6) return true + return false +} +// kilocode_change end + function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] { const system = msgs.filter((msg) => msg.role === "system").slice(0, 2) const final = msgs.filter((msg) => msg.role !== "system").slice(-2) From 1b0f94ec74e66331a02bcc06c337ed662e1e6412 Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:25:29 +0000 Subject: [PATCH 28/39] feat(cli): enable explicit prompt cache options for OpenAI models on Kilo Gateway Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 6 ++-- .../opencode/test/provider/transform.test.ts | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 6009891d05e..efb6ef760ad 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -480,7 +480,7 @@ function mapProviderOptions( export function message(msgs: ModelMessage[], model: Provider.Model, options: Record) { msgs = unsupportedParts(msgs, model) msgs = normalizeMessages(msgs, model, options) - // kilocode_change start - apply caching for anthropic, alibaba, and GPT-5.6+ openai/azure + // kilocode_change start - apply caching for anthropic, alibaba, and GPT-5.6+ openai/azure/kilo-gateway if ( (model.providerID === "anthropic" || model.providerID === "google-vertex-anthropic" || @@ -492,8 +492,10 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.api.npm === "@ai-sdk/alibaba" || ((model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure" || + model.api.npm === "@kilocode/kilo-gateway" || model.providerID === "openai" || - model.providerID === "azure") && + model.providerID === "azure" || + model.providerID === "kilo") && supportsPromptCacheBreakpoint(model.api.id))) && model.api.npm !== "@ai-sdk/gateway" ) { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 2f8c7d23b18..74ef174ade8 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3127,6 +3127,41 @@ describe("ProviderTransform.message - cache control on gateway", () => { }) expect(result[1].content[1].providerOptions?.openai?.promptCacheBreakpoint).toBeUndefined() }) + + test("kilo gateway with openai gpt-5.6 applies caching options", () => { + const model = createModel({ + providerID: "kilo", + api: { + id: "openai/gpt-5.6", + url: "https://api.kilo.ai/api/gateway", + npm: "@kilocode/kilo-gateway", + }, + id: "openai/gpt-5.6", + }) + const msgs = [ + { + role: "system", + content: "You are a helpful assistant", + }, + { + role: "user", + content: "Hello", + }, + ] as any[] + + const result = ProviderTransform.message(msgs, model, {}) as any[] + + expect(result[0].providerOptions.openrouter).toEqual({ + cacheControl: { + type: "ephemeral", + }, + }) + expect(result[1].providerOptions.openrouter).toEqual({ + cacheControl: { + type: "ephemeral", + }, + }) + }) // kilocode_change end }) From bca459832456bdba75e48c675122c324568870ea Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:28:46 +0000 Subject: [PATCH 29/39] refactor(cli): simplify provider checks and narrow kilocode_change markers in transform.ts Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 31 ++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index efb6ef760ad..d366119a5f3 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -384,10 +384,10 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage model.providerID === "anthropic" || model.providerID.includes("bedrock") || model.api.npm === "@ai-sdk/amazon-bedrock" - // kilocode_change start - place caching breakpoint on stable content before trailing const shouldUseContentOptions = !useMessageLevelOptions && Array.isArray(msg.content) && msg.content.length > 0 if (shouldUseContentOptions) { + // kilocode_change start - place caching breakpoint on stable content before trailing const parts = msg.content as any[] const targetIndex = parts.findLastIndex( (part) => @@ -395,25 +395,21 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage typeof part === "object" && part.type !== "tool-approval-request" && part.type !== "tool-approval-response" && - !( - part.type === "text" && - typeof part.text === "string" && - (part.text.startsWith("") || part.text.includes("")) - ) && + !(part.type === "text" && typeof part.text === "string" && part.text.startsWith("")) && !part.synthetic, ) - const targetContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1] + const lastContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1] + // kilocode_change end if ( - targetContent && - typeof targetContent === "object" && - targetContent.type !== "tool-approval-request" && - targetContent.type !== "tool-approval-response" + lastContent && + typeof lastContent === "object" && + lastContent.type !== "tool-approval-request" && + lastContent.type !== "tool-approval-response" ) { - targetContent.providerOptions = mergeDeep(targetContent.providerOptions ?? {}, providerOptions) + lastContent.providerOptions = mergeDeep(lastContent.providerOptions ?? {}, providerOptions) continue } } - // kilocode_change end msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions) } @@ -480,7 +476,6 @@ function mapProviderOptions( export function message(msgs: ModelMessage[], model: Provider.Model, options: Record) { msgs = unsupportedParts(msgs, model) msgs = normalizeMessages(msgs, model, options) - // kilocode_change start - apply caching for anthropic, alibaba, and GPT-5.6+ openai/azure/kilo-gateway if ( (model.providerID === "anthropic" || model.providerID === "google-vertex-anthropic" || @@ -490,18 +485,16 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.id.includes("claude") || model.api.npm === "@ai-sdk/anthropic" || model.api.npm === "@ai-sdk/alibaba" || + // kilocode_change start ((model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure" || - model.api.npm === "@kilocode/kilo-gateway" || - model.providerID === "openai" || - model.providerID === "azure" || - model.providerID === "kilo") && + model.api.npm === "@kilocode/kilo-gateway") && supportsPromptCacheBreakpoint(model.api.id))) && + // kilocode_change end model.api.npm !== "@ai-sdk/gateway" ) { msgs = applyCaching(msgs, model) } - // kilocode_change end // Remap providerOptions keys from stored providerID to expected SDK key const key = sdkKey(model.api.npm) From 7c7c11e33400a4640ad7b6cb031921964c7fe70b Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:34:00 +0000 Subject: [PATCH 30/39] fix(cli): include modified alibaba line in kilocode_change block Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index d366119a5f3..cf876f0c56c 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -484,8 +484,8 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.id.includes("anthropic") || model.id.includes("claude") || model.api.npm === "@ai-sdk/anthropic" || - model.api.npm === "@ai-sdk/alibaba" || // kilocode_change start + model.api.npm === "@ai-sdk/alibaba" || ((model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure" || model.api.npm === "@kilocode/kilo-gateway") && From d3548d179bf9814e16cd4acdeb91411bd046aaac Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:52:47 +0000 Subject: [PATCH 31/39] refactor(cli): remove part.synthetic and any-cast from applyCaching Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index cf876f0c56c..d8b04db4662 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -388,18 +388,15 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage if (shouldUseContentOptions) { // kilocode_change start - place caching breakpoint on stable content before trailing - const parts = msg.content as any[] - const targetIndex = parts.findLastIndex( + const target = msg.content.findLast( (part) => - part && typeof part === "object" && + part !== null && part.type !== "tool-approval-request" && part.type !== "tool-approval-response" && - !(part.type === "text" && typeof part.text === "string" && part.text.startsWith("")) && - !part.synthetic, + !(part.type === "text" && part.text.startsWith("")), ) - const lastContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1] - // kilocode_change end + const lastContent = target ?? msg.content[msg.content.length - 1] if ( lastContent && typeof lastContent === "object" && @@ -409,6 +406,7 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage lastContent.providerOptions = mergeDeep(lastContent.providerOptions ?? {}, providerOptions) continue } + // kilocode_change end } msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions) @@ -476,6 +474,7 @@ function mapProviderOptions( export function message(msgs: ModelMessage[], model: Provider.Model, options: Record) { msgs = unsupportedParts(msgs, model) msgs = normalizeMessages(msgs, model, options) + // kilocode_change start - apply caching for anthropic, alibaba, and GPT-5.6+ openai/azure/kilo-gateway if ( (model.providerID === "anthropic" || model.providerID === "google-vertex-anthropic" || @@ -484,17 +483,16 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re model.id.includes("anthropic") || model.id.includes("claude") || model.api.npm === "@ai-sdk/anthropic" || - // kilocode_change start model.api.npm === "@ai-sdk/alibaba" || ((model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure" || model.api.npm === "@kilocode/kilo-gateway") && supportsPromptCacheBreakpoint(model.api.id))) && - // kilocode_change end model.api.npm !== "@ai-sdk/gateway" ) { msgs = applyCaching(msgs, model) } + // kilocode_change end // Remap providerOptions keys from stored providerID to expected SDK key const key = sdkKey(model.api.npm) From 71abd522b1fd8988046df726f08f9e804e8f45ff Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:06:53 +0000 Subject: [PATCH 32/39] fix(cli): use typed loop and narrow kilocode_change block in applyCaching Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index d8b04db4662..ff534bf107f 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -386,17 +386,24 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage model.api.npm === "@ai-sdk/amazon-bedrock" const shouldUseContentOptions = !useMessageLevelOptions && Array.isArray(msg.content) && msg.content.length > 0 - if (shouldUseContentOptions) { - // kilocode_change start - place caching breakpoint on stable content before trailing - const target = msg.content.findLast( - (part) => + // kilocode_change start - place caching breakpoint on stable content before trailing + if (shouldUseContentOptions && Array.isArray(msg.content)) { + const parts = msg.content + let targetIndex = -1 + for (let i = parts.length - 1; i >= 0; i--) { + const part = parts[i] + if ( + part && typeof part === "object" && - part !== null && part.type !== "tool-approval-request" && part.type !== "tool-approval-response" && - !(part.type === "text" && part.text.startsWith("")), - ) - const lastContent = target ?? msg.content[msg.content.length - 1] + !(part.type === "text" && part.text.startsWith("")) + ) { + targetIndex = i + break + } + } + const lastContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1] if ( lastContent && typeof lastContent === "object" && @@ -406,8 +413,8 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage lastContent.providerOptions = mergeDeep(lastContent.providerOptions ?? {}, providerOptions) continue } - // kilocode_change end } + // kilocode_change end msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions) } From b1191437754b7a301b2e1fc15168df758022260c Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 11:20:23 -0400 Subject: [PATCH 33/39] docs(kilo-docs): document local session search --- packages/kilo-docs/lib/nav/code-with-ai.ts | 4 + .../code-with-ai/agents/chat-interface.md | 4 +- .../code-with-ai/agents/context-mentions.md | 4 +- .../code-with-ai/agents/session-history.md | 285 ++++++++++++++++++ .../pages/collaborate/sessions-sharing.md | 6 +- .../troubleshooting-extension.md | 4 +- 6 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 packages/kilo-docs/pages/code-with-ai/agents/session-history.md diff --git a/packages/kilo-docs/lib/nav/code-with-ai.ts b/packages/kilo-docs/lib/nav/code-with-ai.ts index c8a8c0a2238..0db4e63092f 100644 --- a/packages/kilo-docs/lib/nav/code-with-ai.ts +++ b/packages/kilo-docs/lib/nav/code-with-ai.ts @@ -64,6 +64,10 @@ export const CodeWithAiNav: NavSection[] = [ href: "/code-with-ai/agents/chat-interface", children: "Chat Interface", }, + { + href: "/code-with-ai/agents/session-history", + children: "Session History and Search", + }, { href: "/code-with-ai/agents/context-mentions", children: "Context & Mentions", diff --git a/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md b/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md index c700cb92803..eece30eb088 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md @@ -63,7 +63,7 @@ Open your terminal and run `kilo` to launch the interactive terminal interface ( - **Input prompt** - Type your requests and press Enter to send - **Action buttons** - Approve or reject proposed changes, answer questions - **Agent dropdown** - Switch between agents (e.g. Code, Ask, Plan) from the sidebar -- **Session management** - Start new sessions or resume previous ones +- **Session management** - Start new sessions or [search and resume previous ones](/docs/code-with-ai/agents/session-history) **Providing context:** @@ -89,7 +89,7 @@ You can also rename local sessions from **History** using the edit button or the - **Input prompt** - Type your requests and press Enter to send - **Action buttons** - Approve or reject proposed changes, answer questions - **Agent cycling** - Switch between agents using keybinds or slash commands -- **Session management** - Start new sessions or resume previous ones +- **Session management** - Start new sessions or [search and resume previous ones](/docs/code-with-ai/agents/session-history) - **New task** - Start a new task, available using the `+` button at the top or `New Task` button above the chat input - **Worktree** - Continue the current task with it's git state and session history in the Agent Manager in an isolated worktree - **File changes** - Shows the number of lines changed and opens a diff view diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 7978ddd868f..1e598065ebc 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -31,7 +31,7 @@ Selecting a suggestion inserts the mention and highlights it in the input. File ### Referencing Past Chats -Choosing **Past chats** from the `@` menu opens a searchable picker of your previous sessions in the current workspace, ordered by recency. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. +Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current repository's Git worktree family, ordered by recency. Search matches session titles and worktree names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. Very long transcripts are truncated, keeping the beginning and end, so a single mention cannot fill the context window. @@ -77,7 +77,7 @@ This means the agent can explore your entire project as needed, rather than bein | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | | **Keep editor tabs relevant** | Open tabs are passed as context, so keep relevant files open | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | -| **Reference a past chat** | Type `@` and choose **Past chats** to open a searchable picker of previous sessions in the current workspace. Selecting a session attaches its current transcript as context when you send the message. | +| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current repository's worktree family. Selecting a session attaches its current transcript as context when you send the message. | {% /tab %} {% tab label="CLI" %} diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md new file mode 100644 index 00000000000..a9bf54e50e4 --- /dev/null +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -0,0 +1,285 @@ +--- +title: "Session History and Search" +description: "Find, search, inspect, and resume local Kilo Code sessions" +--- + +# Session history and search + +Kilo Code keeps local session metadata and transcripts so you can resume earlier work. You can search session titles in the history UI or CLI, ask the agent to search transcript content, or inspect the local SQLite database directly. + +{% callout type="info" %} +Local sessions use SQLite, not PostgreSQL. Use `kilo db` or `sqlite3`, not `psql`, to inspect the local database. +{% /callout %} + +## Choose a search method + +Different search surfaces cover different content and scopes: + +| Method | Searches | Scope | +|---|---|---| +| VS Code or JetBrains History | Session titles | Sessions loaded in the selected Local or Cloud history view | +| CLI `/sessions` picker | Session titles | Current project and its Git worktrees | +| `kilo session list --search` | Session titles | Current project, or every local project with `--all` | +| Ask Kilo to recall past chats | Titles and high-signal transcript content | Current project and its Git worktrees | +| VS Code transcript search | Rendered content in the open session | Current session | +| SQLite query | Any stored field you select | Entire selected local database | + +History and CLI list searches do **not** search message content. To find a string inside prompts or replies, ask Kilo to search past chats or use a direct SQLite query. + +## Search in the UI + +{% tabs %} +{% tab label="VS Code" %} + +### Search session history + +1. Open the Kilo Code sidebar. +2. Select the **History** button in the sidebar title bar. You can also select **Show History** below the recent sessions on a new chat. +3. Select **Local** or **Cloud**. +4. Enter part of a session title in **Search sessions**. +5. Select a result to reopen it. + +Local history actions also let you rename, export, or delete a session. Cloud history can be filtered to **Only this repository**. + +The History search is a fuzzy title search over the sessions currently loaded into the view. It does not search prompts or agent replies. + +### Search the open transcript + +Open a session and select the magnifying-glass button in its header. Transcript search supports: + +- Previous and next match navigation +- Match case +- Whole word +- Regular expressions + +Kilo loads older messages while the search is active so the search covers the full local transcript, not only the messages initially visible. + +### Reference another session + +Type `@` in the chat input, select **Past chats**, and search by session title or worktree name. Selecting a result attaches that transcript as context when you send the message. Past chats includes sessions in the current repository's Git worktree family. + +{% /tab %} +{% tab label="JetBrains" %} + +1. Open the Kilo Code tool window. +2. Select **History** in the tool window toolbar. +3. Select **Local** or **Cloud**. +4. Enter part of a session title in **Search sessions**. +5. Select a result to reopen it. + +You can rename or delete local sessions. In Cloud history, enable **Only this repository** to narrow the list. + +The JetBrains History search matches session titles, not transcript content. + +{% /tab %} +{% tab label="CLI TUI" %} + +Run `kilo`, then enter any of these commands: + +```text +/sessions +/resume +/continue +``` + +Start typing in the session picker to filter sessions by title. The picker also supports reopening, pinning, and deleting local sessions. + +{% /tab %} +{% /tabs %} + +## Ask Kilo to search transcripts + +The simplest way to search message content across past local sessions is to ask Kilo directly: + +```text +Search my local sessions for "database disk image is malformed" and summarize the matching conversations. +``` + +Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current project and its Git worktrees. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. + +The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full transcript for a selected result. + +## Search with CLI commands + +### Filter session titles + +List sessions in the current project: + +```bash +kilo session list --search "database migration" +``` + +Search titles across every project in the local database and return JSON: + +```bash +kilo session list --all --search "database migration" --format json +``` + +Limit the number of results: + +```bash +kilo session list --all --search "migration" --max-count 25 +``` + +`--search` performs a SQLite `LIKE` substring search on the title. It does not inspect messages. Use the returned session ID to resume or export a session: + +```bash +kilo --session ses_123 +kilo run --session ses_123 "Summarize where we stopped" +kilo export ses_123 > session.json +``` + +### Find the local database + +Print the database selected by the current CLI environment: + +```bash +kilo db path +``` + +Stable installations normally use these paths: + +| Environment | Default path | +|---|---| +| Windows | `%LOCALAPPDATA%\kilo\kilo.db` | +| macOS | `~/Library/Application Support/kilo/kilo.db` | +| Linux | `~/.local/share/kilo/kilo.db` | +| VS Code Remote SSH | `~/.local/share/kilo/kilo.db` on the remote machine | + +The actual path can differ when `KILO_DB`, `XDG_DATA_HOME`, a development channel, or an isolated development environment is active. Prefer `kilo db path` over assuming a path. + +## Query SQLite directly + +{% callout type="warning" %} +The `kilo db` query command accepts arbitrary SQL, including writes. Use read-only queries, and use `sqlite3 -readonly` for safer interactive inspection. Stop Kilo before copying, replacing, or restoring the database because active transactions may also use `kilo.db-wal` and `kilo.db-shm`. +{% /callout %} + +### Use the built-in database command + +List recently updated sessions: + +```bash +kilo db " + SELECT + id, + title, + directory, + datetime(time_updated / 1000, 'unixepoch') AS updated + FROM session + ORDER BY time_updated DESC + LIMIT 25 +" --format json +``` + +Search non-synthetic text parts for a case-insensitive substring: + +```bash +kilo db " + SELECT + s.id AS session_id, + s.title, + json_extract(m.data, '$.role') AS role, + json_extract(p.data, '$.text') AS text + FROM session AS s + JOIN message AS m ON m.session_id = s.id + JOIN part AS p ON p.message_id = m.id + WHERE json_valid(p.data) + AND json_extract(p.data, '$.type') = 'text' + AND coalesce(json_extract(p.data, '$.synthetic'), 0) = 0 + AND coalesce(json_extract(p.data, '$.ignored'), 0) = 0 + AND instr( + lower(json_extract(p.data, '$.text')), + lower('database disk image is malformed') + ) > 0 + ORDER BY s.time_updated DESC, m.time_created, p.id +" --format json +``` + +Replace the search string in the second `lower(...)` expression. This query searches ordinary user and assistant text. It intentionally does not reproduce Kilo's recall ranking, worktree-family scope, file-reference matching, or tool-error matching. + +### Use the SQLite shell + +Install the `sqlite3` command-line tool, then open the selected database in read-only mode: + +```bash +sqlite3 -readonly "$(kilo db path)" +``` + +Useful commands inside the shell include: + +```sql +.tables +.schema session +.schema message +.schema part +``` + +Run the SQL examples above without the surrounding `kilo db` command. Exit with `.quit`. + +`psql` cannot open this file because `psql` speaks the PostgreSQL client protocol, while local Kilo sessions are stored in an embedded SQLite database. + +## Search through the API or SDK + +Run a local server with authentication before exposing its API: + +```bash +export KILO_SERVER_PASSWORD='replace-with-a-strong-password' +kilo serve --port 4096 +``` + +Search session titles in the current directory: + +```bash +curl -u "kilo:$KILO_SERVER_PASSWORD" \ + --get "http://127.0.0.1:4096/session" \ + --data-urlencode "directory=$PWD" \ + --data-urlencode "roots=true" \ + --data-urlencode "search=migration" \ + --data-urlencode "limit=50" +``` + +Read the complete transcript for one result: + +```bash +curl -u "kilo:$KILO_SERVER_PASSWORD" \ + --get "http://127.0.0.1:4096/session/ses_123/message" \ + --data-urlencode "directory=$PWD" +``` + +The list endpoint's `search` parameter searches titles only. To search transcript text programmatically, list candidate sessions, read their messages, and inspect text parts in your application. + +The JavaScript SDK exposes the same endpoints: + +```ts +import { createKiloClient } from "@kilocode/sdk/v2/client" + +const auth = Buffer.from(`kilo:${process.env.KILO_SERVER_PASSWORD}`).toString("base64") +const client = createKiloClient({ + baseUrl: "http://127.0.0.1:4096", + directory: process.cwd(), + headers: { Authorization: `Basic ${auth}` }, +}) + +const result = await client.session.list( + { roots: true, search: "migration", limit: 50 }, + { throwOnError: true }, +) + +for (const session of result.data) { + const transcript = await client.session.messages( + { sessionID: session.id, limit: 0 }, + { throwOnError: true }, + ) + + // Inspect transcript.data[*].parts for the content your application needs. + console.log(session.title, transcript.data.length) +} +``` + +`limit: 0` requests the complete transcript from the compatibility API. For large sessions, use a positive `limit` and follow the pagination cursor instead. + +## Local and cloud sessions + +Local sessions live in the SQLite database on the machine where Kilo runs. With VS Code Remote SSH, that means the remote host. Cloud sessions and shared-session copies are separate and are not available through local SQLite queries unless you import them locally. + +For database corruption or reset procedures, see [Troubleshooting IDE Extensions](/docs/getting-started/troubleshooting/troubleshooting-extension). Resetting the database removes local session history, so back it up before recovery work. diff --git a/packages/kilo-docs/pages/collaborate/sessions-sharing.md b/packages/kilo-docs/pages/collaborate/sessions-sharing.md index a43be9f0074..86c62f407aa 100644 --- a/packages/kilo-docs/pages/collaborate/sessions-sharing.md +++ b/packages/kilo-docs/pages/collaborate/sessions-sharing.md @@ -5,7 +5,7 @@ description: "Share and collaborate on Kilo Code sessions" # Sessions & Sharing -A session is your platform-agnostic interaction with Kilo. It remembers your repository, your task, and the conversation so you can pause and resume work without losing context. Sessions are private to your account by default; you can optionally share a link with others who can read or fork your session. +A session is your platform-agnostic interaction with Kilo. It remembers your repository, your task, and the conversation so you can pause and resume work without losing context. Local sessions stay on the machine where Kilo runs. Account-backed cloud sessions are private to your account by default. You can optionally share a session link with others who can read or fork it. ## What a session keeps for you @@ -49,7 +49,9 @@ Forking creates a new session in your account, with its own ID, and copies over ## Where your session data lives -To keep sessions fast and resumable, Kilo stores small JSON blobs associated with your session. These include your conversation history and task metadata. If you share a session, Kilo keeps a public copy used by the share link while your private session remains under your account. +Kilo stores local session history and metadata in an SQLite database on the machine where Kilo runs. Cloud sessions and shared-session copies use Kilo's cloud services and are separate from the local database. If you share a session, Kilo keeps a public copy for the share link while the original session remains private. + +See [Session History and Search](/docs/code-with-ai/agents/session-history) to find the local database, search session titles or transcripts, and inspect sessions with the CLI or SQLite. Good practice: diff --git a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md index 23f6a8ae8f4..368d5ac134d 100644 --- a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md +++ b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md @@ -27,7 +27,9 @@ If every prompt fails with `SQLiteError: database disk image is malformed`, Kilo ### Find the database -The database location depends on where Kilo Code is running: +When the kilo CLI uses the same environment as the affected installation, run `kilo db path` to print the selected database. See [Session History and Search](/docs/code-with-ai/agents/session-history) for normal database inspection and search workflows. + +The default database location depends on where Kilo Code is running: | Environment | Database path | |---|---| From 5fb5d4d5c012e5bccb7d2d4c388375154975d4a2 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 11:29:15 -0400 Subject: [PATCH 34/39] docs(kilo-docs): standardize session scope terminology --- .../code-with-ai/agents/context-mentions.md | 4 ++-- .../pages/code-with-ai/agents/session-history.md | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 1e598065ebc..4aa682e9811 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -31,7 +31,7 @@ Selecting a suggestion inserts the mention and highlights it in the input. File ### Referencing Past Chats -Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current repository's Git worktree family, ordered by recency. Search matches session titles and worktree names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. +Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace, ordered by recency. Search matches session titles and workspace names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. Very long transcripts are truncated, keeping the beginning and end, so a single mention cannot fill the context window. @@ -77,7 +77,7 @@ This means the agent can explore your entire project as needed, rather than bein | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | | **Keep editor tabs relevant** | Open tabs are passed as context, so keep relevant files open | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | -| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current repository's worktree family. Selecting a session attaches its current transcript as context when you send the message. | +| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current workspace. Selecting a session attaches its current transcript as context when you send the message. | {% /tab %} {% tab label="CLI" %} diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md index a9bf54e50e4..4488b301b71 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -18,9 +18,9 @@ Different search surfaces cover different content and scopes: | Method | Searches | Scope | |---|---|---| | VS Code or JetBrains History | Session titles | Sessions loaded in the selected Local or Cloud history view | -| CLI `/sessions` picker | Session titles | Current project and its Git worktrees | -| `kilo session list --search` | Session titles | Current project, or every local project with `--all` | -| Ask Kilo to recall past chats | Titles and high-signal transcript content | Current project and its Git worktrees | +| CLI `/sessions` picker | Session titles | Current workspace | +| `kilo session list --search` | Session titles | Current workspace, or every local workspace with `--all` | +| Ask Kilo to recall past chats | Titles and high-signal transcript content | Current workspace | | VS Code transcript search | Rendered content in the open session | Current session | | SQLite query | Any stored field you select | Entire selected local database | @@ -56,7 +56,7 @@ Kilo loads older messages while the search is active so the search covers the fu ### Reference another session -Type `@` in the chat input, select **Past chats**, and search by session title or worktree name. Selecting a result attaches that transcript as context when you send the message. Past chats includes sessions in the current repository's Git worktree family. +Type `@` in the chat input, select **Past chats**, and search by session title or workspace name. Selecting a result attaches that transcript as context when you send the message. Past chats includes sessions in the current workspace. {% /tab %} {% tab label="JetBrains" %} @@ -95,7 +95,7 @@ The simplest way to search message content across past local sessions is to ask Search my local sessions for "database disk image is malformed" and summarize the matching conversations. ``` -Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current project and its Git worktrees. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. +Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current workspace. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full transcript for a selected result. @@ -103,13 +103,13 @@ The search includes archived and child sessions. It excludes reasoning, syntheti ### Filter session titles -List sessions in the current project: +List sessions in the current workspace: ```bash kilo session list --search "database migration" ``` -Search titles across every project in the local database and return JSON: +Search titles across every local workspace and return JSON: ```bash kilo session list --all --search "database migration" --format json @@ -195,7 +195,7 @@ kilo db " " --format json ``` -Replace the search string in the second `lower(...)` expression. This query searches ordinary user and assistant text. It intentionally does not reproduce Kilo's recall ranking, worktree-family scope, file-reference matching, or tool-error matching. +Replace the search string in the second `lower(...)` expression. This query searches ordinary user and assistant text. It intentionally does not reproduce Kilo's recall ranking, workspace scope, file-reference matching, or tool-error matching. ### Use the SQLite shell From c237a69d01ef8ef3a03ec82dcd1e026e3d055697 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 11:34:23 -0400 Subject: [PATCH 35/39] docs(kilo-docs): address session search review --- .../code-with-ai/agents/context-mentions.md | 8 +-- .../code-with-ai/agents/session-history.md | 50 +++++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 4aa682e9811..6cfe92716a2 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -25,15 +25,15 @@ Type `@` in the chat input to get autocomplete suggestions. You can mention: | **File** | Attach a file's contents to your message | `@src/utils.ts` | | **Terminal** | Include your active VS Code terminal output | `@terminal` | | **Git Changes** | Attach uncommitted working-tree diffs and new files | `@git-changes` | -| **Past chats** | Attach a previous session's transcript as context | `@` → **Past chats** → pick a session | +| **Past chats** | Add a previous session's chat history as context | `@` → **Past chats** → pick a session | Selecting a suggestion inserts the mention and highlights it in the input. File contents, terminal output, and git changes are attached as context when you send the message. ### Referencing Past Chats -Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace, ordered by recency. Search matches session titles and workspace names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. +Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace, ordered by recency. Search matches session titles and workspace names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current chat history is added as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. -Very long transcripts are truncated, keeping the beginning and end, so a single mention cannot fill the context window. +Very long chat histories are truncated, keeping the beginning and end, so a single mention cannot fill the context window. ### Drag and Drop @@ -77,7 +77,7 @@ This means the agent can explore your entire project as needed, rather than bein | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | | **Keep editor tabs relevant** | Open tabs are passed as context, so keep relevant files open | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | -| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current workspace. Selecting a session attaches its current transcript as context when you send the message. | +| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current workspace. Selecting a session adds its current chat history as context when you send the message. | {% /tab %} {% tab label="CLI" %} diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md index 4488b301b71..8ae1641ceee 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -5,7 +5,7 @@ description: "Find, search, inspect, and resume local Kilo Code sessions" # Session history and search -Kilo Code keeps local session metadata and transcripts so you can resume earlier work. You can search session titles in the history UI or CLI, ask the agent to search transcript content, or inspect the local SQLite database directly. +Kilo Code keeps local session metadata and chat history so you can resume earlier work. You can search session titles in the history UI or CLI, ask the agent to search chat content, or inspect the local SQLite database directly. {% callout type="info" %} Local sessions use SQLite, not PostgreSQL. Use `kilo db` or `sqlite3`, not `psql`, to inspect the local database. @@ -17,12 +17,14 @@ Different search surfaces cover different content and scopes: | Method | Searches | Scope | |---|---|---| -| VS Code or JetBrains History | Session titles | Sessions loaded in the selected Local or Cloud history view | -| CLI `/sessions` picker | Session titles | Current workspace | -| `kilo session list --search` | Session titles | Current workspace, or every local workspace with `--all` | -| Ask Kilo to recall past chats | Titles and high-signal transcript content | Current workspace | -| VS Code transcript search | Rendered content in the open session | Current session | -| SQLite query | Any stored field you select | Entire selected local database | +| [VS Code History](#search-session-history-in-vs-code) | Session titles | Sessions loaded in the selected Local or Cloud history view | +| [JetBrains History](#search-session-history-in-jetbrains) | Session titles | Sessions loaded in the selected Local or Cloud history view | +| [CLI `/sessions` picker](#search-session-history-in-the-cli-tui) | Session titles | Current workspace | +| [`kilo session list --search`](#filter-session-titles) | Session titles | Current workspace, or every local workspace with `--all` | +| [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace | +| [VS Code chat search](#search-the-open-chat-session) | Rendered content in the open session | Current chat session | +| [VS Code Past chats](#add-a-past-chat-to-the-current-session) | Session titles and workspace names | Current workspace | +| [SQLite query](#query-sqlite-directly) | Any stored field you select | Entire selected local database | History and CLI list searches do **not** search message content. To find a string inside prompts or replies, ask Kilo to search past chats or use a direct SQLite query. @@ -31,7 +33,7 @@ History and CLI list searches do **not** search message content. To find a strin {% tabs %} {% tab label="VS Code" %} -### Search session history +### Search session history in VS Code 1. Open the Kilo Code sidebar. 2. Select the **History** button in the sidebar title bar. You can also select **Show History** below the recent sessions on a new chat. @@ -43,24 +45,26 @@ Local history actions also let you rename, export, or delete a session. Cloud hi The History search is a fuzzy title search over the sessions currently loaded into the view. It does not search prompts or agent replies. -### Search the open transcript +### Search the open chat session -Open a session and select the magnifying-glass button in its header. Transcript search supports: +Open a session and select the magnifying-glass button in its header. Chat search supports: - Previous and next match navigation - Match case - Whole word - Regular expressions -Kilo loads older messages while the search is active so the search covers the full local transcript, not only the messages initially visible. +Kilo loads older messages while the search is active so the search covers the full local chat session, not only the messages initially visible. -### Reference another session +### Add a past chat to the current session -Type `@` in the chat input, select **Past chats**, and search by session title or workspace name. Selecting a result attaches that transcript as context when you send the message. Past chats includes sessions in the current workspace. +Type `@` in the chat input, select **Past chats**, and search by session title or workspace name. Selecting a result adds that past chat's content as context when you send the message from your current session. It does not switch or reopen sessions. Past chats includes sessions in the current workspace. {% /tab %} {% tab label="JetBrains" %} +### Search session history in JetBrains + 1. Open the Kilo Code tool window. 2. Select **History** in the tool window toolbar. 3. Select **Local** or **Cloud**. @@ -69,11 +73,13 @@ Type `@` in the chat input, select **Past chats**, and search by session title o You can rename or delete local sessions. In Cloud history, enable **Only this repository** to narrow the list. -The JetBrains History search matches session titles, not transcript content. +The JetBrains History search matches session titles, not chat content. {% /tab %} {% tab label="CLI TUI" %} +### Search session history in the CLI TUI + Run `kilo`, then enter any of these commands: ```text @@ -87,7 +93,7 @@ Start typing in the session picker to filter sessions by title. The picker also {% /tab %} {% /tabs %} -## Ask Kilo to search transcripts +## Ask Kilo to search past chats The simplest way to search message content across past local sessions is to ask Kilo directly: @@ -97,7 +103,7 @@ Search my local sessions for "database disk image is malformed" and summarize th Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current workspace. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. -The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full transcript for a selected result. +The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full chat history for a selected result. ## Search with CLI commands @@ -238,7 +244,7 @@ curl -u "kilo:$KILO_SERVER_PASSWORD" \ --data-urlencode "limit=50" ``` -Read the complete transcript for one result: +Read the complete chat history for one result: ```bash curl -u "kilo:$KILO_SERVER_PASSWORD" \ @@ -246,7 +252,7 @@ curl -u "kilo:$KILO_SERVER_PASSWORD" \ --data-urlencode "directory=$PWD" ``` -The list endpoint's `search` parameter searches titles only. To search transcript text programmatically, list candidate sessions, read their messages, and inspect text parts in your application. +The list endpoint's `search` parameter searches titles only. To search chat content programmatically, list candidate sessions, read their messages, and inspect text parts in your application. The JavaScript SDK exposes the same endpoints: @@ -266,17 +272,17 @@ const result = await client.session.list( ) for (const session of result.data) { - const transcript = await client.session.messages( + const history = await client.session.messages( { sessionID: session.id, limit: 0 }, { throwOnError: true }, ) - // Inspect transcript.data[*].parts for the content your application needs. - console.log(session.title, transcript.data.length) + // Inspect history.data[*].parts for the content your application needs. + console.log(session.title, history.data.length) } ``` -`limit: 0` requests the complete transcript from the compatibility API. For large sessions, use a positive `limit` and follow the pagination cursor instead. +`limit: 0` requests the complete chat history from the compatibility API. For large sessions, use a positive `limit` and follow the pagination cursor instead. ## Local and cloud sessions From 0da7d5096548c4e5eefc7d2b7f41fd755590d512 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 11:37:46 -0400 Subject: [PATCH 36/39] docs(kilo-docs): clarify CLI session loading --- .../kilo-docs/pages/code-with-ai/agents/session-history.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md index 8ae1641ceee..a368bf4cbc8 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -19,7 +19,7 @@ Different search surfaces cover different content and scopes: |---|---|---| | [VS Code History](#search-session-history-in-vs-code) | Session titles | Sessions loaded in the selected Local or Cloud history view | | [JetBrains History](#search-session-history-in-jetbrains) | Session titles | Sessions loaded in the selected Local or Cloud history view | -| [CLI `/sessions` picker](#search-session-history-in-the-cli-tui) | Session titles | Current workspace | +| [CLI `/sessions` picker](#load-a-different-session-in-the-cli-tui) | Session titles | Current workspace | | [`kilo session list --search`](#filter-session-titles) | Session titles | Current workspace, or every local workspace with `--all` | | [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace | | [VS Code chat search](#search-the-open-chat-session) | Rendered content in the open session | Current chat session | @@ -78,7 +78,7 @@ The JetBrains History search matches session titles, not chat content. {% /tab %} {% tab label="CLI TUI" %} -### Search session history in the CLI TUI +### Load a different session in the CLI TUI Run `kilo`, then enter any of these commands: @@ -88,7 +88,7 @@ Run `kilo`, then enter any of these commands: /continue ``` -Start typing in the session picker to filter sessions by title. The picker also supports reopening, pinning, and deleting local sessions. +Start typing in the session picker to filter sessions by title, then select a result to load that chat session. This switches the TUI to the selected session; it does not add the session as context to your current chat. The picker also supports pinning and deleting local sessions. {% /tab %} {% /tabs %} From 4ff8f5e1105652b98f49210608e9f28e98a92f8b Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Sun, 9 Aug 2026 15:44:33 +0000 Subject: [PATCH 37/39] feat(vscode): show Auto model routes for every kilo-auto model Show the underlying model list in the selector whenever an Auto model includes autoRouting, not only kilo-auto/efficient. Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- .changeset/auto-model-route-choices.md | 5 ++++ .../model-selector-accessibility.spec.ts | 14 ++++++++++ .../tests/unit/model-selector-utils.test.ts | 26 ++++++++++++++++--- .../components/shared/model-selector-utils.ts | 7 +---- .../webview-ui/src/stories/shared.stories.tsx | 13 +++++++++- 5 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 .changeset/auto-model-route-choices.md diff --git a/.changeset/auto-model-route-choices.md b/.changeset/auto-model-route-choices.md new file mode 100644 index 00000000000..6edfcabe146 --- /dev/null +++ b/.changeset/auto-model-route-choices.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show underlying models for every Auto model in the model selector when the catalog includes them. diff --git a/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts b/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts index 785bf9faf2f..de54f3b5e00 100644 --- a/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts +++ b/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts @@ -80,6 +80,20 @@ test("auto efficient details show server description and model choices", async ( await expect(preview).not.toContainText("openai/gpt-5.5") }) +test("auto frontier details show model choices when routes are present", async ({ page }) => { + await load(page, "shared--model-selector-accessible") + + await page.getByRole("button", { name: "Review model: Alpha" }).click() + await page.getByRole("treeitem", { name: /Kilo Auto Frontier/ }).click() + + const preview = page.locator(".model-selector-preview") + await expect(preview).toContainText("Routes each request to the strongest available models.") + await expect(preview).toContainText("Model choices") + await expect(preview).toContainText("openai/gpt-5.5") + await expect(preview).toContainText("anthropic/claude-opus-4.6") + await expect(preview).not.toContainText("google/gemini-2.5-flash") +}) + test("search uses a flat relevance-ranked result list with provider labels", async ({ page }) => { await load(page, "shared--model-selector-accessible") diff --git a/packages/kilo-vscode/tests/unit/model-selector-utils.test.ts b/packages/kilo-vscode/tests/unit/model-selector-utils.test.ts index d1c681f89fc..59956de5ef1 100644 --- a/packages/kilo-vscode/tests/unit/model-selector-utils.test.ts +++ b/packages/kilo-vscode/tests/unit/model-selector-utils.test.ts @@ -128,7 +128,7 @@ describe("isAuto", () => { }) describe("autoChoices", () => { - it("uses backend Auto Efficient routes and resolves names when available", () => { + it("uses backend Auto routes and resolves names when available", () => { expect( autoChoices( { @@ -144,12 +144,32 @@ describe("autoChoices", () => { ]) }) - it("ignores missing routes and non-efficient Auto models", () => { + it("shows routes for any Auto model when present", () => { + expect( + autoChoices( + { + providerID: KILO_GATEWAY_ID, + id: "kilo-auto/frontier", + autoRouting: { models: ["provider/model"] }, + }, + [{ id: "provider/model", name: "Provider: Model" }], + ), + ).toEqual([{ id: "provider/model", name: "Model" }]) + expect( + autoChoices({ + providerID: KILO_GATEWAY_ID, + id: "kilo-auto/free", + autoRouting: { models: ["provider/model"] }, + }), + ).toEqual([{ id: "provider/model", name: "provider/model" }]) + }) + + it("ignores missing routes and non-Auto models", () => { expect(autoChoices({ providerID: KILO_GATEWAY_ID, id: "kilo-auto/efficient" })).toEqual([]) expect( autoChoices({ providerID: KILO_GATEWAY_ID, - id: "kilo-auto/frontier", + id: "anthropic/claude-sonnet", autoRouting: { models: ["provider/model"] }, }), ).toEqual([]) diff --git a/packages/kilo-vscode/webview-ui/src/components/shared/model-selector-utils.ts b/packages/kilo-vscode/webview-ui/src/components/shared/model-selector-utils.ts index d93d142e37b..80d70c9cf8c 100644 --- a/packages/kilo-vscode/webview-ui/src/components/shared/model-selector-utils.ts +++ b/packages/kilo-vscode/webview-ui/src/components/shared/model-selector-utils.ts @@ -10,7 +10,6 @@ import { export { KILO_GATEWAY_ID, PROVIDER_ORDER } export const KILO_AUTO_SMALL_IDS = new Set(["kilo-auto/small", "auto-small"]) -export const KILO_AUTO_EFFICIENT_ID = "kilo-auto/efficient" const AUTO_FALLBACK = "Routes requests automatically." interface Choice { @@ -24,15 +23,11 @@ export function isAuto(model: Pick): boolean ) } -export function isAutoEfficient(model: Pick): boolean { - return model.providerID === KILO_GATEWAY_ID && model.id === KILO_AUTO_EFFICIENT_ID -} - export function autoChoices( model: Pick, catalog: readonly Pick[] = [], ): readonly Choice[] { - if (!isAutoEfficient(model)) return [] + if (!isAuto(model)) return [] const ids = model.autoRouting?.models if (!ids?.length) return [] const names = new Map(catalog.map((item) => [item.id, stripSubProviderPrefix(sanitizeName(item.name))])) diff --git a/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx index 48d6bd94a8a..8753cb96ae1 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx @@ -72,7 +72,18 @@ const ACCESSIBLE_MODELS: EnrichedModel[] = [ }, autoRouting: { models: ["google/gemini-2.5-flash", "anthropic/claude-sonnet-4.6"] }, }, - { id: "omega", name: "Omega", providerID: "openai", providerName: "OpenAI", recommendedIndex: 1 }, + { + id: "kilo-auto/frontier", + name: "Kilo Auto Frontier", + providerID: "kilo", + providerName: "Kilo", + recommendedIndex: 1, + options: { + description: "Routes each request to the strongest available models.", + }, + autoRouting: { models: ["openai/gpt-5.5", "anthropic/claude-opus-4.6"] }, + }, + { id: "omega", name: "Omega", providerID: "openai", providerName: "OpenAI", recommendedIndex: 2 }, { id: "alpha", name: "Alpha", providerID: "kilo", providerName: "Kilo" }, { id: "bravo", name: "Bravo", providerID: "kilo", providerName: "Kilo" }, { id: "charlie", name: "Charlie", providerID: "kilo", providerName: "Kilo" }, From a1776a1ea5a1b22fb12b9127bb71734e017c9d61 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 11:44:34 -0400 Subject: [PATCH 38/39] docs(kilo-docs): document CLI past chat references --- .../code-with-ai/agents/context-mentions.md | 2 ++ .../code-with-ai/agents/session-history.md | 17 +++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 6cfe92716a2..0e433cca870 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -93,6 +93,7 @@ When you describe a task, the agent uses its tools — `read`, `grep`, `glob`, a In the terminal-based TUI, you can provide context in several ways: - **Type `@` for file autocomplete** — In the TUI, type `@` followed by a filename to get autocomplete suggestions. Selecting a file attaches its contents to your message. You can limit how much is included by appending a line range, e.g. `@src/utils.ts#10-50`. +- **Reference a past chat** — Type `@`, choose **Past chats**, then search for and select a session. Kilo adds that chat history as context when you send the message without switching sessions. - **Mention file paths in your message** — Simply refer to files by path in your conversation text (e.g., "look at src/utils.ts") and the agent will read them. - **Use `kilo run -f`** — When using the non-interactive `kilo run` command, pass `-f path/to/file.ts` to explicitly include a file's contents in the context. - **Let the agent find files itself** — The agent has access to `glob` (find files by pattern), `grep` (search file contents), and `read` (read file contents) tools. Describe what you're looking for and it will locate the relevant code. @@ -116,6 +117,7 @@ This means the agent can explore your entire project as needed, rather than bein |---|---| | **Describe the task clearly** | The agent finds context on its own — focus on _what_ you want done rather than _where_ the code is | | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | +| **Reference a past chat** | Type `@` and choose **Past chats** to add another session's chat history as context without switching sessions. | | **Use `kilo run -f`** | Pass key files with `-f` when using `kilo run` for immediate context | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md index a368bf4cbc8..4b78b52ae28 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -19,7 +19,7 @@ Different search surfaces cover different content and scopes: |---|---|---| | [VS Code History](#search-session-history-in-vs-code) | Session titles | Sessions loaded in the selected Local or Cloud history view | | [JetBrains History](#search-session-history-in-jetbrains) | Session titles | Sessions loaded in the selected Local or Cloud history view | -| [CLI `/sessions` picker](#load-a-different-session-in-the-cli-tui) | Session titles | Current workspace | +| [CLI Past chats](#reference-a-past-chat-in-the-cli-tui) | Session titles | Current workspace | | [`kilo session list --search`](#filter-session-titles) | Session titles | Current workspace, or every local workspace with `--all` | | [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace | | [VS Code chat search](#search-the-open-chat-session) | Rendered content in the open session | Current chat session | @@ -78,17 +78,14 @@ The JetBrains History search matches session titles, not chat content. {% /tab %} {% tab label="CLI TUI" %} -### Load a different session in the CLI TUI +### Reference a past chat in the CLI TUI -Run `kilo`, then enter any of these commands: +1. Run `kilo`. +2. Type `@` in the prompt and select **Past chats**. +3. Search for a session by title. +4. Select a session, finish your message, and send it. -```text -/sessions -/resume -/continue -``` - -Start typing in the session picker to filter sessions by title, then select a result to load that chat session. This switches the TUI to the selected session; it does not add the session as context to your current chat. The picker also supports pinning and deleting local sessions. +Kilo adds the selected chat history as context for your message. This does not switch the TUI to the selected session. Use `/sessions`, `/resume`, or `/continue` when you want to load a different session instead. {% /tab %} {% /tabs %} From 33a3eab7510f111cf12d16607d7b0bde9876318b Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sun, 9 Aug 2026 14:29:39 -0400 Subject: [PATCH 39/39] docs(kilo-docs): correct session paths and scope --- .../pages/code-with-ai/agents/context-mentions.md | 4 ++-- .../pages/code-with-ai/agents/session-history.md | 12 ++++++------ .../troubleshooting/troubleshooting-extension.md | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 0e433cca870..b0a7ec85091 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -31,7 +31,7 @@ Selecting a suggestion inserts the mention and highlights it in the input. File ### Referencing Past Chats -Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace, ordered by recency. Search matches session titles and workspace names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current chat history is added as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. +Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace and related Git worktrees, including Agent Manager worktrees, ordered by recency. Search matches session titles and worktree names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current chat history is added as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. Very long chat histories are truncated, keeping the beginning and end, so a single mention cannot fill the context window. @@ -77,7 +77,7 @@ This means the agent can explore your entire project as needed, rather than bein | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | | **Keep editor tabs relevant** | Open tabs are passed as context, so keep relevant files open | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | -| **Reference a past chat** | Type `@` and choose **Past chats** to search sessions in the current workspace. Selecting a session adds its current chat history as context when you send the message. | +| **Reference a past chat** | Type `@` and choose **Past chats** to search the current workspace and related Git worktrees, including Agent Manager worktrees. Selecting a session adds its current chat history as context when you send the message. | {% /tab %} {% tab label="CLI" %} diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md index 4b78b52ae28..02f1c0787ea 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -21,9 +21,9 @@ Different search surfaces cover different content and scopes: | [JetBrains History](#search-session-history-in-jetbrains) | Session titles | Sessions loaded in the selected Local or Cloud history view | | [CLI Past chats](#reference-a-past-chat-in-the-cli-tui) | Session titles | Current workspace | | [`kilo session list --search`](#filter-session-titles) | Session titles | Current workspace, or every local workspace with `--all` | -| [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace | +| [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace and related Git worktrees | | [VS Code chat search](#search-the-open-chat-session) | Rendered content in the open session | Current chat session | -| [VS Code Past chats](#add-a-past-chat-to-the-current-session) | Session titles and workspace names | Current workspace | +| [VS Code Past chats](#add-a-past-chat-to-the-current-session) | Session titles and worktree names | Current workspace and related Git worktrees, including Agent Manager worktrees | | [SQLite query](#query-sqlite-directly) | Any stored field you select | Entire selected local database | History and CLI list searches do **not** search message content. To find a string inside prompts or replies, ask Kilo to search past chats or use a direct SQLite query. @@ -58,7 +58,7 @@ Kilo loads older messages while the search is active so the search covers the fu ### Add a past chat to the current session -Type `@` in the chat input, select **Past chats**, and search by session title or workspace name. Selecting a result adds that past chat's content as context when you send the message from your current session. It does not switch or reopen sessions. Past chats includes sessions in the current workspace. +Type `@` in the chat input, select **Past chats**, and search by session title or worktree name. Selecting a result adds that past chat's content as context when you send the message from your current session. It does not switch or reopen sessions. Past chats includes sessions in the current workspace and related Git worktrees, including Agent Manager worktrees. {% /tab %} {% tab label="JetBrains" %} @@ -98,7 +98,7 @@ The simplest way to search message content across past local sessions is to ask Search my local sessions for "database disk image is malformed" and summarize the matching conversations. ``` -Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current workspace. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. +Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current workspace and related Git worktrees. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full chat history for a selected result. @@ -144,8 +144,8 @@ Stable installations normally use these paths: | Environment | Default path | |---|---| -| Windows | `%LOCALAPPDATA%\kilo\kilo.db` | -| macOS | `~/Library/Application Support/kilo/kilo.db` | +| Windows | `%USERPROFILE%\.local\share\kilo\kilo.db` | +| macOS | `~/.local/share/kilo/kilo.db` | | Linux | `~/.local/share/kilo/kilo.db` | | VS Code Remote SSH | `~/.local/share/kilo/kilo.db` on the remote machine | diff --git a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md index 368d5ac134d..075843369e8 100644 --- a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md +++ b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md @@ -33,8 +33,8 @@ The default database location depends on where Kilo Code is running: | Environment | Database path | |---|---| -| Windows | `%LOCALAPPDATA%\kilo\kilo.db` | -| macOS | `~/Library/Application Support/kilo/kilo.db` | +| Windows | `%USERPROFILE%\.local\share\kilo\kilo.db` | +| macOS | `~/.local/share/kilo/kilo.db` | | Linux | `~/.local/share/kilo/kilo.db` | | VS Code Remote SSH | `~/.local/share/kilo/kilo.db` on the remote machine |