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
inputandoutputtoken counts - Accumulates
cacheReadandcacheWritestatistics - Updates
totalTokensandcost.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:
session-manager/file-operations.test.ts— ConfirmsChildUsageAttributionEntrypersistence with correctchildUsageandaggregateUsagevaluesagent-session-recursion.test.ts— Integration test validating that parent usage reflects child usage after recursive agent executioncontext-tree.test.ts— Ensures the UI's context-tree properly folds child entries into parent node displays
These tests cover the complete lifecycle: generation, persistence, in-memory aggregation, and UI rendering.
Summary
- Attribution entries (
ChildUsageAttributionEntry) link child usage to parent messages viatargetIdwhile 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.tsexposes 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →