Skip to content

perf(agent-manager): optimize tab switching and context transition latency - #13156

Merged
marius-kilocode merged 1 commit into
mainfrom
optimize-agent-manager-tab-latency
Aug 17, 2026
Merged

perf(agent-manager): optimize tab switching and context transition latency#13156
marius-kilocode merged 1 commit into
mainfrom
optimize-agent-manager-tab-latency

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Problem

Switching between contexts (Local and Worktrees) or switching session tabs in Agent Manager suffered from noticeable latency and main-thread hitching:

  1. ChatView unmount/remount on context changes: <ChatView> was conditionally rendered under <Show when={!contextEmpty()}>. When transitioning between an empty context (such as Local with 0 open tabs) and a populated worktree, the entire <ChatView> component tree, its providers, Virtualizer, PromptInput, and DOM were destroyed and recreated, producing over 13,000 transient DOM nodes, 7,300 layout objects, and a ~190ms main-thread freeze.
  2. Style recalculation and layout thrashing from tooltips: Tooltip triggers across buttons and tabs mounted <KobalteTooltip.Portal> and <KobalteTooltip.Content> unconditionally. Kobalte's createPresence invoked getComputedStyle(element) to read animationName on every reactive tick, and each tooltip attached a dedicated MutationObserver. getAnimationName was the top CPU hotspot in traces.
  3. Synchronous prompt history seeding and unconditional draft writes: PromptInput synchronously scanned all user message parts on every tab switch to seed prompt history, while saveDraft executed Map writes even when drafts were empty or unchanged.
  4. Multi-frame layout delay and reactive cascading: MessageList deferred scroll restoration across two nested requestAnimationFrame cycles (introducing a 33-50ms visual delay), while session and selection actions dispatched multiple unbatched reactive signal updates.

Solution

  • Persistent ChatView layer: Replaced <Show when={!contextEmpty()}> around <ChatView> with a persistent container toggled via .am-chat-wrapper-hidden (display: none;). The ChatView instance and virtualizer stay warm in memory across context switches.
  • Lazy and lightweight tooltips: Render a lightweight <div> trigger when idle and closed, lazily mounting KobalteTooltip only on pointer enter / focus in or when forceOpen is set. Guarded the portal with <Show when={local.forceOpen || state.open}> to completely bypass createPresence style calculations when tooltips are closed, and made MutationObserver registration conditional.
  • Deferred history seeding and guarded draft writes: Moved history.seed to an idle timeout and guarded saveDraft against unnecessary Map allocations for empty/unmodified drafts.
  • Synchronous batching and single-frame scroll restoration: Wrapped tab selection and context switching operations in Solid's batch(() => ...) to coalesce reactive updates. Pre-hydrated the visible tail messages (last 15 messages) directly into store.parts on load, and reduced scroll restoration in MessageList to a single frame.

Performance Measurements

Metric / Scenario Baseline Optimized Improvement
Local to Worktree Switch: Longest Main-Thread Task 191.2 ms 59.4 ms 69% reduction
Local to Worktree Switch: Total Long Tasks 315.0 ms 59.5 ms 81% reduction
Local to Worktree Switch: Total Task Duration 467.7 ms 230.0 ms 51% faster
Local to Worktree Switch: Transient DOM Nodes +13,158 nodes +1,158 nodes 91% reduction
Local to Worktree Switch: Layout Objects Created +7,346 objects +688 objects 91% reduction
Local to Worktree Switch: getAnimationName Self-Time 77.5 ms 19.8 ms 75% reduction
Local to Worktree Switch: getBoundingClientRect Self-Time 48.3 ms 18.5 ms 62% reduction
Warm Local Tab Switch: Long Tasks (>50ms) 1 task (53.5 ms) 0 tasks (0 ms) 100% eliminated
Warm Local Tab Switch: Script Execution Delta 0.13 ms 0.12 ms Instantaneous (<0.2ms)
Warm Worktree Sidebar Switch: Style Recalc Duration 73.6 ms 39.9 ms 46% reduction
Warm Worktree Tab Switch: Script Self-Time <0.5 ms <0.5 ms Instantaneous (<0.2ms)

