Automating with UHP
Universal Harness Protocol, or UHP, is Luvus’s single public automation contract. It covers workspaces, tabs, panes, agents, terminal streams, files, Git, DIFF, worktrees, modules, bars, configuration, and events.
Normal commands such as luvus pane list are friendly wrappers over the same
UHP methods. Unix sockets on macOS and Linux and named pipes on Windows are
local transports, not separate APIs.
Discover and inspect
Section titled “Discover and inspect”luvus session list --jsonluvus uhp schemaluvus uhp capabilitiesluvus uhp snapshotUHP is always available to the local owner. Capability discovery returns
luvus-uhp version 1.0, the complete method registry, access contracts,
limits, event sequence, identity rules, and terminal capabilities.
Send one request
Section titled “Send one request”Each ordinary connection carries one UTF-8 JSON request and response. Both are terminated by LF and bounded to the advertised frame limit.
{"id":"pane-1","method":"pane.list","params":{}}Inside a Luvus pane, $LUVUS_SOCKET_PATH contains the selected endpoint. On
macOS or Linux you can send a request directly:
printf '%s\n' '{"id":"pane-1","method":"pane.list","params":{}}' \ | nc -U "$LUVUS_SOCKET_PATH"For a transport-neutral one-frame bridge, including over SSH:
printf '%s\n' '{"id":"snap","method":"session.snapshot","params":{}}' \ | luvus uhp proxy
printf '%s\n' '{"id":"snap","method":"session.snapshot","params":{}}' \ | ssh host luvus uhp proxyuhp proxy forwards one bounded request and response. Persistent event and
terminal streams must connect to the discovered local endpoint.
Responses and mutations
Section titled “Responses and mutations”A success returns the request ID and result. A failure returns the request ID
and a structured error.
{"id":"pane-1","result":{"type":"pane_list","panes":[]}}{"id":"pane-1","error":{"code":"not_found","message":"pane not found"}}If a connection is lost during a mutation, reconcile state before retrying.
Never blindly repeat prompts, terminal input, or agent launches. State results
include a monotonic revision; mutations can pass if_revision and stale
callers receive revision_conflict before execution.
Agents and terminals
Section titled “Agents and terminals”{"id":"explain","method":"agent.explain","params":{"pane":"7"}}{"id":"prompt","method":"agent.prompt","params":{"target":"reviewer","text":"Review the diff","wait":true,"until":["idle","done","blocked"],"timeout_s":600}}Terminal methods use stable PTY identity. Discover terminals first, then reuse
the returned server_generation, terminal_id, and pane_id:
{"id":"inventory","method":"terminal.backend.inventory","params":{}}{"id":"capture","method":"terminal.backend.capture","params":{"server_generation":"...","terminal_id":"...","pane_id":"7","mode":"recent_unwrapped","lines":100,"ansi":false}}terminal.backend.observe provides a bounded read-only ANSI stream.
terminal.backend.control adds correlated input frames and gives one client an
exclusive control lease. Both are change-driven and idle without polling.
Events without races
Section titled “Events without races”- Subscribe with
events.subscribe. - Fetch
session.snapshoton another connection. - Discard buffered events at or below the snapshot sequence.
- Apply later events in order.
- Resnapshot after a gap, overflow, EOF, reconnect, or generation change.
Use luvus uhp events for a stream test. Production clients should reconnect
through the discovered endpoint and pass after_sequence.
Delegated access
Section titled “Delegated access”The endpoint is owner-only and grants the owner full authority. Create an in-memory, expiring token when a harness needs narrower access:
{"id":"token","method":"uhp.token.create","params":{"scopes":["read","terminal"],"ttl_s":3600}}Supply the returned secret in auth. Use uhp.token.list and
uhp.token.revoke to manage it. Never log the secret.
Continue with the UHP reference and UHP method reference.