Files
adolf/docs/concepts/session.md
alvis bedb527145
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Vendor OpenClaw source as Adolf fork baseline
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
2026-07-05 09:36:54 +00:00

7.6 KiB

summary, read_when, title
summary read_when title
How OpenClaw manages conversation sessions
You want to understand session routing and isolation
You want to configure DM scope for multi-user setups
You are debugging daily or idle session resets
Session management

OpenClaw routes every inbound message to a session based on where it came from: DMs, group chats, cron jobs, etc. All session state is owned by the gateway; UI clients query the gateway for session data.

How messages are routed

Source Behavior
Direct messages Shared session by default
Group chats Isolated per group
Rooms/channels Isolated per room
Cron jobs Fresh session per run
Webhooks Isolated per hook

DM isolation

By default, all DMs share one session for continuity, which is fine for single-user setups.

If multiple people can message your agent, enable DM isolation. Without it, all users share the same conversation context, so Alice's private messages would be visible to Bob.
{
  session: {
    dmScope: "per-channel-peer", // isolate by channel + sender
  },
}

session.dmScope options:

Value Behavior
main (default) All DMs share one session
per-peer Isolate by sender, across channels
per-channel-peer Isolate by channel + sender (recommended)
per-account-channel-peer Isolate by account + channel + sender
If the same person contacts you from multiple channels, use `session.identityLinks` to map their identities to one canonical peer id so they share a session.

Dock linked channels

Dock commands move the current direct-chat session's reply route to another linked channel without starting a new session. See Channel docking for examples, config, and troubleshooting.

Verify your setup with openclaw security audit.

Session lifecycle

Sessions are reused until they expire under session.reset:

  • Daily reset (default mode: "daily") - new session at a configured local hour (session.reset.atHour, default 4, 0-23) on the gateway host. Daily freshness is based on when the current sessionId started, not on later metadata writes.
  • Idle reset (mode: "idle") - new session after session.reset.idleMinutes of inactivity. Idle freshness is based on the last real user/channel interaction, so heartbeat, cron, and exec system events do not keep the session alive.
  • Manual reset - type /new or /reset in chat. /new <model> also switches the model.

When both daily and idle resets are configured, whichever expires first wins. Heartbeat, cron, exec, and other system-event turns may write session metadata, but those writes do not extend daily or idle reset freshness. When a reset rolls the session, queued system-event notices for the old session are discarded so stale background updates are not prepended to the first prompt in the new session.

Sessions with an active provider-owned CLI session are not cut by the implicit daily default. Use /reset or configure session.reset explicitly when those sessions should expire on a timer.

Override the default per chat type or per channel:

{
  session: {
    reset: { mode: "daily", atHour: 4 },
    resetByType: {
      group: { mode: "idle", idleMinutes: 120 },
      thread: { mode: "daily", atHour: 6 },
    },
    resetByChannel: {
      discord: { mode: "idle", idleMinutes: 10080 },
    },
  },
}

resetByType supports direct (legacy alias dm), group, and thread. Legacy top-level session.idleMinutes still works as a compatibility alias for an idle-mode default when no session.reset/resetByType block is set.

Where state lives

  • Store: ~/.openclaw/agents/<agentId>/sessions/sessions.json
  • Transcripts: ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl

sessions.json keeps separate lifecycle timestamps:

  • sessionStartedAt: when the current sessionId began; daily reset uses this.
  • lastInteractionAt: last user/channel interaction that extends idle lifetime.
  • updatedAt: last store-row mutation; useful for listing and pruning, but not authoritative for daily/idle reset freshness.

Older rows without sessionStartedAt are resolved from the transcript JSONL session header when available. If an older row also lacks lastInteractionAt, idle freshness falls back to that session start time, not to later bookkeeping writes.

Session maintenance

OpenClaw bounds session storage over time via session.maintenance, defaults shown:

{
  session: {
    maintenance: {
      mode: "enforce", // "enforce" applies cleanup; "warn" only reports
      pruneAfter: "30d",
      maxEntries: 500,
    },
  },
}

For production-sized maxEntries limits, Gateway runtime writes use a small high-water buffer and clean back down to the configured cap in batches. Session store reads do not prune or cap entries during Gateway startup, so startup and isolated cron sessions do not pay for a full store cleanup. openclaw sessions cleanup --enforce applies the cap immediately.

Gateway model-run probe sessions are short-lived by default. Rows matching agent:*:explicit:model-run-<uuid> use fixed 24h retention, but cleanup is pressure-gated: it only removes stale probe rows when session-entry maintenance/cap pressure is reached, and runs before the broader stale-entry age cutoff and entry cap. Normal direct, group, thread, cron, hook, heartbeat, ACP, and sub-agent sessions do not inherit this 24h retention.

Maintenance preserves durable external conversation pointers, including group sessions and thread-scoped chat sessions, while still allowing synthetic cron, hook, heartbeat, ACP, and sub-agent entries to age out.

If you previously used DM isolation and later returned session.dmScope to main, preview stale peer-keyed DM rows with openclaw sessions cleanup --dry-run --fix-dm-scope. Applying the same flag retires those old direct-DM rows and keeps their transcripts as deleted archives.

Preview any maintenance run with openclaw sessions cleanup --dry-run.

Inspecting sessions

Command Shows
openclaw status Session store path and recent activity
openclaw sessions --json All sessions (filter with --active <minutes>)
/status in chat Context usage, model, and toggles
/context list What is in the system prompt

Further reading