Session Management & Compaction (Tìm hiểu sâu)

Tài liệu này giải thích cách OpenClaw quản lý session từ đầu đến cuối:

  • Session routing (cách message đến được map vào sessionKey)
  • Session store (sessions.json) và những gì nó theo dõi
  • Transcript persistence (*.jsonl) và cấu trúc của nó
  • Transcript hygiene (fixup theo provider trước khi chạy)
  • Context limit (context window vs tracked token)
  • Compaction (manual + auto-compaction) và nơi hook pre-compaction work
  • Silent housekeeping (ví dụ: memory write không hiển thị output cho user)

Nếu các bạn muốn xem tổng quan cấp cao trước, hãy bắt đầu với:


Source of truth: Gateway

OpenClaw được thiết kế xoay quanh một Gateway process duy nhất sở hữu session state.

  • Các UI (macOS app, web Control UI, TUI) nên query Gateway để lấy danh sách session và số lượng token.
  • Ở remote mode, các file session nằm trên remote host; việc “check file trên Mac local” sẽ không phản ánh những gì Gateway đang dùng.

Hai lớp persistence

OpenClaw lưu trữ session ở hai lớp:

  1. Session store (sessions.json)

    • Map key/value: sessionKey -> SessionEntry
    • Nhỏ gọn, có thể thay đổi, an toàn khi edit (hoặc xóa entry)
    • Theo dõi metadata của session (session id hiện tại, hoạt động cuối, toggle, bộ đếm token, v.v.)
  2. Transcript (<sessionId>.jsonl)

    • Transcript append-only với cấu trúc cây (các entry có id + parentId)
    • Lưu trữ cuộc hội thoại thực tế + tool call + compaction summary
    • Dùng để rebuild model context cho các turn sau

Vị trí trên đĩa

Với mỗi agent, trên Gateway host:

  • Store: ~/.openclaw/agents/<agentId>/sessions/sessions.json
  • Transcript: ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
    • Telegram topic session: .../<sessionId>-topic-<threadId>.jsonl

OpenClaw resolve các đường dẫn này qua src/config/sessions.ts.


Session key (sessionKey)

sessionKey xác định conversation bucket nào các bạn đang ở (routing + isolation).

Các pattern phổ biến:

  • Main/direct chat (mỗi agent): agent:<agentId>:<mainKey> (mặc định main)
  • Group: agent:<agentId>:<channel>:group:<id>
  • Room/channel (Discord/Slack): agent:<agentId>:<channel>:channel:<id> hoặc ...:room:<id>
  • Cron: cron:<job.id>
  • Webhook: hook:<uuid> (trừ khi bị override)

Các quy tắc chính thức được ghi tại /concepts/session.


Session id (sessionId)

Mỗi sessionKey trỏ đến một sessionId hiện tại (file transcript tiếp tục cuộc hội thoại).

Quy tắc chung:

  • Reset (/new, /reset) tạo sessionId mới cho sessionKey đó.
  • Daily reset (mặc định 4:00 SA theo giờ local trên gateway host) tạo sessionId mới ở message tiếp theo sau reset boundary.
  • Idle expiry (session.reset.idleMinutes hoặc legacy session.idleMinutes) tạo sessionId mới khi message đến sau idle window. Khi cả daily + idle được config, cái nào hết hạn trước thì thắng.

Chi tiết implementation: quyết định xảy ra trong initSessionState()src/auto-reply/reply/session.ts.


Schema của Session store (sessions.json)

Value type của store là SessionEntry trong src/config/sessions.ts.

Các trường chính (không đầy đủ):

  • sessionId: transcript id hiện tại (filename được derive từ cái này trừ khi sessionFile được set)
  • updatedAt: timestamp hoạt động cuối
  • sessionFile: override đường dẫn transcript tùy chọn
  • chatType: direct | group | room (giúp UI và send policy)
  • provider, subject, room, space, displayName: metadata cho group/channel labeling
  • Toggle:
    • thinkingLevel, verboseLevel, reasoningLevel, elevatedLevel
    • sendPolicy (override per-session)
  • Model selection:
    • providerOverride, modelOverride, authProfileOverride
  • Bộ đếm token (best-effort / phụ thuộc provider):
    • inputTokens, outputTokens, totalTokens, contextTokens
  • compactionCount: số lần auto-compaction hoàn thành cho session key này
  • memoryFlushAt: timestamp cho lần memory flush pre-compaction cuối
  • memoryFlushCompactionCount: compaction count khi flush cuối chạy

Store an toàn để edit, nhưng Gateway là authority: nó có thể rewrite hoặc rehydrate entry khi session chạy.


Cấu trúc Transcript (*.jsonl)

Transcript được quản lý bởi SessionManager của @mariozechner/pi-coding-agent.

File là JSONL:

  • Dòng đầu: session header (type: "session", bao gồm id, cwd, timestamp, parentSession tùy chọn)
  • Sau đó: các session entry với id + parentId (cây)

Các loại entry đáng chú ý:

  • message: message user/assistant/toolResult
  • custom_message: message do extension inject vào model context (có thể ẩn khỏi UI)
  • custom: state của extension không vào model context
  • compaction: compaction summary được persist với firstKeptEntryIdtokensBefore
  • branch_summary: summary được persist khi navigate tree branch

