Skip to content

Record agentic invocations in User-Agent header#13023

Merged
williammartin merged 1 commit intotrunkfrom
agent-in-header
Mar 24, 2026
Merged

Record agentic invocations in User-Agent header#13023
williammartin merged 1 commit intotrunkfrom
agent-in-header

Conversation

@williammartin
Copy link
Copy Markdown
Member

@williammartin williammartin commented Mar 24, 2026

Description

There has been a significant increase in agentic use of gh in the last few months and it's important we are able to identify that usage. This PR tries to detect which agent is invoking gh by checking well-known environment variables and includes the agent name in the User-Agent header sent to GitHub APIs.

Supported agents: Codex, Gemini CLI, Copilot CLI, OpenCode, Claude Code, and Amp. The generic AI_AGENT env var is also supported with validation ([a-zA-Z0-9_-]) to prevent header injection.

Format

GitHub CLI 2.x.y Agent/<agent-name>

Acceptance Criteria

Given I set GH_DEBUG=api in the environment
When I invoke gh in a shell tool in Claude Code CLI, Gemini CLI, Codex CLI, Copilot CLI, Amp, or OpenCode
Then I see that the User-Agent header in http requests contains the agent name

Running in copilot CLI:

GH_DEBUG=api gh pr view
│ * Request to https://api.github.com/graphql
│ > POST /graphql HTTP/1.1
│ > Host: api.github.com
│ > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
│ > Authorization: token ████████████████████
│ > Content-Length: 461
│ > Content-Type: application/json
│ > Graphql-Features: merge_queue
│ > Time-Zone: Europe/Amsterdam
│ > User-Agent: GitHub CLI v2.88.1-4-gc1daf35db Agent/copilot-cli
│ > X-Github-Api-Version: 2022-11-28

Given I set GH_DEBUG=api in the environment
When I invoke gh directly (in copilot CLI, run with COPILOT_CLI unset in the environment)
Then I see that the User-Agent header in http requests does not contain an agent name

Running in shell:

GH_DEBUG=api gh pr view
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 1968
> Content-Type: application/json; charset=utf-8
> Graphql-Features: merge_queue
> Time-Zone: Europe/Amsterdam
> User-Agent: GitHub CLI 2.88.1
> X-Github-Api-Version: 2022-11-28

)

const (
accept = "Accept"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was unused.

@williammartin williammartin marked this pull request as ready for review March 24, 2026 17:16
@williammartin williammartin requested review from a team as code owners March 24, 2026 17:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds lightweight agent detection (via well-known environment variables) and threads the detected agent name into API client construction so that gh includes an Agent/<name> suffix in the User-Agent header for GitHub API requests, improving visibility into agentic CLI usage.

Changes:

  • Introduces internal/agents with Detect() plus tests for supported agent environment variable signals and precedence.
  • Extends factory and HTTP client plumbing to pass an invokingAgent value into api.NewHTTPClient, appending it to the User-Agent.
  • Updates call sites and tests for the new factory/HTTP client function signatures.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/agents/detect.go Adds agent detection + validation for AI_AGENT values.
internal/agents/detect_test.go Adds unit tests covering detection signals and precedence.
internal/ghcmd/cmd.go Wires detected agent into factory creation at CLI entrypoint.
api/http_client.go Adds InvokingAgent option and appends it to User-Agent.
api/http_client_test.go Adds test asserting agent suffix is included in User-Agent.
pkg/cmd/factory/default.go Threads invokingAgent through factory into HTTP client options.
pkg/cmd/factory/default_test.go Updates factory constructor usage for new parameter.
pkg/cmd/search/shared/shared_test.go Updates factory constructor usage for new parameter.
pkg/cmd/attestation/verify/verify_integration_test.go Updates factory constructor usage for new parameter.
api/client.go Removes an unused header constant.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +52 to +55
ua := fmt.Sprintf("GitHub CLI %s", opts.AppVersion)
if opts.InvokingAgent != "" {
ua = fmt.Sprintf("%s Agent/%s", ua, opts.InvokingAgent)
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opts.InvokingAgent is appended to the User-Agent verbatim. If a future caller passes an untrusted/unsanitized value (e.g. containing \r/\n or other invalid header characters), requests can fail with "invalid header field value" and this undermines the stated header-injection hardening. Consider validating/sanitizing opts.InvokingAgent inside NewHTTPClient (e.g. allow only [A-Za-z0-9_-]+ and otherwise omit the agent suffix), and add a small test for the invalid-value case.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah we sanitize at the agent layer. I don't want to create a gh.AgentName just to deal with this.

Copy link
Copy Markdown
Member

@babakks babakks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

agentOpencode AgentName = "opencode"
)

var validAgentName = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also dots, slashes (/ and ), and brackets? As some agent may decide to add versions (in brackets).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purely based on what we've seen so far out in the wild, I'm inclined to leave this alone until we see something otherwise.

Detect which AI coding agent is invoking gh by checking well-known
environment variables and include the agent name in the User-Agent
header sent to GitHub APIs.

Supported agents: Codex, Gemini CLI, Copilot CLI, OpenCode,
Claude Code, and Amp. Generic AI_AGENT env var is also supported
with validation to prevent header injection.

Fixes github/cli#1111

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@williammartin
Copy link
Copy Markdown
Member Author

Bypassing package security review cause it's not that interesting.

@williammartin williammartin merged commit 1ea2952 into trunk Mar 24, 2026
11 checks passed
@williammartin williammartin deleted the agent-in-header branch March 24, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants