feat: add cua meta-package and unify telemetry opt-out - #1225
Conversation
Meta-package (pip install cua): - New libs/python/cua package exposing unified API: from cua import Sandbox, Image, ComputerAgent - Depends on cua-sandbox, cua-agent[cloud], cua-cli - cua-agent surface uses lazy __getattr__ imports to avoid import-time side effects when only sandbox symbols are needed - .bumpversion.cfg, cd-py-cua.yml publish workflow, and pypi/cua entry in release-bump-version.yml Telemetry: - Unify opt-out: CUA_TELEMETRY_ENABLED=false is now canonical for both PostHog and OTEL; CUA_TELEMETRY_DISABLED emits a DeprecationWarning and is honoured for backwards compatibility - Move installation ID from site-packages to ~/.config/cua/ so it survives upgrades and is shared across venvs - Add cua-core dep to cua-sandbox; instrument sandbox lifecycle with sandbox_create and sandbox_destroy PostHog events; add telemetry_enabled param to create/connect/ephemeral - Instrument cua-cli: cli_command event on every invocation via try/finally (command, subcommand, status, duration_seconds) - Fix TESTING.md to use CUA_TELEMETRY_ENABLED=false
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📦 Publishable packages changed
Add |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR introduces a unified Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
libs/python/core/core/telemetry/posthog.py (2)
82-84: Consider migrating existing installation IDs from the old location.The installation ID storage moved from
site-packages/.storage/to~/.config/cua/. Existing users will get a new installation ID after upgrading, which may affect telemetry continuity. If preserving installation ID continuity matters, consider checking the old location as a fallback before creating a new ID.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/python/core/core/telemetry/posthog.py` around lines 82 - 84, The code currently creates a new installation ID at config_dir = Path.home() / ".config" / "cua" and writes id_file = config_dir / "installation_id"; update the logic that reads/creates the installation ID to first check the old storage location (e.g., Path(__file__).resolve().parents[...] or the package's site-packages/.storage/installation_id) as a fallback: if an ID file exists in the legacy path, copy or move that value into the new id_file and use it, otherwise generate and write a new ID as before; modify the function that handles installation ID creation/loading (the block using config_dir and id_file) to implement this fallback and ensure atomic write and proper permissions when migrating the legacy file.
52-71: Consider emitting the deprecation warning only once, consistent with otel.py.Same issue as in
otel.py— the warning is emitted on every call tois_telemetry_enabled(). The pipeline failures show this warning appearing multiple times during test runs. A module-level flag would prevent warning spam while still informing users about the deprecation.♻️ Proposed fix to warn only once
+_deprecation_warned = False + + class PostHogTelemetryClient: """Collects and reports telemetry data via PostHog.""" # Global singleton (class-managed) _singleton: Optional["PostHogTelemetryClient"] = None + _deprecation_warned: bool = False ... `@classmethod` def is_telemetry_enabled(cls) -> bool: """True if telemetry is currently active for this process. ... """ # Deprecated env var: CUA_TELEMETRY_DISABLED disabled_val = os.environ.get("CUA_TELEMETRY_DISABLED", "") if disabled_val: import warnings - warnings.warn( - "CUA_TELEMETRY_DISABLED is deprecated. " - "Use CUA_TELEMETRY_ENABLED=false instead.", - DeprecationWarning, - stacklevel=2, - ) + if not cls._deprecation_warned: + warnings.warn( + "CUA_TELEMETRY_DISABLED is deprecated. " + "Use CUA_TELEMETRY_ENABLED=false instead.", + DeprecationWarning, + stacklevel=2, + ) + cls._deprecation_warned = True if disabled_val.lower() in {"1", "true", "yes", "on"}: return False🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/python/core/core/telemetry/posthog.py` around lines 52 - 71, The deprecation warning in is_telemetry_enabled() currently fires on every call; add a module-level boolean flag (e.g., _warned_cua_telemetry_deprecated = False) and update is_telemetry_enabled() to check that flag and only call warnings.warn once, setting the flag to True after the first warn; mirror the pattern used in otel.py so subsequent calls skip emitting the DeprecationWarning while preserving the same warning message and stacklevel.libs/python/core/core/telemetry/otel.py (2)
108-110: Update log message to reflect the canonical opt-out variable.The debug message still references the deprecated
CUA_TELEMETRY_DISABLEDvariable. Consider updating it to reference the canonicalCUA_TELEMETRY_ENABLEDvariable.♻️ Proposed fix
if not is_otel_enabled(): - logger.debug("OpenTelemetry disabled via CUA_TELEMETRY_DISABLED") + logger.debug("OpenTelemetry disabled via environment variable") return False🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/python/core/core/telemetry/otel.py` around lines 108 - 110, The logger.debug message in is_otel_enabled() incorrectly mentions the deprecated CUA_TELEMETRY_DISABLED variable; update the debug text to reference the canonical CUA_TELEMETRY_ENABLED variable and its false state (e.g., "OpenTelemetry disabled via CUA_TELEMETRY_ENABLED=false") so the log matches the actual opt-out mechanism checked by the is_otel_enabled() function.
55-73: Consider emitting the deprecation warning only once.The warning is emitted on every call to
is_otel_enabled()whenCUA_TELEMETRY_DISABLEDis set. This could cause warning spam, especially since this function is called during OTEL initialization and potentially from instrumented functions. Consider using a module-level flag to emit the warning only once.♻️ Proposed fix to warn only once
+_deprecation_warned = False + def is_otel_enabled() -> bool: """Check if OpenTelemetry is enabled. Canonical opt-out: ``CUA_TELEMETRY_ENABLED=false``. ``CUA_TELEMETRY_DISABLED`` is deprecated — a warning is emitted on first use and the value is honoured for backwards compatibility. """ + global _deprecation_warned import warnings disabled_val = os.environ.get("CUA_TELEMETRY_DISABLED", "") if disabled_val: - warnings.warn( - "CUA_TELEMETRY_DISABLED is deprecated. " - "Use CUA_TELEMETRY_ENABLED=false instead.", - DeprecationWarning, - stacklevel=2, - ) + if not _deprecation_warned: + warnings.warn( + "CUA_TELEMETRY_DISABLED is deprecated. " + "Use CUA_TELEMETRY_ENABLED=false instead.", + DeprecationWarning, + stacklevel=2, + ) + _deprecation_warned = True if disabled_val.lower() in {"1", "true", "yes", "on"}: return False🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/python/core/core/telemetry/otel.py` around lines 55 - 73, The deprecation warning for CUA_TELEMETRY_DISABLED is emitted on every call to is_otel_enabled(), causing spam; add a module-level boolean flag (e.g., _cua_telemetry_deprecation_warned = False) and only call warnings.warn when that flag is False, then set the flag to True after warning so subsequent calls skip emitting it; update the logic inside is_otel_enabled() (or the function containing the shown diff) to check and set this flag before returning..github/workflows/cd-py-cua.yml (2)
24-25: Consider using ubuntu-latest for the prepare job.The prepare job only extracts the version string using bash. Using
macos-latestis more expensive thanubuntu-latestand offers no benefit here.♻️ Suggested change
prepare: - runs-on: macos-latest + runs-on: ubuntu-latest🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/cd-py-cua.yml around lines 24 - 25, The prepare job currently uses macos-latest (see prepare and runs-on) which is unnecessary and costly since it only runs a bash extraction; change the prepare job's runs-on value from macos-latest to ubuntu-latest to use the cheaper Linux runner while preserving the job steps and behavior.
34-48: Minor: Redundant version check can be simplified.The check on line 43 for
github.event.inputs.versionis redundant becauseinputs.version(line 34) already covers bothworkflow_callandworkflow_dispatchtriggers in GitHub Actions. The logic still works correctly, but could be simplified.♻️ Simplified version logic
if [ -n "${{ inputs.version }}" ]; then VERSION=${{ inputs.version }} elif [ "${{ github.event_name }}" == "push" ]; then if [[ "${{ github.ref }}" =~ ^refs/tags/cua-v([0-9]+\.[0-9]+\.[0-9]+) ]]; then VERSION=${BASH_REMATCH[1]} else echo "Invalid tag format for cua" exit 1 fi - elif [ -n "${{ github.event.inputs.version }}" ]; then - VERSION=${{ github.event.inputs.version }} else echo "ERROR: No version found!" exit 1 fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/cd-py-cua.yml around lines 34 - 48, The version selection block redundantly checks github.event.inputs.version after already checking inputs.version; simplify by removing the redundant elif that tests [ -n "${{ github.event.inputs.version }}" ] and instead rely on the initial inputs.version check, keeping the push tag branch (the regex using github.ref and BASH_REMATCH to set VERSION) and the final error branch; update only the conditional flow around VERSION so inputs.version is the single source for workflow inputs and no duplicate github.event.inputs.version check remains.libs/python/cua/pyproject.toml (1)
28-28: Consider relaxing the upper Python version bound.The constraint
<3.14will require a package update when Python 3.14 is released. Unless there's a known incompatibility, consider>=3.12without an upper bound, or use a more permissive upper bound like<4.0.♻️ Suggested change
-requires-python = ">=3.12,<3.14" +requires-python = ">=3.12"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/python/cua/pyproject.toml` at line 28, The pyproject.toml requires-python constraint is overly strict ("requires-python = '>=3.12,<3.14'"); update the requires-python entry to relax the upper bound (for example remove the upper bound to ">=3.12" or use a permissive upper bound like ">=3.12,<4.0") so the package won't need an immediate update when Python 3.14 is released; locate and change the requires-python line in pyproject.toml accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@libs/python/cua/cua/__init__.py`:
- Around line 11-18: The docstring example incorrectly uses the Sandbox instance
(sb) after the async with block has ended; move the ComputerAgent creation and
the async for response in agent.run(...) inside the async with
Sandbox.ephemeral(Image.linux()) as sb: context so sb remains valid while used
as a tool. Specifically, ensure Sandbox.ephemeral(Image.linux()) as sb encloses
the agent = ComputerAgent(model="anthropic/claude-sonnet-4-5", tools=[sb]) and
the async for response in agent.run("Open the browser") loop so sb is in scope
and the sandbox is not destroyed before use.
In `@libs/python/cua/README.md`:
- Around line 13-24: The example creates an ephemeral Sandbox via
Sandbox.ephemeral and assigns it to sb inside an async with, but then constructs
and runs a ComputerAgent (agent) using tools=[sb] after the context has exited
so sb is closed; move the ComputerAgent creation and the async for response in
agent.run(...) block inside the same async with that yields sb (i.e., use sb
while still within the Sandbox.ephemeral context) so agent.run() uses an open
sandbox instance.
In `@TESTING.md`:
- Line 16: Update the Windows example to use the canonical environment variable
name instead of the deprecated one: replace references to CUA_TELEMETRY_DISABLED
with CUA_TELEMETRY_ENABLED in the comment (e.g., change the PowerShell example
from $env:CUA_TELEMETRY_DISABLED="1" to $env:CUA_TELEMETRY_ENABLED="false", and
add an equivalent cmd/setx example if desired) so both Unix and Windows examples
consistently use CUA_TELEMETRY_ENABLED.
---
Nitpick comments:
In @.github/workflows/cd-py-cua.yml:
- Around line 24-25: The prepare job currently uses macos-latest (see prepare
and runs-on) which is unnecessary and costly since it only runs a bash
extraction; change the prepare job's runs-on value from macos-latest to
ubuntu-latest to use the cheaper Linux runner while preserving the job steps and
behavior.
- Around line 34-48: The version selection block redundantly checks
github.event.inputs.version after already checking inputs.version; simplify by
removing the redundant elif that tests [ -n "${{ github.event.inputs.version }}"
] and instead rely on the initial inputs.version check, keeping the push tag
branch (the regex using github.ref and BASH_REMATCH to set VERSION) and the
final error branch; update only the conditional flow around VERSION so
inputs.version is the single source for workflow inputs and no duplicate
github.event.inputs.version check remains.
In `@libs/python/core/core/telemetry/otel.py`:
- Around line 108-110: The logger.debug message in is_otel_enabled() incorrectly
mentions the deprecated CUA_TELEMETRY_DISABLED variable; update the debug text
to reference the canonical CUA_TELEMETRY_ENABLED variable and its false state
(e.g., "OpenTelemetry disabled via CUA_TELEMETRY_ENABLED=false") so the log
matches the actual opt-out mechanism checked by the is_otel_enabled() function.
- Around line 55-73: The deprecation warning for CUA_TELEMETRY_DISABLED is
emitted on every call to is_otel_enabled(), causing spam; add a module-level
boolean flag (e.g., _cua_telemetry_deprecation_warned = False) and only call
warnings.warn when that flag is False, then set the flag to True after warning
so subsequent calls skip emitting it; update the logic inside is_otel_enabled()
(or the function containing the shown diff) to check and set this flag before
returning.
In `@libs/python/core/core/telemetry/posthog.py`:
- Around line 82-84: The code currently creates a new installation ID at
config_dir = Path.home() / ".config" / "cua" and writes id_file = config_dir /
"installation_id"; update the logic that reads/creates the installation ID to
first check the old storage location (e.g.,
Path(__file__).resolve().parents[...] or the package's
site-packages/.storage/installation_id) as a fallback: if an ID file exists in
the legacy path, copy or move that value into the new id_file and use it,
otherwise generate and write a new ID as before; modify the function that
handles installation ID creation/loading (the block using config_dir and
id_file) to implement this fallback and ensure atomic write and proper
permissions when migrating the legacy file.
- Around line 52-71: The deprecation warning in is_telemetry_enabled() currently
fires on every call; add a module-level boolean flag (e.g.,
_warned_cua_telemetry_deprecated = False) and update is_telemetry_enabled() to
check that flag and only call warnings.warn once, setting the flag to True after
the first warn; mirror the pattern used in otel.py so subsequent calls skip
emitting the DeprecationWarning while preserving the same warning message and
stacklevel.
In `@libs/python/cua/pyproject.toml`:
- Line 28: The pyproject.toml requires-python constraint is overly strict
("requires-python = '>=3.12,<3.14'"); update the requires-python entry to relax
the upper bound (for example remove the upper bound to ">=3.12" or use a
permissive upper bound like ">=3.12,<4.0") so the package won't need an
immediate update when Python 3.14 is released; locate and change the
requires-python line in pyproject.toml accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5d585ea1-9aca-4522-9f1e-1dc898a3ed34
📒 Files selected for processing (13)
.github/workflows/cd-py-cua.yml.github/workflows/release-bump-version.ymlTESTING.mdlibs/python/core/core/telemetry/otel.pylibs/python/core/core/telemetry/posthog.pylibs/python/cua-cli/cua_cli/main.pylibs/python/cua-sandbox/cua_sandbox/sandbox.pylibs/python/cua-sandbox/pyproject.tomllibs/python/cua/.bumpversion.cfglibs/python/cua/LICENSElibs/python/cua/README.mdlibs/python/cua/cua/__init__.pylibs/python/cua/pyproject.toml
📦 Publishable packages changed
Add |
… set in CI - Clear CUA_TELEMETRY_DISABLED env var in tests that assert telemetry is enabled - Fix Path.home() mock chain to match actual usage pattern - Fix read_text().strip() mock to return string instead of MagicMock Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
…BLED=false Update CI workflows, all test conftest.py fixtures, and comments to use the current CUA_TELEMETRY_ENABLED=false env var instead of the deprecated CUA_TELEMETRY_DISABLED=1, eliminating DeprecationWarnings that were causing test failures. Also fix isort import ordering in cua-sandbox and computer-server files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
- Add SandboxComputerHandler in agent/computers/sandbox.py that adapts cua_sandbox.Sandbox to the AsyncComputerHandler protocol - Wire Sandbox recognition into is_agent_computer() and make_computer_handler() so tools=[sb] works the same as the old Computer wrapper - Normalize Anthropic/X11 key names (e.g. Return → enter) to pynput names used by computer-server's linux handler - Add examples/agents/test_linux_agent.py demonstrating Sandbox.ephemeral with ComputerAgent using an Anthropic model - Lower cua-agent requires-python to >=3.11 for broader compatibility - Add cua-agent as editable dev dep in cua-sandbox for testing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
- Drop cua-computer from cua-agent required deps (move to optional 'computer' extra) - Make all 'from computer import' usages in agent optional (try/except) - Fix typing.override import for Python <3.12 (use typing_extensions fallback) - Lower requires-python to >=3.11 in cua-agent, cua-core, and cua-sandbox Tested: 3.11 ✓ 3.12 ✓ 3.13 ✓ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
…type to Image API Replace all occurrences of the old cua-computer style parameters (os_type=, provider_type=VMProviderType.*) with the correct cua-sandbox Image API (Image.linux(), Image.macos(), Image.windows(), local=True). Also remove VMProviderType from imports where no longer needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
Pillow was previously a transitive dependency via cua-computer. After making cua-computer optional, PIL imports fail. Add Pillow directly to required dependencies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📦 Publishable packages changed
Add |
Summary
pip install cua— new meta-package atlibs/python/cuaexposing a unified API:from cua import Sandbox, Image, ComputerAgent. Depends oncua-sandbox,cua-agent[cloud], andcua-cli. Agent symbols use lazy__getattr__imports to avoid import-time side effects when only sandbox symbols are needed..bumpversion.cfg,cd-py-cua.ymlpublish workflow, andpypi/cuaadded torelease-bump-version.ymlso the package follows the same bump-and-publish flow as every other package.CUA_TELEMETRY_ENABLED=falseis now canonical for both PostHog and OTEL.CUA_TELEMETRY_DISABLEDstill works but emits aDeprecationWarning.site-packages/.storage/to~/.config/cua/so it survives package upgrades and is shared across venvs.cua-coreadded as a dep tocua-sandbox;sandbox_createandsandbox_destroyPostHog events added;telemetry_enabledparam added toSandbox.create/connect/ephemeralto match the existing docs.cli_commandPostHog event fired on everycuainvocation viatry/finally(recordscommand,subcommand,status,exit_code,duration_seconds).TESTING.mdupdated to useCUA_TELEMETRY_ENABLED=false.Test plan
pip install -e libs/python/cuaand verifyfrom cua import Sandbox, Image, ComputerAgentworksfrom cua import Sandboxdoes not trigger agent import-time telemetryCUA_TELEMETRY_ENABLED=falsesuppresses both PostHog and OTEL eventsCUA_TELEMETRY_DISABLED=1still works and prints aDeprecationWarning~/.config/cua/installation_idis created on first runSandbox.ephemeral(..., telemetry_enabled=False)suppresses sandbox eventscua sandbox listfires acli_commandevent in PostHogrelease-bump-versionworkflow showspypi/cuain the service dropdowncua-v*tag triggerscd-py-cua.ymlSummary by CodeRabbit
New Features
cua) consolidating sandbox, agent, and CLI functionalityDocumentation
CUA_TELEMETRY_DISABLEDtoCUA_TELEMETRY_ENABLED=falseChores