|
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 |
||
|---|---|---|
| .claude/rules | ||
| .forgejo/workflows | ||
| src/tamq | ||
| tests | ||
| workplans | ||
| .custodian-brief.md | ||
| .gitignore | ||
| .repo-classification.yaml | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| INTENT.md | ||
| Makefile | ||
| pyproject.toml | ||
| README.md | ||
| SCOPE.md | ||
| uv.lock | ||
| WORK-RECORDS.md | ||
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, readable To: 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,
tmux, and gita. Each repository must already have an exact gita slug and path.
Install or upgrade tamq as an isolated user tool:
make install
command -v tamq
tamq --version
Start an attached two-repository shell session:
tamq flex-auth audit-core
This creates or reuses the managed tamq session, opens same-named windows in
the exact gita paths, starts an ordinary shell behind tamq's transparent PTY
observer, and attaches to the first window. The explicit form is equivalent:
tamq start flex-auth audit-core
Set up the session without attaching, then attach later:
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:
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:
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 places a routed message in the target input buffer
without Enter. trigger places the same input and submits it exactly once:
tamq --mode pushy --command codex flex-auth audit-core
tamq --mode trigger --command codex flex-auth audit-core
All messaging-enabled windows use the same terminal-neutral PTY observer, whether they contain a shell, a tool, or an explicitly selected coding agent. An operator or a worker can emit the same readable line:
To:audit-core: Please review the authentication change.
Operator input is forwarded unchanged and is delivered with /o; a line
originating in worker output has no suffix:
From:flex-auth/o: Please review the authentication change.
From:flex-auth: Worker-generated message.
From: is never routable. The observer suppresses recent exact operator lines
when a TUI echoes or redraws them, so the echo cannot become a second worker
message. Ambiguous exact echoes fail closed. Pushy and trigger still cannot
infer the foreground program or protect input already being edited, so both
remain explicit experiments. Startup requires the readable-duplex, trigger,
and line-limit capabilities and replaces an incompatible old broker.
Exchange messages manually
Each managed window has its own repository identity. From operator input in the
flex-auth window, queue a message with:
To:audit-core: please review the auth boundary
The same line at the start of worker output queues a worker-originated message.
The @, #, reply shorthand, and interactive recipient composer from earlier
alphas have been removed.
Operator input can change allowlisted runtime state:
Cmd: mode=trigger
Cmd: maxmsg=16
Cmd: maxin=2048
Cmd: maxout=65536
Cmd: reset-limits
Cmd: is recognized only on the operator-input path; identical worker output
is inert. The transparent input contract means the line also remains visible
to the foreground program. Small tamq-owned shell absorbers prevent ordinary
shells from reporting To:repo: and Cmd: as missing commands; tamq refuses
to overwrite unrelated files. The explicit CLI send form is:
tamq send 'To:audit-core: please review the auth boundary'
With normal output delivery, the target pane visibly receives:
From:flex-auth/o: please review the auth boundary
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 delivery 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:
tamq inbox
tamq ack <message-id>
Human inbox output uses the same framing. Use --json or tamq history to get
the durable id needed by ack:
From:flex-auth/o: please review the auth boundary
To explicitly consume pending messages through a command, use an inbox filter:
tamq inbox --filter 'cat >> msg.log'
The command runs once per pending message with the readable From: 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.
Pushy places input without Enter; trigger adds exactly one Enter. Both input
modes record accepted delivery as injected.
Every session window has independent, session-lifetime running counters for accepted outbound messages, operator input lines, and normalized worker output lines. Defaults are 8, 1024, and 32768 respectively:
tamq --maxmsg 16 --maxin 2048 --maxout 65536 flex-auth audit-core
The same defaults can be set as maxmsg, maxin, and maxout under [tamq]
in the configuration file. A message is rejected once any counter is equal to
its limit. Tamq displays the applicable count and Cmd: reset-limits resets all
three counters for the current window. Counters survive tap and broker restarts
within the same managed-session generation and are visible in tamq status.
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:
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:
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:
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 and line counters, removes the configured socket/PID/lock files,
deletes only tamq-generated protocol absorbers (including legacy @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:
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
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:
- tmux control mode is the topology and output/control stream;
- the local broker assigns endpoint/source identity and durably queues intent;
tamq tapis a full-duplex PTY proxy around every messaging-enabled shell or explicit command. It forwards bytes unchanged in raw terminal mode, propagates resize and lifecycle signals, observes operator input, and normalizes worker output for start-of-lineTo: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 places one sanitized From: line
without Enter; --mode trigger performs the same placement and submits once.
The PTY observer records explicit operator/worker provenance and suppresses
echoed operator lines before they can route again.
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:
[tamq]
state_dir = "/run/user/1000/tamq"
purge_before = "365d"
purge_max_size = "100MB"
history_max_size = "100MB"
delivery_poll_interval = "0.5"
maxmsg = 8
maxin = 1024
maxout = 32768
[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.