From 62358133c43d2c0dce1c844b32f02d469844501d Mon Sep 17 00:00:00 2001 From: simon <4305831+simonfaltum@users.noreply.github.com> Date: Sat, 30 May 2026 14:35:12 +0200 Subject: [PATCH] Detect VS Code agent via VSCODE_AGENT, remove COPILOT_MODEL heuristic (#1444) ## Why [VS Code 1.121](https://code.visualstudio.com/updates/v1_121) introduced the `VSCODE_AGENT` environment variable, set for any agent-initiated terminal command (Copilot Chat agent mode and other agent extensions). This is the official signal for VS Code agent usage. The previous heuristic detected `COPILOT_MODEL` and reported `copilot-vscode`, but `COPILOT_MODEL` is actually set by Copilot CLI users who bring their own key, not specifically by VS Code. This produced false positives and required a BYOK collapse workaround (drop `copilot-vscode` whenever `COPILOT_CLI` was also set). Over the last 13 weeks, the heuristic identified 3 users / 52 events; `copilot-cli` saw 539 users / 175k events. The signal was effectively dead. ## Changes - Add `VSCODE_AGENT` to `_KNOWN_AGENTS`, reported as `agent/vscode-agent`. - Remove the `COPILOT_MODEL` to `copilot-vscode` mapping. - Remove the BYOK collapse logic. `VSCODE_AGENT` can legitimately stack with `COPILOT_CLI` (e.g. running Copilot CLI from a VS Code agent terminal), so the multi-agent path covers it without special handling. ## Tests - [x] Updated `tests/test_user_agent.py`: new `vscode-agent` case, `VSCODE_AGENT + COPILOT_CLI` stacks to `multiple`. - [x] `pytest tests/test_user_agent.py` passes (42 tests). - [x] `make fmt` clean. This PR mirrors parallel changes in [databricks-sdk-go](https://github.com/databricks/databricks-sdk-go/pull/1697), [databricks-sdk-java](https://github.com/databricks/databricks-sdk-java/pull/810), and [sdk-js](https://github.com/databricks/sdk-js/pull/152). NO_CHANGELOG=true --- databricks/sdk/useragent.py | 12 +++--------- tests/test_user_agent.py | 26 +++++++------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/databricks/sdk/useragent.py b/databricks/sdk/useragent.py index 0536bb688..da04a5265 100644 --- a/databricks/sdk/useragent.py +++ b/databricks/sdk/useragent.py @@ -244,9 +244,6 @@ class _AgentRecord: _AgentRecord("CLINE_ACTIVE", "cline"), # https://github.com/cline/cline (v3.24.0+) _AgentRecord("CODEX_CI", "codex"), # https://github.com/openai/codex _AgentRecord("COPILOT_CLI", "copilot-cli"), # https://github.com/features/copilot - _AgentRecord( - "COPILOT_MODEL", "copilot-vscode" - ), # VS Code Copilot terminal, best-effort heuristic, not officially identified _AgentRecord("CURSOR_AGENT", "cursor"), # Closed source _AgentRecord("GEMINI_CLI", "gemini-cli"), # https://google-gemini.github.io/gemini-cli _AgentRecord( @@ -255,6 +252,9 @@ class _AgentRecord: _AgentRecord("KIRO", "kiro"), # https://kiro.dev/ (Amazon) _AgentRecord("OPENCLAW_SHELL", "openclaw"), # https://github.com/anthropics/openclaw _AgentRecord("OPENCODE", "opencode"), # https://github.com/opencode-ai/opencode + _AgentRecord( + "VSCODE_AGENT", "vscode-agent" + ), # Set by VS Code 1.121+ for agent-initiated terminal commands (https://code.visualstudio.com/updates/v1_121) _AgentRecord("WINDSURF_AGENT", "windsurf"), # https://codeium.com/windsurf (Codeium) ] @@ -291,12 +291,6 @@ def agent_provider() -> str: matches = [a.product for a in _KNOWN_AGENTS if a.env_var in os.environ] - # Known BYOK false positive: Copilot CLI users often set COPILOT_MODEL - # alongside COPILOT_CLI. That pair is a single copilot-cli signal, not a - # stacked multi-agent setup. - if "copilot-cli" in matches and "copilot-vscode" in matches: - matches = [m for m in matches if m != "copilot-vscode"] - if len(matches) == 1: _agent_provider = matches[0] elif len(matches) > 1: diff --git a/tests/test_user_agent.py b/tests/test_user_agent.py index c7f440515..fe52675c7 100644 --- a/tests/test_user_agent.py +++ b/tests/test_user_agent.py @@ -235,11 +235,11 @@ def test_agent_provider_augment(clean_useragent_env): assert useragent.agent_provider() == "augment" -def test_agent_provider_copilot_vscode(clean_useragent_env): - os.environ["COPILOT_MODEL"] = "gpt-4" +def test_agent_provider_vscode_agent(clean_useragent_env): + os.environ["VSCODE_AGENT"] = "1" from databricks.sdk import useragent - assert useragent.agent_provider() == "copilot-vscode" + assert useragent.agent_provider() == "vscode-agent" def test_agent_provider_kiro(clean_useragent_env): @@ -334,23 +334,11 @@ def test_agent_provider_explicit_goose_wins_over_agent_cursor(clean_useragent_en assert useragent.agent_provider() == "goose" -def test_agent_provider_copilot_cli_and_vscode_collapses_to_copilot_cli(clean_useragent_env): - # Copilot CLI users (BYOK mode) often set COPILOT_MODEL alongside - # COPILOT_CLI. Treat the pair as a single copilot-cli signal rather than - # a stacked multi-agent setup. +def test_agent_provider_vscode_agent_and_copilot_cli_reports_multiple(clean_useragent_env): + # VSCODE_AGENT can legitimately stack with other agents (e.g. running + # Copilot CLI from a VS Code agent terminal). + os.environ["VSCODE_AGENT"] = "1" os.environ["COPILOT_CLI"] = "1" - os.environ["COPILOT_MODEL"] = "gpt-4" - from databricks.sdk import useragent - - assert useragent.agent_provider() == "copilot-cli" - - -def test_agent_provider_copilot_byok_collapse_then_still_multiple(clean_useragent_env): - # The Copilot BYOK collapse only removes the copilot-vscode match. If - # another agent is also present, the result is still "multiple". - os.environ["COPILOT_CLI"] = "1" - os.environ["COPILOT_MODEL"] = "gpt-4" - os.environ["CLAUDECODE"] = "1" from databricks.sdk import useragent assert useragent.agent_provider() == "multiple"