Background
In #161 (closes #158) the agent-controlled preview browser was hoisted into an app-level PreviewBrowserProvider (client/src/components/PreviewBrowserPool.tsx) so it survives session/worktree switches. A pane is keyed by projectId:worktreeId.
Problem
A pane is tracked the first time its worktree is visited and is then kept for the lifetime of the app: its entry in the store, its <webview> (once a URL is opened), and its usePreviewBrowserHost WebSocket registration all persist. Nothing untracks a pane when its worktree is deleted/removed.
This is bounded by the number of worktrees and is harmless in normal use, but it leaks a webview process and an idle bridge socket per deleted worktree until the app restarts.
Proposal
When a worktree (or project) is deleted, untrack its pane(s):
- Remove the pane from the store (drops topology entry → unmounts the
<webview> and the PaneFrame, which closes the bridge socket via the existing cleanup).
- Ensure the server-side bridge host map (
server/lib/preview-browser.ts) drops the key when the socket closes (already handled by detach() on close).
Likely the pool needs to expose an untrackPane(key) (or untrackProject(projectId)) action, called from the worktree/project deletion flow.
Notes
Background
In #161 (closes #158) the agent-controlled preview browser was hoisted into an app-level
PreviewBrowserProvider(client/src/components/PreviewBrowserPool.tsx) so it survives session/worktree switches. A pane is keyed byprojectId:worktreeId.Problem
A pane is tracked the first time its worktree is visited and is then kept for the lifetime of the app: its entry in the store, its
<webview>(once a URL is opened), and itsusePreviewBrowserHostWebSocket registration all persist. Nothing untracks a pane when its worktree is deleted/removed.This is bounded by the number of worktrees and is harmless in normal use, but it leaks a webview process and an idle bridge socket per deleted worktree until the app restarts.
Proposal
When a worktree (or project) is deleted, untrack its pane(s):
<webview>and thePaneFrame, which closes the bridge socket via the existing cleanup).server/lib/preview-browser.ts) drops the key when the socket closes (already handled bydetach()onclose).Likely the pool needs to expose an
untrackPane(key)(oruntrackProject(projectId)) action, called from the worktree/project deletion flow.Notes