pub fn parse_jsonl_entries(path: &Path) -> Result<ParsedJsonl>Expand description
Parse a single JSONL file into one UsageEntry per unique message.id.
Pure function: depends only on the file contents. Non-assistant entries, malformed lines, and missing IDs are skipped silently.
Dedup is split by field semantics, because Claude Code emits one JSONL
row per assistant content block (thinking / text / each tool_use) all
sharing the same message.id:
- Billing fields (token counts, model, timestamp, session_id) come from the first row seen for each id — they repeat identically across rows.
- Content fields (
tool_uses,text_chars, and the bash commands fed to the activity classifier) are unioned across every row with that id, thenactivityis classified once at the end using the merged content.
Folding only by message.id (as this parser did before 2026-05-13) drops
every tool_use after the first row, which made the Toolkit and Usage tool
counters undercount by an order of magnitude. The chat parser already
uses (message_id + content) for the same reason — see CLAUDE.md.
Runs a rolling last_user_text cursor during the stream: each genuine
type: "user" row updates it; the snapshot taken at the first row of
each assistant message is used for activity classification.