---
title: Session State
description: Persist and resume eve client sessions with durable session IDs and stream cursors.
---

# Session State



The TypeScript client uses one durable session ID for messages, controls, and
streams. A `ClientSession` adds a local `streamIndex` cursor so reconnects do not
replay events already consumed by that handle.

## Read and persist state

Create the session with its first turn, then persist `session.state` after
consuming the response:

```ts
const { session, response } = await client.sessions.create({
  message: "Create a launch checklist.",
});

await response.result();
await saveSessionState(session.state);
```

The state is deliberately small:

```ts
interface ClientSessionState {
  sessionId: string;
  streamIndex: number;
}
```

It is a remote stream cursor, not a transcript. Persist events separately when
your application renders chat history.

## Resume a saved session

Attach a fixed handle to the saved ID and restore its cursor:

```ts
import type { ClientSessionState } from "eve/client";

const saved = (await loadSessionState()) as ClientSessionState;
const session = client.sessions.attach(saved.sessionId, {
  streamIndex: saved.streamIndex,
});

const response = await session.send("Now shorten it.");
console.log((await response.result()).message);
```

`attach()` performs no request. Every later operation targets exactly
`saved.sessionId`; it never follows or creates a replacement.

`send()` correlates its response with the message accepted by the server. If the
saved cursor is behind, it skips earlier turns and advances past them before
collecting the new result, including when another turn is active or queued. You
do not need to drain the stream before sending. Upgrade the server alongside the
client: a server that omits the accepted delivery identity causes `send()` to
throw instead of returning an ambiguous result.

If a configured reconnect limit ends the stream before the accepted turn reaches
a boundary, collecting its response throws. A partial stream is not a completed
result.

## Waiting, completed, and reset sessions

A session that emits `session.waiting` accepts another message through the same
handle. A terminal or reset session does not. Start a fresh conversation
explicitly with another `client.sessions.create(firstTurn)` call.

```ts
const reset = await session.reset({ reason: "Start over" });
const { session: fresh, response } = await client.sessions.create({ message: "Begin again." });
```

The old `session` remains pinned to its retired ID; `fresh` owns a different ID.

## Multiple sessions

Create one handle per conversation:

```ts
const { session: research, response: researchResponse } = await client.sessions.create({
  message: "Research competitors.",
});
const { session: support, response: supportResponse } = await client.sessions.create({
  message: "Draft a support reply.",
});

await Promise.all([researchResponse.result(), supportResponse.result()]);
await save("research", research.state);
await save("support", support.state);
```

The shared `Client` owns host, auth, headers, and redirect policy. Each
`ClientSession` owns its fixed ID and stream cursor.

## Reconnect an existing stream

`stream()` starts from the handle's saved cursor and advances it as events are
read:

```ts
const session = client.sessions.attach(saved.sessionId, {
  streamIndex: saved.streamIndex,
});

for await (const event of session.stream()) {
  console.log(event.type);
}
```

Use `send()` for new input. For explicit cursors, tail-relative reads, and
bounded catch-up, see [Streaming](./streaming#open-a-stream-manually).

## What to read next

* [Streaming](./streaming): stream events and reconnect by index
* [Sessions, runs & streaming](../../concepts/sessions-runs-and-streaming): the raw HTTP contract
* [eve channel](../../channels/eve): the ID-addressed routes


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)