Fifty-Three Agents Before Lunch
Every coding agent on your laptop keeps a record of what you asked it and what it did. BotSpy reads those records from Claude Code, Codex, Cursor, Antigravity and Grok into one schema. The first thing it showed me was what one Saturday morning of my own work actually looked like: 19 agents working at once. The second was that those records are full of secrets, so they need a privacy control before you build anything on them.
Our weekly Claude Code credits reset at noon on Saturday, and on the morning of Saturday 31 October we still had a lot of them left. Letting them expire felt like a waste, so I spent the morning in a blitz, handing every ticket I could find to an agent before the clock ran out. By 8:52 AM there were 19 agents working at once. I knew I was running a lot in parallel. I did not know it was 19, because nothing I use shows me that number.
The data was already on my machine. Claude Code, Codex, Cursor, Antigravity and Grok each keep a local history of every conversation: your messages, the agent's replies, its tool calls, the sub-agents it started. Each tool stores it differently, in JSON lines, SQLite or per-session folders, and none of them is meant to be read by anything but the tool itself.
BotSpy is a small Rust tool that reads those histories into one common schema: sessions, messages, tool calls, sub-agents and forks. It is open source under the MIT licence (DataParade-io/BotSpy) and on crates.io as botspy.
What it does, and what it never does
BotSpy only reads. It never writes to an agent's store, and it reads live SQLite databases through a private snapshot copy so it cannot interfere with a tool that is running. Those are properties of the code, not intentions; you can check both in the source.
The command line covers the basics:
botspy sourceslists the agent histories it found on this machine, andbotspy doctorsays why one it expected is missing.botspy sessionsandbotspy showlist and display sessions in the common schema.botspy scanimports everything into a local store, andbotspy searchandbotspy statsquery it.
The interesting part is what you can build once all of that history has one shape. The repository has examples for three: a timeline, a meter and a privacy audit.
The timeline


The figure is Saturday from 6:30 AM to 12:30 PM Eastern: 53 Claude Code sessions across five projects. The strip at the top counts agents working at once. Below it, each session has a lane, grouped by project. A bar is an agent working on a turn, from my message to its last reply. A dotted line is the agent waiting for me. The purple connectors mark where a session spawned a sub-agent, which then gets its own lane under its parent.
44 of the 53 sessions are sub-agents: I started nine sessions, and they handed the tickets out.
Two details are deliberate. Each agent platform gets a pattern as well as a colour, because I am colourblind and a chart that relies on hue alone does not work for me. And the timeline never shows session titles or message text. Titles are written by the agent from what you asked, so they can repeat a person's name, a customer or a project you would not put on a chart.
To draw your own, from a BotSpy checkout at v0.4.0:
cargo run --release --example timeline -- \
--since 2026-10-31T10:30:00Z --until 2026-10-31T16:30:00Z \
--source claude_code --no-ids --png timeline.png <data-root>
<data-root> is a folder of links to the agents' history folders, such as claude_code/projects pointing at ~/.claude/projects. Add --theme dark for the dark version. Without a data root, the example draws BotSpy's bundled synthetic sessions, so you can try it before pointing it at your own.
Every platform at once


This is a six-hour slice of 8 September, 10 AM to 4 PM Eastern, when Claude Code, Cursor and Antigravity were all running: 75 sessions in four projects, peaking at 9 agents at once. Each agent product has its own pattern. The Chattic.us project was open in both Cursor and Claude Code, and BotSpy groups it as one project because both agents were working in the same folder. The many short dashed bars are Cursor command-line sub-agents. Their transcripts carry no timestamps, so BotSpy places them by file time and marks them as approximate.
The same example draws every agent when you leave out --source:
cargo run --release --example timeline -- \
--since 2026-09-08T14:00:00Z --until 2026-09-08T20:00:00Z \
--no-ids --png timeline.png <data-root>
A meter for your own manners


The meter example counts how many of my own messages to agents say thanks and how many contain an F-bomb. It is a keyword classifier and nothing more. It counts; it does not show any message. Above is one of my better days, hour by hour.
The count gets more useful over time, because it works as a rough measure of how the day went. When I swear at an agent, something usually is not working.


