Repository navigation
Fix: Recognise Claude Code's second product token - #1369
Conversation
Closes #1363. claude-code was a VALUE in knownClients but never a KEY. ParseUserAgent looks the first product token up against the map's keys, so the Agent-SDK-shaped User-Agent — "claude-code/2.1.233 (claude-vscode, agent-sdk/0.3.233)" — missed and came back unrecognised. Measured on one laptop proxy's session store, that is 70 of 551 Claude Code events, ~13%: they pooled into agentop's catch-all Other row with their spend, and IsKnownAgent was false for them 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 was never affected — AffinityName returns claude-code for every shape, because its comment scan compares against canonical names rather than this map's keys. This is a labeling and aggregation fix, not a session-grouping one. The same class of bug as #1337 fixed for Codex: one agent sending more than one product token, with the lookup table covering only some of them. Tests pin all three live User-Agents, including the negative: "Claude-User (claude-code/…)" must STAY unrecognised, since naming a row out of a comment would move those rows to a different agent in the ledger and the AGENTS pane than the history beside them. Verified that the new test fails without the map entry and that the Claude-User case passes either way, so it is not coupled to the fix. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Mariusz Sabath <mrsabath@gmail.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
huang195
left a comment
There was a problem hiding this comment.
LGTM. Adding claude-code as a key also feeds trailingKnownClient, and I checked that doesn't widen anything: Claude-User (claude-code/…; splits to (claude-code and misses, which the new negative test pins. AffinityName is unaffected since it compares against canonical names.
Two comment nits, outside the diff so not inline, neither blocking:
core/pipeline/client.go:597-598—AffinityName's doc says "the key is the product tokenclaude-cli". Withclaude-codenow also a key, that is half true; maybe "…whileclaude-cli's key is the product token, not the name".core/pipeline/client.go:259-261— theknownClientsgodoc still presentsclaude-clias Claude Code's only product token; could mention it sends both.
Closes #1363.
The bug
claude-codewas a value inknownClientsbut never a key.ParseUserAgentlooks the first product token up against the map's keys, so Claude Code's Agent-SDK-shaped User-Agent missed and came back unrecognised:NamebeforeNameafterclaude-cli/2.1.233 (external, claude-vscode, agent-sdk/0.3.233)claude-code✅claude-codeclaude-code/2.1.233 (claude-vscode, agent-sdk/0.3.233)""❌claude-code✅Claude-User (claude-code/2.1.233; +https://support.anthropic.com/)""""— deliberateWhy it matters
Those 70 events are ~13% of this capture's Claude Code traffic. Unrecognised, their
Label()is the whole raw User-Agent, so they became a series key of their own — pooling into agentop's catch-allOtherrow with their spend, whileIsKnownAgentwas false for them 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 was never affected.
AffinityNamereturnsclaude-codefor all three shapes, because its comment scan compares against canonical names rather than this map's keys. This is a labeling/aggregation fix, not a session-grouping one.The third shape stays unrecognised
Claude-User (claude-code/…)is Claude Code's WebFetch, and leaving it unclaimed is deliberate —AffinityName's doc comment has the reasoning: naming a row out of a comment would move those rows to a different agent in the ledger and the AGENTS pane than the history beside them.It survives the fix incidentally rather than by rule: its first token is
Claude-User, andtrailingKnownClientcuts on/without trimming punctuation, so the later token reads as(claude-codeand misses. That is a property of the parser's shape rather than something it states, which is exactly why there is now a test pinning it.Same class of bug as #1337
That PR fixed this for Codex — one agent sending more than one product token, with the lookup table covering only some of them.
bob/bob-shell/opencodeare worth the same look; OpenCode reportedly uses one UA for every mode, but that is from a source comment rather than a capture.Verification
Name="" IsKnown=falsetoName="claude-code" Version="2.1.233".Claude-Usernegative passes either way — so it is not accidentally coupled to the fix.core/pipelinesuite green;core/cost/...,core/session/...,core/sessionapigreen.go vetandgofmtclean.cmd/agentop/tuihas 3 pre-existing failures on this base commit (router / server-picker, unrelated to User-Agent parsing); verified identical on an untouched checkout of the same commit.Unknown
Whether headless Claude Code (
claude -p) is one of these two shapes or a fourth. Both recognised shapes nameclaude-vscodeandagent-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
Summary by CodeRabbit
claude-codetoken are now recognized, alongside the existingclaude-cliform.Claude-UserUser-Agents remain unrecognized.