anthropics / anthropics/claude-agent-sdk-typescript

result.usage does not aggregate token usage from nested sub-agents — stays flat while total_cost_usd grows with depth

Aperta
#347 0 commenti 1 reazione 0 assegnatari Vedi su GitHub
bug
Lingua principale
Shell
Stelle
1.8k
Fork
226
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

## Summary

When sub-agents spawn their own sub-agents (nested via the `Task` tool, working as of 0.3.172), the `usage` on the final `result` message reports only the **top-level agent's own usage** — tokens consumed by the nested sub-agent tree are not included. Meanwhile `total_cost_usd` **does** grow with depth, so the two fields disagree about what they're measuring.

If top-agent-only usage is the intended behavior, it would be great to have (a) that documented on the `result` message, and (b) some supported way to get per-sub-agent or aggregated tree usage — right now token-based accounting silently under-reports as soon as any nesting happens, and `total_cost_usd` is the only signal that reflects the whole tree.

## Environment

- `@anthropic-ai/claude-agent-sdk` **0.3.172**
- Node 22.x, macOS (darwin 24.6)
- Auth: Claude subscription OAuth
- Model: `claude-opus-4-8[1m]`
- `permissionMode: 'bypassPermissions'`

## Repro

Build a chain of N custom agents, each granted `Task` and instructed to spawn the next; the terminal agent returns a token that round-trips to the top. Vary N and compare `result.usage` vs `total_cost_usd`.

```js
import { query } from '@anthropic-ai/claude-agent-sdk';

const DEPTH = Number(process.argv[2] ?? 6);

const agents = {};
for (let i = 1; i <= DEPTH; i++) {
const name = `l${i}`;
agents[name] =
i === DEPTH
? { description: `Terminal level ${i}`, tools: [], prompt: `Reply with exactly: REACHED@L${i}` }
: {
description: `Level ${i} relay`,
tools: ['Task'],
prompt: `Use the Task tool to launch the 'l${i + 1}' subagent with the prompt 'go'. Reply with exactly what it returns and nothing else.`,
};
}

let result, taskStarts = 0;
for await (const m of query({
prompt: "Use the Task tool to launch the 'l1' subagent with the prompt 'go'. Reply with exactly what it returns.",
options: { model: 'claude-opus-4-8[1m]', maxTurns: 40, permissionMode: 'bypassPermissions', allowedTools: ['Task'], agents },
})) {
if (m.type === 'system' && /task_started/.test(m.subtype || '')) taskStarts++;
if (m.type === 'result') result = m;
}

const u = result.usage;
const total = (u.input_tokens ?? 0) + (u.output_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0);
console.log({ depth: DEPTH, taskStarts, resultText: result.result, totalUsageTokens: total, cost_usd: result.total_cost_usd });
```

## Observed

All depths round-trip correctly (`REACHED@L` comes back, with N distinct sub-agent task starts), but the summed `result.usage` buckets stay flat while `total_cost_usd` climbs:

| depth | result | sub-agent task starts | duration | summed `result.usage` tokens | `total_cost_usd` |
|---|---|---|---|---|---|
| 6 | `REACHED@L6` (2 runs) | 6 | 26–47s | ~58,110 | $0.76 cold / $0.17 warm |
| 12 | `REACHED@L12` | 12 | 49s | ~58,011 | $1.20 |
| 20 | `REACHED@L20` | 20 | 105s | ~58,300 | $1.78 |

20 nested full-context calls cannot plausibly total ~58K tokens — that figure tracks the top agent alone, and is essentially constant regardless of how much work happened underneath it.

## Expected

One of:

1. `result.usage` aggregates usage across the full sub-agent tree (matching what `total_cost_usd` appears to do), or
2. The top-agent-only scope is documented on the `result` message type, and per-sub-agent usage is exposed somewhere consumable (e.g. on sub-agent completion messages), so callers can do the aggregation themselves.

## Why it matters

Anyone metering by tokens (budgets, quotas, per-run accounting, alerting) under-counts as soon as a sub-agent spawns a sub-agent — and the divergence is silent: the run succeeds, the result looks complete, and the usage number is simply wrong for the tree. With nested sub-agents now working in 0.3.172 this seems worth pinning down before more folks build accounting on top of `result.usage`.

Happy to provide full probe output or run variations if useful.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.