If you’ve followed this series from the beginning, you’ve met all the major characters: the Gateway that routes everything, the Agentic Loop that runs the agent, the Memory that persists knowledge across time, the Agent identity that defines who the agent is, the Tools that give the agent hands and eyes, and the Skills that teach it how to work well.
But there’s one concept that quietly holds all of them together, turn by turn, channel by channel, across every conversation you’ve ever had with your agent.
That concept is the Session.
Sessions are so fundamental that most people never think about them — the way you don’t think about RAM until your computer starts swapping. But when things go wrong — the agent forgets context mid-task, two users’ conversations bleed together, a sub-agent runs but its results disappear — it’s almost always a session problem. Understanding sessions deeply is the difference between an agent that feels coherent and one that feels unreliable.
Let’s fix that.
Quick recap: where we are in the series
| # | Topic | What it answers |
|---|---|---|
| #1 | Gateway | What’s the central nervous system? |
| #2 | Agentic Loop | How does the agent actually run? |
| #3 | Memory | How does the agent remember things? |
| #4 | Agent | What is the agent, at its core? |
| #5 | Tools | What can the agent physically do? |
| #6 | Skills | How does the agent know how to do it well? |
| #7 | Sessions | How does the agent track who it’s talking to, where, and what has been said? |
1. What a session actually is (the one-sentence version)
A session is the unit of execution for everything your agent does.
It’s the container that holds:
- A session key — a structured identifier that encodes who is talking and from where
- A message history — every turn of the conversation, including tool calls and their results, stored as a JSONL transcript on disk
- A context snapshot — the assembled system prompt, loaded skills, and active workspace files for this particular run
- Token accounting — running totals of input, output, and context tokens, used for cost tracking and compaction decisions
Every time a message arrives — whether from you via Telegram, from a cron schedule, from a webhook, or from another agent — the Gateway looks up or creates a session, assembles the context from that session’s history, runs the Agentic Loop, and persists the result back to the session’s transcript. The session is the thread that stitches all those individual turns into a continuous, coherent conversation.
Here’s the critical mental model to hold onto: sessions are for reasoning, not storage. The session holds what the model needs to reason right now. Long-term knowledge lives in Memory. Configuration lives in the workspace files covered in Agent #4. The session is the working surface — the whiteboard that gets partially erased and redrawn with each new conversation window.
2. Where sessions live on disk
Sessions aren’t ephemeral — they’re persisted to disk in two complementary structures:
~/.openclaw/agents/<agentId>/sessions/
sessions.json ← the session store: a map of sessionKey → metadata
<sessionId>.jsonl ← the transcript for each session
<sessionId>-topic-<threadId>.jsonl ← Telegram forum topic variant
sessions.json is the index. It maps session keys to session metadata: the session ID, the last update timestamp, token counts, routing hints, and display labels (so the dashboard can show you a human-readable name for each session). Deleting an entry from this file is safe — OpenClaw recreates it on the next message. The store also includes origin metadata so UIs can explain where a session came from.
The .jsonl transcript files are the actual conversation history — one JSON object per line, one line per turn. Each entry records the role (user, assistant, tool), the content, the tool calls and their results, timestamps, and token usage. These files are the canonical record of what happened. OpenClaw never “fixes them up” after the fact — if you need to understand exactly what your agent did and why, the JSONL transcript is the ground truth.
One important operational detail: all session state is owned by the Gateway. UI clients — the macOS app, WebChat, your phone — don’t read the JSONL files directly. They query the Gateway for session lists, token counts, and history. In remote mode, the session store lives on the remote Gateway host, not your local machine. Token counts shown in UIs come from the Gateway’s store fields, not from client-side parsing.
3. Session keys: the naming scheme that holds everything together
Session keys are the most underrated piece of OpenClaw’s architecture. Once you understand them, the entire routing and isolation model clicks into place.
A session key is a structured string that encodes exactly which agent, which channel, and which sender (or group) a session belongs to:
| Session type | Key format | Example |
|---|---|---|
| Direct chat (default) | agent:<agentId>:<mainKey> | agent:main:main |
| Direct chat (per-peer) | agent:<agentId>:dm:<peerId> | agent:main:dm:telegram:123456 |
| Group chat | agent:<agentId>:<channel>:group:<id> | agent:main:telegram:group:987654 |
| Channel/room | agent:<agentId>:<channel>:channel:<id> | agent:main:discord:channel:111222 |
| Telegram forum topic | agent:<agentId>:<channel>:group:<id>:topic:<threadId> | agent:main:telegram:group:987654:topic:42 |
The key insight here: the session key is the routing address. When a message arrives, the Gateway parses it to determine the correct session key, looks it up (or creates it), and dispatches the run. If you understand what key a message maps to, you understand exactly which conversation history the agent will see.
This is also the basis for DM isolation — one of the most common configuration decisions you’ll make.
4. DM isolation: the setting most people get wrong
By default, all direct messages to your agent share a single session — keyed to agent:<agentId>:main. This is fine if you’re the only person who ever talks to your agent. It’s a serious problem the moment a second person sends a DM.
Without DM isolation, all users share the same conversation context — which means User A can see context from User B’s conversation, and vice versa. This is not a bug; it’s the default behavior designed for single-user setups. But it’s a security and privacy problem in any multi-user configuration.
The fix is session.dmScope:
// ~/.openclaw/openclaw.json
{
"session": {
"dmScope": "per-channel-peer"
}
}
OpenClaw gives you four options:
dmScope value | What it means | When to use it |
|---|---|---|
main (default) | All DMs share one session | Single-user personal assistant |
per-peer | Isolated per sender ID, across channels | Multi-user, single-channel inbox |
per-channel-peer | Isolated per channel + sender | Multi-user, multi-channel inbox (recommended) |
per-account-channel-peer | Isolated per account + channel + sender | Multi-account setups on the same channel |
Run openclaw security audit to check your current DM settings and surface risky configurations. When you onboard with the CLI wizard, per-channel-peer is written as the default. But if you set up OpenClaw manually or imported an older config, verify this explicitly — don’t assume.
Identity links: merging the same person across channels
What if the same user talks to your agent on both Telegram and Discord? With per-channel-peer, they’d get two separate sessions with no shared history. session.identityLinks solves this:
JSON{
"session": {
"dmScope": "per-channel-peer",
"identityLinks": {
"alice": ["telegram:123456789", "discord:987654321012345678"]
}
}
}
When OpenClaw sees a message from telegram:123456789, it maps it to the canonical identity alice and uses the same session key as when it sees discord:987654321012345678. Alice gets a continuous, unified conversation regardless of which app she messages from. Multiple phone numbers and channels can map to the same agent main key — they act as different transport paths into one conversation.
5. Session types: not all sessions are created equal
OpenClaw has several distinct session types, each with its own lifecycle and purpose:
5.1 Main sessions
The primary interactive session — where you have day-to-day conversations with your agent. There’s typically one active main session per agent. This is what you see when you open the WebChat at localhost:3100 or message your agent on Telegram. Direct chats collapse to agent:<agentId>:<mainKey> (default: main).
5.2 Sub-agent sessions (spawned sessions)
Child sessions spawned by the main session — or by another sub-agent — to handle specific tasks in parallel or isolation. They run concurrently, report results back to their parent, and then terminate. Sub-agent sessions are auto-archived after agents.defaults.subagents.archiveAfterMinutes (default: 60 minutes). After completion, OpenClaw runs a sub-agent announce step that posts the result back to the requester’s chat channel. Reply ANNOUNCE_SKIP during the announce step to stay silent.
This is how your agent handles tasks like “research three competitors simultaneously” — distributing the work across concurrent sub-agents rather than running them sequentially.
5.3 Cron sessions
Sessions started by a scheduled heartbeat or cron job rather than a human message. They handle external triggers automatically — running on a timer, evaluating the task list in HEARTBEAT.md, and taking action if needed. One sub-agent session with an isolated model can be configured per cron job, which is a useful cost-control lever: run expensive tasks on a capable model, routine cron jobs on a cheaper one.
5.4 Webhook / hook sessions
Sessions triggered by incoming webhook events rather than a user or schedule. These handle external triggers — a GitHub push, a form submission, a Zapier hook — automatically.
The practical takeaway: one agent can have many simultaneous sessions. A main session for your direct conversation, three sub-agent sessions working on parallel research tasks, and a cron session running its heartbeat check — all at the same time, all with separate context windows, all writing their own transcripts.
6. Session lifecycle: how sessions start, persist, and end
Starting a session
Sessions are created on demand when a message arrives and no matching session key exists. There’s no explicit “create session” step. The Gateway resolves the key, checks the store, creates an entry if needed, and proceeds. New session entries are recreated automatically if you delete them from the store.
Session reset
Sessions are reused across turns — by default until they expire. There are three reset modes:
| Reset mode | How it triggers | Configuration |
|---|---|---|
| Daily reset (default) | New session at 4:00 AM local time on the Gateway host | session.reset.mode: "daily", session.reset.atHour: 4 |
| Idle reset | New session after a period of inactivity | session.reset.idleMinutes: N |
| Manual reset | Type /new or /reset in chat | Built-in; customizable via resetTriggers |
When both daily and idle resets are configured, whichever expires first wins. /new <model> also lets you switch to a different model for the new session without any config change. If /new or /reset is sent alone (without a message), OpenClaw runs a short greeting turn to confirm the reset. You can also customize which keywords trigger a reset via session.resetTriggers.
Per-type and per-channel overrides give you fine-grained control: resetByType lets you set different reset policies for dm, group, and thread sessions. resetByChannel overrides the reset policy for a specific channel entirely. This lets you, for example, keep your direct Telegram conversation running for the full day while resetting your Discord group session after 4 hours of idle time.
Ending a session: pruning and compaction
Sessions don’t have a formal “end” — they expire, reset, or get compacted. Two mechanisms keep session storage bounded over time:
Session pruning trims old tool results from the in-memory context right before LLM calls. This is not the same as rewriting the JSONL history — the transcript remains intact. Pruning only affects what gets assembled into the context window for the next inference call. It’s the mechanism that keeps your model from hitting its context limit on long-running conversations.
Compaction is more aggressive and permanent. When a session grows large enough to approach the model’s context window limit, OpenClaw can summarize older conversation turns into a compact compaction entry in the transcript, keeping only recent messages intact. Compaction is persistent — it does rewrite the effective conversation history, replacing many turns with a single summary. The JSONL history is preserved, but future context assembly works from the compacted version.
A critical pre-compaction behavior: when a session nears auto-compaction, OpenClaw can run a silent memory flush turn — a hidden agentic loop iteration that instructs the model to write durable notes to disk before the compaction erases them. This only runs when the workspace is writable. The flush runs once per compaction cycle, tracked in sessions.json, and uses the NO_REPLY / no_reply token so the user sees nothing. This is the mechanism that bridges sessions and Memory — ensuring that knowledge worth keeping gets written to a .md file before the session window closes.
7. Session tools: how agents see and interact with other sessions
Sessions aren’t just containers for one conversation — they’re the backbone of OpenClaw’s multi-agent architecture. The group:messaging tool set includes four session tools that let agents see and interact with other sessions:
| Tool | What it does |
|---|---|
sessions_list | List all visible sessions (filtered by visibility scope) |
sessions_history | Fetch the transcript of a specific session |
sessions_send | Send a message into another session and optionally wait for the reply |
sessions_spawn | Spawn a sub-agent in an isolated session and announce the result back |
The reply-back loop
sessions_send has a built-in reply-back loop: after the primary run in the target session completes, OpenClaw alternates between the requester and target agents for up to 5 rounds. This lets two agents “ping-pong” on a task without you orchestrating every step. Stop the loop early by replying with REPLY_SKIP from either side.
Set timeoutSeconds: 0 for fire-and-forget. Set it higher and the tool waits for a reply — using a server-side wait so reconnects don’t drop the wait state. If the wait times out, you get a { runId, status: "timeout" } response and can call sessions_history later to retrieve the result.
Visibility scoping
By default, sessions tools use tree visibility — an agent can see itself and any sessions it spawned. You can narrow this to self (only itself), widen it to agent (all sessions for the same agent), or open it to all (every session the Gateway knows about). Sandboxed agents default to spawned — they only see sessions they themselves spawned.
{
"tools": {
"sessions": {
"visibility": "tree"
}
},
"agents": {
"defaults": {
"sandbox": {
"sessionToolsVisibility": "spawned"
}
}
}
}
This is how you build multi-agent systems where agents collaborate without having unrestricted access to each other’s conversation history.
8. Send policy: controlling which sessions can message which channels
Not every session should be able to send messages to every channel. session.sendPolicy gives you rule-based control:
{
"session": {
"sendPolicy": {
"rules": [
{
"action": "deny",
"match": { "channel": "discord", "chatType": "group" }
},
{
"action": "deny",
"match": { "keyPrefix": "cron:" }
}
],
"default": "allow"
}
}
}
Rules are evaluated top-down; the first match wins. The default action applies when no rule matches. You can override the policy at runtime with /send on, /send off, or /send inherit in chat — sent as standalone messages so they register as policy changes rather than conversation turns.
Common use cases:
- Block cron sessions from sending to external channels (run silently, write to memory instead)
- Prevent group-chat agents from replying to Discord groups they shouldn’t touch
- Allow sub-agents to send to
internalchannels only, not external messaging platforms
9. Inspecting and maintaining sessions
Inspection commands
# Check session store path and recent activity
openclaw status
# List all sessions, filterable by recent activity
openclaw sessions --json
openclaw sessions --json --active 30 # sessions active in last 30 minutes
# In chat
/status # context usage, model, token counts, and active toggles
Maintenance: keeping the store bounded
OpenClaw applies session-store maintenance to keep sessions.json and transcript artifacts from growing without bound. By default it runs in warn mode — it reports what would be cleaned without doing anything. Set session.maintenance.mode to "enforce" for automatic cleanup:
{
"session": {
"maintenance": {
"mode": "enforce",
"pruneAfter": "30d",
"maxEntries": 500
}
}
}
Preview what would be cleaned without committing:
openclaw sessions cleanup --dry-run
For long-running deployments, setting maintenance to enforce with a reasonable pruneAfter value is highly recommended. Without it, sessions.json and the transcript directory grow indefinitely — not dangerous, but increasingly slow and difficult to navigate.
Migrating sessions to a new machine
If you move your OpenClaw setup to a new machine, sessions migrate with it. Copy $OPENCLAW_STATE_DIR (default: ~/.openclaw) and your workspace, then run openclaw doctor. That preserves config, auth profiles, channel credentials, sessions, and memory. One important caveat: if you only back up your workspace to git (which most people do), you’re preserving memory and bootstrap files — but not session history or auth. Those live under ~/.openclaw/, not in the workspace.
10. Sessions and memory: the handoff that keeps knowledge alive
Sessions and Memory are complementary systems with a clear division of labor:
| Session | Memory | |
|---|---|---|
| Scope | One conversation window | Persistent across all sessions |
| What it holds | Current conversation history, tool results, context | Durable facts, summaries, user preferences |
| Lifespan | Until reset or compacted | Until explicitly deleted |
| How it’s written | Automatically, turn by turn | Agent writes to .md files via fs:write |
| When it matters | “What did we just discuss?” | “What do I know about this user?” |
| Analogy | Working memory | Long-term memory |
The critical connection between them: a well-configured agent writes to Memory before its session resets or compacts. The pre-compaction memory flush (described in section 6) is one automated mechanism for this. But the best practice is to also build this into your agent’s identity — via SOUL.md or HEARTBEAT.md instructions — so the agent proactively writes important context to disk after significant tasks, not just when forced to by compaction pressure.
Skills (covered in #6) can encode this pattern explicitly: “after completing this workflow, write a summary to memory/YYYY-MM-DD-<task>.md.” That turns a transient session result into a durable memory — available in every future session.
11. End-to-end example: sessions at work across a multi-agent task
Let’s make this concrete. You ask your main agent: "Research three AI coding tools and write a comparison report."
Here’s what happens at the session layer:
1. Main session receives the message → Session key: agent:main:main → Full conversation history loaded from sessions.json + JSONL transcript → Active skills roster injected into context
2. Main agent decides to parallelize → Calls sessions_spawn three times, one per tool → Three new sessions created: agent:main:subagent:uuid1, uuid2, uuid3 → Main agent fires and returns; user sees “spawning sub-agents…”
3. Three sub-agents run concurrently → Each has its own isolated context window and transcript → Each uses web_search + web_fetch + fs:write to research and draft notes → Each announces results back to the main session on completion
4. Main agent reassembles results → Reads the three draft files via fs:read → Synthesizes into a unified comparison report → Writes final report to reports/ai-coding-tools-2026.md → Sends summary message to you
5. Session maintenance → Sub-agent sessions archived after 60 minutes → Main session token count updated in sessions.json → If context budget is approaching limit, pre-compaction memory flush writes key findings to memory/
Three sessions, three concurrent Agentic Loops, one coherent result. That’s sessions doing what they’re designed to do.
The three-sentence summary
A session is the structured container — identified by a session key, backed by a JSONL transcript, and owned by the Gateway — that holds a conversation’s full history and powers every turn of the Agentic Loop. Sessions are reused across turns, isolated per sender and channel through configurable dmScope and identityLinks, and kept bounded by pruning, compaction, and maintenance policies. Configure them correctly and your agent feels coherent and stateful; misconfigure them and context leaks between users, sub-agents lose results, and conversations reset at the wrong moment — which is why sessions, despite being invisible when they work, are the foundation every other concept in this series depends on.
What’s next
We’ve now completed the full core stack: Gateway → Agentic Loop → Memory → Agent → Tools → Skills → Sessions. In #8, we go deeper into Plugins — the layer that extends OpenClaw with compiled code rather than Markdown: new channels, new model providers, new tools, new memory backends. If Skills are textbooks and Sessions are the classroom, Plugins are new wings of the building — and the security bar is significantly higher than anything we’ve discussed so far.
References
- OpenClaw Official Docs — Session Management https://docs.openclaw.ai/concepts/session
- OpenClaw Official Docs — Session Pruning https://docs.openclaw.ai/concepts/session-pruning
- OpenClaw Official Docs — Session Tools https://docs.openclaw.ai/concepts/session-tool
- OpenClaw Official Docs — Compaction https://docs.openclaw.ai/concepts/compaction
- openclaw.cc — Session Management | OpenClaw Docs https://openclaw.cc/en/concepts/session
- openclaw-ai.com — Session Management | OpenClaw Docs https://openclaw-ai.com/en/docs/concepts/session
- openclawlab.com — Session Management | OpenClaw Docs https://openclawlab.com/en/docs/concepts/session/
- openclawlab.com — Session Tools | OpenClaw Docs https://openclawlab.com/en/docs/concepts/session-tool/