# Session Management OpenClaw employs a centralized, file-based session management system that handles persistence, lifecycle events, and automated store maintenance. Unlike simple memory-only stores, this system is designed for high-concurrency environments with atomic write guarantees and strict disk budget enforcement. *** ## SessionEntry type The `SessionEntry` structure in `src/config/sessions/types.ts` is the core data model, containing nearly 80 fields that track everything from model overrides to granular token usage and skill snapshots. ```typescript export type SessionEntry = { sessionId: string; updatedAt: number; sessionFile?: string; // Model and Provider Overrides modelProvider?: string; model?: string; providerOverride?: string; modelOverride?: string; thinkingLevel?: string; // Token Tracking and Context inputTokens?: number; outputTokens?: number; totalTokens?: number; totalTokensFresh?: boolean; compactionCount?: number; // Delivery and Origin deliveryContext?: DeliveryContext; lastChannel?: SessionChannelId; origin?: SessionOrigin; // Meta and Snapshots skillsSnapshot?: SessionSkillSnapshot; systemPromptReport?: SessionSystemPromptReport; acp?: SessionAcpMeta; // Agent Control Plane metadata queueMode?: "steer" | "followup" | "collect" | "queue" | "interrupt"; }; ``` Key features of the type system: * **Model Overrides**: Allows per-session binding to specific models or providers, bypassing global defaults. * **Token Intelligence**: Tracks `totalTokensFresh` to determine if usage displays need a refresh. * **ACP Meta**: Preserves state for the Agent Control Plane, ensuring persistent agent behaviors across turns. * **Skill Snapshots**: Captures the state of available [[Skills]] at the time of the session turn. *** ## Store Maintenance Automated maintenance is handled in `src/config/sessions/store-maintenance.ts` to prevent unbounded growth of the session file and ensure performance. ### Pruning and Capping * **`pruneStaleEntries()`**: Removes sessions older than a configured threshold (default: 30 days). It iterates through the store and deletes entries where `updatedAt` is before the cutoff. * **`capEntryCount()`**: Enforces a maximum number of sessions (default: 500). It sorts sessions by `updatedAt` descending and keeps only the most recent $N$ entries. ### File Rotation The `rotateSessionFile()` function monitors the `sessions.json` file size. If it exceeds the limit (default: 10MB), the system: 1. Renames the current file to `sessions.json.bak.{timestamp}`. 2. Maintains a maximum of 3 rotation backups, unlinking older ones. ### Maintenance Configuration The `resolveMaintenanceConfig()` function aggregates parameters from [[Configuration]]: * `pruneAfterMs`: Max age before eviction. * `maxEntries`: Hard limit on session count. * `rotateBytes`: File size trigger for rotation. * `maxDiskBytes`: Total disk budget for session transcripts. *** ## Session Store The `SessionStore` in `src/config/sessions/store.ts` manages the I/O layer with a focus on reliability and cross-platform compatibility. ### Key Features * **Atomic Writes**: Uses `writeTextAtomic` to prevent file corruption during crashes. * **Write Locks**: Implements a `withSessionStoreLock` mechanism using a file-based lock to prevent race conditions between concurrent agent turns. * **Normalization**: Session keys are case-insensitive and trimmed via `normalizeStoreSessionKey()`. Legacy keys are automatically migrated to the normalized format on load. * **Windows Retry Semantics**: Includes specialized retry logic for Windows to handle transient file locks during the "truncate and write" phase. * **ACP Metadata Preservation**: Specifically protects `acp` metadata during updates, ensuring agent state isn't lost if a partial patch is applied. ```typescript export async function updateSessionStore( storePath: string, mutator: (store: Record) => Promise | T, opts?: SaveSessionStoreOptions, ): Promise { return await withSessionStoreLock(storePath, async () => { const store = loadSessionStore(storePath, { skipCache: true }); const previousAcpByKey = collectAcpMetadataSnapshot(store); const result = await mutator(store); preserveExistingAcpMetadata({ previousAcpByKey, nextStore: store, }); await saveSessionStoreUnlocked(storePath, store, opts); return result; }); } ``` *** ## Session Lifecycle Events A lightweight pub/sub system in `src/sessions/session-lifecycle-events.ts` allows other subsystems to react to session changes. ```typescript export type SessionLifecycleEvent = { sessionKey: string; reason: string; parentSessionKey?: string; label?: string; }; export function onSessionLifecycleEvent(listener: SessionLifecycleListener): () => void { SESSION_LIFECYCLE_LISTENERS.add(listener); return () => { SESSION_LIFECYCLE_LISTENERS.delete(listener); }; } export function emitSessionLifecycleEvent(event: SessionLifecycleEvent): void { for (const listener of SESSION_LIFECYCLE_LISTENERS) { listener(event); } } ``` This system is used to trigger [[Memory]] indexing, update [[Channels]] status, or log audit trails when sessions are created or deleted. *** ## Relevance to Luna Luna's current `SessionManager` is a basic dictionary-backed store that lacks persistence and maintenance. To reach parity with OpenClaw, Luna should adopt several patterns: 1. **Atomic Persistence**: Implement a background saver for [[Core]] sessions that uses atomic file swaps to prevent data loss. 2. **Maintenance Tasks**: Add a background service to prune old sessions and cap the total count to prevent memory leaks in the .NET runtime. 3. **Locking**: Use a `SemaphoreSlim` or file-system lock for session updates to support concurrent requests from different [[Channels]]. 4. **Detailed Metadata**: Expand Luna's session objects to include token tracking and model overrides, allowing for better cost management and user customization. Refer to [[Core]] for the current implementation status of Luna's session handling.