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.4"
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_CODE_SESSION_ID |
Per-conversation UUID; shared by sub-agents and all spawned subprocesses. |
| 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 |
Gemini CLI and Crush 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,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). Version suffixes are stripped before matching, soAI_AGENT=goose@1.2.3andAI_AGENT=claude-code_2.1.0_cliboth classify. -
Tool-specific env vars:
Variable Agent CLAUDECODE,CLAUDE_CODE_ENTRYPOINT,CLAUDE_CODE_SESSION_ID,CLAUDE_CODE_EXECPATHClaude Code 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 CODEX_THREAD_ID,CODEX_SANDBOX,CODEX_SANDBOX_NETWORK_DISABLED,CODEX_CIOpenAI Codex ANTIGRAVITY_AGENTAntigravity AUGMENT_AGENTAugment CLINE_ACTIVECline ROO_ACTIVERoo Code CRUSHCrush IFLOW_CLIiFlow CLI OPENCODE,OPENCODE_PID,OPENCODE_CLIENTOpenCode TRAE_AI_SHELL_IDTRAE AI GOOSE_TERMINALGoose REPL_IDReplit AMP_CURRENT_THREAD_IDAmp COPILOT_AGENT_SESSION_ID,COPILOT_MODEL,COPILOT_ALLOW_ALL,COPILOT_GITHUB_TOKENGitHub Copilot AWS_EXECUTION_ENVcontainingAmazonQ-For-CLIAmazon Q Developer CLI Ordering disambiguates compatibility shims: Amp's marker is checked before
CLAUDECODE(Amp sets it for compat), andQWEN_CODEbeforeGEMINI_CLI(Qwen Code is a Gemini CLI fork). -
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.