Report the client environment on API requests - #100
Closed
kylegani wants to merge 1 commit into
Closed
Conversation
Adds an X-Client-Env header alongside the existing source and version headers, reporting whether the CLI was invoked by a named agent, by CI, or interactively. Deploy attribution currently cannot distinguish an agent-driven deploy from a human one. Detection reads the environment only and never influences behaviour, output defaults or confirmation prompts. DO_NOT_TRACK now also disables error reporting, which previously ignored it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Superseded by #101: rebranched to |
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.
Why
Deploy attribution today cannot tell an agent-driven deploy from a human one.
X-Source: cliandX-CLI-Versionare already sent on every request, but neither says what invoked the CLI, so every automated deploy lands in the same bucket as an engineer at a terminal. The mission-control agents page reports a ~32% automation share of deploying principals against a ~70% automation share of build volume, and that gap is currently an undifferentiated blob: service accounts used by CI, by coding agents, and by internal tooling all look identical.What
A new
internal/clientenvpackage classifies the invoking environment and emits it asX-Client-Envalongside the existing source and version headers. Three shapes:agent:<name>for a detected coding agent (claude, cowork, cursor, cursor-cli, codex, gemini, antigravity, augment-cli, opencode, github-copilot, devin, plus anything self-declared viaAI_AGENT)cifor a CI system (CIplus 13 vendor-specific variables)interactiveotherwiseThe header goes on the JSON API client, the multipart
RunApppath, and both device-auth endpoints. Value is computed once per process viasync.Once.Design constraints held
Detection never influences behaviour. No output-format default, no confirmation-prompt suppression, no branch anywhere reads this classification. It is observational only, which is why the non-TTY deploy guard shipped in #87 keys off stdin TTY-ness rather than agent detection.
Detecttakes its environment lookup and filesystem probe as parameters so the whole matrix is testable without touching the real environment.AI_AGENTvalues are untrusted. Lowercased, restricted to[a-z0-9._-], capped at 64 characters. A value that sanitizes to nothing becomesagent:unknownrather than silently degrading tointeractive, so a declared-but-unparseable agent still lands in the agent bucket.REPL_IDis deliberately not a detector. It reports that the process runs on Replit, not that an agent drives it, so keying on it would classify humans working in the Replit editor as agents. A Replit agent that setsAI_AGENTis still detected. This reverses an earlier draft of this branch that mappedREPL_IDtoagent:replit; the test now pins the corrected behaviour both ways.Agent detection precedes CI detection, because an agent running inside a CI job is more specifically an agent, and the hosted-agent surfaces we care about all set
CItoo.Rider:
DO_NOT_TRACKpkg/bugsnagpreviously ignoredDO_NOT_TRACKand only honoured the CLI's own telemetry config. It now disables error reporting whenDO_NOT_TRACKis set to anything other than empty,0, orfalse, matching the config-based path.X-Client-Envitself is still sent underDO_NOT_TRACK. It carries no identity, no machine identifier and no path: it is one enum value on an already-authenticated request, in the same class as theX-SourceandX-CLI-Versionheaders that have always been sent unconditionally. Flagging it here as a deliberate call rather than an oversight.Validation
go test ./internal/clientenv/... ./internal/api/... ./internal/auth/... ./pkg/bugsnag/...green. 30+ detection cases, header-presence tests on both the API client and the OAuth endpoints, and aDO_NOT_TRACKmatrix.gofmtclean.golangci-linton the touched packages reports one finding,nilerrininternal/auth/docker.go, verified pre-existing by re-running against a stash of this branch.make lintandgo build ./...fail on vendoredcortex/server/node_modulesAWS CDK templates, unrelated to this change and present onmain.Depends on
Nothing to work, but the header is only stored once dashboard-backend PR #4126 lands. Until then it is accepted and discarded.