feat(cli): shared host-side governance bootstrap for run and debug#1778
feat(cli): shared host-side governance bootstrap for run and debug#1778viswa-uipath wants to merge 1 commit into
Conversation
1a11593 to
04ad14f
Compare
🚨 Heads up:
|
38435f6 to
9d066a2
Compare
9d066a2 to
91a66d9
Compare
91a66d9 to
6d482ee
Compare
|
shouldn t we apply the governance to evals as well? |
|
Overall blocking concern: this PR crosses package boundaries by making the public The clean contract is to add |
| return None | ||
| if not response.policies: | ||
| return None | ||
|
|
There was a problem hiding this comment.
nit: combine under the same condition
| # it — the tuple is unpacked again inside the | ||
| # inner ``execute_debug_runtime``, but that | ||
| # nested scope's assignment is invisible here. | ||
| governance_dispose = governance_bootstrap[3] |
There was a problem hiding this comment.
This scope workaround is a sign that the bootstrap shape is wrong. Keep one governance object in the outer scope and call governance.dispose() from the final cleanup. Avoid indexing governance_bootstrap[3] and avoid unpacking the same object again inside the nested function.
6d482ee to
5c8fb05
Compare
There was a problem hiding this comment.
Pull request overview
This PR extracts host-side governance bootstrapping into a shared CLI module and reuses it from both uipath run and uipath debug, so they fetch/compile policy and stamp identical audit + telemetry events. It also adds a CLI-side YAML → PolicyIndex compiler and updates dependencies/tests accordingly.
Changes:
- Added shared governance bootstrap module (
_cli/_governance_bootstrap.py) and wired it into bothcli_runandcli_debug. - Added CLI-side governance helpers (
_cli/_governance/yaml_index.py) to compile YAML policies into a runtimePolicyIndex. - Added comprehensive tests for governance bootstrap and YAML compilation, and bumped package/dependency versions (including
pyyaml).
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/uipath/uv.lock | Updates lockfile for new/updated dependencies (runtime bump, PyYAML/types, etc.). |
| packages/uipath/pyproject.toml | Bumps uipath version and updates dependencies (incl. uipath-runtime and pyyaml). |
| packages/uipath/src/uipath/_cli/_governance_bootstrap.py | New shared governance bootstrap used by both run/debug paths. |
| packages/uipath/src/uipath/_cli/_governance/init.py | New CLI governance helper package export. |
| packages/uipath/src/uipath/_cli/_governance/yaml_index.py | New YAML policy compiler producing PolicyIndex for the runtime. |
| packages/uipath/src/uipath/_cli/cli_run.py | Wires shared governance bootstrap into uipath run and manages teardown. |
| packages/uipath/src/uipath/_cli/cli_debug.py | Wires shared governance bootstrap into uipath debug and manages teardown. |
| packages/uipath/src/uipath/_cli/_utils/_common.py | Adds shared is_coded_agent() helper used for consistent agent-type labeling. |
| packages/uipath/src/uipath/_cli/_evals/_telemetry.py | Reuses is_coded_agent() for consistent telemetry labeling. |
| packages/uipath/tests/cli/test_governance_bootstrap.py | New tests covering feature-flag gating, policy fetch/compile branches, cleanup contract, and conversational flag read. |
| packages/uipath/tests/cli/_governance/test_yaml_index.py | New tests covering YAML policy parsing and check-type compilation. |
| packages/uipath/tests/cli/_governance/init.py | Initializes governance test package. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
17fff0a to
cd0d458
Compare
🚨 Heads up:
|
| if entrypoint == "agent.json": | ||
| return "LowCode" | ||
| return "Coded" | ||
| self._current_agent_type: str | None = None |
There was a problem hiding this comment.
this is a breaking change - you need to keep the fallback on the previous implementation. Also make sure you the new values match these
| await factory.dispose() | ||
| if live_tracking_processor is not None: | ||
| live_tracking_processor.shutdown() | ||
| if governance_bootstrap is not None: |
There was a problem hiding this comment.
the runtime should be disposed before the factory
| await factory.dispose() | ||
| if live_tracking_processor is not None: | ||
| live_tracking_processor.shutdown() | ||
| if governance_bootstrap is not None: |
| # with ``agent_type`` / ``agent_framework`` on the settings model. | ||
| return UiPathRuntimeFactorySettings( # type: ignore[call-arg] | ||
| agent_type=_AGENT_TYPE_CODED, | ||
| agent_framework=_AGENT_FRAMEWORK, |
There was a problem hiding this comment.
the functions runtime is not langchain - it's pure python
Extracts the governance-bootstrap helpers used by ``uipath run`` into a shared ``_cli/_governance_bootstrap.py`` module and wires the same path into ``uipath debug`` so both commands stamp identical audit + telemetry events. Adds a CLI-side YAML -> ``PolicyIndex`` compiler so the runtime never has to depend on ``pyyaml`` or the wire policy format. GovernanceBootstrap dataclass ----------------------------- ``resolve_governance()`` returns a ``GovernanceBootstrap`` (frozen dataclass with ``evaluator`` / ``policy_index`` / ``enforcement_mode`` / ``dispose`` fields plus a ``wrap_runtime()`` method) or ``None`` when governance should not fire. Callers hand the base runtime to ``bootstrap.wrap_runtime(delegate, agent_name=..., runtime_id=...)`` and call ``bootstrap.dispose()`` in ``finally`` -- no manual ``UiPathGovernedRuntime`` construction and no tuple unpacking. Factory-declared identity ------------------------- Framework and agent-type wire labels are advertised by the factory via ``UiPathRuntimeFactorySettings.agent_framework`` and ``UiPathRuntimeFactorySettings.agent_type``. The CLI reads both from ``factory.get_settings()`` and forwards them verbatim to ``GovernanceRuntimeMetadata``; the OSS package no longer classifies projects by filename or hardcoded marker registries. Missing values default to ``"unknown"`` at the metadata boundary. Eval telemetry consumes the same source: ``EvalSetRunCreatedEvent`` carries ``agent_type`` populated from the factory, and ``EvalTelemetrySubscriber`` forwards it verbatim to the App Insights ``AgentType`` dimension. The ``is_coded_agent`` helper and low-code filename detection are removed entirely. Error handling -------------- Governance is optional -- a failing bootstrap must not crash the run. Every step in ``resolve_governance()`` that can raise (policy fetch, policy compile, dispatcher init, compensator / audit-manager / evaluator construction) is caught; the CLI logs a warning and runs un-governed. If a failure lands after the dispatcher spawns its background thread and registers its ``atexit`` hook, the recovery path calls ``dispose()`` to unregister and shut down so no thread leaks. ``dispose()`` runs from CLI ``finally`` blocks, so it swallows and debug-logs both ``atexit.unregister`` and dispatcher-shutdown errors -- it must never mask the primary exception. Safe to call more than once: the dispatcher's ``shutdown`` early-returns on repeat calls and ``atexit.unregister`` is a no-op for missing handlers. cli_debug parity ---------------- ``cli_debug`` calls ``resolve_governance()`` after ``factory.get_settings()``, computes ``governance_runtime_id`` once (``ctx.conversation_id or ctx.job_id or "default"``), passes the evaluator into ``factory.new_runtime`` and wraps via ``bootstrap.wrap_runtime`` -- matching ``cli_run``'s shape exactly. Depends on the runtime bumping ``UiPathRuntimeFactorySettings`` with the two new fields; the pin will be updated in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cd0d458 to
8416792
Compare
|



Summary
Extracts the governance-bootstrap helpers used by
uipath runinto a shared_cli/_governance_bootstrap.pymodule and wires the same path intouipath debug, so both commands stamp identical audit + telemetry events. Adds a CLI-side YAML →PolicyIndexcompiler so the runtime layer never has to depend onpyyamlor the wire policy format.GovernanceBootstrapdataclassresolve_governance()returns a frozenGovernanceBootstrap(withevaluator/policy_index/enforcement_mode/disposefields plus awrap_runtime()method) orNonewhen governance should not fire. Callers hand the base runtime tobootstrap.wrap_runtime(delegate, agent_name=..., runtime_id=...)and callbootstrap.dispose()from afinallyblock — no manualUiPathGovernedRuntimeconstruction, no tuple unpacking.Factory-declared identity
Wire labels for governance / App Insights come from
UiPathRuntimeFactorySettings— never from the CLI:agent_framework: str | None— factory declares its framework identity (e.g."langgraph")agent_type: str | None— factory declares the coarse type (e.g."uipath_coded","uipath_lowcode")Both flow:
factory.get_settings()→ CLI →GovernanceRuntimeMetadataverbatim. Missing values default to"unknown"at the metadata boundary.is_coded_agentand low-code filename detection are removed entirely.Eval telemetry consumes the same source:
EvalSetRunCreatedEventcarriesagent_typepopulated from the factory;EvalTelemetrySubscriberforwards it verbatim to the App InsightsAgentTypedimension.Error handling
Governance is optional — a failing bootstrap must not crash the run. Every step in
resolve_governance()that can raise (policy fetch, policy compile, dispatcher init, compensator / audit-manager / evaluator construction) is caught; the CLI logs a warning and runs un-governed. If a failure lands after the dispatcher spawns its background thread and registers itsatexithook, the recovery path callsdispose()to unregister and shut down so no thread leaks.dispose()runs from CLIfinallyblocks, so it swallows and debug-logs bothatexit.unregisterand dispatcher-shutdown errors — it must never mask the primary exception. Safe to call more than once: the dispatcher'sshutdownearly-returns on repeat calls andatexit.unregisteris a no-op for missing handlers.cli_debugparitycli_debugcallsresolve_governance()afterfactory.get_settings(), computesgovernance_runtime_idonce (ctx.conversation_id or ctx.job_id or "default"), passes the evaluator intofactory.new_runtimeand wraps viabootstrap.wrap_runtime— matchingcli_run's shape exactly.Runtime dependency
Depends on
uipath-runtimeshippingUiPathRuntimeFactorySettings.agent_frameworkand.agent_type. The pin will be bumped in a follow-up commit.Adapter follow-ups (outside this PR)
Each third-party factory should declare its own wire labels in
get_settings()— until they do, their governance/App Insights emits"unknown":agent_frameworkagent_typeuipath-agents-python(low-code)"uipath_lowcode"uipath-langchain-python"langgraph""uipath_coded"uipath-integrations-python/*"llamaindex","openai_agents","pydantic_ai","google_adk","agent_framework""uipath_coded"Test plan
tests/cli/test_governance_bootstrap.py— feature-flag gate, policy fetch / compile failures, dispose contract,wrap_runtime, error-path cleanup, agent_type / agent_framework forwardingtests/cli/_governance/test_yaml_index.py— every supported check type + pack/rule plumbingtests/cli/eval/test_eval_telemetry.py— agent_type forwarded from event to App Insights🤖 Generated with Claude Code
Development Packages
uipath