세션 관리
OpenClaw은 에이전트당 하나의 직접 대화 세션을 기본으로 취급합니다. 직접 대화는 agent:<agentId>:<mainKey> (기본값 main)로 합쳐지고, 그룹/채널 대화는 자체 키를 갖습니다. session.mainKey가 적용됩니다.
session.dmScope를 사용하여 다이렉트 메시지의 그룹화 방식을 제어합니다:
main(기본값): 모든 DM이 연속성을 위해 메인 세션을 공유합니다.per-peer: 채널에 상관없이 발신자 ID별로 격리합니다.per-channel-peer: 채널 + 발신자별로 격리합니다 (다중 사용자 인박스에 권장).per-account-channel-peer: 계정 + 채널 + 발신자별로 격리합니다 (다중 계정 인박스에 권장).session.identityLinks를 사용하여 프로바이더 접두사가 붙은 피어 ID를 정규 ID에 매핑하면,per-peer,per-channel-peer,per-account-channel-peer사용 시 동일인이 채널 간에 DM 세션을 공유할 수 있습니다.
보안 DM 모드 (다중 사용자 설정에 권장)
보안 경고: 에이전트가 여러 사람으로부터 DM을 받을 수 있다면 보안 DM 모드 활성화를 강력히 고려해야 합니다. 활성화하지 않으면 모든 사용자가 동일한 대화 컨텍스트를 공유하여 사용자 간에 개인 정보가 유출될 수 있습니다.
기본 설정의 문제 예시:
- Alice (
<SENDER_A>)가 에이전트에게 개인적인 주제(예: 진료 예약)에 대해 메시지를 보냄 - Bob (
<SENDER_B>)이 에이전트에게 “우리 무슨 얘기하고 있었지?”라고 메시지를 보냄 - 두 DM이 같은 세션을 공유하기 때문에 모델이 Alice의 이전 컨텍스트를 사용하여 Bob에게 답할 수 있음
해결 방법: dmScope를 설정하여 사용자별로 세션을 격리하세요:
// ~/.openclaw/openclaw.json
{
session: {
// 보안 DM 모드: 채널 + 발신자별로 DM 컨텍스트 격리.
dmScope: "per-channel-peer",
},
}
활성화해야 하는 경우:
- 둘 이상의 발신자에 대해 페어링 승인이 있는 경우
- 여러 항목이 있는 DM 허용 목록을 사용하는 경우
dmPolicy: "open"을 설정한 경우- 여러 전화번호 또는 계정이 에이전트에게 메시지를 보낼 수 있는 경우
참고:
- 기본값은 연속성을 위한
dmScope: "main"(모든 DM이 메인 세션 공유)입니다. 단일 사용자 설정에 적합합니다. - 로컬 CLI 온보딩은 미설정 시 기본적으로
session.dmScope: "per-channel-peer"를 기록합니다(기존 명시적 값은 유지됨). - 동일 채널의 다중 계정 인박스에는
per-account-channel-peer를 권장합니다. - 같은 사람이 여러 채널로 연락하는 경우
session.identityLinks를 사용하여 DM 세션을 하나의 정규 ID로 통합하세요. openclaw security audit로 DM 설정을 확인할 수 있습니다(보안 참고).
게이트웨이가 신뢰할 수 있는 단일 출처
모든 세션 상태는 게이트웨이(“마스터” OpenClaw)가 소유합니다. UI 클라이언트(macOS 앱, WebChat 등)는 로컬 파일을 읽는 대신 게이트웨이에서 세션 목록과 토큰 수를 조회해야 합니다.
- 원격 모드에서는 관심 대상인 세션 저장소가 Mac이 아닌 원격 게이트웨이 호스트에 있습니다.
- UI에 표시되는 토큰 수는 게이트웨이 저장소 필드(
inputTokens,outputTokens,totalTokens,contextTokens)에서 가져옵니다. 클라이언트는 JSONL 대화 기록을 파싱하여 합계를 “수정”하지 않습니다.
상태 저장 위치
- 게이트웨이 호스트:
- 저장소 파일:
~/.openclaw/agents/<agentId>/sessions/sessions.json(에이전트별).
- 저장소 파일:
- 대화 기록:
~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 토픽 세션은.../<SessionId>-topic-<threadId>.jsonl사용). - 저장소는
sessionKey -> { sessionId, updatedAt, ... }맵입니다. 항목 삭제는 안전하며 다음 메시지에서 재생성됩니다. - 그룹 항목에는 UI에서 세션에 레이블을 지정하기 위한
displayName,channel,subject,room,space가 포함될 수 있습니다. - 세션 항목에는 UI가 세션의 출처를 설명할 수 있도록
origin메타데이터(레이블 + 라우팅 힌트)가 포함됩니다. - OpenClaw은 레거시 Pi/Tau 세션 폴더를 읽지 않습니다.
유지보수
OpenClaw은 sessions.json과 대화 기록 아티팩트를 시간이 지나도 제한된 크기로 유지하기 위해 세션 저장소 유지보수를 적용합니다.
기본값
session.maintenance.mode:warnsession.maintenance.pruneAfter:30dsession.maintenance.maxEntries:500session.maintenance.rotateBytes:10mbsession.maintenance.resetArchiveRetention:pruneAfter를 기본값으로 사용 (30d)session.maintenance.maxDiskBytes: 미설정 (비활성화)session.maintenance.highWaterBytes: 예산 관리가 활성화된 경우maxDiskBytes의80%를 기본값으로 사용
작동 방식
유지보수는 세션 저장소 쓰기 시 실행되며, openclaw sessions cleanup으로 수동 트리거할 수도 있습니다.
mode: "warn": 제거 대상을 보고하지만 항목/대화 기록을 변경하지 않습니다.mode: "enforce": 다음 순서로 정리를 적용합니다:pruneAfter보다 오래된 만료 항목 제거- 항목 수를
maxEntries로 제한 (오래된 것부터) - 더 이상 참조되지 않는 제거된 항목의 대화 기록 파일 보관
- 보존 정책에 따라 오래된
*.deleted.<timestamp>및*.reset.<timestamp>보관 파일 삭제 sessions.json이rotateBytes를 초과하면 로테이션maxDiskBytes가 설정된 경우highWaterBytes를 향해 디스크 예산 적용 (가장 오래된 아티팩트부터, 그 다음 가장 오래된 세션)
대규모 저장소의 성능 주의사항
대용량 세션 저장소는 고트래픽 환경에서 흔합니다. 유지보수 작업은 쓰기 경로에서 수행되므로 매우 큰 저장소는 쓰기 지연을 증가시킬 수 있습니다.
비용 증가 요인:
session.maintenance.maxEntries값이 매우 높은 경우- 만료된 항목을 유지하는 긴
pruneAfter기간 ~/.openclaw/agents/<agentId>/sessions/에 많은 대화 기록/보관 아티팩트가 있는 경우- 합리적인 프루닝/제한 없이 디스크 예산(
maxDiskBytes)을 활성화한 경우
권장 사항:
- 프로덕션에서는
mode: "enforce"를 사용하여 증가를 자동으로 제한 - 시간과 수 제한(
pruneAfter+maxEntries) 모두 설정(하나만이 아닌) - 대규모 배포에서는
maxDiskBytes+highWaterBytes로 하드 상한 설정 highWaterBytes를maxDiskBytes보다 의미 있게 낮게 유지 (기본값 80%)- 설정 변경 후 적용 전
openclaw sessions cleanup --dry-run --json을 실행하여 예상 영향 확인 - 자주 활성화되는 세션의 경우 수동 정리 시
--active-key전달
사용자 정의 예시
보수적인 적용 정책:
{
session: {
maintenance: {
mode: "enforce",
pruneAfter: "45d",
maxEntries: 800,
rotateBytes: "20mb",
resetArchiveRetention: "14d",
},
},
}
세션 디렉터리에 하드 디스크 예산 활성화:
{
session: {
maintenance: {
mode: "enforce",
maxDiskBytes: "1gb",
highWaterBytes: "800mb",
},
},
}
대규모 설치 최적화 (예시):
{
session: {
maintenance: {
mode: "enforce",
pruneAfter: "14d",
maxEntries: 2000,
rotateBytes: "25mb",
maxDiskBytes: "2gb",
highWaterBytes: "1.6gb",
},
},
}
CLI에서 유지보수 미리보기 또는 강제 실행:
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce
세션 프루닝
OpenClaw은 기본적으로 LLM 호출 직전에 인메모리 컨텍스트에서 오래된 도구 결과를 트리밍합니다. JSONL 히스토리를 수정하지 않습니다. /concepts/session-pruning을 참고하세요.
압축 전 메모리 플러시
세션이 자동 압축에 가까워지면 OpenClaw은 모델에게 지속적인 메모를 디스크에 기록하도록 알려주는 무음 메모리 플러시 턴을 실행할 수 있습니다. 워크스페이스가 쓰기 가능한 경우에만 실행됩니다. 메모리 및 압축을 참고하세요.
전송 방식 → 세션 키 매핑
- 직접 대화는
session.dmScope를 따릅니다(기본값main).main:agent:<agentId>:<mainKey>(기기/채널 간 연속성).- 여러 전화번호와 채널이 동일한 에이전트 메인 키에 매핑될 수 있으며, 하나의 대화로 전달되는 전송 방식으로 작동합니다.
per-peer:agent:<agentId>:direct:<peerId>.per-channel-peer:agent:<agentId>:<channel>:direct:<peerId>.per-account-channel-peer:agent:<agentId>:<channel>:<accountId>:direct:<peerId>(accountId 기본값default).session.identityLinks가 프로바이더 접두사가 붙은 피어 ID(예:telegram:123)와 일치하면 정규 키가<peerId>를 대체하여 동일인이 채널 간에 세션을 공유합니다.
- 그룹 대화는 상태를 격리합니다:
agent:<agentId>:<channel>:group:<id>(룸/채널은agent:<agentId>:<channel>:channel:<id>사용).- Telegram 포럼 토픽은 격리를 위해 그룹 ID에
:topic:<threadId>를 추가합니다. - 레거시
group:<id>키는 마이그레이션을 위해 여전히 인식됩니다.
- Telegram 포럼 토픽은 격리를 위해 그룹 ID에
- 수신 컨텍스트는 여전히
group:<id>를 사용할 수 있으며, 채널은Provider에서 추론되어 정규agent:<agentId>:<channel>:group:<id>형식으로 정규화됩니다. - 기타 소스:
- 크론 작업:
cron:<job.id> - 웹훅:
hook:<uuid>(훅에 의해 명시적으로 설정되지 않는 한) - 노드 실행:
node-<nodeId>
- 크론 작업:
수명 주기
- 리셋 정책: 세션은 만료될 때까지 재사용되며, 만료 여부는 다음 수신 메시지에서 평가됩니다.
- 일일 리셋: 기본값은 게이트웨이 호스트의 현지 시간 오전 4시입니다. 세션의 마지막 업데이트가 가장 최근 일일 리셋 시간보다 이전이면 만료됩니다.
- 유휴 리셋 (선택):
idleMinutes로 슬라이딩 유휴 윈도우를 추가합니다. 일일 리셋과 유휴 리셋이 모두 구성된 경우 먼저 만료되는 쪽이 새 세션을 강제합니다. - 레거시 유휴 전용:
session.reset/resetByType설정 없이session.idleMinutes만 설정하면 하위 호환성을 위해 유휴 전용 모드를 유지합니다. - 유형별 오버라이드 (선택):
resetByType을 사용하여direct,group,thread세션의 정책을 오버라이드할 수 있습니다(thread = Slack/Discord 스레드, Telegram 토픽, 커넥터가 제공하는 Matrix 스레드). - 채널별 오버라이드 (선택):
resetByChannel은 채널에 대한 리셋 정책을 오버라이드합니다(해당 채널의 모든 세션 유형에 적용되며reset/resetByType보다 우선). - 리셋 트리거: 정확히
/new또는/reset(및resetTriggers의 추가 항목)은 새 세션 ID를 시작하고 나머지 메시지를 전달합니다./new <model>은 모델 별칭,provider/model, 또는 프로바이더 이름(퍼지 매치)을 받아 새 세션 모델을 설정합니다./new또는/reset만 보내면 OpenClaw이 리셋 확인을 위한 짧은 “인사” 턴을 실행합니다. - 수동 리셋: 저장소에서 특정 키를 삭제하거나 JSONL 대화 기록을 제거하면 다음 메시지에서 재생성됩니다.
- 격리된 크론 작업은 항상 실행마다 새
sessionId를 생성합니다(유휴 재사용 없음).
전송 정책 (선택)
개별 ID를 나열하지 않고 특정 세션 유형에 대한 전달을 차단합니다.
{
session: {
sendPolicy: {
rules: [
{ action: "deny", match: { channel: "discord", chatType: "group" } },
{ action: "deny", match: { keyPrefix: "cron:" } },
// 원시 세션 키(`agent:<id>:` 접두사 포함)를 매칭합니다.
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
],
default: "allow",
},
},
}
런타임 오버라이드 (소유자 전용):
/send on→ 이 세션에서 허용/send off→ 이 세션에서 거부/send inherit→ 오버라이드 해제 후 설정 규칙 사용 독립 메시지로 보내야 등록됩니다.
구성 (선택적 이름 변경 예시)
// ~/.openclaw/openclaw.json
{
session: {
scope: "per-sender", // 그룹 키 별도 유지
dmScope: "main", // DM 연속성 (공유 인박스에는 per-channel-peer/per-account-channel-peer 설정)
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
// 기본값: mode=daily, atHour=4 (게이트웨이 호스트 현지 시간).
// idleMinutes도 설정하면 먼저 만료되는 쪽이 적용됩니다.
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: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
mainKey: "main",
},
}
확인
openclaw status— 저장소 경로와 최근 세션을 표시합니다.openclaw sessions --json— 모든 항목을 덤프합니다(--active <minutes>로 필터링).openclaw gateway call sessions.list --params '{}'— 실행 중인 게이트웨이에서 세션을 가져옵니다(원격 게이트웨이 접근 시--url/--token사용).- 대화에서 독립 메시지로
/status를 보내면 에이전트 도달 가능 여부, 세션 컨텍스트 사용량, 현재 사고/빠른/상세 토글, WhatsApp 웹 자격 증명의 마지막 갱신 시간(재연결 필요 여부 확인에 도움)을 확인할 수 있습니다. /context list또는/context detail을 보내면 시스템 프롬프트와 주입된 워크스페이스 파일의 내용(및 가장 큰 컨텍스트 기여자)을 확인할 수 있습니다./stop을 보내거나 (stop,stop action,stop run,stop openclaw같은 독립 중단 구문) 현재 실행을 중단하고 해당 세션의 큐에 있는 후속 작업을 지우고 거기서 스폰된 서브 에이전트 실행도 중지합니다(응답에 중지된 수가 포함됨).- 독립 메시지로
/compact(선택적 지시사항)를 보내면 오래된 컨텍스트를 요약하고 윈도우 공간을 확보합니다. /concepts/compaction을 참고하세요. - JSONL 대화 기록을 직접 열어 전체 턴을 검토할 수 있습니다.
팁
- 기본 키는 1:1 트래픽 전용으로 유지하고, 그룹은 자체 키를 사용하도록 하세요.
- 자동 정리 시 전체 저장소 대신 개별 키를 삭제하여 다른 곳의 컨텍스트를 보존하세요.
세션 출처 메타데이터
각 세션 항목은 출처를 origin에 기록합니다(최선 노력):
label: 사람이 읽을 수 있는 레이블 (대화 레이블 + 그룹 주제/채널에서 해석)provider: 정규화된 채널 ID (확장 포함)from/to: 수신 엔벨로프의 원시 라우팅 IDaccountId: 프로바이더 계정 ID (다중 계정 시)threadId: 채널이 지원하는 경우 스레드/토픽 ID 출처 필드는 다이렉트 메시지, 채널, 그룹에 대해 채워집니다. 커넥터가 전달 라우팅만 업데이트하는 경우(예: DM 메인 세션을 최신 상태로 유지하기 위해)에도 세션이 설명 메타데이터를 유지하도록 수신 컨텍스트를 제공해야 합니다. 확장에서는 수신 컨텍스트에ConversationLabel,GroupSubject,GroupChannel,GroupSpace,SenderName을 보내고recordSessionMetaFromInbound를 호출(또는updateLastRoute에 같은 컨텍스트를 전달)하여 이를 수행할 수 있습니다.