-
+
data.openFile!(props.input.filePath) : undefined
+ }
/>
{(filepath) => (
-
+
data.openFile?.(filepath)}
+ >
{i18n.t("ui.tool.loaded")} {relativizeProjectPaths(filepath, data.directory)}
@@ -1106,10 +1128,16 @@ ToolRegistry.register({
ToolRegistry.register({
name: "edit",
render(props) {
+ const data = useData()
const i18n = useI18n()
const diffComponent = useDiffComponent()
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
const filename = () => getFilename(props.input.filePath ?? "")
+ const handleFileClick = (e: MouseEvent) => {
+ if (!data.openFile || !props.input.filePath) return
+ e.stopPropagation()
+ data.openFile(props.input.filePath)
+ }
return (
{i18n.t("ui.messagePart.title.edit")}
- {filename()}
+
+ {filename()}
+
- {getDirectory(props.input.filePath!)}
+
+ {getDirectory(props.input.filePath!)}
+
@@ -1159,10 +1199,16 @@ ToolRegistry.register({
ToolRegistry.register({
name: "write",
render(props) {
+ const data = useData()
const i18n = useI18n()
const codeComponent = useCodeComponent()
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
const filename = () => getFilename(props.input.filePath ?? "")
+ const handleFileClick = (e: MouseEvent) => {
+ if (!data.openFile || !props.input.filePath) return
+ e.stopPropagation()
+ data.openFile(props.input.filePath)
+ }
return (
{i18n.t("ui.messagePart.title.write")}
- {filename()}
+
+ {filename()}
+
- {getDirectory(props.input.filePath!)}
+
+ {getDirectory(props.input.filePath!)}
+
@@ -1218,6 +1276,7 @@ interface ApplyPatchFile {
ToolRegistry.register({
name: "apply_patch",
render(props) {
+ const data = useData()
const i18n = useI18n()
const diffComponent = useDiffComponent()
const files = createMemo(() => (props.metadata.files ?? []) as ApplyPatchFile[])
@@ -1265,7 +1324,17 @@ ToolRegistry.register({
- {file.relativePath}
+ {
+ if (!data.openFile) return
+ e.stopPropagation()
+ data.openFile(file.filePath)
+ }}
+ >
+ {file.relativePath}
+
diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx
index 137ea05656c..815c2d3d4a7 100644
--- a/packages/ui/src/context/data.tsx
+++ b/packages/ui/src/context/data.tsx
@@ -52,6 +52,8 @@ export type SessionHrefFn = (sessionID: string) => string
export type SyncSessionFn = (sessionID: string) => void | Promise
+export type OpenFileFn = (filePath: string, line?: number, column?: number) => void
+
export const { use: useData, provider: DataProvider } = createSimpleContext({
name: "Data",
init: (props: {
@@ -63,6 +65,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
onNavigateToSession?: NavigateToSessionFn
onSessionHref?: SessionHrefFn
onSyncSession?: SyncSessionFn
+ onOpenFile?: OpenFileFn
}) => {
return {
get store() {
@@ -77,6 +80,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
navigateToSession: props.onNavigateToSession,
sessionHref: props.onSessionHref,
syncSession: props.onSyncSession,
+ openFile: props.onOpenFile,
}
},
})
diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx
index 1ea1999f8de..a2697892b2f 100644
--- a/packages/ui/src/context/marked.tsx
+++ b/packages/ui/src/context/marked.tsx
@@ -461,6 +461,24 @@ async function highlightCodeBlocks(html: string): Promise {
export type NativeMarkdownParser = (markdown: string) => Promise
+// Matches text that looks like a file path: contains "/" and ends with a file extension,
+// or starts with "./" or "../" or "/". Supports optional :line or :line:col suffix.
+const FILE_PATH_RE =
+ /^((?:\/|\.\.?\/)?(?:[a-zA-Z0-9_@-][a-zA-Z0-9_@./-]*\/)*[a-zA-Z0-9_@.-]+\.[a-zA-Z0-9]+)(?::(\d+)(?::(\d+))?)?$/
+
+function parseFilePath(text: string): { path: string; line?: number; column?: number } | undefined {
+ if (text.includes("://")) return undefined
+ if (text.includes(" ")) return undefined
+ const match = FILE_PATH_RE.exec(text)
+ if (!match) return undefined
+ if (!match[1].includes("/")) return undefined
+ return {
+ path: match[1],
+ line: match[2] ? parseInt(match[2], 10) : undefined,
+ column: match[3] ? parseInt(match[3], 10) : undefined,
+ }
+}
+
export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({
name: "Marked",
init: (props: { nativeParser?: NativeMarkdownParser }) => {
@@ -471,6 +489,15 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
const titleAttr = title ? ` title="${title}"` : ""
return `${text}`
},
+ codespan({ text }) {
+ const file = parseFilePath(text)
+ if (file) {
+ const lineAttr = file.line ? ` data-file-line="${file.line}"` : ""
+ const colAttr = file.column ? ` data-file-col="${file.column}"` : ""
+ return `${text}`
+ }
+ return `${text}`
+ },
},
},
markedKatex({
From 42e70cbdef1901350808ec04ac5a729496ea3e4a Mon Sep 17 00:00:00 2001
From: Olusammytee
Date: Thu, 19 Feb 2026 09:44:20 -0500
Subject: [PATCH 06/47] docs: clarify YAML-first custom modes behavior
---
packages/kilo-docs/pages/customize/custom-modes.md | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/packages/kilo-docs/pages/customize/custom-modes.md b/packages/kilo-docs/pages/customize/custom-modes.md
index a7fdda9fd1a..86bcc17a2c4 100644
--- a/packages/kilo-docs/pages/customize/custom-modes.md
+++ b/packages/kilo-docs/pages/customize/custom-modes.md
@@ -74,7 +74,7 @@ Modes are managed from the Modes area in Kilo Code. Depending on your UI layout,
1. Open the Modes area from the mode selector in the chat panel (or via the icon if shown)
2. Click the Import Mode button (upload icon)
-3. Select the mode's YAML file
+3. Select the mode's YAML file (`.yaml`)
4. Choose the import level:
- **Project:** Available only in current workspace (saved to `.kilocodemodes` file)
- **Global:** Available in all projects (saved to global settings)
@@ -121,11 +121,16 @@ The interface provides fields for Name, Slug, Description, Save Location, Role D
You can directly edit the configuration files to create or modify custom modes. This method offers the most control over all properties. Kilo Code now supports both YAML (preferred) and JSON formats.
-- **Global Modes:** Edit the `custom_modes.yaml` (preferred) or `custom_modes.json` file. Open the Modes area, click next to Global Modes, then choose "Edit Global Modes"
-- **Project Modes:** Edit the `.kilocodemodes` file (which can be YAML or JSON) in your project root. Open the Modes area, click next to Project Modes, then choose "Edit Project Modes"
+- **Global Modes:** Edit `custom_modes.yaml` (primary). `custom_modes.json` is a legacy fallback and may still exist in older setups.
+- **Project Modes:** Edit `.kilocodemodes` in your project root (YAML preferred; JSON still supported for compatibility).
+- **Open from UI:** Open the Modes area, click next to Global or Project Modes, then choose **Edit Global Modes** or **Edit Project Modes**.
These files define an array/list of custom modes.
+{% callout type="info" title="Why JSON Files May Still Exist" %}
+If you see both YAML and JSON mode files, this is usually from legacy configuration. Kilo Code reads YAML first and does not keep both files synchronized line-by-line. In practice, edit YAML unless you have a specific reason to stay on JSON.
+{% /callout %}
+
## YAML Configuration Format (Preferred)
YAML is now the preferred format for defining custom modes due to better readability, comment support, and cleaner multi-line strings.
From cf6997c109f9bad8345b124ef89adfd20b27ae89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?=
Date: Fri, 20 Feb 2026 12:19:06 +0100
Subject: [PATCH 07/47] chore: add kilocode_change markers to PR #479 changes
in packages/ui
---
packages/ui/src/components/markdown.css | 2 ++
packages/ui/src/components/message-part.css | 10 ++++++-
packages/ui/src/components/message-part.tsx | 30 ++++++++++++++++-----
packages/ui/src/context/data.tsx | 6 ++---
packages/ui/src/context/marked.tsx | 4 +++
5 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css
index 1a5c709d659..53ae7d2b5ac 100644
--- a/packages/ui/src/components/markdown.css
+++ b/packages/ui/src/components/markdown.css
@@ -174,6 +174,7 @@
/* background: var(--surface-base); */
/* box-shadow: 0 0 0 0.5px var(--border-weak-base); */
+ /* kilocode_change start */
&.file-link {
cursor: pointer;
text-decoration-line: underline;
@@ -186,6 +187,7 @@
text-decoration-style: solid;
}
}
+ /* kilocode_change end */
}
/* Tables */
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css
index c1bf0a263bf..c18f7a72637 100644
--- a/packages/ui/src/components/message-part.css
+++ b/packages/ui/src/components/message-part.css
@@ -34,7 +34,7 @@
overflow: hidden;
background: var(--surface-weak);
border: 1px solid var(--border-weak-base);
- cursor: pointer;
+ cursor: pointer; /* kilocode_change */
transition: border-color 0.15s ease;
&:hover {
@@ -276,6 +276,7 @@
[data-slot="message-part-title-filename"] {
/* No text-transform - preserve original filename casing */
+ /* kilocode_change start */
&.clickable {
cursor: pointer;
transition: color 0.15s ease;
@@ -285,6 +286,7 @@
color: var(--text-base);
}
}
+ /* kilocode_change end */
}
[data-slot="message-part-path"] {
@@ -301,6 +303,7 @@
direction: rtl;
text-align: left;
+ /* kilocode_change start */
&.clickable {
cursor: pointer;
transition: color 0.15s ease;
@@ -310,6 +313,7 @@
color: var(--text-base);
}
}
+ /* kilocode_change end */
}
[data-slot="message-part-filename"] {
@@ -819,6 +823,7 @@
white-space: nowrap;
flex-grow: 1;
+ /* kilocode_change start */
&.clickable {
cursor: pointer;
transition: color 0.15s ease;
@@ -828,6 +833,7 @@
color: var(--text-base);
}
}
+ /* kilocode_change end */
}
[data-slot="apply-patch-deletion-count"] {
@@ -865,6 +871,7 @@
color: var(--icon-weak);
}
+ /* kilocode_change start */
&.clickable {
cursor: pointer;
transition: color 0.15s ease;
@@ -873,4 +880,5 @@
color: var(--text-base);
}
}
+ /* kilocode_change end */
}
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index f50595b1a0e..3cd29c611fe 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -682,6 +682,7 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
setTimeout(() => setCopied(false), 2000)
}
+ // kilocode_change start
const handleMarkdownClick = (e: MouseEvent) => {
if (!data.openFile) return
const target = e.target
@@ -696,12 +697,13 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
const column = colAttr ? parseInt(colAttr, 10) : undefined
data.openFile(path, line, column)
}
+ // kilocode_change end
return (
-
+
{/* kilocode_change */}
data.openFile!(props.input.filePath) : undefined
}
+ // kilocode_change end
/>
{(filepath) => (
data.openFile?.(filepath)}
+ classList={{ clickable: !!data.openFile }} // kilocode_change
+ onClick={() => data.openFile?.(filepath)} // kilocode_change
>
@@ -1128,16 +1132,18 @@ ToolRegistry.register({
ToolRegistry.register({
name: "edit",
render(props) {
- const data = useData()
+ const data = useData() // kilocode_change
const i18n = useI18n()
const diffComponent = useDiffComponent()
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
const filename = () => getFilename(props.input.filePath ?? "")
+ // kilocode_change start
const handleFileClick = (e: MouseEvent) => {
if (!data.openFile || !props.input.filePath) return
e.stopPropagation()
data.openFile(props.input.filePath)
}
+ // kilocode_change end
return (
{i18n.t("ui.messagePart.title.edit")}
+ {/* kilocode_change start */}
{filename()}
+ {/* kilocode_change end */}
+ {/* kilocode_change start */}
{getDirectory(props.input.filePath!)}
+ {/* kilocode_change end */}
@@ -1199,16 +1209,18 @@ ToolRegistry.register({
ToolRegistry.register({
name: "write",
render(props) {
- const data = useData()
+ const data = useData() // kilocode_change
const i18n = useI18n()
const codeComponent = useCodeComponent()
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
const filename = () => getFilename(props.input.filePath ?? "")
+ // kilocode_change start
const handleFileClick = (e: MouseEvent) => {
if (!data.openFile || !props.input.filePath) return
e.stopPropagation()
data.openFile(props.input.filePath)
}
+ // kilocode_change end
return (
{i18n.t("ui.messagePart.title.write")}
+ {/* kilocode_change start */}
{filename()}
+ {/* kilocode_change end */}
+ {/* kilocode_change start */}
{getDirectory(props.input.filePath!)}
+ {/* kilocode_change end */}
@@ -1276,7 +1292,7 @@ interface ApplyPatchFile {
ToolRegistry.register({
name: "apply_patch",
render(props) {
- const data = useData()
+ const data = useData() // kilocode_change
const i18n = useI18n()
const diffComponent = useDiffComponent()
const files = createMemo(() => (props.metadata.files ?? []) as ApplyPatchFile[])
@@ -1324,6 +1340,7 @@ ToolRegistry.register({
+ {/* kilocode_change start */}
{file.relativePath}
+ {/* kilocode_change end */}
diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx
index 815c2d3d4a7..26dcc2a7593 100644
--- a/packages/ui/src/context/data.tsx
+++ b/packages/ui/src/context/data.tsx
@@ -52,7 +52,7 @@ export type SessionHrefFn = (sessionID: string) => string
export type SyncSessionFn = (sessionID: string) => void | Promise
-export type OpenFileFn = (filePath: string, line?: number, column?: number) => void
+export type OpenFileFn = (filePath: string, line?: number, column?: number) => void // kilocode_change
export const { use: useData, provider: DataProvider } = createSimpleContext({
name: "Data",
@@ -65,7 +65,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
onNavigateToSession?: NavigateToSessionFn
onSessionHref?: SessionHrefFn
onSyncSession?: SyncSessionFn
- onOpenFile?: OpenFileFn
+ onOpenFile?: OpenFileFn // kilocode_change
}) => {
return {
get store() {
@@ -80,7 +80,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
navigateToSession: props.onNavigateToSession,
sessionHref: props.onSessionHref,
syncSession: props.onSyncSession,
- openFile: props.onOpenFile,
+ openFile: props.onOpenFile, // kilocode_change
}
},
})
diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx
index a2697892b2f..315cf55aab0 100644
--- a/packages/ui/src/context/marked.tsx
+++ b/packages/ui/src/context/marked.tsx
@@ -461,6 +461,7 @@ async function highlightCodeBlocks(html: string): Promise {
export type NativeMarkdownParser = (markdown: string) => Promise
+// kilocode_change start
// Matches text that looks like a file path: contains "/" and ends with a file extension,
// or starts with "./" or "../" or "/". Supports optional :line or :line:col suffix.
const FILE_PATH_RE =
@@ -478,6 +479,7 @@ function parseFilePath(text: string): { path: string; line?: number; column?: nu
column: match[3] ? parseInt(match[3], 10) : undefined,
}
}
+// kilocode_change end
export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({
name: "Marked",
@@ -489,6 +491,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
const titleAttr = title ? ` title="${title}"` : ""
return `${text}`
},
+ // kilocode_change start
codespan({ text }) {
const file = parseFilePath(text)
if (file) {
@@ -498,6 +501,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
}
return `${text}`
},
+ // kilocode_change end
},
},
markedKatex({
From d4231c20e303b91cc5606aacb31ae42c1f27855b Mon Sep 17 00:00:00 2001
From: Alex Alecu
Date: Fri, 20 Feb 2026 13:49:51 +0200
Subject: [PATCH 08/47] fix: Close sub-agents, preventing them from becoming
orphan processes
---
packages/opencode/src/cli/cmd/serve.ts | 15 +++++++++++++--
packages/opencode/src/cli/cmd/tui/thread.ts | 7 +++++++
packages/opencode/src/cli/cmd/web.ts | 15 +++++++++++++--
packages/opencode/src/index.ts | 5 ++++-
4 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/packages/opencode/src/cli/cmd/serve.ts b/packages/opencode/src/cli/cmd/serve.ts
index 54189753e92..90c8a1d37af 100644
--- a/packages/opencode/src/cli/cmd/serve.ts
+++ b/packages/opencode/src/cli/cmd/serve.ts
@@ -2,6 +2,7 @@ import { Server } from "../../server/server"
import { cmd } from "./cmd"
import { withNetworkOptions, resolveNetworkOptions } from "../network"
import { Flag } from "../../flag/flag"
+import { Instance } from "../../project/instance" // kilocode_change
export const ServeCommand = cmd({
command: "serve",
@@ -14,7 +15,17 @@ export const ServeCommand = cmd({
const opts = await resolveNetworkOptions(args)
const server = Server.listen(opts)
console.log(`kilo server listening on http://${server.hostname}:${server.port}`) // kilocode_change
- await new Promise(() => {})
- await server.stop()
+ // kilocode_change start - graceful signal shutdown
+ const abort = new AbortController()
+ const shutdown = async () => {
+ await Instance.disposeAll()
+ await server.stop(true)
+ abort.abort()
+ }
+ process.on("SIGTERM", shutdown)
+ process.on("SIGINT", shutdown)
+ process.on("SIGHUP", shutdown)
+ await new Promise((resolve) => abort.signal.addEventListener("abort", resolve))
+ // kilocode_change end
},
})
diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts
index 31fc583f066..3b839f65865 100644
--- a/packages/opencode/src/cli/cmd/tui/thread.ts
+++ b/packages/opencode/src/cli/cmd/tui/thread.ts
@@ -127,6 +127,13 @@ export const TuiThreadCommand = cmd({
process.on("SIGUSR2", async () => {
await client.call("reload", undefined)
})
+ // kilocode_change start - graceful shutdown on external signals
+ const shutdown = async () => {
+ await client.call("shutdown", undefined).catch(() => {})
+ }
+ process.on("SIGHUP", shutdown)
+ process.on("SIGTERM", shutdown)
+ // kilocode_change end
const prompt = await iife(async () => {
const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
diff --git a/packages/opencode/src/cli/cmd/web.ts b/packages/opencode/src/cli/cmd/web.ts
index 76841293277..21493f4c04a 100644
--- a/packages/opencode/src/cli/cmd/web.ts
+++ b/packages/opencode/src/cli/cmd/web.ts
@@ -3,6 +3,7 @@ import { UI } from "../ui"
import { cmd } from "./cmd"
import { withNetworkOptions, resolveNetworkOptions } from "../network"
import { Flag } from "../../flag/flag"
+import { Instance } from "../../project/instance" // kilocode_change
import open from "open"
import { networkInterfaces } from "os"
@@ -75,7 +76,17 @@ export const WebCommand = cmd({
open(displayUrl).catch(() => {})
}
- await new Promise(() => {})
- await server.stop()
+ // kilocode_change start - graceful signal shutdown
+ const abort = new AbortController()
+ const shutdown = async () => {
+ await Instance.disposeAll()
+ await server.stop(true)
+ abort.abort()
+ }
+ process.on("SIGTERM", shutdown)
+ process.on("SIGINT", shutdown)
+ process.on("SIGHUP", shutdown)
+ await new Promise((resolve) => abort.signal.addEventListener("abort", resolve))
+ // kilocode_change end
},
})
diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts
index cc79306bc26..737c0d6f72a 100644
--- a/packages/opencode/src/index.ts
+++ b/packages/opencode/src/index.ts
@@ -26,8 +26,9 @@ import { EOL } from "os"
import { WebCommand } from "./cli/cmd/web"
import { PrCommand } from "./cli/cmd/pr"
import { SessionCommand } from "./cli/cmd/session"
-// kilocode_change start - Import telemetry and legacy migration
+// kilocode_change start - Import telemetry, instance disposal, and legacy migration
import { Telemetry } from "@kilocode/kilo-telemetry"
+import { Instance } from "./project/instance" // kilocode_change
import { migrateLegacyKiloAuth, ENV_FEATURE } from "@kilocode/kilo-gateway"
// kilocode_change - set feature for tracking. 'serve' is spawned by other services
@@ -197,6 +198,8 @@ try {
await Telemetry.shutdown()
// kilocode_change end
+ await Instance.disposeAll() // kilocode_change - safety net disposal (no-op if already disposed)
+
// Some subprocesses don't react properly to SIGTERM and similar signals.
// Most notably, some docker-container-based MCP servers don't handle such signals unless
// run using `docker run --init`.
From 43ae6b242557fe0c4fc40302c3e05a6479adaf29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?=
Date: Fri, 20 Feb 2026 15:27:42 +0100
Subject: [PATCH 09/47] feat(vscode): add session-lifetime hint to permission
auto-approve dialog
Clarifies that 'Allow always' only applies for the duration of the current
session, not globally. Adds translated hint text below the permission
action buttons in all supported locales.
---
.../webview-ui/src/components/chat/ChatView.tsx | 1 +
packages/ui/src/components/message-part.css | 7 +++++++
packages/ui/src/i18n/ar.ts | 1 +
packages/ui/src/i18n/br.ts | 2 ++
packages/ui/src/i18n/bs.ts | 2 ++
packages/ui/src/i18n/da.ts | 2 ++
packages/ui/src/i18n/de.ts | 2 ++
packages/ui/src/i18n/en.ts | 1 +
packages/ui/src/i18n/es.ts | 2 ++
packages/ui/src/i18n/fr.ts | 2 ++
packages/ui/src/i18n/ja.ts | 2 ++
packages/ui/src/i18n/ko.ts | 1 +
packages/ui/src/i18n/no.ts | 2 ++
packages/ui/src/i18n/pl.ts | 2 ++
packages/ui/src/i18n/ru.ts | 2 ++
packages/ui/src/i18n/th.ts | 1 +
packages/ui/src/i18n/zh.ts | 1 +
packages/ui/src/i18n/zht.ts | 1 +
18 files changed, 34 insertions(+)
diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx
index 355e77a4772..fce02449996 100644
--- a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx
+++ b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx
@@ -99,6 +99,7 @@ export const ChatView: Component = (props) => {
{language.t("ui.permission.allowOnce")}
+
{language.t("ui.permission.sessionHint")}
)}
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css
index 44db4f9aa52..6cf9bdc0254 100644
--- a/packages/ui/src/components/message-part.css
+++ b/packages/ui/src/components/message-part.css
@@ -554,6 +554,13 @@
gap: 8px;
justify-content: flex-end;
}
+
+ [data-slot="permission-hint"] {
+ font-size: 11px;
+ color: var(--text-dimmed-base);
+ margin: 4px 0 0 0;
+ text-align: right;
+ }
}
[data-component="question-prompt"] {
diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts
index 7ee17e2e010..36030998bfe 100644
--- a/packages/ui/src/i18n/ar.ts
+++ b/packages/ui/src/i18n/ar.ts
@@ -85,6 +85,7 @@ export const dict = {
"ui.permission.deny": "رفض",
"ui.permission.allowAlways": "السماح دائمًا",
"ui.permission.allowOnce": "السماح مرة واحدة",
+ "ui.permission.sessionHint": '"السماح دائمًا" ينطبق على هذه الجلسة فقط. استخدم الإعدادات للأذونات العامة.',
"ui.message.expand": "توسيع الرسالة",
"ui.message.collapse": "طي الرسالة",
diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts
index 6d7449d8457..36679c5c716 100644
--- a/packages/ui/src/i18n/br.ts
+++ b/packages/ui/src/i18n/br.ts
@@ -85,6 +85,8 @@ export const dict = {
"ui.permission.deny": "Negar",
"ui.permission.allowAlways": "Permitir sempre",
"ui.permission.allowOnce": "Permitir uma vez",
+ "ui.permission.sessionHint":
+ '"Sempre permitir" aplica-se apenas a esta sessão. Use as configurações para permissões globais.',
"ui.message.expand": "Expandir mensagem",
"ui.message.collapse": "Recolher mensagem",
diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts
index 24e4c12068e..6bdae375d0b 100644
--- a/packages/ui/src/i18n/bs.ts
+++ b/packages/ui/src/i18n/bs.ts
@@ -89,6 +89,8 @@ export const dict = {
"ui.permission.deny": "Zabrani",
"ui.permission.allowAlways": "Uvijek dozvoli",
"ui.permission.allowOnce": "Dozvoli jednom",
+ "ui.permission.sessionHint":
+ '"Uvijek dozvoli" primjenjuje se samo na ovu sesiju. Koristite postavke za globalne dozvole.',
"ui.message.expand": "Proširi poruku",
"ui.message.collapse": "Sažmi poruku",
diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts
index 218f3b26a49..799249e8a8f 100644
--- a/packages/ui/src/i18n/da.ts
+++ b/packages/ui/src/i18n/da.ts
@@ -84,6 +84,8 @@ export const dict = {
"ui.permission.deny": "Afvis",
"ui.permission.allowAlways": "Tillad altid",
"ui.permission.allowOnce": "Tillad én gang",
+ "ui.permission.sessionHint":
+ '"Tillad altid" gælder kun for denne session. Brug indstillinger til globale tilladelser.',
"ui.message.expand": "Udvid besked",
"ui.message.collapse": "Skjul besked",
diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts
index 921a12c9967..ef03895b382 100644
--- a/packages/ui/src/i18n/de.ts
+++ b/packages/ui/src/i18n/de.ts
@@ -88,6 +88,8 @@ export const dict = {
"ui.permission.deny": "Verweigern",
"ui.permission.allowAlways": "Immer erlauben",
"ui.permission.allowOnce": "Einmal erlauben",
+ "ui.permission.sessionHint":
+ '"Immer erlauben" gilt nur für diese Sitzung. Globale Berechtigungen in den Einstellungen ändern.',
"ui.message.expand": "Nachricht erweitern",
"ui.message.collapse": "Nachricht reduzieren",
diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts
index 631bc660a65..d44d1d3d87c 100644
--- a/packages/ui/src/i18n/en.ts
+++ b/packages/ui/src/i18n/en.ts
@@ -85,6 +85,7 @@ export const dict = {
"ui.permission.deny": "Deny",
"ui.permission.allowAlways": "Allow always",
"ui.permission.allowOnce": "Allow once",
+ "ui.permission.sessionHint": '"Allow always" applies to this session only. Use settings for global permissions.',
"ui.message.expand": "Expand message",
"ui.message.collapse": "Collapse message",
diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts
index 4fd921b606b..67f1e63ac85 100644
--- a/packages/ui/src/i18n/es.ts
+++ b/packages/ui/src/i18n/es.ts
@@ -85,6 +85,8 @@ export const dict = {
"ui.permission.deny": "Denegar",
"ui.permission.allowAlways": "Permitir siempre",
"ui.permission.allowOnce": "Permitir una vez",
+ "ui.permission.sessionHint":
+ '"Permitir siempre" se aplica solo a esta sesión. Usa la configuración para permisos globales.',
"ui.message.expand": "Expandir mensaje",
"ui.message.collapse": "Colapsar mensaje",
diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts
index 537d01bba94..72b9f15ffd7 100644
--- a/packages/ui/src/i18n/fr.ts
+++ b/packages/ui/src/i18n/fr.ts
@@ -85,6 +85,8 @@ export const dict = {
"ui.permission.deny": "Refuser",
"ui.permission.allowAlways": "Toujours autoriser",
"ui.permission.allowOnce": "Autoriser une fois",
+ "ui.permission.sessionHint":
+ '"Toujours autoriser" s\'applique uniquement à cette session. Utilisez les paramètres pour les autorisations globales.',
"ui.message.expand": "Développer le message",
"ui.message.collapse": "Réduire le message",
diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts
index 6086070bdb2..a0fef4500a2 100644
--- a/packages/ui/src/i18n/ja.ts
+++ b/packages/ui/src/i18n/ja.ts
@@ -84,6 +84,8 @@ export const dict = {
"ui.permission.deny": "拒否",
"ui.permission.allowAlways": "常に許可",
"ui.permission.allowOnce": "今回のみ許可",
+ "ui.permission.sessionHint":
+ "「常に許可」はこのセッションにのみ適用されます。グローバルな権限は設定で変更してください。",
"ui.message.expand": "メッセージを展開",
"ui.message.collapse": "メッセージを折りたたむ",
diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts
index fd394dbb7b5..6d37e883b11 100644
--- a/packages/ui/src/i18n/ko.ts
+++ b/packages/ui/src/i18n/ko.ts
@@ -85,6 +85,7 @@ export const dict = {
"ui.permission.deny": "거부",
"ui.permission.allowAlways": "항상 허용",
"ui.permission.allowOnce": "한 번만 허용",
+ "ui.permission.sessionHint": '"항상 허용"은 현재 세션에만 적용됩니다. 전역 권한은 설정에서 변경하세요.',
"ui.message.expand": "메시지 펼치기",
"ui.message.collapse": "메시지 접기",
diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts
index dcb353614d3..b8cc230d46a 100644
--- a/packages/ui/src/i18n/no.ts
+++ b/packages/ui/src/i18n/no.ts
@@ -88,6 +88,8 @@ export const dict: Record
= {
"ui.permission.deny": "Avslå",
"ui.permission.allowAlways": "Tillat alltid",
"ui.permission.allowOnce": "Tillat én gang",
+ "ui.permission.sessionHint":
+ '"Tillat alltid" gjelder bare for denne økten. Bruk innstillinger for globale tillatelser.',
"ui.message.expand": "Utvid melding",
"ui.message.collapse": "Skjul melding",
diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts
index fb10debbb92..c9f43daada0 100644
--- a/packages/ui/src/i18n/pl.ts
+++ b/packages/ui/src/i18n/pl.ts
@@ -84,6 +84,8 @@ export const dict = {
"ui.permission.deny": "Odmów",
"ui.permission.allowAlways": "Zezwalaj zawsze",
"ui.permission.allowOnce": "Zezwól raz",
+ "ui.permission.sessionHint":
+ '"Zawsze zezwalaj" dotyczy tylko tej sesji. Użyj ustawień, aby zmienić globalne uprawnienia.',
"ui.message.expand": "Rozwiń wiadomość",
"ui.message.collapse": "Zwiń wiadomość",
diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts
index 417fe0ce8bf..144cbee1fd4 100644
--- a/packages/ui/src/i18n/ru.ts
+++ b/packages/ui/src/i18n/ru.ts
@@ -84,6 +84,8 @@ export const dict = {
"ui.permission.deny": "Запретить",
"ui.permission.allowAlways": "Разрешить всегда",
"ui.permission.allowOnce": "Разрешить один раз",
+ "ui.permission.sessionHint":
+ "«Всегда разрешать» применяется только к текущей сессии. Для глобальных разрешений используйте настройки.",
"ui.message.expand": "Развернуть сообщение",
"ui.message.collapse": "Свернуть сообщение",
diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts
index 68bb0d733d9..09f529c25a0 100644
--- a/packages/ui/src/i18n/th.ts
+++ b/packages/ui/src/i18n/th.ts
@@ -85,6 +85,7 @@ export const dict = {
"ui.permission.deny": "ปฏิเสธ",
"ui.permission.allowAlways": "อนุญาตเสมอ",
"ui.permission.allowOnce": "อนุญาตครั้งเดียว",
+ "ui.permission.sessionHint": '"อนุญาตเสมอ" ใช้ได้เฉพาะในเซสชันนี้เท่านั้น ใช้การตั้งค่าสำหรับสิทธิ์ส่วนกลาง',
"ui.message.expand": "ขยายข้อความ",
"ui.message.collapse": "ย่อข้อความ",
diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts
index 53beeb1e4f0..fe572cb5d3f 100644
--- a/packages/ui/src/i18n/zh.ts
+++ b/packages/ui/src/i18n/zh.ts
@@ -89,6 +89,7 @@ export const dict = {
"ui.permission.deny": "拒绝",
"ui.permission.allowAlways": "始终允许",
"ui.permission.allowOnce": "允许一次",
+ "ui.permission.sessionHint": '"始终允许" 仅适用于本次会话。如需全局权限设置,请使用设置。',
"ui.message.expand": "展开消息",
"ui.message.collapse": "收起消息",
diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts
index 1449b0530ac..9fa7cd0b332 100644
--- a/packages/ui/src/i18n/zht.ts
+++ b/packages/ui/src/i18n/zht.ts
@@ -89,6 +89,7 @@ export const dict = {
"ui.permission.deny": "拒絕",
"ui.permission.allowAlways": "永遠允許",
"ui.permission.allowOnce": "允許一次",
+ "ui.permission.sessionHint": '"一律允許" 僅適用於此工作階段。如需全域權限設定,請使用設定。',
"ui.message.expand": "展開訊息",
"ui.message.collapse": "收合訊息",
From 776402da138bd06ee7418f8a39fc551281dad310 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?=
Date: Fri, 20 Feb 2026 15:38:47 +0100
Subject: [PATCH 10/47] fix: correct translation mismatches and add hint to
message-part.tsx permission prompts
- Fix quoted button label in br, pl, ru, zht to match their allowAlways values
- Add permission-hint to both permission prompt locations in message-part.tsx
---
packages/ui/src/components/message-part.tsx | 2 ++
packages/ui/src/i18n/br.ts | 2 +-
packages/ui/src/i18n/pl.ts | 2 +-
packages/ui/src/i18n/ru.ts | 2 +-
packages/ui/src/i18n/zht.ts | 2 +-
5 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index 42dc37abc5c..84995f3cd94 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -659,6 +659,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
{i18n.t("ui.permission.allowOnce")}