Skip to main content

Sessions

Sessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions.

Creating Sessions

Create a session by specifying the agent ID and initial input variables:

typescript

Create and Trigger in One Call

create() followed by attach().execute() is two round trips, and it forces you to create the session before you know whether the user will ever send a message. agentSessions.start() does both in a single streaming request: it creates the session and runs its first trigger, so per-session configuration (model, system-prompt inputs, anything routed through session input) is captured at the moment of the first message, and no session is created for a conversation that never starts.

typescript

After the first trigger, the handle behaves exactly like one from attach() - continuations and later triggers automatically target the now-created session. Calling execute() with a continue request before the first trigger throws, since there is no session to continue yet.

Learning the session id

The created session id reaches you through two channels:

  • onSessionCreated(sessionId) - fired as soon as the id is known, before events stream. This is the ergonomic way to persist your chat-to-session mapping.
  • The first start event - carries sessionId, which the client SDK also surfaces (see Client SDK Overview). Useful when the browser needs the id, or for transports that do not expose response headers.

getSessionId() returns undefined on a deferred handle until the first trigger creates the session, then returns the id.

Retry safety

start() sends a client-supplied idempotencyKey (auto-generated if you omit it). A transient retry of the first request resolves to the same session instead of creating a duplicate or double-counting session usage, so it is safe to retry. Using a stable, meaningful key such as your chatId guarantees one session per chat even across retries or double submits.

Start Options

OptionTypeDescription
agentIdstringAgent to start a session for
inputRecord<string, unknown>Immutable session input, captured at the first trigger
idempotencyKeystringMakes a retried start resolve to the same session; auto-generated
onSessionCreated(sessionId: string) => voidCalled with the session id as soon as it is assigned

start() also accepts the same tools, resources, mcpServers, onToolResults, and rejectClientToolCalls options as attach().

The two-step create() + attach() flow is unchanged and fully supported. start() is additive - reach for it when you want to defer creation to the first message.

Getting Session Messages

To restore a conversation on page load, use getMessages() to retrieve UI-ready messages:

typescript

The returned messages can be passed directly to the client SDK's initialMessages option.

UISessionState Interface

typescript

Full Session State (Debug)

For debugging or internal use, you can retrieve the complete session state including all variables and internal message format:

typescript

Note: Use getMessages() for client-facing code. The get() method returns internal message format that includes hidden content not intended for end users.

Getting Execution Logs

getLogs() returns the chronological execution trace for a session - triggers, messages, tool calls, LLM responses, errors, and other events emitted while the agent ran. Useful for debugging, observability, and building custom timeline views.

typescript

Each entry is a typed variant of ExecutionLogEntry (a discriminated union) so consumers can narrow on entry.type:

typescript

Excluding Model Request Payloads

Model-request entries include the full provider request body and can be large. Pass excludeModelRequests: true to skip them:

typescript

Truncation

Responses are capped at 1000 entries (most recent). When the log exceeds that cap, the response includes total and truncated so consumers can detect this:

typescript

Response Types

StatusTypeDescription
activeExecutionLogsResult{ sessionId, entries, total?, truncated? }. total and truncated are present when known
expiredExpiredSessionState{ sessionId, agentId, status: 'expired', createdAt }

Forward-compatible types: ExecutionLogEntry may gain new variants over time. Include a default case when switching on entry.type so unknown variants are handled gracefully.

Attaching to Sessions

To trigger actions on a session, you need to attach to it first:

typescript

Attach Options

OptionTypeDescription
toolsToolHandlersServer-side tool handler functions
resourcesResource[]Deprecated - resource watchers for real-time updates. Use tools instead.
onToolResults(results: ToolResult[]) => voidCallback invoked after server-side tool results are produced
rejectClientToolCallsbooleanIf true, reject tool calls that have no server handler (no client forwarding)

For MCP tool integration (browser, filesystem, shell via @octavus/computer), register dynamic tools after attaching with session.setDynamicTools(). See Computer for details.

Executing Requests

Once attached, execute requests on the session using execute():

typescript

Request Types

The execute() method accepts a discriminated union:

typescript

This makes it easy to pass requests through from the client:

typescript

Attributing Messages in Multi-User Chats

When several people share one conversation, set sender on the trigger so each user message is attributed to its author. Set it server-side from your authenticated user - never trust a client-supplied identity:

typescript

The runtime stamps the sender onto the user message it creates, so it comes back on UIMessage.sender from getMessages() and survives restore. sender is turn metadata - it is never added to your protocol's trigger input, and agent-initiated turns (no sender) stay unattributed. For instant optimistic display in the browser, also pass it on the client send() (see Client SDK Messages).

Stop Support

Pass an abort signal to allow clients to stop generation:

typescript

When the client aborts the request, the signal propagates through to the LLM provider, stopping generation immediately. Any partial content is preserved.

WebSocket Handling

For WebSocket integrations, use handleSocketMessage() which manages abort controller lifecycle internally:

typescript

The handleSocketMessage() method:

  • Handles trigger, continue, and stop messages
  • Automatically aborts previous requests when a new one arrives
  • Streams events via the onEvent callback
  • Calls onFinish after streaming completes (not called if aborted)

See Socket Chat Example for a complete implementation.

Session Lifecycle

Session Expiration

Sessions expire after a period of inactivity (default: 24 hours). When you call getMessages() or get(), the response includes a status field:

typescript

Response Types

StatusTypeDescription
activeUISessionStateSession is active, includes messages array
expiredExpiredSessionStateSession expired, includes sessionId, agentId, createdAt

Persisting Chat History

To enable session restoration, store the chat messages in your own database after each interaction:

typescript

Best Practice: Store the full UIMessage[] array. This preserves all message parts (text, tool calls, files, etc.) needed for accurate restoration.

Restoring Sessions

When a user returns to your app:

typescript

Restore Response

typescript

Complete Example

Here's a complete session management flow:

typescript

Clearing Sessions

To programmatically clear a session's state (e.g., for testing reset/restore flows), use clear():

typescript

After clearing, the session transitions to expired status. You can then restore it with restore() or create a new session.

typescript

This is idempotent - calling clear() on an already expired session succeeds without error.

Error Handling

typescript