session-history
Search and browse past conversation history across all sessions. Use when recalling prior work, finding old discussions, resuming dropped threads, or when the user references something from a previous conversation that isn't in memory files. Also use when asked to "remember" something discussed before, find "that conversation about X", or continue work from a past session.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-session-history
Repository
Skill path: skills/amor71/session-history
Search and browse past conversation history across all sessions. Use when recalling prior work, finding old discussions, resuming dropped threads, or when the user references something from a previous conversation that isn't in memory files. Also use when asked to "remember" something discussed before, find "that conversation about X", or continue work from a past session.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install session-history into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding session-history to shared team environments
- Use session-history for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: session-history
description: Search and browse past conversation history across all sessions. Use when recalling prior work, finding old discussions, resuming dropped threads, or when the user references something from a previous conversation that isn't in memory files. Also use when asked to "remember" something discussed before, find "that conversation about X", or continue work from a past session.
---
# Session History
Search through past OpenClaw session transcripts (JSONL files in `~/.openclaw/agents/*/sessions/`).
## Quick Reference
```bash
# Search for conversations about a topic
python3 scripts/search_sessions.py "gclid pipeline error"
# List recent sessions
python3 scripts/search_sessions.py --list --days 3
# Search specific agent's history
python3 scripts/search_sessions.py "flight monitor" --agent main
# Wider time range
python3 scripts/search_sessions.py "quantum encryption" --days 30 --max-results 5
```
## Workflow
1. Run `search_sessions.py` with the user's query terms to find relevant sessions
2. Use `sessions_history` tool with the `sessionKey` to pull full context from a match
3. If `sessions_history` doesn't work (old/closed sessions), read the JSONL file directly with `read`
4. Summarize what was found β don't dump raw transcripts
## When to Use
- User says "remember when we discussed X?" or "we talked about Y last week"
- Resuming a thread that isn't captured in memory files
- Finding a decision, code snippet, or error from a past session
- Cross-referencing what was said vs what's in MEMORY.md
## Tips
- Also check `memory_search` first β it indexes session transcripts too
- Combine both: `memory_search` for semantic matching, `search_sessions.py` for keyword/exact matching
- The script searches user AND assistant messages
- JSONL path format: `~/.openclaw/agents/{agent_id}/sessions/{session_uuid}.jsonl`
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### scripts/search_sessions.py
```python
#!/usr/bin/env python3
"""Search through OpenClaw session transcripts for relevant conversations.
Usage:
python3 search_sessions.py <query> [--days N] [--max-results N] [--agent AGENT]
python3 search_sessions.py --list [--days N] [--agent AGENT]
Examples:
python3 search_sessions.py "error blog patches"
python3 search_sessions.py "flight monitor" --days 7
python3 search_sessions.py --list --days 3
python3 search_sessions.py "gclid pipeline" --agent main --max-results 5
"""
import argparse
import json
import os
import re
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
def find_session_dirs():
"""Find all agent session directories."""
base = Path.home() / ".openclaw" / "agents"
dirs = {}
if base.exists():
for agent_dir in base.iterdir():
sessions_dir = agent_dir / "sessions"
if sessions_dir.is_dir():
dirs[agent_dir.name] = sessions_dir
return dirs
def parse_session(path: Path):
"""Parse a JSONL session file into metadata + messages."""
meta = {}
messages = []
try:
with open(path) as f:
for line in f:
line = line.strip()
if not line:
continue
obj = json.loads(line)
t = obj.get("type")
if t == "session":
meta = obj
elif t == "message":
msg = obj.get("message", {})
role = msg.get("role", "")
content = msg.get("content", "")
# Extract text from content array
if isinstance(content, list):
texts = []
for block in content:
if isinstance(block, dict) and block.get("type") == "text":
texts.append(block.get("text", ""))
text = "\n".join(texts)
elif isinstance(content, str):
text = content
else:
text = ""
if text.strip():
messages.append({
"role": role,
"text": text,
"timestamp": obj.get("timestamp", ""),
})
except (json.JSONDecodeError, OSError):
pass
return meta, messages
def session_timestamp(meta):
"""Get session start time as datetime."""
ts = meta.get("timestamp", "")
if ts:
try:
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
except ValueError:
pass
return None
def search_text(query_terms, text):
"""Score text against query terms. Returns (score, matched_terms)."""
text_lower = text.lower()
matched = []
score = 0
for term in query_terms:
count = text_lower.count(term.lower())
if count > 0:
matched.append(term)
score += count
return score, matched
def excerpt_around_match(text, term, context_chars=150):
"""Get an excerpt around the first match of a term."""
idx = text.lower().find(term.lower())
if idx == -1:
return text[:300]
start = max(0, idx - context_chars)
end = min(len(text), idx + len(term) + context_chars)
excerpt = text[start:end]
if start > 0:
excerpt = "..." + excerpt
if end < len(text):
excerpt = excerpt + "..."
return excerpt
def main():
parser = argparse.ArgumentParser(description="Search OpenClaw session history")
parser.add_argument("query", nargs="*", help="Search terms")
parser.add_argument("--list", action="store_true", help="List recent sessions")
parser.add_argument("--days", type=int, default=14, help="Look back N days (default: 14)")
parser.add_argument("--max-results", type=int, default=10, help="Max results (default: 10)")
parser.add_argument("--agent", default=None, help="Filter by agent id (main, chai, nori)")
parser.add_argument("--verbose", "-v", action="store_true", help="Show message excerpts")
args = parser.parse_args()
if not args.list and not args.query:
parser.print_help()
sys.exit(1)
session_dirs = find_session_dirs()
if not session_dirs:
print("No session directories found.")
sys.exit(1)
cutoff = datetime.now(timezone.utc) - timedelta(days=args.days)
query_str = " ".join(args.query) if args.query else ""
query_terms = query_str.split() if query_str else []
results = []
agents = [args.agent] if args.agent else list(session_dirs.keys())
for agent_id in agents:
if agent_id not in session_dirs:
continue
sessions_dir = session_dirs[agent_id]
for jsonl_path in sessions_dir.glob("*.jsonl"):
# Quick file age check
stat = jsonl_path.stat()
file_mtime = datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
if file_mtime < cutoff:
continue
meta, messages = parse_session(jsonl_path)
ts = session_timestamp(meta)
if ts and ts < cutoff:
continue
if args.list:
# List mode β show session summaries
msg_count = len(messages)
user_msgs = [m for m in messages if m["role"] == "user"]
first_user = user_msgs[0]["text"][:120] if user_msgs else "(no user messages)"
results.append({
"agent": agent_id,
"session_id": meta.get("id", jsonl_path.stem),
"timestamp": ts.isoformat() if ts else "unknown",
"messages": msg_count,
"preview": first_user.replace("\n", " "),
"path": str(jsonl_path),
})
else:
# Search mode
all_text = "\n".join(m["text"] for m in messages)
score, matched = search_text(query_terms, all_text)
if score > 0:
# Find best excerpt
best_excerpt = ""
for term in matched:
for m in messages:
if term.lower() in m["text"].lower():
best_excerpt = excerpt_around_match(m["text"], term)
break
if best_excerpt:
break
user_msgs = [m for m in messages if m["role"] == "user"]
first_user = user_msgs[0]["text"][:120] if user_msgs else ""
results.append({
"agent": agent_id,
"session_id": meta.get("id", jsonl_path.stem),
"timestamp": ts.isoformat() if ts else "unknown",
"score": score,
"matched_terms": matched,
"messages": len(messages),
"preview": first_user.replace("\n", " "),
"excerpt": best_excerpt.replace("\n", " "),
"path": str(jsonl_path),
})
# Sort
if args.list:
results.sort(key=lambda r: r["timestamp"], reverse=True)
else:
results.sort(key=lambda r: r["score"], reverse=True)
results = results[:args.max_results]
if not results:
if args.list:
print(f"No sessions found in the last {args.days} days.")
else:
print(f"No sessions matching '{query_str}' in the last {args.days} days.")
sys.exit(0)
# Output
if args.list:
print(f"π Recent sessions (last {args.days} days):\n")
for r in results:
ts_short = r["timestamp"][:16].replace("T", " ") if r["timestamp"] != "unknown" else "?"
print(f" [{r['agent']}] {ts_short} ({r['messages']} msgs) β {r['preview'][:80]}")
if args.verbose:
print(f" Path: {r['path']}")
print()
else:
print(f"π Found {len(results)} sessions matching '{query_str}':\n")
for i, r in enumerate(results, 1):
ts_short = r["timestamp"][:16].replace("T", " ") if r["timestamp"] != "unknown" else "?"
print(f" {i}. [{r['agent']}] {ts_short} (score: {r['score']}, {r['messages']} msgs)")
print(f" Matched: {', '.join(r['matched_terms'])}")
if r.get("excerpt"):
print(f" Excerpt: {r['excerpt'][:200]}")
print(f" Path: {r['path']}")
print()
if __name__ == "__main__":
main()
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "amor71",
"slug": "session-history",
"displayName": "Session History",
"latest": {
"version": "1.0.0",
"publishedAt": 1771532008087,
"commit": "https://github.com/openclaw/skills/commit/92a09936032ea9e0af0545060d29eb16844271be"
},
"history": []
}
```