Skip to content

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

@kilo-code-bot

Description

@kilo-code-bot

Summary

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

This issue documents the specific memory accumulation patterns found in the codebase, ordered by severity.

Critical Issues

1. No message/part cleanup on session switch (HIGHEST IMPACT)

Files: packages/kilo-vscode/webview-ui/src/context/session.tsx:796-831

The SolidJS store holds messages: Record<string, Message[]> and parts: Record<string, Part[]>. When the user switches sessions, handleMessagesLoaded() adds the new session's messages+parts to the store but never removes the old session's data. Only handleSessionDeleted() (line 1135) does proper cleanup.

If a user visits 10 different sessions during a 3-hour window, all 10 sessions' full message histories accumulate in the webview's JS heap permanently. A single heavy session with 100+ tool calls can easily be 5-20 MB of retained JSON. Across many sessions this compounds fast.

Additionally, handleSessionsLoaded() (line 1115) reconciles the sessions index (removing stale entries) but leaves orphaned messages and parts for those removed sessions.

2. No message list virtualization

File: packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx:142-159

<For each={userMessages()}>
  {(msg, idx) => <VscodeSessionTurn ... />}
</For>

Every message in the current session is rendered as a full DOM subtree simultaneously. No virtual scrolling, no windowing, no "load more" pagination. The desktop app (packages/app/) has turnInit = 10 / turnBatch = 8 windowing — the VS Code extension does not.

In a long session (3 hours of active coding), you can accumulate hundreds of messages. Each VscodeSessionTurn renders the user message, all assistant messages, and all tool parts with their output. The DOM tree grows without bound.

3. SSE message.part.updated publishes unstripped full parts

File: packages/opencode/src/session/index.ts:830-833

Bus.publish(MessageV2.Event.PartUpdated, {
  part: structuredClone(part),
})

The structuredClone(part) contains the full raw part including metadata.filediff.before/after (full file contents for edit tools). stripPartMetadata() is only applied on REST API reads, NOT on Bus/SSE events. The extension's KiloProvider does apply slimPart() before postMessage (line 2901-2906), but the full payload still traverses the SSE pipe and gets parsed in the extension host before trimming.

4. session.diff SSE events carry full file before/after content

Files: packages/opencode/src/session/summary.ts:94-97, packages/opencode/src/snapshot/index.ts:278-286

Snapshot.FileDiff includes before: string and after: string — complete file contents. Published raw via Bus. If a session edits 10 files averaging 50KB each, that's ~1MB per diff event, firing after every turn.

Medium Issues

5. slimPart only covers 5 of 15+ tool types

File: packages/kilo-vscode/src/kilo-provider/slim-metadata.ts:142-148

Only edit, apply_patch, multiedit, write, and bash are slimmed. Notably missing: webfetch (can store entire web pages), websearch, codesearch, read, glob, grep, task, and all MCP tools.

6. trackedSessionIds grows monotonically

File: packages/kilo-vscode/src/KiloProvider.ts:148

Every session opened and every child session spawned via the task tool is added. Only removed on explicit session deletion. This broadens the SSE event filter over time, causing unnecessary postMessage forwarding.

7. imageDrafts Map holds base64 data URLs indefinitely

File: packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx:36

Module-level Map stores full base64 data URLs per session draft. A single screenshot can be 1-5 MB as base64. Never cleaned on session deletion.

8. messageSessionIdsByMessageId Map never cleaned per-session

File: packages/kilo-vscode/src/services/cli-backend/connection-service.ts:52

Grows with every message across all sessions. Only cleared on dispose().

Recommendations

Immediate wins (low-effort, high-impact):

  1. Evict old sessions from the webview store on session switch. Delete store.messages[oldSessionID] and associated store.parts entries. Turns memory from O(sessions visited) to O(1).
  2. Clean up orphaned messages/parts in handleSessionsLoaded. When sessions are removed from the index, also delete their messages and parts.
  3. Add webfetch, websearch, codesearch, and MCP tool slimming to slim-metadata.ts. Cap their output the same way bash is capped.
  4. Clean imageDrafts, drafts, reviewDrafts on session deletion.

Medium-effort improvements:

  1. Implement message list virtualization similar to the desktop app's turnInit/turnBatch approach.
  2. Apply stripPartMetadata server-side on SSE events, not just REST reads.
  3. Strip before/after from session.diff events before SSE publishing.
  4. Prune trackedSessionIds when switching sessions.

Longer-term:

  1. Server-side SSE session filtering — currently all events broadcast to all clients.
  2. Cursor-based pagination for session.messages() — currently no offset/cursor, always loads from beginning.

Context

  • Electron's V8 pointer compression imposes a hard ~4 GB heap limit per process
  • The renderer process (which hosts extension webviews) is the process that grey-screens on OOM
  • Total system RAM is irrelevant — the crash happens at the V8 heap ceiling regardless
  • This affects all users but is most pronounced with heavy tool usage, long sessions, and multi-session workflows (Agent Manager)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions