Skip to content

fix: reduce renderer memory accumulation - #8759

Closed
IamCoder18 wants to merge 4 commits into
Kilo-Org:mainfrom
IamCoder18:fix/renderer-memory-accumulation
Closed

fix: reduce renderer memory accumulation#8759
IamCoder18 wants to merge 4 commits into
Kilo-Org:mainfrom
IamCoder18:fix/renderer-memory-accumulation

Conversation

@IamCoder18

Copy link
Copy Markdown
Contributor

Context

The VS Code extension's webview (renderer process) has several patterns that cause unbounded memory growth during normal usage. With Electron's ~4GB V8 heap limit, a 3-hour active session with moderate tool usage can approach this ceiling and trigger the "grey screen" OOM crash.

Closes #8607

Implementation

1. Message List Virtualization (Webview)

  • Added turnInit = 10 / turnBatch = 8 windowing in MessageList.tsx
  • Initial load shows only the 10 most recent messages
  • Toggle button to show all / show recent messages
  • Session ID tracking to reset only on session change (not on every message update)

2. Session Cleanup (Webview)

  • handleSessionsLoaded() now cleans up orphaned messages, parts, todos, agent selections, drafts, and pending optimistic messages when sessions are removed from the index
  • selectSession() cleans up old session data when switching away
  • handleSessionDeleted() calls clearSessionDrafts()

3. SSE Event Stripping (Server)

  • session/index.ts: Apply stripPartMetadata() before publishing PartUpdated SSE events (removes filediff.before/after)
  • session/summary.ts: Strip before/after from diff events before publishing

4. Metadata Slimming (Extension)

  • Expanded slimPart to cover more tool types: webfetch, websearch, codesearch, read, grep, task
  • Preserved sessionId in task metadata for sub-agent linking

How to Test

  1. Open the VS Code extension with bun run extension
  2. Create a session with many messages (20+ user turns with tool calls)
  3. Verify initial view shows only ~10 recent messages with "Show earlier" button
  4. Click button to show all messages, click again to collapse
  5. Switch between multiple sessions - verify memory doesn't grow unbounded
  6. Use webfetch/websearch tools - verify payloads are slimmed in DevTools

Get in Touch

My discord is @iamcoder18 and I am in the Kilo discord server.

Comment thread packages/kilo-vscode/src/kilo-provider/slim-metadata.ts Outdated
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx Outdated
@kilo-code-bot

kilo-code-bot Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

No outstanding issues remain. All previously flagged items have been resolved.

Resolved Issues (full cycle)
  • WARNINGauto_expand_history config field now mirrored in cloud repo: kilo-org/cloud#3489 merged (confirmed by contributor).
  • WARNING — Race condition in queueMicrotask fixed: if (currentSessionID() === oldID) return guard added in session.tsx, preventing stale eviction when rapidly switching A→B→A.
  • WARNING — Draft cleanup now works: PromptInput.tsx imports from the new draft-store.ts module; deleteDraftsForSession is called from handleSessionDeleted for real deletions only.
  • WARNINGactiveUserIndex computed memo (dead code) removed from MessageList.tsx.
  • WARNINGwebfetch/websearch/codesearch/task slimmers added to slim-metadata.ts.
  • WARNINGsubagent_type is now preserved in the task slimmer's input.
  • ✅ Session cleanup on switch (selectSession) and deletion (handleSessionDeleted) now properly evicts messages, parts, todos, agentSelections, pages, and the loaded set.
Files Reviewed
  • packages/kilo-vscode/webview-ui/src/context/session.tsx
  • packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
  • packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx
  • packages/kilo-vscode/src/kilo-provider/slim-metadata.ts
  • packages/kilo-vscode/webview-ui/src/utils/draft-store.ts
  • packages/opencode/src/config/config.ts
  • packages/kilo-vscode/tests/unit/prompt-send-contract.test.ts

Incremental note: The HEAD commit (6a16f20) is a revert of a main-branch merge, restoring the branch to the previously-reviewed state (2e571b06). No new code changes were introduced since the last review cycle.


Reviewed by claude-4.6-sonnet-20260217 · 954,963 tokens

Review guidance: REVIEW.md from base branch main

Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
Comment thread packages/kilo-vscode/webview-ui/src/context/session.tsx Outdated
@marius-kilocode

Copy link
Copy Markdown
Collaborator

@IamCoder18 did you actually profile this?

