세션 로그은(는) 무엇을 하나요?
세션 JSONL 파일에 저장된 전체 대화 기록을 검색합니다. 사용자가 이전/부모 대화를 참조하거나 이전에 언급된 내용을 물을 때 사용합니다.
트리거
사용자가 이전 채팅, 부모 대화, 또는 메모리 파일에 없는 과거 맥락에 대해 질문할 때 이 스킬을 사용합니다.
위치
세션 로그는 활성 상태 디렉토리에 있습니다:
$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/ (기본값: ~/.openclaw/agents/<agentId>/sessions/).
시스템 프롬프트 Runtime 라인의 agent=<id> 값을 사용하세요.
sessions.json- 세션 키를 세션 ID에 매핑하는 인덱스<session-id>.jsonl- 세션별 전체 대화 기록<session-id>.jsonl.reset.<timestamp>Z-/new또는/reset명령으로 보관된 기록<session-id>.jsonl.deleted.<timestamp>Z- 세션이 삭제될 때 보관된 기록
기록을 검색할 때는 보관된 (.reset.*, .deleted.*) 변형도 포함하세요. 이 파일들에도 실제 대화 내용이 포함되어 있습니다. 아래의 단순 글롭 예제는 활성 상태인 *.jsonl 파일만 찾습니다. 전체 검색이 필요할 때는 "보관된 기록 포함" 스니펫을 사용하세요.
구조
각 .jsonl 파일에는 다음과 같은 메시지가 포함됩니다:
type: "session" (메타데이터) 또는 "message"timestamp: ISO 타임스탬프message.role: "user", "assistant", 또는 "toolResult"message.content[]: 텍스트, 생각, 또는 도구 호출 (사람이 읽을 수 있는 내용은type=="text"필터)message.usage.cost.total: 응답당 비용
일반적인 질의
보관된 기록 포함 (.reset.*, .deleted.*)
# 검색 가능한 모든 기록 경로(활성 + 보관)를 출력하는 Bash 헬퍼.
# 호출자의 셸 옵션에 영향을 주지 않도록 nullglob을 로컬에서 저장/복원합니다.
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
list_session_transcripts() {
local _nullglob_state
_nullglob_state=$(shopt -p nullglob 2>/dev/null)
shopt -s nullglob
for f in "$SESSION_DIR"/*.jsonl \
"$SESSION_DIR"/*.jsonl.reset.*Z \
"$SESSION_DIR"/*.jsonl.deleted.*Z; do
[ -f "$f" ] && printf '%s\n' "$f"
done
eval "$_nullglob_state"
}
아래에 단순 *.jsonl 글롭이 표시된 곳마다 보관된 세션을 포함해야 할 경우 list_session_transcripts (또는 이에 상응하는 find 호출)을 사용하세요:
find "$SESSION_DIR" -maxdepth 1 -type f \
\( -name '*.jsonl' -o -name '*.jsonl.reset.*Z' -o -name '*.jsonl.deleted.*Z' \) -print
모든 세션을 날짜와 크기별로 나열
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
size=$(ls -lh "$f" | awk '{print $5}')
echo "$date $size $(basename $f)"
done | sort -r
팁: 목록에 보관된 .reset / .deleted 파일도 포함하려면 for f in ... 라인을 list_session_transcripts에 대한 while-read 루프로 바꾸세요 (위 스니펫 참조). while-read 패턴은 공백이나 기타 IFS 문자가 있는 경로에도 안전합니다:
while IFS= read -r f; do
date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
size=$(ls -lh "$f" | awk '{print $5}')
echo "$date $size $(basename "$f")"
done < <(list_session_transcripts) | sort -r
특정 날짜의 세션 찾기
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
head -1 "$f" | jq -r '.timestamp' | grep -q "2026-01-06" && echo "$f"
done
세션에서 사용자 메시지 추출
jq -r 'select(.message.role == "user") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl
어시스턴트 응답에서 키워드 검색
jq -r 'select(.message.role == "assistant") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl | rg -i "keyword"
세션의 총 비용 가져오기
jq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl
일별 비용 요약
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' "$f")
echo "$date $cost"
done | awk '{a[$1]+=$2} END {for(d in a) print d, "$"a[d]}' | sort -r
세션의 메시지 및 토큰 개수 세기
jq -s '{
messages: length,
user: [.[] | select(.message.role == "user")] | length,
assistant: [.[] | select(.message.role == "assistant")] | length,
first: .[0].timestamp,
last: .[-1].timestamp
}' <session>.jsonl
도구 사용 분석
jq -r '.message.content[]? | select(.type == "toolCall") | .name' <session>.jsonl | sort | uniq -c | sort -rn
모든 세션에서 특정 구문 검색
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
# 활성 세션만:
rg -l "phrase" "$SESSION_DIR"/*.jsonl
# 활성 + 보관 (`.reset.*`, `.deleted.*`) — 압축/리셋/삭제된 내용까지 확인할 때 사용:
rg -l "phrase" "$SESSION_DIR"/*.jsonl \
"$SESSION_DIR"/*.jsonl.reset.*Z \
"$SESSION_DIR"/*.jsonl.deleted.*Z 2>/dev/null
팁
- 세션은 추가 전용 JSONL입니다 (줄당 하나의 JSON 객체).
- 큰 세션은 수 MB가 될 수 있으므로
head/tail로 샘플링하세요. sessions.json인덱스는 채팅 제공자(discord, whatsapp 등)를 세션 ID에 매핑합니다.- 리셋/압축된 세션은
.jsonl.reset.<timestamp>Z접미사를 가집니다. 여전히 전체 기록을 포함하며 검색 가능합니다. - 삭제된 세션은
.jsonl.deleted.<timestamp>Z접미사를 가집니다. 이 역시 검색 가능합니다. - 단순한
*.jsonl글롭은 두 보관 형태를 모두 놓칩니다. 전체 기록이 필요할 때는 명시적으로 포함하세요 (위의 "보관된 기록 포함" 스니펫 참조).
빠른 텍스트 전용 힌트 (노이즈 최소화)
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' "$SESSION_DIR"/<id>.jsonl | rg 'keyword'