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

# Streaming

> client.stream — a resumable async generator over a session's events.

`client.stream(sessionId, options?)` is the one consumer of `GET /v1/sessions/{id}/stream`. It yields `Event` objects (the core `EventSchema`) as an async generator, reconnecting across dropped connections without gaps or duplicates.

## Signature

```ts theme={"system"}
stream(sessionId: string, options?: StreamOptions): AsyncGenerator<Event>
```

```ts theme={"system"}
import type { StreamOptions } from "@usenaive-sdk/vetta";
```

<ResponseField name="afterSeq" type="number">Resume point. **Exclusive**: events with `seq > afterSeq` are delivered, so resuming from the last seq you saw does not replay it. Default `0` — from the beginning.</ResponseField>
<ResponseField name="maxRetries" type="number">Consecutive failed reconnects tolerated before the stream gives up. Default `5`.</ResponseField>

## Consuming

```ts theme={"system"}
let last = 0;
for await (const event of client.stream(sessionId, { afterSeq: last })) {
  last = event.seq;
  console.log(event.type, event.seq);
}
```

Track `event.seq` as you go; if your process restarts, pass the last one back as `afterSeq` and the stream picks up exactly where it left off.

## Semantics worth knowing

* **Ends at `session.idle`.** The generator returns as soon as the session yields control — it does not wait for the server to close the socket, which stays open for its own keepalive window.
* **Reconnects on drops.** A closed connection without an idle event is retried on the same cursor, up to `maxRetries` consecutive failures. A delivered event resets the failure count.
* **Duplicates are impossible.** Anything at or below the cursor is dropped on the floor, so an overlapping replay after a reconnect never emits twice.
* **A 4xx is not a drop.** Client errors (except `429`) are the server's answer — they are thrown as [`ApiError`](/docs/sdk/errors) immediately rather than retried.

## Polling instead

If SSE is awkward in your runtime, `sessions.events(id, { after_seq, limit })` reads the same log as pages — same events, same exclusive cursor. See [`sessions`](/docs/sdk/sessions#events).

## In a browser

Do not call `stream` from a browser: it would put the control-plane key in reachable memory. The dashboard re-serves the SSE body same-origin using the `send` escape hatch instead, so the key stays server-side.
