You start a coding agent on a refactor. You go make coffee. You come back to a clean diff and a passing test suite. Great.
But what happened in between? How many tool calls did it make? Did it try anything that got blocked? Did it access files it did not need? Did it reach for the network?
Until now, the honest answer was: you do not know.
$ agentjail logs
2026-06-21 02:14:33 [ALLOW] Bash: git status
2026-06-21 02:14:35 [ALLOW] Read: src/payments/process.go
2026-06-21 02:14:41 [ASK] Write: src/payments/process.go
2026-06-21 02:14:58 [DENY] Bash: curl https://...
...
# 847 more lines. Good luck finding what you need.
The black box problem
Coding agents are not scripts. They do not follow a predictable path. The
same prompt can produce wildly different tool call sequences depending on
what the agent finds along the way. An agent that starts with
git status might end up at curl if it decides a dependency needs
updating.
Blocking dangerous calls is necessary. But if you cannot see the full sequence of what happened — the allows, the denials, the asks, across every session — you are flying blind. You do not know what your agent actually does. You only know what it told you it did.
agentjail ui
Starting with v0.1.2, agentjail ui opens a local replay viewer in your
browser. Every decision the daemon makes lands in a SQLite store, and the
UI queries it in real time.
Here is what it looks like in practice.
Sessions are not UUIDs anymore. The sidebar used to show
a3f2b1c8-4d2e-.... Now it shows claude-code - main - agentjail. The
agent, the branch, the repo. Git info is looked up once per session, so
there is zero overhead. You glance at the sidebar and know which session
belongs to which project.
# Before v0.2.4
+-------------------------------------+
| Sessions |
| * a3f2b1c8-4d2e-11ef-b3a9... |
| o 71cc8d02-9a1b-11ef-c241... |
| o 0f5e3a7d-2b8c-11ef-d182... |
+-------------------------------------+
# After v0.2.4
+-------------------------------------+
| Active |
| * claude-code . main . payments |
| |
| Recent |
| o cursor . feat/auth . api 2h ago |
| o claude-code . main . infra ystrdy |
+-------------------------------------+
Active sessions glow green. Anything with activity in the last 10 minutes is grouped under “Active.” Older sessions show relative timestamps (“2h ago”, “yesterday”). No more scrolling through a flat list trying to remember which UUID was the important one.
The timeline tells you where. Every event row shows the working
directory basename. When you are running agents across three repos, this
is the difference between “the agent ran rm somewhere” and “the agent
ran rm in the payments service.”
Live ticker. The header bar shows “last event: 3s ago” and updates every second. Leave the UI open on a second monitor and you always know whether your agent is still working or has gone quiet.
+-----------------------------------------------------------+
| agentjail v0.2.4 last event: 3s ago * live |
+-----------------------------------------------------------+
Agent logos. Each session shows whether it came from Claude Code, Cursor, Codex, or OpenCode. Useful when you are testing the same task across agents and want to compare their behavior.
Replay any session
The UI is live, but sometimes you want the terminal.
agentjail replay plays back any session from the SQLite store:
# What sessions exist?
agentjail replay --list
# Replay one
agentjail replay --session abc123
# Tail a live session
agentjail replay --follow
$ agentjail replay --session abc123
2026-06-21 02:14:33 ALLOW Bash git status
2026-06-21 02:14:35 ALLOW Read src/payments/process.go
2026-06-21 02:14:41 ASK Write src/payments/process.go [approved]
2026-06-21 02:14:58 DENY Bash curl https://api.stripe.com/... [no-outbound-curl]
2026-06-21 02:15:02 ALLOW Bash go test ./...
Every event includes the tool name, the verdict, the rule that fired (if any), and a timestamp. It is the audit trail you wish your agent came with.
It works on a 2019 MacBook Air
One thing we did not expect: the SQLite store was getting the CLI killed
on memory-constrained Macs. macOS jetsam (the OOM killer) was
terminating agentjail logs on machines with less than 200 MB free RAM.
We capped the SQLite cache at 1 MB, disabled memory-mapped I/O, and
added a fallback to streaming daemon.log if the database cannot open.
// Memory-constrained SQLite settings for low-RAM systems
db.SetMaxOpenConns(1)
db.Exec("PRAGMA cache_size = -1000") // 1 MB page cache cap
db.Exec("PRAGMA mmap_size = 0") // no memory-mapped I/O
Developer tooling that crashes on a 4-year-old laptop is developer tooling that gets uninstalled. We would rather degrade gracefully.
Try it
agentjail update
agentjail ui
The next time your agent runs overnight, you will know exactly what it did. The next time you wake up to a clean diff, you will know exactly what the agent touched — and exactly what it tried.