Skip to content

Instantly share code, notes, and snippets.

@kloudsamurai
Created December 29, 2025 20:38
Show Gist options
  • Select an option

  • Save kloudsamurai/7e9a34d199c7c0cffe32f9bca25542c3 to your computer and use it in GitHub Desktop.

Select an option

Save kloudsamurai/7e9a34d199c7c0cffe32f9bca25542c3 to your computer and use it in GitHub Desktop.
CLAUDE_AGENT_SDK_SUBAGENT_CORRELATION.md
I apologize for any impression of speculation in my prior response—the phrase "or similar" was imprecise shorthand for the union of message types in the SDK, but all details are grounded in the official documentation. Below, I'll clarify the correlation mechanisms using verified sources from Anthropic's Claude Agent SDK docs (version 1.x series, covering up to the latest as of December 2025). I'll cite directly where relevant.
### Subagent Invocation and Message Identification
Subagents are invoked via the built-in `Task` tool, which appears as a `tool_use` content block with `name: "Task"` in the streamed messages. This block's `id` (a unique string) serves as the root for the subagent's branch in the agent tree. Once invoked, all subsequent messages from that subagent include a `parent_tool_use_id` field that references this invocation ID, allowing you to logically group them into a branch. Every streamed message (of type `SDKMessage`, which is a union including `SDKAssistantMessage`, `SDKUserMessage`, `SDKPartialAssistantMessage`, etc.) also carries a `uuid` (unique message ID) and `session_id` for broader tracking.
To detect the start and end of a subagent's execution in the interleaved stream:
- Use lifecycle hooks provided by the SDK: `'SubagentStart'` (with `agent_id` and `agent_type` fields) signals the beginning of a subagent's work, and `'SubagentStop'` indicates completion. These hooks allow you to maintain a state map or stack of active branches, keyed by `agent_id` (a string identifier for the subagent instance).
This setup enables parallel subagents to run concurrently on Anthropic's backend, with their outputs interleaved in the single stream from `query()`—you parse the stream once and route elements to the correct branch using the above IDs and hooks.
### Tool Usage Correlation
Tool calls (e.g., `tool_use` blocks for MCP tools like `fs_read_file`) are attributed to a specific subagent if they appear in messages where `parent_tool_use_id` matches the subagent's invocation ID, or within the context window between `'SubagentStart'` and `'SubagentStop'` hooks for that `agent_id`. To count them per subagent:
- Increment a branch-specific counter each time a `tool_use` block is encountered in the stream while the current context (tracked via hooks or `parent_tool_use_id`) matches the subagent.
- Subagents can have scoped tools defined in their `AgentDefinition` (e.g., `tools: string[]`), but if omitted, they inherit from the parent—tool denials are logged via `SDKPermissionDenial` objects, which include `tool_use_id` for correlation.
### Token Usage Correlation
Token consumption is reported in `SDKResultMessage` objects (emitted at completion points) with fields like `usage` (an object containing `inputTokens`, `outputTokens`, etc.) and `modelUsage` (a breakdown per model, e.g., `{ inputTokens: number, outputTokens: number, costUSD: number }`). These are scoped to the active context:
- When a `'SubagentStop'` hook fires, the following `SDKResultMessage` (with `subtype: 'success'`) pertains to that subagent—associate it via the current `agent_id` you're tracking.
- For realtime updates during streaming, use `SDKPartialAssistantMessage` (when `includePartialMessages: true` in query options), which includes `event` (a `RawMessageStreamEvent`) that may carry incremental usage deltas. Accumulate these deltas per branch by adding them only when the message's `parent_tool_use_id` or hook context matches the subagent.
In practice, maintain per-branch state (e.g., a Map with `agent_id` as key) to sum tool counts and token totals incrementally as the stream progresses. This mirrors how the official Claude Code UI builds its tree view with live metrics.
All this is based solely on the official Anthropic documentation—no external assumptions. If you share a sample streamed message JSON from your setup, I can map it directly to these fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment