Summary
The Claude Code SessionEnd capture only parses the single main-session transcript. Any generation that runs in a subagent — the Task tool, multi-agent workflows, and looped/iterative agent runs that spawn subagents — is silently dropped. For subagent-heavy sessions this omits the majority of token usage from PostHog.
Environment
PostHog/ai-plugin (default branch)
- Recent Claude Code (subagent transcripts stored in a separate directory — see below)
Root cause
Recent Claude Code stores transcripts in two places:
- Main session:
~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
- Subagents:
~/.claude/projects/<encoded-cwd>/<session-id>/subagents/agent-<id>.jsonl (one file per subagent, each with a .meta.json sidecar)
Subagent turns are not duplicated into the main session file (the main file contains no isSidechain: true entries). Each subagent file carries its own assistant entries with full usage (input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens).
find_session_log(session_id, cwd) in posthog_llma/parser.py resolves only <session-id>.jsonl directly, then falls back to a single-level glob */{session_id}.jsonl. Neither reaches the subagent files — they're one directory deeper and named agent-<id>.jsonl, not <session-id>.jsonl. parse_session then reads that one file only. hooks/session-end-llma.py reconstructs the path via find_session_log and does not use the transcript_path provided in the hook payload.
There are no references to subagents, agent-*.jsonl, isSidechain, sidechain, agentId, or the Task tool anywhere in the capture path.
Reproduction
- Run a Claude Code session that spawns one or more subagents (invoke the Task tool / a multi-agent workflow / a loop that delegates to subagents).
- Let the SessionEnd hook fire.
- Inspect
~/.claude/projects/<encoded-cwd>/<session-id>/subagents/ — the agent-*.jsonl files contain assistant usage entries.
- Compare against the
$ai_generation events that reached PostHog: none of the subagent generations appear; captured token totals match only the main <session-id>.jsonl.
Expected vs. actual
- Expected: token usage for all generations in the session, including subagents, is captured.
- Actual: only main-session generations are captured; all subagent usage is omitted.
Impact
For sessions using multi-agent workflows or Task-tool delegation — where most of the work runs in subagents — the reported spend can understate actual token usage by a large margin.
Suggested fix
After locating the main session file, also enumerate <encoded-cwd>/<session-id>/subagents/agent-*.jsonl, parse each, and merge their generations/spans into the result. Subagent entries already carry usage, the parent sessionId, and promptId; the .meta.json sidecar provides an agent slug/id that could tag events (e.g. an is_subagent / agent-name property) so main vs. subagent spend can be split in PostHog.
Summary
The Claude Code SessionEnd capture only parses the single main-session transcript. Any generation that runs in a subagent — the Task tool, multi-agent workflows, and looped/iterative agent runs that spawn subagents — is silently dropped. For subagent-heavy sessions this omits the majority of token usage from PostHog.
Environment
PostHog/ai-plugin(default branch)Root cause
Recent Claude Code stores transcripts in two places:
~/.claude/projects/<encoded-cwd>/<session-id>.jsonl~/.claude/projects/<encoded-cwd>/<session-id>/subagents/agent-<id>.jsonl(one file per subagent, each with a.meta.jsonsidecar)Subagent turns are not duplicated into the main session file (the main file contains no
isSidechain: trueentries). Each subagent file carries its ownassistantentries with fullusage(input_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens).find_session_log(session_id, cwd)inposthog_llma/parser.pyresolves only<session-id>.jsonldirectly, then falls back to a single-level glob*/{session_id}.jsonl. Neither reaches the subagent files — they're one directory deeper and namedagent-<id>.jsonl, not<session-id>.jsonl.parse_sessionthen reads that one file only.hooks/session-end-llma.pyreconstructs the path viafind_session_logand does not use thetranscript_pathprovided in the hook payload.There are no references to
subagents,agent-*.jsonl,isSidechain,sidechain,agentId, or the Task tool anywhere in the capture path.Reproduction
~/.claude/projects/<encoded-cwd>/<session-id>/subagents/— theagent-*.jsonlfiles contain assistantusageentries.$ai_generationevents that reached PostHog: none of the subagent generations appear; captured token totals match only the main<session-id>.jsonl.Expected vs. actual
Impact
For sessions using multi-agent workflows or Task-tool delegation — where most of the work runs in subagents — the reported spend can understate actual token usage by a large margin.
Suggested fix
After locating the main session file, also enumerate
<encoded-cwd>/<session-id>/subagents/agent-*.jsonl, parse each, and merge their generations/spans into the result. Subagent entries already carryusage, the parentsessionId, andpromptId; the.meta.jsonsidecar provides an agent slug/id that could tag events (e.g. anis_subagent/ agent-name property) so main vs. subagent spend can be split in PostHog.