How Child Agent Usage Statistics Are Attributed to Parent Sessions in Prime Agent

Child agent usage statistics are attributed to parent sessions through a ChildUsageAttributionEntry record that merges token counts, cost, and cache metrics into the parent turn's usage summary while preserving individual child entries for audit purposes.

Prime Agent's hierarchical agent architecture allows parent sessions to spawn child (sub-agent) runs for delegated tasks. When these child runs complete, their resource consumption—tokens, API costs, and cache operations—must roll up into the parent's usage totals. The PrimeIntellect-ai/prime-agent repository implements this through a formal attribution mechanism that balances aggregation with traceability.

The Attribution Mechanism

The flow begins when a child run finishes and produces a Usage object from its final assistant message, defined in packages/ai/src/types.ts. Rather than immediately mutating the parent session, Prime Agent creates a persisted attribution entry that enables both aggregation and debugging.

ChildUsageAttributionEntry Structure

The core data structure is defined in packages/coding-agent/src/core/session-manager.ts (lines 62-68):

Field Description
type Fixed value "child_usage_attributed"
targetId ID of the parent assistant message that spawned the child
childUsage Raw Usage object from the child run
aggregateUsage Running total of all child usage for this parent turn
origin Attribution trigger: "spawn_task", "agent_message", or "direct_user"

Merging Usage into Parent Sessions

The actual merge operation occurs through addAssistantUsage(parentUsage, childUsage) in packages/coding-agent/src/core/usage.ts (lines 34-43). This function:

  • Adds input and output token counts
  • Accumulates cacheRead and cacheWrite statistics
  • Updates totalTokens and cost.total

The parent session's in-memory Usage object is updated immediately, while the ChildUsageAttributionEntry is appended to the session log for persistence and UI inspection.

Code Implementation

Creating and Persisting Attribution Entries

import { addAssistantUsage, emptyUsage } from "./usage.js";
import type { Usage } from "@earendil-works/pi-ai";

// Child usage returned from sub-agent completion
const childUsage: Usage = {
  input: 1200,
  output: 400,
  cacheRead: 0,
  cacheWrite: 0,
  totalTokens: 1600,
  cost: { total: 0.02 },
};

// Initialize or retrieve parent message usage
const parentUsage: Usage = emptyUsage();

// Merge child metrics into parent totals
addAssistantUsage(parentUsage, childUsage);

// Create persisted audit record
const attribution: ChildUsageAttributionEntry = {
  type: "child_usage_attributed",
  id: crypto.randomUUID(),
  parentId: parentMessageId,
  timestamp: new Date().toISOString(),
  targetId: parentMessageId,  // Links to spawning message
  childUsage,                 // Original child metrics
  aggregateUsage: parentUsage, // Updated running total
  origin: "agent_message",     // How child was invoked
};

// Append to session log
sessionManager.appendEntry(attribution);

Reading Aggregated Usage from Sessions

import { Session } from "@prime-agent/coding-agent";

const session = await Session.load(sessionFile);
const parentMsg = session.getMessageById(parentMessageId);

// Usage includes all child contributions
const totalUsage = parentMsg.message.usage;

console.log(`Total tokens: ${totalUsage.totalTokens}`);
console.log(`Total cost: $${totalUsage.cost.total.toFixed(4)}`);

UI Presentation in the Tree Selector

The interactive TUI component at packages/coding-agent/src/modes/interactive/components/tree-selector.ts surfaces child usage attribution at three locations (approximately lines 308, 558, and 784). When rendering nodes, it checks for entries with type === "child_usage_attributed":

// Tree-selector.ts excerpt
if (entry.type === "child_usage_attributed") {
  const { childUsage, aggregateUsage } = entry;
  
  return (
    <NodeBadge>
      Child: {childUsage.totalTokens} tokens
      <Tooltip>
        Aggregate: {aggregateUsage.totalTokens} tokens
        Origin: {entry.origin}
      </Tooltip>
    </NodeBadge>
  );
}

This allows operators to see both the immediate child contribution and the cumulative impact of all children spawned from a parent turn.

Test Coverage

Three test suites verify the attribution pipeline:

These tests cover the complete lifecycle: generation, persistence, in-memory aggregation, and UI rendering.

Summary

  • Attribution entries (ChildUsageAttributionEntry) link child usage to parent messages via targetId while preserving original metrics
  • Usage merging through addAssistantUsage() aggregates tokens, cost, and cache statistics into parent totals
  • Dual persistence maintains both running aggregates (for summaries) and individual entries (for debugging)
  • UI integration in tree-selector.ts exposes child breakdowns without cluttering the main session view

Frequently Asked Questions

How does Prime Agent distinguish between direct LLM calls and child agent usage?

Direct LLM calls update the parent message's usage field immediately. Child agent usage follows a two-step process: the child's Usage object is stored in a ChildUsageAttributionEntry, then merged via addAssistantUsage(). The origin field tracks whether the child was spawned via spawn_task, agent_message, or direct_user invocation.

Can I inspect individual child usage after it's been aggregated?

Yes. The ChildUsageAttributionEntry preserved in the session log contains the original childUsage object unchanged. Load the session file and filter entries by type: "child_usage_attributed" to see per-child breakdowns regardless of aggregation state.

What performance overhead does usage attribution add?

Minimal. addAssistantUsage() performs simple numeric addition on six fields. The attribution entry itself is a small JSON object appended to the session log. According to the source at usage.ts:34-43, no complex calculations or external I/O occur during the merge operation.

Does aggregation handle nested children (grand-child agents)?

Yes. When a child spawns its own children, their usage rolls into the child first, then the child complete's Usage (now including its descendants) rolls into the grandparent. The aggregateUsage field at each level captures the full subtree, creating a complete hierarchical accounting.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →