CoderClaw treats one direct-chat session per agent as primary. Direct chats collapse to agent:<agentId>:<mainKey> (default main), while group/channel chats get their own keys. session.mainKey is honored.
Use session.dmScope to control how direct messages are grouped:
main (default): all DMs share the main session for continuity.per-peer: isolate by sender id across channels.per-channel-peer: isolate by channel + sender (recommended for multi-user inboxes).per-account-channel-peer: isolate by account + channel + sender (recommended for multi-account inboxes).
Use session.identityLinks to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using per-peer, per-channel-peer, or per-account-channel-peer.Security Warning: If your agent can receive DMs from multiple people, you should strongly consider enabling secure DM mode. Without it, all users share the same conversation context, which can leak private information between users.
Example of the problem with default settings:
<SENDER_A>) messages your agent about a private topic (for example, a medical appointment)<SENDER_B>) messages your agent asking “What were we talking about?”The fix: Set dmScope to isolate sessions per user:
// ~/.coderclaw/coderclaw.json
{
session: {
// Secure DM mode: isolate DM context per channel + sender.
dmScope: "per-channel-peer",
},
}
When to enable this:
dmPolicy: "open"Notes:
dmScope: "main" for continuity (all DMs share the main session). This is fine for single-user setups.per-account-channel-peer.session.identityLinks to collapse their DM sessions into one canonical identity.coderclaw security audit (see security).All session state is owned by the gateway (the “master” CoderClaw). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
inputTokens, outputTokens, totalTokens, contextTokens). Clients do not parse JSONL transcripts to “fix up” totals.~/.coderclaw/agents/<agentId>/sessions/sessions.json (per agent).~/.coderclaw/agents/<agentId>/sessions/<SessionId>.jsonl (Telegram topic sessions use .../<SessionId>-topic-<threadId>.jsonl).sessionKey -> { sessionId, updatedAt, ... }. Deleting entries is safe; they are recreated on demand.displayName, channel, subject, room, and space to label sessions in UIs.origin metadata (label + routing hints) so UIs can explain where a session came from.CoderClaw trims old tool results from the in-memory context right before LLM calls by default. This does not rewrite JSONL history. See /concepts/session-pruning.
When a session nears auto-compaction, CoderClaw can run a silent memory flush turn that reminds the model to write durable notes to disk. This only runs when the workspace is writable. See Memory and Compaction.
session.dmScope (default main).
main: agent:<agentId>:<mainKey> (continuity across devices/channels).
per-peer: agent:<agentId>:dm:<peerId>.per-channel-peer: agent:<agentId>:<channel>:dm:<peerId>.per-account-channel-peer: agent:<agentId>:<channel>:<accountId>:dm:<peerId> (accountId defaults to default).session.identityLinks matches a provider-prefixed peer id (for example telegram:123), the canonical key replaces <peerId> so the same person shares a session across channels.agent:<agentId>:<channel>:group:<id> (rooms/channels use agent:<agentId>:<channel>:channel:<id>).
:topic:<threadId> to the group id for isolation.group:<id> keys are still recognized for migration.group:<id>; the channel is inferred from Provider and normalized to the canonical agent:<agentId>:<channel>:group:<id> form.cron:<job.id>hook:<uuid> (unless explicitly set by the hook)node-<nodeId>idleMinutes adds a sliding idle window. When both daily and idle resets are configured, whichever expires first forces a new session.session.idleMinutes without any session.reset/resetByType config, CoderClaw stays in idle-only mode for backward compatibility.resetByType lets you override the policy for direct, group, and thread sessions (thread = Slack/Discord threads, Telegram topics, Matrix threads when provided by the connector).resetByChannel overrides the reset policy for a channel (applies to all session types for that channel and takes precedence over reset/resetByType)./new or /reset (plus any extras in resetTriggers) start a fresh session id and pass the remainder of the message through. /new <model> accepts a model alias, provider/model, or provider name (fuzzy match) to set the new session model. If /new or /reset is sent alone, CoderClaw runs a short “hello” greeting turn to confirm the reset.sessionId per run (no idle reuse).Block delivery for specific session types without listing individual ids.
{
session: {
sendPolicy: {
rules: [
{ action: "deny", match: { channel: "discord", chatType: "group" } },
{ action: "deny", match: { keyPrefix: "cron:" } },
// Match the raw session key (including the `agent:<id>:` prefix).
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
],
default: "allow",
},
},
}
Runtime override (owner only):
/send on → allow for this session/send off → deny for this session/send inherit → clear override and use config rules
Send these as standalone messages so they register.// ~/.coderclaw/coderclaw.json
{
session: {
scope: "per-sender", // keep group keys separate
dmScope: "main", // DM continuity (set per-channel-peer/per-account-channel-peer for shared inboxes)
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
// Defaults: mode=daily, atHour=4 (gateway host local time).
// If you also set idleMinutes, whichever expires first wins.
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
resetByType: {
thread: { mode: "daily", atHour: 4 },
direct: { mode: "idle", idleMinutes: 240 },
group: { mode: "idle", idleMinutes: 120 },
},
resetByChannel: {
discord: { mode: "idle", idleMinutes: 10080 },
},
resetTriggers: ["/new", "/reset"],
store: "~/.coderclaw/agents/{agentId}/sessions/sessions.json",
mainKey: "main",
},
}
coderclaw status — shows store path and recent sessions.coderclaw sessions --json — dumps every entry (filter with --active <minutes>).coderclaw gateway call sessions.list --params '{}' — fetch sessions from the running gateway (use --url/--token for remote gateway access)./status as a standalone message in chat to see whether the agent is reachable, how much of the session context is used, current thinking/verbose toggles, and when your WhatsApp web creds were last refreshed (helps spot relink needs)./context list or /context detail to see what’s in the system prompt and injected workspace files (and the biggest context contributors)./stop as a standalone message to abort the current run, clear queued followups for that session, and stop any sub-agent runs spawned from it (the reply includes the stopped count)./compact (optional instructions) as a standalone message to summarize older context and free up window space. See /concepts/compaction.Each session entry records where it came from (best-effort) in origin:
label: human label (resolved from conversation label + group subject/channel)provider: normalized channel id (including extensions)from/to: raw routing ids from the inbound envelopeaccountId: provider account id (when multi-account)threadId: thread/topic id when the channel supports it
The origin fields are populated for direct messages, channels, and groups. If a
connector only updates delivery routing (for example, to keep a DM main session
fresh), it should still provide inbound context so the session keeps its
explainer metadata. Extensions can do this by sending ConversationLabel,
GroupSubject, GroupChannel, GroupSpace, and SenderName in the inbound
context and calling recordSessionMetaFromInbound (or passing the same context
to updateLastRoute).