diff --git a/.changeset/large-worktree-diffs.md b/.changeset/large-worktree-diffs.md
new file mode 100644
index 00000000000..72bdbbcdde1
--- /dev/null
+++ b/.changeset/large-worktree-diffs.md
@@ -0,0 +1,5 @@
+---
+"@kilocode/cli": patch
+---
+
+Reduce Agent Manager memory usage when viewing large worktree diffs.
diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx
index 41388c8622a..0bd5c666168 100644
--- a/packages/kilo-ui/src/components/message-part.tsx
+++ b/packages/kilo-ui/src/components/message-part.tsx
@@ -1005,7 +1005,7 @@ function ToolFileAccordion(props: { path: string; actions?: JSX.Element; childre
- {`\u202A${getDirectory(props.path)}\u202C`}
+ {`\u2066${getDirectory(props.path)}\u2069`}
{getFilename(props.path)}
@@ -1517,7 +1517,7 @@ function ToolMetaLine(props: {
>
{props.filename}
- {props.path}
+ {`\u2066${props.path}\u2069`}
{(changes) => }
@@ -2196,7 +2196,7 @@ ToolRegistry.register({
- {`\u202A${getDirectory(file.relativePath)}\u202C`}
+ {`\u2066${getDirectory(file.relativePath)}\u2069`}
()
+ for (const sec of state.getSections()) {
+ if (!sec.collapsed) continue
+ for (const id of state.getWorktreesInSection(sec.id)) skipped.add(id)
+ }
+ this.statsPoller.syncSkips(skipped)
+ }
+
private pushState(): void {
const state = this.state
if (!state) return
@@ -1272,6 +1285,9 @@ export class AgentManagerProvider implements Disposable {
...run,
})
+ // Sync skip set before enabling the poller so the first poll cycle
+ // already excludes worktrees in collapsed sections.
+ this.syncPollerSkips()
this.statsPoller.setEnabled(worktrees.length > 0 || this.panel !== undefined)
this.prBridge.poller.setEnabled(worktrees.length > 0)
}
diff --git a/packages/kilo-vscode/src/agent-manager/GitStatsPoller.ts b/packages/kilo-vscode/src/agent-manager/GitStatsPoller.ts
index 8b283946f71..b815658168d 100644
--- a/packages/kilo-vscode/src/agent-manager/GitStatsPoller.ts
+++ b/packages/kilo-vscode/src/agent-manager/GitStatsPoller.ts
@@ -84,12 +84,14 @@ export class GitStatsPoller {
}
}
- skipWorktree(id: string): void {
- this.skipWorktreeIds.add(id)
+ /** Replace the entire skip set with the given IDs. */
+ syncSkips(ids: Set): void {
+ this.skipWorktreeIds = ids
}
- unskipWorktree(id: string): void {
- this.skipWorktreeIds.delete(id)
+ /** Pre-emptively exclude a single worktree (e.g. before deletion). */
+ skipWorktree(id: string): void {
+ this.skipWorktreeIds.add(id)
}
setEnabled(enabled: boolean): void {
diff --git a/packages/kilo-vscode/src/agent-manager/WorktreeStateManager.ts b/packages/kilo-vscode/src/agent-manager/WorktreeStateManager.ts
index eacf044ae56..e31affefe71 100644
--- a/packages/kilo-vscode/src/agent-manager/WorktreeStateManager.ts
+++ b/packages/kilo-vscode/src/agent-manager/WorktreeStateManager.ts
@@ -352,6 +352,15 @@ export class WorktreeStateManager {
return this.sections.get(id)
}
+ /** Return IDs of worktrees assigned to the given section. */
+ getWorktreesInSection(id: string): string[] {
+ const result: string[] = []
+ for (const wt of this.worktrees.values()) {
+ if (wt.sectionId === id) result.push(wt.id)
+ }
+ return result
+ }
+
addSection(name: string, color: string | null, worktreeIds?: string[]): Section {
this.setNormalizedWorktreeOrder(this.worktreeOrder)
const id = generateId("sec")
diff --git a/packages/kilo-vscode/src/kilo-provider-utils.ts b/packages/kilo-vscode/src/kilo-provider-utils.ts
index 223f1b86d1e..2bc1b2fe085 100644
--- a/packages/kilo-vscode/src/kilo-provider-utils.ts
+++ b/packages/kilo-vscode/src/kilo-provider-utils.ts
@@ -195,10 +195,12 @@ export async function loadSessions(ctx: SessionRefreshContext): Promise()
const extra = await Promise.all(
[...worktreeDirs].map((dir) =>
list(dir).catch((err: unknown) => {
console.error(`[Kilo] Failed to list sessions for ${dir}:`, err)
+ failed.add(dir)
return [] as Session[]
}),
),
@@ -212,9 +214,19 @@ export async function loadSessions(ctx: SessionRefreshContext): Promise sessionToWebview(s)),
+ ...(preserve.length ? { preserveSessionIds: preserve } : {}),
})
return projectID
diff --git a/packages/kilo-vscode/tests/unit/kilo-provider-session-refresh.test.ts b/packages/kilo-vscode/tests/unit/kilo-provider-session-refresh.test.ts
index 7747f980adb..2dd31cf7b9b 100644
--- a/packages/kilo-vscode/tests/unit/kilo-provider-session-refresh.test.ts
+++ b/packages/kilo-vscode/tests/unit/kilo-provider-session-refresh.test.ts
@@ -157,6 +157,86 @@ describe("KiloProvider pending session refresh", () => {
expect((sent[0] as { sessions: { id: string }[] }).sessions.map((s) => s.id)).toEqual(["ses_worktree"])
})
+ it("preserves session ids when worktree directory listing fails", async () => {
+ const sent: unknown[] = []
+ const ctx = createContext({
+ connectionState: "connected",
+ sessionDirectories: new Map([
+ ["ses_wt1", "/worktree1"],
+ ["ses_wt2", "/worktree2"],
+ ]),
+ listSessions: async (dir) => {
+ if (dir === "/repo") {
+ return [
+ {
+ id: "ses_root",
+ projectID: "project",
+ title: "root",
+ directory: "/repo",
+ time: { created: 1, updated: 1 },
+ },
+ ] as never
+ }
+ if (dir === "/worktree1") throw new Error("backend not ready")
+ return [
+ {
+ id: "ses_wt2",
+ projectID: "project",
+ title: "wt2",
+ directory: "/worktree2",
+ time: { created: 2, updated: 2 },
+ },
+ ] as never
+ },
+ postMessage: (msg) => sent.push(msg),
+ })
+
+ await loadSessions(ctx)
+
+ expect(sent).toHaveLength(1)
+ const msg = sent[0] as { sessions: { id: string }[]; preserveSessionIds?: string[] }
+ expect(msg.sessions.map((s) => s.id)).toEqual(["ses_root", "ses_wt2"])
+ expect(msg.preserveSessionIds).toEqual(["ses_wt1"])
+ })
+
+ it("omits preserveSessionIds when all directories succeed", async () => {
+ const sent: unknown[] = []
+ const ctx = createContext({
+ connectionState: "connected",
+ sessionDirectories: new Map([["ses_wt", "/worktree"]]),
+ listSessions: async (dir) => {
+ if (dir === "/repo") {
+ return [
+ {
+ id: "ses_root",
+ projectID: "project",
+ title: "root",
+ directory: "/repo",
+ time: { created: 1, updated: 1 },
+ },
+ ] as never
+ }
+ return [
+ {
+ id: "ses_wt",
+ projectID: "project",
+ title: "wt",
+ directory: "/worktree",
+ time: { created: 2, updated: 2 },
+ },
+ ] as never
+ },
+ postMessage: (msg) => sent.push(msg),
+ })
+
+ await loadSessions(ctx)
+
+ expect(sent).toHaveLength(1)
+ const msg = sent[0] as { sessions: { id: string }[]; preserveSessionIds?: string[] }
+ expect(msg.sessions.map((s) => s.id)).toEqual(["ses_root", "ses_wt"])
+ expect(msg.preserveSessionIds).toBeUndefined()
+ })
+
it("flushes deferred refresh via flushPendingSessionRefresh", async () => {
const { calls, fn } = createListSessions()
const ctx = createContext()
diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-multiple-tabs-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-multiple-tabs-chromium-linux.png
new file mode 100644
index 00000000000..bc4bc545585
--- /dev/null
+++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-multiple-tabs-chromium-linux.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee8d8e04b09b58973a1ff66d5a3d8648ba590f88312cf1f37412c39ccdac0480
+size 3973
diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-single-tab-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-single-tab-chromium-linux.png
new file mode 100644
index 00000000000..999c6b2f9e9
--- /dev/null
+++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-single-tab-chromium-linux.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f0e9ea1be8fbb44f05bed7960d1ba13f0d4c5e904e4025e4ab4743c4991e93c8
+size 4696
diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-with-review-tab-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-with-review-tab-chromium-linux.png
new file mode 100644
index 00000000000..0760abd4b86
--- /dev/null
+++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/agentmanager/tab-bar-with-review-tab-chromium-linux.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:66a7ecedac685e17c442bb036d7392320e0642ef43da77ed8dc23be29bc7988e
+size 2935
diff --git a/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx b/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx
index 9588831f47a..31ac21b70e0 100644
--- a/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx
+++ b/packages/kilo-vscode/webview-ui/agent-manager/DiffPanel.tsx
@@ -431,7 +431,7 @@ export const DiffPanel: Component = (props) => {
- {getDirectory(diff.file)}
+ {`\u2066${getDirectory(diff.file)}\u2069`}
{getFilename(diff.file)}
0}>
diff --git a/packages/kilo-vscode/webview-ui/agent-manager/FullScreenDiffView.tsx b/packages/kilo-vscode/webview-ui/agent-manager/FullScreenDiffView.tsx
index d43797d2ef9..fa8a5389c24 100644
--- a/packages/kilo-vscode/webview-ui/agent-manager/FullScreenDiffView.tsx
+++ b/packages/kilo-vscode/webview-ui/agent-manager/FullScreenDiffView.tsx
@@ -507,7 +507,7 @@ export const FullScreenDiffView: Component = (props) =>
- {`\u202A${getDirectory(diff.file)}\u202C`}
+ {`\u2066${getDirectory(diff.file)}\u2069`}
{getFilename(diff.file)}
0}>
diff --git a/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css b/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
index a13c96c5edc..6f3b8f791ce 100644
--- a/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
+++ b/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
@@ -1143,6 +1143,11 @@ button.am-section-toggle:hover .am-section-label {
touch-action: none;
}
+.am-tab-sortable > [data-component="tooltip-trigger"],
+.am-tab-sortable > [data-slot="context-menu-trigger"] > [data-component="tooltip-trigger"] {
+ height: 100%;
+}
+
.am-tab-dragging {
opacity: 0.25;
}
diff --git a/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx b/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx
index 50fd704c536..96841f7b7f6 100644
--- a/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx
+++ b/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx
@@ -85,7 +85,7 @@ const DiffVirtualContent: Component = () => {
- {`\u202A${directory()}/\u202C`}
+ {`\u2066${directory()}/\u2069`}
{filename()}
+{d().additions}
diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PermissionDiff.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PermissionDiff.tsx
index 110fe1b6fb0..1e2ce56c5cd 100644
--- a/packages/kilo-vscode/webview-ui/src/components/chat/PermissionDiff.tsx
+++ b/packages/kilo-vscode/webview-ui/src/components/chat/PermissionDiff.tsx
@@ -64,7 +64,7 @@ export const PermissionDiff: Component = (props) => {
- {directory() && {directory()}/}
+ {directory() && {`\u2066${directory()}/\u2069`}}
{filename()}
diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx
index ce36fe84673..49fdda93a4a 100644
--- a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx
+++ b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx
@@ -246,7 +246,7 @@ export const VscodeSessionTurn: Component = (props) => {
- {`\u202A${getDirectory(diff.file)}\u202C`}
+ {`\u2066${getDirectory(diff.file)}\u2069`}
{getFilename(diff.file)}
diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx
index c05364d5ac2..ea21f0730a4 100644
--- a/packages/kilo-vscode/webview-ui/src/context/session.tsx
+++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx
@@ -703,7 +703,7 @@ export const SessionProvider: ParentComponent = (props) => {
break
case "sessionsLoaded":
- handleSessionsLoaded(message.sessions)
+ handleSessionsLoaded(message.sessions, message.preserveSessionIds)
break
case "sessionUpdated":
@@ -1196,16 +1196,20 @@ export const SessionProvider: ParentComponent = (props) => {
setStore("todos", sessionID, items)
}
- function handleSessionsLoaded(loaded: SessionInfo[]) {
+ function handleSessionsLoaded(loaded: SessionInfo[], preserve?: string[]) {
+ const kept = preserve?.length ? new Set(preserve) : undefined
batch(() => {
// Reconcile: remove sessions not in the loaded list to prevent stale
// entries from other projects accumulating in the store.
+ // Sessions whose worktree directories failed to list are preserved —
+ // their absence is transient, not a real deletion.
const ids = new Set(loaded.map((s) => s.id))
setStore(
"sessions",
produce((sessions) => {
for (const id of Object.keys(sessions)) {
if (id.startsWith("cloud:")) continue
+ if (kept?.has(id)) continue
if (!ids.has(id)) delete sessions[id]
}
}),
diff --git a/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
index 4134b90beeb..ce7d852dc8a 100644
--- a/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
+++ b/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
@@ -1,7 +1,7 @@
/** @jsxImportSource solid-js */
/**
* Stories for Agent Manager components:
- * FileTree, DiffPanel, FullScreenDiffView, WorktreeItem
+ * FileTree, DiffPanel, FullScreenDiffView, WorktreeItem, TabBar
*/
import type { Meta, StoryObj } from "storybook-solidjs-vite"
@@ -10,6 +10,10 @@ import { FileTree } from "../../agent-manager/FileTree"
import { DiffPanel } from "../../agent-manager/DiffPanel"
import { FullScreenDiffView } from "../../agent-manager/FullScreenDiffView"
import { WorktreeItem } from "../../agent-manager/WorktreeItem"
+import { IconButton } from "@kilocode/kilo-ui/icon-button"
+import { Icon } from "@kilocode/kilo-ui/icon"
+import { TooltipKeybind } from "@kilocode/kilo-ui/tooltip"
+import { ContextMenu } from "@kilocode/kilo-ui/context-menu"
import type { WorktreeFileDiff, WorktreeState, WorktreeGitStats, PRStatus } from "../types/messages"
import "../../agent-manager/agent-manager.css"
import "../../agent-manager/agent-manager-review.css"
@@ -444,3 +448,127 @@ export const WorktreeItemGrouped: Story = {
)
},
}
+
+// ---------------------------------------------------------------------------
+// TabBar — renders tab bar structure matching SortableTab / SortableReviewTab
+// DOM to verify the tooltip-trigger height chain is correct.
+// ---------------------------------------------------------------------------
+
+/**
+ * Mock tab matching the real SortableTab DOM:
+ * .am-tab-sortable > [context-menu-trigger] > [tooltip-trigger] > .am-tab
+ */
+const MockTab = (props: { title: string; active?: boolean }) => (
+
+
+
+
+
+ {props.title}
+
+
+
+
+
+
+
+
+)
+
+/** Mock review tab matching SortableReviewTab DOM (no ContextMenu wrapper). */
+const MockReviewTab = (props: { active?: boolean }) => (
+
+
+
+
+ Review
+
+
+
+
+
+
+)
+
+export const TabBarMultipleTabs: Story = {
+ name: "TabBar — multiple tabs with active",
+ render: () => (
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+}
+
+export const TabBarWithReviewTab: Story = {
+ name: "TabBar — with review tab",
+ render: () => (
+
+
+
+ ),
+}
+
+export const TabBarSingleTab: Story = {
+ name: "TabBar — single active tab",
+ render: () => (
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts
index 83e4e18fcf5..32071d057fc 100644
--- a/packages/kilo-vscode/webview-ui/src/types/messages.ts
+++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts
@@ -599,6 +599,7 @@ export interface MessageCreatedMessage {
export interface SessionsLoadedMessage {
type: "sessionsLoaded"
sessions: SessionInfo[]
+ preserveSessionIds?: string[]
}
export interface CloudSessionsLoadedMessage {
diff --git a/packages/opencode/src/bus/global.ts b/packages/opencode/src/bus/global.ts
index 43386dd6b20..dc23020d27c 100644
--- a/packages/opencode/src/bus/global.ts
+++ b/packages/opencode/src/bus/global.ts
@@ -8,3 +8,4 @@ export const GlobalBus = new EventEmitter<{
},
]
}>()
+GlobalBus.setMaxListeners(50) // kilocode_change — surface warning if SSE listeners accumulate
diff --git a/packages/opencode/src/kilocode/review/worktree-diff.ts b/packages/opencode/src/kilocode/review/worktree-diff.ts
index 3bccf81c79c..2d85677c9a9 100644
--- a/packages/opencode/src/kilocode/review/worktree-diff.ts
+++ b/packages/opencode/src/kilocode/review/worktree-diff.ts
@@ -1,6 +1,5 @@
// kilocode_change - new file
import { $ } from "bun"
-import { createTwoFilesPatch } from "diff"
import fs from "node:fs/promises"
import path from "node:path"
import z from "zod"
@@ -245,7 +244,7 @@ export namespace WorktreeDiff {
const additions = meta.status === "added" && meta.additions === 0 && !meta.tracked ? lines(after) : meta.additions
return {
file: meta.file,
- patch: createTwoFilesPatch(meta.file, meta.file, before, after),
+ patch: "",
before,
after,
additions,
diff --git a/packages/opencode/src/server/routes/event.ts b/packages/opencode/src/server/routes/event.ts
index 989b857710d..565486d3c3a 100644
--- a/packages/opencode/src/server/routes/event.ts
+++ b/packages/opencode/src/server/routes/event.ts
@@ -70,14 +70,27 @@ export const EventRoutes = () =>
stream.onAbort(stop)
+ // kilocode_change start
+ // On Windows, stream.onAbort() may never fire after a client disconnects
+ // (delayed TCP RST detection via IOCP). Without this try/catch, the
+ // GlobalBus listener, heartbeat interval, and AsyncQueue stay alive
+ // indefinitely for each dead connection — leaking memory on every
+ // SSE reconnect. Catching write errors lets us clean up eagerly.
try {
for await (const data of q) {
if (data === null) return
- await stream.writeSSE({ data })
+ try {
+ await stream.writeSSE({ data })
+ } catch {
+ log.info("event write failed, cleaning up dead stream")
+ stop()
+ return
+ }
}
} finally {
stop()
}
+ // kilocode_change end
})
},
)
diff --git a/packages/opencode/src/server/routes/global.ts b/packages/opencode/src/server/routes/global.ts
index 733a0ef6b27..ddfa4c9acad 100644
--- a/packages/opencode/src/server/routes/global.ts
+++ b/packages/opencode/src/server/routes/global.ts
@@ -56,14 +56,27 @@ async function streamEvents(c: Context, subscribe: (q: AsyncQueue
stream.onAbort(stop)
+ // kilocode_change start
+ // On Windows, stream.onAbort() may never fire after a client disconnects
+ // (delayed TCP RST detection via IOCP). Without this try/catch, the
+ // GlobalBus listener, heartbeat interval, and AsyncQueue stay alive
+ // indefinitely for each dead connection — leaking memory on every
+ // SSE reconnect. Catching write errors lets us clean up eagerly.
try {
for await (const data of q) {
if (data === null) return
- await stream.writeSSE({ data })
+ try {
+ await stream.writeSSE({ data })
+ } catch {
+ log.info("global event write failed, cleaning up dead stream")
+ stop()
+ return
+ }
}
} finally {
stop()
}
+ // kilocode_change end
})
}
diff --git a/packages/opencode/test/kilocode/worktree-diff.test.ts b/packages/opencode/test/kilocode/worktree-diff.test.ts
index 9a284a3add5..cb6d08794ae 100644
--- a/packages/opencode/test/kilocode/worktree-diff.test.ts
+++ b/packages/opencode/test/kilocode/worktree-diff.test.ts
@@ -1,6 +1,7 @@
import { test, expect, describe } from "bun:test"
import { $ } from "bun"
import { tmpdir } from "../fixture/fixture"
+import { WorktreeDiff } from "../../src/kilocode/review/worktree-diff"
import path from "path"
/**
@@ -145,6 +146,19 @@ describe("worktree diff git commands", () => {
expect(allFiles).toContain("new-file.py")
})
+ test("worktree detail does not include unused patch content", async () => {
+ await using tmp = await setupRepo()
+ const dir = tmp.path
+
+ await Bun.write(path.join(dir, "existing.txt"), "hello\nmodified\n")
+
+ const detail = await WorktreeDiff.detail({ dir, base: "HEAD", file: "existing.txt" })
+
+ expect(detail?.patch).toBe("")
+ expect(detail?.before).toBe("hello\n")
+ expect(detail?.after).toBe("hello\nmodified\n")
+ })
+
test("worktree scenario: branch with no new commits, only untracked files", async () => {
// This is the exact scenario from the screenshot:
// - Worktree created from main
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index 63542d48e5b..527fcc92931 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -1301,7 +1301,7 @@ function ToolFileAccordion(props: { path: string; actions?: JSX.Element; childre
- {`\u202A${getDirectory(props.path)}\u202C`}
+ {`\u2066${getDirectory(props.path)}\u2069`}
{getFilename(props.path)}
@@ -2112,7 +2112,7 @@ ToolRegistry.register({
- {`\u202A${getDirectory(file.relativePath)}\u202C`}
+ {`\u2066${getDirectory(file.relativePath)}\u2069`}
{getFilename(file.relativePath)}
diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx
index 398755d01f9..d874697c6c8 100644
--- a/packages/ui/src/components/session-review.tsx
+++ b/packages/ui/src/components/session-review.tsx
@@ -509,7 +509,7 @@ export const SessionReview = (props: SessionReviewProps) => {
- {`\u202A${getDirectory(file)}\u202C`}
+ {`\u2066${getDirectory(file)}\u2069`}
{getFilename(file)}
diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx
index 29301462ec3..92e5eab8e14 100644
--- a/packages/ui/src/components/session-turn.tsx
+++ b/packages/ui/src/components/session-turn.tsx
@@ -499,7 +499,7 @@ export function SessionTurn(
- {`\u202A${getDirectory(diff.file)}\u202C`}
+ {`\u2066${getDirectory(diff.file)}\u2069`}
{getFilename(diff.file)}