agent-manager

Architecture

Agent Manager is a full terminal UI app built with Ink 5 (React 18), node-pty, and xterm-headless. This page describes the high-level architecture and how the pieces fit together.

Directory Structure

src/
├── cli.tsx                         # Entry point (Commander + Ink)
├── app.tsx                         # Root component
├── agents/
│   ├── agent-session.ts            # Session lifecycle & events
│   ├── agent-registry.ts           # Driver registry
│   ├── agent-detector.ts           # Standalone process scanner
│   └── drivers/
│       ├── base-driver.ts          # Abstract driver interface
│       ├── claude-driver.ts        # Claude Code driver
│       └── gemini-driver.ts        # Gemini CLI driver
├── connectors/
│   ├── connector-interface.ts      # Connector contract
│   ├── connector-manager.ts        # Routes events to connectors
│   └── adapters/
│       ├── slack-adapter.ts        # Slack (Web API + Socket Mode)
│       ├── discord-adapter.ts      # Discord (discord.js)
│       └── telegram-adapter.ts     # Telegram (Telegraf)
├── terminal/
│   ├── pty-manager.ts              # node-pty wrapper
│   ├── xterm-bridge.ts            # xterm-headless rendering
│   └── screen-buffer.ts           # FPS-limited display buffer
├── hooks/
│   ├── hook-server.ts              # Unix socket IPC server
│   ├── hook.ts                     # PermissionRequest handler
│   ├── post-tool-hook.ts           # PostToolUse handler
│   ├── notification-hook.ts        # Notification handler
│   ├── stop-hook.ts                # Stop event handler
│   └── install.ts                  # Hook installer/uninstaller
├── portal/
│   ├── shared.ts                   # Protocol types (client ↔ server)
│   ├── portal-server.ts            # Unix socket server (main app)
│   ├── portal-client.ts            # IPC client functions
│   ├── portal-picker.tsx           # Interactive session picker
│   └── ansi-renderer.ts            # ScreenContent → ANSI converter
├── store/
│   ├── app-store.ts                # Session state (Zustand)
│   ├── settings-store.ts           # Persisted config (Zustand)
│   ├── connector-store.ts          # Connector lifecycle & bridges
│   └── portal-store.ts             # Portal connection tracking
├── config/
│   ├── config.ts                   # Zod schema & load/save
│   └── defaults.ts                 # Default configuration
└── ui/
    ├── layout/
    │   ├── shell.tsx               # Main layout container
    │   ├── sidebar.tsx             # Session list panel
    │   ├── main-pane.tsx           # Terminal + header
    │   └── status-bar.tsx          # Bottom status indicators
    ├── components/
    │   ├── terminal-view.tsx       # xterm grid renderer
    │   ├── agent-tab.tsx           # Session tab
    │   ├── new-session-dialog.tsx  # Create session dialog
    │   ├── adopt-agent-dialog.tsx  # Adopt agent dialog
    │   ├── standalone-agents.tsx   # Detected agents list
    │   ├── settings-panel.tsx      # Settings UI
    │   ├── agents-settings-content.tsx  # Agent install/update UI
    │   ├── help-modal.tsx          # Keybindings reference
    │   └── path-input.tsx          # Directory autocomplete
    └── hooks/
        ├── use-keybindings.ts      # Keyboard shortcut handler
        ├── use-raw-input.ts        # Raw stdin forwarding
        ├── use-terminal-size.ts    # Terminal dimensions
        ├── use-spinner.ts          # Cycling animation hook
        ├── use-git-info.ts         # Git branch/status polling
        └── use-portal-connected.ts # Portal connection state
bash

How It Works

  1. CLI entry (cli.tsx) sets up the alternate screen buffer, mouse tracking, and renders the Ink app. The portal subcommand launches the interactive picker or connects to a session directly.
  2. App (app.tsx) initializes stores, starts connectors and the portal server, and renders the shell layout.
  3. Sessions are managed by AppStore — each session owns a PtyManager (node-pty), XtermBridge (headless terminal emulation), and ScreenBuffer (FPS-limited rendering).
  4. Keyboard input is captured by useRawInput and forwarded to the active session's PTY, except when intercepted by useKeybindings for shortcuts.
  5. Portal server (portal-server.ts) listens on a Unix socket (/tmp/agent-manager/portal.sock). Portal clients subscribe to sessions, receive throttled screen frames, and forward keystrokes — providing full remote access without disrupting the main UI.
  6. Hooks are installed as Claude Code lifecycle hooks that send events to the manager's IPC server, which routes them through ConnectorManager to the configured messaging adapters.
  7. State flows through Zustand stores with selector-based subscriptions for efficient re-renders.

Key Components

Agents

The agent layer uses a driver abstraction. BaseDriver defines the interface that all agent drivers implement: spawn(), resume(), continue(), and detectStatus(). AgentSession wraps a driver instance with lifecycle management and event emission. AgentDetector scans for standalone processes that can be adopted.

Terminal

Each session gets a PtyManager (node-pty pseudoterminal), XtermBridge (xterm-headless for ANSI parsing and terminal state), and ScreenBuffer (FPS-limited rendering at 30fps default). The screen buffer converts xterm cell data to styled characters for Ink rendering.

Connectors

ConnectorManager routes events to all enabled adapters. Each adapter implements ConnectorInterface with methods for permission requests, tool results, and notifications. Dependencies are optional — only load what's installed.

Hooks

HookServer runs a Unix socket IPC server. Claude Code hooks send events as JSON messages. Permission requests block until a connector reply arrives or timeout.

Portal

PortalServer listens on another Unix socket. Clients can list sessions, subscribe for screen frames, forward input, and create new sessions. Frames are throttled to avoid flooding.

Store

State is managed with Zustand. AppStore holds session state, SettingsStore persists config, ConnectorStore manages connector lifecycles, and PortalStore tracks portal connections. Selector-based subscriptions minimize re-renders.

UI

Built with Ink 5 (React for the terminal). The shell layout is responsive: wide terminals get a sidebar with session list, narrow terminals get a compact tab bar. Components use React hooks for keybindings, raw input, terminal size tracking, and animations.