UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

122 lines (89 loc) 3.87 kB
--- title: "Session State" description: "Persist and resume eve client sessions with durable session IDs and stream cursors." --- 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