Repository navigation
Bug: Claude Code's second User-Agent is unrecognised, so ~13% of its events label as Other #1363
Description
What happens
Claude Code sends three distinct User-Agents on live traffic, and knownClients recognises only one. Measured from one laptop proxy's session store:
| User-Agent | Events | ParseUserAgent().Name |
|---|---|---|
claude-cli/2.1.233 (external, claude-vscode, agent-sdk/0.3.233) |
469 | claude-code ✅ |
claude-code/2.1.233 (claude-vscode, agent-sdk/0.3.233) |
70 | "" ❌ |
Claude-User (claude-code/2.1.233; +https://support.anthropic.com/) |
12 | "" — deliberate, see below |
The middle row is the bug: claude-code is a value in knownClients but never a key. ParseUserAgent looks the first product token up against the map's keys, and that token is claude-code, so the lookup misses and the traffic is unrecognised.
Why it matters
Those 70 events — ~13% of this capture's Claude Code traffic — pool into agentop's catch-all Other row in the AGENTS pane instead of claude-code, and their spend goes with them. pipeline.IsKnownAgent is false for them, so they are also excluded wherever that gates (core/cost/usage). Claude Code is the reference integration, so this is the one agent whose attribution was assumed correct.
Session attribution is unaffected — AffinityName() returns claude-code for all three, because its comment-scan fallback compares against canonical names and claude-code is one. So this is a labeling/aggregation bug, not a session-grouping one.
The third row is not a bug
Claude-User (claude-code/…) staying unrecognised is intentional, and AffinityName's doc comment explains why: reading an agent's name out of a comment into Name would move those rows to a different agent in the ledger and the AGENTS pane than the history beside them. Leave it as is.
Suggested fix
One entry:
"claude-cli": "claude-code",
"claude-code": "claude-code", // the Agent-SDK-shaped UAI probed this locally and verified three things:
claude-code/2.1.233 (…)labels asclaude-code/ version2.1.233.Claude-User (claude-code/…)still does NOT label — its first token isClaude-User, andtrailingKnownClientcuts on/without trimming punctuation, so the later token reads as(claude-codeand misses. The deliberate behaviour survives.- The existing
core/pipelinesuite stays green.
Worth adding a TestParseUserAgent_* row for each of the three live UAs above, including the negative for Claude-User, so the deliberate case is pinned alongside the fixed one.
How this was found
While verifying Codex's agent recognition for #1337, which fixed the same class of bug for Codex: an agent that sends more than one product token, with the lookup table covering only some of them. Codex needed codex-tui and codex_cli_rs added beside codex_exec for exactly this reason. Worth a look at whether bob/bob-shell/opencode have unlisted second tokens too — OpenCode reportedly uses one UA for every mode, but that is from a source comment rather than a capture.
Not fixed in #1337 deliberately: that PR is Codex support, and this is a different agent.
Unknown
Whether headless Claude Code (claude -p) is one of these two shapes or a fourth. Both recognised shapes mention claude-vscode and agent-sdk, so they look like different components (CLI binary vs Agent SDK) rather than interactive-vs-headless, but I have no headless capture to confirm it.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
fix: Recognise Claude Code's second product token