Agent automation
Let agents and scripts read and type into specific tmux panes through the tmux-ide CLI, MCP server and SDK, with operation handles for safe recovery.
Agent automation lets scripts and agents read another tmux pane's output and type
into it, like a scoped and auditable tmux send-keys / capture-pane. It works
through the tmux-ide CLI, an MCP server or the SDK. All three share one
operation API. They need a running daemon of the same version, and they never
fall back to raw tmux when a request fails.
For quick, human-style messaging between agents, tmux-ide send and
tmux-ide wait are simpler; see Multi-agent teams.
Use automation when you need exact pane identity, attribution and retry-safe
handles.
Read and send in two commands
List the panes you can target, then pass one endpoint in a read or send intent on
stdin. This example uses jq to pick the pane titled Tests:
tmux-ide automation panes --json > panes.json
jq '{kind: "read", target: (.panes[] | select(.title == "Tests") | .endpoint)}' panes.json \
| tmux-ide automation read --json
jq '{kind: "send", target: (.panes[] | select(.title == "Tests") | .endpoint), text: "npm test", enter: true}' panes.json \
| tmux-ide automation send --jsonread returns up to 16 KiB of the pane's text. send types the text and, with
enter: true, presses Enter. The sections below explain endpoints, the
two-step reserve and execute flow for safe retries, and sender attribution.
Find a pane's endpoint
tmux-ide automation panes --jsonEach result includes a display title, session name, and an endpoint. Copy the
endpoint unchanged into your request. It identifies the machine, tmux server
generation, workspace, and pane lifetime. Pane numbers and titles alone are
not unique across servers. Rediscover after a server or pane is replaced.
A read intent is { "kind": "read", "target": endpoint }. The CLI and MCP
resolve an omitted source from the invoking pane’s credential. Use source: null
to deliberately leave the caller unidentified. Discovery also returns source
when the daemon can verify that credential.
A send intent also includes "text" and "enter", with "kind": "send".
enter: true presses Enter after the text and can execute a shell command.
Input is limited to 16 KiB of UTF-8 text and cannot contain NUL.
Reserve, execute and recover a send safely
Save your intent as intent.json, then reserve an operation before executing it:
tmux-ide automation reserve --json < intent.json > reservation.json
jq '{version:1,handle,intent}' reservation.json > execution.json
tmux-ide automation execute --json < execution.jsonReservation does not send input or capture a pane. Keep the returned handle and the returned, resolved intent until you know the result. On a timeout or lost response, check the handle:
tmux-ide automation status GENERATION OPERATION_ID --jsonYou may retry the same execution.json. Do not reserve a new operation to
retry an uncertain send. outcome-unknown can mean the daemon restarted or
retention expired; it does not prove that nothing happened. A completed send
means the tmux command completed, not that the recipient consumed the input.
tmux-ide automation send --json and tmux-ide automation read --json accept
an intent on stdin and combine reservation with execution. Use the explicit
two-step flow when the caller must persist the handle before an effect.
Read text is returned only with the first execution response, up to 16 KiB.
Check the byte counts and truncated flag. Status and replay responses retain
metadata only; a replay reports replay-unavailable and does not capture again.
Attribute who sent it
Omit source in CLI requests and MCP tmux_prepare calls to resolve the
invoking pane automatically. Save the resolved intent returned with the
reservation and pass it unchanged to execution. Explicit source: null disables
this resolution. An invalid credential fails discovery; it never silently
changes an operation’s identity.
Use source: null when the caller has no verified pane identity. To claim a
source, supply its discovered endpoint and a valid credential for that exact
pane lifetime. The CLI and MCP process attempt to read the invoking pane's
credential using its actual TMUX socket and TMUX_PANE; the daemon validates
it separately from the target. A title, native pane number, or matching text
does not establish the sender's identity.
Receipts distinguish cli, sdk, and mcp adapter origins. This is declared
transport metadata, not proof of which agent called it. A reservation binds
that origin along with the intent and source authority; retries must use the
same adapter origin. Older automation clients that omit it retain sdk.
Source and target may belong to different registered servers on the same daemon. Invalid or stale source claims are rejected rather than reassigned.
Use automation from an MCP client
Configure your harness to launch this stdio server:
{ "command": "tmux-ide", "args": ["mcp"] }The exact surrounding configuration depends on your harness. The server offers:
| Tool | Purpose |
|---|---|
tmux_panes | Discover current endpoint identities |
tmux_prepare | Reserve a read or send without executing it |
tmux_execute | Execute the saved handle and unchanged intent |
tmux_operation_status | Check an operation without repeating it |
tmux_interactions | Read one bounded batch of interaction metadata |
tmux_interactions accepts a server-scoped resume cursor and waits up to 30 seconds.
Carry the returned cursor forward. A gap explicitly indicates missing history.
Events contain no terminal contents. The server does not expose an arbitrary
shell runner or HTTP proxy.
Use the TypeScript SDK
The workspace package @tmux-ide/sdk exports the same automation client:
import { createTmuxIdeAutomationSdk } from "@tmux-ide/sdk";
const client = createTmuxIdeAutomationSdk({
baseUrl,
ownerToken,
// sourceCredential: verified credential, when claiming a source
});
const { panes } = await client.discover();
const target = panes.find((pane) => pane.title === "Tests")?.endpoint;
if (!target) throw new Error("Select a current target pane first");
const intent = { kind: "read" as const, target, source: null };
const { handle } = await client.reserve(intent);
// Persist handle + intent before calling execute when recovery matters.
const response = await client.execute(handle, intent);Use AutomationInvocationError.handle for recovery after an uncertain request.
The SDK also provides status(handle) and a scoped subscribe method. Protect
the daemon owner token as a local automation credential; it grants authority
to send input to managed panes.
Limits of observation coverage
An authored operation and an observed tmux command provide different evidence. The automation API records the requested operation and its result. When native evidence can be explicitly correlated with that operation, it enriches the same interaction rather than appearing as another send or read.
Stock tmux observation has partial coverage. Missing activity does not prove that no command ran, and a hook's client is not automatically the original sender. Raw commands may have an unknown source. Server registrations and pane lifetimes keep evidence from separate tmux servers apart, even when their pane numbers match.
With stock observation, typing through the tmux-ide viewer can appear in activity details as “Send command · sender unknown.” This is a command observation, not evidence that another agent sent input. Pane headers and Home’s transient indicators show only completed interactions with a bound source, excluding viewer and same-pane activity. Unknown command observations remain in history and do not replace a named interaction. Repeated reads update one short-lived indicator.
Exact viewer suppression requires native ownership evidence; stock client labels alone cannot establish the issuer.
Experimental native observation uses a bounded metadata journal on a capable tmux server. It is disabled by default and must be selected explicitly. Replacing the tmux executable does not upgrade an already running server or enable this coverage for existing sessions. Development-instance configuration is described in the development worktree guide.
Observation describes effects, not intent: captured output does not prove an agent understood it, and queued input does not prove the recipient acted on it. Viewer connections used by tmux-ide do not represent another agent reading or messaging the pane. Native connection evidence is not a security boundary against another process running as the same user.
History is bounded and can be lost when a server restarts or a reader falls behind. Keep the server-scoped cursor and handle reported gaps or unavailable coverage explicitly. A lost observation must not trigger a resend of an uncertain operation.
An external desktop agent without a tmux pane currently remains unidentified. A CLI/MCP adapter label does not prove which agent invoked it; external-client registration is not yet available.
See also
- Multi-agent teams — coordinate agents with send and wait
- CLI reference — every automation subcommand
- Agent detection — the status agents publish
Troubleshooting
Fix common tmux-ide problems with one command each: tmux version errors, missing keys, wrong agent states, Claude hooks, SSH and restore.
Multi-agent teams
Run Claude Code, Codex and other agents side by side, coordinating through tmux-ide team groups, a shared status bus, direct messages and wait commands.