telemetry: add agentName to anonymous metadata#95586
Conversation
Adds `agentName: string | null` to the anonymous telemetry meta block (alongside ciName, nextVersion, etc.), reporting the detected AI coding agent or null. - Vendor @vercel/detect-agent into compiled and wrap its async determineAgent() in a memoized getAgentName() helper (caches the promise; detection can't change over the process lifetime). - Make getAnonymousMeta() async and include the field; also surface the meta block in NEXT_TELEMETRY_DEBUG output. - Drop the duplicate sync detect-agent.ts; the AGENTS.md auto-gen hook now shares the same vendored detection.
Tests PassedCommit: f8d7f60 |
Stats from current PR🔴 1 regression, 1 improvement
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URLCommit: f8d7f60 |
|
heads up @gaojude this replaces the manually copied agent detection logic with a vendored copy, like you suggested. I integrated that into your AGENT.md generation code, so any time we change the upstream package you can just update the vendor version. |
| * Detection is delegated to `@vercel/detect-agent`, which performs an async | ||
| * filesystem probe (e.g. for Devin) in addition to environment-variable checks, | ||
| * so it can be relatively expensive. The detected agent cannot change over the |
There was a problem hiding this comment.
It seems a little silly that detectAgent is async at all: https://github.com/vercel/vercel/blob/e12b1bd68c6e69ab1065dda89c9209ae1bdfb549/packages/detect-agent/src/index.ts#L146
It could just trust that if DEVIN_LOCAL_PATH is set, that we're in Devin, I don't really understand why it needs to also look for the file.
The fact that it's async makes this code a bit more annoying, and is also requiring the callsites to become async, which is all unfortunate.
There was a problem hiding this comment.
Nod. I'm just trusting the CLI team knows what they're doing here. It's frustrating that only one agent causes the whole thing to be async, but we're really lucky that all callsites were async / writing to the FS anyways.
| sessionId: this.sessionId, | ||
| }, | ||
| meta: getAnonymousMeta(), | ||
| meta: await getAnonymousMeta(), |
There was a problem hiding this comment.
If NEXT_TELEMETRY_DEBUG is set, we're now computing this twice? I guess that's okay because NEXT_TELEMETRY_DEBUG isn't normally set, but it's gross.
There was a problem hiding this comment.
I think we're the only people who run NEXT_TELEMETRY_DEBUG
| // NOTE: This file duplicates the detection helper added in | ||
| // https://github.com/vercel/next.js/pull/91854 (branch: imm/agent-telemetry). | ||
| // When that PR lands, whichever branch merges second should collapse the | ||
| // duplicate — the contents are intentionally identical so the merge is a | ||
| // no-op. The env variable checks mirror the same functionality in the | ||
| // `vercel` CLI. |
| env: { | ||
| AI_AGENT: '', | ||
| CURSOR_TRACE_ID: '', | ||
| CURSOR_AGENT: '', | ||
| GEMINI_CLI: '', | ||
| CODEX_SANDBOX: '', | ||
| CODEX_CI: '', | ||
| CODEX_THREAD_ID: '', | ||
| ANTIGRAVITY_AGENT: '', | ||
| AUGMENT_AGENT: '', | ||
| OPENCODE_CLIENT: '', | ||
| CLAUDECODE: '', | ||
| CLAUDE_CODE: '', | ||
| REPL_ID: '', | ||
| COPILOT_MODEL: '', | ||
| COPILOT_ALLOW_ALL: '', | ||
| COPILOT_GITHUB_TOKEN: '', |
There was a problem hiding this comment.
Can we use the KNOWN_AGENTS export from the detect-agent package? https://github.com/vercel/vercel/blob/e12b1bd68c6e69ab1065dda89c9209ae1bdfb549/packages/detect-agent/src/index.ts#L50
Add agentName to anonymous telemetry metadata
What
Adds an agentName: string | null field to the anonymous telemetry metadata block that accompanies every payload, alongside existing fields like ciName, nextVersion, and isCI. The value is the detected AI coding agent driving the process (e.g. claude, cursor, codex), or null when none is detected.
Why
We currently have no visibility into how much Next.js development happens under AI coding agents. This field lets us understand that split in the same anonymous way we already track other CLI usage.
How
Agent detection via @vercel/detect-agent
next/dist/compiled/@vercel/detect-agent(new taskfile ncc task, externals entry, and type declaration), matching how I understand other third-party deps are bundled.telemetry/agent-name.ts.getAgentName()caches the promise fromdetermineAgent(): the first call runs detection once, concurrent callers share that in-flight promise, and every later call resolves against the already-settled result. The agent shouldn't change over the process lifetime, so this is safe. Detection failures resolve to null so telemetry never throws on this path.Wiring into the payload
getAnonymousMeta()is now async and includesagentName: await getAgentName().storage.tsawaits it on the send path (the record method was thankfully already async).Consolidating agent detection
getAgentName(). Now we have a single source of truth and can easily get upstream changes from the vercel CLI package.ensureAgentRulesForDevbecame async, its only caller in start-server.ts was already in an async scope, so this is easy.Testing