@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from 7c4c780 to 4106a14 Compare April 18, 2026 15:27
Comment thread packages/kilo-vscode/src/kilo-provider/slim-metadata.ts Outdated
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
Comment thread packages/kilo-vscode/webview-ui/src/context/session.tsx
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from 460ed7f to 69e353f Compare April 18, 2026 23:26
Comment thread packages/kilo-vscode/webview-ui/src/context/session.tsx
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from c122550 to 293dcca Compare April 19, 2026 01:02
@IamCoder18

Copy link
Copy Markdown
Contributor Author

@marius-kilocode I profiled my changes by switching between 10 memory-intensive sessions.

  • Memory remained stable at ~92MB (with a 0.2MB increase) after session switching.
  • Session loading time reduced by 2-3 seconds.
  • Detached elements reduced from 426 to <100.

Comment thread packages/kilo-vscode/src/kilo-provider/slim-metadata.ts
Comment thread packages/kilo-vscode/src/kilo-provider/slim-metadata.ts
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch 2 times, most recently from 3a5ebf6 to b13862a Compare April 23, 2026 02:35
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from b13862a to 93a79b9 Compare May 6, 2026 03:29
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from 12424fc to 1c97731 Compare May 6, 2026 23:06
@IamCoder18

Copy link
Copy Markdown
Contributor Author

Hi @marius-kilocode,

I’ve just pushed a few updates to this PR. I’ve rebased against main and resolved some complex merge conflicts, so the branch is now fully up to date. All checks are passing too.

I fixed an issue where the scroll location would jump when new messages were loaded.
I've manually tested both the auto-expand and manual expansion to ensure they work reliably.

I had also resolved the issues you pointed out earlier and had replied to each of them.

Whenever you have a moment, I’d appreciate it if you could take another look at the changes!

@marius-kilocode

Copy link
Copy Markdown
Collaborator

I can see some weird issues with this branch:

image

Where collapse buttons would be missing. Looks like there are some conflicts like this.

@marius-kilocode

Copy link
Copy Markdown
Collaborator

The change and optimization might work, but the changes are risky, this is what my bot found:

High: queued prompts can render twice or get the wrong queued state in MessageList. See packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx:321 and packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx:342.

High: switching away from a session deletes its transcript cache but does not clear the “loaded” marker, so switching back can produce empty or stale transcript behavior. See packages/kilo-vscode/webview-ui/src/context/session.tsx:2082.

High: switching sessions now deletes unsent prompt drafts, including review/image draft state, which is a user-visible regression. See packages/kilo-vscode/webview-ui/src/context/session.tsx:2116.

@marius-kilocode

marius-kilocode commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Can you please re-validate the current head against these cases?

  1. Rapid A -> B -> A switching between two long sessions, confirm transcripts never come back empty/stale and scroll restore still behaves.
  2. Queue 2-3 prompts while one response is still running, confirm each queued turn appears exactly once and active/queued state stays correct.
  3. Type an unsent draft in session A, switch to B, then back to A, confirm the draft survives. If easy, please also try one image/review draft.
  4. Please rerun the renderer memory profile on the current commit and share the scenario plus heap/detached-node numbers again.

Please validate session switching in both places:

  1. Normal VS Code chat sidebar: switch rapidly between two long existing sessions via history/recent sessions and confirm neither transcript comes back empty or stale.
  2. Agent Manager, if that was the surface used for the memory profiling: switch repeatedly between populated sessions/cards and confirm the same behavior there.

Please say which surface the memory profile was run against.

Thanks, I want to make sure the latest force-pushed version still has the memory win without regressing core chat behavior.

@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch 2 times, most recently from 9cae484 to 16993a2 Compare May 21, 2026 03:35
Comment thread packages/kilo-vscode/webview-ui/src/utils/draft-store.ts
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch 2 times, most recently from 345c859 to b5f27ad Compare May 21, 2026 03:50
@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from b5f27ad to d733bc1 Compare May 21, 2026 03:55
@IamCoder18

Copy link
Copy Markdown
Contributor Author

Hey! Thank you so much for your detailed feedback!

I essentially re-applied the core changes on main and resolved the issues pointed out.

Here are my runs (all with the sidebar):

Draft with Image Survives

draftwithimage.mp4

Switching between two sessions, no empty list

loadingagainanddraft.mp4

Queued Messages

loadingagainanddraft.mp4

