TUIOSTUIOS

Control Protocol

Drive a TUIOS daemon from scripts with the JSON verb protocol

The TUIOS daemon speaks a line-oriented JSON protocol on its Unix socket. It is meant for scripts, automation, and programs that drive TUIOS without a terminal attached. Every request is one line of JSON, and every response is one line of JSON.

This is a separate surface from the remote control CLI commands. Those commands are convenient for shell one-liners; the verb protocol is what you want when you need structured errors, blocking waits, or an event stream.

Connecting

The verb protocol shares the daemon socket with the binary client protocol. The daemon decides which one you are speaking by looking at the first byte you send: a line beginning with { (or leading whitespace) is a verb-protocol client, and a binary client's first byte is the high byte of a length prefix, which is never {.

That means there is no separate port, no handshake negotiation, and no special socket. Connect to the socket and write JSON.

# Linux, when XDG_RUNTIME_DIR is set
echo '{"id":1,"verb":"list-sessions"}' | socat - UNIX-CONNECT:"$XDG_RUNTIME_DIR/tuios/tuios.sock"

See Sessions for the socket path on each platform.

Discovering the protocol

You do not have to read this page to use the protocol. The daemon describes itself:

# Every verb, its parameters, and example requests
tuios list-verbs

# Just one verb
tuios list-verbs capture-pane

list-verbs reports the protocol version, every verb and parameter, the accepted values for each constrained parameter, the stable error codes, and the request and response envelope shapes. The same information is available over the socket as the list-verbs verb.

The parameter schema list-verbs returns is generated from the same value sets the request validator uses, so the accepted values it advertises cannot disagree with what the daemon will actually take.

Envelopes

A request carries an id, a verb, and optional params:

{"id":1,"verb":"send-keys","params":{"session":"work","keys":"ls,Enter"}}

A successful response echoes the id and carries a result. An error response carries an error object with a stable code, a human-readable message, and often a hint naming what would resolve the failure: the closest verb spelling, the parameter that was wrong, or the set of values that do exist.

Existing consumers that read only code and message are unaffected by hints, which are all optional.

Unknown parameters are refused, not ignored. A request naming a parameter the verb does not take fails with invalid_params, and the hint carries the closest spelling and everything the verb does accept. A call that cannot do what it asked for never reports success, and a refusal is also what a newer caller gets from an older daemon.

Verbs

Thirty-five verbs are implemented. The daemon is the authority on this list: tuios list-verbs always reflects the binary you are talking to.

Discovery and handshake

VerbPurpose
helloReport the daemon's protocol version and the range it accepts. Takes client, version, protocol.
list-verbsDescribe every verb, or one named verb.

Both sides of the handshake compare wire versions, and each refuses a peer outside its accepted range with a message that says which side is newer.

Sessions and windows

VerbPurpose
list-sessionsList all sessions the daemon holds.
session-infoReport details about one session.
list-windowsList the windows in a session, with index, workspace, size, and detected agent.
new-windowCreate a window. Optional name, workspace, cwd, focus, and command, an argv to exec as the window's process instead of a shell; the window closes when the program exits.
split-windowSplit relative to a window. Needs an attached client.
focus-windowFocus a window, by target or by direction. Directional focus needs an attached client.
move-windowMove a window to a workspace.
set-windowChange a window's name or minimized state.
select-workspaceSwitch the session's current workspace.
list-workspacesList workspaces, their names, and their display order.
set-layoutApply tiling, equalize, or rotate. Needs an attached client.
run-commandRun any named tuios command, the same vocabulary tapes use.
close-windowClose a window.
kill-sessionTerminate a session and every window in it.

Input and output

VerbPurpose
send-keysSend parsed key tokens, for example "ctrl+b,n". Optional literal and raw flags. Tokens split on spaces and commas, so use send-text for text that contains either.
send-textWrite literal text to a window's PTY.
capture-paneCapture a pane's contents.
resizeResize a window's PTY. Requires width and height.

Options

VerbPurpose
list-optionsDescribe every settable option path, its type, default, and accepted values.
set-optionSet a session option by path, for example appearance.dockbar_position. The path is validated against the option registry, and the value is validated before anything is recorded. Applied live when a client is attached.
get-optionRead the value in effect for an option path.
list-themesList every theme the daemon can apply, built-in and user-imported, re-reading the user themes directory on each call.

set-option refuses a path that does not exist with the option_not_found error, whose hint carries the closest match and the full path list. When it reports applied: false, the response says why. get-option reports the value in effect along with a source field that says whether it came from the session or from defaults.

Naming and arrangement

VerbPurpose
set-session-nameSet a session's display name. The original name stays its identity.
set-session-accentSet or clear a session's accent colour.
set-workspace-nameName a workspace. Its number stays its identity.
set-workspace-orderSet the display order of a session's workspaces.

Agent state

VerbPurpose
set-agent-stateReport an agent's state for a pane, with a source tier.
get-agent-stateRead the agent state the daemon holds for a pane.
explain-agent-detectShow why a pane was or was not detected as running an agent.
explain-agent-screenShow what the screen rules make of a pane.

Events and waiting

VerbPurpose
subscribeOpen a long-lived event stream on this connection.
unsubscribeClose this connection's event stream.
wait-forBlock until a condition matches, or fail with the timeout code.

Capturing panes

capture-pane reads one of two buffers, named by source:

  • visible (default) captures the visible screen.
  • recent captures recent scrollback.

Set styled to include ANSI styling. Restrict the output with lines to keep the last N lines, or with start and end for a 1-based inclusive range. When start or end is given, lines is ignored.

{"id":1,"verb":"capture-pane","params":{"session":"work","source":"recent","lines":50}}

There is no unwrapped capture. A recent-unwrapped source was once accepted and behaved as a silent alias for recent; it has been retired, and asking for it now returns an error explaining why. The emulator does not record which rows are soft wrapped, so unwrapping would mean guessing at row boundaries. Long lines come back split at the pane width.

Waiting for conditions

wait-for replaces the poll-and-capture loops that scripts otherwise write. It blocks until the condition matches or the timeout expires.

ConditionMatches when
session-existsThe named session exists.
window-outputOutput matching pattern (a regular expression) appears.
window-exitThe window's process exits.
window-idleThe window produces no output for idle milliseconds.

idle defaults to 500 ms and timeout defaults to 30000 ms. pattern is required by window-output. A wait that expires fails with the timeout error code rather than returning a non-match result.

{"id":1,"verb":"wait-for","params":{"condition":"window-output","session":"work","pattern":"done","timeout":10000}}

Event stream

subscribe turns the connection into an event stream. Events are delivered from the moment of subscription and there is no backfill, so subscribe before you trigger the thing you want to observe.

Filter with types to receive only some events, and set queue to change how many events buffer before the stream marks a gap (default 256). If a consumer falls behind far enough to overflow that buffer, the stream emits a gap event rather than silently dropping events.

The event types are:

window-created, window-closed, window-exit, window-retitled, window-focused, window-moved, window-minimized, window-restored, workspace-switched, output, bell, mode-changed, session-created, session-closed, and gap.

{"id":1,"verb":"subscribe","params":{"session":"work","types":["window-created","window-closed"]}}

Error codes

Codes are stable and safe to branch on.

CodeMeaning
invalid_requestNot a valid request envelope, or the connection is in the wrong state for the verb.
unknown_verbNo such verb. The hint carries the closest match and the full verb list.
invalid_paramsA parameter was missing, malformed, or outside its accepted set. The hint names the parameter.
session_not_foundThe named session does not exist. The hint lists the sessions that do.
window_not_foundThe window target did not resolve. The hint lists the addressable windows.
no_windowsThe session exists but holds no windows to act on.
pty_not_foundThe target window has no live PTY; its shell has already exited.
needs_clientThe verb needs a live renderer, and no client is attached.
option_not_foundNo option by that path exists. The hint carries the closest match and the full path list; list-options describes them.
command_failedA verb routed to the attached client came back failed.
timeoutA wait-for condition did not match before its timeout.
protocol_mismatchThe caller's protocol version is outside the range this daemon accepts.
internalUnexpected server-side failure.

Verbs that need an attached client

Some work cannot happen without a renderer. A verb that routes to the attached TUI returns needs_client when the session is headless, rather than appearing to succeed. If you are driving a session created with tuios new --detach, expect this for anything that depends on the client's view of the world, and attach a client if you need it.

On this page