Skip to main content

PTY-for-AI

A pattern for controlling AI CLI tools at the terminal level, enabling cross-LLM orchestration without provider-specific APIs.

The Problem

Every AI coding CLI (Claude Code, Codex, Gemini CLI, Aider) communicates with its agents through structured I/O — JSON pipes, API calls, or protocol messages. This creates walls:
  • Subagents can only call tools, not run interactive terminal sessions
  • One AI cannot control another AI at the terminal level
  • Multi-agent teams are locked to a single provider
  • Cross-LLM collaboration requires per-pair plugins (N² problem)

The Pattern

Instead of structured I/O between agents, use PTY sessions — real terminal instances that any CLI can run in.
Structured I/O (how everyone else does it):
  Parent AI → spawn child → stdin(JSON) → stdout(JSON) → parse
  = Pipe. Not a terminal. Not interactive.
  = Child can call tools but cannot run another CLI.

PTY-for-AI (what NIIA does):
  Daemon → PTY session → real terminal
  = Anything that runs in a terminal is controllable.
  = Interactive read/write. Any CLI. Any LLM.

How It Works

1. Daemon manages PTY sessions

A persistent daemon (launchd/systemd) owns PTY sessions. Sessions survive terminal closures, reboots, and SSH disconnects.

2. Headless server bridges to network

A headless server connects PTY sessions to a gateway, enabling remote access without a visible terminal window.

3. CLI commands control sessions

# Write to a PTY session (type into terminal)
niia write --session <ID> $'claude\r'

# Wait for output to settle
niia wait-idle --session <ID>

# Read the answer
niia get-answer --session <ID> "your question"

4. Any CLI is a “worker”

# Claude as worker
niia write --session S1 $'claude\r'

# Gemini as worker
niia write --session S2 $'gemini\r'

# Codex as worker
niia write --session S3 $'codex\r'

# SSH into a server
niia write --session S4 $'ssh production\r'
The daemon doesn’t know or care what’s running inside. PTY-level control is LLM-agnostic by design.

Why PTY, Not Pipe

Structured I/O (pipe)PTY-for-AI
ProtocolJSON/NDJSONTerminal I/O
InteractiveNoYes
TargetSame LLM onlyAny CLI
Cross-LLMPlugin per pair (N²)One interface (N)
Provider lock-inYesNo
Heterogeneous teamsNoYes
Cost routingModel selection onlyLLM-level routing
FailoverNot possibleSwitch PTY session
OfflineNot possibleLocal LLM in PTY

Session Plugins

PTY sessions are configured through daemon-external plugins. The daemon binary is never modified — plugins transform session parameters before spawn.
PluginWhat it doesHow
worktreeGit-isolated workspaceChanges cwd to worktree path
sandboxOS-level process isolationWraps shell with sandbox-exec
scratchpadShared knowledge directoryCreates shared dir in workspace
envEnvironment injectionSets env vars on session
timeoutSession lifetime limitWraps shell with timeout
Plugins compose. Order matters: worktree runs before sandbox, so sandbox restricts writes to the worktree path.
# Worktree + sandbox in one command
niia serve --worktree feat-x --sandbox

Architecture

┌──────────────────────────────────────────────────┐
│  niia CLI (plugin layer)                         │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐          │
│  │worktree  │ │ sandbox  │ │scratchpad│  ...     │
│  └────┬─────┘ └────┬─────┘ └──────────┘          │
│       │            │                             │
│       ▼            ▼                             │
│  CreateSession { cwd: wt_path, shell: sb_cmd }   │
├───────────────────┬──────────────────────────────┤
│  PTY daemon       │  Just spawn. Unchanged.      │
├───────────────────┼──────────────────────────────┤
│  Headless server  │  Bridge to network.          │
├───────────────────┼──────────────────────────────┤
│  Gateway / P2P    │  Remote access.              │
└───────────────────┘──────────────────────────────┘
The daemon protocol already supports cwd and shell fields. Plugins work by transforming these fields before the daemon sees them. No daemon modification needed — ever.

Cross-Machine

PTY-for-AI extends across machines through gateway relay or P2P:
# Control AI on a remote machine
niia remote write MACHINE-B "claude\r"
niia remote read MACHINE-B

# Remote machine's PTY sessions are accessible
# as if they were local
No VPN required. Works through NAT. Encrypted end-to-end.

Comparison with Existing Approaches

Claude Code Teams

Spawns Claude CLI instances in tmux/iTerm2 panes. Limited to Claude only. Requires visible terminal. Single machine.

Codex Plugin for Claude Code

Subprocess JSON-RPC to Codex server. One direction (Codex → Claude). Requires both installed. Single machine.

claude-squad

Tmux session manager for multiple AI CLIs. Requires tmux. No headless. No remote. No daemon persistence.

Warp

Terminal replacement with multi-agent UI. Requires Warp app. No headless. No cross-machine. No daemon.

PTY-for-AI (NIIA)

Daemon-managed headless PTY sessions. Any CLI. Any LLM. Cross-machine. Persistent. Plugin-extensible.

The Declarative Layer: connector.json

PTY-for-AI is the execution layer. connector.json is the declarative spec that sits on top. connector.json describes which agents run, in what topology (pipeline, dialogue, meeting, mesh, recursive), on which machines, with what isolation. The runtime reads connector.json and translates it into PTY daemon calls.
niia run workflow.connector.json
# → reads spec → creates PTY sessions → applies plugins → runs pipeline
See Introducing connector.json for the full spec.

Design Principles

  1. PTY, not pipe — terminal-level control, not structured I/O
  2. Daemon, not process — persistent, survives everything
  3. Headless, not visual — no terminal window needed
  4. Plugins, not patches — extend without modifying daemon
  5. LLM-agnostic — if it runs in a terminal, it’s controllable
  6. Infrastructure, not prompts — orchestration in daemon, not in system prompts

PTY-for-AI is an open spec by OpenCLIs. Runtime implementation by NIIA (Monolex). Spec: pty4ai.com