amux - Agent modified tmux
Overview
amux (Agent-Modified tmux) is a custom build of tmux designed to provide clean, robust bidirectional programmatic control for AI CLI agents and automated scripting.
Standard tmux requires emulating physical keystrokes (send-keys) and screen-scraping the visual terminal grid (capture-pane). This introduces significant failure modes:
- Quoting and Escaping Hell: Nested quotes, shell variables, subshells, and escapes are easily mangled when passed across nested SSH sessions or bash wrappers.
- Terminal Echo & Input Race Conditions: Rapid key injection can race with shell readline redraws, bracketed paste triggers, or auto-suggestions.
- ANSI & Layout Pollution: Screen scraping mixes prompt redraws, color escape codes, truncated lines at terminal column boundaries, and terminal dimensions with actual command output.
- No Command Completion or Busy-State Visibility: Standard tmux cannot distinguish a pane running a command from a pane sitting idle at a prompt without polling
capture-paneor guessing viamonitor-silence.
amux provides symmetric, full-duplex programmatic control:
- Zero-Escape Writing (
write-pane -f): Direct PTY stream injection bypassing keystroke emulation and shellescaping.escaping, with immediatePANE_CMDRUNNINGstate pre-marking on execution (-eor trailing\n). - Direct Stream Reading & Synchronous Waiting (
read-pane): Direct PTY output buffer capture (wp->stream_buf) bypassing 2D screen grid layout truncation and ANSIpollution.pollution, with deterministic OSC 133 /tcgetpgrpcommand completion waiting (-w), state inspection (-S), and exit-code propagation (-e/-E).
Architecture & Session Hierarchy
The tmux sessions on the host are organized into clean layers managed by /home/luke/bin/tmux_control:
+-------------------------------------------------------+
| mtx / mx: Outer Master Session |
| Socket: /tmp/tmux-1000/tmux_luke_mtx |
| Config: ~/.tmux.conf_outer |
| |
| +-----------------------------------------------+ |
| | stx / sx: User Interactive Session | |
| | Socket: /tmp/tmux-1000/tmux_luke_stx | |
| | Config: ~/.tmux.conf_inner | |
| +-----------------------------------------------+ |
| |
| +-----------------------------------------------+ |
| | atx / ax: Agent Session (amux) | |
| | Socket: /tmp/tmux-1000/tmux_luke_atx | |
| | Config: ~/.tmux.conf_agent | |
| +-----------------------------------------------+ |
+-------------------------------------------------------+
atx [session]: Attaches to or creates the agent session insidemtx. Styled with a high-visibility orange status bar (#ff8800),[AGENT]badge, live per-window[RUN]and[ERR:<code>]badges, andC-bprefix key (matching inner session convention without colliding with outermtxC-a).ax <command>: Runs commands directly on the agent socket (e.g.ax list-panes,ax write-pane ...,ax read-pane ...).- Binary: The custom binary is built from
/home/luke/git/amux(/home/luke/git/tmux) and installed at/usr/local/bin/tmux-custom. The system tmux (/usr/bin/tmux) remains unmodified as a fallback.
Persistent Agent Environment & OSC 133 Shell Integration
When the agent session spawns, it automatically sources ~/.agent_profile:
- Persistent Environment Variables:
AGENT_SESSION=1ROUTER=danknastyROUTER_REPO=/home/luke/git/router_configMC_DIR=/data/minecraftMC_ROUTER_V4=10.0.0.113MC_ROUTER_V6=fd77:7777:5010::1337:f1ac:d00d:3cMC_BACKEND_PREFIX=fd77:7777:5010::1337:3c:d00d:
- Persistent Helper Functions:
rcfg: Git wrapper for router config (git -C $ROUTER_REPO)rssh: Direct SSH to danknastyrdo <cmd>: Direct non-interactive execution on routermipctl: Shortcut for/data/minecraft/ipctl
PS0 emits \033]133;C;k=1\007 immediately before command execution begins.
PROMPT_COMMAND captures the command's exit status $? (__amux_osc133_pre) before history syncing and emits \033]133;D;<exit_code>\007\033]133;A\007 (__amux_osc133_post) right before rendering PS1 (\033]133;B\007).
Window Naming & Task Lifecycle Methodology
To maintain strict isolation between tasks, prevent command collisions, and keep workspaces clean, windows in atx follow a structured naming convention:
<project_or_domain>-<target>-<task_suffix>
Naming Pattern Examples
mc-pokelovers-bluemap: BlueMap setup or rendering for pokeloversmc-pokelovers-datapacks: Managing datapacks and gameplay tweaksmc-pokelovers-console: Direct interactive Minecraft server consolertr-dns-sync: Router DNS record and deSEC synchronizationrtr-pf-rules: Firewall rule adjustments and testingbld-amux-stream: Compiling and patching tmux C source codebkp-verify-daily: Backup execution and PAR2 verification
Window Lifecycle Protocol
-
Window Creation (Task Start): Before running multi-step commands for a task, verify if the dedicated window exists; if not, spawn it:
ax list-windows -F '#{window_name}' | grep -q '^mc-pokelovers-bluemap$' || \ ax new-window -n "mc-pokelovers-bluemap" "bash --rcfile ~/.agent_profile" -
Scoped Execution via File Injection: Write the script or commands to a file on
/dev/shm,clearflushanythelingeringoutput buffer, and injectit, and deleteinto thetemporarytargetfile:window usingwrite-pane -f:ax read-pane -C -t "mc-pokelovers-bluemap" ax write-pane -t "mc-pokelovers-bluemap" -e -f /dev/shm/task_payload.task.sh rm -f /dev/shm/task_payload.task.sh -
CleanSynchronous OutputReading:Retrieval & Exit Verification:ReadWait for the command orstreammulti-line script to finish, isolate theexact output directly from the queue without screen scraping:# ReadcleanoutputOSCand133 output, drain thequeuequeue, and propagate the exit code:ax read-pane -w -O -c -E -t "mc-pokelovers-bluemap" -
Status Renaming: If a task transitions to a persistent state (e.g. watching logs, running tests), update the title:
ax rename-window -t "mc-pokelovers-bluemap" "mc-pokelovers-watch" -
Task Completion Confirmation & Cleanup Rule:
- When a task or subtask is complete, the outcome must be explicitly verified (service status, connectivity, file state).
- Once verified, promptly close the dedicated task window:
ax kill-window -t "mc-pokelovers-bluemap" - No completed task windows are left lingering in the agent session. Confirm to the user that the task is finished and that its task window has been torn down.
Built-In Commands
write-pane (alias: writep)
Injects raw binary data, files, or Base64 payloads directly into the target pane's PTY input buffer. It completely bypasses terminal keystroke decoding and shell quoting.quoting, records the current stream buffer mark (wp->stream_mark), and immediately pre-marks the pane as running (PANE_CMDRUNNING) when newline execution (-e or trailing \n) is triggered.
tmux write-pane [-en] [-f file] [-b base64-data] [-t target-pane] [data ...]
Flags:
-f <file>: Reads the given file from disk and streams its raw bytes directly into the pane's input buffer. (Recommended Standard)-e: Automatically appends a newline (\n/ Enter) to trigger immediate execution.-b <base64-data>: Decodes a base64 payload in C (b64_pton) and writes raw bytes to the PTY.-n: Do not separate multiple positional arguments with spaces.-t <target-pane>: Specifies the target session, window, or pane (e.g.-t mc-pokelovers-bluemap).
read-pane (alias: readp)
Reads raw or filtered output directly from the target pane's 16 MB in-memory stream buffer (wp->stream_buf), bypassingqueries 2Dcommand execution state/exit status, or synchronously waits for command completion without screen grid scraping entirely. Output is unaffected by terminal column width, pane geometry, or scrollback limits.polling.
tmux read-pane [-cClOprs]cCeElOprsSw] [-b buffer-name] [-f file] [-o file] [-T timeout] [-t target-pane]
Flags:
- Default (Clean Mode): Automatically strips ANSI / VT100 CSI, OSC, APC/title, and charset escape sequences, removes bracketed paste
triggers (\033[?2004h/l),triggers, and normalizes\r\nlinebreaks to standard\n.
-w (wait for completion): Suspends the client until all active/queued commands in the target pane finish (OSC 133;D / 133;A with a 25 ms multi-line settle window, or PTY foreground process group completion) before reading.
-T <seconds> (timeout): Maximum seconds to wait when -w is specified; exits with return code 124 on timeout.
-O (OSC 133 isolation): Semantically extracts command output blocks between OSC 133;C (output start) and OSC 133;D (output end) produced since the last write-pane (or buffer drain), completely eliminating prompt redraws and command echo across both single-line and multi-line script injections.
-c (consume): Drains read bytes from the pane buffer (FIFO behavior). Repeated calls return only new output produced since the last read.
-C (clear/flush): Immediately flushes the pane output buffer without reading (ideal before running a new command).
-S (status summary): Prints a single-line structured state summary without modifying the stream buffer:
state=<idle|running|dead|unknown> exit_code=<N|-> duration=<N> unread_bytes=<N> command=<cmd>
-e (exit code): Prints the numeric exit status (0..255, or -1 if running/unknown) of the last completed OSC 133 command.
-E (propagate exit code): Propagates a non-zero command exit status from the target pane directly to the read-pane client exit status ($?).
-l (length): Returns the current number of queued bytes in the pane buffer without draining.
-r (raw): Bypasses ANSI stripping and outputs the raw, unmodified PTY byte stream.
-s (strip): Explicitly strip ANSI escape codes (default behavior).
-OCD-o <file> / -f <file>: Streams buffer content directly to a file on disk.
-b <buffer-name>: Stores captured content into a tmux paste buffer.
-t <target-pane>: Target pane specification.
Format Variables & Status Bar Integration
amux exposes pane command state and buffer metrics as native tmux format variables usable in list-panes -F, display-message -p, and status lines:
#{pane_command_state}
running, idle, dead, or unknown (combines OSC 133 and PTY tcgetpgrp tracking)
#{pane_last_exit_code}
Exit code (0..255) of the last completed OSC 133 command (empty if none)
#{pane_stream_bytes}
Unread byte count currently queued in wp->stream_buf
#{pane_command_running}
1 if an OSC 133 command is running, 0 otherwise
#{pane_command_status}
Exit status from most recent OSC 133 command
#{pane_command_duration}
Elapsed duration in seconds of the current or most recent OSC 133 command
Inspecting All Agent Panes at Once
ax list-panes -a -F '#{window_name} (#{pane_id}): state=#{pane_command_state} exit=#{pane_last_exit_code} duration=#{pane_command_duration}s unread=#{pane_stream_bytes}B'
In ~/.tmux.conf_agent, window titles in the orange [AGENT] status bar automatically display [RUN] while a command is executing and [ERR:<code>] if the last command exited with a non-zero code.
Standard Workflow: Synchronous Zero-Escape Bidirectional Automation
Step 1: Write andScript Flushto /dev/shm
#cat File:> /dev/shm/script.pyjob.sh << 'SCRIPT'
python3 - << 'PY'
import json
importjson, os
data = print(json.dumps({
"user": os.getenv("USER"),
"paths": ["/data/minecraft", "/home/luke/git"],
"quote_test": 'Double "quotes" and single \'quotes\' work without escapes!'
}
print(json.dumps(data,, indent=2))
PY
SCRIPT
Step 2: FlushFlush, BufferInject, &Wait, Injectand Read Clean Output
# Flush previous output from buffer
ax read-pane -C -t mc-pokelovers-datapacks
# Inject script
ax write-pane -t mc-pokelovers-datapacks -e -f /dev/shm/script.pyjob.sh
rm -f /dev/shm/script.pyjob.sh
Step 3: Read Clean Queued Output
# ReadBlocks outputuntil cleanlycompletion, withoutextracts screenclean scrapingOSC or133 line-wrappingoutput, distortion:drains buffer, and propagates exit code
ax read-pane -w -O -c -E -t mc-pokelovers-datapacks
Structured Audit Logging
Both write-pane and read-pane integrate with systemd journal for structured security and execution logging:
- Injected scripts are archived to
/data/amux/scripts/<timestamp>_<window>.sh. - Audit events are sent to the systemd journal under identifier
amux(includingAMUX_PANE_ID,AMUX_WINDOW_NAME,AMUX_READ_SIZE,AMUX_CONSUMED,AMUX_CMD_STATE, andAMUX_EXIT_CODE):journalctl -t amux -n 20 --no-pager
Rebuilding & Updating amux
The source tree is maintained at /home/luke/git/amux (/home/luke/git/tmux):
cd /home/luke/git/tmuxamux
make -j$j"$(nproc)"
sudo install -m 755 tmux /usr/local/bin/tmux-custom
Existing sessions can continue running undisturbed; new sessions or restarts of tmux_control (atx, stx, mtx) automatically pick up the updated binary.