SessionEnd capture omits subagent token usage (Task tool, multi-agent workflows, looped runs) — only the main-session transcript is parsed
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 84
- Forks
- 15
- Avg merge
- 14h 34m
- Merged PRs (30d)
- 7
Description
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.jsonsidecar)
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/— theagent-*.jsonlfiles contain assistantusageentries. - Compare against the
$ai_generationevents 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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in posthog_llma/parser.py with find_session_log and parse_session, then inspect hooks/session-end-llama.py to see how the hook reconstructs the transcript path. Reproduce a SessionEnd capture with subagents and inspect the agent-*.jsonl files and their usage entries. Done means PostHog receives token usage from the main session and all subagent transcripts.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, observability-sre
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100