Repository navigation
Description
Last verified: 2026-10-02. Corrections welcome in the comments — please include a source (code link, docs, or observed env dump).
Problem
Scripts and CLIs detect CI via CI=true and adapt: no prompts, CI-friendly output. There is no equivalent for AI coding agents, so every tool builds its own detection matrix of vendor-specific variables (CLAUDECODE, CURSOR_AGENT, GEMINI_CLI, CODEX_CI, …).
Agents need something different from CI:
- Interactivity — like CI, agents can't answer prompts
- Output — CI wants less output; agents want more actionable output (full tracebacks, failing test names, structured errors)
- Attribution — tools want to know which agent drove a command (telemetry, User-Agent, commit attribution)
Proposal
Standardize AI_AGENT, the variable the ecosystem has already converged on, and define what nobody has yet: its value format and semantics.
Producers (agents)
- MUST set
AI_AGENTin the environment of every command they spawn. - The value MUST match
^[A-Za-z0-9_-]{1,64}$(the formatghenforces) and SHOULD start with the agent's registered slug. Qualifiers (surface, version) MAY follow, e.g.claude-code_2-1-284_agent,github_copilot_vscode_agent. - SHOULD NOT overwrite an inherited
AI_AGENTset by another agent: the outermost agent wins. - MAY keep setting their tool-specific variables.
Consumers (tools)
- MUST treat any non-empty value other than
0,false,no,offas "running under an agent". - SHOULD identify the agent by matching the value against a registry, by registered slug prefix, falling back to
unknown. - MUST NOT use it for authorization or to skip confirmations. It is an unauthenticated hint.
- SHOULD sanitize the value before putting it in URLs, headers, or logs.
AGENT (legacy)
Producers MAY also set AGENT. Consumers MAY read it as a fallback after AI_AGENT, treating 1/true as an unnamed agent and any other value as a name.
Producer adoption
| Agent | AI_AGENT |
AGENT |
Tool-specific | Ref |
|---|---|---|---|---|
| Claude Code | claude-code_<version>_agent |
– | CLAUDECODE=1, CLAUDE_CODE_CHILD_SESSION=1 |
Changelog 2.1.120 |
| VS Code Copilot | github_copilot_vscode_agent |
– | COPILOT_AGENT=1 |
microsoft/vscode#320129, #327433 |
| pi | pi |
– | PI_CODING_AGENT=true |
earendil-works/pi#7493 |
| Crush | crush |
crush |
CRUSH=1 |
internal/shell/shell.go |
| OpenCode | opencode |
1 |
OPENCODE=1 |
anomalyco/opencode#51975 |
| Hermes Agent | hermes-agent |
– | HERMES_AGENT=true |
sdairs/is-ai-agent#6 (observed) |
| OpenHands | openhands |
– | – | OpenHands/software-agent-sdk#4366 |
| Kilo Code | requested | 1 |
KILO=1, OPENCODE=1 |
Kilo-Org/kilocode#12854 |
| Goose | – | goose (regressed in shell tool) |
GOOSE_TERMINAL=1 |
aaif-goose/goose#12606 |
| Codex CLI | requested | – | CODEX_CI=1, CODEX_THREAD_ID |
openai/codex#36883 |
| Gemini CLI | – | – | GEMINI_CLI=1 |
shellExecutionService.ts |
| GitHub Copilot CLI | unknown | – | COPILOT_CLI=1 |
Changelog |
| Cursor | – | – | CURSOR_AGENT |
Docs |
| Augment | – | – | AUGMENT_AGENT=1 |
Docs |
| Cline | – | – | CLINE_ACTIVE=true (VS Code terminals) |
VscodeTerminalRegistry.ts |
| Qwen Code | – | – | QWEN_CODE=1 |
shellExecutionService.ts |
| Kimi CLI | requested | – | – | MoonshotAI/kimi-cli#2585 |
| Aider | requested | – | – | Aider-AI/aider#5521 |
| Amp | unknown | unverified | AMP_CURRENT_THREAD_ID |
AGENT=amp documented only for the permission delegate helper |
| Devin | – | – | /opt/.devin (file) |
vercel/detect-agent |
Notes:
GROK_AGENTis an input setting of Grok Build (which agent profile to run), not a marker it sets (docs).CLAUDECODEis also set in Claude Code IDE terminals, andREPL_IDacross the whole Replit platform: both cause false positives when used alone.
Consumer adoption
AI_AGENT is read first, before vendor-specific variables, by:
- CLIs:
gh, Firebase, Supabase, Netlify, Stripe, Sentry, CircleCI, DuckDB, Dagger - JS tooling: Jest, Vitest, Storybook, Turborepo, Next.js
- PHP tooling: Composer, PHPStan, Psalm, Laravel Pint
- Python: Pydantic AI, MLflow, huggingface_hub, crewAI
- Detection libraries:
detect-agent, std-env, laravel/agent-detector, mise-agent-env, is-ai-agent
AGENT is read four incompatible ways, which is why this proposal keeps it as legacy only:
| Interpretation | Consumers |
|---|---|
Only AGENT=1 counts; any other value means not an agent |
Bun (oven-sh/bun#44459), ami |
| Only known names count | Psalm (goose), gh / Netlify (amp), Wrangler |
| Any non-empty value counts | Stripe, DuckDB, Dagger, Modal, Sentry, testthat |
| Ignored on purpose | std-env, detect-agent, Composer, PHPStan, mise |
Registries
Two machine-readable agent registries exist:
vercel/detect-agentagents.json(with JSON Schema)- Hugging Face
/api/agent-harnesses(docs)
This proposal relies on an existing registry for slugs rather than creating another.
Security considerations
AI_AGENTis a hint, not an identity. Anything in the process tree can set it, and prompt injection can poison environment variables (CVE-2026-22708).- Keep agent-specific behavior to output and interactivity. Never auto-confirm destructive actions because an agent is detected.
- Behavior changes should be overridable: PHP-CS-Fixer#9655 made detection opt-in after user pushback, and several CLIs expose
--agent yes|no|autoor a tool-specific override (e.g.NETLIFY_AGENT).
Precedent
CI=true: set by GitHub Actions, GitLab CI, CircleCI, Jenkins, …NO_COLOR: no-color.orgTERM=dumb: limited terminal capabilities
Out of scope
- Per-agent identity in multi-agent runs (raised by @withzombies). Agents already expose session IDs (
CLAUDE_CODE_SESSION_ID,CODEX_THREAD_ID,PI_SESSION_ID,AGENT_SESSION_ID), and W3CTRACEPARENTcovers parent/child relationships. Worth a separate proposal. - Agent-specific instructions: Need a standardized way to show different AGENTS.md files and/or content to different agents #185 depends on agents knowing their own identity, which this proposal provides.
Open questions
- Version — keep it inside the value (as Claude Code does) or move it to a separate variable?
- Nesting — is "outermost agent wins" the right default? Terminal multiplexers and agent-in-agent setups can leak a stale value (herdrdev/herdr#4631).
- Defaults — should producers also set
NO_COLOR,TERM=dumb, andPAGER=cat(as Codex and Mistral Vibe already do)? - Registry governance — can one of the existing registries move to neutral ownership (e.g. under AAIF, alongside AGENTS.md)?
- Allowlisted boundaries —
AcceptEnv/SendEnv, containers, CI secret allowlists each needAI_AGENTadded explicitly (ddev/ddev#8378). Should this spec recommend forwarding it?
Decision log
2026-10 — AGENT discarded in favor of AI_AGENT. This issue originally proposed AGENT=[agent-name]|1|true. The industry moved to AI_AGENT instead:
- Claude Code (2.1.120), VS Code Copilot (#320129), pi (#7493), Hermes, OpenHands, and OpenCode shipped
AI_AGENT. Requests forAGENTwere closed by Claude Code (#24838), Codex (#13416), and Gemini CLI (#21032). - Most consumers read
AI_AGENTfirst, and several ignoreAGENTon purpose. AGENTnever got a defined value format and is now read in four incompatible ways (see above).AGENTis generic enough to collide with unrelated tooling, the original concern raised by VS Code (#258911), which itself adoptedAI_AGENT.
Supporting both names as equals was also discarded: every allowlisted boundary (ssh, containers, CI) needs each name added separately, so two names cost more than one.
Activity
gmeligio commentedon Feb 28, 2026
I think this standard environment variable should be added in the agent process. See the related discussion anthropics/claude-code#24838 .
ianlet commentedon Mar 2, 2026
Project
gooseimplemented theAGENT=gooseenvironment variable in their tool aaif-goose/goose#7017 as well asampwithAGENT=amp.sw-square commentedon Mar 3, 2026
Re "Variable name" in open questions: I'm personally a fan of
AGENToverAI_AGENT.There's lots of precedence with using shorter names (
LANG,EDITOR/VISUAL,CI,TERM, etc), and the word "agent" is quickly converging to exclusively refer to AI (and didn't really have a commonly used/clearly defined meaning beforehand). The leadingAI_feels quite redundant (as wouldTERM_PROGAMorUSER_LANGwould be), and I just see no reason for the stratificationwithzombies commentedon Mar 9, 2026
This would help for basic agent detection, but there’s another important case worth covering: many subagents running at once. I'm running into issues with this when attempting to convert my MCP uses in CLI uses.
For tools that keep per-agent local state, a boolean like AGENT=1 is not enough. They need a stable identifier for the current agent, otherwise sibling subagents are indistinguishable. Depth alone is not sufficient either, because you can have multiple subagents at the same depth.
Something like this would cover both cases cleanly:
That keeps the current proposal simple while making it usable for multi-agent runtimes, not just single-agent command adaptation.
alies-dev commentedon Mar 22, 2026
I've been documenting CLI flags that reduce output noise for AI agents across 10+ ecosystems (awesome-ai-friendly-cli). A few things I found that are relevant here.
The gap between
CI=trueandAGENTis real. In CI you want minimal output. For agents you often want the opposite - structured and actionable, which means more detail, not less. Examples:pytest --tb=longgives agents full tracebacks they can actually fix - you'd never use that in CIgo test -vshows individual test names the agent needs to understand what failed - CI wants summary only--display-errors --display-warningssurfaces PHP-level issues hidden by default - useful for agents, noise in CICI=trueconflates "non-interactive" with "minimal output." AnAGENTvariable could signal "non-interactive but give me everything actionable."Some tools already consume agent env vars. Bun's
isAIAgent()checksAGENT=1plus tool-specific vars. Laravel Pint auto-switches to--format agentwhenCLAUDECODEis detected. Both had to build their own detection matrices because there's no single standard - which is exactly what this proposal fixes.+1 for
AGENToverAI_AGENT. MirrorsCI- short, already used by Goose and Amp.Practical note: until this is standardized, the most reliable approach is instructing agents via CLAUDE.md/AGENTS.md to use quiet flags explicitly. That's what awesome-ai-friendly-cli documents - per-tool flags you need right now while the ecosystem converges.
feat: detect AI-agent CLI invocations in telemetry
chore: add agent invocation telemetry (#1210)
maxwellb commentedon Aug 5, 2026
Adopting this or any other agent "detection" could answer one of the questions in #185 about file names.
feat(telemetry): widen assistant detection from published marker sets
feat: split runtime context from coding agent, add project id (#6867)
CrimsonGlory commentedon Sep 7, 2026
Currently Grok uses GROK_AGENT=1
mthamil107 commentedon Sep 23, 2026
Two things from outside the thread, both checkable in a minute, neither of which changes which name should win.
The value grammar is undefined in practice, and today it bites the one producer that shipped.
Bun's
is_ai_agent()(src/bun_core/output.rs):Two consequences.
AGENT=goose— the only value with a merged, public implementation behind it — reads as not an agent. And because that check returns early, setting it also suppresses theCLAUDECODEandREPL_IDfallbacks underneath, so a Goose user on Bun is detected worse than ifAGENTwere unset. Meanwhile the open PR on the Vercel side (vercel/vercel#15350) treats any non-emptyAGENTas a name, which makesAGENT=1an agent called "1".So the two consumers named in the original post read the same variable incompatibly, right now. Whichever name the thread settles on,
[agent-name]|1|trueneeds to become one of those three, or adoption makes detection worse rather than better.Adoption table refresh, since the first post asked for corrections. Of the attempts to set
AGENT: goose merged it (aaif-goose/goose#7017, February). claude-code#24838 was stale-closed and then locked with no reply; openai/codex#13416 was closed; google-gemini/gemini-cli#21032 was closed unmerged. On the other side, Vercel moveddetect-agentinto its own repository with a machine-readableagents.jsonregistry and a JSON schema, and its README now describesAI_AGENTas a standard it is promoting;AGENTis not in that registry. The Firebase CLI also readsAI_AGENTfirst, citing detect-agent.One correction I cannot make myself: the table credits Amp with
AGENT=amp, citing the permissions-delegation page. I could not findAGENTon that page, and the npm package is a stub that does not carry the binary. If someone has the source for that row I will happily stand corrected — I mention it only because the table is doing real work in this discussion.One thing that is easy to miss from inside a process. Accepting both names costs nothing when both are environment variables in the same process. It stops being free at any boundary with an explicit allowlist —
AcceptEnvandSendEnvin sshd and ssh config, container runtimes, CI secret allowlists — where each name is a separate manual opt-in by somebody who is not in this thread. That is a real cost of not converging, and it is an argument for picking one, not for picking mine.[-]Proposal: Standard environment variable for agent runtime detection[/-][+]Proposal: Standardize AI_AGENT for agent runtime detection[/+]ianlet commentedon Oct 2, 2026
I've rewritten the description to reflect where the ecosystem landed: this proposal now standardizes
AI_AGENTinstead ofAGENT.Since this issue was opened, Claude Code, VS Code Copilot, pi, OpenCode, Crush, Hermes, and OpenHands have shipped
AI_AGENT, and most tools that detect agents (gh, Vercel, Firebase, Jest, Vitest, Composer, PHPStan, …) read it first. MeanwhileAGENTnever got a defined value format, and consumers now read it in four incompatible ways. The full reasoning is in the new "Decision log" section.What changed in the description:
AI_AGENT, withAGENTkept as a legacy fallbackAGENTThanks to everyone who contributed:
== "1"check, the closedAGENTrequests, and the Amp row, which is now marked unverified). The allowlist-boundary argument is now the case against supporting two names.TRACEPARENT. It deserves its own proposal.GROK_AGENTis an input setting (which agent profile to run), not a marker Grok sets.The open questions at the bottom are where feedback is most useful right now, especially on keeping the version inside the value and on whether the outermost agent should win when agents are nested.
help: --help-all, and an AI agent's --help prints the commands below too
help: --help-all, and an AI agent's --help prints the commands below too
feat(host-process): set BUZZ_VERSION for every spawned process (#2674)