# tmux-amq Tmux Agentic Message Queueing (`tamq`) provides repository-aware tmux sessions and a local, durable message queue. It does not assume who or what uses a pane. The local alpha provides tmux endpoint lifecycle, local SQLite history and leases, direct `@repo:` routing, JSONL export/replay, and a Unix-socket protocol for a later coordination-engine adapter. ## Install and start a local session Prerequisites must be available on `PATH`: Python 3.11+, [uv](https://docs.astral.sh/uv/), tmux, and gita. Each repository must already have an exact gita slug and path. Install or upgrade `tamq` as an isolated user tool: ```bash make install command -v tamq tamq --version ``` Start an attached two-repository shell session: ```bash tamq flex-auth audit-core ``` This creates or reuses the managed `tamq` session, opens same-named windows in the exact gita paths, leaves tmux's ordinary interactive shells untouched, and attaches to the first window. The explicit form is equivalent: ```bash tamq start flex-auth audit-core ``` Set up the session without attaching, then attach later: ```bash tamq start --detach flex-auth audit-core tamq status tamq attach ``` No initial command runs unless requested. To run one explicitly in each newly created window, use `--command`; tamq runs exactly that command and makes no assumption about its purpose: ```bash tamq --command codex flex-auth audit-core tamq start --command 'htop --tree' flex-auth audit-core ``` `--cmd` remains an alias for `--command`. Repeated starts reuse existing windows and never run another initial command in them. Select an endpoint delivery mode at startup. `output` is the safe default; `inbox` keeps messages durable without displaying them: ```bash tamq --mode output flex-auth audit-core tamq --mode inbox flex-auth audit-core ``` `--no-display` remains a compatibility alias for `--mode inbox`. Experimental `pushy` mode submits each routed message to the target pane as input. It is intended for coding-agent interfaces that queue user prompts: ```bash tamq --mode pushy --command codex flex-auth audit-core ``` For newly created windows with an explicit command, pushy mode automatically places that command behind tamq's transparent PTY tap. Inside the coding-agent input, use `#repo:` to avoid interfaces that reserve `@` for file mentions or templates: ```text #audit-core: Please review the authentication change. ``` The tap observes this complete line and queues the same durable route as `@audit-core: ...`; it forwards the source input unchanged to its occupant. `#repo:` is therefore an agent/tap convention, not a shell command—at an ordinary shell it remains a comment. Existing windows are never respawned, so recreate a session that was originally started without pushy observation. Terminal protocol replies and local backspace editing are removed only from tamq's observation copy, so strict routing remains reliable in TUIs such as Codex while the wrapped program still receives the original byte stream. The submitted target line is sanitized, sender-labelled, deliberately non-routable, and then followed by exactly one Enter key: ```text # from flex-auth: please review the auth boundary [m-...] ``` The `# from` shape cannot match the outbound `#repo:` grammar, preventing a tapped recipient from bouncing the message back. Its leading `#` also makes an empty ordinary shell prompt treat the line as a comment. Pushy mode cannot determine whether a pane is an agent, a shell, or whether someone is already editing input: it can append to that input and submit the combined line. Use it only for panes whose occupant is known to accept or queue asynchronous prompts. Switching an existing endpoint to pushy may also submit pending messages that have never been displayed. Pushy startup requires a capability for this non-routable framing and restarts an older broker that only advertised generic pushy input. As a second circuit breaker, the tap refuses a legacy `#sender: body [message-id]` line when that identifier belongs to the corresponding durable inbound delivery. This keeps mixed-version local processes from reflecting a message between panes. ## Exchange messages manually Each managed shell exports its own repository slug as `TAMQ_REPO`. From the `flex-auth` window, queue a message using the repository command installed for the session: ```bash @audit-core: please review the auth boundary ``` The spelling without the trailing colon is equivalent: ```bash @audit-core please review the auth boundary ``` Run bare `@` to open the inline composer. It shows the latest sender as the recipient, and ordinary prose is read directly rather than parsed by the shell: ```text $ @ @audit-core: What's up? ``` Press Tab to cycle through the other repository windows while preserving the draft, Enter to send, or Ctrl-C to cancel without queuing. The initial recipient is the sender of the latest durable inbound message, including an already acknowledged message; without history, it is the first peer window. Self-addressed messages are ignored. The one-line fast path remains available, but its text is parsed by the shell and therefore follows ordinary shell quoting rules: ```bash @ "What's up?" tamq reply "What's up?" ``` These are tamq-owned executable commands beside the installed `tamq` command, not shell-specific aliases. Set `TAMQ_COMMAND_DIR` before startup to select a different writable command directory already present on your shell's `PATH`. Tamq refuses to overwrite unrelated commands. The shims only use tamq's durable send operation. The long form remains available: ```bash tamq send '@audit-core: please review the auth boundary' ``` With normal `output` delivery, the target pane visibly receives: ```text #flex-auth: please review the auth boundary [m-...] ``` This uses the pane's tmux-reported `/dev/pts/` device—the same Unix terminal-output mechanism underlying tools such as `write(1)`. For an ordinary shell with screen rows above its cursor, tamq confines scrolling to those rows, writes the comment immediately above the input row, and restores the cursor. Thus a partially typed command remains in place. If no safe row exists, tamq uses ordinary line output; alternate-screen programs receive the conservative fallback and may redraw over it. Neither path uses `send-keys`, sends Enter, or places bytes on the foreground process's stdin, so the durable inbox remains authoritative. In the `audit-core` window, inspect and acknowledge it: ```bash tamq inbox tamq ack ``` Human inbox output is safe to paste into an ordinary shell because every line is a comment. It includes the durable id needed by `ack`: ```text #flex-auth: please review the auth boundary [m-...] ``` To explicitly consume pending messages through a command, use an inbox filter: ```bash tamq inbox --filter 'cat >> msg.log' ``` The command runs once per pending message with the comment form on standard input. `TAMQ_MESSAGE_ID`, `TAMQ_SENDER_REPO`, and `TAMQ_TARGET_REPO` are set in its environment. A zero exit acknowledges that message; a non-zero exit leaves it and all later messages pending. Filters never run in the background and cannot be combined with `--all` or `--json`. Outside a managed window, use `tamq inbox --repo audit-core` and optionally `--json`. Output-displayed messages remain durable and pending until acknowledged. Neither `output` nor `inbox` mode injects terminal keystrokes, so they cannot execute or alter a command being typed in the target pane. Pushy mode intentionally crosses that boundary and records accepted submissions as `injected`. After upgrading from an earlier alpha, recreate the managed session once so existing panes inherit the neutral shell contract, repository command `PATH`, and terminal-output broker registration: ```bash tamq stop tmux kill-session -t tamq tamq flex-auth audit-core ``` For degraded tmux-only use, `tamq start --no-service ...` skips socket endpoint registration and durable messaging. Stop the broker and remove the managed tmux session explicitly when finished: ```bash tamq stop tmux kill-session -t tamq ``` For a runaway or stale installation, use the emergency cleanup command from a terminal outside the managed session. It is a dry run unless `--yes` is given: ```bash tamq cleanup tamq cleanup --yes ``` Confirmed cleanup stops only the verified tamq broker, closes only a tmux session carrying tamq's management marker, disconnects transient endpoints, clears leases, removes the configured socket/PID/lock files, deletes only tamq-generated `@repo` shims, and removes owned stale `tamq-*` tmux sockets. Durable message history and unrelated tmux sessions or files are preserved. The command is idempotent; an ownership mismatch is reported instead of being removed. History remains a separate explicit operation. A reflected delivery incident can be selected by durable receipt direction, dry-run, and then removed without age-based purging: ```bash tamq purge --feedback-chain m-... tamq purge --feedback-chain m-... --yes ``` Uninstall the user tool from the checkout with `make uninstall`, or from anywhere with `uv tool uninstall tmux-amq`. ## Development ```bash uv run pytest uv run tamq --help uv run tamq --version uv run tamq start net-kingdom railiance-platform uv run tamq attach ``` The alpha includes the local SQLite queue, neutral tmux endpoint manager, strict gita target validation, manual inbox, control-mode client, and an explicit PTY tap for integration experiments. Coordination-engine integration remains a later phase. ## Terminal architecture `tamq` separates terminal coordination into three layers: 1. tmux control mode is the topology and output/control stream; 2. the local broker assigns endpoint/source identity and durably queues intent; 3. Explicit `tamq tap` mode is a full-duplex PTY proxy around a command. It forwards bytes unchanged in raw terminal mode, propagates terminal resize and lifecycle signals, and observes complete input lines for `@repo:`/`#repo:` routing. This keeps tmux-specific topology concerns separate from reusable terminal I/O observation and message identity. Normal endpoints use terminal-output delivery: each message is written once to the target pane's PTY output and stays pending in the durable inbox until acknowledged. `--mode inbox` selects inbox-only manual mode. Neither becomes pane input. Experimental `--mode pushy` sends one checked tmux command list containing sanitized literal input followed by Enter, then records the message as `injected`. When pushy mode creates explicitly commanded windows, the same PTY tap observes outbound `@repo:` and `#repo:` lines without rewriting them. The older pane-delivery experiment remains available only with the explicit `tamq start --tap --command ...` opt-in. Both input paths remain subject to the retry and acknowledgement limitations tracked by `TAMQ-WP-0003`. The visible endpoint label is `tmux-amq-`; each boot also receives an instance nonce so PID reuse cannot collide with prior leases or receipts. ## Configuration Configuration is read from `${XDG_CONFIG_HOME:-~/.config}/tamq/config.toml` (or `$TAMQ_CONFIG`). Environment variables such as `TAMQ_SOCKET` and `TAMQ_STATE_DIR` take precedence. The purge and startup advisory defaults can be tuned without changing command lines: ```toml [tamq] state_dir = "/run/user/1000/tamq" purge_before = "365d" purge_max_size = "100MB" history_max_size = "100MB" delivery_poll_interval = "0.5" [policy.profiles.diagnostics] safety_gated_max_attempts = 2 delivery_ack_mode = "acknowledged" ``` Policy profiles accept a safety-gated retry cap of at most nine attempts, but the alpha delivery path does not enforce attempt counting yet. Explicit acknowledgement exists, while `delivery_ack_mode` enforcement and bounded retry state are tracked in `TAMQ-WP-0003`. Use `--policy-profile` to select a profile; `--orwell` enables explicitly unsafe local diagnostics. The Unix socket service supports structured `ping`, `register`, `send`, `history`, `ack`, `endpoints`, and `disconnect` operations. Endpoint registrations and messages are persisted in the same local SQLite database; delivery remains delegated to the control-mode adapter.