Merge remote-tracking branch 'origin/main' into feat-show-approval-reason-outside-workspace-reads

# Conflicts:
#	packages/kilo-ui/src/components/message-part.tsx
This commit is contained in:
Bruno Agatao
2026-08-07 14:33:46 +02:00
67 changed files with 1109 additions and 371 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Move the "why was this tool call approved" line to after the tool output instead of between the header and body, add an icon to it, and add a Display setting to hide it.
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Keep Agent Manager terminals aligned and correctly wrapped when the terminal panel is narrow.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Keep recent sessions and the Show History action inset and usable in narrow VS Code sidebars.
+12 -21
View File
@@ -470,6 +470,8 @@
},
"devDependencies": {
"@axe-core/playwright": "4.11.3",
"@babel/core": "^7.28.4",
"@babel/preset-typescript": "^7.27.1",
"@playwright/test": "1.57.0",
"@storybook/addon-a11y": "10.2.10",
"@storybook/addon-docs": "10.2.10",
@@ -481,6 +483,7 @@
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.7.1",
"babel-preset-solid": "^1.9.9",
"esbuild": "^0.27.2",
"esbuild-plugin-solid": "^0.6.0",
"eslint": "^9.39.2",
@@ -984,23 +987,23 @@
},
},
"trustedDependencies": [
"esbuild",
"protobufjs",
"web-tree-sitter",
"esbuild",
"tree-sitter-bash",
"protobufjs",
],
"patchedDependencies": {
"@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
"mammoth@1.12.0": "patches/mammoth@1.12.0.patch",
"@ff-labs/fff-bun@0.9.4": "patches/@ff-labs%2Ffff-bun@0.9.4.patch",
"pacote@21.5.1": "patches/pacote@21.5.1.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"pacote@21.5.1": "patches/pacote@21.5.1.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
"effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch",
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"mammoth@1.12.0": "patches/mammoth@1.12.0.patch",
"@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch",
},
"overrides": {
"@effect/platform-node-shared": "4.0.0-beta.74",
@@ -5443,10 +5446,6 @@
"@solid-primitives/resize-observer/@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
"@standard-community/standard-json/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="],
"@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="],
"@storybook/addon-links/storybook": ["storybook@10.4.4", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-Nn0qFRxU5fyABa6dGRftfL3lz0Y+HkKOaAkfytF8S4Q2K6Szwwq7TwPAEs3Wsj8hBQbYhsobrKADcPsyXQpJaA=="],
"@storybook/addon-onboarding/storybook": ["storybook@10.4.4", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-Nn0qFRxU5fyABa6dGRftfL3lz0Y+HkKOaAkfytF8S4Q2K6Szwwq7TwPAEs3Wsj8hBQbYhsobrKADcPsyXQpJaA=="],
@@ -6153,14 +6152,6 @@
"@smithy/util-stream/@smithy/core/@smithy/types": ["@smithy/types@4.14.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ=="],
"@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-json/effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
"@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-openapi/effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
"@storybook/addon-links/storybook/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
"@storybook/addon-links/storybook/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
@@ -531,6 +531,7 @@ type Endpoint14_1Input = {
readonly cwd?: Endpoint14_1Request["payload"]["cwd"]
readonly title?: Endpoint14_1Request["payload"]["title"]
readonly env?: Endpoint14_1Request["payload"]["env"]
readonly size?: Endpoint14_1Request["payload"]["size"]
}
const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Input) =>
raw["pty.create"]({
@@ -541,6 +542,7 @@ const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Inpu
cwd: input?.["cwd"],
title: input?.["title"],
env: input?.["env"],
size: input?.["size"],
},
}).pipe(Effect.mapError(mapClientError))
+1
View File
@@ -840,6 +840,7 @@ export function make(options: ClientOptions) {
cwd: input?.["cwd"],
title: input?.["title"],
env: input?.["env"],
size: input?.["size"],
},
successStatus: 200,
declaredStatuses: [400, 401],
+13
View File
@@ -2632,6 +2632,7 @@ export type PtysCreateInput = {
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["command"]
readonly args?: {
readonly command?: string
@@ -2639,6 +2640,7 @@ export type PtysCreateInput = {
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["args"]
readonly cwd?: {
readonly command?: string
@@ -2646,6 +2648,7 @@ export type PtysCreateInput = {
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["cwd"]
readonly title?: {
readonly command?: string
@@ -2653,6 +2656,7 @@ export type PtysCreateInput = {
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["title"]
readonly env?: {
readonly command?: string
@@ -2660,7 +2664,16 @@ export type PtysCreateInput = {
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["env"]
readonly size?: {
readonly command?: string
readonly args?: ReadonlyArray<string>
readonly cwd?: string
readonly title?: string
readonly env?: { readonly [x: string]: string }
readonly size?: { readonly rows: number; readonly cols: number }
}["size"]
}
export type PtysCreateOutput = {
+11 -1
View File
@@ -214,7 +214,17 @@ const layer = Layer.effect(
}
yield* Effect.logInfo("creating session", { id, cmd: command, args, cwd })
const { spawn } = yield* Effect.promise(() => pty())
const proc = yield* Effect.sync(() => spawn(command, args, { name: "xterm-256color", cwd, env }))
// kilocode_change start - spawn with initial terminal dimensions
const proc = yield* Effect.sync(() =>
spawn(command, args, {
name: "xterm-256color",
cwd,
env,
cols: input.size?.cols,
rows: input.size?.rows,
}),
)
// kilocode_change end
const info: Info = {
id,
title: input.title || `Terminal ${id.slice(-4)}`,
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e3f86dabc39700b3a86508c1e6499a546eeabf720832b62c9ce12f2d97378ebd
size 34649
oid sha256:00092414080e2f70fb9b3166e8d06a1692af93e33717d0ab8fea5fe96e6a6982
size 34468
@@ -507,21 +507,25 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty
}
}
/* "why was this allowed" line inside a tool's expanded body */
/* "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-slot="tool-approval-line"] {
display: flex;
flex-wrap: wrap;
align-items: baseline;
align-items: center;
gap: 4px;
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;
[data-slot="tool-approval-decision"] {
font-weight: var(--font-weight-medium);
color: var(--text-strong);
svg {
flex-shrink: 0;
color: var(--text-weak);
}
[data-slot="tool-approval-rule"] {
+13 -7
View File
@@ -44,18 +44,24 @@ export function BasicTool(props: BasicToolProps) {
writeToolOpen(key(), open)
props.onOpenChange?.(open)
}
// Renders after the body/tool list, not before — it's context about what
// happened, not part of the header.
const details = () => (
<div data-slot="basic-tool-details">
<Show when={inBody() && approval()}>{(value) => <ToolApprovalLine display={value()} />}</Show>
{props.children}
<Show when={inBody() && approval()}>{(value) => <ToolApprovalLine display={value()} />}</Show>
</div>
)
if (!("children" in props) && !inBody()) {
return <Base {...props} defaultOpen={initial()} retainDetails={props.defer} onOpenChange={change} />
}
// A <Show>, not a plain `if`: inBody() tracks the visibility toggle, which can
// flip after mount (Settings), so the branch must stay reactive.
return (
<Base {...props} defaultOpen={initial()} retainDetails={props.defer} onOpenChange={change} hasDetails={inBody()}>
{details()}
</Base>
<Show
when={"children" in props || inBody()}
fallback={<Base {...props} defaultOpen={initial()} retainDetails={props.defer} onOpenChange={change} />}
>
<Base {...props} defaultOpen={initial()} retainDetails={props.defer} onOpenChange={change} hasDetails={inBody()}>
{details()}
</Base>
</Show>
)
}
@@ -49,7 +49,7 @@ import { Tooltip } from "./tooltip"
import { IconButton } from "./icon-button"
import { TextShimmer } from "@opencode-ai/ui/text-shimmer"
import { ToolApprovalProvider, resolveToolApproval, useToolApproval } from "./tool-approval"
export { ToolApprovalProvider, resolveToolApproval } from "./tool-approval"
export { ToolApprovalProvider, resolveToolApproval, ToolApprovalVisibilityProvider } from "./tool-approval"
import { GrowBox } from "./grow-box"
import { COLLAPSIBLE_SPRING } from "./motion"
import { busy, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils"
@@ -1,4 +1,5 @@
import { createContext, useContext, Show, type Accessor, type ParentProps } from "solid-js"
import { Icon } from "./icon"
/**
* Explains why a tool call was auto-approved, inside the expanded tool row.
@@ -33,8 +34,23 @@ export function ToolApprovalProvider(props: ParentProps<{ value: Accessor<ToolAp
return <Context.Provider value={props.value}>{props.children}</Context.Provider>
}
/**
* Whether the approval line should render at all. Hosts that expose a "hide
* auto-approval reason" display setting wrap their tree in
* `ToolApprovalVisibilityProvider`; without one, the line stays visible.
*/
const VisibilityContext = createContext<Accessor<boolean>>(() => true)
export function ToolApprovalVisibilityProvider(props: ParentProps<{ value: Accessor<boolean> }>) {
return <VisibilityContext.Provider value={props.value}>{props.children}</VisibilityContext.Provider>
}
/** Read the approval for the tool row below, gated by the visibility toggle
* here (not per call site) so a new `ToolApprovalProvider` usage can't forget it. */
export function useToolApproval() {
return useContext(Context)
const value = useContext(Context)
const visible = useContext(VisibilityContext)
return () => (visible() ? value() : undefined)
}
/** Read the raw approval payload off a tool part's metadata, if present. */
@@ -83,6 +99,7 @@ export function ToolApprovalLine(props: { display: ToolApprovalDisplay }) {
const manual = () => props.display.approval.source === "manual"
return (
<div data-slot="tool-approval-line" data-source={props.display.approval.source}>
<Icon name="shield" size="small" />
<span data-slot="tool-approval-decision">{props.display.decision}</span>
<Show when={!manual()}>
<Show when={props.display.source}>{(text) => <span data-slot="tool-approval-source">{text()}</span>}</Show>
+1 -1
View File
@@ -3,4 +3,4 @@
* Only add icons here that are actually used — esbuild/Vite will
* tree-shake unused exports but explicit re-exports keep the API small.
*/
export { WandSparkles } from "lucide-solid"
export { default as WandSparkles } from "lucide-solid/icons/wand-sparkles"
+172 -114
View File
@@ -1,10 +1,96 @@
const esbuild = require("esbuild")
const path = require("path")
const { solidPlugin } = require("esbuild-plugin-solid")
const fs = require("fs")
const crypto = require("crypto")
const core = require("@babel/core")
const solid = require("babel-preset-solid")
const ts = require("@babel/preset-typescript")
const production = process.argv.includes("--production")
const watch = process.argv.includes("--watch")
/**
* Cache transformed Solid JSX files in memory and on disk to avoid
* re-parsing and re-transforming unchanged files across builds and webviews.
*/
const solidCacheDir = path.join(__dirname, "node_modules", ".cache", "esbuild-solid")
const solidMemCache = new Map()
const buildScriptHash = crypto
.createHash("sha256")
.update(fs.readFileSync(__filename, "utf8"))
.update(require("babel-preset-solid/package.json").version || "")
.update(require("@babel/preset-typescript/package.json").version || "")
.digest("hex")
.slice(0, 8)
if (!fs.existsSync(solidCacheDir)) {
try {
fs.mkdirSync(solidCacheDir, { recursive: true })
} catch (err) {
console.warn("[esbuild] could not create solid cache directory", err)
}
}
const cachedSolidPlugin = {
name: "esbuild:solid-cached",
setup(build) {
build.onLoad({ filter: /\.(t|j)sx$/ }, async (args) => {
let mtime = 0
let size = 0
try {
const st = fs.statSync(args.path)
mtime = st.mtimeMs
size = st.size
} catch (err) {
console.warn("[esbuild] could not stat source file for cache key", args.path, err)
}
const cacheKey = `${args.path}:${mtime}:${size}:${buildScriptHash}`
const memHit = solidMemCache.get(cacheKey)
if (memHit) return { contents: memHit, loader: "js" }
const diskKey = crypto.createHash("sha256").update(cacheKey).digest("hex") + ".js"
const diskPath = path.join(solidCacheDir, diskKey)
if (fs.existsSync(diskPath)) {
try {
const diskCode = fs.readFileSync(diskPath, "utf8")
solidMemCache.set(cacheKey, diskCode)
return { contents: diskCode, loader: "js" }
} catch (err) {
console.warn("[esbuild] cache read failed, rebuilding", diskPath, err)
}
}
const source = fs.readFileSync(args.path, "utf8")
const { name, ext } = path.parse(args.path)
const filename = name + ext
const result = await core.transformAsync(source, {
presets: [
[solid, {}],
[ts, {}],
],
filename,
sourceMaps: "inline",
})
if (result?.code === void 0 || result.code === null) {
throw new Error("No result was provided from Babel")
}
if (solidMemCache.size > 2000) solidMemCache.clear()
solidMemCache.set(cacheKey, result.code)
try {
fs.writeFileSync(diskPath, result.code)
} catch (err) {
console.warn("[esbuild] cache write failed", diskPath, err)
}
return { contents: result.code, loader: "js" }
})
},
}
/**
* Force all solid-js imports (from kilo-ui and the webview) to resolve to
* the **same** copy so SolidJS contexts are shared across packages.
@@ -123,7 +209,7 @@ const svgSpritePlugin = {
name: "svg-sprite-inline",
setup(build) {
build.onLoad({ filter: /sprite\.svg$/ }, (args) => {
const content = require("fs").readFileSync(args.path, "utf8")
const content = fs.readFileSync(args.path, "utf8")
return {
contents: `
const svg = ${JSON.stringify(content)};
@@ -160,69 +246,8 @@ const cssPackageResolvePlugin = {
},
}
function createBrowserWebviewContext(entryPoint, outfile) {
return esbuild.context({
entryPoints: [entryPoint],
bundle: true,
format: "iife",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
outfile,
logLevel: "silent",
loader: {
".woff": "file",
".woff2": "file",
".ttf": "file",
},
plugins: [
solidDedupePlugin,
pierreWorkerAliasPlugin,
markdownWorkerUrlPlugin,
svgSpritePlugin,
cssPackageResolvePlugin,
solidPlugin(),
esbuildProblemMatcherPlugin,
],
})
}
// Bundle Pierre's Shiki worker into a single self-contained asset that the
// webviews load off the main thread for syntax highlighting.
function createShikiWorkerContext() {
return esbuild.context({
entryPoints: ["kilo-shiki-worker"],
bundle: true,
format: "iife",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
outfile: "dist/shiki-worker.js",
logLevel: "silent",
plugins: [shikiWorkerEntryPlugin, esbuildProblemMatcherPlugin],
})
}
function createMarkdownShikiWorkerContext() {
return esbuild.context({
entryPoints: [path.join(__dirname, "..", "ui", "src", "components", "markdown-shiki.worker.ts")],
bundle: true,
format: "esm",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
outfile: "dist/markdown-shiki-worker.js",
logLevel: "silent",
plugins: [esbuildProblemMatcherPlugin],
})
}
async function main() {
// Build extension
const extensionCtx = await esbuild.context({
function getExtensionConfig() {
return {
entryPoints: ["src/extension.ts"],
bundle: true,
format: "cjs",
@@ -239,68 +264,101 @@ async function main() {
outfile: "dist/extension.js",
external: ["vscode"],
logLevel: "silent",
plugins: [esbuildProblemMatcherPlugin],
})
plugins: watch ? [esbuildProblemMatcherPlugin] : [],
}
}
// Build Agent Manager webview (SolidJS, shares components with sidebar)
const agentManagerCtx = await createBrowserWebviewContext(
"webview-ui/agent-manager/index.tsx",
"dist/agent-manager.js",
)
function getWebviewsConfig() {
return {
entryPoints: {
"agent-manager": "webview-ui/agent-manager/index.tsx",
kiloclaw: "webview-ui/kiloclaw/index.tsx",
marketplace: "webview-ui/marketplace/index.tsx",
"diff-viewer": "webview-ui/diff-viewer/index.tsx",
"diff-virtual": "webview-ui/diff-virtual/index.tsx",
webview: "webview-ui/src/index.tsx",
},
outdir: "dist",
bundle: true,
format: "iife",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
logLevel: "silent",
loader: {
".woff": "file",
".woff2": "file",
".ttf": "file",
},
plugins: [
solidDedupePlugin,
pierreWorkerAliasPlugin,
markdownWorkerUrlPlugin,
svgSpritePlugin,
cssPackageResolvePlugin,
cachedSolidPlugin,
...(watch ? [esbuildProblemMatcherPlugin] : []),
],
}
}
// Build KiloClaw webview (SolidJS, standalone chat panel)
const kiloClawCtx = await createBrowserWebviewContext("webview-ui/kiloclaw/index.tsx", "dist/kiloclaw.js")
function getShikiWorkerConfig() {
return {
entryPoints: ["kilo-shiki-worker"],
bundle: true,
format: "iife",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
outfile: "dist/shiki-worker.js",
logLevel: "silent",
plugins: [shikiWorkerEntryPlugin, ...(watch ? [esbuildProblemMatcherPlugin] : [])],
}
}
// Build Marketplace webview (SolidJS, standalone catalog panel)
const marketplaceCtx = await createBrowserWebviewContext("webview-ui/marketplace/index.tsx", "dist/marketplace.js")
function getMarkdownShikiWorkerConfig() {
return {
entryPoints: [path.join(__dirname, "..", "ui", "src", "components", "markdown-shiki.worker.ts")],
bundle: true,
format: "esm",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "browser",
outfile: "dist/markdown-shiki-worker.js",
logLevel: "silent",
plugins: watch ? [esbuildProblemMatcherPlugin] : [],
}
}
// Build Diff Viewer webview (SolidJS, reuses Agent Manager diff components)
const diffViewerCtx = await createBrowserWebviewContext("webview-ui/diff-viewer/index.tsx", "dist/diff-viewer.js")
// Build Diff Virtual webview (lightweight single-file diff for permission approval)
const diffVirtualCtx = await createBrowserWebviewContext("webview-ui/diff-virtual/index.tsx", "dist/diff-virtual.js")
// Build webview
const webviewCtx = await createBrowserWebviewContext("webview-ui/src/index.tsx", "dist/webview.js")
// Build the shared Shiki highlighting worker asset
const shikiWorkerCtx = await createShikiWorkerContext()
const markdownShikiWorkerCtx = await createMarkdownShikiWorkerContext()
async function main() {
const extensionConfig = getExtensionConfig()
const webviewsConfig = getWebviewsConfig()
const shikiWorkerConfig = getShikiWorkerConfig()
const markdownShikiWorkerConfig = getMarkdownShikiWorkerConfig()
if (watch) {
const [extensionCtx, webviewsCtx, shikiWorkerCtx, markdownShikiWorkerCtx] = await Promise.all([
esbuild.context(extensionConfig),
esbuild.context(webviewsConfig),
esbuild.context(shikiWorkerConfig),
esbuild.context(markdownShikiWorkerConfig),
])
await Promise.all([
extensionCtx.watch(),
webviewCtx.watch(),
agentManagerCtx.watch(),
diffViewerCtx.watch(),
diffVirtualCtx.watch(),
kiloClawCtx.watch(),
marketplaceCtx.watch(),
webviewsCtx.watch(),
shikiWorkerCtx.watch(),
markdownShikiWorkerCtx.watch(),
])
} else {
await Promise.all([
extensionCtx.rebuild(),
webviewCtx.rebuild(),
agentManagerCtx.rebuild(),
kiloClawCtx.rebuild(),
marketplaceCtx.rebuild(),
diffViewerCtx.rebuild(),
diffVirtualCtx.rebuild(),
shikiWorkerCtx.rebuild(),
markdownShikiWorkerCtx.rebuild(),
])
await Promise.all([
extensionCtx.dispose(),
webviewCtx.dispose(),
agentManagerCtx.dispose(),
diffViewerCtx.dispose(),
diffVirtualCtx.dispose(),
kiloClawCtx.dispose(),
marketplaceCtx.dispose(),
shikiWorkerCtx.dispose(),
markdownShikiWorkerCtx.dispose(),
esbuild.build(extensionConfig),
esbuild.build(webviewsConfig),
esbuild.build(shikiWorkerConfig),
esbuild.build(markdownShikiWorkerConfig),
])
}
}
+11 -3
View File
@@ -1191,6 +1191,11 @@
"default": false,
"description": "Show tokens-per-second (prompt-processing / text-generation) badges on assistant messages and the task header"
},
"kilo-code.new.showAutoApprovalReason": {
"type": "boolean",
"default": true,
"description": "Show a line on tool calls explaining why they were auto-approved (matched rule, agent default, YOLO mode, etc.)"
},
"kilo-code.new.chat.shiftTabCyclesVariant": {
"type": "boolean",
"default": true,
@@ -1226,12 +1231,12 @@
"prepare:cli-binary": "bun script/local-bin.ts",
"prepare:sdk": "bun script/prepare-sdk.ts",
"build:launch": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check:production",
"compile": "bun run prepare:cli-binary -- --force && bun run rebuild-sdk && bun run build:check",
"watch": "bun run rebuild-sdk && bun run --parallel watch:esbuild watch:tsc",
"compile": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check",
"watch": "bun run prepare:sdk && bun run --parallel watch:esbuild watch:tsc",
"watch:esbuild": "bun run prepare:cli-binary && node esbuild.js --watch",
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
"watch:cli": "bun script/watch-cli.ts",
"package": "bun run prepare:cli-binary && bun run rebuild-sdk && bun run build:check:production",
"package": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check:production",
"build:check": "bun run --parallel check-types check-types:webview lint bundle",
"build:check:production": "bun run --parallel check-types check-types:webview lint bundle:production",
"bundle": "bun esbuild.js",
@@ -1263,6 +1268,8 @@
},
"devDependencies": {
"@axe-core/playwright": "4.11.3",
"@babel/core": "^7.28.4",
"@babel/preset-typescript": "^7.27.1",
"@playwright/test": "1.57.0",
"@storybook/addon-a11y": "10.2.10",
"@storybook/addon-docs": "10.2.10",
@@ -1274,6 +1281,7 @@
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.7.1",
"babel-preset-solid": "^1.9.9",
"esbuild": "^0.27.2",
"esbuild-plugin-solid": "^0.6.0",
"eslint": "^9.39.2",
+7 -5
View File
@@ -252,13 +252,15 @@ async function ensureBuiltBinary(): Promise<string> {
)
}
// Use the repository-pinned Bun version throughout. Newer canaries can fail compilation
// and must not cause packaged snapshots to fall back to the browser-mode source wrapper.
const pkg = await Bun.file(join(repoDir, "package.json")).json()
const bun = String(pkg.packageManager)
log("Installing dependencies in opencode package...")
await $`bunx ${bun} install --frozen-lockfile`.cwd(opencodeDir)
await $`bunx ${bun} run build --single --skip-install`.cwd(opencodeDir)
log("Building CLI binary...")
try {
await $`bunx ${bun} run build --single --skip-install`.cwd(opencodeDir)
} catch (err) {
log(`Pinned bunx build failed (${err}), running via active bun runtime...`)
await $`bun run script/build.ts --single --skip-install`.cwd(opencodeDir)
}
const built = await findKiloBinaryInOpencodeDist()
if (!built) {
+10
View File
@@ -186,6 +186,10 @@ import {
import { canonicalizePath, projectIdFor, samePath } from "./agent-manager/project/paths"
import { validChatSetting, watchChatConfig } from "./kilo-provider/chat-settings"
import { buildThroughputSettingMessage, watchThroughputConfig } from "./kilo-provider/throughput-settings"
import {
buildAutoApprovalReasonSettingMessage,
watchAutoApprovalReasonConfig,
} from "./kilo-provider/auto-approval-reason-settings"
let maxCost = 0
@@ -419,6 +423,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private indexingConfigDisposable: vscode.Disposable | null = null
private chatConfigDisposable: vscode.Disposable | null = null
private throughputConfigDisposable: vscode.Disposable | null = null
private autoApprovalReasonConfigDisposable: vscode.Disposable | null = null
private telemetryStateDisposable: vscode.Disposable | null = null
private viewStateDisposable: vscode.Disposable | null = null
private visibilityDisposable: vscode.Disposable | null = null
@@ -1002,6 +1007,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.chatConfigDisposable = watchChatConfig((msg) => this.postMessage(msg))
this.throughputConfigDisposable?.dispose()
this.throughputConfigDisposable = watchThroughputConfig((msg) => this.postMessage(msg))
this.autoApprovalReasonConfigDisposable?.dispose()
this.autoApprovalReasonConfigDisposable = watchAutoApprovalReasonConfig((msg) => this.postMessage(msg))
this.telemetryStateDisposable?.dispose()
this.telemetryStateDisposable = watchTelemetryState((msg) => this.postMessage(msg))
this.webviewMessageDisposable = webview.onDidReceiveMessage(async (message) => {
@@ -1832,6 +1839,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.sendNotificationSettings()
this.sendTimelineSetting()
this.postMessage(buildThroughputSettingMessage())
this.postMessage(buildAutoApprovalReasonSettingMessage())
this.postMessage({ type: "extensionDataReady" })
console.log("[Kilo New] KiloProvider: ✅ initializeConnection completed successfully")
@@ -4072,6 +4080,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.sendNotificationSettings()
this.sendTimelineSetting()
this.postMessage(buildThroughputSettingMessage())
this.postMessage(buildAutoApprovalReasonSettingMessage())
this.sendWorkStyle()
await ModelState.reset(this.client, (msg) => this.postMessage(msg))
@@ -5001,6 +5010,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.indexingConfigDisposable?.dispose()
this.chatConfigDisposable?.dispose()
this.throughputConfigDisposable?.dispose()
this.autoApprovalReasonConfigDisposable?.dispose()
this.telemetryStateDisposable?.dispose()
this.autoApproveBridge?.dispose()
this.visibleTaskStreams.clear()
@@ -51,6 +51,7 @@ interface Entry {
export class TerminalManager {
private readonly entries = new Map<string, Entry>()
private readonly restarts = new Map<string, Promise<void>>()
private readonly pending = new Map<string, { cols: number; rows: number }>()
constructor(private readonly deps: TerminalManagerDeps) {}
@@ -67,7 +68,14 @@ export class TerminalManager {
worktreeId: string | null
cwd: string
title: string
cols?: number
rows?: number
}): Promise<{ terminalId: string; worktreeId: string | null; title: string; wsUrl: string }> {
const initial =
this.pending.get(params.terminalId) ??
(params.cols !== undefined && params.rows !== undefined ? { cols: params.cols, rows: params.rows } : undefined)
this.pending.delete(params.terminalId)
const client = this.deps.getClient()
const { data, error } = await client.pty.create({
directory: params.cwd,
@@ -76,6 +84,7 @@ export class TerminalManager {
// xterm's DOM renderer cannot draw the Unicode sextant glyphs used by
// Kilo's modern wordmark, so use the compatible logo in embedded tabs.
env,
size: initial,
})
if (error || !data) {
const err = error instanceof Error ? error.message : String(error ?? "unknown error")
@@ -89,15 +98,39 @@ export class TerminalManager {
title: data.title ?? params.title,
}
this.entries.set(params.terminalId, entry)
// If a resize arrived while pty.create was in flight that differed from `initial`, apply it now.
const latest = this.pending.get(params.terminalId)
if (latest && (latest.cols !== initial?.cols || latest.rows !== initial?.rows)) {
this.pending.delete(params.terminalId)
const { error: resizeErr } = await client.pty.update({
directory: entry.cwd,
ptyID: entry.ptyID,
size: latest,
})
if (resizeErr) {
const err = resizeErr instanceof Error ? resizeErr.message : String(resizeErr)
this.deps.log(`Initial terminal resize failed (${params.terminalId}): ${err}`)
}
}
const wsUrl = this.deps.buildWsUrl(entry.ptyID, entry.cwd)
this.deps.log(`Terminal created: ${params.terminalId} -> pty ${entry.ptyID} cwd=${entry.cwd}`)
return { terminalId: params.terminalId, worktreeId: entry.worktreeId, title: entry.title, wsUrl }
}
/** Forward a resize event to the backend PTY. Missing terminals are a no-op. */
/**
* Forward a resize event to the backend PTY.
*
* If the terminal creation is still in flight, dimensions are queued into
* `pending` and applied during PTY initialization before the WebSocket
* URL is returned.
*/
async resize(terminalId: string, cols: number, rows: number): Promise<void> {
const entry = this.entries.get(terminalId)
if (!entry) return
if (!entry) {
this.pending.set(terminalId, { cols, rows })
return
}
this.pending.delete(terminalId)
const client = this.deps.getClient()
const { error } = await client.pty.update({
directory: entry.cwd,
@@ -126,6 +159,7 @@ export class TerminalManager {
* failed delete would be silently logged as a successful close and
* the server-side PTY would linger until `kilo serve` exits. */
async close(terminalId: string): Promise<void> {
this.pending.delete(terminalId)
const entry = this.entries.get(terminalId)
if (!entry) return
this.entries.delete(terminalId)
@@ -187,6 +221,7 @@ export class TerminalManager {
* is sampled mid-shutdown.
*/
async dispose(): Promise<void> {
this.pending.clear()
const snapshot = [...this.entries.values()]
if (snapshot.length === 0) {
this.entries.clear()
@@ -88,7 +88,7 @@ export class TerminalRouter {
handle(m: AgentManagerInMessage): boolean {
if (!isTerminalMessage(m)) return false
if (m.type === "agentManager.terminal.create") {
void this.handleCreate(m.createId, m.placement, m.worktreeId)
void this.handleCreate(m.createId, m.placement, m.worktreeId, m.cols, m.rows)
return true
}
if (m.type === "agentManager.terminal.close") {
@@ -132,7 +132,13 @@ export class TerminalRouter {
return manager.dispose()
}
private async handleCreate(createId: string, placement: TerminalPlacement, worktreeId: string | null): Promise<void> {
private async handleCreate(
createId: string,
placement: TerminalPlacement,
worktreeId: string | null,
cols?: number,
rows?: number,
): Promise<void> {
const generation = this.generation
const manager = this.manager
const cwd = this.resolveCwd(worktreeId)
@@ -155,7 +161,7 @@ export class TerminalRouter {
// Join the shared backend connection instead of racing its synchronous
// client accessor when this is the first Kilo action in the window.
await this.deps.getClientAsync()
const created = await manager.create({ terminalId: createId, worktreeId, cwd, title })
const created = await manager.create({ terminalId: createId, worktreeId, cwd, title, cols, rows })
if (generation !== this.generation) {
await manager.close(created.terminalId)
return
@@ -946,6 +946,8 @@ interface TerminalCreateIn {
placement: TerminalPlacement
/** null for LOCAL, worktree id otherwise */
worktreeId: string | null
cols?: number
rows?: number
}
interface TerminalCloseIn {
@@ -0,0 +1,19 @@
import * as vscode from "vscode"
type Post = (msg: unknown) => void
export function buildAutoApprovalReasonSettingMessage() {
const config = vscode.workspace.getConfiguration("kilo-code.new")
return {
type: "autoApprovalReasonSettingLoaded" as const,
visible: config.get<boolean>("showAutoApprovalReason", true),
}
}
export function watchAutoApprovalReasonConfig(post: Post): vscode.Disposable {
return vscode.workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration("kilo-code.new.showAutoApprovalReason")) {
post(buildAutoApprovalReasonSettingMessage())
}
})
}
@@ -6,6 +6,7 @@ import type { SuggestionContext } from "./handlers/suggestion"
import type { KiloClient } from "@kilocode/sdk/v2/client"
import { buildChatSettingsMessage } from "./chat-settings"
import { buildThroughputSettingMessage } from "./throughput-settings"
import { buildAutoApprovalReasonSettingMessage } from "./auto-approval-reason-settings"
import { handleModelUsageMessage, type ModelUsageMessage } from "./model-usage"
type Ctx = {
@@ -71,6 +72,10 @@ export async function routeEarlyMessage(
ctx.post(buildThroughputSettingMessage())
return true
}
if (message.type === "requestAutoApprovalReasonSetting") {
ctx.post(buildAutoApprovalReasonSettingMessage())
return true
}
if (message.type === "requestSpeechToTextModels") {
await ctx.speechToTextModels()
return true
@@ -39,6 +39,29 @@ test("does not refit hidden terminal buffers during resize", () => {
expect(callback!.indexOf("if (!props.active) return")).toBeLessThan(callback!.indexOf("fit.fit()"))
})
test("keeps raw PTY line endings and initializes Unicode widths before attaching", () => {
expect(terminal).toContain("convertEol: false")
expect(terminal).toContain('term.unicode.activeVersion = "15-graphemes"')
expect(terminal.indexOf("term.loadAddon(new UnicodeGraphemesAddon())")).toBeLessThan(
terminal.indexOf("open(props.wsUrl)"),
)
})
test("fits and forces the initial PTY dimensions before socket attach", () => {
expect(terminal).toContain("const syncSize = (force = false)")
expect(terminal).toContain("if (props.active) syncSize(true)")
expect(terminal.indexOf("fitNow()\n open(props.wsUrl)")).toBeGreaterThan(-1)
})
test("re-sends dimensions when an optimistic terminal receives its PTY", () => {
const created = terminal.match(
/if \(message\.terminalId === props\.terminalId && !ws\) \{([\s\S]*?)\n \}/,
)?.[1]
expect(created).toBeDefined()
expect(created).toContain("fitNow()")
expect(created!.indexOf("fitNow()")).toBeLessThan(created!.indexOf("open(message.wsUrl)"))
})
test("clamps the restored inspector width to the shared layout bounds", () => {
expect(clampPanelWidth(undefined, 1200)).toBe(600)
expect(clampPanelWidth(500, 1200)).toBe(500)
@@ -343,4 +343,62 @@ describe("Agent Manager terminal routing", () => {
})
await router.dispose()
})
it("applies initial create dimensions and queues resize messages before creation settles", async () => {
const creates: Array<Record<string, unknown>> = []
const updates: Array<{ ptyID: string; size?: { cols: number; rows: number } }> = []
let createResolver: ((value: { data: { id: string; title: string } }) => void) | undefined
const client = {
pty: {
create: (params: Record<string, unknown>) =>
new Promise<{ data: { id: string; title: string } }>((resolve) => {
creates.push(params)
createResolver = resolve
}),
remove: async () => ({ data: true }),
update: async (params: { ptyID: string; size?: { cols: number; rows: number } }) => {
updates.push(params)
return { data: true }
},
},
} as unknown as KiloClient
const router = new TerminalRouter({
getClient: () => client,
getClientAsync: async () => client,
getServerConfig: () => ({ baseUrl: "http://127.0.0.1:4096", password: "secret" }),
getRoot: () => "/workspace",
getWorktreePath: () => undefined,
getProjectId: () => "prj-1",
log: () => undefined,
post: () => undefined,
getTerminalFont: () => font,
})
router.handle({
type: "agentManager.terminal.create",
createId: "queued",
placement: "side",
worktreeId: null,
cols: 60,
rows: 20,
})
await wait()
expect(creates).toHaveLength(1)
expect(creates[0]?.size).toEqual({ cols: 60, rows: 20 })
// Send a resize before pty.create settles (optimistic side terminal layout)
router.handle({
type: "agentManager.terminal.resize",
terminalId: "queued",
cols: 55,
rows: 18,
})
await wait()
expect(updates).toHaveLength(0)
createResolver?.({ data: { id: "pty-queued", title: "Terminal 1" } })
await wait()
expect(updates).toEqual([{ directory: "/workspace", ptyID: "pty-queued", size: { cols: 55, rows: 18 } }])
await router.dispose()
})
})
@@ -0,0 +1,62 @@
import { describe, it, expect } from "bun:test"
import fs from "node:fs"
import path from "node:path"
import { builtinModules } from "node:module"
const ROOT = path.resolve(import.meta.dir, "../..")
const PKG_FILE = path.join(ROOT, "package.json")
const ESBUILD_FILE = path.join(ROOT, "esbuild.js")
const BUILTINS = new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)])
function extractPackageName(specifier: string): string | null {
if (specifier.startsWith(".") || specifier.startsWith("/")) return null
if (BUILTINS.has(specifier)) return null
if (specifier.startsWith("node:")) return null
if (specifier.startsWith("@")) {
const parts = specifier.split("/")
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier
}
return specifier.split("/")[0]
}
function findImportsAndRequires(content: string): string[] {
const specifiers = new Set<string>()
const requireRegex = /require\(["']([^"']+)["']\)/g
const importRegex = /(?:import|from)\s+["']([^"']+)["']/g
for (const match of content.matchAll(requireRegex)) {
specifiers.add(match[1])
}
for (const match of content.matchAll(importRegex)) {
specifiers.add(match[1])
}
return Array.from(specifiers)
}
describe("Build Script Dependency Declarations", () => {
it("esbuild.js must declare all imported/required packages in package.json", () => {
const pkg = JSON.parse(fs.readFileSync(PKG_FILE, "utf8"))
const declared = new Set([
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.devDependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
"vscode",
])
const esbuildContent = fs.readFileSync(ESBUILD_FILE, "utf8")
const specifiers = findImportsAndRequires(esbuildContent)
const undeclared: string[] = []
for (const spec of specifiers) {
const pkgName = extractPackageName(spec)
if (pkgName && !declared.has(pkgName)) {
undeclared.push(`${spec} (package: ${pkgName})`)
}
}
expect(undeclared).toEqual([])
})
})
@@ -24,17 +24,24 @@ afterEach(async () => {
)
})
function gitExec(args: string[]) {
const res = Bun.spawnSync(args, { stdout: "ignore", stderr: "pipe" })
if (res.exitCode !== 0) {
const err = Buffer.from(res.stderr).toString("utf8")
throw new Error(`git command failed (${args.join(" ")}): ${err}`)
}
}
/** Create a temp git repo with an initial commit (required for worktrees). */
async function createTempRepo(): Promise<string> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "kilo-wt-"))
tempDirs.push(dir)
const git = simpleGit(dir)
await git.init()
await git.addConfig("user.email", "test@test.com")
await git.addConfig("user.name", "Test")
gitExec(["git", "init", "-b", "main", dir])
gitExec(["git", "-C", dir, "config", "user.email", "test@test.com"])
gitExec(["git", "-C", dir, "config", "user.name", "Test"])
await fs.writeFile(path.join(dir, "README.md"), "init")
await git.add(".")
await git.commit("initial commit")
gitExec(["git", "-C", dir, "add", "."])
gitExec(["git", "-C", dir, "commit", "-m", "initial commit"])
return dir
}
@@ -51,31 +58,18 @@ async function changedFiles(cwd: string): Promise<string[]> {
/** Create a temp repo with a bare origin remote so origin/<branch> refs exist. */
async function createTempRepoWithOrigin(): Promise<{ bare: string; clone: string }> {
// Use a non-bare seed repo to control the initial branch name, then clone bare
const seed = await fs.mkdtemp(path.join(os.tmpdir(), "kilo-wt-seed-"))
tempDirs.push(seed)
const seedGit = simpleGit(seed)
await seedGit.init()
await seedGit.addConfig("user.email", "test@test.com")
await seedGit.addConfig("user.name", "Test")
await fs.writeFile(path.join(seed, "README.md"), "init")
await seedGit.add(".")
await seedGit.commit("initial commit")
// Ensure branch is named "main" regardless of system default
const seedBranch = (await seedGit.revparse(["--abbrev-ref", "HEAD"])).trim()
if (seedBranch !== "main") await seedGit.raw(["branch", "-m", seedBranch, "main"])
// Clone to bare, then clone again as working copy
const bare = await fs.mkdtemp(path.join(os.tmpdir(), "kilo-wt-bare-"))
tempDirs.push(bare)
await simpleGit().clone(seed, bare, ["--bare"])
const clone = await fs.mkdtemp(path.join(os.tmpdir(), "kilo-wt-clone-"))
tempDirs.push(clone)
await simpleGit().clone(bare, clone)
const cloneGit = simpleGit(clone)
await cloneGit.addConfig("user.email", "test@test.com")
await cloneGit.addConfig("user.name", "Test")
tempDirs.push(bare, clone)
gitExec(["git", "init", "--bare", "-b", "main", bare])
gitExec(["git", "clone", bare, clone])
gitExec(["git", "-C", clone, "config", "user.email", "test@test.com"])
gitExec(["git", "-C", clone, "config", "user.name", "Test"])
await fs.writeFile(path.join(clone, "README.md"), "init")
gitExec(["git", "-C", clone, "add", "."])
gitExec(["git", "-C", clone, "commit", "-m", "initial commit"])
gitExec(["git", "-C", clone, "push", "-u", "origin", "main"])
return { bare, clone }
}
@@ -152,7 +152,10 @@ export const TerminalTab: Component<Props> = (props) => {
onMount(() => {
const term = new Terminal({
convertEol: true,
// PTY output already contains terminal line endings. Converting LF to
// CRLF here corrupts raw PTY output and is especially visible when a
// narrow terminal wraps and redraws the prompt.
convertEol: false,
cursorBlink: true,
cursorInactiveStyle: "outline",
fontFamily: props.font.fontFamily,
@@ -164,17 +167,11 @@ export const TerminalTab: Component<Props> = (props) => {
const fit = new FitAddon()
term.loadAddon(fit)
term.open(host)
// Fit on the next frame — `host` might still have 0px dimensions
// during the initial layout pass otherwise.
requestAnimationFrame(() => {
try {
fit.fit()
} catch (err) {
// Host still detached at mount time. ResizeObserver will retry
// once layout kicks in. Logged so regressions don't hide.
log("initial fit() threw", err)
}
})
// Unicode width must be configured before the first PTY bytes are parsed.
// Loading it later can leave already-wrapped graphemes with stale cell
// widths, which moves the cursor in narrow terminals.
term.loadAddon(new UnicodeGraphemesAddon())
term.unicode.activeVersion = "15-graphemes"
// Pass Agent Manager hotkeys through to the parent key handler so
// ⌘T / ⌘⇧T / ⌘W / terminal cycling / ⌘⌥← still work while focused.
@@ -356,11 +353,9 @@ export const TerminalTab: Component<Props> = (props) => {
}
const disposeData = term.onData(send)
const disposeBinary = term.onBinary(send)
open(props.wsUrl)
// These addons are not needed to paint the initial prompt. Defer them
// until after the first frame so their startup work, especially the
// Unicode 15 width tables, does not delay the shell connection.
// until after the first frame so their startup work does not delay the
// shell connection.
const loadAddons = () => {
deferred = undefined
if (closed) return
@@ -375,16 +370,8 @@ export const TerminalTab: Component<Props> = (props) => {
)
// OSC 52 clipboard support for shell programs such as tmux and neovim.
term.loadAddon(new ClipboardAddon())
// Use grapheme-aware width tables for newer emoji and ZWJ sequences.
term.loadAddon(new UnicodeGraphemesAddon())
term.unicode.activeVersion = "15-graphemes"
term.refresh(0, Math.max(0, term.rows - 1))
}
frame = requestAnimationFrame(() => {
frame = undefined
deferred = requestAnimationFrame(loadAddons)
})
const restarted = (url: string) => {
open(url)
}
@@ -395,10 +382,12 @@ export const TerminalTab: Component<Props> = (props) => {
let resizeTimer: ReturnType<typeof setTimeout> | undefined
let lastCols = term.cols
let lastRows = term.rows
const syncSize = () => {
if (term.cols === lastCols && term.rows === lastRows) return
let synced = false
const syncSize = (force = false) => {
if (!force && synced && term.cols === lastCols && term.rows === lastRows) return
lastCols = term.cols
lastRows = term.rows
synced = true
vscode.postMessage({
type: "agentManager.terminal.resize",
terminalId: props.terminalId,
@@ -406,6 +395,16 @@ export const TerminalTab: Component<Props> = (props) => {
rows: term.rows,
})
}
const fitNow = () => {
try {
fit.fit()
if (props.active) syncSize(true)
} catch (err) {
// Host still detached at mount time. ResizeObserver will retry
// once layout kicks in. Logged so regressions don't hide.
log("fit() threw", err)
}
}
const ro = new ResizeObserver(() => {
if (!props.active) return
try {
@@ -422,6 +421,16 @@ export const TerminalTab: Component<Props> = (props) => {
resizeTimer = setTimeout(syncSize, RESIZE_DEBOUNCE_MS)
})
ro.observe(host)
// Wait for the first committed layout before attaching the socket. This
// prevents the shell from emitting its first prompt at xterm's default
// 80 columns, which is most visible in a narrow side panel.
frame = requestAnimationFrame(() => {
frame = undefined
if (closed) return
fitNow()
open(props.wsUrl)
deferred = requestAnimationFrame(loadAddons)
})
// ---- Repaint recovery ----
//
@@ -452,7 +461,7 @@ export const TerminalTab: Component<Props> = (props) => {
if (!isRenderable()) return
try {
fit.fit()
syncSize()
syncSize(!synced)
} catch (err) {
// Layout not settled yet; ResizeObserver retries on next change.
log("repaint fit() threw", err)
@@ -485,6 +494,9 @@ export const TerminalTab: Component<Props> = (props) => {
if (message.terminalId === props.terminalId && !ws) {
term.options.fontFamily = message.font.fontFamily
term.options.fontSize = message.font.fontSize
// Optimistic side terminals can fit before their backend PTY exists;
// force the first resize again once the created response arrives.
fitNow()
scheduleRepaint()
open(message.wsUrl)
}
@@ -628,10 +628,32 @@ function newId(): string {
}
/**
* Build the close-terminal handler the main component wires to the
* close button. Picks the next visible tab before dropping the entry
* so focus flows naturally; notifies the extension last.
* Estimate initial terminal geometry from the current DOM container.
* Provides best-effort columns and rows so PTY spawn avoids the default
* 80-column line width before the first xterm fit pass commits.
*/
function measureInitialDimensions(
placement: TerminalPlacement,
font: TerminalFont,
): { cols: number; rows: number } | undefined {
if (typeof document === "undefined") return undefined
const selector =
placement === "side"
? ".am-side-terminal-layer, .am-side-terminal, .am-diff-panel-wrapper"
: ".am-terminal-layer, .am-detail-stack"
const host = document.querySelector(selector) as HTMLElement | null
const rect = host?.getBoundingClientRect()
if (!rect || rect.width <= 0 || rect.height <= 0) return undefined
const cellWidth = font.fontSize > 0 ? font.fontSize * 0.6 : 7.2
const cellHeight = font.fontSize > 0 ? font.fontSize * 1.2 : 14.4
const availableWidth = Math.max(0, rect.width - 30)
const availableHeight = Math.max(0, rect.height - 16)
return {
cols: Math.max(10, Math.floor(availableWidth / cellWidth)),
rows: Math.max(3, Math.floor(availableHeight / cellHeight)),
}
}
export function createTerminalHandlers(deps: TerminalHandlerDeps) {
const activate = (id: string) => {
deps.state.setActiveId(id)
@@ -645,11 +667,15 @@ export function createTerminalHandlers(deps: TerminalHandlerDeps) {
const requestNew = () => {
const sel = deps.getSelection()
if (sel === null) return
const font = deps.getFont()
const dims = measureInitialDimensions("tab", font)
deps.postMessage({
type: "agentManager.terminal.create",
createId: newId(),
placement: "tab",
worktreeId: sel === deps.LOCAL ? null : sel,
cols: dims?.cols,
rows: dims?.rows,
})
}
@@ -659,12 +685,14 @@ export function createTerminalHandlers(deps: TerminalHandlerDeps) {
// project-namespaced state key and must not leak into the message.
const sel = deps.getSelection()
const id = newId()
const font = deps.getFont()
const dims = measureInitialDimensions("side", font)
deps.state.beginSide(key, id)
deps.state.add(key === deps.LOCAL ? null : key, {
id,
title: "Terminal",
wsUrl: "",
font: deps.getFont(),
font,
placement: "side",
})
deps.state.setSideActive(key, id)
@@ -674,6 +702,8 @@ export function createTerminalHandlers(deps: TerminalHandlerDeps) {
createId: id,
placement: "side",
worktreeId: sel === null || sel === deps.LOCAL ? null : sel,
cols: dims?.cols,
rows: dims?.rows,
})
}
@@ -104,6 +104,19 @@ const DisplayTab: Component = () => {
</Switch>
</SettingsRow>
<SettingsRow
title={language.t("settings.display.autoApprovalReason.title")}
description={language.t("settings.display.autoApprovalReason.description")}
>
<Switch
checked={Boolean(settings()["showAutoApprovalReason"] ?? true)}
onChange={(checked: boolean) => updateSetting("showAutoApprovalReason", checked)}
hideLabel
>
{language.t("settings.display.autoApprovalReason.title")}
</Switch>
</SettingsRow>
<SettingsRow
title={language.t("settings.display.terminalCommand.title")}
description={language.t("settings.display.terminalCommand.description")}
@@ -77,6 +77,7 @@ function loadedSettings(message: ExtensionMessage): Record<string, unknown> | un
return { "chat.shiftTabCyclesVariant": message.settings.shiftTabCyclesVariant }
}
if (message.type === "throughputSettingLoaded") return { showTokenThroughput: message.visible }
if (message.type === "autoApprovalReasonSettingLoaded") return { showAutoApprovalReason: message.visible }
}
export const ConfigProvider: ParentComponent = (props) => {
@@ -13,6 +13,7 @@ import { useConfig } from "./config"
import { useVSCode } from "./vscode"
import type { ExtensionMessage } from "../types/messages"
import { applyFontSize, clampFontSize, readFontSize } from "../font-size"
import { ToolApprovalVisibilityProvider } from "@kilocode/kilo-ui/message-part"
interface DisplayContextValue {
reasoningAutoCollapse: Accessor<boolean>
@@ -23,6 +24,8 @@ interface DisplayContextValue {
// every AssistantMessage and the aggregated row in TaskHeader, so flipping
// the setting once updates both surfaces without round-trips.
throughputVisible: Accessor<boolean>
// Whether the "why was this tool call approved" line renders on tool calls.
autoApprovalReasonVisible: Accessor<boolean>
}
export const DisplayContext = createContext<DisplayContextValue>()
@@ -33,15 +36,20 @@ export const DisplayProvider: ParentComponent = (props) => {
const reasoningAutoCollapse = createMemo(() => config().auto_collapse_reasoning ?? false)
const [fontSize, setFontSizeSignal] = createSignal(readFontSize())
const [throughputVisible, setThroughputVisible] = createSignal(false)
const [autoApprovalReasonVisible, setAutoApprovalReasonVisible] = createSignal(true)
// Request the throughput toggle once on mount; the extension posts back
// Request both toggles once on mount; the extension posts back
// (and onDidChangeConfiguration forwards subsequent edits).
onMount(() => vscode.postMessage({ type: "requestThroughputSetting" }))
onMount(() => {
vscode.postMessage({ type: "requestThroughputSetting" })
vscode.postMessage({ type: "requestAutoApprovalReasonSetting" })
})
const unsubscribe = vscode.onMessage((message: ExtensionMessage) => {
if (message.type === "ready" && message.fontSize !== undefined) setFontSizeSignal(clampFontSize(message.fontSize))
if (message.type === "fontSizeChanged") setFontSizeSignal(clampFontSize(message.fontSize))
if (message.type === "throughputSettingLoaded") setThroughputVisible(Boolean(message.visible))
if (message.type === "autoApprovalReasonSettingLoaded") setAutoApprovalReasonVisible(Boolean(message.visible))
})
createEffect(() => {
@@ -62,9 +70,13 @@ export const DisplayProvider: ParentComponent = (props) => {
vscode.postMessage({ type: "updateSetting", key: "fontSize", value: next })
},
throughputVisible,
autoApprovalReasonVisible,
}}
>
{props.children}
{/* Bridges the toggle into kilo-ui's generic gate so every tool render hides the line consistently. */}
<ToolApprovalVisibilityProvider value={autoApprovalReasonVisible}>
{props.children}
</ToolApprovalVisibilityProvider>
</DisplayContext.Provider>
)
}
+9 -6
View File
@@ -1105,19 +1105,22 @@ export const dict = {
"settings.display.shiftTabCycle.title": "تبديل جهد الاستدلال باستخدام Shift+Tab",
"settings.display.shiftTabCycle.description":
"اضغط على Shift+Tab في حقل إدخال الموجه للتبديل إلى مستوى جهد الاستدلال التالي. عطّل هذا الخيار للاحتفاظ بـ Shift+Tab للتنقل بين عناصر التركيز باستخدام لوحة المفاتيح.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "كتل أوامر الطرفية",
"settings.display.terminalCommand.description": "اختر ما إذا كانت كتل أوامر الطرفية تبدأ موسّعة أم مطوية.",
"settings.display.terminalCommand.expanded": "موسّعة",
"settings.display.terminalCommand.collapsed": "مطوية",
"settings.display.codeEdit.title": "كتل تعديلات التعليمات البرمجية",
"settings.display.codeEdit.description":
"اختر ما إذا كانت الكتل التي تعرض تعديلات التعليمات البرمجية والفروقات تبدأ موسّعة أم مطوية.",
"settings.display.codeEdit.expanded": "موسّعة",
"settings.display.codeEdit.collapsed": "مطوية",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "إظهار إنتاجية الرموز",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"عرض معدل توليد النص (رموز/ثانية) على آخر رسالة من المساعد وفي رأس المهمة. مخفي بشكل افتراضي للحفاظ على تنظيم المحادثة.",
"settings.display.autoApprovalReason.title": "إظهار سبب الموافقة التلقائية",
"settings.display.autoApprovalReason.description":
"إظهار سطر عند استدعاءات الأدوات يوضح سبب الموافقة التلقائية عليها (قاعدة مطابقة، إعداد افتراضي للوكيل، وضع YOLO، إلخ).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1148,19 +1148,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Alternar o esforço de raciocínio com Shift+Tab",
"settings.display.shiftTabCycle.description":
"Pressione Shift+Tab em um campo de entrada de prompt para alternar para o próximo nível de esforço de raciocínio. Desative para manter Shift+Tab para navegação de foco pelo teclado.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Blocos de comando do terminal",
"settings.display.terminalCommand.description":
"Escolha se os blocos de comando do terminal começam expandidos ou recolhidos.",
"settings.display.terminalCommand.expanded": "Expandidos",
"settings.display.terminalCommand.collapsed": "Recolhidos",
"settings.display.codeEdit.title": "Blocos de edição de código",
"settings.display.codeEdit.description":
"Escolha se os blocos que exibem edições de código e diferenças começam expandidos ou recolhidos.",
"settings.display.codeEdit.expanded": "Expandidos",
"settings.display.codeEdit.collapsed": "Recolhidos",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Mostrar taxa de tokens",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Exibe a taxa de geração de texto (tokens/s) na última mensagem do assistente e no cabeçalho da tarefa. Oculto por padrão para manter o chat organizado.",
"settings.display.autoApprovalReason.title": "Mostrar motivo da aprovação automática",
"settings.display.autoApprovalReason.description":
"Mostra uma linha nas chamadas de ferramentas explicando por que foram aprovadas automaticamente (regra correspondente, padrão do agente, modo YOLO, etc.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1138,19 +1138,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Promijeni napor razmišljanja pomoću Shift+Tab",
"settings.display.shiftTabCycle.description":
"Pritisnite Shift+Tab u polju za unos upita da pređete na sljedeći nivo napora razmišljanja. Onemogućite ovu opciju kako biste zadržali Shift+Tab za navigaciju fokusom putem tastature.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Blokovi terminalskih naredbi",
"settings.display.terminalCommand.description":
"Odaberite da li blokovi terminalskih naredbi počinju prošireni ili sažeti.",
"settings.display.terminalCommand.expanded": "Prošireni",
"settings.display.terminalCommand.collapsed": "Sažeti",
"settings.display.codeEdit.title": "Blokovi izmjena koda",
"settings.display.codeEdit.description":
"Odaberite da li će blokovi koji prikazuju izmjene koda i razlike u početku biti prošireni ili sažeti.",
"settings.display.codeEdit.expanded": "Prošireni",
"settings.display.codeEdit.collapsed": "Sažeti",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Prikaži protok tokena",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Prikazuje brzinu generisanja teksta (tokena/s) na najnovijoj poruci asistenta i u zaglavlju zadatka. Podrazumevano skriveno radi urednijeg chata.",
"settings.display.autoApprovalReason.title": "Prikaži razlog automatskog odobravanja",
"settings.display.autoApprovalReason.description":
"Prikazuje red uz pozive alata koji objašnjava zašto su automatski odobreni (odgovarajuće pravilo, podrazumevana vrijednost agenta, YOLO režim itd.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+9 -6
View File
@@ -1134,19 +1134,22 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Skift ræsonnementsindsats med Shift+Tab",
"settings.display.shiftTabCycle.description":
"Tryk på Shift+Tab i et promptindtastningsfelt for at skifte til næste niveau af ræsonnementsindsats. Deaktivér for at beholde Shift+Tab til tastaturnavigation af fokus.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Terminalkommandoblokke",
"settings.display.terminalCommand.description": "Vælg om terminalkommandoblokke starter foldet ud eller sammen.",
"settings.display.terminalCommand.expanded": "Foldet ud",
"settings.display.terminalCommand.collapsed": "Foldet sammen",
"settings.display.codeEdit.title": "Koderedigeringsblokke",
"settings.display.codeEdit.description":
"Vælg, om blokke, der viser koderedigeringer og forskelle, starter foldet ud eller sammen.",
"settings.display.codeEdit.expanded": "Foldet ud",
"settings.display.codeEdit.collapsed": "Foldet sammen",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Vis genereringshastighed",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Viser tekstgenereringshastigheden (tokens/sek.) på den seneste assistentmeddelelse og i opgavehovedet. Skjult som standard for at holde chatten ryddig.",
"settings.display.autoApprovalReason.title": "Vis grund til automatisk godkendelse",
"settings.display.autoApprovalReason.description":
"Viser en linje ved værktøjskald, der forklarer, hvorfor de blev automatisk godkendt (matchende regel, agent-standard, YOLO-tilstand osv.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1161,19 +1161,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Reasoning-Aufwand mit Shift+Tab durchlaufen",
"settings.display.shiftTabCycle.description":
"Drücken Sie Shift+Tab in einem Prompt-Eingabefeld, um zur nächsten Stufe des Reasoning-Aufwands zu wechseln. Deaktivieren Sie dies, um Shift+Tab für die Tastaturfokusnavigation beizubehalten.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Terminalbefehlsblöcke",
"settings.display.terminalCommand.description":
"Wählen Sie, ob Terminalbefehlsblöcke anfangs aus- oder eingeklappt sind.",
"settings.display.terminalCommand.expanded": "Ausgeklappt",
"settings.display.terminalCommand.collapsed": "Eingeklappt",
"settings.display.codeEdit.title": "Blöcke für Codebearbeitungen",
"settings.display.codeEdit.description":
"Wählen Sie, ob Blöcke mit Codebearbeitungen und Unterschieden anfangs aus- oder eingeklappt sind.",
"settings.display.codeEdit.expanded": "Ausgeklappt",
"settings.display.codeEdit.collapsed": "Eingeklappt",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Token-Durchsatz anzeigen",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Zeigt die Textgenerierungsrate (Tokens/Sek.) in der letzten Assistentennachricht und im Aufgabenkopf an. Standardmäßig ausgeblendet, um den Chat übersichtlich zu halten.",
"settings.display.autoApprovalReason.title": "Grund für automatische Genehmigung anzeigen",
"settings.display.autoApprovalReason.description":
"Zeigt bei Tool-Aufrufen eine Zeile an, die erklärt, warum sie automatisch genehmigt wurden (passende Regel, Agent-Standard, YOLO-Modus usw.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
@@ -1121,6 +1121,9 @@ export const dict = {
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"settings.display.autoApprovalReason.title": "Show Auto-Approval Reason",
"settings.display.autoApprovalReason.description":
"Show a line on tool calls explaining why they were auto-approved (matched rule, agent default, YOLO mode, etc.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1151,19 +1151,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Alternar el esfuerzo de razonamiento con Shift+Tab",
"settings.display.shiftTabCycle.description":
"Pulsa Shift+Tab en un campo de entrada de prompt para cambiar al siguiente nivel de esfuerzo de razonamiento. Desactívalo para conservar Shift+Tab para la navegación del foco con el teclado.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Bloques de comandos de terminal",
"settings.display.terminalCommand.description":
"Elige si los bloques de comandos de terminal aparecen inicialmente expandidos o contraídos.",
"settings.display.terminalCommand.expanded": "Expandidos",
"settings.display.terminalCommand.collapsed": "Contraídos",
"settings.display.codeEdit.title": "Bloques de edición de código",
"settings.display.codeEdit.description":
"Elige si los bloques de edición de código y de diferencias aparecen inicialmente expandidos o contraídos.",
"settings.display.codeEdit.expanded": "Expandidos",
"settings.display.codeEdit.collapsed": "Contraídos",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Mostrar rendimiento de tokens",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Muestra la tasa de generación de texto (tokens/s) en el último mensaje del asistente y en el encabezado de la tarea. Oculto de forma predeterminada para mantener el chat ordenado.",
"settings.display.autoApprovalReason.title": "Mostrar motivo de aprobación automática",
"settings.display.autoApprovalReason.description":
"Muestra una línea en las llamadas a herramientas que explica por qué se aprobaron automáticamente (regla coincidente, valor predeterminado del agente, modo YOLO, etc.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+3
View File
@@ -1133,6 +1133,9 @@ export const dict = {
"settings.display.tokenThroughput.title": "نمایش توان عملیاتی توکن",
"settings.display.tokenThroughput.description":
"نرخ تولید متن (توکن/ثانیه) را در آخرین پیام دستیار و در سربرگ وظیفه نمایش می‌دهد. به‌طور پیش‌فرض پنهان است تا چت شلوغ نشود.",
"settings.display.autoApprovalReason.title": "نمایش دلیل تأیید خودکار",
"settings.display.autoApprovalReason.description":
"نمایش خطی در فراخوانی ابزارها که توضیح می‌دهد چرا به‌طور خودکار تأیید شده‌اند (قانون مطابق، پیش‌فرض عامل، حالت YOLO و غیره).",
"chat.throughput.tooltip":
"میانگین {{speed}} توکن/ثانیه برای این نوبت. شامل توکن‌های خروجی و استدلال می‌شود؛ زمان اجرای ابزار و انتظار را شامل نمی‌شود.",
+10 -6
View File
@@ -1168,19 +1168,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Parcourir l'effort de raisonnement avec Shift+Tab",
"settings.display.shiftTabCycle.description":
"Appuyez sur Shift+Tab dans un champ de saisie de prompt pour passer au niveau d'effort de raisonnement suivant. Désactivez cette option pour conserver Shift+Tab pour la navigation du focus au clavier.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Blocs de commande de terminal",
"settings.display.terminalCommand.description":
"Choisissez si les blocs de commande de terminal sont initialement développés ou réduits.",
"settings.display.terminalCommand.expanded": "Développés",
"settings.display.terminalCommand.collapsed": "Réduits",
"settings.display.codeEdit.title": "Blocs de modification du code",
"settings.display.codeEdit.description":
"Choisissez si les blocs de modification du code et de différences sont initialement développés ou réduits.",
"settings.display.codeEdit.expanded": "Développés",
"settings.display.codeEdit.collapsed": "Réduits",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Afficher le débit de tokens",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Affiche le taux de génération de texte (tokens/s) sur le dernier message de l'assistant et dans l'en-tête de la tâche. Masqué par défaut pour garder le chat épuré.",
"settings.display.autoApprovalReason.title": "Afficher la raison de l'approbation automatique",
"settings.display.autoApprovalReason.description":
"Affiche une ligne sur les appels d'outils expliquant pourquoi ils ont été approuvés automatiquement (règle correspondante, agent par défaut, mode YOLO, etc.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+5 -2
View File
@@ -1004,9 +1004,12 @@ export const dict = {
"settings.display.codeEdit.expanded": "Espansi",
"settings.display.codeEdit.collapsed": "Compressi",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Mostra velocità di generazione dei token",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Mostra la velocità di generazione del testo (token/sec) sull'ultimo messaggio dell'assistente e nell'intestazione dell'attività. Nascosto per impostazione predefinita per mantenere la chat ordinata.",
"settings.display.autoApprovalReason.title": "Mostra motivo dell'approvazione automatica",
"settings.display.autoApprovalReason.description":
"Mostra una riga sulle chiamate agli strumenti che spiega perché sono state approvate automaticamente (regola corrispondente, predefinito dell'agente, modalità YOLO, ecc.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1126,19 +1126,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Shift+Tab で推論の強度を切り替える",
"settings.display.shiftTabCycle.description":
"プロンプト入力欄で Shift+Tab を押すと、次の推論の強度レベルに切り替わります。Shift+Tab をキーボードフォーカスの移動に使用する場合は、無効にしてください。",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "ターミナルコマンドブロック",
"settings.display.terminalCommand.description":
"ターミナルコマンドブロックを最初から展開するか折りたたむかを選択します。",
"settings.display.terminalCommand.expanded": "展開",
"settings.display.terminalCommand.collapsed": "折りたたみ",
"settings.display.codeEdit.title": "コード編集ブロック",
"settings.display.codeEdit.description":
"コード編集ブロックと差分ブロックを最初から展開するか折りたたむかを選択します。",
"settings.display.codeEdit.expanded": "展開",
"settings.display.codeEdit.collapsed": "折りたたみ",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "トークンスループットを表示",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"最新のアシスタントメッセージとタスクヘッダーにテキスト生成速度(トークン/秒)を表示します。チャットを整理するためデフォルトでは非表示です。",
"settings.display.autoApprovalReason.title": "自動承認の理由を表示",
"settings.display.autoApprovalReason.description":
"ツール呼び出しが自動承認された理由(一致したルール、エージェントのデフォルト、YOLOモードなど)を示す行を表示します。",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+9 -6
View File
@@ -1115,18 +1115,21 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Shift+Tab으로 추론 강도 전환",
"settings.display.shiftTabCycle.description":
"프롬프트 입력란에서 Shift+Tab을 눌러 다음 추론 강도 수준으로 전환합니다. Shift+Tab을 키보드 포커스 탐색에 사용하려면 비활성화하세요.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "터미널 명령 블록",
"settings.display.terminalCommand.description": "터미널 명령 블록을 처음부터 펼칠지 접을지 선택합니다.",
"settings.display.terminalCommand.expanded": "펼침",
"settings.display.terminalCommand.collapsed": "접힘",
"settings.display.codeEdit.title": "코드 편집 블록",
"settings.display.codeEdit.description": "코드 편집 블록과 차이점 블록을 처음부터 펼칠지 접을지 선택합니다.",
"settings.display.codeEdit.expanded": "펼침",
"settings.display.codeEdit.collapsed": "접힘",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "토큰 처리량 표시",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"최신 어시스턴트 메시지와 작업 헤더에 텍스트 생성 속도(토큰/초)를 표시합니다. 채팅을 깔끔하게 유지하기 위해 기본적으로 숨겨져 있습니다.",
"settings.display.autoApprovalReason.title": "자동 승인 이유 표시",
"settings.display.autoApprovalReason.description":
"도구 호출이 자동으로 승인된 이유(일치한 규칙, 에이전트 기본값, YOLO 모드 등)를 설명하는 줄을 표시합니다.",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1108,19 +1108,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Doorloop niveaus van redeneringsinspanning met Shift+Tab",
"settings.display.shiftTabCycle.description":
"Druk op Shift+Tab in een promptinvoerveld om naar het volgende niveau van redeneringsinspanning te gaan. Schakel dit uit om Shift+Tab te behouden voor focusnavigatie via het toetsenbord.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Terminalopdrachtblokken",
"settings.display.terminalCommand.description":
"Kies of terminalopdrachtblokken standaard uitgeklapt of ingeklapt zijn.",
"settings.display.terminalCommand.expanded": "Uitgeklapt",
"settings.display.terminalCommand.collapsed": "Ingeklapt",
"settings.display.codeEdit.title": "Blokken met codebewerkingen",
"settings.display.codeEdit.description":
"Kies of blokken met codebewerkingen en verschillen standaard uitgeklapt of ingeklapt zijn.",
"settings.display.codeEdit.expanded": "Uitgeklapt",
"settings.display.codeEdit.collapsed": "Ingeklapt",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Tokendoorvoer weergeven",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Toont de tekstgeneratiesnelheid (tokens/sec) bij het laatste assistentbericht en in de taakkop. Standaard verborgen om de chat overzichtelijk te houden.",
"settings.display.autoApprovalReason.title": "Reden voor automatische goedkeuring weergeven",
"settings.display.autoApprovalReason.description":
"Toont een regel bij tool-aanroepen die uitlegt waarom ze automatisch zijn goedgekeurd (overeenkomende regel, agentstandaard, YOLO-modus, enz.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+8 -4
View File
@@ -1132,10 +1132,11 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Bytt resonnementsinnsats med Shift+Tab",
"settings.display.shiftTabCycle.description":
"Trykk Shift+Tab i et promptinndatafelt for å bytte til neste nivå for resonnementsinnsats. Deaktiver for å beholde Shift+Tab for tastaturnavigering av fokus.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Blokker for terminalkommandoer",
"settings.display.terminalCommand.description":
"Velg om blokker for terminalkommandoer skal være utvidet eller skjult fra start.",
"settings.display.terminalCommand.expanded": "Utvidet",
"settings.display.terminalCommand.collapsed": "Skjult",
"settings.display.codeEdit.title": "Blokker for kodeendringer",
"settings.display.codeEdit.description":
"Velg om blokker for kodeendringer og forskjeller skal være utvidet eller skjult fra start.",
@@ -1145,6 +1146,9 @@ export const dict = {
"settings.display.tokenThroughput.title": "Vis genereringshastighet",
"settings.display.tokenThroughput.description":
"Vis tekstgenereringshastighet (tokens/sek) på den siste assistentmeldingen og i oppgaveoverskriften. Skjult som standard for å holde chatten ryddig.",
"settings.display.autoApprovalReason.title": "Vis årsak til automatisk godkjenning",
"settings.display.autoApprovalReason.description":
"Viser en linje ved verktøykall som forklarer hvorfor de ble automatisk godkjent (samsvarende regel, agentstandard, YOLO-modus osv.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1139,19 +1139,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Przełączaj wysiłek rozumowania za pomocą Shift+Tab",
"settings.display.shiftTabCycle.description":
"Naciśnij Shift+Tab w polu wprowadzania promptu, aby przełączyć się na następny poziom wysiłku rozumowania. Wyłącz tę opcję, aby zachować Shift+Tab do nawigacji fokusem za pomocą klawiatury.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Bloki poleceń terminala",
"settings.display.terminalCommand.description":
"Wybierz, czy bloki poleceń terminala mają być początkowo rozwinięte czy zwinięte.",
"settings.display.terminalCommand.expanded": "Rozwinięte",
"settings.display.terminalCommand.collapsed": "Zwinięte",
"settings.display.codeEdit.title": "Bloki edycji kodu",
"settings.display.codeEdit.description":
"Wybierz, czy bloki edycji kodu i podglądy różnic mają być początkowo rozwinięte czy zwinięte.",
"settings.display.codeEdit.expanded": "Rozwinięte",
"settings.display.codeEdit.collapsed": "Zwinięte",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Pokaż przepustowość tokenów",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Wyświetla szybkość generowania tekstu (tokeny/s) w ostatniej wiadomości asystenta i w nagłówku zadania. Domyślnie skryte, aby czat był przejrzysty.",
"settings.display.autoApprovalReason.title": "Pokaż powód automatycznego zatwierdzenia",
"settings.display.autoApprovalReason.description":
"Pokazuje wiersz przy wywołaniach narzędzi wyjaśniający, dlaczego zostały automatycznie zatwierdzone (dopasowana reguła, wartość domyślna agenta, tryb YOLO itp.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1133,19 +1133,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Переключать усилие рассуждения с помощью Shift+Tab",
"settings.display.shiftTabCycle.description":
"Нажмите Shift+Tab в поле ввода запроса, чтобы перейти к следующему уровню усилий рассуждения. Отключите эту настройку, чтобы сохранить Shift+Tab для навигации по фокусу с помощью клавиатуры.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Блоки команд терминала",
"settings.display.terminalCommand.description":
"Выберите, будут ли блоки команд терминала изначально развёрнуты или свёрнуты.",
"settings.display.terminalCommand.expanded": "Развёрнуты",
"settings.display.terminalCommand.collapsed": "Свёрнуты",
"settings.display.codeEdit.title": "Блоки изменений кода",
"settings.display.codeEdit.description":
"Выберите, будут ли блоки изменений кода и различий изначально развёрнуты или свёрнуты.",
"settings.display.codeEdit.expanded": "Развёрнуты",
"settings.display.codeEdit.collapsed": "Свёрнуты",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Показывать пропускную способность токенов",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Отображает скорость генерации текста (токенов/с) в последнем сообщении ассистента и в заголовке задачи. По умолчанию скрыто, чтобы не загромождать чат.",
"settings.display.autoApprovalReason.title": "Показывать причину автоодобрения",
"settings.display.autoApprovalReason.description":
"Показывает строку у вызовов инструментов, объясняющую, почему они были одобрены автоматически (совпавшее правило, значение агента по умолчанию, режим YOLO и т. д.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+9 -6
View File
@@ -1113,18 +1113,21 @@ export const dict = {
"settings.display.shiftTabCycle.title": "สลับระดับความพยายามในการให้เหตุผลด้วย Shift+Tab",
"settings.display.shiftTabCycle.description":
"กด Shift+Tab ในช่องป้อนพรอมต์เพื่อสลับไปยังระดับความพยายามในการให้เหตุผลถัดไป ปิดใช้งานเพื่อคง Shift+Tab ไว้สำหรับการนำทางโฟกัสด้วยแป้นพิมพ์",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "บล็อกคำสั่งเทอร์มินัล",
"settings.display.terminalCommand.description": "เลือกว่าบล็อกคำสั่งเทอร์มินัลจะเริ่มต้นแบบขยายหรือยุบ",
"settings.display.terminalCommand.expanded": "ขยาย",
"settings.display.terminalCommand.collapsed": "ยุบ",
"settings.display.codeEdit.title": "บล็อกการแก้ไขโค้ด",
"settings.display.codeEdit.description": "เลือกว่าบล็อกการแก้ไขโค้ดและบล็อกแสดงความแตกต่างจะเริ่มต้นแบบขยายหรือยุบ",
"settings.display.codeEdit.expanded": "ขยาย",
"settings.display.codeEdit.collapsed": "ยุบ",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "แสดงอัตราการประมวลผลโทเคน",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"แสดงอัตราการสร้างข้อความ (โทเคน/วินาที) บนข้อความล่าสุดของผู้ช่วยและในส่วนหัวของงาน ซ่อนโดยค่าเริ่มต้นเพื่อให้แชทดูเรียบร้อย",
"settings.display.autoApprovalReason.title": "แสดงเหตุผลการอนุมัติอัตโนมัติ",
"settings.display.autoApprovalReason.description":
"แสดงบรรทัดในการเรียกใช้เครื่องมือเพื่ออธิบายว่าเหตุใดจึงได้รับการอนุมัติอัตโนมัติ (กฎที่ตรงกัน ค่าเริ่มต้นของเอเจนต์ โหมด YOLO ฯลฯ)",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1094,19 +1094,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Shift+Tab ile akıl yürütme eforunu değiştir",
"settings.display.shiftTabCycle.description":
"Bir sonraki akıl yürütme eforu seviyesine geçmek için komut girişinde Shift+Tab tuşlarına basın. Shift+Tab tuşunu klavye odağında gezinmek için korumak üzere devre dışı bırakın.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Terminal Komut Blokları",
"settings.display.terminalCommand.description":
"Terminal komut bloklarının başlangıçta genişletilmiş mi yoksa daraltılmış mı olacağını seçin.",
"settings.display.terminalCommand.expanded": "Genişletilmiş",
"settings.display.terminalCommand.collapsed": "Daraltılmış",
"settings.display.codeEdit.title": "Kod Düzenleme Blokları",
"settings.display.codeEdit.description":
"Kod düzenleme ve fark bloklarının başlangıçta genişletilmiş mi yoksa daraltılmış mı olacağını seçin.",
"settings.display.codeEdit.expanded": "Genişletilmiş",
"settings.display.codeEdit.collapsed": "Daraltılmış",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Token İşleme Hızını Göster",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"En son asistan mesajında ve görev başlığında metin üretim hızını (token/sn) gösterir. Sohbeti dağınık göstermemek için varsayılan olarak gizlidir.",
"settings.display.autoApprovalReason.title": "Otomatik Onay Nedenini Göster",
"settings.display.autoApprovalReason.description":
"Araç çağrılarının neden otomatik olarak onaylandığını açıklayan bir satır gösterir (eşleşen kural, aracı varsayılanı, YOLO modu vb.).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+10 -6
View File
@@ -1094,19 +1094,23 @@ export const dict = {
"settings.display.shiftTabCycle.title": "Перемикати зусилля міркування за допомогою Shift+Tab",
"settings.display.shiftTabCycle.description":
"Натисніть Shift+Tab у полі введення запиту, щоб перейти до наступного рівня зусиль міркування. Вимкніть цю опцію, щоб зберегти Shift+Tab для навігації фокусом за допомогою клавіатури.",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "Блоки команд термінала",
"settings.display.terminalCommand.description":
"Виберіть, чи будуть блоки команд термінала спочатку розгорнутими чи згорнутими.",
"settings.display.terminalCommand.expanded": "Розгорнуті",
"settings.display.terminalCommand.collapsed": "Згорнуті",
"settings.display.codeEdit.title": "Блоки редагування коду",
"settings.display.codeEdit.description":
"Виберіть, чи будуть блоки редагування коду та відмінностей спочатку розгорнутими чи згорнутими.",
"settings.display.codeEdit.expanded": "Розгорнуті",
"settings.display.codeEdit.collapsed": "Згорнуті",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "Показувати пропускну здатність токенів",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"Показує швидкість генерації тексту (токенів/с) на останньому повідомленні асистента та в заголовку завдання. За замовчуванням приховано, щоб чат залишався охайним.",
"settings.display.autoApprovalReason.title": "Показувати причину автосхвалення",
"settings.display.autoApprovalReason.description":
"Показує рядок біля викликів інструментів, що пояснює, чому їх автоматично схвалено (відповідне правило, стандартне значення агента, режим YOLO тощо).",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+9 -6
View File
@@ -1073,18 +1073,21 @@ export const dict = {
"settings.display.shiftTabCycle.title": "使用 Shift+Tab 切换推理强度",
"settings.display.shiftTabCycle.description":
"在提示输入框中按 Shift+Tab 可切换到下一个推理强度等级。禁用此选项可将 Shift+Tab 用于键盘焦点导航。",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "终端命令块",
"settings.display.terminalCommand.description": "选择终端命令块的初始状态:展开或折叠。",
"settings.display.terminalCommand.expanded": "展开",
"settings.display.terminalCommand.collapsed": "折叠",
"settings.display.codeEdit.title": "代码编辑块",
"settings.display.codeEdit.description": "选择代码编辑块和差异块的初始状态:展开或折叠。",
"settings.display.codeEdit.expanded": "展开",
"settings.display.codeEdit.collapsed": "折叠",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "显示令牌吞吐量",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"在最新的助手消息和任务标题中显示文本生成速率(令牌/秒)。默认隐藏以保持聊天简洁。",
"settings.display.autoApprovalReason.title": "显示自动批准原因",
"settings.display.autoApprovalReason.description":
"在工具调用中显示一行说明其被自动批准的原因(匹配的规则、代理默认值、YOLO 模式等)。",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
+9 -6
View File
@@ -1036,18 +1036,21 @@ export const dict = {
"settings.display.shiftTabCycle.title": "使用 Shift+Tab 切換推理強度",
"settings.display.shiftTabCycle.description":
"在提示輸入框中按 Shift+Tab 可切換至下一個推理強度等級。停用此選項可保留 Shift+Tab 用於鍵盤焦點導覽。",
"settings.display.terminalCommand.title": "Terminal Command Blocks",
"settings.display.terminalCommand.description": "Choose whether terminal command blocks start expanded or collapsed.",
"settings.display.terminalCommand.expanded": "Expanded",
"settings.display.terminalCommand.collapsed": "Collapsed",
"settings.display.terminalCommand.title": "終端命令區塊",
"settings.display.terminalCommand.description": "選擇終端命令區塊的初始狀態:展開或收合。",
"settings.display.terminalCommand.expanded": "展開",
"settings.display.terminalCommand.collapsed": "收合",
"settings.display.codeEdit.title": "程式碼編輯區塊",
"settings.display.codeEdit.description": "選擇程式碼編輯區塊與差異區塊的初始狀態:展開或收合。",
"settings.display.codeEdit.expanded": "展開",
"settings.display.codeEdit.collapsed": "收合",
"settings.display.tokenThroughput.title": "Show Token Throughput",
"settings.display.tokenThroughput.title": "顯示權杖吞吐量",
"settings.display.tokenThroughput.description":
"Display the text-generation rate (tokens/sec) on the latest assistant message and in the task header. Hidden by default to keep the chat uncluttered.",
"在最新的助理訊息與工作標題中顯示文字生成速率(權杖/秒)。預設隱藏,以保持對話簡潔。",
"settings.display.autoApprovalReason.title": "顯示自動核准原因",
"settings.display.autoApprovalReason.description":
"在工具呼叫中顯示一行說明其被自動核准的原因(符合的規則、代理預設值、YOLO 模式等)。",
"chat.throughput.tooltip":
"Average {{speed}} tokens/s for this turn. Includes output and reasoning tokens; excludes tool execution and waiting time.",
@@ -143,10 +143,14 @@
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
height: 100%;
width: 100%;
min-width: 0;
color: var(--vscode-descriptionForeground);
text-align: center;
padding: 20px;
padding-inline: 12px;
gap: 20px;
}
@@ -210,6 +210,7 @@
font-family: inherit;
font-size: var(--kilo-font-size-12);
font-weight: 500;
white-space: nowrap;
cursor: pointer;
transition:
background-color 0.15s,
@@ -660,6 +660,11 @@ export interface ThroughputSettingLoadedMessage {
visible: boolean
}
export interface AutoApprovalReasonSettingLoadedMessage {
type: "autoApprovalReasonSettingLoaded"
visible: boolean
}
export interface WorkStyleLoadedMessage {
type: "workStyleLoaded"
style: WorkStyleState
@@ -1359,6 +1364,7 @@ export type ExtensionMessage =
| NotificationSettingsLoadedMessage
| TimelineSettingLoadedMessage
| ThroughputSettingLoadedMessage
| AutoApprovalReasonSettingLoadedMessage
| WorkStyleLoadedMessage
| WorkStyleAppliedMessage
| WorkStyleApplyFailedMessage
@@ -459,6 +459,10 @@ export interface RequestThroughputSettingMessage {
type: "requestThroughputSetting"
}
export interface RequestAutoApprovalReasonSettingMessage {
type: "requestAutoApprovalReasonSetting"
}
export interface RequestWorkStyleMessage {
type: "requestWorkStyle"
}
@@ -809,6 +813,8 @@ export interface AgentManagerTerminalCreateRequest {
createId: string
placement: TerminalPlacement
worktreeId: string | null
cols?: number
rows?: number
}
// Close a terminal tab
@@ -1444,6 +1450,7 @@ export type WebviewMessage =
| UpdateSettingRequest
| RequestTimelineSettingMessage
| RequestThroughputSettingMessage
| RequestAutoApprovalReasonSettingMessage
| RequestWorkStyleMessage
| SetWorkStyleMessage
| ApplyWorkStyleMessage
+44 -4
View File
@@ -51,9 +51,46 @@ async function copyTreeSitterWasms(outputDir: string) {
console.log(`copied ${languageWasmFiles.length + 1} tree-sitter wasm files to ${targetDir}`)
}
// kilocode_change start
async function isKiloConsoleUpToDate(app: string, out: string) {
const indexHtml = path.join(out, "index.html")
if (!fs.existsSync(indexHtml)) return false
const outStat = await fs.promises.stat(indexHtml)
const inputs = [
path.join(app, "src"),
path.join(app, "package.json"),
path.join(app, "vite.config.ts"),
path.join(app, "index.html"),
path.resolve(dir, "../kilo-web-ui/src"),
path.resolve(dir, "../kilo-indexing/src"),
path.resolve(dir, "../kilo-ui/src"),
path.resolve(dir, "../ui/src"),
path.resolve(dir, "../sdk/js/src"),
path.resolve(dir, "../../bun.lock"),
]
for (const p of inputs) {
if (!fs.existsSync(p)) continue
const st = await fs.promises.stat(p)
if (st.isDirectory()) {
const glob = new Bun.Glob("**/*")
for await (const file of glob.scan({ cwd: p })) {
const fileStat = await fs.promises.stat(path.join(p, file))
if (fileStat.mtimeMs > outStat.mtimeMs) return false
}
} else if (st.mtimeMs > outStat.mtimeMs) {
return false
}
}
return true
}
async function buildKiloConsole() {
const app = path.resolve(dir, "../kilo-console")
const out = path.join(app, "dist")
if (await isKiloConsoleUpToDate(app, out)) {
console.log(`reusing existing Kilo Console build at ${out}`)
return out
}
console.log("building Kilo Console")
const proc = Bun.spawn([process.execPath, "run", "build"], {
cwd: app,
@@ -66,6 +103,7 @@ async function buildKiloConsole() {
if (code !== 0) throw new Error(`Kilo Console build failed with exit code ${code}`)
return out
}
// kilocode_change end
async function copyKiloConsole(input: string, outputDir: string) {
const target = path.join(outputDir, "console")
@@ -217,11 +255,13 @@ const targets = singleFlag
})
: allTargets
await $`rm -rf dist`
// kilocode_change start
const kiloConsoleDist = await buildKiloConsole()
const kiloSandboxWorker = await KiloSandboxWorker.bundle()
const kiloSandboxNetwork = await KiloSandboxNetwork.bundle()
await $`rm -rf dist`
const [kiloConsoleDist, kiloSandboxWorker, kiloSandboxNetwork] = await Promise.all([
buildKiloConsole(),
KiloSandboxWorker.bundle(),
KiloSandboxNetwork.bundle(),
])
// kilocode_change end
const binaries: Record<string, string> = {}
+35 -3
View File
@@ -10,9 +10,41 @@ process.chdir(dir)
const modelsUrl = process.env.KILO_MODELS_URL || "https://models.dev"
// kilocode_change start
const raw = process.env.MODELS_DEV_API_JSON
? await Bun.file(process.env.MODELS_DEV_API_JSON).text()
: await fetch(`${modelsUrl}/api.json`).then((x) => x.text())
const cacheFile = path.resolve(dir, "node_modules/.cache/models-dev-api.json")
const raw = await (async () => {
if (process.env.MODELS_DEV_API_JSON) {
return await Bun.file(process.env.MODELS_DEV_API_JSON).text()
}
const cached = Bun.file(cacheFile)
try {
if (await cached.exists()) {
const st = await cached.stat()
if (st && Date.now() - st.mtimeMs < 6 * 3600 * 1000) {
return await cached.text()
}
}
} catch (err) {
console.warn("[generate] cache read check failed, fetching live", err)
}
try {
const res = await fetch(`${modelsUrl}/api.json`, { signal: AbortSignal.timeout(5000) })
if (!res.ok) throw new Error(`Failed to fetch models.dev snapshot: HTTP ${res.status}`)
const text = await res.text()
try {
await Bun.write(cacheFile, text)
} catch (err) {
console.warn("[generate] cache write failed", err)
}
return text
} catch (err) {
try {
if (await cached.exists()) return await cached.text()
} catch (fallbackErr) {
console.warn("[generate] cache fallback read failed", fallbackErr)
}
throw err
}
})()
export const modelsData = JSON.stringify(parseModelsSnapshot(raw).data)
// kilocode_change end
console.log("Loaded models.dev snapshot")
+83 -4
View File
@@ -1,17 +1,78 @@
// kilocode_change - new file
import path from "path"
import fs from "fs/promises"
import fsSync from "fs"
import crypto from "crypto"
import { createRequire } from "module"
export namespace TestCli {
export const ENV = "KILO_TEST_CLI_PATH"
export async function build(root: string, dir: string) {
async function fingerprint(root: string): Promise<string> {
const hash = crypto.createHash("sha256")
const repo = path.resolve(root, "../..")
const pkgs = path.join(repo, "packages")
const lock = path.join(repo, "bun.lock")
if (fsSync.existsSync(lock)) {
const st = fsSync.statSync(lock)
hash.update("bun.lock").update(String(st.mtimeMs)).update(String(st.size))
}
const ignored = new Set(["kilo-vscode", "kilo-jetbrains", "kilo-docs"])
const entries = fsSync.readdirSync(pkgs, { withFileTypes: true })
for (const ent of entries) {
if (!ent.isDirectory() || ignored.has(ent.name)) continue
const dir = path.join(pkgs, ent.name)
const targets =
ent.name === "sdk"
? [path.join(dir, "js", "src"), path.join(dir, "js", "package.json")]
: [path.join(dir, "src"), path.join(dir, "migration"), path.join(dir, "package.json")]
for (const target of targets) {
if (!fsSync.existsSync(target)) continue
const st = fsSync.statSync(target)
if (st.isDirectory()) {
const glob = new Bun.Glob("**/*.{ts,tsx,sql,json,txt}")
for await (const file of glob.scan({ cwd: target })) {
const p = path.join(target, file)
const fst = fsSync.statSync(p)
hash.update(`${ent.name}:${file}`).update(String(fst.mtimeMs)).update(String(fst.size))
}
} else {
hash.update(`${ent.name}:pkg`).update(String(st.mtimeMs)).update(String(st.size))
}
}
}
return hash.digest("hex")
}
export async function build(root: string, targetDir?: string) {
if (path.resolve(process.cwd()) !== path.resolve(root)) {
throw new Error(`CLI test bundle must be built from ${root}`)
}
const dir = targetDir ?? path.join(root, ".artifacts", "test-cli-cached")
const out = path.join(dir, "src/storage")
const bin = path.join(out, "cli.js")
const hashFile = path.join(dir, ".hash")
if (!targetDir) {
const currentHash = await fingerprint(root)
try {
if (fsSync.existsSync(bin) && fsSync.existsSync(hashFile)) {
const storedHash = fsSync.readFileSync(hashFile, "utf8").trim()
if (storedHash === currentHash) {
return bin
}
}
} catch {}
}
const { createSolidTransformPlugin } = await import("@opentui/solid/bun-plugin")
const entry = "./src/index.ts"
const out = path.join(dir, "src/storage")
const result = await Bun.build({
entrypoints: [entry],
outdir: out,
@@ -44,8 +105,26 @@ export namespace TestCli {
return
}
})()
if (target) await fs.symlink(target, path.join(scope, name.replace("@opentui/", "")), kind)
if (target) {
const link = path.join(scope, name.replace("@opentui/", ""))
try {
await fs.rm(link, { recursive: true, force: true })
await fs.symlink(target, link, kind)
} catch (err) {
console.warn(`[test-cli] failed to link native variant ${name}:`, err)
}
}
}
return path.join(out, "cli.js")
if (!targetDir) {
try {
const currentHash = await fingerprint(root)
await fs.writeFile(hashFile, currentHash)
} catch (err) {
console.warn("[test-cli] failed to save fingerprint hash:", err)
}
}
return bin
}
}
+3 -11
View File
@@ -187,25 +187,17 @@ type Proc = ReturnType<typeof Bun.spawn>
const xmldir = ci ? path.join(os.tmpdir(), `opencode-junit-${process.pid}`) : ""
if (ci) await fs.mkdir(xmldir, { recursive: true })
// kilocode_change start
const supplied = process.env[TestCli.ENV]
const binprefix = path.join(root, ".artifacts", "test-cli-")
const built = supplied
? { binary: supplied, dir: undefined }
: await (async () => {
await fs.mkdir(path.dirname(binprefix), { recursive: true })
const dir = await fs.mkdtemp(binprefix)
return { binary: await TestCli.build(root, dir), dir }
})()
: { binary: await TestCli.build(root), dir: undefined }
async function cleanBinary() {
if (!built.dir) return
const expected = path.dirname(binprefix)
const valid =
path.dirname(built.dir) === expected && path.basename(built.dir).startsWith(path.basename(binprefix))
if (!valid) throw new Error(`Refusing to remove unexpected test CLI directory: ${built.dir}`)
// The generated directory contains the bundle, emitted assets, and copied migrations.
await fs.rm(built.dir, { recursive: true, force: true })
}
// kilocode_change end
const counter = { done: 0 }
const pad = String(files.length).length
@@ -81,11 +81,18 @@ describe("pty HttpApi bridge", () => {
expect(list.status).toBe(200)
expect(await list.json()).toEqual([])
// kilocode_change start - test initial spawn dimensions
const created = await app().request(PtyPaths.create, {
method: "POST",
headers: { ...headers, "content-type": "application/json" },
body: JSON.stringify({ command: "/usr/bin/env", args: ["sh", "-c", "sleep 5"], title: "demo" }),
body: JSON.stringify({
command: "/usr/bin/env",
args: ["sh", "-c", "sleep 5"],
title: "demo",
size: { cols: 50, rows: 20 },
}),
})
// kilocode_change end
expect(created.status).toBe(200)
const info = await created.json()
+8
View File
@@ -45,6 +45,14 @@ export const CreateInput = Schema.Struct({
cwd: optional(Schema.String),
title: optional(Schema.String),
env: optional(Schema.Record(Schema.String, Schema.String)),
// kilocode_change start - spawn with initial terminal dimensions
size: optional(
Schema.Struct({
rows: PositiveInt,
cols: PositiveInt,
}),
),
// kilocode_change end
})
export interface CreateInput extends Schema.Schema.Type<typeof CreateInput> {}
+1 -2
View File
@@ -143,8 +143,7 @@ if (!legacyPatched.includes(sandbox)) {
}
await Bun.write(legacyTypesPath, legacyPatched)
await $`bun prettier --write src/gen`
await $`bun prettier --write src/v2`
await $`bun prettier --write src/gen src/v2`
await $`rm -rf dist tsconfig.tsbuildinfo`
await $`bun tsc`
await $`rm openapi.json`
+14
View File
@@ -3428,6 +3428,10 @@ export class Pty extends HeyApiClient {
env?: {
[key: string]: string
}
size?: {
rows: number
cols: number
}
},
options?: Options<never, ThrowOnError>,
) {
@@ -3443,6 +3447,7 @@ export class Pty extends HeyApiClient {
{ in: "body", key: "cwd" },
{ in: "body", key: "title" },
{ in: "body", key: "env" },
{ in: "body", key: "size" },
],
},
],
@@ -4430,6 +4435,8 @@ export class Session2 extends HeyApiClient {
directory?: string
workspace?: string
messageID?: string
file?: string
full?: "true" | "false"
},
options?: Options<never, ThrowOnError>,
) {
@@ -4442,6 +4449,8 @@ export class Session2 extends HeyApiClient {
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "query", key: "messageID" },
{ in: "query", key: "file" },
{ in: "query", key: "full" },
],
},
],
@@ -10831,6 +10840,10 @@ export class Pty2 extends HeyApiClient {
env?: {
[key: string]: string
}
size?: {
rows: number
cols: number
}
},
options?: Options<never, ThrowOnError>,
) {
@@ -10845,6 +10858,7 @@ export class Pty2 extends HeyApiClient {
{ in: "body", key: "cwd" },
{ in: "body", key: "title" },
{ in: "body", key: "env" },
{ in: "body", key: "size" },
],
},
],
+14 -2
View File
@@ -465,6 +465,8 @@ export type IndexingWarning = {
export type SnapshotFileDiff = {
file?: string
patch?: string
before?: string
after?: string
additions: number
deletions: number
status?: "added" | "deleted" | "modified"
@@ -2888,11 +2890,11 @@ export type WorktreeResetInput = {
export type WorktreeDiffItem = {
file?: string
patch?: string
before: string
after: string
additions: number
deletions: number
status?: "added" | "deleted" | "modified"
before: string
after: string
tracked: boolean
generatedLike: boolean
summarized: boolean
@@ -12326,6 +12328,10 @@ export type PtyCreateData = {
env?: {
[key: string]: string
}
size?: {
rows: number
cols: number
}
}
path?: never
query?: {
@@ -13177,6 +13183,8 @@ export type SessionDiffData = {
directory?: string
workspace?: string
messageID?: string
file?: string
full?: "true" | "false"
}
url: "/session/{sessionID}/diff"
}
@@ -20244,6 +20252,10 @@ export type V2PtyCreateData = {
env?: {
[key: string]: string
}
size?: {
rows: number
cols: number
}
}
path?: never
query?: {
+60 -7
View File
@@ -4539,6 +4539,21 @@
"additionalProperties": {
"type": "string"
}
},
"size": {
"type": "object",
"properties": {
"rows": {
"type": "integer",
"exclusiveMinimum": 0
},
"cols": {
"type": "integer",
"exclusiveMinimum": 0
}
},
"required": ["rows", "cols"],
"additionalProperties": false
}
},
"additionalProperties": false
@@ -6587,6 +6602,23 @@
"pattern": "^msg"
},
"required": false
},
{
"name": "file",
"in": "query",
"schema": {
"type": "string"
},
"required": false
},
{
"name": "full",
"in": "query",
"schema": {
"type": "string",
"enum": ["true", "false"]
},
"required": false
}
],
"responses": {
@@ -24318,6 +24350,21 @@
"additionalProperties": {
"type": "string"
}
},
"size": {
"type": "object",
"properties": {
"rows": {
"type": "integer",
"exclusiveMinimum": 0
},
"cols": {
"type": "integer",
"exclusiveMinimum": 0
}
},
"required": ["rows", "cols"],
"additionalProperties": false
}
},
"additionalProperties": false
@@ -27026,6 +27073,12 @@
"patch": {
"type": "string"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
},
"additions": {
"type": "number"
},
@@ -34498,6 +34551,12 @@
"patch": {
"type": "string"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
},
"additions": {
"type": "number"
},
@@ -34508,12 +34567,6 @@
"type": "string",
"enum": ["added", "deleted", "modified"]
},
"before": {
"type": "string"
},
"after": {
"type": "string"
},
"tracked": {
"type": "boolean"
},
@@ -34527,7 +34580,7 @@
"type": "string"
}
},
"required": ["additions", "deletions", "before", "after", "tracked", "generatedLike", "summarized", "stamp"],
"required": ["before", "after", "additions", "deletions", "tracked", "generatedLike", "summarized", "stamp"],
"additionalProperties": false
},
"SnapshotSummaryFileDiff": {