Asciicast Pruning in VibeTunnel
Overview
VibeTunnel implements an intelligent pruning system to prevent session recordings from growing indefinitely. This is critical for long-running terminal sessions (like Claude Code sessions) that can generate gigabytes of output over time. The pruning system detects terminal clear operations and uses them as safe points to discard old content.The Problem
Terminal sessions can run for hours or days, generating massive amounts of output:- A typical Claude Code session can produce 100MB+ of output per hour
- Without pruning, session files can grow to several gigabytes
- Large files cause performance issues for streaming and playback
- Most of the old content is no longer relevant after screen clears
How Pruning Works
1. Real-time Detection During Recording
When a PTY session is created (in the forwarder process), theAsciinemaWriter
monitors all terminal output for pruning sequences:
2. Pruning Sequences
The system recognizes these ANSI escape sequences as safe pruning points:\x1b[3J
- Clear scrollback buffer (most common in modern terminals)\x1bc
- Terminal reset (RIS - Reset to Initial State)\x1b[2J
- Clear screen\x1b[H\x1b[J
- Home cursor + clear (older pattern)\x1b[H\x1b[2J
- Home cursor + clear screen variant\x1b[?1049h
- Enter alternate screen (vim, less, etc)\x1b[?1049l
- Exit alternate screen\x1b[?47h
- Save screen and enter alternate screen (legacy)\x1b[?47l
- Restore screen and exit alternate screen (legacy)
3. Byte Position Tracking
TheAsciinemaWriter
maintains precise byte position tracking:
- Asciinema files use JSON encoding, which changes byte counts
- UTF-8 encoding means character count ≠ byte count
- We need exact byte positions to safely resume streaming
4. Position Calculation
When a pruning sequence is detected, we calculate its exact byte position in the file:5. Storing Pruning Information
When a pruning sequence is detected, thePtyManager
updates the session info:
6. Using Pruning During Playback
When a client connects to stream a session, theStreamWatcher
:
- Reads the stored
lastClearOffset
from session info - Starts reading the asciicast file from that position instead of the beginning
- This skips all the old content before the last clear
7. Retroactive Pruning Detection
TheStreamWatcher
also scans for pruning sequences when analyzing existing content:
- A session was recorded without pruning detection
- Multiple clear sequences exist in the buffered content
- We need to find the most recent clear point
Architecture
Component Responsibilities
-
PruningDetector (
utils/pruning-detector.ts
)- Single source of truth for pruning sequences
- Provides detection and position calculation functions
- Ensures consistency between components
-
AsciinemaWriter (
pty/asciinema-writer.ts
)- Real-time detection during recording
- Precise byte position tracking
- Invokes callbacks when sequences detected
-
PtyManager (
pty/pty-manager.ts
)- Registers pruning callbacks
- Updates session info with clear offsets
- Coordinates between writer and session manager
-
StreamWatcher (
services/stream-watcher.ts
)- Uses stored pruning offsets for efficient streaming
- Performs retroactive detection on existing content
- Handles replay from pruning points
Data Flow
Benefits
- Prevents Unbounded Growth: Session files stay manageable even for long-running sessions
- Improves Performance: Clients don’t need to download/process gigabytes of old data
- Preserves User Experience: Users see current terminal state, not irrelevant history
- Automatic: Works transparently without user intervention
- Safe: Only prunes at explicit clear points, never loses important data
Testing
The pruning system includes comprehensive tests:-
Unit Tests (
test/unit/pruning-detector.test.ts
)- Sequence detection accuracy
- Byte position calculation
- UTF-8 handling
-
Integration Tests (
test/unit/asciinema-writer.test.ts
)- Real-time detection during writes
- Callback timing and accuracy
- File position validation
Debugging
To debug pruning:-
Check for pruning detection in logs:
-
Verify session info:
-
Enable debug logging to see detailed pruning calculations:
Limitations
- Requires Forwarder Restart: Pruning runs in the forwarder process, so existing sessions won’t benefit until restarted
- Clear Sequence Dependent: Only prunes when terminal is explicitly cleared
- No Manual Pruning: Currently no way to manually trigger pruning
- Single Pruning Point: Only tracks the most recent clear, not multiple checkpoints
Future Improvements
- Multiple Checkpoints: Track several pruning points for more granular history
- Time-based Pruning: Prune content older than X hours
- Size-based Pruning: Trigger pruning when file exceeds certain size
- Compression: Compress old segments instead of discarding
- Manual Pruning API: Allow users to explicitly mark pruning points
Performance Analysis: Old vs New Pruning Logic (2025-07-27)
Old Implementation (Before commit 627309ebf)
Architecture:- Pruning detection was duplicated in 3 places:
pty-manager.ts
- During data write (imprecise)stream-watcher.ts
- During playback (retroactive)- Inline sequence definitions in multiple files
- Double Processing: Data was scanned for pruning sequences twice:
- Once in pty-manager during write (but couldn’t calculate accurate positions)
- Again in stream-watcher during playback
- Inefficient String Searching: Multiple
lastIndexOf()
calls on potentially large strings - Imprecise Byte Calculations: PTY manager couldn’t track exact byte positions
- Memory Overhead: Entire file had to be re-read and parsed during playback
New Implementation (After commit 627309ebf)
Architecture:- Centralized pruning detection in
pruning-detector.ts
- Real-time detection in
asciinema-writer.ts
- Precise byte position tracking
-
Single-Pass Detection:
- Pruning sequences detected once during write
- Exact byte positions calculated and potentially stored
- No need to re-scan during playback
-
Optimized Detection:
-
Precise Byte Tracking:
Performance Comparison
Aspect | Old Logic | New Logic | Improvement |
---|---|---|---|
Detection Timing | Retroactive (on playback) | Real-time (on write) | ✅ No playback delay |
Processing Passes | 2 (write + read) | 1 (write only) | ✅ 50% reduction |
Byte Accuracy | Approximate | Exact | ✅ Precise pruning |
Memory Usage | Re-read entire file | Stream processing | ✅ Lower memory |
CPU Usage | O(n) on each client connect | O(1) lookup | ✅ Much faster |
Code Duplication | 3 implementations | 1 centralized | ✅ Maintainable |
Real-World Impact
For a session with 18MB of data (like the example log showing offset 18,223,170): Old System:- Client connects → Read 18MB file → Scan for pruning sequences → Skip 20k events
- Time: ~100-500ms depending on disk speed
- Client connects → Read pre-calculated offset → Start streaming from position
- Time: ~1-10ms
Conclusion
The new pruning logic is significantly faster because:- Eliminates redundant processing - Detection happens once, not on every playback
- Reduces I/O - No need to read/parse the entire file to find prune points
- Improves scalability - O(1) vs O(n) for client connections
- Better accuracy - Exact byte positions prevent edge cases
- Large session files (10MB+)
- Multiple concurrent viewers
- Sessions with many clear operations