Wrap each agent in a tmux session for persistence
Author: model-collapseCreated Feb 25, 2026Updated May 27, 2026
Labelstype: featurearea: host
Problem
When using VS Code Remote (SSH, WSL, etc.), closing a terminal kills the Claude process immediately. There's no way to disconnect from a remote session and reconnect later without losing all running agents. This is especially painful for long-running tasks where Claude is actively working.
Solution
Each agent now runs inside a tmux session when tmux is available. This means:
- Closing a terminal only detaches from tmux -- Claude keeps running in the background
- Detached agents stay visible in the office with a semi-transparent appearance and gray X bubble
- Clicking a detached agent reattaches by opening a new terminal connected to the tmux session
- Orphaned sessions are auto-discovered on startup, so reopening VS Code reconnects to agents that were left running
- Graceful fallback -- when tmux is not installed, everything works exactly as before
Changes
New File
src/tmuxManager.ts-- Pure functions for all tmux shell interactions: availability check (cached), session naming, list/check/kill sessions, build attach/new-session commands
Backend
| File | Changes |
|---|---|
src/constants.ts |
Added TMUX_SESSION_PREFIX, TMUX_HEALTH_CHECK_INTERVAL_MS |
src/types.ts |
terminalRef now nullable, added tmuxSessionName and isDetached to AgentState/PersistedAgent |
src/agentManager.ts |
Launch wraps in tmux; persist/restore handles detached state; new discoverOrphanedTmuxSessions(); sendExistingAgents() notifies webview of detached agents |
src/PixelAgentsViewProvider.ts |
Terminal close = detach (if tmux alive); focus = reattach; close = kill tmux; 5s health check timer for dead sessions; null terminalRef guards |
src/fileWatcher.ts |
Guarded null terminalRef in terminal adoption code |
src/extension.ts |
Dev build activation log |
Webview
| File | Changes |
|---|---|
webview-ui/src/constants.ts |
DETACHED_CHARACTER_ALPHA = 0.45 |
webview-ui/src/office/types.ts |
Added isDetached to Character, extended bubbleType with 'detached' |
webview-ui/src/office/engine/characters.ts |
Default isDetached: false in createCharacter |
webview-ui/src/office/engine/officeState.ts |
New setAgentDetached() method |
webview-ui/src/hooks/useExtensionMessages.ts |
Handles agentDetached / agentReattached messages |
webview-ui/src/office/engine/renderer.ts |
Detached characters at 45% opacity; renders detached bubble sprite |
webview-ui/src/office/sprites/spriteData.ts |
New BUBBLE_DETACHED_SPRITE (gray X symbol) |
webview-ui/src/office/components/ToolOverlay.tsx |
Shows "Detached" status text and gray dot |
webview-ui/src/index.css |
Added --pixel-status-detached CSS variable |
How It Works
Terminal open: VS Code Terminal <--> tmux session <--> claude process
Terminal closed: (detached) tmux session <--> claude process (still running!)
Reattach: New Terminal <--> tmux session <--> claude processtmux session names follow the pattern pixel-agents-{agentId}-{sessionUuid}, which allows the extension to discover and adopt orphaned sessions by matching the UUID to JSONL transcript files.
Edge Cases Handled
- tmux not installed:
isTmuxAvailable()returns false, all behavior falls back to current direct-terminal mode - tmux session dies while detached: Health check (5s) detects dead sessions and removes the agent
- Multiple VS Code windows: Only adopts tmux sessions whose JSONL file belongs to the current workspace
- Extension dispose/deactivate: Does NOT kill tmux sessions -- they intentionally persist
- Backward compatibility:
PersistedAgent.tmuxSessionNameis optional; old persisted data works unchanged
Testing
- tmux path: Launch agent -> verify
tmux lsshows session -> close terminal -> character goes semi-transparent -> click to reattach -> close via X button -> tmux session killed - No tmux: Rename tmux binary -> launch agent -> verify direct-terminal behavior unchanged
- Persistence: Launch agent -> close VS Code -> reopen -> agent restored as detached -> click to reattach
- Auto-discovery: Launch agent -> close VS Code -> reopen -> orphaned tmux session found and shown
- Health check: Detach agent -> manually
tmux kill-session-> wait 5s -> agent removed
Source: pixel-agents-hq/pixel-agents