diff --git a/.github/workflows/auto-docs.yml b/.github/workflows/auto-docs.yml
index 7af1b3748a2..0ea386e281a 100644
--- a/.github/workflows/auto-docs.yml
+++ b/.github/workflows/auto-docs.yml
@@ -10,7 +10,6 @@ jobs:
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.title, 'feat:')
runs-on: ubuntu-latest
-
steps:
- name: Send webhook safely
run: |
diff --git a/bun.lock b/bun.lock
index f4d56ee65ac..b2262ea37fa 100644
--- a/bun.lock
+++ b/bun.lock
@@ -15,12 +15,13 @@
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"@types/mime-types": "3.0.1",
+ "@typescript/native-preview": "catalog:",
"glob": "13.0.5",
"husky": "9.1.7",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
- "turbo": "2.5.6",
+ "turbo": "2.8.13",
},
},
"packages/app": {
@@ -48,7 +49,7 @@
"@thisbeyond/solid-dnd": "0.7.5",
"diff": "catalog:",
"fuzzysort": "catalog:",
- "ghostty-web": "0.4.0",
+ "ghostty-web": "github:anomalyco/ghostty-web#main",
"luxon": "catalog:",
"marked": "catalog:",
"marked-shiki": "catalog:",
@@ -110,7 +111,7 @@
},
"packages/desktop-electron": {
"name": "@opencode-ai/desktop-electron",
- "version": "7.0.46",
+ "version": "7.0.47",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -494,6 +495,7 @@
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.3",
"@solidjs/meta": "catalog:",
+ "@solidjs/router": "catalog:",
"@typescript/native-preview": "catalog:",
"dompurify": "3.3.1",
"fuzzysort": "catalog:",
@@ -2865,7 +2867,7 @@
"get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="],
- "ghostty-web": ["ghostty-web@0.4.0", "", {}, "sha512-0puDBik2qapbD/QQBW9o5ZHfXnZBqZWx/ctBiVtKZ6ZLds4NYb+wZuw1cRLXZk9zYovIQ908z3rvFhexAvc5Hg=="],
+ "ghostty-web": ["ghostty-web@github:anomalyco/ghostty-web#4af877d", {}, "anomalyco-ghostty-web-4af877d", "sha512-fbEK8mtr7ar4ySsF+JUGjhaZrane7dKphanN+SxHt5XXI6yLMAh/Hpf6sNCOyyVa2UlGCd7YpXG/T2v2RUAX+A=="],
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
@@ -3993,19 +3995,19 @@
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
- "turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="],
+ "turbo": ["turbo@2.8.13", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.13", "turbo-darwin-arm64": "2.8.13", "turbo-linux-64": "2.8.13", "turbo-linux-arm64": "2.8.13", "turbo-windows-64": "2.8.13", "turbo-windows-arm64": "2.8.13" }, "bin": { "turbo": "bin/turbo" } }, "sha512-nyM99hwFB9/DHaFyKEqatdayGjsMNYsQ/XBNO6MITc7roncZetKb97MpHxWf3uiU+LB9c9HUlU3Jp2Ixei2k1A=="],
- "turbo-darwin-64": ["turbo-darwin-64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A=="],
+ "turbo-darwin-64": ["turbo-darwin-64@2.8.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-PmOvodQNiOj77+Zwoqku70vwVjKzL34RTNxxoARjp5RU5FOj/CGiC6vcDQhNtFPUOWSAaogHF5qIka9TBhX4XA=="],
- "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA=="],
+ "turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kI+anKcLIM4L8h+NsM7mtAUpElkCOxv5LgiQVQR8BASyDFfc8Efj5kCk3cqxuxOvIqx0sLfCX7atrHQ2kwuNJQ=="],
- "turbo-linux-64": ["turbo-linux-64@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA=="],
+ "turbo-linux-64": ["turbo-linux-64@2.8.13", "", { "os": "linux", "cpu": "x64" }, "sha512-j29KnQhHyzdzgCykBFeBqUPS4Wj7lWMnZ8CHqytlYDap4Jy70l4RNG46pOL9+lGu6DepK2s1rE86zQfo0IOdPw=="],
- "turbo-linux-arm64": ["turbo-linux-arm64@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ=="],
+ "turbo-linux-arm64": ["turbo-linux-arm64@2.8.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-OEl1YocXGZDRDh28doOUn49QwNe82kXljO1HXApjU0LapkDiGpfl3jkAlPKxEkGDSYWc8MH5Ll8S16Rf5tEBYg=="],
- "turbo-windows-64": ["turbo-windows-64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg=="],
+ "turbo-windows-64": ["turbo-windows-64@2.8.13", "", { "os": "win32", "cpu": "x64" }, "sha512-717bVk1+Pn2Jody7OmWludhEirEe0okoj1NpRbSm5kVZz/yNN/jfjbxWC6ilimXMz7xoMT3IDfQFJsFR3PMANA=="],
- "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="],
+ "turbo-windows-arm64": ["turbo-windows-arm64@2.8.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-R819HShLIT0Wj6zWVnIsYvSNtRNj1q9VIyaUz0P24SMcLCbQZIm1sV09F4SDbg+KCCumqD2lcaR2UViQ8SnUJA=="],
"turndown": ["turndown@7.2.0", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A=="],
diff --git a/package.json b/package.json
index e350051098c..1468f8ab10d 100644
--- a/package.json
+++ b/package.json
@@ -69,12 +69,13 @@
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"@types/mime-types": "3.0.1",
+ "@typescript/native-preview": "catalog:",
"glob": "13.0.5",
"husky": "9.1.7",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
- "turbo": "2.5.6"
+ "turbo": "2.8.13"
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
diff --git a/packages/app/e2e/files/file-viewer.spec.ts b/packages/app/e2e/files/file-viewer.spec.ts
index bee67c7d128..49fe1baa138 100644
--- a/packages/app/e2e/files/file-viewer.spec.ts
+++ b/packages/app/e2e/files/file-viewer.spec.ts
@@ -101,3 +101,56 @@ test("cmd+f opens text viewer search while prompt is focused", async ({ page, go
await expect(findInput).toBeVisible()
await expect(findInput).toBeFocused()
})
+
+test("cmd+f opens text viewer search while prompt is not focused", async ({ page, gotoSession }) => {
+ await gotoSession()
+
+ await page.locator(promptSelector).click()
+ await page.keyboard.type("/open")
+
+ const command = page.locator('[data-slash-id="file.open"]').first()
+ await expect(command).toBeVisible()
+ await page.keyboard.press("Enter")
+
+ const dialog = page
+ .getByRole("dialog")
+ .filter({ has: page.getByPlaceholder(/search files/i) })
+ .first()
+ await expect(dialog).toBeVisible()
+
+ const input = dialog.getByRole("textbox").first()
+ await input.fill("package.json")
+
+ const items = dialog.locator('[data-slot="list-item"][data-key^="file:"]')
+ let index = -1
+ await expect
+ .poll(
+ async () => {
+ const keys = await items.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("data-key") ?? ""))
+ index = keys.findIndex((key) => /packages[\\/]+app[\\/]+package\.json$/i.test(key.replace(/^file:/, "")))
+ return index >= 0
+ },
+ { timeout: 30_000 },
+ )
+ .toBe(true)
+
+ const item = items.nth(index)
+ await expect(item).toBeVisible()
+ await item.click()
+
+ await expect(dialog).toHaveCount(0)
+
+ const tab = page.getByRole("tab", { name: "package.json" })
+ await expect(tab).toBeVisible()
+ await tab.click()
+
+ const viewer = page.locator('[data-component="file"][data-mode="text"]').first()
+ await expect(viewer).toBeVisible()
+
+ await viewer.click()
+ await page.keyboard.press(`${modKey}+f`)
+
+ const findInput = page.getByPlaceholder("Find")
+ await expect(findInput).toBeVisible()
+ await expect(findInput).toBeFocused()
+})
diff --git a/packages/app/package.json b/packages/app/package.json
index 24c97f05b11..ee1f04fa987 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -57,7 +57,7 @@
"@thisbeyond/solid-dnd": "0.7.5",
"diff": "catalog:",
"fuzzysort": "catalog:",
- "ghostty-web": "0.4.0",
+ "ghostty-web": "github:anomalyco/ghostty-web#main",
"luxon": "catalog:",
"marked": "catalog:",
"marked-shiki": "catalog:",
diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts
index ed790174674..23e77c93e58 100644
--- a/packages/app/src/components/prompt-input/submit.ts
+++ b/packages/app/src/components/prompt-input/submit.ts
@@ -16,6 +16,7 @@ import { Identifier } from "@/utils/id"
import { Worktree as WorktreeState } from "@/utils/worktree"
import { buildRequestParts } from "./build-request-parts"
import { setCursorPosition } from "./editor-dom"
+import { formatServerError } from "@/utils/server-errors"
type PendingPrompt = {
abort: AbortController
@@ -286,7 +287,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
.catch((err) => {
showToast({
title: language.t("prompt.toast.commandSendFailed.title"),
- description: errorMessage(err),
+ description: formatServerError(err, language.t, language.t("common.requestFailed")),
})
restoreInput()
})
diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx
index 47030aa177e..08ae4d3194e 100644
--- a/packages/app/src/components/session-context-usage.tsx
+++ b/packages/app/src/components/session-context-usage.tsx
@@ -39,7 +39,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
const usd = createMemo(
() =>
- new Intl.NumberFormat(language.locale(), {
+ new Intl.NumberFormat(language.intl(), {
style: "currency",
currency: "USD",
}),
@@ -77,7 +77,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
{(ctx) => (
<>
- {ctx().total.toLocaleString(language.locale())}
+ {ctx().total.toLocaleString(language.intl())}
{language.t("context.usage.tokens")}
diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx
index 04d44b3818e..be5118c2eaf 100644
--- a/packages/app/src/components/session/session-context-tab.tsx
+++ b/packages/app/src/components/session/session-context-tab.tsx
@@ -128,7 +128,7 @@ export function SessionContextTab() {
const usd = createMemo(
() =>
- new Intl.NumberFormat(language.locale(), {
+ new Intl.NumberFormat(language.intl(), {
style: "currency",
currency: "USD",
}),
@@ -136,7 +136,7 @@ export function SessionContextTab() {
const metrics = createMemo(() => getSessionContextMetrics(messages(), sync.data.provider.all))
const ctx = createMemo(() => metrics().context)
- const formatter = createMemo(() => createSessionContextFormatter(language.locale()))
+ const formatter = createMemo(() => createSessionContextFormatter(language.intl()))
const cost = createMemo(() => {
return usd().format(metrics().totalCost)
@@ -200,7 +200,7 @@ export function SessionContextTab() {
const stats = [
{ label: "context.stats.session", value: () => info()?.title ?? params.id ?? "—" },
- { label: "context.stats.messages", value: () => counts().all.toLocaleString(language.locale()) },
+ { label: "context.stats.messages", value: () => counts().all.toLocaleString(language.intl()) },
{ label: "context.stats.provider", value: providerLabel },
{ label: "context.stats.model", value: modelLabel },
{ label: "context.stats.limit", value: () => formatter().number(ctx()?.limit) },
@@ -213,8 +213,8 @@ export function SessionContextTab() {
label: "context.stats.cacheTokens",
value: () => `${formatter().number(ctx()?.cacheRead)} / ${formatter().number(ctx()?.cacheWrite)}`,
},
- { label: "context.stats.userMessages", value: () => counts().user.toLocaleString(language.locale()) },
- { label: "context.stats.assistantMessages", value: () => counts().assistant.toLocaleString(language.locale()) },
+ { label: "context.stats.userMessages", value: () => counts().user.toLocaleString(language.intl()) },
+ { label: "context.stats.assistantMessages", value: () => counts().assistant.toLocaleString(language.intl()) },
{ label: "context.stats.totalCost", value: cost },
{ label: "context.stats.sessionCreated", value: () => formatter().time(info()?.time.created) },
{ label: "context.stats.lastActivity", value: () => formatter().time(ctx()?.message.time.created) },
@@ -307,7 +307,7 @@ export function SessionContextTab() {
{breakdownLabel(segment.key)}
-
{segment.percent.toLocaleString(language.locale())}%
+
{segment.percent.toLocaleString(language.intl())}%
)}
diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx
index b7a544ba9a9..f2ecd51501e 100644
--- a/packages/app/src/components/session/session-new-view.tsx
+++ b/packages/app/src/components/session/session-new-view.tsx
@@ -51,26 +51,26 @@ export function NewSessionView(props: NewSessionViewProps) {
return (
{language.t("command.session.new")}
-
-
-
+
+
+
{getDirectory(projectRoot())}
{getFilename(projectRoot())}
-
-
-
{label(current())}
+
{(project) => (
-
-
-
+
+
+
{language.t("session.new.lastModified")}
{DateTime.fromMillis(project().time.updated ?? project().time.created)
- .setLocale(language.locale())
+ .setLocale(language.intl())
.toRelative()}
diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx
index 21db62950dd..a9839758b72 100644
--- a/packages/app/src/components/settings-providers.tsx
+++ b/packages/app/src/components/settings-providers.tsx
@@ -17,6 +17,7 @@ type ProviderItem = ReturnType
["connected"]>[num
const PROVIDER_NOTES = [
{ match: (id: string) => id === "opencode", key: "dialog.provider.opencode.note" },
+ { match: (id: string) => id === "opencode-go", key: "dialog.provider.opencodeGo.tagline" },
{ match: (id: string) => id === "anthropic", key: "dialog.provider.anthropic.note" },
{ match: (id: string) => id.startsWith("github-copilot"), key: "dialog.provider.copilot.note" },
{ match: (id: string) => id === "openai", key: "dialog.provider.openai.note" },
@@ -181,21 +182,11 @@ export const SettingsProviders: Component = () => {
{item.name}
-
-
- {language.t("dialog.provider.opencode.tagline")}
-
-
{language.t("dialog.provider.tag.recommended")}
- <>
-
- {language.t("dialog.provider.opencodeGo.tagline")}
-
- {language.t("dialog.provider.tag.recommended")}
- >
+ {language.t("dialog.provider.tag.recommended")}
diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx
index 65483df575e..c3fc89d7d50 100644
--- a/packages/app/src/context/global-sync.tsx
+++ b/packages/app/src/context/global-sync.tsx
@@ -228,10 +228,7 @@ function createGlobalSync() {
showToast({
variant: "error",
title: language.t("toast.session.listFailed.title", { project }),
- description: formatServerError(err, {
- unknown: language.t("error.chain.unknown"),
- invalidConfiguration: language.t("error.server.invalidConfiguration"),
- }),
+ description: formatServerError(err, language.t),
})
})
@@ -261,8 +258,7 @@ function createGlobalSync() {
setStore: child[1],
vcsCache: cache,
loadSessions,
- unknownError: language.t("error.chain.unknown"),
- invalidConfigurationError: language.t("error.server.invalidConfiguration"),
+ translate: language.t,
})
})()
@@ -331,8 +327,7 @@ function createGlobalSync() {
url: globalSDK.url,
}),
requestFailedTitle: language.t("common.requestFailed"),
- unknownError: language.t("error.chain.unknown"),
- invalidConfigurationError: language.t("error.server.invalidConfiguration"),
+ translate: language.t,
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore: setBootStore,
})
diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts
index 7d9251d3e69..2a55aab853e 100644
--- a/packages/app/src/context/global-sync/bootstrap.ts
+++ b/packages/app/src/context/global-sync/bootstrap.ts
@@ -36,8 +36,7 @@ export async function bootstrapGlobal(input: {
connectErrorTitle: string
connectErrorDescription: string
requestFailedTitle: string
- unknownError: string
- invalidConfigurationError: string
+ translate: (key: string, vars?: Record) => string
formatMoreCount: (count: number) => string
setGlobalStore: SetStoreFunction
}) {
@@ -91,10 +90,7 @@ export async function bootstrapGlobal(input: {
const results = await Promise.allSettled(tasks)
const errors = results.filter((r): r is PromiseRejectedResult => r.status === "rejected").map((r) => r.reason)
if (errors.length) {
- const message = formatServerError(errors[0], {
- unknown: input.unknownError,
- invalidConfiguration: input.invalidConfigurationError,
- })
+ const message = formatServerError(errors[0], input.translate)
const more = errors.length > 1 ? input.formatMoreCount(errors.length - 1) : ""
showToast({
variant: "error",
@@ -122,8 +118,7 @@ export async function bootstrapDirectory(input: {
setStore: SetStoreFunction
vcsCache: VcsCache
loadSessions: (directory: string) => Promise | void
- unknownError: string
- invalidConfigurationError: string
+ translate: (key: string, vars?: Record) => string
}) {
if (input.store.status !== "complete") input.setStore("status", "loading")
@@ -145,10 +140,7 @@ export async function bootstrapDirectory(input: {
showToast({
variant: "error",
title: `Failed to reload ${project}`,
- description: formatServerError(err, {
- unknown: input.unknownError,
- invalidConfiguration: input.invalidConfigurationError,
- }),
+ description: formatServerError(err, input.translate),
})
input.setStore("status", "partial")
return
diff --git a/packages/app/src/context/language.tsx b/packages/app/src/context/language.tsx
index cae4b835419..2d39799543c 100644
--- a/packages/app/src/context/language.tsx
+++ b/packages/app/src/context/language.tsx
@@ -104,6 +104,26 @@ const LOCALES: readonly Locale[] = [
"tr",
]
+const INTL: Record = {
+ en: "en",
+ zh: "zh-Hans",
+ zht: "zh-Hant",
+ ko: "ko",
+ de: "de",
+ es: "es",
+ fr: "fr",
+ da: "da",
+ ja: "ja",
+ pl: "pl",
+ ru: "ru",
+ ar: "ar",
+ no: "nb-NO",
+ br: "pt-BR",
+ th: "th",
+ bs: "bs",
+ tr: "tr",
+}
+
const LABEL_KEY: Record = {
en: "language.en",
zh: "language.zh",
@@ -217,6 +237,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
)
const locale = createMemo(() => normalizeLocale(store.locale))
+ const intl = createMemo(() => INTL[locale()])
const dict = createMemo(() => DICT[locale()])
@@ -233,6 +254,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
return {
ready,
locale,
+ intl,
locales: LOCALES,
label,
t,
diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx
index ac5da60e862..75d1334a5a5 100644
--- a/packages/app/src/context/local.tsx
+++ b/packages/app/src/context/local.tsx
@@ -35,6 +35,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const agent = (() => {
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden))
+ const models = useModels()
+
const [store, setStore] = createStore<{
current?: string
}>({
@@ -53,11 +55,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
setStore("current", undefined)
return
}
- if (name && available.some((x) => x.name === name)) {
- setStore("current", name)
- return
- }
- setStore("current", available[0].name)
+ const match = name ? available.find((x) => x.name === name) : undefined
+ const value = match ?? available[0]
+ if (!value) return
+ setStore("current", value.name)
+ if (!value.model) return
+ setModel({
+ providerID: value.model.providerID,
+ modelID: value.model.modelID,
+ })
+ if (value.variant)
+ models.variant.set({ providerID: value.model.providerID, modelID: value.model.modelID }, value.variant)
},
move(direction: 1 | -1) {
const available = list()
@@ -71,11 +79,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const value = available[next]
if (!value) return
setStore("current", value.name)
- if (value.model)
- setModel({
- providerID: value.model.providerID,
- modelID: value.model.modelID,
- })
+ if (!value.model) return
+ setModel({
+ providerID: value.model.providerID,
+ modelID: value.model.modelID,
+ })
+ if (value.variant)
+ models.variant.set({ providerID: value.model.providerID, modelID: value.model.modelID }, value.variant)
},
}
})()
diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts
index f939b222814..5ca652a639b 100644
--- a/packages/app/src/i18n/fr.ts
+++ b/packages/app/src/i18n/fr.ts
@@ -551,9 +551,6 @@ export const dict = {
"settings.general.row.theme.description": "Personnaliser le thème d'Kilo.",
"settings.general.row.font.title": "Police",
"settings.general.row.font.description": "Personnaliser la police mono utilisée dans les blocs de code",
- "settings.general.row.reasoningSummaries.title": "Afficher les résumés de raisonnement",
- "settings.general.row.reasoningSummaries.description":
- "Afficher les résumés de raisonnement du modèle dans la chronologie",
"settings.general.row.shellToolPartsExpanded.title": "Développer les parties de l'outil shell",
"settings.general.row.shellToolPartsExpanded.description":
"Afficher les parties de l'outil shell développées par défaut dans la chronologie",
diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts
index 35321260cb0..f07df4a2f20 100644
--- a/packages/app/src/i18n/th.ts
+++ b/packages/app/src/i18n/th.ts
@@ -113,7 +113,6 @@ export const dict = {
"dialog.model.empty": "ไม่พบผลลัพธ์โมเดล",
"dialog.model.manage": "จัดการโมเดล",
"dialog.model.manage.description": "ปรับแต่งโมเดลที่จะปรากฏในตัวเลือกโมเดล",
- "dialog.model.manage.provider.toggle": "สลับโมเดล {{provider}} ทั้งหมด",
"dialog.model.unpaid.freeModels.title": "โมเดลฟรีที่จัดหาให้โดย Kilo",
"dialog.model.unpaid.addMore.title": "เพิ่มโมเดลเพิ่มเติมจากผู้ให้บริการยอดนิยม",
@@ -495,7 +494,6 @@ export const dict = {
"session.review.loadingChanges": "กำลังโหลดการเปลี่ยนแปลง...",
"session.review.empty": "ยังไม่มีการเปลี่ยนแปลงในเซสชันนี้",
"session.review.noChanges": "ไม่มีการเปลี่ยนแปลง",
- "session.review.noVcs": "ไม่พบ git VCS ดังนั้นจะไม่สามารถตรวจจับการเปลี่ยนแปลงของเซสชันได้",
"session.files.selectToOpen": "เลือกไฟล์เพื่อเปิด",
"session.files.all": "ไฟล์ทั้งหมด",
@@ -512,11 +510,6 @@ export const dict = {
"session.todo.collapse": "ย่อ",
"session.todo.expand": "ขยาย",
- "session.modeSwitch.switching": "กำลังสลับไปยังโหมด {{mode}}…",
- "session.modeSwitch.waiting": "รอให้งานปัจจุบันเสร็จสมบูรณ์",
- "session.modeSwitch.notAvailable": "ตัวแทนไม่พร้อมใช้งาน",
- "session.modeSwitch.fallback": 'ไม่พบ "{{requested}}" กำลังใช้ "{{actual}}"',
-
"session.new.worktree.main": "สาขาหลัก",
"session.new.worktree.mainWithBranch": "สาขาหลัก ({{branch}})",
"session.new.worktree.create": "สร้าง worktree ใหม่",
@@ -613,8 +606,6 @@ export const dict = {
"settings.general.row.theme.description": "ปรับแต่งวิธีการที่ Kilo มีธีม",
"settings.general.row.font.title": "ฟอนต์",
"settings.general.row.font.description": "ปรับแต่งฟอนต์โมโนที่ใช้ในบล็อกโค้ด",
- "settings.general.row.reasoningSummaries.title": "แสดงสรุปการให้เหตุผล",
- "settings.general.row.reasoningSummaries.description": "แสดงสรุปการให้เหตุผลของโมเดลในไทม์ไลน์",
"settings.general.row.shellToolPartsExpanded.title": "ขยายส่วนเครื่องมือ shell",
"settings.general.row.shellToolPartsExpanded.description": "แสดงส่วนเครื่องมือ shell แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts
index 9959f501c40..c71e66268b1 100644
--- a/packages/app/src/i18n/zht.ts
+++ b/packages/app/src/i18n/zht.ts
@@ -117,7 +117,6 @@ export const dict = {
"dialog.model.empty": "找不到模型",
"dialog.model.manage": "管理模型",
"dialog.model.manage.description": "自訂模型選擇器中顯示的模型。",
- "dialog.model.manage.provider.toggle": "切換所有 {{provider}} 模型",
"dialog.model.unpaid.freeModels.title": "Kilo 提供的免費模型",
"dialog.model.unpaid.addMore.title": "從熱門提供者新增更多模型",
@@ -493,7 +492,6 @@ export const dict = {
"session.review.loadingChanges": "正在載入變更...",
"session.review.empty": "此工作階段暫無變更",
"session.review.noChanges": "沒有變更",
- "session.review.noVcs": "未偵測到 git VCS,因此無法偵測到工作階段變更",
"session.files.selectToOpen": "選取要開啟的檔案",
"session.files.all": "所有檔案",
"session.files.binaryContent": "二進位檔案(無法顯示內容)",
@@ -508,11 +506,6 @@ export const dict = {
"session.todo.collapse": "折疊",
"session.todo.expand": "展開",
- "session.modeSwitch.switching": "正在切換到 {{mode}} 模式…",
- "session.modeSwitch.waiting": "等待目前任務完成",
- "session.modeSwitch.notAvailable": "Agent 不可用",
- "session.modeSwitch.fallback": '找不到 "{{requested}}",使用 "{{actual}}"',
-
"session.new.worktree.main": "主分支",
"session.new.worktree.mainWithBranch": "主分支 ({{branch}})",
"session.new.worktree.create": "建立新的 worktree",
@@ -608,8 +601,6 @@ export const dict = {
"settings.general.row.theme.description": "自訂 Kilo 的主題。",
"settings.general.row.font.title": "字型",
"settings.general.row.font.description": "自訂程式碼區塊使用的等寬字型",
- "settings.general.row.reasoningSummaries.title": "顯示推理摘要",
- "settings.general.row.reasoningSummaries.description": "在時間軸中顯示模型推理摘要",
"settings.general.row.shellToolPartsExpanded.title": "展開 shell 工具區塊",
"settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊",
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 53e545db496..b9c8a31c9d5 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -44,6 +44,7 @@ import { playSound, soundSrc } from "@/utils/sound"
import { createAim } from "@/utils/aim"
import { setNavigate } from "@/utils/notification-click"
import { Worktree as WorktreeState } from "@/utils/worktree"
+import { setSessionHandoff } from "@/pages/session/handoff"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme"
@@ -67,7 +68,12 @@ import {
sortedRootSessions,
workspaceKey,
} from "./layout/helpers"
-import { collectOpenProjectDeepLinks, deepLinkEvent, drainPendingDeepLinks } from "./layout/deep-links"
+import {
+ collectNewSessionDeepLinks,
+ collectOpenProjectDeepLinks,
+ deepLinkEvent,
+ drainPendingDeepLinks,
+} from "./layout/deep-links"
import { createInlineEditorController } from "./layout/inline-editor"
import {
LocalWorkspace,
@@ -1182,9 +1188,20 @@ export default function Layout(props: ParentProps) {
const handleDeepLinks = (urls: string[]) => {
if (!server.isLocal()) return
+
for (const directory of collectOpenProjectDeepLinks(urls)) {
openProject(directory)
}
+
+ for (const link of collectNewSessionDeepLinks(urls)) {
+ openProject(link.directory, false)
+ const slug = base64Encode(link.directory)
+ if (link.prompt) {
+ setSessionHandoff(slug, { prompt: link.prompt })
+ }
+ const href = link.prompt ? `/${slug}/session?prompt=${encodeURIComponent(link.prompt)}` : `/${slug}/session`
+ navigateWithSidebarReset(href)
+ }
}
onMount(() => {
@@ -1834,7 +1851,7 @@ export default function Layout(props: ParentProps) {
}}
style={{ width: panelProps.mobile ? undefined : `${Math.max(layout.sidebar.width() - 64, 0)}px` }}
>
-
+
{(p) => (
<>
@@ -1843,7 +1860,7 @@ export default function Layout(props: ParentProps) {
renameProject(p(), next)}
+ onSave={(next) => renameProject(p, next)}
class="text-14-medium text-text-strong truncate"
displayClass="text-14-medium text-text-strong truncate"
stopPropagation
@@ -1852,7 +1869,7 @@ export default function Layout(props: ParentProps) {
- {p().worktree.replace(homedir(), "~")}
+ {p.worktree.replace(homedir(), "~")}
@@ -1871,7 +1888,7 @@ export default function Layout(props: ParentProps) {
icon="dot-grid"
variant="ghost"
data-action="project-menu"
- data-project={base64Encode(p().worktree)}
+ data-project={base64Encode(p.worktree)}
class="shrink-0 size-6 rounded-md data-[expanded]:bg-surface-base-active"
classList={{
"opacity-0 group-hover/project:opacity-100 data-[expanded]:opacity-100": !panelProps.mobile,
@@ -1880,24 +1897,24 @@ export default function Layout(props: ParentProps) {
/>
- showEditProjectDialog(p())}>
+ showEditProjectDialog(p)}>
{language.t("common.edit")}
toggleProjectWorkspaces(p())}
+ data-project={base64Encode(p.worktree)}
+ disabled={p.vcs !== "git" && !layout.sidebar.workspaces(p.worktree)()}
+ onSelect={() => toggleProjectWorkspaces(p)}
>
- {layout.sidebar.workspaces(p().worktree)()
+ {layout.sidebar.workspaces(p.worktree)()
? language.t("sidebar.workspaces.disable")
: language.t("sidebar.workspaces.enable")}
@@ -1908,8 +1925,8 @@ export default function Layout(props: ParentProps) {
closeProject(p().worktree)}
+ data-project={base64Encode(p.worktree)}
+ onSelect={() => closeProject(p.worktree)}
>
{language.t("common.close")}
@@ -1934,7 +1951,7 @@ export default function Layout(props: ParentProps) {
size="large"
icon="plus-small"
class="w-full"
- onClick={() => navigateWithSidebarReset(`/${base64Encode(p().worktree)}/session`)}
+ onClick={() => navigateWithSidebarReset(`/${base64Encode(p.worktree)}/session`)}
>
{language.t("command.session.new")}
@@ -1943,7 +1960,7 @@ export default function Layout(props: ParentProps) {
@@ -1958,7 +1975,7 @@ export default function Layout(props: ParentProps) {
keybind={command.keybind("workspace.new")}
placement="top"
>
-
+ }
+ >
+ {content()}
{content()}
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css
index 577452fde03..bb809c79024 100644
--- a/packages/ui/src/components/message-part.css
+++ b/packages/ui/src/components/message-part.css
@@ -609,6 +609,46 @@
justify-content: center;
}
+[data-component="exa-tool-output"] {
+ width: 100%;
+ padding-top: 8px;
+ display: flex;
+ flex-direction: column;
+}
+
+[data-slot="basic-tool-tool-subtitle"].exa-tool-query {
+ display: block;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+[data-slot="exa-tool-links"] {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+[data-slot="exa-tool-link"] {
+ display: block;
+ max-width: 100%;
+ color: var(--text-interactive-base);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ &:hover {
+ color: var(--text-interactive-base);
+ }
+
+ &:visited {
+ color: var(--text-interactive-base);
+ }
+}
+
[data-component="todos"] {
padding: 10px 0 24px 0;
display: flex;
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index 1cd98368d18..528d5577ccf 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -76,6 +76,7 @@ import { TextShimmer } from "./text-shimmer"
import { AnimatedCountList } from "./tool-count-summary"
import { ToolStatusTitle } from "./tool-status-title"
import { animate } from "motion"
+import { useLocation } from "@solidjs/router"
function ShellSubmessage(props: { text: string; animate?: boolean }) {
let widthRef: HTMLSpanElement | undefined
@@ -266,6 +267,18 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo {
title: i18n.t("ui.tool.webfetch"),
subtitle: input.url,
}
+ case "websearch":
+ return {
+ icon: "window-cursor",
+ title: i18n.t("ui.tool.websearch"),
+ subtitle: input.query,
+ }
+ case "codesearch":
+ return {
+ icon: "code",
+ title: i18n.t("ui.tool.codesearch"),
+ subtitle: input.query,
+ }
case "task":
return {
icon: "task",
@@ -326,6 +339,18 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo {
}
}
+function urls(text: string | undefined) {
+ if (!text) return []
+ const seen = new Set()
+ return [...text.matchAll(/https?:\/\/[^\s<>"'`)\]]+/g)]
+ .map((item) => item[0].replace(/[),.;:!?]+$/g, ""))
+ .filter((item) => {
+ if (seen.has(item)) return false
+ seen.add(item)
+ return true
+ })
+}
+
const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"])
const HIDDEN_TOOLS = new Set(["todowrite", "todoread"])
@@ -621,6 +646,32 @@ function contextToolSummary(parts: ToolPart[]) {
return { read, search, list }
}
+function ExaOutput(props: { output?: string }) {
+ const links = createMemo(() => urls(props.output))
+
+ return (
+ 0}>
+
+
+ )
+}
+
export function registerPartComponent(type: string, component: PartComponent) {
PART_MAPPING[type] = component
}
@@ -1563,11 +1614,64 @@ ToolRegistry.register({
},
})
+ToolRegistry.register({
+ name: "websearch",
+ render(props) {
+ const i18n = useI18n()
+ const query = createMemo(() => {
+ const value = props.input.query
+ if (typeof value !== "string") return ""
+ return value
+ })
+
+ return (
+
+
+
+ )
+ },
+})
+
+ToolRegistry.register({
+ name: "codesearch",
+ render(props) {
+ const i18n = useI18n()
+ const query = createMemo(() => {
+ const value = props.input.query
+ if (typeof value !== "string") return ""
+ return value
+ })
+
+ return (
+
+
+
+ )
+ },
+})
+
ToolRegistry.register({
name: "task",
render(props) {
const data = useData()
const i18n = useI18n()
+ const location = useLocation()
const childSessionId = () => props.metadata.sessionId as string | undefined
const title = createMemo(() => i18n.t("ui.tool.agent", { type: props.input.subagent_type || props.tool }))
const description = createMemo(() => {
@@ -1584,8 +1688,7 @@ ToolRegistry.register({
const direct = data.sessionHref?.(sessionId)
if (direct) return direct
- if (typeof window === "undefined") return
- const path = window.location.pathname
+ const path = location.pathname
const idx = path.indexOf("/session")
if (idx === -1) return
return `${path.slice(0, idx)}/session/${sessionId}`
diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx
index 6d288e21474..e1dc5012ed5 100644
--- a/packages/ui/src/components/session-review.tsx
+++ b/packages/ui/src/components/session-review.tsx
@@ -355,8 +355,6 @@ export const SessionReview = (props: SessionReviewProps) => {
if (typeof window === "undefined") return
const onKeyDown = (event: KeyboardEvent) => {
- if (event.defaultPrevented) return
-
const mod = event.metaKey || event.ctrlKey
if (!mod) return
diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css
index 15d7b503525..cf1e9811594 100644
--- a/packages/ui/src/components/session-turn.css
+++ b/packages/ui/src/components/session-turn.css
@@ -171,10 +171,8 @@
[data-slot="session-turn-diff-path"] {
display: flex;
+ flex-grow: 1;
min-width: 0;
- align-items: baseline;
- overflow: hidden;
- white-space: nowrap;
font-family: var(--font-family-sans);
font-size: var(--font-size-small);
@@ -182,24 +180,16 @@
}
[data-slot="session-turn-diff-directory"] {
- flex: 1 1 auto;
- color: var(--text-weak);
- min-width: 0;
+ color: var(--text-base);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
direction: rtl;
- unicode-bidi: plaintext;
text-align: left;
}
[data-slot="session-turn-diff-filename"] {
flex-shrink: 0;
- max-width: 100%;
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
color: var(--text-strong);
font-weight: var(--font-weight-medium);
}
diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css
index 3a0c2dea206..fc99804d8de 100644
--- a/packages/ui/src/components/tabs.css
+++ b/packages/ui/src/components/tabs.css
@@ -241,26 +241,26 @@
[data-slot="tabs-trigger"] {
.tab-fileicon-color,
.tab-fileicon-mono {
- transition: opacity 120ms ease;
+ pointer-events: none;
}
.tab-fileicon-color {
- opacity: 0;
+ display: none;
}
.tab-fileicon-mono {
- opacity: 1;
+ display: block;
color: currentColor;
}
&[data-selected],
&:hover {
.tab-fileicon-color {
- opacity: 1;
+ display: block;
}
.tab-fileicon-mono {
- opacity: 0;
+ display: none;
}
}
}
diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx
index 055e504654b..63105d00fc4 100644
--- a/packages/ui/src/components/tooltip.tsx
+++ b/packages/ui/src/components/tooltip.tsx
@@ -47,7 +47,7 @@ export function Tooltip(props: TooltipProps) {
{local.children}
-
+
{local.children}
diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx
index b5970c560fb..3cf17443742 100644
--- a/packages/ui/src/context/marked.tsx
+++ b/packages/ui/src/context/marked.tsx
@@ -1,6 +1,6 @@
import { marked } from "marked"
import markedKatex from "marked-katex-extension"
-
+import markedShiki from "marked-shiki"
import katex from "katex"
import { bundledLanguages, type BundledLanguage } from "shiki"
import { parseFilePath } from "../file-path" // kilocode_change
@@ -534,9 +534,7 @@ export async function deferredHighlight(
onComplete?: () => void,
signal?: { aborted: boolean },
): Promise {
- const blocks = Array.from(
- container.querySelectorAll("pre > code[data-lang]:not([data-highlighted])"),
- )
+ const blocks = Array.from(container.querySelectorAll("pre > code[data-lang]:not([data-highlighted])"))
if (blocks.length === 0) {
onComplete?.()
return
@@ -591,10 +589,13 @@ export async function deferredHighlight(
resolve()
}
if (!highlighter.getLoadedLanguages().includes(language)) {
- highlighter.loadLanguage(language as BundledLanguage).then(highlight).catch((err) => {
- console.warn("Failed to load language for highlighting", language, err)
- resolve()
- })
+ highlighter
+ .loadLanguage(language as BundledLanguage)
+ .then(highlight)
+ .catch((err) => {
+ console.warn("Failed to load language for highlighting", language, err)
+ resolve()
+ })
return
}
highlight()
@@ -659,6 +660,22 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
throwOnError: false,
nonStandard: true,
}),
+ markedShiki({
+ async highlight(code, lang) {
+ const highlighter = await getSharedHighlighter({ themes: ["Kilo"], langs: [] })
+ if (!(lang in bundledLanguages)) {
+ lang = "text"
+ }
+ if (!highlighter.getLoadedLanguages().includes(lang)) {
+ await highlighter.loadLanguage(lang as BundledLanguage)
+ }
+ return highlighter.codeToHtml(code, {
+ lang: lang || "text",
+ theme: "Kilo",
+ tabindex: false,
+ })
+ },
+ }),
)
// kilocode_change end
diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts
index 380de01f7c7..e86761238fa 100644
--- a/packages/ui/src/i18n/ar.ts
+++ b/packages/ui/src/i18n/ar.ts
@@ -94,6 +94,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "جلب الويب",
+ "ui.tool.websearch": "بحث الويب",
+ "ui.tool.codesearch": "بحث الكود",
"ui.tool.shell": "Shell",
"ui.tool.patch": "تصحيح",
"ui.tool.todos": "المهام",
diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts
index 033c6a9b1a0..1ab4fe0daaf 100644
--- a/packages/ui/src/i18n/br.ts
+++ b/packages/ui/src/i18n/br.ts
@@ -94,6 +94,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Buscar Web",
+ "ui.tool.websearch": "Pesquisa na Web",
+ "ui.tool.codesearch": "Pesquisa de Código",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Tarefas",
diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts
index 8f542cae08e..cfc08c7f08c 100644
--- a/packages/ui/src/i18n/bs.ts
+++ b/packages/ui/src/i18n/bs.ts
@@ -98,6 +98,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Web preuzimanje",
+ "ui.tool.websearch": "Pretraga weba",
+ "ui.tool.codesearch": "Pretraga koda",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Lista zadataka",
diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts
index 233beea1097..232eb43ccd1 100644
--- a/packages/ui/src/i18n/da.ts
+++ b/packages/ui/src/i18n/da.ts
@@ -93,6 +93,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webhentning",
+ "ui.tool.websearch": "Websøgning",
+ "ui.tool.codesearch": "Kodesøgning",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Opgaver",
diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts
index f607d55e24d..6d4859800e3 100644
--- a/packages/ui/src/i18n/de.ts
+++ b/packages/ui/src/i18n/de.ts
@@ -99,6 +99,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webabruf",
+ "ui.tool.websearch": "Websuche",
+ "ui.tool.codesearch": "Codesuche",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Aufgaben",
diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts
index 0fd16713ef2..55458918584 100644
--- a/packages/ui/src/i18n/en.ts
+++ b/packages/ui/src/i18n/en.ts
@@ -95,6 +95,8 @@ export const dict: Record = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "Web Search",
+ "ui.tool.codesearch": "Code Search",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "To-dos",
diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts
index 95f886e1631..a8f943fc114 100644
--- a/packages/ui/src/i18n/es.ts
+++ b/packages/ui/src/i18n/es.ts
@@ -94,6 +94,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "Búsqueda web",
+ "ui.tool.codesearch": "Búsqueda de código",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Parche",
"ui.tool.todos": "Tareas",
diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts
index d42ad546668..4d1934234ab 100644
--- a/packages/ui/src/i18n/fr.ts
+++ b/packages/ui/src/i18n/fr.ts
@@ -94,6 +94,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "Recherche Web",
+ "ui.tool.codesearch": "Recherche de code",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Tâches",
diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts
index 1771f56cc1d..67093969c68 100644
--- a/packages/ui/src/i18n/ja.ts
+++ b/packages/ui/src/i18n/ja.ts
@@ -93,6 +93,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "Web検索",
+ "ui.tool.codesearch": "コード検索",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Todo",
diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts
index aab6e31ab83..0d256bbe58b 100644
--- a/packages/ui/src/i18n/ko.ts
+++ b/packages/ui/src/i18n/ko.ts
@@ -94,6 +94,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "웹 가져오기",
+ "ui.tool.websearch": "웹 검색",
+ "ui.tool.codesearch": "코드 검색",
"ui.tool.shell": "셸",
"ui.tool.patch": "패치",
"ui.tool.todos": "할 일",
diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts
index 7ff274e63d2..03a396bed39 100644
--- a/packages/ui/src/i18n/no.ts
+++ b/packages/ui/src/i18n/no.ts
@@ -97,6 +97,8 @@ export const dict: Record = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webhenting",
+ "ui.tool.websearch": "Nettsøk",
+ "ui.tool.codesearch": "Kodesøk",
"ui.tool.shell": "Shell",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Gjøremål",
diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts
index 8c16b8b1c55..1bae8a0714a 100644
--- a/packages/ui/src/i18n/pl.ts
+++ b/packages/ui/src/i18n/pl.ts
@@ -93,6 +93,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Pobieranie sieciowe",
+ "ui.tool.websearch": "Wyszukiwanie w sieci",
+ "ui.tool.codesearch": "Wyszukiwanie kodu",
"ui.tool.shell": "Terminal",
"ui.tool.patch": "Patch",
"ui.tool.todos": "Zadania",
diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts
index 7c09204e5cd..44bf393b59a 100644
--- a/packages/ui/src/i18n/ru.ts
+++ b/packages/ui/src/i18n/ru.ts
@@ -93,6 +93,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "Веб-поиск",
+ "ui.tool.codesearch": "Поиск кода",
"ui.tool.shell": "Оболочка",
"ui.tool.patch": "Патч",
"ui.tool.todos": "Задачи",
diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts
index baad59cfd2e..e3552f95638 100644
--- a/packages/ui/src/i18n/th.ts
+++ b/packages/ui/src/i18n/th.ts
@@ -95,6 +95,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "ดึงจากเว็บ",
+ "ui.tool.websearch": "ค้นหาเว็บ",
+ "ui.tool.codesearch": "ค้นหาโค้ด",
"ui.tool.shell": "เชลล์",
"ui.tool.patch": "แพตช์",
"ui.tool.todos": "รายการงาน",
diff --git a/packages/ui/src/i18n/tr.ts b/packages/ui/src/i18n/tr.ts
index 9f13d00e955..8e536c51768 100644
--- a/packages/ui/src/i18n/tr.ts
+++ b/packages/ui/src/i18n/tr.ts
@@ -100,6 +100,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Web getir",
+ "ui.tool.websearch": "Web Araması",
+ "ui.tool.codesearch": "Kod Araması",
"ui.tool.shell": "Kabuk",
"ui.tool.patch": "Yama",
"ui.tool.todos": "Görevler",
diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts
index 5360a086049..e603edd520d 100644
--- a/packages/ui/src/i18n/zh.ts
+++ b/packages/ui/src/i18n/zh.ts
@@ -98,6 +98,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "网络搜索",
+ "ui.tool.codesearch": "代码搜索",
"ui.tool.shell": "Shell",
"ui.tool.patch": "补丁",
"ui.tool.todos": "待办",
diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts
index 8f163e4ca47..1b186450e9d 100644
--- a/packages/ui/src/i18n/zht.ts
+++ b/packages/ui/src/i18n/zht.ts
@@ -98,6 +98,8 @@ export const dict = {
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
"ui.tool.webfetch": "Webfetch",
+ "ui.tool.websearch": "網頁搜尋",
+ "ui.tool.codesearch": "程式碼搜尋",
"ui.tool.shell": "Shell",
"ui.tool.patch": "修補",
"ui.tool.todos": "待辦",
diff --git a/script/upstream/utils/config.ts b/script/upstream/utils/config.ts
index 0d14818a669..255d63dad7e 100644
--- a/script/upstream/utils/config.ts
+++ b/script/upstream/utils/config.ts
@@ -74,7 +74,6 @@ export const defaultConfig: MergeConfig = {
"SECURITY.md",
"AGENTS.md",
// GitHub workflows - MANUAL REVIEW (can break CI/CD)
- ".github/workflows/publish-stable.yml",
".github/workflows/publish.yml",
".github/workflows/close-stale-prs.yml",
".github/pull_request_template.md",
diff --git a/turbo.json b/turbo.json
index 0cc3904a8b2..ba0d254ef83 100644
--- a/turbo.json
+++ b/turbo.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://turborepo.com/schema.json",
+ "$schema": "https://v2-8-13.turborepo.dev/schema.json",
"globalEnv": ["CI", "KILO_DISABLE_SHARE"],
"globalPassThroughEnv": ["CI", "KILO_DISABLE_SHARE"],
"tasks": {