The heap snapshots didn't show much of a change. I may be doing it wrong since I don't have much experience with memory related changes. If I am, I would appreciate it if you could guide me through properly identifying improvement.

Here are my heap snapshots (run against the sidebar) anyway, if it helps:

Before my changes

heapsnapshotsbefore.mp4

After my changes

heapsnapshotsafter.mp4

Lastly, I'm going to open a PR in the cloud repo to mirror the new config value as per the contributing guidelines.

@IamCoder18

IamCoder18 commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

Created the PR in the cloud repo to mirror the config value: Kilo-Org/cloud#3408

@IamCoder18

Copy link
Copy Markdown
Contributor Author

@marius-kilocode I think this is ready to review.

@kilo-code-bot

kilo-code-bot Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Found a current P1 issue that should be fixed before merge:

  • packages/kilo-vscode/webview-ui/src/context/session.tsx dispatches sessionDeleted when switching away from a session as part of in-memory cache cleanup, and packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx treats that event as a real deletion and deletes all text/review/image draft entries for that session. Even though the draftKey effect saves the old draft during setCurrentSessionID(id), the queued cleanup then fires sessionDeleted and removes the saved draft. A user can lose an unsent prompt and attached review/image draft simply by switching A -> B -> A.

Suggested fixes:

  1. Do not dispatch sessionDeleted from the normal selectSession cache-unload path. Reserve sessionDeleted for actual server/session deletion only.
  2. If other code needs to observe cache eviction, introduce a separate event such as sessionUnloaded and ensure PromptInput does not clear drafts for it.
  3. Keep draft cleanup in the real deletion path (handleSessionDeleted) only.
  4. Consolidate the draft maps so deletion is not split between private PromptInput maps and the new draft-store.ts maps; e.g. export the existing maps/helper from one module, have PromptInput use those maps, and call deleteDraftsForSession(sessionID) only for real deletions.

This preserves the memory-cache cleanup goal while avoiding active user data loss on ordinary session navigation.

@IamCoder18

IamCoder18 commented May 23, 2026

Copy link
Copy Markdown
Contributor Author

Found a current P1 issue that should be fixed before merge:

  • packages/kilo-vscode/webview-ui/src/context/session.tsx dispatches sessionDeleted when switching away from a session as part of in-memory cache cleanup, and packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx treats that event as a real deletion and deletes all text/review/image draft entries for that session. Even though the draftKey effect saves the old draft during setCurrentSessionID(id), the queued cleanup then fires sessionDeleted and removes the saved draft. A user can lose an unsent prompt and attached review/image draft simply by switching A -> B -> A.

Suggested fixes:

  1. Do not dispatch sessionDeleted from the normal selectSession cache-unload path. Reserve sessionDeleted for actual server/session deletion only.
  2. If other code needs to observe cache eviction, introduce a separate event such as sessionUnloaded and ensure PromptInput does not clear drafts for it.
  3. Keep draft cleanup in the real deletion path (handleSessionDeleted) only.
  4. Consolidate the draft maps so deletion is not split between private PromptInput maps and the new draft-store.ts maps; e.g. export the existing maps/helper from one module, have PromptInput use those maps, and call deleteDraftsForSession(sessionID) only for real deletions.

This preserves the memory-cache cleanup goal while avoiding active user data loss on ordinary session navigation.

Thanks for the detailed analysis! However, after tracing every code path you described , the described scenario cannot occur in the current codebase. Here's why:

  1. sessionDeleted is dispatched in exactly one place: KiloProvider.handleDeleteSession() (KiloProvider.ts:1635), which fires only after a successful HTTP DELETE /session/:id — i.e. explicit user-initiated deletion. Session switching (selectSession) never reaches this path.

  2. handleSessionsLoaded (the session-list reconciliation path that runs on session switching) does not call handleSessionDeleted. It simply prunes stale entries from the reactive store with produce, no side effects, no event dispatch.

  3. PromptInput.tsx has no sessionDeleted handler at all. The vscode.onMessage subscriber (lines 381–493) handles setChatBoxMessage, appendChatBoxMessage, appendReviewComments, triggerTask, sendMessageFailed, sessionCreated, focusInput, enhancePromptResult, and enhancePromptErrorsessionDeleted is not among them. The module-level draft Maps (drafts, reviewDrafts, imageDrafts) are not cleared by any event.

  4. The draftKey effect (PromptInput.tsx:214–235) correctly calls saveDraft(prev, …) on session change, preserving the prior session's draft. Switching back (A→B→A) restores it. Nothing fires after saveDraft that would clear the Map.

