DoubleAgent analyzer¶
doubleagent is a forensic analyzer of AI agent use on a system. It takes
an archive from the doubleagent collectors, an extracted collection, or any
loose directory tree such as a copied home directory or a mounted disk image,
detects which AI agents left state in it, and parses the transcripts it
knows how to read into one normalised JSONL timeline. Detection uses the
catalog, the collectors' table of agents and the paths under a home
directory where each keeps its state, bundled with the analyzer. It runs
on the analyst's workstation, never on the host under investigation, so
unlike the collectors it may carry dependencies.
It needs Python 3.10 or later. pip install . in this directory installs
the doubleagent package and the doubleagent command with their two
required dependencies: python-dateutil, which reads the --since and
--until times, and zstandard, which decompresses Zed threads, Codex and
Open Interpreter .jsonl.zst rollouts and OpenClaw's compressed transcript
rows. To run from a checkout without installing, pip install -r
requirements.txt and run python3 -m doubleagent from this directory.
--version prints doubleagent <version>. CHANGELOG.md lists what changed in
each version, including changes to the timeline.jsonl and sessions.jsonl
fields.
Quick start¶
# What is in this collection?
doubleagent detect /cases/host01/host01_20261003T165531Z_agent-artifacts.tar.gz
# Build the timeline
doubleagent timeline /cases/host01/host01_*.tar.gz -o /cases/host01/analysis
# Every Bash command Claude Code ran, from the timeline
jq -r 'select(.agent == "claude-code" and .tool_name == "Bash") | .text' /cases/host01/analysis/timeline.jsonl
# Only 1 October 2026 (UTC), or only the last three days
doubleagent timeline /cases/host01/host01_*.tar.gz -o /cases/host01/oct1 --since 2026-10-01 --until 2026-10-01
doubleagent timeline /cases/host01/host01_*.tar.gz -o /cases/host01/recent --since 3d
# Only events whose text mentions curl or wget, with the paired tool calls and results
doubleagent timeline /cases/host01/host01_*.tar.gz -o /cases/host01/net --match 'curl|wget' -i
# A home directory copied off a host by other means, or a mounted image
doubleagent timeline /mnt/evidence -o /cases/host02/analysis --host host02
# A single agent directory copied on its own, under its original name
doubleagent detect /cases/host03/alice/.claude --user alice
# Fleet inventory: one saved collector --inventory stdout per host in a directory
doubleagent inventory /cases/fleet/inventory -o /cases/fleet/fleet-inventory.csv
Commands¶
| Command | What it does | Writes | Page |
|---|---|---|---|
detect |
Reports the host, users, homes and agents found in an input, with file counts, bytes and whether a parser exists, in one table for homes and, for a collection, one for project directories. | stdout only; --json for one object |
docs/output.md |
timeline |
Parses every transcript it can read into one timeline, optionally filtered by time or text. | timeline.jsonl, sessions.jsonl and inventory.json in -o |
docs/output.md, docs/filtering.md |
inventory |
Merges saved collector --inventory output from many hosts into one CSV. |
fleet-inventory.csv, or the -o path |
docs/inventory.md |
catalog |
Prints the bundled catalog, or with --agents each agent with parser or detect only. |
stdout only | docs/inputs.md |
Options¶
doubleagent detect INPUT [--json] [--files] [common options]
doubleagent timeline INPUT -o DIR [--include-thinking] [--agent NAME]...
[--since WHEN] [--until WHEN] [--keep-undated] [--match REGEX]... [-i]
[common options]
doubleagent inventory [-o FILE] INPUT...
doubleagent catalog [--agents]
doubleagent --version
| Option | Meaning |
|---|---|
--json |
detect: print one machine-readable object. |
--files |
detect: also list every attributed file. |
-o, --output |
timeline: output directory, required. inventory: CSV path. |
--include-thinking |
timeline: emit thinking and reasoning blocks as thinking rows. |
--agent NAME |
timeline: run only this agent's parsers. Repeatable. |
--since WHEN, --until WHEN |
timeline: keep rows inside this window. |
--keep-undated |
timeline: with a window, also keep rows without a timestamp. |
--match REGEX |
timeline: keep rows whose text matches, with the paired call or result. Repeatable. |
-i, --ignore-case |
timeline: make every --match case-insensitive. |
--agents |
catalog: list agents and whether each has a parser. |
--host NAME |
Host name to record; overrides collection.json. |
--user NAME |
User to record for every home whose user is empty. |
--work-dir DIR |
Where to extract an archive. |
--keep-extracted |
Keep the extracted archive. |
docs/options.md has the full meaning of each option, which subcommands take it, and the full exit-code table.
| Exit code | Meaning |
|---|---|
0 |
Rows or agents were found, or the command always succeeds (detect --json, catalog, --version). |
1 |
Nothing was found: no timeline rows, no agent artifacts, or no inventory lines. |
2 |
The input cannot be opened or extracted, an option value is invalid, or the command line is invalid. |
Parsers¶
The agents page lists every agent in the catalog
with its status (parsed, a parser backlog, or collected with nothing to
parse), the files each parser reads and the evidence it was validated
against. Thinking and reasoning blocks are left out unless
--include-thinking is passed.
Documentation¶
| Page | Covers |
|---|---|
| docs/inputs.md | Accepted inputs, loose mode, unreadable inputs and problem: lines, detection from the bundled catalog, routing files to parsers |
| docs/output.md | detect output and --json keys, timeline files and stdout, the inventory.json, timeline.jsonl and sessions.jsonl schemas |
| docs/filtering.md | --since, --until, --keep-undated and --match, the WHEN forms and periods |
| docs/searching.md | Reading and searching large output with jq, grep, DuckDB, Python and PowerShell |
| docs/inventory.md | The inventory command, its CSV columns and rollup |
| docs/options.md | Every option with its subcommands, and the exit codes |
| docs/development.md | Running the tests and lint checks, reading a real install, protobuf and SQLite stores, adding a parser |
The evidence behind every parser, with citations and the level each claim rests on, is one document per agent, listed in the research index.
License¶
Copyright 2026 Sean Whalen. Licensed under the Apache License, Version 2.0. See LICENSE.