Skip to content

Automating with UHP

Universal Harness Protocol, or UHP, is Artuditu’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 artu 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.

Terminal window
artu session list --json
artu uhp schema
artu uhp capabilities
artu uhp snapshot

UHP is always available to the local owner. Capability discovery returns artuditu-uhp version 1.0, the complete method registry, access contracts, limits, event sequence, identity rules, and terminal capabilities.

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 Artuditu pane, $ARTUDITU_SOCKET_PATH contains the selected endpoint. On macOS or Linux you can send a request directly:

Terminal window
printf '%s\n' '{"id":"pane-1","method":"pane.list","params":{}}' \
| nc -U "$ARTUDITU_SOCKET_PATH"

For a transport-neutral one-frame bridge, including over SSH:

Terminal window
printf '%s\n' '{"id":"snap","method":"session.snapshot","params":{}}' \
| artu uhp proxy
printf '%s\n' '{"id":"snap","method":"session.snapshot","params":{}}' \
| ssh host artu uhp proxy

uhp proxy forwards one bounded request and response. Persistent event and terminal streams must connect to the discovered local endpoint.

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.

{"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.

  1. Subscribe with events.subscribe.
  2. Fetch session.snapshot on another connection.
  3. Discard buffered events at or below the snapshot sequence.
  4. Apply later events in order.
  5. Resnapshot after a gap, overflow, EOF, reconnect, or generation change.

Use artu uhp events for a stream test. Production clients should reconnect through the discovered endpoint and pass after_sequence.

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.