From d9ec30598af2e9ad6acd70bcf0b69975670b14cb Mon Sep 17 00:00:00 2001 From: Lars Moan Date: Mon, 14 Sep 2026 19:24:43 +0000 Subject: [PATCH 1/3] fix: avoid sudo after local ucode updates --- src/ucode/agents/claude.py | 2 +- tests/test_agent_claude.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ucode/agents/claude.py b/src/ucode/agents/claude.py index b5cc7207..239c372e 100644 --- a/src/ucode/agents/claude.py +++ b/src/ucode/agents/claude.py @@ -363,7 +363,7 @@ def render_overlay( # traffic to ucode. header_lines = [ "x-databricks-use-coding-agent-mode: true", - f"User-Agent: ucode/{ucode_version()} claude/{agent_version('claude')}", + f"User-Agent: ucode/{ucode_version().split('+', 1)[0]} claude/{agent_version('claude')}", ] if provider: header_lines.append(f"{MODEL_PROVIDER_SERVICE_HEADER}: {provider}") diff --git a/tests/test_agent_claude.py b/tests/test_agent_claude.py index 4a8a0ac1..f24fbc19 100644 --- a/tests/test_agent_claude.py +++ b/tests/test_agent_claude.py @@ -456,6 +456,16 @@ def _ua(self, monkeypatch) -> str: def test_user_agent_present(self, monkeypatch): assert "User-Agent: ucode/0.1.0 claude/2.1.136" in self._ua(monkeypatch) + def test_user_agent_omits_local_build_metadata(self, monkeypatch): + monkeypatch.setattr(claude, "ucode_version", lambda: "0.1.0+172.g673cff4") + monkeypatch.setattr(claude, "agent_version", lambda binary: "2.1.136") + + overlay, _ = claude.render_overlay(WS, "s4") + + assert ( + "User-Agent: ucode/0.1.0 claude/2.1.136" in overlay["env"]["ANTHROPIC_CUSTOM_HEADERS"] + ) + def test_existing_databricks_header_preserved(self, monkeypatch): assert "x-databricks-use-coding-agent-mode: true" in self._ua(monkeypatch) From 2490321ee1394563db4c53ed80655c49c233f601 Mon Sep 17 00:00:00 2001 From: Lars Moan Date: Mon, 14 Sep 2026 19:35:34 +0000 Subject: [PATCH 2/3] fix: align Claude user agent tests --- src/ucode/agents/claude.py | 4 ++-- src/ucode/telemetry.py | 5 +++++ tests/test_agent_claude.py | 6 +++--- tests/test_e2e_user_agent.py | 5 +++-- tests/test_telemetry.py | 7 +++++++ 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ucode/agents/claude.py b/src/ucode/agents/claude.py index 239c372e..8c99bbb1 100644 --- a/src/ucode/agents/claude.py +++ b/src/ucode/agents/claude.py @@ -55,7 +55,7 @@ sync_smart_routing_hooks, ) from ucode.state import MANAGED_OVERLAY_KEY, is_tool_managed, mark_tool_managed, save_state -from ucode.telemetry import agent_version, ucode_version +from ucode.telemetry import agent_version, ucode_release_version from ucode.tracing import tracing_env from ucode.ui import print_note, print_success, print_warning @@ -363,7 +363,7 @@ def render_overlay( # traffic to ucode. header_lines = [ "x-databricks-use-coding-agent-mode: true", - f"User-Agent: ucode/{ucode_version().split('+', 1)[0]} claude/{agent_version('claude')}", + f"User-Agent: ucode/{ucode_release_version()} claude/{agent_version('claude')}", ] if provider: header_lines.append(f"{MODEL_PROVIDER_SERVICE_HEADER}: {provider}") diff --git a/src/ucode/telemetry.py b/src/ucode/telemetry.py index 639ddae9..0dc18ea8 100644 --- a/src/ucode/telemetry.py +++ b/src/ucode/telemetry.py @@ -25,6 +25,11 @@ def ucode_version() -> str: return "unknown" +def ucode_release_version() -> str: + """Return the package release version without local build metadata.""" + return ucode_version().split("+", 1)[0] + + @cache def agent_version(binary: str) -> str: """Return the agent CLI's reported version, or "unknown" on any failure. diff --git a/tests/test_agent_claude.py b/tests/test_agent_claude.py index f24fbc19..26f9cf34 100644 --- a/tests/test_agent_claude.py +++ b/tests/test_agent_claude.py @@ -448,7 +448,7 @@ def test_managed_keys_include_env_entries(self): class TestRenderOverlayUserAgent: def _ua(self, monkeypatch) -> str: - monkeypatch.setattr(claude, "ucode_version", lambda: "0.1.0") + monkeypatch.setattr(claude, "ucode_release_version", lambda: "0.1.0") monkeypatch.setattr(claude, "agent_version", lambda binary: "2.1.136") overlay, _ = claude.render_overlay(WS, "s4") return overlay["env"]["ANTHROPIC_CUSTOM_HEADERS"] @@ -457,7 +457,7 @@ def test_user_agent_present(self, monkeypatch): assert "User-Agent: ucode/0.1.0 claude/2.1.136" in self._ua(monkeypatch) def test_user_agent_omits_local_build_metadata(self, monkeypatch): - monkeypatch.setattr(claude, "ucode_version", lambda: "0.1.0+172.g673cff4") + monkeypatch.setattr(claude, "ucode_release_version", lambda: "0.1.0") monkeypatch.setattr(claude, "agent_version", lambda binary: "2.1.136") overlay, _ = claude.render_overlay(WS, "s4") @@ -863,7 +863,7 @@ def test_managed_file_merges_anthropic_custom_headers(self, monkeypatch): } } self._patch(monkeypatch, private_writes, managed_writes, existing_managed_settings) - monkeypatch.setattr(claude, "ucode_version", lambda: "1.0") + monkeypatch.setattr(claude, "ucode_release_version", lambda: "1.0") monkeypatch.setattr(claude, "agent_version", lambda _binary: "2.0") state = {"workspace": WS, "codex_models": []} diff --git a/tests/test_e2e_user_agent.py b/tests/test_e2e_user_agent.py index 55f2f1d7..4296f49b 100644 --- a/tests/test_e2e_user_agent.py +++ b/tests/test_e2e_user_agent.py @@ -23,7 +23,7 @@ import pytest -from ucode.telemetry import agent_version, ucode_version +from ucode.telemetry import agent_version, ucode_release_version, ucode_version def _require_binary(binary: str): @@ -126,7 +126,8 @@ def capture_server(): def _expected_ua(agent_name: str, binary: str) -> str: - return f"ucode/{ucode_version()} {agent_name}/{agent_version(binary)}" + version = ucode_release_version() if agent_name == "claude" else ucode_version() + return f"ucode/{version} {agent_name}/{agent_version(binary)}" def _assert_ua(req: _CapturedRequest, expected: str) -> None: diff --git a/tests/test_telemetry.py b/tests/test_telemetry.py index a60a7863..1a35b522 100644 --- a/tests/test_telemetry.py +++ b/tests/test_telemetry.py @@ -15,6 +15,13 @@ def test_returns_string(self): assert telemetry.ucode_version() != "" +class TestUcodeReleaseVersion: + def test_drops_local_build_metadata(self, monkeypatch): + monkeypatch.setattr(telemetry, "ucode_version", lambda: "0.1.0+172.g673cff4") + + assert telemetry.ucode_release_version() == "0.1.0" + + class TestAgentVersion: def setup_method(self): # The helper is @cache'd; clear between tests so each gets a clean run. From 5067dc68f261973a92df7a9787223446614fe3d5 Mon Sep 17 00:00:00 2001 From: Lars Moan Date: Tue, 15 Sep 2026 11:50:30 +0000 Subject: [PATCH 3/3] fix: keep Claude managed settings stable --- src/ucode/agents/claude.py | 2 +- tests/test_agent_claude.py | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ucode/agents/claude.py b/src/ucode/agents/claude.py index 8c99bbb1..8de3ea7f 100644 --- a/src/ucode/agents/claude.py +++ b/src/ucode/agents/claude.py @@ -363,7 +363,7 @@ def render_overlay( # traffic to ucode. header_lines = [ "x-databricks-use-coding-agent-mode: true", - f"User-Agent: ucode/{ucode_release_version()} claude/{agent_version('claude')}", + f"User-Agent: ucode/{ucode_release_version()}", ] if provider: header_lines.append(f"{MODEL_PROVIDER_SERVICE_HEADER}: {provider}") diff --git a/tests/test_agent_claude.py b/tests/test_agent_claude.py index 26f9cf34..eda66055 100644 --- a/tests/test_agent_claude.py +++ b/tests/test_agent_claude.py @@ -447,24 +447,20 @@ def test_managed_keys_include_env_entries(self): class TestRenderOverlayUserAgent: - def _ua(self, monkeypatch) -> str: + def _ua(self, monkeypatch, claude_version: str = "2.1.136") -> str: monkeypatch.setattr(claude, "ucode_release_version", lambda: "0.1.0") - monkeypatch.setattr(claude, "agent_version", lambda binary: "2.1.136") + monkeypatch.setattr(claude, "agent_version", lambda binary: claude_version) overlay, _ = claude.render_overlay(WS, "s4") return overlay["env"]["ANTHROPIC_CUSTOM_HEADERS"] def test_user_agent_present(self, monkeypatch): - assert "User-Agent: ucode/0.1.0 claude/2.1.136" in self._ua(monkeypatch) + assert "User-Agent: ucode/0.1.0" in self._ua(monkeypatch) - def test_user_agent_omits_local_build_metadata(self, monkeypatch): - monkeypatch.setattr(claude, "ucode_release_version", lambda: "0.1.0") - monkeypatch.setattr(claude, "agent_version", lambda binary: "2.1.136") - - overlay, _ = claude.render_overlay(WS, "s4") + def test_user_agent_does_not_change_when_claude_updates(self, monkeypatch): + user_agent = self._ua(monkeypatch, "2.1.272") - assert ( - "User-Agent: ucode/0.1.0 claude/2.1.136" in overlay["env"]["ANTHROPIC_CUSTOM_HEADERS"] - ) + assert user_agent == "x-databricks-use-coding-agent-mode: true\nUser-Agent: ucode/0.1.0" + assert "claude/" not in user_agent def test_existing_databricks_header_preserved(self, monkeypatch): assert "x-databricks-use-coding-agent-mode: true" in self._ua(monkeypatch) @@ -873,7 +869,7 @@ def test_managed_file_merges_anthropic_custom_headers(self, monkeypatch): merged_headers = json.loads(text)["env"]["ANTHROPIC_CUSTOM_HEADERS"] assert merged_headers.splitlines() == [ "X-Enterprise-Header: retain", # Preserved from existing managed settings. - "User-Agent: ucode/1.0 claude/2.0", # From ucode; overwrites existing. + "User-Agent: ucode/1.0", # From ucode; overwrites existing. "x-databricks-use-coding-agent-mode: true", # Newly added by ucode. ]