Reviewing sessions
The review app is a local web page served by the Postrun server. It reads the store, lists every recorded session, and shows one session at a time as a report: the files it touched, the commands it ran, and a timeline of every step. It updates live while agents are working.
Open it
pnpm serve
pnpm serve builds the review app, then serves it with the store at http://127.0.0.1:1234/. On start it prints the store path, the sessions in it, and the address:
postrun store /home/you/.postrun/postrun.db: 3 session(s)
...
serving UI from /path/to/postrun/apps/ui/out
ingest: POST http://127.0.0.1:1234/api/ingest (bearer token in /home/you/.postrun/ingest-token)
listening on http://127.0.0.1:1234/ (127.0.0.1 only; ctrl-c to stop)
Options:
pnpm serve --port 4321 # or PORT=4321; the flag wins, then PORT, then 1234
pnpm serve --db /some/other/postrun.db
The server only reads the store; it never runs adapters. Sessions arrive from pnpm capture, pnpm ingest or the ingest API, and appear without a restart. All of these can run at the same time: they share the SQLite file.
The session list
The home page lists every stored session, newest first. Each row shows:
- The agent badge and version.
- The session title (the first line of the first prompt) and the start of the session id.
- The workspace folder name.
- When it started, as a date and a relative time.
- The step count, broken down by type, with failed and reference-only counts.
- The cost the agent reported.
- The number of flags.
Above the list, the agent chips filter by agent kind (the address becomes /?agent=<kind>), and the total shows the number of sessions and their combined cost. Click a row to open the session.
The session view
A session opens at /session?id=<id>.
Summary
The top card shows the agent, the title and version, and:
| Item | What it is |
|---|---|
| cost | Reported API cost and number of API requests |
| steps, turns | Totals for the session |
| failed | Steps whose outcome is failed |
| reference-only | Steps whose content was not available inline |
| workspace | The working directory |
| when | Start and end time, or open while the session has no end, plus the number of segments |
| owner | The owner id and the machine it was captured on |
| tokens | Input and output tokens |
Files touched
Every file the session created, edited or read, with a count of each and how many of those steps failed.
Commands run
Every distinct command, with how many times it ran and the exit codes seen. Commands whose output is not inline are tagged output not inline.
Timeline
The timeline groups steps by turn. Each turn shows its number, the prompt that started it, and its mode (for Cline, plan or act; otherwise no mode). Each step shows its seq, its type, a one-line summary, and a status:
okfailed, followed by the error type when the agent reported onereference-only, when the content is a pointer rather than inline
Click a step, or focus it and press Enter, to open everything it recorded:
| Step | What opens |
|---|---|
command | The command, its working directory and exit code, then stdout and stderr |
edit | The file and the change as a diff: removed lines in red, added lines in green |
read | The file and the line range read |
message | The full prompt or reply |
other | The tool name and everything recorded for it |
Each step also shows when it happened, the permission decision when one was recorded, and any error or flag. Output longer than 100,000 characters is cut, with a button to show all of it. Expand all and Collapse all sit next to the timeline heading. To link to one step, add #step-<seq> to the address: that step opens and scrolls into view. An open step stays open while the session updates live.
Long sessions stay quick to open and to watch:
- The timeline shows the 30 most recent turns. Show earlier turns above them adds 50 at a time. A link to a step shows every turn.
- The session arrives with each step's first 2 KB of output; opening a step loads the rest.
- A live update fetches only the steps that changed since the last one, at most once every two seconds, and redraws only the turns that changed.
A Claude Code session recorded while pnpm capture was stopped shows not recorded for cost: see Troubleshooting.
Live updates
The app keeps one connection per browser tab to the live events stream. When a session is written, the list and the open session refetch in place. The pill in the top bar shows the connection state:
| Pill | Meaning |
|---|---|
live | Connected. Changes appear as they are written. |
connecting | Opening or reopening the connection. |
server offline | The server cannot be reached. What is on screen stays, and the app catches up when the server is back. |
A Claude Code session updates at the end of each assistant turn. A Cline session updates shortly after Cline saves its messages file.
Export
The Export report button on a session opens the export panel. It lists every value redaction will mask before you download. See Exporting and redaction.
Deleting a session
The Delete button on a session removes it from Postrun on this computer. A short panel asks you to confirm first, with Cancel selected, and says how many steps will go.
Deleting removes:
- the session's steps, turns and summary from the store, with SQLite's secure delete on, so the content is overwritten rather than left in free pages, and the write-ahead log is cleared;
- its raw capture files, for a Claude Code session.
Postrun then remembers only the session id, so the recorder will not record that session again, even if the agent keeps writing to it. Pushes for it get 410. Any other tab showing the session says it is no longer in Postrun.
Deleting does not touch the agent's own copy. Claude Code keeps its transcripts under ~/.claude/projects and Cline keeps tasks in ~/.cline/data/sessions; delete them there if you want them gone everywhere.
The same works from a terminal with pnpm delete. The button is not shown in the demo.
Read routes
The review app uses these routes, which you can also call from scripts on the same machine. They need no token.
| Route | Returns |
|---|---|
GET /api/sessions | { sessions, agents }, newest first. Add ?agent=<kind> to filter. |
GET /api/sessions/<id> | The full session (summary, segments, actors, turns, steps) plus report (files touched, commands run). 404 if not found. |
GET /api/sessions/<id>/export | The redacted HTML report, as a download. |
GET /api/sessions/<id>/export/review | What the export would mask, as JSON. |
GET /api/events | The live events stream. |
DELETE /api/sessions/<id> | Deletes the session (see above). Only accepted from the review app itself: requests from another site get 403. 404 if not found. |
Every response carries x-content-type-options: nosniff, referrer-policy: no-referrer and cache-control: no-store, and no CORS headers.
Working on the review app
To run the app with hot reload, start the core server and the Next dev server in two terminals:
pnpm dev:core # API and the last built UI on 127.0.0.1:1234
pnpm dev:ui # Next dev server on 127.0.0.1:3000, with /api proxied to 127.0.0.1:1234
Set UI_PORT to move the dev server, and POSTRUN_CORE_URL to proxy to a core server on another address.