diff --git a/packages/uipath-core/pyproject.toml b/packages/uipath-core/pyproject.toml index 185d84ab6..bcc05c8a3 100644 --- a/packages/uipath-core/pyproject.toml +++ b/packages/uipath-core/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-core" -version = "0.5.28" +version = "0.5.29" description = "UiPath Core abstractions" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath-core/src/uipath/core/governance/config.py b/packages/uipath-core/src/uipath/core/governance/config.py index cbcbd577a..164a46dcb 100644 --- a/packages/uipath-core/src/uipath/core/governance/config.py +++ b/packages/uipath-core/src/uipath/core/governance/config.py @@ -1,15 +1,17 @@ """Governance configuration. -Process-level feature-flag gate that decides whether the Python -governance checker runs at all. The -:class:`uipath.core.governance.EnforcementMode` value type is defined -in :mod:`uipath.core.governance.models`; the per-policy runtime state -that selects a mode (backend-supplied via the ``/runtime/policy`` -client) lives outside this package. +Process-level feature-flag gate that lets direct callers (agents +constructing an evaluator themselves) decide whether to wire up +governance. The :class:`uipath.core.governance.EnforcementMode` value +type is defined in :mod:`uipath.core.governance.models`; the per-policy +runtime state that selects a mode (backend-supplied via the +``/runtime/policy`` client) lives outside this package. """ from __future__ import annotations +import warnings + from uipath.core.feature_flags import FeatureFlags # Feature flag name controlling whether governance runs. @@ -22,6 +24,14 @@ def is_governance_enabled() -> bool: """Return whether the ``EnablePythonGovernanceChecker`` flag is enabled. + .. deprecated:: + The CLI ``run`` / ``debug`` bootstrap no longer consults this + gate — it always fetches policy and lets the backend-supplied + enforcement mode decide. This helper remains only for direct + callers (agents constructing an evaluator themselves) that gate + their own wiring on the flag, and will be removed in a future + major release. + Governance is **off by default** — the flag must be explicitly set to ``true`` (programmatically via the ``FeatureFlags`` registry, or via the ``UIPATH_FEATURE_EnablePythonGovernanceChecker`` env var) @@ -34,4 +44,10 @@ def is_governance_enabled() -> bool: gitops) and its own ``UIPATH_FEATURE_`` env-var fallback. 2. Default ``False`` (governance disabled). """ + warnings.warn( + "is_governance_enabled() is deprecated;" + "It will be removed in a future major release.", + DeprecationWarning, + stacklevel=2, + ) return FeatureFlags.is_flag_enabled(GOVERNANCE_FEATURE_FLAG, default=False) diff --git a/packages/uipath-core/tests/governance/test_config.py b/packages/uipath-core/tests/governance/test_config.py index 54642a413..abdd469f6 100644 --- a/packages/uipath-core/tests/governance/test_config.py +++ b/packages/uipath-core/tests/governance/test_config.py @@ -24,6 +24,17 @@ def test_governance_flag_name_is_stable(): assert GOVERNANCE_FEATURE_FLAG == "EnablePythonGovernanceChecker" +def test_is_governance_enabled_is_deprecated(): + """The helper is retained for external callers but warns on use. + + The CLI bootstrap no longer gates on it; direct callers (agents + wiring their own evaluator) may still use it, but should migrate + off before it is removed in a future major release. + """ + with pytest.warns(DeprecationWarning): + is_governance_enabled() + + def test_is_governance_enabled_defaults_to_false(): """With nothing configured, the gate defaults to disabled. @@ -33,22 +44,26 @@ def test_is_governance_enabled_defaults_to_false(): keeps the SDK safe-by-default for callers that haven't yet integrated with the governance backend. """ - assert is_governance_enabled() is False + with pytest.warns(DeprecationWarning): + assert is_governance_enabled() is False def test_is_governance_enabled_respects_programmatic_disable(): """Programmatic ``False`` flips the gate off.""" FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: False}) - assert is_governance_enabled() is False + with pytest.warns(DeprecationWarning): + assert is_governance_enabled() is False def test_is_governance_enabled_respects_programmatic_enable(): """Programmatic ``True`` keeps the gate on.""" FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: True}) - assert is_governance_enabled() is True + with pytest.warns(DeprecationWarning): + assert is_governance_enabled() is True def test_is_governance_enabled_reads_env_var_fallback(monkeypatch): """When nothing is configured programmatically, the env-var fallback wins.""" monkeypatch.setenv(f"UIPATH_FEATURE_{GOVERNANCE_FEATURE_FLAG}", "false") - assert is_governance_enabled() is False + with pytest.warns(DeprecationWarning): + assert is_governance_enabled() is False diff --git a/packages/uipath-core/uv.lock b/packages/uipath-core/uv.lock index a1322e25d..79a6c22cd 100644 --- a/packages/uipath-core/uv.lock +++ b/packages/uipath-core/uv.lock @@ -1011,7 +1011,7 @@ wheels = [ [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.29" source = { editable = "." } dependencies = [ { name = "opentelemetry-instrumentation" }, diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index 6ecc8cc13..9ea13f55a 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -1063,7 +1063,7 @@ wheels = [ [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.29" source = { editable = "../uipath-core" } dependencies = [ { name = "opentelemetry-instrumentation" }, diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index 7413e2515..3fe81ecdd 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,11 +1,11 @@ [project] name = "uipath" -version = "2.13.2" +version = "2.14.0" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ - "uipath-core>=0.5.26, <0.6.0", + "uipath-core>=0.5.29, <0.6.0", "uipath-runtime>=0.11.7, <0.13.0", "uipath-platform>=0.2.1, <0.3.0", "click>=8.3.1", diff --git a/packages/uipath/src/uipath/_cli/_governance_bootstrap.py b/packages/uipath/src/uipath/_cli/_governance_bootstrap.py index 675b42721..d0055c967 100644 --- a/packages/uipath/src/uipath/_cli/_governance_bootstrap.py +++ b/packages/uipath/src/uipath/_cli/_governance_bootstrap.py @@ -13,7 +13,6 @@ from dataclasses import dataclass from uipath.core.governance import EnforcementMode, PolicyContext -from uipath.core.governance.config import is_governance_enabled from uipath.platform import UiPath from uipath.platform.governance import UiPathPlatformGovernanceProvider from uipath.platform.governance._live_track_event_dispatcher import ( @@ -91,9 +90,6 @@ async def resolve_governance( to :class:`PolicyContext` so the backend can select the conversational or autonomous policy view. """ - if not is_governance_enabled(): - return None - context = PolicyContext(is_conversational=is_conversational) try: diff --git a/packages/uipath/tests/cli/test_governance_bootstrap.py b/packages/uipath/tests/cli/test_governance_bootstrap.py index fabe3650c..977b2fd02 100644 --- a/packages/uipath/tests/cli/test_governance_bootstrap.py +++ b/packages/uipath/tests/cli/test_governance_bootstrap.py @@ -127,22 +127,6 @@ def _stub_provider( class TestResolveGovernance: - async def test_returns_none_when_feature_flag_disabled( - self, monkeypatch: pytest.MonkeyPatch - ) -> None: - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: False, - ) - assert ( - await resolve_governance( - agent_framework="langgraph", - agent_type="uipath_coded", - is_conversational=False, - ) - is None - ) - @pytest.mark.parametrize("is_conversational", [True, False]) async def test_is_conversational_forwarded_verbatim_to_policy_context( self, @@ -155,10 +139,6 @@ async def test_is_conversational_forwarded_verbatim_to_policy_context( bootstrap must forward the exact value to :class:`PolicyContext` so the backend can select the conversational or autonomous policy view.""" - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -192,10 +172,6 @@ async def test_returns_none_when_policy_fetch_fails( monkeypatch: pytest.MonkeyPatch, cwd: Path, ) -> None: - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -218,10 +194,6 @@ async def test_returns_none_when_mode_is_disabled( monkeypatch: pytest.MonkeyPatch, cwd: Path, ) -> None: - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -249,10 +221,6 @@ async def test_returns_none_when_mode_is_none( monkeypatch: pytest.MonkeyPatch, cwd: Path, ) -> None: - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -278,10 +246,6 @@ async def test_returns_none_when_policies_empty( monkeypatch: pytest.MonkeyPatch, cwd: Path, ) -> None: - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -313,10 +277,6 @@ async def test_returns_none_when_policy_compilation_fails( governance skips cleanly rather than propagating a ``YAMLError`` out of ``resolve_governance``. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -352,10 +312,6 @@ async def test_success_returns_bootstrap_with_dispose_contract( """Happy path -- named fields populated and dispose unregisters atexit + shuts the dispatcher down. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -427,10 +383,6 @@ async def test_agent_type_forwarded_verbatim_to_metadata( the project; the factory does via :attr:`UiPathRuntimeFactorySettings.agent_type`. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -471,10 +423,6 @@ async def test_agent_framework_none_passes_through_as_unknown( ) -> None: """A factory with no ``agent_framework`` opinion emits ``"unknown"`` -- symmetric with the ``agent_type`` fallback.""" - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -511,10 +459,6 @@ async def test_agent_type_none_passes_through_to_metadata( ) -> None: """A factory with no ``agent_type`` opinion yields ``None`` on the metadata -- the backend decides how to interpret the gap.""" - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -557,10 +501,6 @@ async def test_wrap_runtime_produces_governed_runtime_with_bootstrap_fields( the caller's ``agent_name`` / ``runtime_id``. This is the code path CLI callers replaced their manual construction with. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -607,10 +547,6 @@ async def test_returns_none_when_dispatcher_init_fails( is optional and a failing bootstrap must not crash the CLI. No ``atexit`` hook should leak. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -667,10 +603,6 @@ async def test_returns_none_and_cleans_up_when_evaluator_setup_fails( raises, ``resolve_governance`` must unregister the ``atexit`` hook AND shut the dispatcher down before returning ``None``. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) class _ExplodingEvaluator: def __init__(self, *_a: Any, **_kw: Any) -> None: @@ -739,10 +671,6 @@ async def test_dispose_swallows_dispatcher_shutdown_errors( raise, or it will mask the primary exception. A shutdown that blows up should be logged at debug and swallowed. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, @@ -807,10 +735,6 @@ async def test_dispose_atexit_hook_matches_dispatcher_shutdown( callable that ``atexit.register`` received — otherwise unregister is a silent no-op and the dispatcher lingers. """ - monkeypatch.setattr( - "uipath._cli._governance_bootstrap.is_governance_enabled", - lambda: True, - ) _install_fake_runtime_governance( monkeypatch, audit_manager_cls=_FakeAuditManager, diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index e7cf75a96..e2d1a378c 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2598,7 +2598,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.2" +version = "2.14.0" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2709,7 +2709,7 @@ dev = [ [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.29" source = { editable = "../uipath-core" } dependencies = [ { name = "opentelemetry-instrumentation" },