Skip to content

Session Management ​

Sessions enable multi-turn conversations by persisting message history between requests. The server manages session creation, expiration, and cleanup automatically.

Overview ​

Create
Active
Inactive
Expired

Cleanup sweep runs every 5 minutes

Storage ​

Sessions are stored as individual JSON files on disk:

  • 📁 ./.seepient/sessions/
    • 📄 550e8400-e29b-41d4-a716-446655440000.json
    • 📄 660f9511-f3ac-52e5-b827-557766551111.json
    • 📄 ...

The session directory defaults to ./.seepient/sessions/ in the working directory from which the server is started, and can be overridden with the SEEPIENT_SESSION_DIR environment variable.

Session file format ​

Each file contains a single session as JSON:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "messages": [
    {
      "id": "msg_001",
      "role": "user",
      "content": "What is closures in JavaScript?",
      "timestamp": 1712505600000
    },
    {
      "id": "msg_002",
      "role": "assistant",
      "content": "A closure is a function that has access to variables from its outer scope...",
      "timestamp": 1712505602000
    }
  ],
  "createdAt": 1712505600000,
  "updatedAt": 1712505602000,
  "provider": "anthropic",
  "model": "claude-sonnet-4-6-20260320"
}
FieldTypeDescription
idstringSession UUID (v4)
messagesarrayOrdered list of messages
messages[].idstringMessage UUID
messages[].rolestring"user" or "assistant"
messages[].contentstringMessage text
messages[].timestampnumberUnix timestamp in milliseconds
createdAtnumberSession creation timestamp (ms)
updatedAtnumberLast update timestamp (ms)
providerstringLLM provider (optional)
modelstringModel ID (optional)

TTL and expiration ​

Sessions expire based on two independent conditions:

ConditionDefaultConfigurable
Absolute TTL24 hours from creationSEEPIENT_SESSION_TTL env (seconds) or sessionTTL server option
Inactivity timeout30 minutes since last activityinactivityTimeout server option (milliseconds)

A session is considered expired when either condition is met. Expired sessions are automatically deleted from memory and disk.

Configuring TTL ​

typescript
import { runSeepientServer } from "seepient/server";

await runSeepientServer({
  sessionTTL: 7200, // 2 hours in seconds
});

Or via environment variable:

bash
SEEPIENT_SESSION_TTL=7200 seepient server

Concurrency limits ​

Each API key is limited to 5 concurrent active sessions by default. When the limit is reached, creating a new session returns an error.

Maximum concurrent sessions (5) reached for this API key.

To free up a slot, either wait for sessions to expire or delete them explicitly.

Auto-cleanup ​

The server runs a cleanup sweep every 5 minutes that:

  1. Iterates all in-memory sessions
  2. Checks TTL and inactivity conditions
  3. Deletes expired sessions from memory and disk

The cleanup timer uses .unref() so it does not prevent graceful process shutdown.

Session creation & attachment ​

Sessions are created and attached explicitly by specifying a sessionId string. Omission of sessionId (or sessionId: null/undefined) executes a stateless one-shot turn per Decision D1: no session is created in memory or on disk, and message history is neither loaded nor persisted.

Via WebSocket ​

To execute a session-backed turn, provide a client-generated or known sessionId:

json
{
  "type": "chat",
  "id": "msg-001",
  "message": "Hello!",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}

If the session does not exist, the server creates it under the authenticated API key; subsequent messages referencing that sessionId append to its history.

If sessionId is omitted or null, the WebSocket executes a stateless one-shot turn without attaching to or creating a session.

Via REST ​

The REST /v1/chat endpoint supports both stateless turns and session-backed turns:

bash
curl -X POST http://localhost:7337/v1/chat \
  -H "Authorization: Bearer seepient_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Hello!",
    "sessionId": "550e8400-e29b-41d4-a716-446655440000"
  }'

When sessionId is provided in the request body, the server loads the session history, acquires an in-flight turn lock, persists the user message before generation, and persists the assistant response on completion. If sessionId is omitted, the turn executes statelessly without persistence.

Reconnection protocol ​

When a WebSocket connection drops, clients can recover their session state:

1. Reconnect with reconnect ​

json
{
  "type": "reconnect",
  "sessionId": "550e8400-...",
  "lastSeenId": "msg_005"
}

The server responds with a replay message containing any messages after lastSeenId:

json
{
  "type": "replay",
  "messages": [
    { "id": "msg_006", "role": "assistant", "content": "...", "timestamp": 1712505610000 }
  ],
  "currentStatus": "ready"
}

2. Resume with resume ​

json
{
  "type": "resume",
  "sessionId": "550e8400-..."
}

The server responds with the full session history:

json
{
  "type": "session_resumed",
  "sessionId": "550e8400-...",
  "messages": [/* all messages */]
}

3. Session not found ​

If the session has expired or does not exist:

json
{
  "type": "error",
  "code": "SESSION_NOT_FOUND",
  "retryable": false,
  "message": "Session 550e8400-... not found or expired"
}

Reconnect vs Resume

Use reconnect when you know the last message you received and only need the delta. Use resume when you need the full conversation history (e.g., page refresh).

Durability & Persistence Guarantees ​

  • Audit writes are durable: Security and audit log entries perform fsync before committing, guaranteeing durability across crashes.
  • Chat history persistence is best-effort: Chat turn persistence is asynchronous and fire-and-forget; answered is not persisted immediately on disk before the HTTP response or WebSocket done frame is sent. In the event of an immediate process crash or power loss, the final turn may not be persisted.
  • Atomic writes: File persistence uses atomic temporary file writes and rename operations. A crash mid-write never leaves a corrupt session file.
  • Restart survival: The server enumerates durable sessions across server restarts via FilePersistenceBackend.list(). When using in-memory storage, GET /v1/sessions reports source: "memory" to explicitly document why historical sessions are absent after restart.

Released under the Business Source License 1.1.