Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ Zero dependencies beyond `rich`. No API keys, no network: it reads local files.
**Timestamps without an offset are treated as UTC.** Explicit offsets are respected when sorting
runs and calculating durations. Runs with missing or invalid timestamps sort before dated runs.

**Two passes over the transcript, not one.** Results can appear before every use has been seen in
unusual orderings. A 34MB file is cheap to scan twice compared to getting the pairing subtly wrong.
**One pass over the transcript, with two maps.** `tool_use` and `tool_result` blocks are collected
into separate maps and joined by `tool_use_id` afterwards. This keeps pairing correct when records
appear in unusual orderings.

**A torn final line is skipped, not fatal.** A live session being appended to yields half-written
JSON. Refusing to parse would mean you cannot analyse a run until it is over, which is exactly
Expand Down
4 changes: 2 additions & 2 deletions agentrace/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ def _records(path: Path) -> Iterator[dict]:
def parse_session(path: Path) -> Session:
"""Pull every Agent delegation out of one transcript.

Two passes over the file rather than one: results can appear before we have seen every use in
weird orderings, and a 34MB file is cheap to scan twice compared to getting this subtly wrong.
One pass collects tool_use and tool_result blocks into separate maps, then joins them by
tool_use_id. Buffering both sides keeps pairing correct when records arrive in a weird order.
"""
uses: dict[str, dict] = {}
results: dict[str, dict] = {}
Expand Down
Loading