Merge remote-tracking branch 'origin/main' into johnnyeric/kilo-opencode-v1.18.0

This commit is contained in:
Johnny Eric Amancio
2026-08-10 12:02:37 +02:00
109 changed files with 4330 additions and 1349 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Show underlying models for every Auto model in the model selector when the catalog includes them.
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Automatically expose broad reasoning effort options for custom provider models and link saved providers to advanced JSON configuration.
@@ -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.
+6
View File
@@ -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.
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Set explicit prompt cache breakpoints on stable prefixes for OpenAI GPT-5.6+ models.
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Show the permission approval reason for reads and writes outside the workspace, matching other tools
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Surface process exit signals and structured spawn failure details in server startup diagnostics
+5
View File
@@ -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.
@@ -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",
@@ -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
@@ -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 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 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 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 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 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" %}
@@ -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 |
@@ -0,0 +1,288 @@
---
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 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.
{% /callout %}
## Choose a search method
Different search surfaces cover different content and scopes:
| Method | Searches | Scope |
|---|---|---|
| [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 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 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 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.
## Search in the UI
{% tabs %}
{% tab label="VS Code" %}
### 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.
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 chat session
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 chat session, not only the messages initially visible.
### Add a past chat to the current session
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" %}
### 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**.
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 chat content.
{% /tab %}
{% tab label="CLI TUI" %}
### Reference a past chat in the CLI TUI
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.
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 %}
## Ask Kilo to search past chats
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 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.
## Search with CLI commands
### Filter session titles
List sessions in the current workspace:
```bash
kilo session list --search "database migration"
```
Search titles across every local workspace 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 | `%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 |
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, workspace 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 chat history 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 chat content 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 history = await client.session.messages(
{ sessionID: session.id, limit: 0 },
{ throwOnError: true },
)
// Inspect history.data[*].parts for the content your application needs.
console.log(session.title, history.data.length)
}
```
`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
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.
@@ -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 |
|---|---|---|
@@ -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:
@@ -27,12 +27,14 @@ 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 |
|---|---|
| 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 |
+2786
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
allowBuilds:
core-js: true
Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

@@ -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);
}
@@ -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, ToolApprovalVisibilityProvider } 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(() => <ImagePreview src={url} alt={alt} />)
// 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 (
<>
<BasicTool
hideDetails
hideDetails={!approval()?.approval.outsideWorkspace}
{...props}
icon="glasses"
onSubtitleClick={
@@ -54,4 +54,28 @@ describe("resolveToolApproval", () => {
expect(out?.source).toBe("ui.approval.source.agent(agent=code)")
expect(out?.rule).toBeUndefined()
})
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(file=hello.txt)")
})
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()
})
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()
})
})
@@ -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"
/**
@@ -12,6 +13,10 @@ 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
/** 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. */
@@ -20,6 +25,7 @@ export type ToolApprovalDisplay = {
decision: string
source?: string
rule?: string
outsideWorkspace?: string
}
const SOURCE_KEYS = ["agent", "global", "project", "yolo", "session", "manual", "default"] as const
@@ -82,11 +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 && filename ? t("ui.approval.outsideWorkspace", { file: filename }) : undefined,
}
}
@@ -101,6 +112,9 @@ export function ToolApprovalLine(props: { display: ToolApprovalDisplay }) {
<Show when={props.display.source}>{(text) => <span data-slot="tool-approval-source">{text()}</span>}</Show>
<Show when={props.display.rule}>{(text) => <span data-slot="tool-approval-rule">{text()}</span>}</Show>
</Show>
<Show when={props.display.outsideWorkspace}>
{(text) => <span data-slot="tool-approval-outside-workspace">{text()}</span>}
</Show>
</div>
)
}
@@ -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()
})
})
@@ -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.
@@ -14,6 +13,7 @@ export interface AutoScrollOptions {
working: () => boolean
onUserInteracted?: () => void
bottomThreshold?: number
overflowAnchor?: "none" | "auto" | "dynamic"
}
export function createAutoScroll(options: AutoScrollOptions) {
@@ -24,7 +24,6 @@ export function createAutoScroll(options: AutoScrollOptions) {
let scroll: HTMLElement | undefined
let settling = false
let settleTimer: ReturnType<typeof setTimeout> | undefined
let stopTimer: ReturnType<typeof setTimeout> | undefined
let cleanup: (() => void) | undefined
const [store, setStore] = createStore({
@@ -100,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
@@ -110,52 +109,25 @@ export function createAutoScroll(options: AutoScrollOptions) {
return
}
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
}
// 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 +145,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 +176,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,13 +200,12 @@ export function createAutoScroll(options: AutoScrollOptions) {
if (!el) return
el.style.overflowAnchor = "auto"
updateOverflowAnchor(el)
cleanup = userActivity.listen(el)
}
onCleanup(() => {
if (settleTimer) clearTimeout(settleTimer)
if (stopTimer) clearTimeout(stopTimer)
if (cleanup) cleanup()
})
@@ -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()
}
@@ -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,
@@ -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 })),
@@ -155,7 +163,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,
}),
})
}
}
@@ -73,6 +73,7 @@ function slimEdit(state: Record<string, unknown>): Record<string, unknown> {
}
}
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<string, unknown>): Record<string, unknown> {
if (isObj(meta)) {
const slim: Record<string, unknown> = {}
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<string, unknown>[]).map((f) => {
const diff = patch(f.patch) ?? patch(f.diff)
@@ -115,6 +117,7 @@ function slimMultiedit(state: Record<string, unknown>): Record<string, unknown>
if (isObj(meta)) {
const slim: Record<string, unknown> = {}
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<string, unknown>[]).map((r) => {
const rs: Record<string, unknown> = {}
@@ -149,6 +152,7 @@ function slimWrite(state: Record<string, unknown>): Record<string, unknown> {
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 = {
@@ -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.")
})
})
@@ -138,7 +138,7 @@ export class KiloConnectionService {
update: async () => undefined,
} satisfies Pick<vscode.Memento, "get" | "update">)
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<void> {
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
@@ -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",
@@ -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",
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
+6 -4
View File
@@ -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
+5 -3
View File
@@ -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
@@ -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",
+4 -2
View File
@@ -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
@@ -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",
@@ -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",
+3 -1
View File
@@ -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",
@@ -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",
+4 -2
View File
@@ -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
+3 -1
View File
@@ -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",
+3 -1
View File
@@ -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
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
@@ -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.warn("[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))
}
})
@@ -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<typeof VariantConfigSchema>
@@ -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")
@@ -32,6 +32,19 @@ 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()
// 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 + 9,
)
}
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,6 +86,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()
await assertTooltipFitsViewport(content, `path tooltip "${seeded}"`, page)
}
for (const seeded of [SEEDED_URL, SEEDED_URL_2]) {
@@ -99,6 +113,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()
await assertTooltipFitsViewport(content, `URL tooltip "${seeded}"`, page)
}
for (const [label, card] of [
@@ -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", () => {
@@ -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<string, unknown>
expect(saved.variants).toEqual({ high: raw })
})
it("serializes image modality when supportsImages is set", () => {
const form = base()
form.models[0].supportsImages = true
@@ -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",
@@ -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([])
@@ -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:")
})
})
@@ -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<string, typeof saved>).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
@@ -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", () => {
@@ -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<string, any>
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<string, any>
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<string, any>
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<string, any>
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", () => {
@@ -903,7 +903,7 @@ const AgentBehaviourTab: Component = () => {
"border-bottom": index() < skillPaths().length - 1 ? "1px solid var(--border-weak-base)" : "none",
}}
>
<Tooltip value={path} class="settings-skills-row-trigger">
<Tooltip value={path} class="settings-skills-row-trigger" contentClass="settings-skills-tooltip-content">
<span
style={{
width: "100%",
@@ -960,7 +960,7 @@ const AgentBehaviourTab: Component = () => {
"border-bottom": index() < skillUrls().length - 1 ? "1px solid var(--border-weak-base)" : "none",
}}
>
<Tooltip value={url} class="settings-skills-row-trigger">
<Tooltip value={url} class="settings-skills-row-trigger" contentClass="settings-skills-tooltip-content">
<span
style={{
width: "100%",
@@ -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,
@@ -89,6 +90,7 @@ function modes(raw: unknown): Modalities {
function parseVariant([name, cfg]: [string, Record<string, unknown>]): 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 +462,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,
@@ -581,6 +564,19 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => {
{language.t("provider.custom.description.link")}
</a>
{language.t("provider.custom.description.suffix")}
<Show when={editing()}>
<div style={{ "margin-top": "8px" }}>
<a
href="#"
onClick={(e) => {
e.preventDefault()
vscode.postMessage(configMessage("global", language.t))
}}
>
{language.t("provider.custom.edit.advanced")}
</a>
</div>
</Show>
</div>
<div style={{ display: "flex", "flex-direction": "column", gap: "16px" }}>
@@ -673,7 +669,6 @@ const CustomProviderDialog = (props: CustomProviderDialogProps) => {
{(m, i) => (
<ModelCard
m={m}
i={i}
errors={errors.models[i()] ?? {}}
t={language.t}
canRemove={form.models.length > 1}
@@ -682,23 +677,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)
}
/>
)}
</For>
@@ -1,8 +1,6 @@
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 { Show } from "solid-js"
import { useLanguage } from "../../context/language"
export type Translator = ReturnType<typeof useLanguage>["t"]
@@ -23,6 +21,7 @@ export type Modalities = {
export type VariantEntry = {
name: string
raw?: Record<string, unknown>
enableThinking: EnableThinkingValue
thinking: ThinkingTypeValue
splitReasoning: SplitReasoningValue
@@ -40,264 +39,8 @@ export type ModelEntry = {
variants: VariantEntry[]
}
type SelectOption<T> = { value: T; labelKey: string }
const ENABLE_THINKING_OPTIONS: SelectOption<EnableThinkingValue>[] = [
{ 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<ThinkingTypeValue>[] = [
{ 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<SplitReasoningValue>[] = [
{ 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<ChatTemplateArgsValue>[] = [
{ 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<ReasoningEffortValue>[] = [
{ 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<OutputEffortValue>[] = [
{ 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 (
<div>
<Show when={!props.isFirst()}>
<div
style={{
"border-top": "1px solid var(--border-weak-base, var(--vscode-panel-border))",
margin: "4px 0",
}}
/>
</Show>
<div
style={{
display: "flex",
gap: "8px",
"align-items": "stretch",
"flex-direction": "column",
"padding-top": "4px",
}}
>
<div style={{ "min-width": "100px", flex: "1 1 80px" }}>
<TextField
label={props.t("provider.custom.models.variants.name.label")}
placeholder={props.t("provider.custom.models.variants.name.placeholder")}
value={props.v.name}
onChange={props.onChangeName}
validationState={props.error?.name ? "invalid" : undefined}
error={props.error?.name}
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.enableThinking.label")}
</label>
<Select
options={ENABLE_THINKING_OPTIONS}
current={ENABLE_THINKING_OPTIONS.find((o) => o.value === props.v.enableThinking)}
value={(o) => String(o.value)}
label={(o) => props.t(o.labelKey)}
onSelect={(o) => props.onChangeEnableThinking(o?.value)}
placeholder={props.t("provider.custom.models.variants.enableThinking.placeholder")}
variant="secondary"
size="small"
triggerVariant="settings"
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.thinking.label")}
</label>
<Select
options={THINKING_OPTIONS}
current={THINKING_OPTIONS.find((o) => 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"
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.splitReasoning.label")}
</label>
<Select
options={SPLIT_REASONING_OPTIONS}
current={SPLIT_REASONING_OPTIONS.find((o) => o.value === props.v.splitReasoning)}
value={(o) => String(o.value)}
label={(o) => props.t(o.labelKey)}
onSelect={(o) => props.onChangeSplitReasoning(o?.value)}
placeholder={props.t("provider.custom.models.variants.splitReasoning.placeholder")}
variant="secondary"
size="small"
triggerVariant="settings"
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.reasoningEffort.label")}
</label>
<Select
options={REASONING_EFFORT_OPTIONS}
current={REASONING_EFFORT_OPTIONS.find((o) => 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"
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.outputEffort.label")}
</label>
<Select
options={OUTPUT_EFFORT_OPTIONS}
current={OUTPUT_EFFORT_OPTIONS.find((o) => o.value === props.v.outputEffort)}
value={(o) => String(o.value)}
label={(o) => props.t(o.labelKey)}
onSelect={(o) => props.onChangeOutputEffort(o?.value)}
placeholder={props.t("provider.custom.models.variants.outputEffort.placeholder")}
variant="secondary"
size="small"
triggerVariant="settings"
/>
</div>
<div
style={{
display: "flex",
"flex-direction": "column",
gap: "4px",
flex: "0 0 auto",
}}
>
<label
style={{ "font-size": "var(--kilo-font-size-12)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.chatTemplateArgs.label")}
</label>
<Select
options={CHAT_TEMPLATE_ARGS_OPTIONS}
current={CHAT_TEMPLATE_ARGS_OPTIONS.find((o) => 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"
/>
</div>
<IconButton
type="button"
icon="trash"
variant="ghost"
onClick={props.onRemove}
aria-label={props.t("provider.custom.models.variants.remove")}
style={{ "margin-bottom": "4px" }}
/>
</div>
</div>
)
}
type ModelCardProps = {
m: ModelEntry
i: () => number
errors: { id?: string; name?: string; variants?: Array<{ name?: string }> }
t: Translator
canRemove: boolean
@@ -306,18 +49,11 @@ 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) {
const issue = () => props.errors.variants?.find((error) => error.name)?.name
return (
<div
style={{
@@ -399,39 +135,15 @@ export function ModelCard(props: ModelCardProps) {
{props.t("provider.custom.models.modalities.image")}
</label>
{/* Variants — only available when reasoning is enabled */}
<Show when={props.m.reasoning}>
<Show when={props.m.variants.length > 0}>
<div style={{ display: "flex", "flex-direction": "column", gap: "0" }}>
<label
style={{ "font-size": "var(--kilo-font-size-11)", "font-weight": "500", color: "var(--text-weak-base)" }}
>
{props.t("provider.custom.models.variants.label")}
</label>
<For each={props.m.variants}>
{(v, vi) => (
<VariantRow
v={v}
vi={vi}
isFirst={() => 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())}
/>
)}
</For>
</div>
</Show>
<Button type="button" size="small" variant="ghost" icon="plus-small" onClick={props.onAddVariant}>
{props.t("provider.custom.models.variants.add")}
</Button>
<Show when={issue()}>
{(error) => (
<span
role="alert"
style={{ "font-size": "var(--kilo-font-size-12)", color: "var(--vscode-errorForeground)" }}
>
{error()}
</span>
)}
</Show>
</div>
)
@@ -57,8 +57,9 @@ const PROVIDER_ID = /^[a-z0-9][a-z0-9-_]*$/
function checkVariant(v: VariantEntry, seen: Set<string>, 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 }
}
@@ -105,6 +106,7 @@ function checkProviderID(id: string, editing: boolean, disabled: string[], exist
}
function serializeVariant(v: VariantEntry): [string, Record<string, unknown>] {
if (v.raw) return [v.name.trim(), v.raw]
const cfg: Record<string, unknown> = {}
if (v.enableThinking !== undefined) cfg.enable_thinking = v.enableThinking
if (v.thinking !== undefined) cfg.thinking = { type: v.thinking }
@@ -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<SettingsProps> = (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)
@@ -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<EnrichedModel, "providerID" | "id">): boolean
)
}
export function isAutoEfficient(model: Pick<EnrichedModel, "providerID" | "id">): boolean {
return model.providerID === KILO_GATEWAY_ID && model.id === KILO_AUTO_EFFICIENT_ID
}
export function autoChoices(
model: Pick<EnrichedModel, "providerID" | "id" | "autoRouting">,
catalog: readonly Pick<EnrichedModel, "id" | "name">[] = [],
): 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))]))
+2 -38
View File
@@ -297,6 +297,7 @@ export const dict = {
"ui.approval.source.yolo": "بواسطة وضع الموافقة التلقائية (YOLO)",
"ui.approval.source.session": "بواسطة قاعدة موافقة تلقائية للجلسة",
"ui.approval.source.default": "افتراضيًا",
"ui.approval.outsideWorkspace": "(خارج مساحة العمل: {{file}})",
"session.tab.review": "مراجعة",
"session.review.filesChanged": "تم تغيير {{count}} ملفات",
@@ -442,44 +443,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 أعلاه وحاول مرة أخرى.",
@@ -493,6 +456,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",
+2 -39
View File
@@ -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": "(fora do seu espaço de trabalho: {{file}})",
"session.tab.review": "Revisão",
"session.review.filesChanged": "{{count}} Arquivos Alterados",
@@ -453,45 +454,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.",
@@ -505,6 +467,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",
+2 -39
View File
@@ -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": "(izvan vašeg radnog prostora: {{file}})",
"session.tab.review": "Pregled",
"session.review.filesChanged": "Izmijenjeno {{count}} datoteka",
@@ -495,45 +496,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":
@@ -548,6 +510,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",
+2 -39
View File
@@ -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": "(uden for dit arbejdsområde: {{file}})",
"session.tab.review": "Gennemgang",
"session.review.filesChanged": "{{count}} Filer ændret",
@@ -493,45 +494,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.",
@@ -545,6 +507,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",
+2 -39
View File
@@ -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": "(außerhalb deines Arbeitsbereichs: {{file}})",
"session.tab.review": "Überprüfung",
"session.review.filesChanged": "{{count}} Dateien geändert",
@@ -503,45 +504,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":
@@ -556,6 +518,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",
+2 -39
View File
@@ -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": "(outside your workspace: {{file}})",
"session.tab.review": "Review",
"session.review.filesChanged": "{{count}} Files Changed",
@@ -407,45 +408,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.",
@@ -459,6 +421,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",
+2 -39
View File
@@ -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": "(fuera de tu espacio de trabajo: {{file}})",
"session.tab.review": "Revisión",
"session.review.filesChanged": "{{count}} Archivos Cambiados",
@@ -496,45 +497,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":
@@ -549,6 +511,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",
+2 -39
View File
@@ -302,6 +302,7 @@ export const dict = {
"ui.approval.source.yolo": "توسط حالت تأیید خودکار (YOLO)",
"ui.approval.source.session": "توسط قانون تأیید خودکار جلسه",
"ui.approval.source.default": "به‌طور پیش‌فرض",
"ui.approval.outsideWorkspace": "(خارج از فضای کاری شما: {{file}})",
"session.tab.review": "بررسی",
"session.review.filesChanged": "{{count}} فایل تغییر یافته",
@@ -408,45 +409,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 بالا را بررسی کرده و دوباره امتحان کنید.",
@@ -460,6 +422,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",
+2 -39
View File
@@ -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": "(hors de votre espace de travail : {{file}})",
"session.tab.review": "Revue",
"session.review.filesChanged": "{{count}} fichiers modifiés",
@@ -497,45 +498,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.",
@@ -549,6 +511,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",
+2 -39
View File
@@ -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": "(fuori dall'area di lavoro: {{file}})",
"session.tab.review": "Revisione",
"session.review.filesChanged": "{{count}} file modificati",
"session.review.loadingChanges": "Caricamento modifiche...",
@@ -317,45 +318,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.",
@@ -369,6 +331,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",
+2 -38
View File
@@ -304,6 +304,7 @@ export const dict = {
"ui.approval.source.yolo": "自動承認(YOLO)モードによって",
"ui.approval.source.session": "セッションの自動承認ルールによって",
"ui.approval.source.default": "デフォルトで",
"ui.approval.outsideWorkspace": "(ワークスペース外:{{file}})",
"session.tab.review": "レビュー",
"session.review.filesChanged": "{{count}} ファイル変更",
@@ -489,44 +490,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キーを確認して再試行してください。",
@@ -540,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",
+2 -38
View File
@@ -305,6 +305,7 @@ export const dict = {
"ui.approval.source.yolo": "자동 승인(YOLO) 모드에 의해",
"ui.approval.source.session": "세션 자동 승인 규칙에 의해",
"ui.approval.source.default": "기본값으로",
"ui.approval.outsideWorkspace": "(작업 영역 외부: {{file}})",
"session.tab.review": "검토",
"session.review.filesChanged": "{{count}}개 파일 변경됨",
@@ -450,44 +451,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 키를 확인하고 다시 시도하세요.",
@@ -501,6 +464,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",
+2 -39
View File
@@ -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": "(buiten je werkruimte: {{file}})",
"session.tab.review": "Beoordelen",
"session.review.filesChanged": "{{count}} bestanden gewijzigd",
@@ -446,45 +447,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":
@@ -499,6 +461,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",
+2 -39
View File
@@ -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": "(utenfor arbeidsområdet ditt: {{file}})",
"session.tab.review": "Gjennomgang",
"session.review.filesChanged": "{{count}} filer endret",
@@ -456,45 +457,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.",
@@ -508,6 +470,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",
+2 -39
View File
@@ -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": "(poza obszarem roboczym: {{file}})",
"session.tab.review": "Przegląd",
"session.review.filesChanged": "Zmieniono {{count}} plików",
@@ -451,45 +452,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":
@@ -504,6 +466,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",
+2 -39
View File
@@ -303,6 +303,7 @@ export const dict = {
"ui.approval.source.yolo": "режимом автоодобрения (YOLO)",
"ui.approval.source.session": "правилом автоодобрения сессии",
"ui.approval.source.default": "по умолчанию",
"ui.approval.outsideWorkspace": "(за пределами вашей рабочей области: {{file}})",
"session.tab.review": "Обзор",
"session.review.filesChanged": "{{count}} файлов изменено",
@@ -490,45 +491,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-ключ выше и попробуйте снова.",
@@ -542,6 +504,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",
+2 -39
View File
@@ -302,6 +302,7 @@ export const dict = {
"ui.approval.source.yolo": "โดยโหมดอนุมัติอัตโนมัติ (YOLO)",
"ui.approval.source.session": "โดยกฎอนุมัติอัตโนมัติของเซสชัน",
"ui.approval.source.default": "ตามค่าเริ่มต้น",
"ui.approval.outsideWorkspace": "(นอกพื้นที่ทำงานของคุณ: {{file}})",
"session.tab.review": "ตรวจสอบ",
"session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง",
@@ -487,45 +488,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 ด้านบนแล้วลองอีกครั้ง",
@@ -539,6 +501,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",
+2 -39
View File
@@ -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": "(çalışma alanınızın dışında: {{file}})",
"session.tab.review": "İnceleme",
"session.review.filesChanged": "{{count}} Dosya Değişti",
@@ -441,45 +442,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":
@@ -494,6 +456,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ı",
+2 -39
View File
@@ -307,6 +307,7 @@ export const dict = {
"ui.approval.source.yolo": "режимом автосхвалення (YOLO)",
"ui.approval.source.session": "правилом автосхвалення сесії",
"ui.approval.source.default": "за замовчуванням",
"ui.approval.outsideWorkspace": "(за межами вашого робочого простору: {{file}})",
"session.tab.review": "Огляд",
"session.review.filesChanged": "{{count}} файлів змінено",
@@ -445,45 +446,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-ключ вище і спробуйте ще раз.",
@@ -497,6 +459,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": "Назва-заголовка",
+2 -38
View File
@@ -292,6 +292,7 @@ export const dict = {
"ui.approval.source.yolo": "由自动批准(YOLO)模式",
"ui.approval.source.session": "由会话自动批准规则",
"ui.approval.source.default": "默认",
"ui.approval.outsideWorkspace": "(工作区之外:{{file}})",
"session.tab.review": "审查",
"session.review.filesChanged": "{{count}} 个文件变更",
@@ -472,44 +473,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 密钥后重试。",
@@ -523,6 +486,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",
+2 -38
View File
@@ -290,6 +290,7 @@ export const dict = {
"ui.approval.source.yolo": "由自動核准(YOLO)模式",
"ui.approval.source.session": "由工作階段自動核准規則",
"ui.approval.source.default": "預設",
"ui.approval.outsideWorkspace": "(工作區之外:{{file}})",
"session.tab.review": "審查",
"session.review.filesChanged": "{{count}} 個檔案變更",
@@ -432,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 金鑰後重試。",
@@ -483,6 +446,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",
@@ -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" },
@@ -146,3 +146,9 @@
flex: 1;
min-width: 0;
}
/* Wrap long skill paths/URLs within the shared 320px tooltip width. */
.settings-skills-tooltip-content {
white-space: normal;
overflow-wrap: anywhere;
}
@@ -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"),
},
}
}
@@ -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
],
}),
)
+16 -1
View File
@@ -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
@@ -27,6 +27,21 @@ 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
/** 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<string, unknown> | 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, path?: string): Approval {
if (permission !== "external_directory") return approval
return { ...approval, outsideWorkspace: true, ...(path ? { outsideWorkspacePath: path } : {}) }
}
export type Scope = "global" | "local"
@@ -71,13 +86,29 @@ 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<string, unknown> | undefined,
next: Record<string, unknown> | 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,
...(prior.outsideWorkspacePath ? { outsideWorkspacePath: prior.outsideWorkspacePath } : {}),
},
}
}
/**
@@ -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,39 @@ 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<Provider.Model["variants"]>
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(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)
// ---------------------------------------------------------------------------
@@ -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,
]
+11 -5
View File
@@ -37,6 +37,7 @@ import {
KILO_MODEL_SCHEMA_EXTENSIONS,
patchModelsDevModel as patchKiloModel,
patchConfigModel as patchKiloConfigModel,
customProviderVariants,
patchCustomLoaderResult,
patchKiloProviderPrivacy,
kiloSmallModelPriority,
@@ -1547,11 +1548,16 @@ const layer = Layer.effect(
// variants: {}, // kilocode_change, moved into patchKiloConfigModel
...patchKiloConfigModel(model, existingModel), // kilocode_change
}
const variants =
existingModel?.api.npm === parsedModel.api.npm
? (existingModel.variants ?? ProviderTransform.variants(parsedModel))
: ProviderTransform.variants(parsedModel)
const merged = mergeDeep(variants, model.variants ?? {})
// kilocode_change start
const baseGenerate = (m: typeof parsedModel) =>
existingModel?.api.npm === m.api.npm
? (existingModel.variants ?? ProviderTransform.variants(m))
: ProviderTransform.variants(m)
const generated = Object.keys(model.variants ?? {}).length
? {}
: customProviderVariants(parsedModel, model.provider?.npm ?? provider.npm, baseGenerate)
const merged = mergeDeep(generated, model.variants ?? {})
// kilocode_change end
parsedModel.variants = mapValues(
pickBy(merged, (v): v is NonNullable<typeof v> => !!v && !v.disabled), // kilocode_change - drop null delete sentinels
(v) => omit(v, ["disabled"]),
+52 -3
View File
@@ -328,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)
@@ -351,6 +365,18 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage
alibaba: {
cacheControl: { type: "ephemeral" },
},
// kilocode_change start
...(supportsPromptCacheBreakpoint(model.api.id)
? {
openai: {
promptCacheBreakpoint: { mode: "explicit" },
},
azure: {
promptCacheBreakpoint: { mode: "explicit" },
},
}
: {}),
// kilocode_change end
}
for (const msg of unique([...system, ...final])) {
@@ -360,8 +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) {
const lastContent = msg.content[msg.content.length - 1]
// kilocode_change start - place caching breakpoint on stable content before trailing <environment_details>
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.type !== "tool-approval-request" &&
part.type !== "tool-approval-response" &&
!(part.type === "text" && part.text.startsWith("<environment_details>"))
) {
targetIndex = i
break
}
}
const lastContent = targetIndex >= 0 ? parts[targetIndex] : parts[parts.length - 1]
if (
lastContent &&
typeof lastContent === "object" &&
@@ -372,6 +414,7 @@ function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage
continue
}
}
// kilocode_change end
msg.providerOptions = mergeDeep(msg.providerOptions ?? {}, providerOptions)
}
@@ -438,6 +481,7 @@ function mapProviderOptions(
export function message(msgs: ModelMessage[], model: Provider.Model, options: Record<string, unknown>) {
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" ||
@@ -446,11 +490,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" ||
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.api.npm === "@kilocode/kilo-gateway") &&
supportsPromptCacheBreakpoint(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)

Some files were not shown because too many files have changed in this diff Show More