Master OpenClaw Session Management & Compaction
Source of truth: the Gateway
Section titled “Source of truth: the Gateway”You should think of the Gateway process as the single point of authority for session state. It owns everything related to how your sessions are handled and stored.
- UIs like the macOS app, the web Control UI, or the TUI need to query the Gateway to get session lists and token counts.
- If you are running in remote mode, the session files live on the remote host. Checking your local files on your Mac won’t show you what the Gateway is actually using.
Two persistence layers
Section titled “Two persistence layers”OpenClaw saves your session data using two different layers to keep things organized:
-
Session store (
sessions.json)- This is a key/value map that links a
sessionKeyto aSessionEntry. - It is small, mutable, and safe for you to edit or delete entries.
- It tracks metadata like the current session ID, last activity, toggles, and token counters.
- This is a key/value map that links a
-
Transcript (
<sessionId>.jsonl)- This is an append-only transcript that uses a tree structure where entries have an
idand aparentId. - It stores the actual conversation, tool calls, and compaction summaries.
- OpenClaw uses this to rebuild the model context for future turns.
- This is an append-only transcript that uses a tree structure where entries have an
On-disk locations
Section titled “On-disk locations”On the Gateway host, you can find these files organized per agent:
- Store:
~/.openclaw/agents/<agentId>/sessions/sessions.json - Transcripts:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl- Telegram topic sessions:
.../<sessionId>-topic-<threadId>.jsonl
- Telegram topic sessions:
OpenClaw handles the resolution of these paths through src/config/sessions.ts.
Store maintenance and disk controls
Section titled “Store maintenance and disk controls”You can use automatic maintenance controls under session.maintenance to manage your sessions.json and transcript files. You have several options to configure:
mode: Set this towarn(default) orenforce.pruneAfter: This is the age cutoff for stale entries, which defaults to30d.maxEntries: This caps the number of entries insessions.json, defaulting to500.rotateBytes: This rotatessessions.jsonwhen it gets too large, with a default of10mb.resetArchiveRetention: This manages how long to keep*.reset.<timestamp>transcript archives. It defaults to the same aspruneAfter, but you can set it tofalseto disable cleanup.maxDiskBytes: This is an optional budget for your sessions directory.highWaterBytes: This is the target usage after a cleanup, defaulting to80%ofmaxDiskBytes.
When you set the mode to enforce, OpenClaw follows a specific order to clean up your disk budget:
- It removes the oldest archived or orphan transcript artifacts first.
- If usage is still above the target, it evicts the oldest session entries and their transcript files until usage is at or below
highWaterBytes.
If you use mode: "warn", OpenClaw will report what it would evict but it won’t actually change your files. You can also run maintenance manually whenever you want:
openclaw sessions cleanup --dry-runopenclaw sessions cleanup --enforceCron sessions and run logs
Section titled “Cron sessions and run logs”When you run isolated cron jobs, they create their own session entries and transcripts. You can manage how long these stay around using dedicated retention controls:
cron.sessionRetention(default24h) handles pruning old isolated cron sessions from your store. You can set this tofalseto disable it.cron.runLog.maxBytesandcron.runLog.keepLinestake care of the~/.openclaw/cron/runs/<jobId>.jsonlfiles. The defaults are2_000_000bytes and2000lines.
Session keys (sessionKey)
Section titled “Session keys (sessionKey)”You can think of a sessionKey as the identifier for your conversation bucket. It is what the system uses for routing and keeping different chats isolated from each other.
Here are the common patterns you’ll see:
- Main/direct chat (per agent):
agent:<agentId>:<mainKey>(defaultmain) - 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 you override it)
If you want to see the full canonical rules, they are documented at /concepts/session.
Session ids (sessionId)
Section titled “Session ids (sessionId)”Every sessionKey points to a specific sessionId, which is the actual transcript file where your conversation continues.
Here is how the logic works:
- Reset: Using
/newor/resetcreates a freshsessionIdfor that key. - Daily reset: By default, at 4:00 AM local time on your gateway host, the next message you send will trigger a new
sessionId. - Idle expiry: You can use
session.reset.idleMinutes(or the legacysession.idleMinutes) to start a new session if a message arrives after the idle window. If you have both daily and idle resets configured, the one that happens first wins. - Thread parent fork guard: If a parent session gets too big,
session.parentForkMaxTokens(default100000) stops the system from forking the transcript so the new thread starts fresh. You can set this to0to turn it off.
If you’re curious about the implementation details, this all happens in initSessionState() inside src/auto-reply/reply/session.ts.
Session store schema (sessions.json)
Section titled “Session store schema (sessions.json)”The session store uses the SessionEntry type found in src/config/sessions.ts. It holds all the important details about your active sessions.
Some of the key fields include:
sessionId: The current transcript ID.updatedAt: The timestamp of the last activity.sessionFile: An optional path if you want to override the transcript location.chatType: This can bedirect,group, orroom, which helps the UI and send policies.- Metadata: Fields like
provider,subject,room,space, anddisplayNamehelp with labeling. - Toggles: You can set levels for
thinkingLevel,verboseLevel,reasoningLevel, andelevatedLevel, or override thesendPolicy. - Model selection: Overrides for
providerOverride,modelOverride, andauthProfileOverride. - Token counters: It tracks
inputTokens,outputTokens,totalTokens, andcontextTokens. - Compaction:
compactionCounttracks how often auto-compaction has finished, whilememoryFlushAtandmemoryFlushCompactionCounttrack your last memory flush.
You can edit this store manually if you need to, but remember that the Gateway is the ultimate authority. It might rewrite or update entries while sessions are running.
Transcript structure (*.jsonl)
Section titled “Transcript structure (*.jsonl)”You will find that transcripts are managed by the SessionManager within @mariozechner/pi-coding-agent. The system uses the JSONL format to keep things organized.
The file structure follows a specific order:
- The first line is the session header. It uses
type: "session"and includes theid,cwd, andtimestamp. You might also see an optionalparentSessionhere. - The following lines are session entries. These use an
idand aparentIdto build a tree structure.
You should be aware of these specific entry types:
message: These are your standard user, assistant, or toolResult messages.custom_message: These are messages injected by extensions. They enter the model context, but you can hide them from the UI.custom: This tracks extension state that does not enter the model context.compaction: This is a persisted compaction summary that includesfirstKeptEntryIdandtokensBefore.branch_summary: This is a summary saved when you are navigating a specific branch in the tree.
OpenClaw does not try to “fix up” these transcripts. Instead, the Gateway uses the SessionManager to handle all reading and writing.
Context windows vs tracked tokens
Section titled “Context windows vs tracked tokens”You need to distinguish between two different concepts when you look at how tokens are handled:
- Model context window: This is the hard cap for each model. It limits the tokens that are actually visible to the model.
- Session store counters: These are rolling statistics written into
sessions.json. You will see these values used for the/statusendpoint and your dashboards.
If you are tuning your limits, keep these two points in mind:
- The context window value comes from the model catalog. If you need to change it, you can override it through your config.
- The
contextTokensvalue in the store is a runtime estimate used for reporting. You should not treat it as a strict guarantee.
For more details on how this works, you can look at /token-use.
Compaction: what it is
Section titled “Compaction: what it is”Compaction is how you handle long-running conversations without losing the thread. It summarizes older parts of a chat into a single compaction entry in the transcript. This keeps your most recent messages intact so the model stays focused.
After a compaction occurs, future turns see:
- The compaction summary
- Every message after
firstKeptEntryId
You should know that compaction is persistent. This is a key difference from session pruning, which is temporary. You can check the details on that here: /concepts/session-pruning.
When auto-compaction happens (Pi runtime)
Section titled “When auto-compaction happens (Pi runtime)”If you are using the embedded Pi agent, auto-compaction is handled for you. It triggers in two specific scenarios:
- Overflow recovery: When the model returns a context overflow error, the system runs a compaction and retries the request.
- Threshold maintenance: After a successful turn, the system checks if you are nearing the limit:
contextTokens > contextWindow - reserveTokens
In this logic:
contextWindowis the total context window of the modelreserveTokensis the headroom kept for prompts and the next model output
These rules follow Pi runtime semantics. While OpenClaw consumes the events, Pi is the component that decides when it is time to compact.
Compaction settings (reserveTokens, keepRecentTokens)
Section titled “Compaction settings (reserveTokens, keepRecentTokens)”You can find Pi’s compaction settings within the Pi configuration. Here is how the structure looks:
{ compaction: { enabled: true, reserveTokens: 16384, keepRecentTokens: 20000, },}OpenClaw also enforces a safety floor for embedded runs to keep things running smoothly:
- If
compaction.reserveTokens < reserveTokensFloor, OpenClaw bumps it up automatically. - The default floor is set to
20000tokens. - You can set
agents.defaults.compaction.reserveTokensFloor: 0if you need to disable this floor. - If your setting is already higher than the floor, OpenClaw leaves it alone.
The reason for this is to leave enough headroom for multi-turn “housekeeping” tasks, such as memory writes, before compaction becomes unavoidable.
If you want to see the logic behind this, check out ensurePiCompactionReserveTokens() in src/agents/pi-settings.ts. This is called from src/agents/pi-embedded-runner.ts.
User-visible surfaces
Section titled “User-visible surfaces”You can check the status of compaction and your current session state through these methods:
- Use
/statusinside any chat session. - Run the
openclaw statuscommand in your CLI. - Use
openclaw sessionsorsessions --jsonfor detailed data. - Enable verbose mode to see
🧹 Auto-compaction completealong with the compaction count.
Silent housekeeping (NO_REPLY)
Section titled “Silent housekeeping (NO_REPLY)”OpenClaw supports “silent” turns for background tasks where you don’t want intermediate output to clutter the conversation.
The convention is straightforward: the assistant starts its output with NO_REPLY to signal that it should not deliver a reply to the user. OpenClaw then strips or suppresses this message in the delivery layer.
As of 2026.1.10, OpenClaw also suppresses draft/typing streaming when a partial chunk begins with NO_REPLY. This ensures that silent operations don’t leak any partial output while the turn is in progress.
Pre-compaction “memory flush” (implemented)
Section titled “Pre-compaction “memory flush” (implemented)”The goal here is to run a silent agentic turn that writes durable state to your disk (like memory/YYYY-MM-DD.md in the agent workspace) before auto-compaction happens. This prevents compaction from erasing critical context that you might still need.
OpenClaw uses the pre-threshold flush approach:
- Monitor session context usage.
- When it crosses a “soft threshold” (below Pi’s compaction threshold), run a silent “write memory now” directive to the agent.
- Use
NO_REPLYso the user sees nothing. - Track the flush in
sessions.jsonto ensure it runs once per compaction cycle.
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 and system prompt include a
NO_REPLYhint to suppress delivery. - The flush runs once per compaction cycle (tracked in
sessions.json). - The flush runs only for embedded Pi sessions, while 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_compacthook in the extension API, but OpenClaw’s flush logic lives on the Gateway side today.
Troubleshooting checklist
Section titled “Troubleshooting checklist”- Session key wrong? Start with /concepts/session and confirm the
sessionKeyin/status. - Store vs transcript mismatch? Confirm the Gateway host and the store path from
openclaw status. - Compaction spam? Check if your model context window is too small or if
reserveTokensis too high for the model window, which can cause earlier compaction. You can also enable or tune session pruning to handle tool-result bloat. - Silent turns leaking? Confirm the reply starts with the exact
NO_REPLYtoken and you’re on a build that includes the streaming suppression fix.
OpenClaw Expert
Still stuck?
If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.