Skip to content

Commit ae4dec7

Browse files
Add agent information to UserAgent (stripe#1803)
1 parent 42db802 commit ae4dec7

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

lib/stripe/api_requestor.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,9 @@ def self.maybe_gc_connection_managers
928928
user_agent = "Stripe/#{api_mode} RubyBindings/#{Stripe::VERSION}"
929929
user_agent += " " + format_app_info(Stripe.app_info) unless Stripe.app_info.nil?
930930

931+
ai_agent = SystemProfiler.detect_ai_agent
932+
user_agent += " AIAgent/#{ai_agent}" unless ai_agent.empty?
933+
931934
headers = {
932935
"User-Agent" => user_agent,
933936
"Authorization" => "Bearer #{req_opts[:api_key]}",
@@ -1089,6 +1092,23 @@ def self.uname_from_system_ver
10891092
"uname lookup failed"
10901093
end
10911094

1095+
AI_AGENTS = [
1096+
["ANTIGRAVITY_CLI_ALIAS", "antigravity"],
1097+
["CLAUDECODE", "claude_code"],
1098+
["CLINE_ACTIVE", "cline"],
1099+
["CODEX_SANDBOX", "codex_cli"],
1100+
["CURSOR_AGENT", "cursor"],
1101+
["GEMINI_CLI", "gemini_cli"],
1102+
["OPENCODE", "open_code"],
1103+
].freeze
1104+
1105+
def self.detect_ai_agent(env = ENV)
1106+
AI_AGENTS.each do |env_var, agent_name|
1107+
return agent_name if env[env_var] && !env[env_var].empty?
1108+
end
1109+
""
1110+
end
1111+
10921112
def initialize
10931113
@uname = self.class.uname
10941114
end
@@ -1097,7 +1117,7 @@ def user_agent
10971117
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} " \
10981118
"(#{RUBY_RELEASE_DATE})"
10991119

1100-
{
1120+
ua = {
11011121
application: Stripe.app_info,
11021122
bindings_version: Stripe::VERSION,
11031123
lang: "ruby",
@@ -1108,6 +1128,11 @@ def user_agent
11081128
uname: @uname,
11091129
hostname: Socket.gethostname,
11101130
}.delete_if { |_k, v| v.nil? }
1131+
1132+
ai_agent = self.class.detect_ai_agent
1133+
ua[:ai_agent] = ai_agent unless ai_agent.empty?
1134+
1135+
ua
11111136
end
11121137
end
11131138

test/stripe/api_requestor_test.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,9 @@ class RequestorTest < Test::Unit::TestCase
797797
context "app_info" do
798798
should "send app_info if set" do
799799
old = Stripe.app_info
800+
801+
APIRequestor::SystemProfiler.stubs(:detect_ai_agent).returns("")
802+
800803
Stripe.set_app_info(
801804
"MyAwesomePlugin",
802805
partner_id: "partner_1234",
@@ -832,6 +835,26 @@ class RequestorTest < Test::Unit::TestCase
832835
end
833836
end
834837

838+
context "ai_agent" do
839+
should "include AI agent in request headers" do
840+
APIRequestor::SystemProfiler.stubs(:detect_ai_agent).returns("cursor")
841+
842+
stub_request(:post, "#{Stripe::DEFAULT_API_BASE}/v1/account")
843+
.with do |req|
844+
assert_match(/AIAgent\/cursor$/, req.headers["User-Agent"])
845+
846+
data = JSON.parse(req.headers["X-Stripe-Client-User-Agent"])
847+
assert_equal "cursor", data["ai_agent"]
848+
849+
true
850+
end.to_return(body: JSON.generate(object: "account"))
851+
852+
client = APIRequestor.new("sk_test_123")
853+
client.send(request_method, :post, "/v1/account", :api,
854+
&@read_body_chunk_block)
855+
end
856+
end
857+
835858
context "error handling" do
836859
should "handle error response with empty body" do
837860
stub_request(:post, "#{Stripe::DEFAULT_API_BASE}/v1/charges")
@@ -1734,5 +1757,23 @@ class SystemProfilerTest < Test::Unit::TestCase
17341757
_ = APIRequestor::SystemProfiler.uname_from_system_ver
17351758
end
17361759
end
1760+
1761+
context ".detect_ai_agent" do
1762+
should "detect agent when env var is set" do
1763+
assert_equal "claude_code", APIRequestor::SystemProfiler.detect_ai_agent({"CLAUDECODE" => "1"})
1764+
end
1765+
1766+
should "return empty string when no agent env vars are set" do
1767+
assert_equal "", APIRequestor::SystemProfiler.detect_ai_agent({})
1768+
end
1769+
1770+
should "return first matching agent when multiple env vars are set" do
1771+
assert_equal "cursor", APIRequestor::SystemProfiler.detect_ai_agent({"CURSOR_AGENT" => "1", "OPENCODE" => "1"})
1772+
end
1773+
1774+
should "ignore empty string env vars" do
1775+
assert_equal "", APIRequestor::SystemProfiler.detect_ai_agent({"CLAUDECODE" => ""})
1776+
end
1777+
end
17371778
end
17381779
end

0 commit comments

Comments
 (0)