Add AI agent detection to user-agent header#88
Merged
Conversation
Adds detection for 15 AI coding agents (amp, antigravity, augment, claude-code, cline, codex, copilot-cli, copilot-vscode, cursor, gemini-cli, goose, kiro, openclaw, opencode, windsurf) to the core user-agent. The detected product name is emitted as a single agent/<name> segment by createDefault so that Databricks can understand which agents are invoking the SDK. Detection honors the agents.md standard AGENT env var with an unknown fallback, and resolves ambiguity conservatively by emitting no segment when two explicit matchers fire at once. Explicit product env vars always take precedence over AGENT=<name>. Behavior matches the parallel changes in databricks-sdk-go #1637, databricks-sdk-java #768, and databricks-sdk-py #1394. Signed-off-by: simon <simon.faltum@databricks.com>
parthban-db
reviewed
Apr 20, 2026
Nested agents (e.g. a Cursor CLI subagent spawned by Claude Code) set multiple agent env vars on the same process. The previous ambiguity guard silently dropped the signal in that case. Report "multiple" instead so the stacked case is visible in telemetry. Also collapse the known BYOK false positive where Copilot CLI users have COPILOT_MODEL set alongside COPILOT_CLI: that pair now reports "copilot-cli" rather than "multiple". Co-authored-by: Isaac Signed-off-by: simon <simon.faltum@databricks.com>
parthban-db
approved these changes
Apr 20, 2026
parthban-db
left a comment
Contributor
There was a problem hiding this comment.
LGTM modulo comment.
| export {ClientInfo, ClientInfoError} from './clientinfo'; | ||
| export {addToDefault, setPartner, setProduct} from './base'; | ||
| export {createDefault} from './default'; | ||
| export {agentProvider} from './agent'; |
Contributor
There was a problem hiding this comment.
Why do we expose this? I don't think users need to access it.
simonfaltum
added a commit
that referenced
this pull request
Apr 20, 2026
agentProvider is an internal detail of clientinfo. The only consumer inside the SDK (default.ts) imports it directly from ./agent, so the public re-export had no user-facing purpose. Addresses review feedback from #88. Co-authored-by: Isaac Signed-off-by: simon <simon.faltum@databricks.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds detection for 15 AI coding agents (amp, antigravity, augment, claude-code, cline, codex, copilot-cli, copilot-vscode, cursor, gemini-cli, goose, kiro, openclaw, opencode, windsurf) so the SDK emits a single
agent/<name>segment in its user-agent string when an agent is identified. Mirrors parallel work in the Go (databricks/databricks-sdk-go#1637), Java (databricks/databricks-sdk-java#768), and Python (databricks/databricks-sdk-py#1394) SDKs so all four SDKs ship the same canonical list and precedence rules.Why
Databricks wants visibility into which AI coding agents are calling our APIs so that we can understand adoption, prioritize fixes for the environments our customers use, and detect compatibility issues early. The three sibling SDKs just landed this feature; the JS SDK has a smaller detection list (9 agents), emits one segment per detected agent instead of a single canonical segment, and does not honor the
AGENT=<name>standard from agents.md. Without this change, traffic from JS SDK users running inside agents is invisible or reported inconsistently with the other SDKs.The library policy in
.agent/rules/libraries.mdcprefers picking a dependency over hand-rolling. We intentionally deviate here: the canonical agent list, env var names, and precedence rules are coordinated across four SDKs, and existing libraries (std-env,@vercel/detect-agent) cover different subsets of agents, apply different precedence, and would re-introduce drift the moment we add a new agent. Implementation is ~80 lines with zero dependencies and matches the Go/Java/Python implementations.What changed
Interface changes
packages/core/src/clientinfo/agent.ts(new) - ExportsagentProvider()(cached for the process lifetime) andlookupAgentProvider()(uncached, primarily for tests).clearAgentCache()is exported from the module file (not the barrel) for tests only, matching the pattern documented in.agent/rules/testing.mdcfor intentionally-unbarreled symbols.packages/core/src/clientinfo/index.ts- AddsagentProviderto the public barrel.Behavioral changes
createDefault()now appends at most oneagent/<name>segment instead of one per matching env var. When two explicit matchers fire simultaneously (ambiguity), noagent/segment is emitted.AGENT=<name>is now honored as a fallback. When no explicit env var matches,AGENT=<known-product>maps to that product, any other non-emptyAGENTvalue maps toagent/unknown, and an empty or unsetAGENTemits nothing.AGENT=<name>(e.g.CLAUDECODE=1+AGENT=goosereportsclaude-code).sync.Once, Java's volatile lazy init, and Python's_agent_providersentinel.Internal changes
KNOWN_AGENTSlist anddetectAgents()function inpackages/core/src/clientinfo/default.tsmove to the new module.default.test.tstest casemultiple agents all reportedis replaced bymultiple agents are ambiguous and omit the agent segmentto reflect the new ambiguity semantics. Two new cases cover theAGENTfallback path. AddsclearAgentCache()calls inbeforeEach/afterEachsince detection is now cached.packages/core/vitest.config.browser.tsexcludestests/clientinfo/agent.test.tsfor the same reasondefault.test.tsis excluded: agent detection readsprocess.envand is Node-only.How is this tested?
packages/core/tests/clientinfo/agent.test.tsmirrors the Go test cases fromuseragent/agent_test.go: every agent detected via its primary env var, empty-string env values counting as set, ambiguity when two explicit matchers fire,AGENTfallback for known and unknown values, explicit env vars winning overAGENT=<name>, the pinnedCOPILOT_CLI+COPILOT_MODELambiguity case for Copilot CLI BYOK users, and cache persistence after env changes.npm run format:check,npm run lint,npm run typecheck,npm test, andnpm run test:browserall pass. Core package runs 240 unit tests (29 new) and 150 browser tests.