# Terminal: Automation

**Page:** api/terminal/automation

[Download Raw Markdown](./api/terminal/automation.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



The Terminal Automation API provides programmatic control over TUI (text-based UI) applications running in managed terminal sessions. Use these endpoints to snapshot the screen, search rendered output with regex, inject key presses and pasted text, and block until a target condition is met — without driving a real keyboard. Endpoints are backed by a server-side libvterm parser that mirrors the browser's xterm.js state, so snapshots reflect exactly what a user would see.

## Screen Inspection

### `GET /api/v1/terminal/snapshot`

Returns a rendered snapshot of the terminal screen as seen by a user. The snapshot includes the visible text grid (`lines`), cursor position, window title, fullscreen (alt-screen) state, reverse-video highlight spans, and a monotonic `sequence` counter. On first call for a session the parser is lazily initialized by replaying the session's output buffer.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | query | string | Yes | Terminal session ID (numeric 1-65535) |
| `include_colors` | query | boolean | No | Include ANSI SGR `colored_lines` array alongside plain text `lines`. Default: `false` |
| `include_highlights` | query | boolean | No | Include reverse-video highlight spans. Default: `true` |
| `scroll_offset` | query | integer | No | Lines into scrollback (0 = live viewport). Default: `0` |

### Response



```json
{
  "terminal_id": 1042,
  "sequence": 4271,
  "title": "vim /etc/hosts",
  "alt_screen": false,
  "dimensions": { "rows": 24, "cols": 80 },
  "cursor": { "row": 7, "col": 12, "visible": true },
  "lines": [
    "  GNU nano 5.4        /etc/hosts              ",
    "127.0.0.1   localhost",
    "::1         localhost",
    ""
  ],
  "highlights": [
    { "row": 1, "col_start": 0, "col_end": 12, "reverse": true }
  ],
  "colored_lines": null
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid parameters"
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```


```json
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "VTerm memory cap exceeded"
}
```



### SDK

```ts
const snapshot = await client.terminal.terminalAutomation.getTerminalSnapshot({
  terminal_id: "1042",
  include_colors: true,
  include_highlights: true,
  scroll_offset: 0
});
```

### `GET /api/v1/terminal/find`

Search the rendered terminal screen (or scrollback) for a PCRE2 regular expression pattern. Returns cell-coordinate hits with matched text. The scan enforces an internal 500 ms wall-clock bound to prevent ReDoS.


The scan stops after 500 ms and sets `deadline_exceeded: true` in the response. Shape patterns to terminate quickly, or use anchored expressions.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | query | string | Yes | Terminal session ID |
| `pattern` | query | string | Yes | PCRE2 regex pattern to search for (max 1024 bytes) |
| `scope` | query | string | No | Search scope: `screen` (default), `scrollback`, or `all` |
| `limit` | query | integer | No | Maximum number of hits to return (default 100, max 1000) |
| `case_insensitive` | query | boolean | No | Case-insensitive matching. Default: `false` |
| `scroll_offset` | query | integer | No | Scrollback offset for screen scope (0 = live viewport). Default: `0` |

### Response



```json
{
  "terminal_id": 1042,
  "pattern": "Error.*",
  "scope": "screen",
  "total": 2,
  "truncated": false,
  "deadline_exceeded": false,
  "hits": [
    { "row": 12, "col": 4, "text": "Error: file not found" },
    { "row": 18, "col": 0, "text": "Error: permission denied" }
  ]
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid parameters or regex"
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```


```json
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "VTerm memory cap exceeded"
}
```



### SDK

```ts
const results = await client.terminal.terminalAutomation.findInTerminal({
  terminal_id: "1042",
  pattern: "Error.*",
  scope: "screen",
  limit: 100,
  case_insensitive: false,
  scroll_offset: 0
});
```

### `GET /api/v1/terminal/keys`

Returns the full list of key names accepted by `/api/v1/terminal/press`, including aliases and canonical forms. Useful for client-side validation and discoverability. Single printable characters (a-z, 0-9, punctuation) are also accepted but not listed individually.

This endpoint takes no parameters.

### Response



```json
{
  "keys": [
    "enter", "tab", "escape", "backspace", "space",
    "up", "down", "left", "right",
    "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
    "ctrl+a", "ctrl+b", "ctrl+c", "ctrl+d", "ctrl+e", "ctrl+f",
    "ctrl+g", "ctrl+h", "ctrl+i", "ctrl+j", "ctrl+k", "ctrl+l",
    "ctrl+m", "ctrl+n", "ctrl+o", "ctrl+p", "ctrl+q", "ctrl+r",
    "ctrl+s", "ctrl+t", "ctrl+u", "ctrl+v", "ctrl+w", "ctrl+x",
    "ctrl+y", "ctrl+z",
    "esc", "bs", "del", "home", "end", "page_up", "page_down",
    "insert", "kp_enter", "kp_plus", "kp_minus"
  ]
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```



### SDK

```ts
const { keys } = await client.terminal.terminalAutomation.listSupportedKeys();
```

## State & Metrics

### `GET /api/v1/terminal/{terminal_id}/automation`

Returns the VT parser state for a specific session: whether vterm is active, dimensions, update sequence counter, time since last screen change, alt-screen flag, title, scrollback length, and active waiter count. Useful for debugging automation workflows ("why did my wait timeout? did the screen actually change?").

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | path | string | Yes | Terminal session ID |

### Response



```json
{
  "terminal_id": 1042,
  "vterm_active": true,
  "dimensions": { "rows": 24, "cols": 80 },
  "update_seq": 4271,
  "ms_since_change": 142,
  "alt_screen": false,
  "title": "vim /etc/hosts",
  "scrollback_len": 1240,
  "active_waiters": 1
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Malformed terminal_id in the URL path (not numeric 1-65535)."
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```



### SDK

```ts
const state = await client.terminal.terminalAutomation.getSessionAutomationState({
  terminal_id: "1042"
});
```

### `GET /api/v1/terminal/automation/metrics`

Returns global metrics for the server-side VT parser: active vterm session count, memory used/cap in MB, total active wait-waiters across all sessions, and configured limits. Use to monitor resource usage, tune `--vterm-memory-cap-mb`, and detect leaks.

This endpoint takes no parameters.

### Response



```json
{
  "active_sessions": 14,
  "memory_used_mb": 87.4,
  "memory_cap_mb": 512,
  "active_waiters": 3,
  "limits": {
    "max_waiters_per_session": 16,
    "max_body_size_mb": 8
  }
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```



### SDK

```ts
const metrics = await client.terminal.terminalAutomation.getAutomationMetrics();
```

## Input

### `POST /api/v1/terminal/press`

Send one or more named key presses to a terminal session. Keys are encoded through libvterm's keyboard API which respects the terminal's current application-cursor mode (DECCKM) and keypad mode (DECKPAM), ensuring correct byte sequences for programs like `vim`, `htop`, and `tmux`.


All keys are validated before any are sent. A single unknown key rejects the entire request with no partial writes. Use `/api/v1/terminal/keys` to discover the supported key set.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | query | string | Yes | Terminal session ID |

### Request Body

Exactly one of `keys` or `key` is required.

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `keys` | array | No | Array of key names to press in sequence (e.g. `["ctrl+c", "arrow_up", "enter"]`). Mutually exclusive with `key`. Maximum 256 entries per request. |
| `key` | string | No | Single key name for one-shot press (e.g. `"enter"`). Mutually exclusive with `keys`. |

```json
{
  "keys": ["ctrl+c", "arrow_up", "enter"]
}
```

### Response



```json
{
  "terminal_id": 1042,
  "keys_pressed": 2,
  "bytes_written": 6
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Unknown key name or invalid request"
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "session_readonly"
}
```


```json
{
  "statusCode": 413,
  "error": "Payload Too Large",
  "message": "Request body exceeds --max-body-size cap (default 8 MB)."
}
```


```json
{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "Write to the session's PTY or socket failed, OR the per-request 1 MiB drain cap was hit mid-sequence."
}
```


```json
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "VTerm memory cap exceeded"
}
```



### SDK

```ts
await client.terminal.terminalAutomation.pressTerminalKeys({
  terminal_id: "1042",
  data: {
    keys: ["ctrl+c", "arrow_up", "enter"]
  }
});
```

### `POST /api/v1/terminal/paste`

Paste text into a terminal session with optional bracketed paste mode. When `bracketed=true` (default), the text is wrapped in bracketed paste escape sequences if the running program has enabled DECSET 2004 (e.g., `vim`, `zsh`), preventing auto-indent mangling and other paste artifacts. UTF-8 text including emoji and CJK is fully supported.


The envelope is only emitted when the running program has enabled DECSET 2004. The response's `bracketed_active` reflects whether libvterm actually emitted the envelope. `esc_neutralized` reports the count of input CSI-starter codepoints substituted with U+FFFD inside the envelope body to prevent an embedded end-marker from terminating the frame early.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | query | string | Yes | Terminal session ID |

### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | string | Yes | Text to paste (UTF-8) |
| `bracketed` | boolean | No | Use bracketed paste mode if the program supports it. Default: `true` |

```json
{
  "text": "git pull origin main\n",
  "bracketed": true
}
```

### Response



```json
{
  "terminal_id": 1042,
  "bytes_written": 1284,
  "bracketed_active": true,
  "esc_neutralized": 0
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid request"
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "session_readonly"
}
```


```json
{
  "statusCode": 413,
  "error": "Payload Too Large",
  "message": "Request body exceeds --max-body-size cap (default 8 MB)."
}
```


```json
{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "Write to the session's PTY or socket failed, OR the per-request 1 MiB paste drain cap was hit."
}
```


```json
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "VTerm memory cap exceeded"
}
```



### SDK

```ts
await client.terminal.terminalAutomation.pasteTerminalText({
  terminal_id: "1042",
  data: {
    text: "git pull origin main\n",
    bracketed: true
  }
});
```

## Synchronization

### `POST /api/v1/terminal/wait`

Block until a terminal condition is met, then return an atomic snapshot of the screen at the moment of resolution. The response includes a full snapshot for the `matched`, `stable`, `timeout`, and `exited` statuses so clients avoid a TOCTOU race between `wait` and a follow-up `/snapshot` call. The `vterm_reinit` status is the lone exception — it fires when the VT parser was torn down mid-wait (typically due to a memory-cap resize) and no coherent snapshot can be captured; the client should retry.


A maximum of 16 concurrent waiters per session is enforced. Excess waiters receive a 429.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | query | string | Yes | Terminal session ID |

### Request Body

`pattern` is required when `mode` is `regex` or `either`.

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `mode` | string | No | Wait mode: `stable`, `regex`, or `either`. Default: `stable` |
| `debounce_ms` | integer | No | Stable mode debounce in milliseconds (10-60000). Default: `100` |
| `pattern` | string | No | PCRE2 regex pattern (required for `regex`/`either` modes, max 1024 bytes) |
| `timeout_ms` | integer | No | Hard deadline in milliseconds (10-300000). Default: `5000` |
| `search_scope` | string | No | Where to search: `screen`, `scrollback`, or `all`. Default: `screen` |
| `include_colors` | boolean | No | Include `colored_lines` in response snapshot. Default: `false` |
| `include_highlights` | boolean | No | Include highlights in response snapshot. Default: `true` |

```json
{
  "mode": "stable",
  "debounce_ms": 150,
  "timeout_ms": 5000
}
```

### Response



```json
{
  "status": "matched",
  "match": {
    "row": 3,
    "col": 0,
    "text": "build successful"
  },
  "snapshot": {
    "terminal_id": 1042,
    "sequence": 4312,
    "title": "make",
    "alt_screen": false,
    "lines": [
      "$ make build",
      "Compiling project v1.2.3...",
      "build successful"
    ]
  }
}
```


```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid parameters or regex"
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Session not found"
}
```


```json
{
  "statusCode": 405,
  "error": "Method Not Allowed",
  "message": "method_not_allowed"
}
```


```json
{
  "statusCode": 413,
  "error": "Payload Too Large",
  "message": "Request body exceeds --max-body-size cap (default 8 MB)."
}
```


```json
{
  "statusCode": 429,
  "error": "Too Many Requests",
  "message": "Too many concurrent waiters"
}
```


```json
{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "Waiter could not be created (OOM)."
}
```


```json
{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "VTerm memory cap exceeded"
}
```



### SDK

```ts
const result = await client.terminal.terminalAutomation.waitForTerminal({
  terminal_id: "1042",
  data: {
    mode: "stable",
    debounce_ms: 150,
    timeout_ms: 5000
  }
});
```