tmaxx: The tmux Bridge That Makes AI Agents Deliver Messages for Real
A deliberately simple terminal transport for Claude and Codex, with transcript verification, pane discovery, and just enough structure to keep multi-agent work from drifting off the rails.
- tmaxx is interesting because it does not trust a send operation until the transcript proves the message was delivered.
- The project turns tmux into a practical transport layer by pairing pane inspection with transcript-backed verification.
- Its real innovation is not a fancy protocol, but a small loop that discovers panes, normalizes provider histories, and retries when the UI lies.
- The design is intentionally blunt, which makes it easier for humans to intervene and harder for agents to drift silently off course.
Most agent tools start with APIs, queues, or event streams. tmaxx starts with something rougher: tmux panes and terminal text. Then it adds one crucial idea. A message is not delivered just because it was sent. It is delivered when the transcript says it is there.
Why tmaxx Does Not Trust the Terminal
That skepticism is the whole project. tmaxx sends keys into a pane, then polls the durable history store to see whether the agent actually recorded the message. If the log has not caught up, it nudges the prompt and tries again. The screen is treated as a hint. The transcript is treated as evidence.
The Bridge: From Pane State to Actual Message Delivery
The low-level mechanics live in the tmux layer. The code checks whether a pane is busy, looks for buffered state, and uses a small “nudge” when terminal input is not cooperating. That makes the transport look almost primitive, but the primitive part is the point. If the interface is already messy, the tool has to be stubborn.
export function sendMessageToPane(paneId: string, message: string) {
const inspection = inspectPane(paneId)
if (inspection.paneIsWorking) {
return { ok: false, reason: 'pane busy' }
}
if (inspection.hasBufferedInput) {
nudgePromptInput(paneId)
}
tmuxSendKeys(paneId, message)
return confirmTranscriptDelivery(paneId, message)
}
The important detail is not the keystroke itself. It is the confirmation loop around it. The tool accepts that a terminal can lag, buffer, or silently absorb input. So it asks the transcript a second time before it believes anything.
How tmaxx Knows Which Agent Lives in Which Pane
Pane discovery is the glue that makes the bridge usable without a central registry. `tmaxx` inspects child processes, parses command arguments, and looks for session clues like `resume
| Method | Source of truth | Delivery guarantee | Operational overhead | Human interveneability |
|---|---|---|---|---|
| Blind send-keys workflow | Whatever the terminal looked like | None | Low at first, high when it breaks | High, but mostly manual |
| API-first orchestrator | Backend events and service contracts | Strong inside the system boundary | Moderate to high | Low unless exposed through tooling |
| tmaxx | Durable transcripts plus pane inspection | Confirmed by transcript polling | Low, because it rides on tmux and local history | High, because the terminal stays visible |
Claude and Codex, Unified Into One Message Model
The transcript layer is where the project stops being a terminal trick and becomes a messaging system. Claude history lives in JSONL files. Codex history lives in SQLite. `tmaxx` reads both through a single `readChat` path and normalizes them into the same message shape. That lets the rest of the code ask one question: did the message become durable?
type ChatMessage = {
role: 'user' | 'assistant'
content: string
sentAt?: string
}
const chat = await readChat({ provider: 'claude', sessionId })
for (const message of chat.messages) {
console.log(message.role, message.content)
}
This is a clean example of adapter design without ceremony. The stores are different. The contract is not. Once the transcript is read into a common model, delivery verification becomes provider-agnostic.
tmaxx tmaxx is a deliberately stupid tool for agents to send messages to each other through existing tmux panes.
The Envelope Pattern Makes Terminal Text Feel Like a Protocol
The project’s envelope format is the quiet move that makes the whole thing legible to agents. Raw terminal text gets wrapped with metadata like sender, recipient, and timestamp. That turns a blob of chatty output into something closer to a message contract, while still leaving the transport itself completely local and visible.
That balance matters. Too much structure and the tool becomes another protocol empire. Too little structure and the terminal becomes a pile of ambiguous noise. `tmaxx` stays on the narrow line between them.
Why This Design Is So Small and So Opinionated
The philosophy is right there in the README. It is not trying to abstract the terminal away. It is trying to use the terminal as-is, with a verification loop layered on top. That is why the design feels almost austere. It accepts the mess, then makes the mess checkable.
| Approach | What it optimizes | What it sacrifices |
|---|---|---|
| Cloud-native orchestration | Scale and formal contracts | Direct visibility into the work surface |
| Agent UI wrappers | Convenience | Trust in what actually happened |
| tmaxx | Inspectable delivery and easy intervention | Elegance in the conventional sense |





