> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gravixlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# PTY Sessions Overview

> Programmatic pseudo-terminal sessions that outlive the client connection

A PTY session is a real pseudo-terminal allocated inside the runtime and owned by the
execution plane rather than by the connection that created it. Create a session, stream its
output, drop the connection, and re-attach later to the same shell with its scrollback
intact.

This is the programmatic counterpart to the interactive
[web terminal](/documentation/agentruntime/ssh-access/web-terminal). Use it when an agent
needs to drive an interactive process rather than run one-shot commands:

* A REPL that holds state between inputs.
* An installer or migration tool that asks questions part way through.
* A TUI, a pager, or anything that behaves differently when it detects a terminal.
* A long running foreground job you want to interrupt with Ctrl-C rather than kill.

Because it is a genuine PTY, programs see a terminal device: `isatty` is true, line editing
and job control work, `SIGWINCH` is delivered on resize, and colour output is enabled.

## Lifecycle

<Steps>
  <Step title="Create">
    `POST /v1/agents/runtime/{runtime_id}/pty` starts a shell and returns a `session_id`.
    The session keeps running after the call returns.
  </Step>

  <Step title="Stream">
    `GET .../pty/{session_id}/stream` replays the retained scrollback and then follows live
    output. Multiple readers can stream the same session.
  </Step>

  <Step title="Drive">
    `POST .../pty/{session_id}/input` writes to the terminal.
    `.../resize` and `.../signal` control the terminal geometry and the foreground job.
  </Step>

  <Step title="Kill">
    `DELETE .../pty/{session_id}` terminates the session. Sessions are also reaped when the
    runtime is stopped or deleted.
  </Step>
</Steps>

## Endpoints

| Method   | Path                                              | Purpose                                                                        |
| -------- | ------------------------------------------------- | ------------------------------------------------------------------------------ |
| `POST`   | `/v1/agents/runtime/{id}/pty`                     | [Create a session](/documentation/agentruntime/pty-sessions/create-session)    |
| `GET`    | `/v1/agents/runtime/{id}/pty`                     | [List sessions](/documentation/agentruntime/pty-sessions/manage-sessions)      |
| `GET`    | `/v1/agents/runtime/{id}/pty/{session_id}`        | [Describe a session](/documentation/agentruntime/pty-sessions/manage-sessions) |
| `GET`    | `/v1/agents/runtime/{id}/pty/{session_id}/stream` | [Stream output](/documentation/agentruntime/pty-sessions/stream-output)        |
| `POST`   | `/v1/agents/runtime/{id}/pty/{session_id}/input`  | [Send input](/documentation/agentruntime/pty-sessions/send-input)              |
| `POST`   | `/v1/agents/runtime/{id}/pty/{session_id}/resize` | [Resize](/documentation/agentruntime/pty-sessions/manage-sessions)             |
| `POST`   | `/v1/agents/runtime/{id}/pty/{session_id}/signal` | [Send a signal](/documentation/agentruntime/pty-sessions/manage-sessions)      |
| `DELETE` | `/v1/agents/runtime/{id}/pty/{session_id}`        | [Kill a session](/documentation/agentruntime/pty-sessions/manage-sessions)     |

## End to end example

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from gravixlayer import GravixLayer

  client = GravixLayer()  # defaults to cloud="aws", region="us-east-1"
  sandbox = client.runtime.create()  # defaults to template="base-small"

  session = sandbox.pty.create(cols=120, rows=40)
  sandbox.pty.send_input(session.session_id, "python3\n")
  sandbox.pty.send_input(session.session_id, "2 ** 100\n")

  for event in sandbox.pty.stream(session.session_id):
      if event["type"] == "data":
          text = event["data"].decode("utf-8", "replace")
          print(text, end="")
          if ">>>" in text and "1267650600228229401496703205376" in text:
              break

  sandbox.pty.kill(session.session_id)
  sandbox.kill()
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { GravixLayer } from 'gravixlayer';

  const client = new GravixLayer(); // defaults to cloud="aws", region="us-east-1"
  const sandbox = await client.runtime.create(); // defaults to template="base-small"

  const session = await sandbox.pty.create({ cols: 120, rows: 40 });
  await sandbox.pty.sendInput(session.sessionId, 'python3\n');
  await sandbox.pty.sendInput(session.sessionId, '2 ** 100\n');

  for await (const event of sandbox.pty.stream(session.sessionId)) {
    if (event.type === 'data') {
      const text = new TextDecoder().decode(event.data);
      process.stdout.write(text);
      if (text.includes('>>>') && text.includes('1267650600228229401496703205376')) {
        break;
      }
    }
  }

  await sandbox.pty.kill(session.sessionId);
  await sandbox.kill();
  ```
</CodeGroup>

## Session object (`PtySession`)

| Field          | Type    | Description                                 |
| -------------- | ------- | ------------------------------------------- |
| `session_id`   | string  | Identifier used by every other PTY endpoint |
| `runtime_id`   | string  | Owning runtime                              |
| `pid`          | integer | Process ID of the shell inside the guest    |
| `shell`        | string  | Shell that was launched                     |
| `args`         | array   | Arguments passed to the shell               |
| `working_dir`  | string  | Initial working directory                   |
| `cols`, `rows` | integer | Current terminal geometry                   |
| `status`       | string  | `running` or `exited`                       |
| `exit_code`    | integer | Meaningful only once `status` is `exited`   |
| `created_at`   | string  | RFC3339 creation timestamp                  |

## Limits

* **8 concurrent sessions per runtime.** Creating a ninth fails; kill sessions you no longer
  need.
* **256 KB of scrollback per session.** Older output is discarded as new output arrives, so
  a session that has been unattended for a long time replays only its recent tail.
* **Exited sessions are retained briefly** so you can read the final output and exit code,
  then reaped automatically.
* **Only permitted shells may be launched.** The `shell` argument is validated against the
  runtime's allowlist rather than executed directly.
