Repository navigation
fix(analytics): detect AI agents from the variables that they set - #859
Merged
Merged
Conversation
Most environment variables in known_agents.json were not real. No agent sets CLAUDE_CODE, CURSOR_SESSION_ID, CODEX_SESSION, DEVIN_SESSION, GITHUB_COPILOT, WINDSURF_SESSION, or CLINE_TASK_ID. Users set AIDER_MODEL themselves. Thus ldcli reported almost all agent runs as unknown-non-interactive. This change replaces the list with variables from the source code or documentation of each agent. It also reads AI_AGENT, a variable that many agents set. The value starts with the agent name, for example claude-code_2-1-295_agent. An unknown name becomes ai-agent:<name>. An entry can now match a value with equals or contains. Amazon Q, Aider, and the Cursor terminal role need this. The tests use the variables that each agent sets, and they make sure that the old names do not match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Execute called os.Exit(1) before it sent CLI Command Completed. os.Exit stops the process immediately, so ldcli did not send an error outcome after version 1.16.2. Execute now sends the event, waits for it, and then exits with code 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ld-ahartmann
approved these changes
Oct 9, 2026
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Reviewed by Cursor Bugbot for commit a22f976. Configure here.
| {"env_var": "CLINE_ACTIVE", "label": "cline"}, | ||
| {"env_var": "ROO_ACTIVE", "label": "roo-code"}, | ||
| {"env_var": "ROO_CLI_RUNTIME", "label": "roo-code"}, | ||
| {"env_var": "AMP_CURRENT_THREAD_ID", "label": "amp"}, |
There was a problem hiding this comment.
Amp runs mislabeled as Claude Code
Medium Severity
Amp sets CLAUDECODE=1 along with AMP_CURRENT_THREAD_ID. The agents list matches CLAUDECODE first, so when AI_AGENT is absent the fallback path labels those runs claude-code instead of amp. Other detectors check Amp’s variable before CLAUDECODE for this reason.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a22f976. Configure here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Requirements
I ran the tests and the binary on Linux arm64 only.
Related issues
This PR fixes the agent detection that REL-12753 (#659) added.
Describe the solution you've provided
The
agent_contextevent property tells us which AI coding agent ran ldcli. In the last 30 days, Mixpanel showed almost no named agents. Only one account sentclaude-code, and only one user sentaider. All other agent runs went tounknown-non-interactive.The cause is the list of environment variables in
cmd/analytics/known_agents.json. Each name was compared to the source code, documentation, and live environment of the agent.CLAUDE_CODE,CLAUDE_CODE_SESSIONCLAUDECODE=1,AI_AGENTCURSOR_SESSION_IDCURSOR_AGENT=1CURSOR_TRACE_IDCODEX_SESSION,CODEX_SANDBOX_IDCODEX_THREAD_ID,CODEX_SESSION_ID,CODEX_CIDEVIN_SESSIONAI_AGENT=devin_<version>_agentGITHUB_COPILOTAI_AGENT,COPILOT_AGENT,COPILOT_CLIWINDSURF_SESSIONWINDSURF_CASCADE_TERMINALCLINE_TASK_IDCLINE_ACTIVEAIDER_MODELOR_APP_NAME=AiderThe first commit makes these changes:
AI_AGENTbefore the other variables.AI_AGENTis a shared convention that agents.md#136 describes. Claude Code, Copilot in VS Code, Devin, OpenCode, Crush, and Amp set it. TheghCLI and@vercel/detect-agentread it. The value starts with the agent name, for exampleclaude-code_2-1-295_agent. ldcli matches the start of the value. An unknown name becomesai-agent:<name>without the version.OPENCODE=1, so ldcli examinesKILOfirst.equalsorcontains. Amazon Q, Aider, and the Cursor terminal role need this.The order of the checks stays the same:
LD_CLI_AGENT, then the agent variables, then the TTY check, then the CI variables. Some Cursor versions setCI=1. Because ldcli examinesCURSOR_AGENTbefore the CI variables, these runs now get the labelcursor, notci.The second commit fixes a different problem in
CLI Command Completed. Since #589 (version 1.16.2),Executecalledos.Exit(1)before it sent the event. Thus ldcli did not send anerroroutcome. In the last 30 days, all 4,542 error events came from version 1.16.1 or earlier.Executenow sends the event before it exits. The exit code stays 1.I ran both binaries in a Claude Code session against a local server that recorded the tracking requests:
Describe alternatives you've considered
I did not use
@vercel/detect-agentas a model. It detects Cursor withCURSOR_TRACE_IDand Copilot withCOPILOT_MODEL. These variables also occur for people or come from the configuration of the user. I also did not useREPL_ID,TERM_PROGRAM, or the genericAGENTvariable. Each of them also occurs when a person types the command.Additional context
These limits remain:
WINDSURF_CASCADE_TERMINALcomes from the jfrog-cli detector. The Windsurf source code is not public.The dashboard counts will change slowly, because only users who upgrade send the new labels. A blank
agent_contextdoes not always mean a person at a terminal. Versions before 3.0.0 do not send the property, and these versions sent about 45% of the blank runs.🤖 Generated with Claude Code
Note
Overview
Fixes
agent_contextanalytics so ldcli recognizes AI coding agents instead of labeling almost everythingunknown-non-interactive.Detection now reads
AI_AGENT(afterLD_CLI_AGENT) using configurable prefix rules and a sanitizedai-agent:<name>fallback for unknown agents.known_agents.jsonis replaced with env vars agents actually set (e.g.CLAUDECODE,CURSOR_AGENT,CODEX_*,COPILOT_*) and supportsequals/containsvalue checks to avoid false positives (Aider, Amazon Q, Cursor terminal role). Legacy variable names that agents never set are removed.Executeincmd/root.gono longer callsos.Exit(1)before analytics flush; failed commands now emit CLI Command Completed withoutcome=errorbefore exiting.Reviewed by Cursor Bugbot for commit a22f976. Bugbot is set up for automated code reviews on this repo. Configure here.