Adapter Architecture
This page describes the runtime architecture behind gmux adapters: which component does what, how sessions are discovered, how file-backed integrations work, and how children can report status back to gmux.
Read this page if you are working on gmux internals, debugging adapter behavior, or trying to understand how a launched process becomes a live or resumable sidebar entry.
If you want the user-facing overview, see Adapters. If you want to add support for a new tool, see Writing an Adapter.
Two processes, one adapter system
Section titled “Two processes, one adapter system”Adapters are defined once in packages/adapter and used by both gmux and gmuxd.
gmuxis per-session. It launches the child, owns the PTY, injects environment variables, and interprets live output.gmuxdis per-machine. It discovers running sessions, watches adapter-owned files, and surfaces resumable sessions.
That split is why the adapter system is a set of small interfaces instead of one giant one.
Responsibility split
Section titled “Responsibility split”| Concern | Component | How |
|---|---|---|
| Adapter availability detection | gmuxd | Adapter.Discover() |
| Command matching | gmux | Adapter.Match() |
| Child env injection | gmux | Adapter.Env() |
| PTY output monitoring | gmux | Adapter.Monitor() |
| Child self-report API | gmux | Unix socket HTTP endpoints |
| Launch menu discovery | gmuxd | Launchable + compiled adapter set |
| Session file discovery | gmuxd | SessionFiler |
| Session file attribution | gmuxd | file scanner + matching |
| Live file monitoring | gmuxd | FileMonitor.ParseNewLines() |
| Resumable session discovery | gmuxd | SessionFiler + Resumer |
| Resume command generation | gmuxd | Resumer.ResumeCommand() |
Launch lifecycle
Section titled “Launch lifecycle”When you run a command through gmux:
gmuxresolves the adapterGMUX_ADAPTER=<name>override, if set- otherwise first matching registered adapter wins
- otherwise shell fallback
gmuxstarts the child under a PTYgmuxinjects the standardGMUX_*environment variablesgmuxfeeds PTY output intoAdapter.Monitor()gmuxserves the session on its Unix socket (/meta,/events, terminal attach, child callbacks)gmuxddiscovers the runner socket, queries/meta, and subscribes to/events
The command itself is never rewritten by the adapter. Adapters can add environment variables, but what the user launched is exactly what runs.
Adapter resolution
Section titled “Adapter resolution”Adapter selection happens entirely in gmux:
- Explicit override:
GMUX_ADAPTER=<name> - Registered adapters in order: first
Match()wins - Shell fallback: always matches, always last
This keeps matching cheap and predictable. A false negative is low-cost because the shell adapter still gives basic behavior.
Adapter discovery and available launchers
Section titled “Adapter discovery and available launchers”Every adapter now implements a required discovery probe:
type Adapter interface { Name() string Discover() bool Match(command []string) bool Env(ctx EnvContext) []string Monitor(output []byte) *Status}gmuxd runs Discover() for every compiled adapter in parallel during startup.
That tells gmux which adapters are actually usable on the current machine.
For the built-in adapters:
- shell always returns
true - pi runs
pi --versionand returns true only if the command succeeds
Launchers and Launchable
Section titled “Launchers and Launchable”Launch menu entries are still derived from adapter instances instead of a parallel global launcher registry.
Adapters that want to appear in the UI implement:
type Launchable interface { Launchers() []Launcher}gmuxd aggregates launchers from the compiled adapter set by checking which adapters implement Launchable, then filters that list based on each adapter’s Discover() result.
A few important consequences:
- launch menu support is optional, like other adapter capabilities
- adapter availability is mandatory, because every adapter must implement
Discover() - one adapter can expose zero, one, or many launch presets
gmuxdno longer shells out togmux adaptersto discover launchers- the shell fallback also implements
Launchable, so shell appears in the UI without a separate special-case launcher registry - unavailable launchers are omitted from the launch config entirely
The current built-in launcher ordering is simple:
- non-fallback adapters contribute launchers in adapter registration order
- shell is appended last
File-backed adapters
Section titled “File-backed adapters”Some tools write session or conversation files to disk. Those integrations use optional capabilities discovered by gmuxd.
SessionFiler
Section titled “SessionFiler”type SessionFiler interface { SessionRootDir() string SessionDir(cwd string) string ParseSessionFile(path string) (*SessionFileInfo, error)}Use this when a tool stores session state on disk and gmux should be able to discover or inspect it.
SessionRootDir()returns the root containing all per-project session directoriesSessionDir(cwd)returns the directory for one working directoryParseSessionFile(path)extracts display metadata such as ID, title, cwd, created time, and message count
FileMonitor
Section titled “FileMonitor”type FileMonitor interface { ParseNewLines(lines []string, filePath string) []FileEvent}Use this when new file content should update the live sidebar. gmuxd tracks offsets and passes only appended lines. The filePath parameter gives adapters access to the full session file for context lookups (e.g. reading preceding events).
Typical uses:
- title changes
- status updates inferred from appended records
- metadata updates from structured session logs
Resumer
Section titled “Resumer”type Resumer interface { ResumeCommand(info *SessionFileInfo) []string CanResume(path string) bool}Use this when a finished session can be resumed later.
CanResume(path)filters out invalid or empty filesResumeCommand(info)tells gmux how to resume the session when the user clicks it
File attribution and live updates
Section titled “File attribution and live updates”For adapters that implement SessionFiler, gmuxd does more than just scan files.
Session file attribution
Section titled “Session file attribution”When a tool starts writing files in a watched directory, gmuxd needs to figure out which running session owns which file. Adapters control this by implementing FileAttributor:
type FileAttributor interface { AttributeFile(filePath string, candidates []FileCandidate) string}Each candidate carries SessionID, Cwd, StartedAt, and Scrollback (recent terminal text). The adapter decides how to match:
- pi uses content similarity between the file text and terminal scrollback
- claude and codex use metadata matching (cwd + timestamp proximity from the file’s session header)
Typical flow:
- watch the adapter’s
SessionDir(cwd)via inotify - notice file creation or writes
- call the adapter’s
AttributeFileto match the file to a live session - once attributed, keep the association sticky
- track the active file per session — when a different file is attributed (e.g. the user runs
/newor/resumein the tool’s TUI),resume_keyupdates to the new file’s session ID
This is what lets gmux connect a running session to a later-created conversation file.
Live file monitoring
Section titled “Live file monitoring”After attribution, gmuxd can continue watching the file:
- read newly appended lines
- if the session still has no adapter title (common when the tool creates the file before the first user message), re-derive the title from
ParseSessionFile()on the full file - pass new lines to
ParseNewLines() - apply returned
FileEvents to the live session - publish the updates to the frontend via SSE
That is how file-backed tools can update titles or other metadata in real time even when those changes never appear in terminal output.
Resumable sessions
Section titled “Resumable sessions”For adapters that implement Resumer, sessions transition seamlessly between alive and resumable states.
Live → resumable transition
Section titled “Live → resumable transition”When a session exits, gmuxd checks whether its adapter implements Resumer and whether the session has an attributed file (identified by resume_key, set during file attribution). If so:
- the resume command is derived from the adapter’s
ResumeCommand() commandis set to the resume commandresumableis derived automatically (!alive && resume-capable kind && command present)- the session appears in the sidebar as clickable to resume — no intermediate “exited” state
File-discovered sessions
Section titled “File-discovered sessions”For adapters that implement both SessionFiler and Resumer, gmuxd also discovers sessions from files on disk (e.g. from before the daemon started):
- enumerate files under
SessionRootDir()/ knownSessionDir(cwd)directories - filter them with
CanResume(path) - parse them with
ParseSessionFile(path) - deduplicate them against live sessions by resume key
- publish them as resumable entries
When the user resumes one, gmuxd uses ResumeCommand() to launch the new live session.
For concrete examples, see Claude Code, Codex, or pi.
Child awareness protocol
Section titled “Child awareness protocol”Every child launched by gmux gets a small protocol for detecting gmux and reporting back.
Environment variables
Section titled “Environment variables”| Variable | Purpose |
|---|---|
GMUX | Simple detection flag (1) |
GMUX_SOCKET | Unix socket path for callbacks to the runner |
GMUX_SESSION_ID | Unique session identifier |
GMUX_ADAPTER | Name of the matched adapter |
GMUX_RUNNER_VERSION | Version of the gmux runner hosting the session |
Most tools ignore these. gmux-aware tools, wrappers, or hooks can use them to integrate directly.
Child-to-runner endpoints
Section titled “Child-to-runner endpoints”Served by gmux on the session socket:
| Endpoint | Method | Purpose |
|---|---|---|
/meta | GET | Read current session metadata |
/meta | PATCH | Update title and subtitle |
/status | PUT | Set or clear application status |
/events | GET | Subscribe to live state changes |
Example:
curl --unix-socket "$GMUX_SOCKET" http://localhost/status \ -X PUT -H 'Content-Type: application/json' \ -d '{"label":"building","working":true}'This is the escape hatch for tools that want native gmux integration without needing a custom PTY parser.
Status sources
Section titled “Status sources”A session’s displayed state can come from multiple places:
- process lifecycle defaults from gmux itself
- adapter PTY monitoring via
Monitor() - file-backed updates via
FileMonitor - direct child callbacks via
/statusandPATCH /meta
The important design point is that adapters do not own the whole session model. They contribute structured hints into a runner-owned session state that gmuxd then aggregates and serves.
Built-in examples
Section titled “Built-in examples”- Shell: fallback adapter; watches terminal title escape sequences and contributes the default shell launcher
- Claude Code: file-backed adapter; supports launch presets, status detection, title extraction, live file updates, and resume
- Codex: file-backed adapter with date-nested session storage; supports launch presets, status detection, title extraction, and resume
- pi: file-backed adapter; supports launch presets, status detection, title extraction, live file updates, and resume
See Adapters for the high-level overview and the Integrations section for concrete integration behavior.