How Munder Difflin's PtyManager Handles PTY Processes for Agents
Munder Difflin's PtyManager runs every agent inside an isolated pseudo-terminal (PTY), managing the full lifecycle from cross-platform command resolution to graceful termination while routing all terminal output exclusively to the owning window.
The PtyManager class in the chaitanyagiri/munder-difflin repository abstracts the complexity of agent execution by providing a centralized system for spawning, monitoring, and terminating PTY sessions. Located in src/main/pty.ts, this implementation ensures that each agent operates within its own isolated terminal environment while maintaining strict ownership boundaries between application windows.
PTY Session Architecture and Owner Isolation
Session Tracking with Map Storage
The manager maintains active sessions using a private Map structure keyed by unique session identifiers. Each entry stores the node-pty process instance, current working directory, original command, timestamps, and a reference to the owning Electron WebContents.
As implemented at lines 4-5 in src/main/pty.ts:
private sessions = new Map<string, PtySession>();
This design allows O(1) lookup for session management operations including data routing, resizing, and termination.
Secure Output Routing to Window Owners
When a floor (window) spawns a PTY, the manager records the WebContents instance as the session's owner. All terminal output and exit events are sent exclusively to this owner using the safeSend method, preventing data leakage between different agent windows.
The routing mechanism uses namespaced IPC channels:
pty:data:<id>for terminal output streamspty:exit:<id>for process termination signals
This implementation at lines 41-60 ensures that owner: WebContents | null remains the sole recipient of all session events, maintaining strict isolation between concurrent agent operations.
Cross-Platform Command Resolution and Windows Compatibility
PATH Resolution and Caching
Before spawning any process, the manager resolves bare commands (e.g., claude) against the user's PATH environment. Successful lookups are cached in the resolvedCommands map to avoid expensive shell invocations on subsequent spawns.
The resolveCommand method at lines 81-88 performs this resolution using the interactive shell environment captured from src/main/shellEnv.ts, ensuring accurate PATH context even when the application launches from a non-interactive parent process.
Windows Shim Decoding and Fallback Handling
On Windows systems, the manager handles .cmd and .bat npm shims that cannot execute directly. The resolveWindowsShimSpawn method attempts to decode npm-style shims using parseNpmCmdShim; when successful, it spawns the real interpreter with the preserved argument array, maintaining support for multi-line data (the Hive protocol).
If shim decoding fails, the system falls back to cmd.exe /d /s /c "<command>" via buildCmdCommandLine (lines 13-25), though this warns that multi-line arguments may be truncated. This dual-path approach at lines 68-84 ensures robust cross-platform compatibility while optimizing for the common npm-based tool installation pattern.
PTY Spawning and Real-Time Data Flow
Spawning Process with Environment Capture
The spawn method at lines 25-70 creates new PTY instances using the resolved executable and appropriate arguments. Environment variables include the captured interactive shell PATH, locale settings, and any agent-specific variables passed via opts.env.
ptyMgr.spawn(
{
id: 'agent-123',
cwd: '/home/user/project',
command: 'claude',
args: ['--interactive'],
env: { HIVE_ROOT: '/path/to/hive' }
},
mainWindow.webContents
);
Output Streaming and Idle Tracking
Once spawned, the PTY's onData callback (lines 88-95) forwards terminal output to the owner window via safeSend. The manager simultaneously updates the lastOutputAt timestamp for each data event, enabling idle detection through the idleFor API.
ptyMgr.write('agent-123', 'ls -la\n');
This real-time streaming architecture ensures low-latency terminal interaction while providing the metadata necessary for handshake and keepalive logic.
Lifecycle Management and Termination
Graceful Exit Handling
When a PTY exits naturally, the onExit handler at lines 97-105 emits the pty:exit:<id> event to the owner, removes the session from the active Map, and invokes any registered exitHandler. This callback enables higher-level cleanup such as archiving worktrees or removing git worktrees, ensuring consistent resource management regardless of how the process terminated.
Explicit Kill Operations and Bulk Cleanup
For explicit termination, the kill method at lines 50-62 sends SIGKILL to the child process and invokes ensureKilled from src/main/procKill.ts to guarantee complete process group removal. The killAll method (lines 87-101) disables the exit handler during application shutdown to prevent redundant teardown operations, then iterates through all active sessions.
// Clean up when a floor closes
ptyMgr.killByOwner(floorWindow.webContents);
Utility APIs for Session Monitoring
The manager exposes several diagnostic and control methods at lines 64-78:
list()returns all active session metadatalastOutputAt(id)andidleFor(id)provide activity timestampswrite(id, data)sends input to the PTY stdinresize(id, cols, rows)updates terminal dimensions
console.log(ptyMgr.list());
Summary
- Map-based session tracking uses unique IDs for O(1) lookup of PTY instances and metadata at lines 4-5.
- Owner-specific routing via
WebContentsreferences andsafeSendprevents cross-window data leakage. - Cross-platform command resolution includes PATH caching and Windows shim decoding with cmd.exe fallback.
- Real-time data streaming captures output through
onDatacallbacks while tracking idle states via timestamps. - Robust termination combines graceful exit handlers with
ensureKilledfromsrc/main/procKill.tsfor guaranteed cleanup.
Frequently Asked Questions
How does PtyManager ensure PTY output is isolated to specific windows?
The manager stores an owner: WebContents reference for each session at lines 41-60 and routes all pty:data:<id> and pty:exit:<id> events exclusively through the safeSend method to that owner. This prevents terminal output from one agent floor appearing in another window.
What happens when PtyManager encounters a Windows .cmd or .bat shim?
The system attempts to decode npm-style shims using parseNpmCmdShim within resolveWindowsShimSpawn (lines 68-84). If successful, it spawns the real interpreter with preserved arguments; otherwise, it falls back to cmd.exe /d /s /c (lines 13-25) with a warning that multi-line arguments may be truncated.
How does the manager handle PTY cleanup when the application shuts down?
killAll() disables the exit handler to prevent redundant archiving work, then terminates all active sessions. Individual kill() operations use ensureKilled from src/main/procKill.ts to guarantee process group removal, ensuring no zombie agents persist after application closure.
Can PTY sessions be monitored for idle state?
Yes. The manager updates lastOutputAt timestamps during every onData event (lines 88-95) and exposes idleFor(id) and lastOutputAt(id) methods at lines 64-78, allowing the application to detect inactive agents and trigger keepalive handshakes or timeouts accordingly.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →