Rex splits terminal UI from PTY supervision. The client renders; the daemon owns child processes and disk.
flowchart LR
subgraph client [rex]
CLI[CLI subcommands]
TUI[Bubble Tea board]
end
subgraph daemon [rex-daemon]
Server[UDS server]
Store[state.Store]
PTY[PTY supervisors]
end
subgraph agents [Agent CLIs]
Claude[claude]
Others[codex gemini ...]
end
client -->|JSONL UDS| Server
Server --> Store
Server --> PTY
PTY --> agents
Store --> Disk[(meta.json transcript.log)]
| Component | Path | Role |
|---|---|---|
| CLI | internal/surface/cli/ |
Subcommands, selectors, exit codes |
| TUI | internal/surface/tui/ |
Board, wizard, attach overlay |
| Client | internal/wire/client/ |
Socket dial, intents, events |
| Server | internal/daemon/server/ |
Intent handlers, fan-out |
| Store | internal/daemon/state/ |
In-memory sessions, persistence |
| PTY | internal/daemon/pty/ |
One child per session |
| Adapter | internal/daemon/adapter/ |
PTY output → state |
| Registry | internal/catalog/registry/ |
Tool definitions |
| Settings | internal/catalog/settings/ |
config.yaml registry |
- User runs
rex newor the TUI wizard →NewSessionintent. - Server checks concurrency cap (
TryAcquireSession). - Server creates
state.Session, resolvesregistry.Toolcommand line. pty.Supervisorstarts the child with model args and optional effort.- Adapter reads output → updates
Session.State→ store broadcastsSessionUpdated. - Optional summarizer enqueues session id → patches
descriptionvia Ollama.
rex attach <sel>or TUIenter→Subscribewithreplay.- Daemon sends transcript tail (up to 256 KiB) then live
SessionOutput. - Client stdin →
SendInput/Replyon the supervisor input channel. - Detach (Ctrl+]) stops forwarding; child keeps running.
| Decision | Rationale |
|---|---|
| Two binaries, UDS | TUI can exit without killing agents; multiple clients; scriptable CLI |
| State from adapters | Works for any CLI agent; no vendor HTTP APIs |
| Raw transcript log | Faithful replay and debugging |
| Crash on daemon restart | OS children are gone; persisted live states → crashed |
| Single settings registry | One edit point for TUI, CLI, YAML |
| Optional summarizer | Descriptions must not block core kanban |
| Silent audio fallback | Headless and SSH environments still run the board |
Detail: agents/DECISIONS.md.
- One JSON object per line on the socket;
vmust equal1. Hellogates event delivery to that connection.[]byteon the wire is base64-encoded.- Transcripts append raw PTY bytes; classification uses sanitized views.
- Daemon restart remaps
queued/working/needs_inputtocrashed. - Concurrency cap acquired at spawn; released on session end.
- SIGHUP reloads the tool registry only; running PTYs unchanged.
Deletestops and removes;Completesignals done without removal.
| Extend | Mechanism |
|---|---|
| New agent CLI | tools.yaml + adapter detect block |
| User settings | internal/catalog/settings/registry.go |
| Daemon hooks | ~/.config/rex/init.lua (in-process; trusted) |
| Wire protocol | Bump ProtocolVersion and coordinate client + server |
Do not extend the wire protocol without a version bump. OpenSession and Shutdown intents exist in constants but are not implemented.
- STRUCTURE.md — repository layout
- protocol.md — JSONL reference
- paths.md — files and environment
- agents/ARCHITECTURE.md — dependency graph for agents