Skip to content

SessionEnd capture omits subagent token usage (Task tool, multi-agent workflows, looped runs) — only the main-session transcript is parsed #166

Description

@madrussa

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

  1. 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).
  2. Let the SessionEnd hook fire.
  3. Inspect ~/.claude/projects/<encoded-cwd>/<session-id>/subagents/ — the agent-*.jsonl files contain assistant usage entries.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions