Skills › AI & Agent Engineering › Memory & context
session-logs
Search and analyze your own session logs (older/parent conversations) using jq.
The full skill
—
name: session-logs
description: Search and analyze your own session logs (older/parent conversations) using jq.
metadata:
{
"openclaw":
{
"emoji": "📜",
"requires": { "bins": ["jq", "rg"] },
"install":
[
{
"id": "brew-jq",
"kind": "brew",
"formula": "jq",
"bins": ["jq"],
"label": "Install jq (brew)",
},
{
"id": "brew-rg",
"kind": "brew",
"formula": "ripgrep",
"bins": ["rg"],
"label": "Install ripgrep (brew)",
},
],
},
}
—
# session-logs
Search your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.
## Trigger
Use this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.
## Location
Session logs live under the active state directory:
`$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` (default: `~/.openclaw/agents/<agentId>/sessions/`).
Use the `agent=<id>` value from the system prompt Runtime line.
– **`sessions.json`** – Index mapping session keys to session IDs
– **`<session-id>.jsonl`** – Full conversation transcript per session
## Structure
Each `.jsonl` file contains messages with:
– `type`: "session" (metadata) or "message"
– `timestamp`: ISO timestamp
– `message.role`: "user", "assistant", or "toolResult"
– `message.content[]`: Text, thinking, or tool calls (filter `type=="text"` for human-readable content)
– `message.usage.cost.total`: Cost per response
## Common Queries
### List all sessions by date and size
“`bash
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
“`
### Find sessions from a specific day
“`bash
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
“`
### Extract user messages from a session
“`bash
jq -r 'select(.message.role == "user") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl
“`
### Search for keyword in assistant responses
“`bash
jq -r 'select(.message.role == "assistant") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl | rg -i "keyword"
“`
### Get total cost for a session
“`bash
jq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl
“`
### Daily cost summary
“`bash
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
“`
### Count messages and tokens in a session
“`bash
jq -s '{
messages: length,
user: [.[] | select(.message.role == "user")] | length,
assistant: [.[] | select(.message.role == "assistant")] | length,
first: .[0].timestamp,
last: .[-1].timestamp
}' <session>.jsonl
“`
### Tool usage breakdown
“`bash
jq -r '.message.content[]? | select(.type == "toolCall") | .name' <session>.jsonl | sort | uniq -c | sort -rn
“`
### Search across ALL sessions for a phrase
“`bash
AGENT_ID="<agentId>"
SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
rg -l "phrase" "$SESSION_DIR"/*.jsonl
“`
## Tips
– Sessions are append-only JSONL (one JSON object per line)
– Large sessions can be several MB – use `head`/`tail` for sampling
– The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs
– Deleted sessions have `.deleted.<timestamp>` suffix
## Fast text-only hint (low noise)
“`bash
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'
“`