Sessions
A session is the unit of conversation persistence in Agentrail. It holds the message history for a single user's ongoing conversation with an agent.
What a Session Is
Each session has:
- a unique
sessionId(UUID) - an owner identity:
tenantId+userId+agentId - an ordered list of messages (the conversation history)
- a cumulative token usage record
Sessions are created on the first request and resumed on subsequent requests by passing the sessionId in the request body. If no sessionId is provided, a new session is created automatically.
Session Lifecycle
First request (no sessionId)
│
▼
getOrCreate → new session created → sessionId returned in response
│
▼
Client stores sessionId and sends it on subsequent requests
│
▼
getOrCreate → existing session resumedSession Store Contract
The host layer depends on the AgentrailSessionStore contract rather than any concrete storage implementation. This means you can swap out the storage backend without changing your host code.
The required contract includes:
| Method | Called when |
|---|---|
getOrCreate | Every request — creates or resumes the session |
loadMessagesWithBudget | Every request — loads history trimmed to the token budget |
loadAllMessages | During compaction — loads full history to assess whether to compact |
appendMessages | After agent completes — persists the new turn's messages |
recordTurn | After agent completes — persists token usage |
compactIfNeeded | During each request — summarizes old history if token threshold is exceeded |
Additional optional methods (readMemoryDocument, writeMemoryDocument, readToolResultArtifact, writeToolResultArtifact) unlock memo tools and tool-result compaction. When also passing your store as memoProvider to SandboxManager, implement listToolResultArtifactIds as part of the SandboxMemoProvider contract to enable full /workspace/memo/** sandbox access. See Session Store Reference for the complete interface.
Default Implementation: SessionManager
The default session store is SessionManager from @agentrail/app. It uses the local filesystem to persist sessions, with one directory per session:
import { SessionManager } from "@agentrail/app";
const sessionManager = new SessionManager("/tmp/agentrail-sessions");The path is the root directory under which all tenant/session data is stored. In production, point dataDir at a persistent volume so sessions survive container restarts. For multi-instance deployments, mount a shared filesystem (NFS, EFS, etc.) at the same path on all instances.
Turn Persistence
A turn is one complete interaction: a user message followed by the agent's full response (which may involve multiple LLM calls and tool executions).
After each turn, the host:
- Calls
appendMessagesto write the new user and assistant messages - Calls
recordTurnto record token usage for billing or observability
During streaming, appendMessages is called incrementally after each internal reasoning turn (one LLM call plus any tool executions it triggers), rather than once at the end of the full interaction. This means messages are durable on disk as soon as each reasoning step completes, not only after the SSE stream closes. recordTurn is still called once when the entire interaction finishes. Plugins can react to this via the onTurnPersisted hook.
Note: "internal reasoning turn" here refers to the runtime's
turn.completeevent, which is distinct from the public session concept of a "turn" (one full user interaction).
History Loading with Token Budget
The host does not load unlimited history. On each request, it calls loadMessagesWithBudget to retrieve as many recent messages as fit within the model's context window (minus room for the current turn and context providers).
This ensures the context window is never overflowed by long sessions. The contextWindow field on the profile controls the token limit used for this calculation.
For long-running single requests, Agentrail can also apply in-loop reactive compaction before the next model call. That path rewrites only the in-memory request history; persisted session files are still managed by request-boundary compaction.
Implementing a Custom Store
For most deployments, SessionManager with a persistent or shared volume is the recommended approach. For advanced use cases where the AgentrailSessionStore interface needs to be implemented from scratch (for example, integrating with an existing message persistence layer or adding custom access controls), any object satisfying the interface can be passed via sessionStore.
See the Session Store Reference for the full interface and Build a Storage Backend for a complete implementation guide.