Surface the invoking AI agent on the CLI's User-Agent - #3632
Open
noamshemesh wants to merge 1 commit into
Open
Conversation
Contributor
|
All contributors have signed the CLA ✍️ ✅ |
RemiBou
approved these changes
Jul 31, 2026
RemiBou
reviewed
Jul 31, 2026
| t.Setenv("CLAUDECODE", "true") | ||
| corecommands.ResetExecutionContextForTest() | ||
|
|
||
| assert.Equal(t, "my-wrapper/9.9.9 (agent:claude)", GetCliUserAgentWithAgent()) |
Contributor
There was a problem hiding this comment.
isnt "agent/claude" more conventional ?
Collaborator
Author
There was a problem hiding this comment.
maybe also ai-agent/claude ? wdyt?
RemiBou
approved these changes
Jul 31, 2026
noamshemesh
force-pushed
the
AGW-86-agent-user-agent
branch
from
July 31, 2026 14:55
5b12a65 to
73a25f8
Compare
jfrog-cli-core already detects which AI harness invoked the CLI, from a table of
harness environment variables (Claude Code, Gemini, Goose, Cursor, Copilot,
Kilocode, Roo Code, Codex, plus a generic AGENT fallback collapsed to
"unknown"). That answer never reached the wire: it fed telemetry labels, prompt
suppression, AI-formatted help and the survey link, but not the request. So an
agent running "jf rt download" and a human running it were byte identical to
any server in the path.
GetCliUserAgentWithAgent appends the detected agent as an additional product
token, so the wire value becomes:
jfrog-cli-go/2.117.0 ai-agent/claude
A product token rather than a comment: it is the native User-Agent shape
(compare "Mozilla/5.0 ... Chrome/120 Safari/537.36"), so anything that splits on
whitespace and reads name/version pairs surfaces it as a structured component
instead of discarding it as comment text. "ai-agent" rather than "agent",
because a bare "agent" would collide with this codebase's existing use of the
word for the CLI itself (SetCliUserAgentName, build-info's agent name).
The product-version slot deliberately carries the harness name, not a version:
the execution-context detector exposes no harness version. If one is ever
wanted it belongs in its own token rather than crammed in here.
The leading product token is unchanged and stays first, so anything parsing only
that is unaffected, and a non-agent invocation is byte identical to today.
JFROG_CLI_USER_AGENT overrides are preserved — the marker is appended to
whatever the override resolves to, never replacing it. Injection-safe by
construction: DetectExecutionContext returns a name from a fixed table or the
literal "unknown", so a raw environment value is never propagated.
Note that execution_context.go already lists "User-Agent enrichment" among its
call sites in a comment, but no such call site existed; this appears to be the
intent that never landed.
The value is attribution metadata, not a credential: harness environment
variables are client-set and trivially forged or unset, so consumers must treat
it as a routing or census hint only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
noamshemesh
force-pushed
the
AGW-86-agent-user-agent
branch
from
July 31, 2026 14:58
73a25f8 to
950a4d0
Compare
Collaborator
Author
|
I have read the CLA Document and I hereby sign the CLA |
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.
What
jfrog-cli-corealready detects which AI harness invoked the CLI, from a table of harnessenvironment variables — Claude Code, Gemini, Goose, Cursor, Copilot, Kilocode, Roo Code, Codex,
plus a generic
AGENTfallback collapsed tounknown(common/commands/execution_context.go).That answer never reached the wire. Its five call sites are telemetry labels, interactive-prompt
suppression, AI-formatted help, the survey link, and the Cursor trace ID — none of them touch the
outbound request. So an agent running
jf rt downloadand a human running it are byte identical toanything in the request path.
This appends the detected agent to the CLI's User-Agent as an RFC 9110 comment:
Why a comment rather than a new header or a rewritten token
jfrog-client-go. The only global per-request header hook thereis
addUberTraceIdHeaderIfSet, so a new header means a second repo and a release the CLI thenbumps to. That remains the durable form; this lands the same signal now with no new plumbing.
JFROG_CLI_USER_AGENToverrides are preserved — the marker is appended to whatever the overrideresolves to, never replacing it.
Worth noting:
execution_context.goalready lists "User-Agent enrichment" among its call sites inthe memoization comment, but no such call site existed. This looks like intent that never landed.
Safety
Injection-safe by construction:
DetectExecutionContextreturns a name from a fixed table or theliteral
unknown, so a raw environment value is never propagated onto the wire. A test asserts noCR/LF can appear.
The value is attribution metadata, not a credential. Harness environment variables are
client-set and trivially forged or unset. Consumers must treat it as a routing/census hint only —
it must never gate authorization.
Tests
15 unit tests in
utils/cliutils:agentEnvDetectorstable, plus the genericAGENT→unknownfallbackJFROG_CLI_USER_AGENToverride preserved with the marker appendedGetCliUserAgentomits the slash)Tests clear the agent env vars and reset the memoized execution context, so they are deterministic
even when the suite itself runs inside Claude Code or Cursor.
go build ./...andgo vetclean.Downstream
JFrog Agent Gateway consumes this marker to attribute diverted requests to a specific harness
rather than a generic "agent", verified end-to-end against a live JPD. That side is already open as
a separate PR and is inert until this ships.
🤖 Generated with Claude Code