Skip to content

fix(vscode): release tool animations on unmount to stop transcript leak - #14280

Merged
marius-kilocode merged 2 commits into
mainfrom
investigate-session-switching-performance
Sep 18, 2026
Merged

marius-kilocode merged 2 commits into
mainfrom
investigate-session-switching-performance

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Agent Manager session switching gets slower the longer a VS Code window stays open. A long-lived window accumulates unmounted tool cards and transcript rows, so every switch pays for more garbage collection and render work. A fresh window with the same sessions switches fast, which is why the regression only appears after hours of use.

Why This Change Was Made

The tool card fade path kept unmounted rows alive through two mechanisms.

  1. BasicTool (shared upstream file) read the trigger JSX getter twice. The copy built only for the Match condition was never inserted into the DOM, but its onMount still ran, so useToolFade started a Motion animation on a detached node. Blink does not advance animations whose target is outside the document, so onfinish never fired and the document timeline kept the Animation alive.
  2. When a card unmounted while a fade was in flight (normal during streaming, and guaranteed when switching while fades run), cleanup called anim.stop(). Motion only clears MotionValue.animation when the animation finishes, which a cancelled animation never does. The stale reference cycles through the WAAPI Animation, its target element and Motion's per-element visual element store. That cycle crosses the V8 and Blink heaps and is not collected, and the element then pinned the whole transcript row through Solid prop closures.

Changes:

  • packages/ui/src/components/basic-tool.tsx: read the trigger getter once with createMemo, and complete() the collapsible height animation on cleanup.
  • packages/kilo-ui/src/components/motion.tsx: add a settle() helper that calls complete() instead of stop() when an element goes away.
  • packages/kilo-ui/src/components/tool-utils.ts: useToolFade skips detached nodes and settles on cleanup; useRowWipe settles a started animation that it previously left running; useCollapsible and useGrowIn settle on cleanup.
  • packages/kilo-ui/src/components/grow-box.tsx: settle on cleanup.

User Impact

Agent Manager session switching stays fast in long-lived windows. Tool card fade, wipe and collapse animations are unchanged visually.

Evidence

Measured in an isolated VS Code instance with the real Agent Manager state (31 sessions across 26 worktrees) imported.

Scenario Before After
Retained DOM nodes over 24 switches (forced GC) +3,448 0 (heap identical)
Detached <div> after ~100 switches 3,273 244 (Solid template cache and tooltip fragments)
Retained Motion Animation objects 66 4

Heap snapshot dominator chains showed every retained transcript row dominated by DocumentTimeline > Animation > AsyncMotionValueAnimation > promise reactions > ShellText props > detached row.

Manual Test

  1. Restart the extension host, open Agent Manager, and switch between two worktrees 20 to 30 times quickly (under 1 second apart) so fades are in flight.
  2. Open the Agent Manager Webview Developer Tools, take a heap snapshot, and filter by Detached. There should be no vscode-session-turn rows and only a few Animation objects.
  3. Let a bash-heavy session stream, then repeat step 2. The counts should stay flat.
  4. Tool card subtitles and shell descriptions still wipe in, and collapsing and expanding cards still animates.

Tool card fade animations kept unmounted transcript rows alive, so Agent
Manager switching slowed down the longer a window stayed open. A stopped
Motion animation never finishes, so Motion kept a reference cycle to the
removed element through its visual element store.

Settle interrupted animations with complete() instead of stop(), skip
animating nodes outside the document, and read the BasicTool trigger
getter once instead of building and discarding a detached subtree.
@kilo-code-bot

kilo-code-bot Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental review since 815a81b46cadc7854af1e4a89524fed0080db070: only a kilocode_change marker formatting change in packages/ui/src/components/basic-tool.tsx (turn a single-line marker into a start/end block around the same heightAnim?.complete() cleanup). No behavioral change.

Files Reviewed (5 files)
  • .changeset/tool-fade-leak.md
  • packages/kilo-ui/src/components/grow-box.tsx
  • packages/kilo-ui/src/components/motion.tsx
  • packages/kilo-ui/src/components/tool-utils.ts
  • packages/ui/src/components/basic-tool.tsx
Previous Review Summary (commit 815a81b)

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

Previous review (commit 815a81b)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • .changeset/tool-fade-leak.md
  • packages/kilo-ui/src/components/grow-box.tsx
  • packages/kilo-ui/src/components/motion.tsx
  • packages/kilo-ui/src/components/tool-utils.ts
  • packages/ui/src/components/basic-tool.tsx

Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

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.

2 participants