Most days are quiet, and the bad ones are easy to see: 21 F-bombs on 6 September, 12 on 25 September, and 10 each on 1 and 18 October. The five worst days account for 61 of the 115 F-bombs on this chart. That gives me a list of dates to go back to and ask what went wrong.
Across all my history, 24 August to 1 November, I typed 69 thanks and 149 F-bombs: about two F-bombs for every thanks. The chart shows 56 and 115 of them, the ones with a timestamp, because Cursor's command-line agent does not record when a message was sent.
| Agent | Sessions | Thanks | F-bombs |
|---|---|---|---|
| Claude Code | 2,892 | 49 | 68 |
| Cursor (editor and command line) | 992 | 18 | 62 |
| Antigravity | 86 | 2 | 19 |
| Codex | 8 | 0 | 0 |
The first version of the meter said the opposite: two thanks for every F-bomb. Agents store text they add themselves under the user's name. In Claude Code that is skill instructions, summaries of earlier conversation and messages between sessions; of 17,796 "user" messages, I typed 5,711. Cursor's editor had simulated 1,723 of its 3,330. If you count anything in these histories, check who actually wrote the message first.
cargo run --release --example meter -- --by day --utc-offset -4 \
--since 2026-08-24T04:00:00Z --until 2026-11-02T03:59:59Z --png days.png <data-root>
cargo run --release --example meter -- --by hour --utc-offset -4 \
--since 2026-10-20T04:00:00Z --until 2026-10-21T03:59:59Z --png day.png <data-root>
Without --png it prints the counts as text.
The history is full of secrets
Here is the problem with all of this. Agent histories are where people paste things. API keys to get a call working, a .env file to debug configuration, a database URL, a customer's email from a support ticket. The agent reads them, and the history keeps them. A tool that makes that history easy to mine also makes those secrets easy to move into whatever you build on top: a dashboard, a dataset, a fine-tuning run.
So BotSpy's examples do not read raw text. They read text that has been through LeftBlock, our open source secret and personal-data scanner (on PyPI as leftblock). LeftBlock started as a pre-commit gate built on gitleaks rules, with an optional deep scan that uses OpenAI's Privacy Filter model. Version 0.9.0 added a streaming mode for exactly this: leftblock scan --jsonl reads {id, text} records on standard input and returns findings as byte ranges with a rule and a kind. It never returns the matched value. It exits 0 when clean, 1 when it found something and 2 on error.
BotSpy's examples/privacy sends every text-bearing part of every session through it: messages, the agent's thinking, tool results and the arguments of tool calls. audit reports what it found by rule and kind. redact replaces each range with [REDACTED:<rule>]. The meter runs only on redacted text, and transcript text is never written to disk. Those are enforced in the example's code, not left to whoever runs it.
How much does each layer catch? We measured it with LeftBlock 0.6.0 on Samsung's CredData dataset (commit fc9b64bed812), a labelled set of credentials in real repositories whose values have been replaced with same-shape fakes. A critical secret here is a line labelled as a cloud key, private key, provider token, password or authentication header. The model is too slow to read all 19 million lines, so every detector ran on the same windows: all 5,884 critical lines plus a stratified random sample of 1,095 others, five lines either side, about 4.9 MB of text. Rates are weighted back to the full set.
| Detector | Critical-secret recall (95% interval) | Precision |
|---|---|---|
| Gitleaks, stock rules | 29.8% (28.7–31.0) | |
| Gitleaks with LeftBlock's rules | 31.6% (30.4–32.8) | 81.1% |
| Privacy Filter model alone | 83.8% (82.9–84.7) | 95.5% |
| LeftBlock rules and the model together | 85.8% (84.9–86.7) | 90.3% |
The rules miss the secrets that have no fixed shape. They found 8.5% of the passwords and 15.6% of the authentication headers; the model found 78.1% and 91.5%. Private keys, which do have a shape, were at 94.1% with rules alone and 99.7% with both. Precision with LeftBlock's rules is lower partly by design: they also flag personal data such as email addresses, which a credentials-only label set counts as a false positive.
The first time we ran OpenAI's Privacy Filter over our own work, it found a real service password that our existing rules had missed. We replaced it with a generated secret and confirmed the old one no longer opens anything.
To repeat it, from a clone of LeftBlock:
python3 -m venv .venv-eval && . .venv-eval/bin/activate
pip install -e . -r eval/requirements.txt
python -m eval check
python -m eval reproduce
--skip-deep runs only the rules half. The model half took about 80 minutes on a laptop CPU. Versions are pinned in eval/pins.py, and the published results are in eval/results/0.6.0.md. LeftBlock has changed since 0.6.0 (0.8.1 added checksum validation for one kind of private key), and the evaluation has not been re-run on 0.9.0.
Neither layer catches everything, which is why the examples redact before they analyse rather than trusting anyone to remember not to look.
What to check in your own setup
If you build anything on agent histories, yours or your team's, hold it to one property: no transcript text leaves the machine, or reaches storage, without passing a secret scanner first. Then check the inputs you forgot about. Tool-call arguments and tool results are where the agent pastes the files it read, so they can hold secrets that no message mentions. We have not measured how often.
Limits
- Cursor's editor does not record which conversation started a sub-agent, so on Cursor sub-agents are not linked to their parents yet.
- Cursor's command-line transcripts have no timestamps, so those sessions are drawn as approximate spans from file times, with a dashed outline.
- The meter is a toy keyword classifier. It will miss sarcasm and count "thanks, but no".
- The recall figures are for one dataset and LeftBlock 0.6.0. Precision was measured on CredData's hard negatives, not on code in general. CredData's fake values also break the shape of many AWS key ids, which overstates how many of those the rules miss: on the 102 that kept a valid shape, stock gitleaks found all of them. Your secrets will not look like CredData's, and 85.8% means some get through. Redaction lowers the risk; it does not remove it.
- The model scan is optional, slow on a CPU, and never runs in LeftBlock's commit hook.
- The agent formats change without notice. BotSpy reads what each agent stores today, and I expect to fix a reader every few weeks.
Versions: BotSpy 0.4.0 for the figures, examples and counts, plus the meter's --by and --utc-offset options, which came after it and are on the develop branch; LeftBlock 0.9.0 for the redaction; LeftBlock 0.6.0 for the recall measurements.
Ready to make your diagrams meaningful?
Connect your repositories, extract verified dataflow diagrams automatically, and conduct continuous risk reviews.

