This document explains how CoderClaw manages sessions end-to-end:
sessionKey)sessions.json) and what it tracks*.jsonl) and its structureIf you want a higher-level overview first, start with:
CoderClaw is designed around a single Gateway process that owns session state.
CoderClaw persists sessions in two layers:
sessions.json)
sessionKey -> SessionEntry<sessionId>.jsonl)
id + parentId)Per agent, on the Gateway host:
~/.coderclaw/agents/<agentId>/sessions/sessions.json~/.coderclaw/agents/<agentId>/sessions/<sessionId>.jsonl
.../<sessionId>-topic-<threadId>.jsonlCoderClaw resolves these via src/config/sessions.ts.
sessionKey)A sessionKey identifies which conversation bucket you’re in (routing + isolation).
Common patterns:
agent:<agentId>:<mainKey> (default main)agent:<agentId>:<channel>:group:<id>agent:<agentId>:<channel>:channel:<id> or ...:room:<id>cron:<job.id>hook:<uuid> (unless overridden)The canonical rules are documented at /concepts/session.
sessionId)Each sessionKey points at a current sessionId (the transcript file that continues the conversation).
Rules of thumb:
/new, /reset) creates a new sessionId for that sessionKey.sessionId on the next message after the reset boundary.session.reset.idleMinutes or legacy session.idleMinutes) creates a new sessionId when a message arrives after the idle window. When daily + idle are both configured, whichever expires first wins.Implementation detail: the decision happens in initSessionState() in src/auto-reply/reply/session.ts.
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 timestampsessionFile: optional explicit transcript path overridechatType: direct | group | room (helps UIs and send policy)provider, subject, room, space, displayName: metadata for group/channel labelingthinkingLevel, verboseLevel, reasoningLevel, elevatedLevelsendPolicy (per-session override)providerOverride, modelOverride, authProfileOverrideinputTokens, outputTokens, totalTokens, contextTokenscompactionCount: how often auto-compaction completed for this session keymemoryFlushAt: timestamp for the last pre-compaction memory flushmemoryFlushCompactionCount: compaction count when the last flush ranThe store is safe to edit, but the Gateway is the authority: it may rewrite or rehydrate entries as sessions run.
*.jsonl)Transcripts are managed by @mariozechner/pi-coding-agent’s SessionManager.
The file is JSONL:
type: "session", includes id, cwd, timestamp, optional parentSession)id + parentId (tree)Notable entry types:
message: user/assistant/toolResult messagescustom_message: extension-injected messages that do enter model context (can be hidden from UI)custom: extension state that does not enter model contextcompaction: persisted compaction summary with firstKeptEntryId and tokensBeforebranch_summary: persisted summary when navigating a tree branchCoderClaw intentionally does not “fix up” transcripts; the Gateway uses SessionManager to read/write them.
Two different concepts matter:
sessions.json (used for /status and dashboards)If you’re tuning limits:
contextTokens in the store is a runtime estimate/reporting value; don’t treat it as a strict guarantee.For more, see /token-use.
Compaction summarizes older conversation into a persisted compaction entry in the transcript and keeps recent messages intact.
After compaction, future turns see:
firstKeptEntryIdCompaction is persistent (unlike session pruning). See /concepts/session-pruning.
In the embedded Pi agent, auto-compaction triggers in two cases:
contextTokens > contextWindow - reserveTokens
Where:
contextWindow is the model’s context windowreserveTokens is headroom reserved for prompts + the next model outputThese are Pi runtime semantics (CoderClaw consumes the events, but Pi decides when to compact).
reserveTokens, keepRecentTokens)Pi’s compaction settings live in Pi settings:
{
compaction: {
enabled: true,
reserveTokens: 16384,
keepRecentTokens: 20000,
},
}
CoderClaw also enforces a safety floor for embedded runs:
compaction.reserveTokens < reserveTokensFloor, CoderClaw bumps it.20000 tokens.agents.defaults.compaction.reserveTokensFloor: 0 to disable the floor.Why: leave enough headroom for multi-turn “housekeeping” (like memory writes) before compaction becomes unavoidable.
Implementation: ensurePiCompactionReserveTokens() in src/agents/pi-settings.ts
(called from src/agents/pi-embedded-runner.ts).
You can observe compaction and session state via:
/status (in any chat session)coderclaw status (CLI)coderclaw sessions / sessions --json🧹 Auto-compaction complete + compaction countNO_REPLY)CoderClaw supports “silent” turns for background tasks where the user should not see intermediate output.
Convention:
NO_REPLY to indicate “do not deliver a reply to the user”.As of 2026.1.10, CoderClaw also suppresses draft/typing streaming when a partial chunk begins with NO_REPLY, so silent operations don’t leak partial output mid-turn.
Goal: before auto-compaction happens, run a silent agentic turn that writes durable
state to disk (e.g. memory/YYYY-MM-DD.md in the agent workspace) so compaction can’t
erase critical context.
CoderClaw uses the pre-threshold flush approach:
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:
NO_REPLY hint to suppress delivery.sessions.json).workspaceAccess: "ro" or "none").Pi also exposes a session_before_compact hook in the extension API, but CoderClaw’s
flush logic lives on the Gateway side today.
sessionKey in /status.coderclaw status.reserveTokens too high for the model window can cause earlier compaction)NO_REPLY (exact token) and you’re on a build that includes the streaming suppression fix.