Skip to content

Proposal: Standardize AI_AGENT for agent runtime detection #136

Open

Description

@ianlet

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)

  1. MUST set AI_AGENT in the environment of every command they spawn.
  2. The value MUST match ^[A-Za-z0-9_-]{1,64}$ (the format gh enforces) 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.
  3. SHOULD NOT overwrite an inherited AI_AGENT set by another agent: the outermost agent wins.
  4. MAY keep setting their tool-specific variables.

Consumers (tools)

  1. MUST treat any non-empty value other than 0, false, no, off as "running under an agent".
  2. SHOULD identify the agent by matching the value against a registry, by registered slug prefix, falling back to unknown.
  3. MUST NOT use it for authorization or to skip confirmations. It is an unauthenticated hint.
  4. 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_AGENT is an input setting of Grok Build (which agent profile to run), not a marker it sets (docs).
  • CLAUDECODE is also set in Claude Code IDE terminals, and REPL_ID across the whole Replit platform: both cause false positives when used alone.

Consumer adoption

AI_AGENT is read first, before vendor-specific variables, by:

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:

This proposal relies on an existing registry for slugs rather than creating another.

Security considerations

  • AI_AGENT is 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|auto or a tool-specific override (e.g. NETLIFY_AGENT).

Precedent

  • CI=true: set by GitHub Actions, GitLab CI, CircleCI, Jenkins, …
  • NO_COLOR: no-color.org
  • TERM=dumb: limited terminal capabilities

Out of scope

Open questions

  1. Version — keep it inside the value (as Claude Code does) or move it to a separate variable?
  2. Nesting — is "outermost agent wins" the right default? Terminal multiplexers and agent-in-agent setups can leak a stale value (herdrdev/herdr#4631).
  3. Defaults — should producers also set NO_COLOR, TERM=dumb, and PAGER=cat (as Codex and Mistral Vibe already do)?
  4. Registry governance — can one of the existing registries move to neutral ownership (e.g. under AAIF, alongside AGENTS.md)?
  5. Allowlisted boundaries — AcceptEnv/SendEnv, containers, CI secret allowlists each need AI_AGENT added 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 for AGENT were closed by Claude Code (#24838), Codex (#13416), and Gemini CLI (#21032).
  • Most consumers read AI_AGENT first, and several ignore AGENT on purpose.
  • AGENT never got a defined value format and is now read in four incompatible ways (see above).
  • AGENT is generic enough to collide with unrelated tooling, the original concern raised by VS Code (#258911), which itself adopted AI_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

  1. gmeligio commented on Feb 28, 2026

    @gmeligio

    I think this standard environment variable should be added in the agent process. See the related discussion anthropics/claude-code#24838 .

  2. ianlet commented on Mar 2, 2026

    @ianlet
    Author

    Project goose implemented the AGENT=goose environment variable in their tool aaif-goose/goose#7017 as well as amp with AGENT=amp.

  3. sw-square commented on Mar 3, 2026

    @sw-square

    Re "Variable name" in open questions: I'm personally a fan of AGENT over AI_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 leading AI_ feels quite redundant (as would TERM_PROGAM or USER_LANG would be), and I just see no reason for the stratification

  4. withzombies commented on Mar 9, 2026

    @withzombies

    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:

    • AGENT=codex / AGENT=claude for generic detection
    • AGENT_ID= for per-agent state
    • optionally PARENT_AGENT_ID= for parent/subagent relationships
    • optionally AGENT_NAME= for logs/debugging
    • optionally AGENT_DEPTH= as extra metadata, but not as the primary key

    That keeps the current proposal simple while making it usable for multi-agent runtimes, not just single-agent command adaptation.

  5. alies-dev commented on Mar 22, 2026

    @alies-dev

    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=true and AGENT is 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=long gives agents full tracebacks they can actually fix - you'd never use that in CI
    • go test -v shows individual test names the agent needs to understand what failed - CI wants summary only
    • PHPUnit's --display-errors --display-warnings surfaces PHP-level issues hidden by default - useful for agents, noise in CI

    CI=true conflates "non-interactive" with "minimal output." An AGENT variable could signal "non-interactive but give me everything actionable."

    Some tools already consume agent env vars. Bun's isAIAgent() checks AGENT=1 plus tool-specific vars. Laravel Pint auto-switches to --format agent when CLAUDECODE is detected. Both had to build their own detection matrices because there's no single standard - which is exactly what this proposal fixes.

    +1 for AGENT over AI_AGENT. Mirrors CI - 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.

  6. maxwellb commented on Aug 5, 2026

    @maxwellb

    Adopting this or any other agent "detection" could answer one of the questions in #185 about file names.

  7. CrimsonGlory commented on Sep 7, 2026

    @CrimsonGlory

    Currently Grok uses GROK_AGENT=1

  8. mthamil107 commented on Sep 23, 2026

    @mthamil107

    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):

    if let Some(env) = env_var::AGENT.get() {
        return env == b"1";
    }
    if is_verbose() { return false; }
    if env_var::CLAUDECODE.get().unwrap_or(false) { return true; }

    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 the CLAUDECODE and REPL_ID fallbacks underneath, so a Goose user on Bun is detected worse than if AGENT were unset. Meanwhile the open PR on the Vercel side (vercel/vercel#15350) treats any non-empty AGENT as a name, which makes AGENT=1 an 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|true needs 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 moved detect-agent into its own repository with a machine-readable agents.json registry and a JSON schema, and its README now describes AI_AGENT as a standard it is promoting; AGENT is not in that registry. The Firebase CLI also reads AI_AGENT first, citing detect-agent.

    One correction I cannot make myself: the table credits Amp with AGENT=amp, citing the permissions-delegation page. I could not find AGENT on 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 — AcceptEnv and SendEnv in 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.

  9. changed the title [-]Proposal: Standard environment variable for agent runtime detection[/-] [+]Proposal: Standardize AI_AGENT for agent runtime detection[/+] on Oct 2, 2026
  10. ianlet commented on Oct 2, 2026

    @ianlet
    Author

    I've rewritten the description to reflect where the ecosystem landed: this proposal now standardizes AI_AGENT instead of AGENT.

    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. Meanwhile AGENT never 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:

    • Name: AI_AGENT, with AGENT kept as a legacy fallback
    • Value format and semantics: allowed characters, prefix matching against an existing registry, nested agents, truthy values
    • Adoption tables: re-verified against source code as of 2026-10-02, with a separate table for how consumers read AGENT
    • New sections: security considerations, out of scope, and open questions

    Thanks to everyone who contributed:

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions