122 lines
5.5 KiB
Markdown
122 lines
5.5 KiB
Markdown
# Inter-Agent tmux Comms — Standard & Tooling
|
|
|
|
Reliable, self-identifying messaging between Mosaic agents running in tmux panes
|
|
(Claude Code / Codex / OpenCode REPLs), across hosts.
|
|
|
|
## The addressing standard (required)
|
|
|
|
Every cross-agent tmux message MUST begin with an addressing preamble:
|
|
|
|
```
|
|
[<src_host>:<src_session> -> <dst_host>:<dst_session>] <message>
|
|
```
|
|
|
|
- `host` = `hostname -s` of the machine the agent runs on (e.g. `web1`, `sb-it-mgr-0-lt`).
|
|
- `session` = the tmux session name (e.g. `mos-claude`, `rev0-4`, `installer-1`).
|
|
- **Replies FLIP the preamble**: the recipient answers with `[<dst> -> <src>] ...`.
|
|
|
|
Why: a fresh or context-wiped agent always knows who sent a message and to whom.
|
|
No ambiguity about origin or lane after a tmux wipe / session restart.
|
|
|
|
Example exchange:
|
|
|
|
```
|
|
[web1:mos-claude -> sb-it-mgr-0-lt:installer-1] status on #29?
|
|
[sb-it-mgr-0-lt:installer-1 -> web1:mos-claude] Q2 done, opening PR #34.
|
|
```
|
|
|
|
## The helper: `agent-send.sh`
|
|
|
|
Prepends the preamble automatically (auto-detecting your own `host:session`) and
|
|
dispatches transport to local OR remote panes; application acceptance remains unknown.
|
|
|
|
```bash
|
|
# Local target (same host, default tmux server)
|
|
agent-send.sh -s <dst_session> -m "message"
|
|
|
|
# Local target on a Mosaic fleet socket
|
|
agent-send.sh -L mosaic-fleet -s '=coder0' -m "message"
|
|
|
|
# Remote target (over ssh)
|
|
agent-send.sh -H user@host -s <dst_session> -m "message"
|
|
|
|
# From a file / stdin
|
|
agent-send.sh -H user@host -s <dst_session> -f msg.txt
|
|
echo "msg" | agent-send.sh -s <dst_session>
|
|
```
|
|
|
|
Key flags: `-L` named tmux socket · `-s` dst session (required) · `-H` ssh target for remote · `-n` dst
|
|
hostname for the preamble (else auto-resolved) · `-m`/`-f`/stdin body · `-S`
|
|
override source label · `-v` transport metadata only · `-r N` compatibility-only, no retries.
|
|
|
|
For durable fleet use, prefer exact tmux targets such as `=coder0`. The helper
|
|
normalizes exact session targets to pane-qualified targets internally so pane
|
|
commands do not fall back to tmux's prefix matching behavior.
|
|
|
|
## Named socket isolation
|
|
|
|
Durable Mosaic fleets should use a dedicated tmux socket, for example:
|
|
|
|
```bash
|
|
tmux -L mosaic-fleet ls
|
|
agent-send.sh -L mosaic-fleet -s '=coder0' -m "status?"
|
|
send-message.sh -L mosaic-fleet -t '=coder0' -m "raw pane message"
|
|
```
|
|
|
|
This keeps fleet operations away from the user's default tmux server. It is the
|
|
safe rollout path on hosts that already have manual tmux sessions.
|
|
|
|
## Why a helper exists (the submission gotcha)
|
|
|
|
Pasting into an interactive REPL via raw `tmux send-keys` is unreliable: a
|
|
trailing `Enter` is frequently swallowed and the message sits as an **unsubmitted
|
|
draft** ("Press up to edit queued messages"). Over an `ssh -> nested tmux` hop the
|
|
plain `Enter` keyname often does not register at all — `C-m` is needed.
|
|
|
|
`send-message.sh` solves this for a **local** pane: bracketed-paste the body
|
|
(so multi-line content doesn't submit early), pause, then send `Enter` as its own
|
|
keystroke. It does not send automatic extra Enters. The legacy `-r` option
|
|
is accepted for compatibility but no longer authorizes flushes.
|
|
|
|
Jason approved the **transport-only contract** after independent review demonstrated
|
|
that screen layouts cannot prove application acceptance. Exit 0 means tmux accepted
|
|
buffer load, one paste and one Enter command, not that the application submitted,
|
|
queued or processed the message. Output explicitly says:
|
|
`transport dispatched; application acceptance unknown`.
|
|
|
|
No capture-pane or editor/footer parser participates in transport success. Hidden
|
|
retained drafts can coexist with successful transport; they are never called
|
|
confirmed delivery. Failure exits remain nonzero (1 target resolution, 2 transport
|
|
failed/partial/uncertain, 3 usage; wrapper 4 ambiguous socket). Failed paste is not
|
|
retried with another mode. Never blindly replay a partial/uncertain operation.
|
|
Verbose output is content-free transport metadata. Runtime-bound receipts are
|
|
separate future work, not implemented by this tool.
|
|
|
|
`agent-send.sh` solves the **remote** case by _shipping `send-message.sh` over ssh_
|
|
(`ssh host bash -s -- ... < send-message.sh`) and running it local to the target
|
|
pane — so the reliable send-keys always happens on the pane's own host. The remote
|
|
needs only `bash` + `tmux` + `base64`; **no mosaic install required there**. The
|
|
message crosses the wire as base64 (`-b`). Every remote command argument is
|
|
independently POSIX-shell quoted; retry syntax is validated before invoking SSH.
|
|
The executing-shell regression suite checks quote-bearing target/socket arguments
|
|
and rejects an invalid retry before any SSH call.
|
|
|
|
## Files
|
|
|
|
- `agent-send.sh` — inter-agent wrapper (preamble + local/remote dispatch).
|
|
- `send-message.sh` — low-level reliable single-pane submitter (`-b` base64 input).
|
|
- `auto-submit-drafts.sh` — watchdog that flushes stable unsubmitted prompt
|
|
drafts on a coordinator pane (default target `mos-claude`); run it as a
|
|
long-lived process alongside the coordinator session.
|
|
- `agent-send.test.sh` — regression + grammar lock for `agent-send.sh`.
|
|
- `test-send-message-socket.sh` — smoke test for named-socket isolation.
|
|
|
|
## Distribution
|
|
|
|
These live in the installed framework copy at
|
|
`~/.mosaic/tools/tmux/`. `install.sh` rsyncs the framework **source tree**
|
|
to each host, so to propagate permanently, land both files in the framework
|
|
source repo and re-run the installer on each host. Until then, `agent-send.sh`
|
|
already works against any reachable host because it ships `send-message.sh` over
|
|
ssh per-send — no pre-install on the target host is needed to _send to_ it.
|