OpenClawSkills
GitHub
Reference β€’ TutorialHeader.readTime

Session Management Deep Dive

Deep dive: session store + transcripts, lifecycle, and (auto)compaction internals

This document explains how OpenClaw manages sessions end-to-end:

- Session routing (how inbound messages map to a sessionKey)

- Session store (sessions.json) and what it tracks

- Transcript persistence (*.jsonl) and its structure

- Transcript hygiene (provider-specific fixups before runs)

- Context limits (context window vs tracked tokens)

- Compaction (manual + auto-compaction) and where to hook pre-compaction work

- Silent housekeeping (e.g. memory writes that shouldn't produce user-visible output)

If you want a higher-level overview first, start with:

- /concepts/session

- /concepts/compaction

- /concepts/session-pruning

- /reference/transcript-hygiene

Tutorial.step

Two persistence layers

OpenClaw persists sessions in two layers:

1. Session store (sessions.json)

- Key/value map: sessionKey -> SessionEntry

- Small, mutable, safe to edit (or delete entries)

- Tracks session metadata (current session id, last activity, toggles, token counters, etc.)

2. Transcript (<sessionId>.jsonl)

- Append-only transcript with tree structure (entries have id + parentId)

- Stores actual conversation + tool calls + compaction summaries

- Used to rebuild model context for future turns

Tutorial.step

Session keys (`sessionKey`)

A sessionKey identifies _which conversation bucket_ you're in (routing + isolation).

Common patterns:

- Main/direct chat (per agent): agent:<agentId>:<mainKey> (default main)

- Group: agent:<agentId>:<channel>:group:<id>

- Room/channel (Discord/Slack): agent:<agentId>:<channel>:channel:<id> or ...:room:<id>

- Cron: cron:<job.id>

- Webhook: hook:<uuid> (unless overridden)

The canonical rules are documented at /concepts/session.

Tutorial.step

Session store schema (`sessions.json`)

The store's value type is SessionEntry in src/config/sessions.ts.

Key fields (not exhaustive):

- sessionId: current transcript id (filename is derived from this unless sessionFile is set)

- updatedAt: last activity timestamp

- sessionFile: optional explicit transcript path override

- chatType: direct | group | room (helps UIs and send policy)

- provider, subject, room, space, displayName: metadata for group/channel labeling

Toggles:

- thinkingLevel, verboseLevel, reasoningLevel, elevatedLevel

- sendPolicy (per-session override)

Model selection:

- providerOverride, modelOverride, authProfileOverride

Token counters (best-effort / provider-dependent):

- inputTokens, outputTokens, totalTokens, contextTokens

- compactionCount: how often auto-compaction completed for this session key

- memoryFlushAt: timestamp for last pre-compaction memory flush

- memoryFlushCompactionCount: compaction count when last flush ran

The store is safe to edit, but Gateway is authority: it may rewrite or rehydrate entries as sessions run.

Tutorial.step

Context windows vs tracked tokens

Two different concepts matter:

1. Model context window: hard cap per model (tokens visible to model)

2. Session store counters: rolling stats written into sessions.json (used for /status and dashboards)

If you're tuning limits:

- The context window comes from model catalog (and can be overridden via config).

- contextTokens in store is a runtime estimate/reporting value; don't treat it as a strict guarantee.

For more, see /token-use.

Tutorial.step

When auto-compaction happens (Pi runtime)

In embedded Pi agent, auto-compaction triggers in two cases:

1. Overflow recovery: model returns a context overflow error β†’ compact β†’ retry.

2. Threshold maintenance: after a successful turn, when:

contextTokens > contextWindow - reserveTokens

Where:

- contextWindow is model's context window

- reserveTokens is headroom reserved for prompts + next model output

These are Pi runtime semantics (OpenClaw consumes events, but Pi decides when to compact).

Tutorial.step

User-visible surfaces

You can observe compaction and session state via:

- /status (in any chat session)

- openclaw status (CLI)

- openclaw sessions / sessions --json

- Verbose mode: 🧹 Auto-compaction complete + compaction count

Tutorial.step

Pre-compaction "memory flush" (implemented)

Goal: before auto-compaction happens, run a silent agentic turn that writes durable state to disk (e.g. memory/YYYY-MM-DD.md in agent workspace) so compaction can't erase critical context.

OpenClaw uses pre-threshold flush approach:

1. Monitor session context usage.

2. When it crosses a "soft threshold" (below Pi's compaction threshold), run a silent "write memory now" directive to agent.

3. Use NO_REPLY so the user sees nothing.

Config (agents.defaults.compaction.memoryFlush):

- enabled (default: true)

- softThresholdTokens (default: 4000)

- prompt (user message for the flush turn)

- systemPrompt (extra system prompt appended for the flush turn)

Notes:

- The default prompt/system prompt include a NO_REPLY hint to suppress delivery.

- The flush runs once per compaction cycle (tracked in sessions.json).

- The flush runs only for embedded Pi sessions (CLI backends skip it).

- The flush is skipped when the session workspace is read-only (workspaceAccess: "ro" or "none").

- See Memory for the workspace file layout and write patterns.

Pi also exposes a session_before_compact hook in the extension API, but OpenClaw's flush logic lives on the Gateway side today.

Tutorial.step

Troubleshooting checklist

- Session key wrong? Start with /concepts/session and confirm the sessionKey in /status.

- Store vs transcript mismatch? Confirm the Gateway host and the store path from openclaw status.

- Compaction spam? Check:

- model context window (too small)

- compaction settings (reserveTokens too high for the model window can cause earlier compaction)

- tool-result bloat: enable/tune session pruning

- Silent turns leaking? Confirm the reply starts with NO_REPLY (exact token) and you're on a build that includes the streaming suppression fix.