How We Built the OpenClaw Session Viewer
A lightweight, server-side viewer for AI agent session logs — from frustrating JSONL files to a paginated conversation.

OpenClaw agent sessions are stored as JSONL files — one JSON object per line, one file per session. This is great for reliability and append-only logging. It's terrible for reading.
A single active session can easily grow to 36MB+. Open that in a browser and your tab crashes. Open it in VS Code and the whole editor stutters. grep works, but it's not great when you want to understand a conversation flow, see what tools were called, or trace a chain of reasoning.
We needed a way to browse session logs like a conversation — not a log file.
The Approach
The obvious solution would be to load the file and render it in the browser. But that defeats the purpose — the browser still has to parse 36MB of JSON. So we went the other way:
Server-side parsing. A Flask app reads the JSONL files on disk, extracts the conversation — user messages, assistant responses, tool calls, tool results — and returns paginated chunks of 100 messages at a time. The browser only ever sees what fits on one screen.
This means even the biggest session files load instantly. No loading spinner, no memory warning, just a conversation.
Architecture
The app is deliberately small — about 400 lines of Python, no database, no build step.
It reads from your OpenClaw agents directory, parses JSONL files, and renders them. That's it. No external API calls, no data leaving your machine.
Key Design Decisions
Why Flask? Because the app is simple enough that a framework would add more complexity than it solves. Flask gives us routing, templating, and a test client. Everything else is just Python.
Why Jinja2 templates instead of a SPA? Two reasons. First, the app works with JavaScript disabled — it's just HTML with pagination links. Second, server-side rendering means the initial page load is instant, no JS bundle to download.
Why no database? The session files already are the database. They're append-only, sorted by time, and each one is self-contained. Adding a database would just duplicate data and add complexity.
Features We're Proud Of
Copy to clipboard. Click the 📋 button on any message to copy its content. Useful for grabbing prompts, debugging tool calls, or sharing snippets.
Expandable tool calls. Long JSON arguments are truncated by default with a "Show full" toggle. Tool results work the same way. Keeps the conversation readable without hiding important details.
Pagination with page numbers. Not just prev/next. You get "1 2 3 … 20 21" so you can jump to any page.
Prometheus metrics. The /metrics endpoint exposes session counts, message totals, and tool call stats per agent. We use this in our Grafana dashboards.
CI/CD and Quality
Every change goes through GitHub Actions:
Lint — flake8 catches syntax errors and style issues
Tests — 10 pytest tests covering routes, agent stats, metrics, and caching
Releases — push a
v*tag and GitHub automatically creates a release with changelog
Local development is one command: make check runs lint and tests together.
What's Next
Some ideas we're considering:
Search within a session (Ctrl+F but server-side)
Side-by-side session comparison
Export a session as a readable transcript (PDF or markdown)
Docker Compose setup for easy deployment
Try It
If you're an OpenClaw user and this sounds useful, give it a try:
github.com/CoraCodeDev/oc-session-viewer
MIT licensed, runs on any Python 3.10+ host. Pull requests welcome.
Built by CoraCode.

