Some checks failed
tamq-ci / test (push) Failing after 6s
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
328 lines
12 KiB
Markdown
328 lines
12 KiB
Markdown
# 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/<number>` 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 <message-id>
|
|
```
|
|
|
|
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-<PID>`; 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.
|