Skip to content

RTECO-1017 - Detect agent + CI invocation context, enrich metrics wit…#1555

Open
fluxxBot wants to merge 1 commit intomasterfrom
RTECO-1017-CIdetect
Open

RTECO-1017 - Detect agent + CI invocation context, enrich metrics wit…#1555
fluxxBot wants to merge 1 commit intomasterfrom
RTECO-1017-CIdetect

Conversation

@fluxxBot
Copy link
Copy Markdown
Contributor

@fluxxBot fluxxBot commented May 4, 2026

…h agent/is_agent/is_interactive

  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • All static analysis checks passed.
  • This pull request is on the master branch.
  • I used gofmt for formatting the code before submitting the pull request.

Summary

Adds detection of CLI invocation context — agent (Claude, Cursor, Gemini, Goose, Copilot, Windsurf, etc.), CI provider, and stdin TTY — and enriches the existing
jfcli_commands_count metric with the new dimensions.

Motivation

differentiate human / CI / agent invocations of jf. Backend currently can't tell whether a command came from a developer's desktop, a CI runner, or an AI agent. Without this, we can't measure agent-driven usage or separate human vs automated traffic.

Changes

  • New common/commands/execution_context.go — single source of truth for invocation context. DetectExecutionContext() returns agent name, CI system, TTY presence, and
    propagated trace ID.
  • New common/commands/execution_context_test.go — unit tests covering each agent env var, CI providers, trace ID gating, and no-env fallback.
  • Extended utils/metrics/metrics.goMetricsData gains IsAgent, Agent, IsInteractive fields (additive, omitempty).
  • Updated common/commands/metrics_collector.goCollectMetrics populates the new fields via DetectExecutionContext.

Detection

Agent identity is matched against an env-var table (first match wins, then generic AGENT fallback):

Agent Env vars
claude CLAUDECODE, CLAUDE_CODE_ENTRYPOINT
gemini GEMINI_CLI
goose GOOSE_TERMINAL
cursor CURSOR_AGENT, CURSOR_TRACEID
copilot COPILOT_CLI
kilocode KILO_IPC_SOCKET_PATH, KILO_SERVER_PASSWORD
roo_code ROO_CODE_IPC_SOCKET_PATH
replit REPLIT_AGENT
windsurf WINDSURF_SESSION_ID
aider AIDER_MODEL
codex CODEX_HOME

CI detection reuses the existing detectCISystem() provider list (Jenkins, Travis, CircleCI, GitHub Actions, GitLab, Buildkite, Bamboo, Azure DevOps, TeamCity, Drone, Bitbucket,
AWS CodeBuild, plus generic fallbacks).

TraceID is gated on agent identity — only Cursor's CURSOR_TRACEID is honored, and only when the detected agent is cursor. Prevents stale leakage from outer shells.

@fluxxBot fluxxBot added the new feature Automatically generated release notes label May 4, 2026
Comment on lines +62 to +65
// Fallback: generic AGENT env var (goose convention, codex pending).
if v := os.Getenv("AGENT"); v != "" {
return v
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

instead of falling back lets return "unknown" agent.

// DetectExecutionContext captures all signals about who executed the CLI.
func DetectExecutionContext() ExecutionContext {
ec := ExecutionContext{
IsInteractive: term.IsTerminal(int(os.Stdin.Fd())),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

jfrog-cli core has a common util which can tell whether it is terminal or not we can reuse it.

// Dimensions are independent: an agent may run inside CI; both will be reported.
type ExecutionContext struct {
Agent string // e.g. "claude", "cursor", "gemini", "" if none
CISystem string // e.g. "github_actions", "" if not CI
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

detecting CISystem is already part of existing metrics

{"GOOSE_TERMINAL", "goose"},
{"CURSOR_AGENT", "cursor"},
{"CURSOR_TRACEID", "cursor"},
{"COPILOT_CLI", "copilot"},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

both tests and code should have same source so that if there is any new addition devs doesnt have to update in tests again

CISystem: ec.CISystem,
IsContainer: isRunningInContainer(),
IsAgent: ec.IsAgent,
Agent: ec.Agent,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we also should updated user-agent in http requests with the agent detected.

{"replit", []string{"REPLIT_AGENT"}},
{"windsurf", []string{"WINDSURF_SESSION_ID"}},
{"aider", []string{"AIDER_MODEL"}},
{"codex", []string{"CODEX_HOME"}},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CODEX_HOME — false positive, always-on. Not only when codex is ran.

func detectAgentTraceID(agent string) string {
switch agent {
case "cursor":
return os.Getenv("CURSOR_TRACEID")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the correct env variable is CURSOR_TRACE_ID otherwise we can never fetch trace ID

Suggested change
return os.Getenv("CURSOR_TRACEID")
return os.Getenv("CURSOR_TRACE_ID")

{"kilocode", []string{"KILO_IPC_SOCKET_PATH", "KILO_SERVER_PASSWORD"}},
{"roo_code", []string{"ROO_CODE_IPC_SOCKET_PATH"}},
{"replit", []string{"REPLIT_AGENT"}},
{"windsurf", []string{"WINDSURF_SESSION_ID"}},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

couldnt find windsurf session id in docs is it WINDSURF=1 ?

Suggested change
{"windsurf", []string{"WINDSURF_SESSION_ID"}},
{"windsurf", []string{"WINDSURF"}},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants