Repository navigation
feat(cli): detect more agent runtimes in telemetry - #7423
Conversation
Agent runtime rules: - copilot_agent: key on COPILOT_AGENT=1 or AI_AGENT=github_copilot_* (VS Code agent terminals) instead of the unverified Actions markers. - copilot_cli: COPILOT_CLI=1 or COPILOT_AGENT_SESSION_ID, ahead of copilot_agent. - kilo (KILO=1, KILOCODE_VERSION), ahead of opencode. - opencode (OPENCODE=1, OPENCODE_SESSION_ID). - qwen_code (QWEN_CODE=1), junie (JUNIE_SHIM_PATH), antigravity (ANTIGRAVITY_AGENT), qoder (QODER_AGENT), kiro (AGENT_DISPLAY_OUT, AGENT_CONTEXT_OUT), openhands (AI_AGENT=openhands). - AI_AGENT fallback after every vendor rule: known names map to their runtime, any other slug-safe name becomes other_ai_agent. Bare AGENT is not used. Discovery hints: other_ai_agent keeps its hints, agent_hint prefers AI_AGENT over AGENT, and AGENT_TOOLSDIRECTORY, GSM_SKIP_SSH_AGENT_WORKAROUND and EDGE_METRICS_SESSION_ID are no longer hint keys. CI: name AWS CodeBuild (CODEBUILD_BUILD_ID) as codebuild. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add rules for closed-source agents whose markers show up in the CLI's own agent_env_hints on non-interactive runs: WorkBuddy, CodeBuddy, Trae, Pippit, Doubao, Aily, Grok, Kimi, ZCode, SWE-agent and apps built on the Claude Agent SDK. Qoder also matches its CLI and agent SDK keys. The AI_AGENT name map now covers the names agents report (Devin, Amp, v0, Rovo Dev, Base44, Trae, Antigravity, OpenHands, Copilot, and hermes-agent), and a known name in the bare AGENT variable counts too. Unknown AGENT values such as AGENT=1 still do not. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every dsh model shell call runs with DSH_SHELL=1 and the agent's DSH_SESSION_ID. HyperFrames Desktop also runs dsh; its runs carry HYPERFRAMES_CLIENT, so the client field separates the two. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 2 important · 2 nits · head 46dfccb0a
The DSH_SESSION_ID arm counts a person in DSH's interactive terminal as an agent, and because isAgentDriven now uses it, the CLI stops prompting there. Details are inline. AGENT=1 still returns null, and the _/- boundary checks out.
Scope
I read agent_runtime.ts and system.ts end to end and checked the tests against them. I checked each marker against upstream source where one exists: ZCode, the Grok Build changelog, DSH, and the Claude Agent SDK. Where none exists, I used third-party detectors and public env dumps. CI on this head was still running when I posted.
— Genesis (Claude)
Copilot's COPILOT_AGENT / AI_AGENT and COPILOT_CLI pass down to the agents it starts, and TERM_PROGRAM / REPL_ID only say where a shell runs. Those rules now come after every other agent rule, so opencode or Qwen started by Copilot, or Kilo in a Cursor terminal, report as themselves. dsh keys on DSH_SHELL=1 only: it sets DSH_SESSION_ID on the terminal a person types into too. Qoder drops its app-wide SDK and setting keys, CodeBuddy drops CODEBUDDY_AGENT_RUNTIME (a setting it reads), and Kiro's markers must be non-empty. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| // not that an agent drives it, so every agent marker above wins over them. | ||
| // Cursor's TERM_PROGRAM identifies the IDE terminal only. | ||
| // Source: https://docs.cursor.com/en/agent/terminal | ||
| { name: "cursor", check: (env) => env["TERM_PROGRAM"] === "cursor", agentOnly: () => false }, |
There was a problem hiding this comment.
important: an agent identified only through AI_AGENT/AGENT still loses to these editor and workspace rules, because aiAgentFallback runs after all of VENDOR_RULES. Probed on this head: AI_AGENT=amp + TERM_PROGRAM=cursor → cursor, AI_AGENT=devin_1_agent + REPL_ID → replit, and an unknown AI_AGENT in Cursor → cursor. The last one also drops the discovery hints, since system.ts only sends them for null/other_ai_agent. Suggested fix: split the trailing cursor/windsurf/replit entries into a separate list that detectAgentRuntime checks after aiAgentFallback, and add a test for Amp in Cursor.
…red-row line counts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
The CLI telemetry agent detector now recognizes more coding agents and has a generic
AI_AGENTfallback:copilot_agentrule keyed onCOPILOT_AGENT_ID/RUNNER_NAME=Copilotinside GitHub Actions, and those markers don't appear in any upstream source. It now matches what VS Code actually sets on agent terminals:COPILOT_AGENT=1orAI_AGENT=github_copilot_*.copilot_cli,kilo,opencode,qwen_code,junie,antigravity,qoder,kiro,openhands.AI_AGENTfallback: runs after every vendor rule. Known names (claude-code, opencode, crush, pi, hermes) map to their runtime, matching exactly or as a<name>_prefix. Any other slug-safe value becomesother_ai_agent, and its discovery hints are still sent so it can be promoted to a rule later.agent_env_hintson non-interactive runs. That is direct evidence the key is in the env of the shell the agent spawned. New runtimes:workbuddy,codebuddy,trae,pippit,doubao,aily,grok,kimi,zcode,swe_agent, andclaude_agent_sdk(an app built on the Claude Agent SDK, running outside Claude Code's own shell tool). Qoder also matches its CLI and agent SDK keys. Only key presence is checked; several of these values are tokens and are never read. App-wide settings (Qoder's SDK and spawn-depth keys,CODEBUDDY_AGENT_RUNTIME) were left out after review.hermes-agent), matching on a_or-boundary. A known name in the bareAGENTvariable counts too; unknownAGENTvalues such asAGENT=1still do not.dsh, fromDSH_SHELL=1(deepseek-ai/deepseek-harness).CODEBUILD_BUILD_ID) is detected ascodebuild.AGENT_TOOLSDIRECTORY(the GitHub runner toolcache path),GSM_SKIP_SSH_AGENT_WORKAROUNDandEDGE_METRICS_SESSION_IDonly matched the hint pattern by accident and no longer count as hint keys.agent_hintorder: it now prefersAI_AGENToverAGENT, because opencode-style agents setAGENT=1next to their real name.Why
Agents with no rule fall into the
agent_runtime = nullbucket, together with plain shells. Discovery hints from recent CLI versions show markers for several of these agents. The first commit's rules meet the existing bar: the agent's own source or docs say the marker is set in the environment of the shell it spawns. The second commit's rules use the CLI's discovery hints as that evidence instead, since those agents are closed source.Related work
None found.
How
COPILOT_AGENT/AI_AGENTandCOPILOT_CLI, OpenHands'AI_AGENT, the Agent SDK version key, and the editor/workspace markers (TERM_PROGRAM=cursor|windsurf, Replit). So opencode or Qwen started by Copilot, or Kilo in a Cursor terminal, report as themselves. Cursor's own agent (CURSOR_AGENT) keeps its early slot.copilot_clicomes beforecopilot_agent, because the CLI run from a VS Code agent terminal inheritsCOPILOT_AGENT=1.kilocomes beforeopencode, because Kilo is an opencode fork and its bash tool also setsOPENCODE=1.DSH_SHELL=1only. dsh also setsDSH_SESSION_IDon the interactive terminal a person types into, so that key would count a person as an agent.AGENT_DISPLAY_OUT/AGENT_CONTEXT_OUT, which are only set while the agent is driving. It does not useTERM_PROGRAM=kiro, which Kiro also sets on a person's terminal.isAgentDriven: also returns true whenAI_AGENTis set.GROK_AGENT, Trae'sTRAE_AI_SHELL_ID).Test plan
AI_AGENTprefix matching (pidoes not matchpilot), theother_ai_agenthints path, CodeBuild CI, the dropped hint keys, each discovery-hint rule, WorkBuddy over CodeBuddy, vendor markers over the Agent SDK key, and known vs unknown bareAGENTvalues.vitest run src/telemetry src/utils/attendedTerminal.test.ts: 457/457. The full cliunitandspawnprojects pass.bun run lint,oxfmt --check,tsc --noEmit, comment-citation and comment-ratchet checks pass.— Rames
🤖 Generated with Claude Code