> ## 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.

# Session Handle

> A stateful object that owns a PTY stream, buffers its output and waits for exit

The raw [stream](/documentation/agentruntime/pty-sessions/stream-output) endpoint is a
one-shot iterator: you attach, you consume, and if you want to know what was printed you
accumulate it yourself. A handle wraps that into a stateful object that owns the connection
for you.

A handle reads the stream on a background thread (or asyncio task), buffers the output,
records the exit code, and lets you ask "is it connected yet?" and "has it finished?"
without writing that bookkeeping in your own code. It is the natural shape for driving an
interactive program: start it, wait for the prompt, send input, wait for exit.

<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)

  with sandbox.pty.handle(session.session_id) as pty:
      pty.connect()
      pty.wait_for_connection(timeout=10)

      pty.send_input("python3 -c 'print(6 * 7)'\n")
      result = pty.wait_for_completion(timeout=30)

      print(pty.output.decode("utf-8", "replace"))
      print("exit code:", pty.exit_code)

  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 });

  const pty = sandbox.pty.handle(session.sessionId).connect();
  await pty.sendInput("python3 -c 'print(6 * 7)'\n");
  const result = await pty.waitForExit(30_000);

  console.log(pty.text);
  console.log('exit code:', result.exitCode);

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

Creating a handle performs no I/O. Nothing is sent until you call `connect()`.

## Async

`AsyncPtyHandle` mirrors the same surface with `await` and an async context manager.

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

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

  async with sandbox.pty.handle(session.session_id) as pty:
      await pty.connect()
      await pty.wait_for_connection(timeout=10)
      await pty.send_input("uname -a\n")
      await pty.wait_for_completion(timeout=30)
      print(pty.output.decode("utf-8", "replace"))

  await 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();

  const pty = sandbox.pty.handle(session.sessionId).connect();
  await pty.sendInput('uname -a\n');
  await pty.waitForExit(30_000);
  console.log(pty.text);
  await pty.disconnect();
  await sandbox.kill();
  ```
</CodeGroup>

## Methods

| Python                                | TypeScript                    | Description                                                                              |
| ------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------- |
| `connect(on_data=None, on_exit=None)` | `connect({ onData, onExit })` | Start reading the stream. Optional callbacks fire per output chunk and on exit           |
| `wait_for_connection(timeout=None)`   | —                             | Block until the stream has opened (Python). TypeScript `connect()` returns once attached |
| `wait_for_completion(timeout=None)`   | `waitForExit(timeoutMs)`      | Wait until the session process exits                                                     |
| `disconnect()`                        | `disconnect()`                | Stop reading. The guest session keeps running                                            |
| `refresh()`                           | —                             | Re-fetch the session record                                                              |
| `send_input(data)`                    | `sendInput(data)`             | Write to stdin (`str` / `bytes`, or `string` / `Uint8Array`)                             |
| `resize(cols, rows)`                  | `resize(cols, rows)`          | Resize the terminal                                                                      |
| `send_signal(signal)`                 | `sendSignal(signal)`          | Deliver a signal, for example `"SIGINT"`                                                 |
| `kill()`                              | `kill()`                      | Terminate the session                                                                    |

## Properties

| Property       | Type                   | Description                                        |
| -------------- | ---------------------- | -------------------------------------------------- |
| `runtime_id`   | string                 | Runtime the session belongs to                     |
| `session_id`   | string                 | Session being followed                             |
| `is_connected` | boolean                | Whether the stream is open **right now**           |
| `output`       | bytes                  | Buffered output since `connect()`, capped at 1 MiB |
| `exit_code`    | integer or `None`      | Exit code once the process has exited              |
| `error`        | string or `None`       | Stream error message, if the stream failed         |
| `session`      | `PtySession` or `None` | Last observed session record                       |

## Behaviour notes

* **`wait_for_connection` means "was opened", not "still open".** A short-lived command can
  open, produce output and finish before you call it; that still returns `True`. Use
  `is_connected` when you specifically need to know whether the stream is open at this
  instant.
* **`wait_for_connection` auto-connects only once.** If you never called `connect()` it
  connects for you. It will not silently re-attach a stream that has already finished, so
  buffered output is never duplicated.
* **The output buffer is bounded at 1 MiB.** Once full, the oldest bytes are dropped. For a
  complete transcript, pass an `on_data` callback and write the chunks yourself, or redirect
  the program's output to a file in the runtime.
* **Disconnecting does not kill the session.** The guest session and its scrollback survive,
  so you can re-attach later with a fresh handle. Call `kill()` to terminate it.
* **The context manager disconnects on exit**, including on exception, so a handle cannot
  leak a reader thread or an open HTTP response.