OpenClaw cố ý không “fix up” transcript; Gateway dùng SessionManager để đọc/ghi chúng.


Context window vs tracked token

Hai khái niệm khác nhau quan trọng:

  1. Model context window: giới hạn cứng mỗi model (token mà model nhìn thấy)
  2. Session store counter: thống kê rolling được ghi vào sessions.json (dùng cho /status và dashboard)

Nếu các bạn đang tune limit:

  • Context window đến từ model catalog (và có thể override qua config).
  • contextTokens trong store là giá trị ước tính/báo cáo runtime; đừng coi nó như đảm bảo chặt chẽ.

Xem thêm tại /token-use.


Compaction: nó là gì

Compaction tóm tắt cuộc hội thoại cũ thành một entry compaction được persist trong transcript và giữ nguyên các message gần đây.

Sau compaction, các turn sau sẽ thấy:

  • Compaction summary
  • Message sau firstKeptEntryId

Compaction là persistent (không giống session pruning). Xem /concepts/session-pruning.


Khi nào auto-compaction xảy ra (Pi runtime)

Trong embedded Pi agent, auto-compaction trigger trong hai trường hợp:

  1. Overflow recovery: model trả về context overflow error → compact → retry.
  2. Threshold maintenance: sau một turn thành công, khi:

contextTokens > contextWindow - reserveTokens

Trong đó:

  • contextWindow là context window của model
  • reserveTokens là headroom dành cho prompt + model output tiếp theo

Đây là ngữ nghĩa Pi runtime (OpenClaw consume event, nhưng Pi quyết định khi nào compact).


Cài đặt Compaction (reserveTokens, keepRecentTokens)

Cài đặt compaction của Pi nằm trong Pi settings:

{
  compaction: {
    enabled: true,
    reserveTokens: 16384,
    keepRecentTokens: 20000,
  },
}

OpenClaw cũng enforce safety floor cho embedded run:

  • Nếu compaction.reserveTokens < reserveTokensFloor, OpenClaw tăng nó lên.
  • Floor mặc định là 20000 token.
  • Set agents.defaults.compaction.reserveTokensFloor: 0 để tắt floor.
  • Nếu nó đã cao hơn, OpenClaw để nguyên.

Tại sao: để lại đủ headroom cho “housekeeping” nhiều turn (như memory write) trước khi compaction trở nên không thể tránh khỏi.

Implementation: ensurePiCompactionReserveTokens() trong src/agents/pi-settings.ts (được gọi từ src/agents/pi-embedded-runner.ts).


Giao diện user có thể thấy

Các bạn có thể quan sát compaction và session state qua:

  • /status (trong bất kỳ chat session nào)
  • openclaw status (CLI)
  • openclaw sessions / sessions --json
  • Verbose mode: 🧹 Auto-compaction complete + compaction count

Silent housekeeping (NO_REPLY)

OpenClaw hỗ trợ turn “silent” cho background task mà user không nên thấy output trung gian.

Quy ước:

  • Assistant bắt đầu output với NO_REPLY để chỉ ra “đừng gửi reply cho user”.
  • OpenClaw strip/suppress cái này ở delivery layer.

Kể từ 2026.1.10, OpenClaw cũng suppress draft/typing streaming khi partial chunk bắt đầu bằng NO_REPLY, nên silent operation không leak partial output giữa turn.


Pre-compaction “memory flush” (đã implement)

Mục tiêu: trước khi auto-compaction xảy ra, chạy một silent agentic turn ghi durable state vào đĩa (ví dụ: memory/YYYY-MM-DD.md trong agent workspace) để compaction không thể xóa context quan trọng.

OpenClaw dùng approach pre-threshold flush:

  1. Monitor session context usage.
  2. Khi nó vượt qua “soft threshold” (dưới compaction threshold của Pi), chạy silent directive “write memory now” cho agent.
  3. Dùng NO_REPLY để user không thấy gì.

Config (agents.defaults.compaction.memoryFlush):

  • enabled (mặc định: true)
  • softThresholdTokens (mặc định: 4000)
  • prompt (user message cho flush turn)
  • systemPrompt (system prompt thêm vào cho flush turn)

Lưu ý:

  • Prompt/system prompt mặc định bao gồm hint NO_REPLY để suppress delivery.
  • Flush chạy một lần mỗi compaction cycle (được track trong sessions.json).
  • Flush chỉ chạy cho embedded Pi session (CLI backend bỏ qua).
  • Flush bị skip khi session workspace là read-only (workspaceAccess: "ro" hoặc "none").
  • Xem Memory cho workspace file layout và write pattern.

Pi cũng expose hook session_before_compact trong extension API, nhưng logic flush của OpenClaw nằm ở Gateway side hiện tại.


Checklist troubleshooting

  • Session key sai? Bắt đầu với /concepts/session và xác nhận sessionKey trong /status.
  • Store vs transcript không khớp? Xác nhận Gateway host và đường dẫn store từ openclaw status.
  • Compaction spam? Kiểm tra:
    • model context window (quá nhỏ)
    • cài đặt compaction (reserveTokens quá cao cho model window có thể gây compaction sớm hơn)
    • tool-result bloat: enable/tune session pruning
  • Silent turn bị leak? Xác nhận reply bắt đầu bằng NO_REPLY (token chính xác) và các bạn đang dùng build có streaming suppression fix.