control board: reply from the board through agent-send.sh (#1505)

Piece 2 of the MVP (#1503). A one-line reply box and Send in the detail
of rows with a live registration; POST /api/reply runs
tools/tmux/agent-send.sh -s <session> -S <host>:control-board
[-L <socket>] -m <text> once for one seat and returns the exit code,
stdout and stderr. The page shows delivered or failed with the tool's
stderr; other rows say "reply needs a registered seat". No send-keys,
queue, retries, history or broadcast; packages/seat and agent-send.sh
untouched. Every message ends with a fixed trailer telling the seat to
answer in its own session (Jason's refinement after the first Gate C
exchange; the board has no pane). Board suite 98/98.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 11:30:30 -05:00
co-authored by Claude Fable 5.1
parent a62ca1904f
commit 867619dca2
8 changed files with 508 additions and 19 deletions
+29
View File
@@ -64,8 +64,37 @@ Routes served:
more than 4096 bytes): `{"project", "agent", "lastActivity", "seen"}`.
`seen` defaults to `true`; pass `false` to unsee. Bad input, a missing
header, or an oversized body gets a `400`.
- `POST /api/reply` — send one line of text to one registered seat through
`tools/tmux/agent-send.sh` (#1505). Body: `{"agent": "<project>/<agent>",
"text"}`, JSON, at most 4096 bytes, text at most 2000 characters. The
server rescans, finds the row, and runs
`agent-send.sh -s <session> -S <host>:control-board [-L <socket>] -m <text>`
with the session and socket from the row's registration; `MOSAIC_TMUX_SOCKET`
is stripped from the tool's environment so the registration is the only
source. The text is sent as typed, followed on its own line by a fixed
trailer: `(control-board: answer in your own session as usual; the board
reads your transcript. Do not agent-send to control-board.)`. The board is
a sender without a pane: replies to it are read from the seat's
transcript, never sent, and `agent-send.sh -s control-board` refuses
because no such session exists (Jason's refinement after the first real
exchange, 2026-09-12). It answers `200` with `{delivered, exitCode, signal, stdout,
stderr, agent, session, socket, sentAt}` whenever the tool ran, delivered
or not; a non-zero exit is reported with the tool's stderr, never retried
and never routed around. Refusals before the tool runs: `400` bad body,
empty or blank text, text too long; `404` unknown row; `409` the row has
no registration, the registration is stale (pid gone), or it has no tmux
session; `500` the tool could not be started. One seat per request; no
queue, no history, no broadcast, no raw `send-keys`.
- `GET /healthz`.
On the page, the detail of a row with a live registration and a tmux
session has a one-line reply box and a Send button; every other row's
detail says "reply needs a registered seat". After Send the row shows
`delivered <time> to tmux <session>` or `failed (exit N): <stderr>`. A
draft that has not been sent, and the last receipt, survive the page's
periodic refresh; a delivered reply clears the box and triggers a rescan
so the seat's reaction shows without waiting for the next tick.
## Exit codes
- `0` — scan completed and status files were written, or the server stopped cleanly.