is-ai-agent
Detect whether a CLI is being invoked by an AI coding agent, and identify which one.
Inspired by the AGENT environment variable proposal. Lets your CLI adapt its output — structured errors, more verbose tracebacks, no interactive prompts — when it's running under an agent rather than a human.
Install
[]
= "0.5"
Usage
use ;
if is_ai_agent
if let Some = detect
Session id
Many agentic CLIs expose a stable identifier for the current session/conversation to the subprocesses they spawn. Agent::session_id surfaces it as a single unified field, so a downstream tool can stamp it onto outbound requests (headers, query comments) and a backend can correlate every call that belongs to the same agent run.
The value is opaque and only comparable within the same agent — pair it with agent.id before correlating. It is None when the detected agent doesn't publish one.
| Agent | Source env var | Notes |
|---|---|---|
| Claude Code / Claude Cowork | CLAUDE_CODE_SESSION_ID |
Per-conversation UUID; shared by sub-agents and all spawned subprocesses. |
| CodeBuddy | CODEBUDDY_SESSION_ID, then CLAUDE_SESSION_ID |
Dual-written to a CLAUDE_* mirror. |
| OpenAI Codex | CODEX_THREAD_ID |
|
| Amp | AMP_CURRENT_THREAD_ID, then AGENT_THREAD_ID |
|
| Qwen Code | QWEN_CODE_SESSION_ID |
|
| Cursor / Cursor CLI | CURSOR_TRACE_ID |
Trace id; per-session vs per-command scope is undocumented, so correlation may fragment. |
| GitHub Copilot | COPILOT_AGENT_SESSION_ID, then COPILOT_AGENT_JOB_ID |
Job id is set by the Actions-based cloud coding agent. |
| Warp | OZ_RUN_ID |
Run id; scope undocumented, so correlation may fragment. |
| Cline | CLINE_TASK_ID |
|
| Roo Code | ROO_CODE_TASK_ID |
Gemini CLI, Crush, and Grok CLI expose a session id only to hooks, not to ordinary subprocesses, so no id is available there.
Trace context
When the agent runs under OpenTelemetry tracing, it may publish a TRACEPARENT to its subprocesses. Agent::traceparent exposes the raw W3C value (forward it as a traceparent header to keep downstream requests on the same distributed trace), and Agent::trace_id() extracts just the 32-hex trace-id correlation key.
TRACEPARENT is a standard var name, so it's read directly rather than per agent. Among the agents here, Claude Code and Qwen Code propagate it — both gated behind telemetry being enabled, and off by default (Claude Code additionally requires CLAUDE_CODE_PROPAGATE_TRACEPARENT; Qwen Code requires outboundCorrelation.propagateTraceContext). For other agents a traceparent appears only if one was already present in the ambient shell and inherited.
For tests or callers that want to consult a captured environment instead of the live process, use detect_with:
use detect_with;
let agent = detect_with;
Detection order
-
The generic
AGENT(the agents.md proposal) andAI_AGENT(@vercel/detect-agent) env vars, when their value names a known agent (goose,amp,claude-code,cowork,cursor,cursor-cli,gemini-cli,codex,augment,cline,opencode,trae,devin,replit,antigravity,github-copilot,crush,qwen-code,iflow-cli,amazon-q-cli,roo-code,codebuddy,grok-cli,warp,pi,kiro,firebender,openhands,vecli,v0). The whole value is matched first (VS Code's Copilot agent mode setsAI_AGENT=github_copilot_vscode_agent), then version suffixes are stripped, soAI_AGENT=goose@1.2.3,AI_AGENT=v0@1.2.3, andAI_AGENT=claude-code_2-1-201_agentall classify. -
Tool-specific env vars:
Variable Agent CLAUDECODE,CLAUDE_CODE_ENTRYPOINT,CLAUDE_CODE_SESSION_ID,CLAUDE_CODE_EXECPATHClaude Code CLAUDE_CODE_IS_COWORK(refines any Claude Code identification when present)Claude Cowork CODEBUDDY,CODEBUDDY_SESSION_ID,CODEBUDDY_PROJECT_DIR(checked before Claude Code — CodeBuddy mirrors someCLAUDE_*vars)CodeBuddy CURSOR_AGENT(set by both the CLI and the IDE's agent terminals)Cursor CURSOR_TRACE_IDandPAGER=head -n 10000 | cat(older builds; the PAGER override distinguishes agent mode from a human in Cursor's terminal)Cursor CURSOR_SANDBOX,CURSOR_EXTENSION_HOST_ROLE=agent-execCursor CLI GEMINI_CLIGemini CLI QWEN_CODEQwen Code VECLI_SANDBOX,VECLI_DIR(checked beforeGEMINI_CLI— veCLI is a Gemini CLI fork)veCLI CODEX_THREAD_ID,CODEX_SANDBOX,CODEX_SANDBOX_NETWORK_DISABLED,CODEX_CIOpenAI Codex ANTIGRAVITY_AGENT,ANTIGRAVITY_PROJECT_IDAntigravity AUGMENT_AGENTAugment CLINE_ACTIVE,CLINE_TASK_IDCline ROO_CODE_TASK_IDRoo Code CRUSHCrush IFLOW_CLIiFlow CLI GROK_AGENTGrok CLI OZ_RUN_IDWarp PI_CODING_AGENTPi KIRO_AGENT_PATHKiro AGENT_CONTEXT_OUTandAGENT_DISPLAY_OUT(the Kiro CLI exports this FIFO pair only while its agent drives the command; either alone is too generic)Kiro FIREBENDER_TERMINALFirebender PS1orPROMPT_COMMANDcontaining###PS1JSON###OpenHands OPENCODE,OPENCODE_PID,OPENCODE_BIN_PATH,OPENCODE_SERVER,OPENCODE_APP_INFO,OPENCODE_MODES,OPENCODE_CLIENTOpenCode TRAE_AI_SHELL_IDTRAE AI GOOSE_TERMINALGoose REPL_IDReplit AMP_CURRENT_THREAD_IDAmp COPILOT_AGENT_SESSION_ID,COPILOT_AGENT,COPILOT_CLI,COPILOT_AGENT_JOB_ID,COPILOT_MODEL,COPILOT_ALLOW_ALL,COPILOT_GITHUB_TOKENGitHub Copilot AWS_EXECUTION_ENVcontainingAmazonQ-For-CLIAmazon Q Developer CLI Ordering disambiguates compatibility shims and forks: Amp's and CodeBuddy's markers are checked before
CLAUDECODE(Amp sets it for compat; CodeBuddy mirrorsCLAUDE_*vars), andQWEN_CODE/VECLI_*beforeGEMINI_CLI(both are Gemini CLI forks that inheritGEMINI_CLI=1). -
Filesystem signals:
Path Agent /opt/.devinDevin -
A bare truthy
AGENT/AI_AGENT(e.g.AGENT=1) as a last resort, resolving toAgentId::Unknown. Tool-specific vars outrank it, so agents that set both (e.g. OpenCode setsAGENT=1andOPENCODE=1) are still identified.
The detected Agent carries the Signal that matched, so callers can see exactly how detection fired.
License
Dual-licensed under MIT or Apache-2.0, at your option.