@vercel/detect-agent
Advanced tools
+19
| # @vercel/detect-agent | ||
| ## 1.0.0 | ||
| ### Major Changes | ||
| - Change return format to be an object to future proof ([#13965](https://github.com/vercel/vercel/pull/13965)) | ||
| ## 0.2.0 | ||
| ### Minor Changes | ||
| - Improve agent detection ([#13762](https://github.com/vercel/vercel/pull/13762)) | ||
| ## 0.1.0 | ||
| ### Minor Changes | ||
| - Initial release ([#13745](https://github.com/vercel/vercel/pull/13745)) |
+123
| # @vercel/detect-agent | ||
| A lightweight utility for detecting if code is being executed by an AI agent or automated development environment. | ||
| ## Installation | ||
| ```bash | ||
| npm install @vercel/detect-agent | ||
| ``` | ||
| ## Usage | ||
| ```typescript | ||
| import { determineAgent } from '@vercel/detect-agent'; | ||
| const { isAgent, agent } = await determineAgent(); | ||
| if (isAgent) { | ||
| console.log(`Running in ${agent.name} environment`); | ||
| // Adapt behavior for AI agent context | ||
| } | ||
| ``` | ||
| ## Supported Agents | ||
| This package can detect the following AI agents and development environments: | ||
| - **Custom agents** via `AI_AGENT` environment variable | ||
| - **Cursor** (cursor editor and cursor-cli) | ||
| - **Claude Code** (Anthropic's Claude) | ||
| - **Devin** (Cognition Labs) | ||
| - **Gemini CLI** (Google) | ||
| - **Codex** (OpenAI) | ||
| - **Replit** (online IDE) | ||
| ## The AI_AGENT Standard | ||
| We're promoting `AI_AGENT` as a universal environment variable standard for AI development tools. This allows any tool or library to easily detect when it's running in an AI-driven environment. | ||
| ### For AI Tool Developers | ||
| Set the `AI_AGENT` environment variable to identify your tool: | ||
| ```bash | ||
| export AI_AGENT="your-tool-name" | ||
| # or | ||
| AI_AGENT="your-tool-name" your-command | ||
| ``` | ||
| ### Recommended Naming Convention | ||
| - Use lowercase with hyphens for multi-word names | ||
| - Include version information if needed, separated by an `@` symbol | ||
| - Examples: `claude-code`, `cursor-cli`, `devin@1`, `custom-agent@2.0` | ||
| ## Use Cases | ||
| ### Adaptive Behavior | ||
| ```typescript | ||
| import { determineAgent } from '@vercel/detect-agent'; | ||
| async function setupEnvironment() { | ||
| const { isAgent, agent } = await determineAgent(); | ||
| if (isAgent) { | ||
| // Running in AI environment - adjust behavior | ||
| process.env.LOG_LEVEL = 'verbose'; | ||
| console.log(`🤖 Detected AI agent: ${agent.name}`); | ||
| } | ||
| } | ||
| ``` | ||
| ### Telemetry and Analytics | ||
| ```typescript | ||
| import { determineAgent } from '@vercel/detect-agent'; | ||
| async function trackUsage(event: string) { | ||
| const result = await determineAgent(); | ||
| analytics.track(event, { | ||
| agent: result.isAgent ? result.agent.name : 'human', | ||
| timestamp: Date.now(), | ||
| }); | ||
| } | ||
| ``` | ||
| ### Feature Toggles | ||
| ```typescript | ||
| import { determineAgent } from '@vercel/detect-agent'; | ||
| async function shouldEnableFeature(feature: string) { | ||
| const result = await determineAgent(); | ||
| // Enable experimental features for AI agents | ||
| if (result.isAgent && feature === 'experimental-ai-mode') { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| ``` | ||
| ## Contributing | ||
| We welcome contributions! Please see our [contributing guidelines](../../CONTRIBUTING.md). | ||
| ### Adding New Agent Support | ||
| To add support for a new AI agent: | ||
| 1. Add detection logic to `src/index.ts` | ||
| 2. Add comprehensive test cases in `test/unit/determine-agent.test.ts` | ||
| 3. Update this README with the new agent information | ||
| 4. Follow the existing priority order pattern | ||
| ## Links | ||
| - [GitHub Repository](https://github.com/vercel/vercel/tree/main/packages/detect-agent) | ||
| - [npm Package](https://www.npmjs.com/package/@vercel/detect-agent) | ||
| - [Vercel Documentation](https://vercel.com/docs) |
+28
-11
@@ -21,2 +21,3 @@ "use strict"; | ||
| __export(src_exports, { | ||
| KNOWN_AGENTS: () => KNOWN_AGENTS, | ||
| determineAgent: () => determineAgent | ||
@@ -27,3 +28,3 @@ }); | ||
| var import_node_fs = require("node:fs"); | ||
| const devinLocalPath = "/opt/.devin"; | ||
| const DEVIN_LOCAL_PATH = "/opt/.devin"; | ||
| const CURSOR = "cursor"; | ||
@@ -36,34 +37,50 @@ const CURSOR_CLI = "cursor-cli"; | ||
| const CODEX = "codex"; | ||
| const KNOWN_AGENTS = { | ||
| CURSOR, | ||
| CURSOR_CLI, | ||
| CLAUDE, | ||
| DEVIN, | ||
| REPLIT, | ||
| GEMINI, | ||
| CODEX | ||
| }; | ||
| async function determineAgent() { | ||
| if (process.env.AI_AGENT) { | ||
| return process.env.AI_AGENT; | ||
| const name = process.env.AI_AGENT.trim(); | ||
| if (name) { | ||
| return { | ||
| isAgent: true, | ||
| agent: { name } | ||
| }; | ||
| } | ||
| } | ||
| if (process.env.CURSOR_TRACE_ID) { | ||
| return CURSOR; | ||
| return { isAgent: true, agent: { name: CURSOR } }; | ||
| } | ||
| if (process.env.CURSOR_AGENT) { | ||
| return CURSOR_CLI; | ||
| return { isAgent: true, agent: { name: CURSOR_CLI } }; | ||
| } | ||
| if (process.env.GEMINI_CLI) { | ||
| return GEMINI; | ||
| return { isAgent: true, agent: { name: GEMINI } }; | ||
| } | ||
| if (process.env.CODEX_SANDBOX) { | ||
| return CODEX; | ||
| return { isAgent: true, agent: { name: CODEX } }; | ||
| } | ||
| if (process.env.CLAUDECODE || process.env.CLAUDE_CODE) { | ||
| return CLAUDE; | ||
| return { isAgent: true, agent: { name: CLAUDE } }; | ||
| } | ||
| if (process.env.REPL_ID) { | ||
| return REPLIT; | ||
| return { isAgent: true, agent: { name: REPLIT } }; | ||
| } | ||
| try { | ||
| await (0, import_promises.access)(devinLocalPath, import_node_fs.constants.F_OK); | ||
| return DEVIN; | ||
| await (0, import_promises.access)(DEVIN_LOCAL_PATH, import_node_fs.constants.F_OK); | ||
| return { isAgent: true, agent: { name: DEVIN } }; | ||
| } catch (error) { | ||
| return false; | ||
| } | ||
| return { isAgent: false, agent: void 0 }; | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| KNOWN_AGENTS, | ||
| determineAgent | ||
| }); |
+29
-1
@@ -1,1 +0,29 @@ | ||
| export declare function determineAgent(): Promise<string | false>; | ||
| declare const CURSOR: "cursor"; | ||
| declare const CURSOR_CLI: "cursor-cli"; | ||
| declare const CLAUDE: "claude"; | ||
| declare const DEVIN: "devin"; | ||
| declare const REPLIT: "replit"; | ||
| declare const GEMINI: "gemini"; | ||
| declare const CODEX: "codex"; | ||
| export type KnownAgentNames = typeof CURSOR | typeof CURSOR_CLI | typeof CLAUDE | typeof DEVIN | typeof REPLIT | typeof GEMINI | typeof CODEX; | ||
| export interface KnownAgentDetails { | ||
| name: KnownAgentNames; | ||
| } | ||
| export type AgentResult = { | ||
| isAgent: true; | ||
| agent: KnownAgentDetails; | ||
| } | { | ||
| isAgent: false; | ||
| agent: undefined; | ||
| }; | ||
| export declare const KNOWN_AGENTS: { | ||
| readonly CURSOR: "cursor"; | ||
| readonly CURSOR_CLI: "cursor-cli"; | ||
| readonly CLAUDE: "claude"; | ||
| readonly DEVIN: "devin"; | ||
| readonly REPLIT: "replit"; | ||
| readonly GEMINI: "gemini"; | ||
| readonly CODEX: "codex"; | ||
| }; | ||
| export declare function determineAgent(): Promise<AgentResult>; | ||
| export {}; |
+28
-11
@@ -21,2 +21,3 @@ "use strict"; | ||
| __export(src_exports, { | ||
| KNOWN_AGENTS: () => KNOWN_AGENTS, | ||
| determineAgent: () => determineAgent | ||
@@ -27,3 +28,3 @@ }); | ||
| var import_node_fs = require("node:fs"); | ||
| const devinLocalPath = "/opt/.devin"; | ||
| const DEVIN_LOCAL_PATH = "/opt/.devin"; | ||
| const CURSOR = "cursor"; | ||
@@ -36,34 +37,50 @@ const CURSOR_CLI = "cursor-cli"; | ||
| const CODEX = "codex"; | ||
| const KNOWN_AGENTS = { | ||
| CURSOR, | ||
| CURSOR_CLI, | ||
| CLAUDE, | ||
| DEVIN, | ||
| REPLIT, | ||
| GEMINI, | ||
| CODEX | ||
| }; | ||
| async function determineAgent() { | ||
| if (process.env.AI_AGENT) { | ||
| return process.env.AI_AGENT; | ||
| const name = process.env.AI_AGENT.trim(); | ||
| if (name) { | ||
| return { | ||
| isAgent: true, | ||
| agent: { name } | ||
| }; | ||
| } | ||
| } | ||
| if (process.env.CURSOR_TRACE_ID) { | ||
| return CURSOR; | ||
| return { isAgent: true, agent: { name: CURSOR } }; | ||
| } | ||
| if (process.env.CURSOR_AGENT) { | ||
| return CURSOR_CLI; | ||
| return { isAgent: true, agent: { name: CURSOR_CLI } }; | ||
| } | ||
| if (process.env.GEMINI_CLI) { | ||
| return GEMINI; | ||
| return { isAgent: true, agent: { name: GEMINI } }; | ||
| } | ||
| if (process.env.CODEX_SANDBOX) { | ||
| return CODEX; | ||
| return { isAgent: true, agent: { name: CODEX } }; | ||
| } | ||
| if (process.env.CLAUDECODE || process.env.CLAUDE_CODE) { | ||
| return CLAUDE; | ||
| return { isAgent: true, agent: { name: CLAUDE } }; | ||
| } | ||
| if (process.env.REPL_ID) { | ||
| return REPLIT; | ||
| return { isAgent: true, agent: { name: REPLIT } }; | ||
| } | ||
| try { | ||
| await (0, import_promises.access)(devinLocalPath, import_node_fs.constants.F_OK); | ||
| return DEVIN; | ||
| await (0, import_promises.access)(DEVIN_LOCAL_PATH, import_node_fs.constants.F_OK); | ||
| return { isAgent: true, agent: { name: DEVIN } }; | ||
| } catch (error) { | ||
| return false; | ||
| } | ||
| return { isAgent: false, agent: void 0 }; | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| KNOWN_AGENTS, | ||
| determineAgent | ||
| }); |
+33
-5
| { | ||
| "name": "@vercel/detect-agent", | ||
| "version": "0.2.0", | ||
| "version": "1.0.0", | ||
| "description": "Detect if code is running in an AI agent or automated development environment", | ||
| "keywords": [ | ||
| "ai", | ||
| "agent", | ||
| "detection", | ||
| "environment", | ||
| "cursor", | ||
| "claude", | ||
| "devin", | ||
| "automation", | ||
| "ai-agent" | ||
| ], | ||
| "license": "Apache-2.0", | ||
| "author": "Vercel", | ||
| "main": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.js" | ||
| } | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public" | ||
| }, | ||
| "homepage": "https://vercel.com/docs", | ||
| "homepage": "https://github.com/vercel/vercel/tree/main/packages/detect-agent#readme", | ||
| "repository": { | ||
@@ -15,4 +36,12 @@ "type": "git", | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/vercel/vercel/issues" | ||
| }, | ||
| "engines": { | ||
| "node": ">=14" | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| "dist", | ||
| "README.md", | ||
| "CHANGELOG.md" | ||
| ], | ||
@@ -28,6 +57,5 @@ "dependencies": {}, | ||
| "build": "node ../../utils/build.mjs", | ||
| "vitest-run": "vitest -c ../../vitest.config.mts", | ||
| "vitest-unit": "glob --absolute 'test/unit/**/*.test.ts' 'test/unit/**/*.test.mts'", | ||
| "test": "vitest -c ../../vitest.config.mts", | ||
| "type-check": "tsc --noEmit" | ||
| } | ||
| } |
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
21999
36.52%7
40%195
46.62%0
-100%0
-100%0
-100%124
Infinity%