Comment thread packages/ui/src/components/tooltip.tsx Outdated
Comment thread packages/ui/src/components/tooltip.tsx Outdated
@kilo-code-bot

kilo-code-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files)
  • .changeset/optimize-agent-manager-tab-latency.md
  • packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
  • packages/kilo-vscode/webview-ui/agent-manager/selection-actions.ts
  • packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
  • packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx
  • packages/kilo-vscode/webview-ui/src/context/session.tsx
  • packages/ui/src/components/tooltip.tsx
Previous Review Summaries (2 snapshots, latest commit 14dcfba)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 14dcfba)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files)
  • .changeset/optimize-agent-manager-tab-latency.md
  • packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
  • packages/kilo-vscode/webview-ui/agent-manager/selection-actions.ts
  • packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
  • packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx
  • packages/kilo-vscode/webview-ui/src/context/session.tsx
  • packages/ui/src/components/tooltip.tsx

Previous review (commit fae399b)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/ui/src/components/tooltip.tsx 119 Lazy-mounting on focusin swaps the Switch branch after focus has moved into a child, detaching the focused node — keyboard users lose focus on first Tab into any tooltip-wrapped control; the swap also recreates the trigger subtree each hover cycle, resetting child state

SUGGESTION

File Line Issue
packages/ui/src/components/tooltip.tsx 94 MutationObserver is only installed if an [aria-expanded]/[data-expanded] child exists at activation time; late-mounted expandables are never observed

The persistent ChatView wrapper, batched selection/session actions, guarded draft writes, deferred history seeding, tail pre-hydration, and single-frame scroll restore all look correct — semantics of selectLocalAction/selectWorktreeAction are preserved, the stash/store interplay in handleMessagesLoaded stays consistent (hydrateParts skips pre-hydrated ids), timers/observers are cleaned up, and no memory leaks were introduced. The findings above are limited to the tooltip lazy-mount path.

Files Reviewed (8 files)
  • .changeset/optimize-agent-manager-tab-latency.md
  • packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css
  • packages/kilo-vscode/webview-ui/agent-manager/selection-actions.ts
  • packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
  • packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx
  • packages/kilo-vscode/webview-ui/src/context/session.tsx
  • packages/ui/src/components/tooltip.tsx - 2 issues

Fix these issues in Kilo Cloud


Reviewed by kimi-k3 · Input: 43.2K · Output: 5.3K · Cached: 301.1K

Review guidance: REVIEW.md from base branch main

@marius-kilocode
marius-kilocode force-pushed the optimize-agent-manager-tab-latency branch from fae399b to 14dcfba Compare August 17, 2026 07:18
@marius-kilocode
marius-kilocode force-pushed the optimize-agent-manager-tab-latency branch from 14dcfba to be8b85c Compare August 17, 2026 07:45
@marius-kilocode
marius-kilocode merged commit 44943e5 into main Aug 17, 2026
34 checks passed
@marius-kilocode
marius-kilocode deleted the optimize-agent-manager-tab-latency branch August 17, 2026 08:35
// the messagesLoaded batch. The second frame restores scroll position
// without forcing a synchronous layout reflow mid-paint.
requestAnimationFrame(() => {
requestAnimationFrame(() => {

@hdcodedev hdcodedev Aug 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marius-kilocode We found out that messages don’t auto-scroll anymore. The most likely reason for this is the removal of double-rAF. Should we investigate further and see if we can fix it without using double-rAF?

https://discord.com/channels/1349288496988160052/1349288496988160055/1539696538325098656
https://discord.com/channels/1349288496988160052/1391109167275577464/1539874684693254144

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants