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
- Ngôn ngữ chính
- Shell
- Star
- 1.8k
- Fork
- 226
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Mô tả
## 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.
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Đánh giá
Issue này chưa được đánh giá.