This was shown in the video, which I am showing here again:

draftwithimage.mp4

However, this used to be an issue previously that I had resolved.
If there are any other issue, please point them out, and I'll fix them.

IamCoder18 and others added 3 commits June 3, 2026 11:12
This change addresses OOM crashes by reducing memory usage in the webview renderer process:

- chore(webview): add message list virtualization with initial load limiting
- perf(session): implement aggressive session cleanup on switch and deletion
- perf(server): strip heavy metadata (file diffs/tool output) from SSE events
- refactor(metadata): add slim parsers for websearch, webfetch, codesearch, and task tools
- feat(settings): add auto-expand history toggle to UI
@marius-kilocode
marius-kilocode force-pushed the fix/renderer-memory-accumulation branch from d733bc1 to 28664c0 Compare June 3, 2026 09:29
if (oldID && oldID !== id) {
const msgs = store.messages[oldID] ?? []
const msgIds = msgs.map((m) => m.id)
queueMicrotask(() => {

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.

WARNING: Race condition with rapid session switching (A→B→A)

The queueMicrotask captures oldID but runs after selectSession returns. If the user switches A→B then quickly back to A before the microtask fires:

  1. A→B: oldID=A, microtask queued to delete A's store data, currentSessionID = B
  2. B→A: ready = loaded().has("A") === true (microtask hasn't fired yet), setCurrentSessionID("A"), mode: "focus" sent
  3. Microtask 1 fires: deletes A's messages, parts, todos, etc. from store → the active session A now shows empty
  4. Reconcile from mode: "focus" eventually repopulates, but there's a visible blank flash

A guard inside the microtask would prevent this: skip the delete if currentSessionID() has already switched back to oldID.

queueMicrotask(() => {
  if (currentSessionID() === oldID) return // switched back — don't evict
  ...
})

@IamCoder18

Copy link
Copy Markdown
Contributor Author

@marius-kilocode

The Cloud PR (Kilo-Org/cloud#3489) adding the config change is merged, so these changes can be merged without risking invalid config issues!

@IamCoder18

IamCoder18 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

I actually attempted to resolve merge conflicts, but that cause some issues. Then I tried to undo that commit, but it caused a revert commit that reset all changes from this branch's base. I'll try to fix it. Sorry about that.

@IamCoder18
IamCoder18 force-pushed the fix/renderer-memory-accumulation branch from 6a16f20 to 2e571b0 Compare June 13, 2026 14:43
@IamCoder18

Copy link
Copy Markdown
Contributor Author

Force-pushed to fix it.
Merge conflicts remain.

@marius-kilocode

Copy link
Copy Markdown
Collaborator

@IamCoder18 I will have another look there, are a few things that are still risky with this PR. Which is why I haven't merged it. Sorry about this, but this is really hard to trace back later.

@marius-kilocode

Copy link
Copy Markdown
Collaborator

@IamCoder18 can you still reproduce memory issues in the current main? We rewrote the whole session rendering to use virtualized bounded transcript rows (see: #11094) mainly for performance reasons and less for memory. But it should also mean that we don't accumulate memory anymore.

@marius-kilocode

Copy link
Copy Markdown
Collaborator

We really appreciate the effort you put into this. Looking at the PR we probably should not integrate anymore:

  • Server-side PartUpdated stripping
  • auto_expand_history
  • Session eviction on every switch
  • Inline diff-size changes

What still makes sense:

  • Draft cleanup on actual session deletion
  • Additional metadata slimming

Would you like me to extract those into separate PR's or do you prefer to do this yourself?

@IamCoder18

Copy link
Copy Markdown
Contributor Author

We really appreciate the effort you put into this. Looking at the PR we probably should not integrate anymore:

  • Server-side PartUpdated stripping
  • auto_expand_history
  • Session eviction on every switch
  • Inline diff-size changes

What still makes sense:

  • Draft cleanup on actual session deletion
  • Additional metadata slimming

Would you like me to extract those into separate PR's or do you prefer to do this yourself?

Yeah I can do this later today or tomorrow.

@IamCoder18

Copy link
Copy Markdown
Contributor Author

Made the first PR to resolve the unsent drafts: #11442

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Closing this as we follow up on this, thanks @IamCoder18!

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.

Memory accumulation in VS Code extension webview risks renderer OOM (grey screen)

2 participants