[build-system] requires = ["setuptools>=68.0"] build-backend = "setuptools.build_meta" [project] name = "omnigent" # Keep in sync with omnigent/version.py's VERSION constant. version = "0.21.0.dev0" description = "Omnigent: declarative agent authoring and runtime framework" readme = "README.md" requires-python = ">=3.12" authors = [ { name = "Databricks, Inc." }, ] keywords = ["agents", "llm", "ai", "databricks", "cli"] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] dependencies = [ # SDK path-deps, version-locked: the three packages release together at # one version, so a published `omnigent==X` must resolve the SDK wheels # built alongside it (release-omnigent.yml verifies these pins match the # release tag). Local/editable installs still resolve via tool.uv.sources. "omnigent-client==0.21.0.dev0", "omnigent-ui-sdk==0.21.0.dev0", # Merged from omnigent + omnigent. "pyyaml>=6.0,<7", # OpenClaw stores its wrapped acpx registry as JSON5. "json5>=0.10,<1", # openai-agents<0.18.2 omits a field required by openai>=2.45. # Keep this cap until the websockets<15 compatibility pin can be lifted. "openai>=1.0,<2.45", # >=14: the `e2b` extra's SDK (>=2.26) needs rich>=14 (was >=13,<14). "rich>=14,<16", "prompt_toolkit>=3.0,<4", "mcp>=1.0,<2", "starlette>=1.0.1,<2", "uvicorn[standard]>=0.30,<1", # websockets >=15 asyncio client hangs on macOS before emitting any handshake # bytes; pin below 15 until upstream fixes the regression (see issue #1514). "websockets>=10.4,<15", "httpx>=0.27,<1", "ftfy>=6.0", # Portable zombie-aware PID liveness checks for the host daemon # lifecycle (omnigent/host/local_server.py). "psutil>=5.9,<8", # CEL (Common Expression Language) for inline policy evaluation. # Non-Turing-complete, side-effect-free, guaranteed to terminate. # NOT pure-Python: cel-python hard-depends on google-re2 (a C++ extension, # imported at celpy module scope with no fallback), whose sdist needs bazel. # Packagers must pin its prebuilt wheel — see WHEEL_PACKAGES in # .github/scripts/homebrew/generate_formula.py. "cel-python>=0.5", # OS keychain for storing model-provider API keys (OSS model # selection). Used by omnigent/onboarding/secrets.py; falls back to # a 0600 file when no keyring backend is available (e.g. headless). "keyring>=24,<26", # Decode and validate public branding assets before serving them. # uv.lock pins 13.0.0 for its security fixes; the floor stays at 12.2 so # installs behind a package-age cooldown mirror still resolve until 13 ages in. "pillow>=12.2,<14", # Round-trip (comment/format-preserving) TOML editing of the user's # ~/.codex/config.toml when removing a Databricks provider # (omnigent/onboarding/ucode_cleanup.py). stdlib tomllib is read-only. "tomlkit>=0.12,<1", # omnigent deps (runtime, spec, server, persistent terminal, etc.). "alembic>=1.0,<2", "anyio>=4.0,<5", "cachetools>=5.0,<8", "filelock>=3.0", # _HostGroup (omnigent/cli.py) rewrites a leading positional server URL # into `--server` before Click parses it, so it no longer depends on # Click's internal `protected_args` (made read-only in click 8.2, slated # for removal in click 9). Upper bound `<10` only guards against an # as-yet-unreleased click 10 with unknown breaking changes. "click>=8.0,<10", "fastapi>=0.100,<1", # pexpect/pyte back the POSIX tmux/PTY terminal stack, which is disabled # on Windows (no usable PTY); omnigent never imports them on the core # path, so skip them there to avoid shipping an unusable dependency. "pexpect>=4.9,<5; platform_system != 'Windows'", "pyte>=0.8,<1; platform_system != 'Windows'", "pydantic>=2.0,<3", "sqlalchemy>=2.0,<3", # Client-side compression for opaque text columns (omnigent/db/compression.py), # so per-conversation JSON/text is stored uniformly compressed across SQLite, # PostgreSQL, and MySQL rather than depending on each backend's storage engine. "zstandard>=0.22,<1", "tiktoken>=0.7,<1", # Server performance metrics use the no-op API even when tracing is off. "opentelemetry-api>=1.20,<2", # The Claude Code and OpenAI Agents harnesses are part of the # baseline install — every CUJ command in the README depends on at # least one of them, so making them optional just forces every user # to remember `--extra all`. Issue #406. "claude-agent-sdk>=0.1.62", "openai-agents>=0.0.17", "PyJWT[crypto]>=2.8,<3", "argon2-cffi>=23.1,<26", # PEP 440 version comparison for the update check / `omni upgrade` # (omnigent/update_check.py, omnigent/cli.py): compares the installed # version against the latest PyPI release. Previously only a # transitive dep; pinned here now that we import it directly. "packaging>=23,<27", # CA root bundle for client TLS verification (omnigent/tls.py, # omnigent/inner/egress/ca.py). Required as a real fallback on uv / # python-build-standalone interpreters whose OpenSSL has no default cert # path; previously only transitive, pinned here now that we import it. # 2026.10.19 drops the roots behind the compromised eMudhra intermediates. # custom-lint: disable-next=young-dependency-floor -- CA security fix; proxy serves it "certifi>=2026.10.19", # Protobuf schema for the AI-gateway routing API # (omnigent/api/routing/v1/routing.proto). # Previously only a transitive dep; declared directly now that we ship a proto. # Upper bound widened to <8 so the google-antigravity extra can pull in # protobuf 7.x without conflicting with this core floor. Our own # routing_pb2 gencode was compiled against protobuf 6.x; the protobuf # runtime guarantee allows a newer runtime (7.x) to load older gencode, so # a plain install stays on 6.x and an antigravity install upgrades to 7.x. "protobuf>=6,<8", # RFC 5545 recurrence rules for the scheduled-task scheduler # (omnigent/server/scheduled/rrule.py). Imported at module top and pulled # onto the core server boot path via app.py; previously only a transitive # dep, declared directly now that we import it. "python-dateutil>=2.8,<3", # IANA time-zone database for `zoneinfo`. Windows ships no system tz # database, so `ZoneInfo("UTC")` raises ZoneInfoNotFoundError there # unless this package is installed. Evaluated at module top in # omnigent/server/scheduled/rrule.py and scheduler.py, both pulled onto # the core server boot path via app.py, so a Windows install crashes on # `omnigent server start` without it. POSIX platforms use the system # database and don't need the wheel. "tzdata>=2024.1; platform_system == 'Windows'", ] [project.optional-dependencies] # Retained as no-op aliases so existing `--extra` invocations keep # working; the underlying packages are now default deps above. claude-sdk = [] openai-agents = [] cockroachdb = [ "psycopg[binary]>=3.1,<4", "sqlalchemy-cockroachdb>=2.0,<3", ] # `all` carries dependencies moved out of the default install that internal # CI / dev workflows still exercise. all = [ "databricks-sdk[sandbox]>=0.153,<1", "opentelemetry-sdk>=1.20,<2", "opentelemetry-exporter-otlp-proto-grpc>=1.20,<2", "opentelemetry-exporter-otlp-proto-http>=1.20,<2", "opentelemetry-instrumentation-fastapi>=0,<1", "opentelemetry-instrumentation-httpx>=0,<1", "opentelemetry-instrumentation-sqlalchemy>=0,<1", ] # From omnigent. bedrock = ["boto3>=1.30,<2", "botocore>=1.30,<2"] # The credential store's AWS KMS backend (omnigent/stores/credential_store/secret_cipher.py) # imports boto3 lazily, only when OMNIGENT_CREDENTIAL_KMS_KEY_ID is set. Its own extra, so a # KMS deployment installs `omnigent[kms]`; the Vault backend uses `vault` (hvac) instead, and # the backend-agnostic base store needs neither. kms = ["boto3>=1.30,<2", "botocore>=1.30,<2"] # S3-compatible artifact store (AWS S3, Cloudflare R2, MinIO, …). Selected # with ``OMNIGENT_ARTIFACT_URI=s3://bucket/prefix``; the store imports boto3 # lazily, so only users of this backend need the extra. s3 = ["boto3>=1.30,<2", "botocore>=1.30,<2"] vertex = ["google-auth>=2.0,<3"] # Modal sandbox launcher (`omnigent sandbox --provider modal`). The # launcher module ships in the base package and imports the SDK lazily, # so only users of the provider need this extra. modal = ["modal>=1.0,<2"] # Daytona sandbox launcher (`omnigent sandbox --provider daytona` and # server-managed `sandbox.provider: daytona`). # Same lazy-import posture as the modal extra. Pre-1.0 SDK: pin minor. daytona = ["daytona>=0.180,<1"] # Blaxel sandbox launcher (`omnigent sandbox --provider blaxel` and # server-managed `sandbox.provider: blaxel`). blaxel = ["blaxel>=0.4.1,<0.5"] # Boxlite sandbox launcher: server-managed `sandbox.provider: boxlite`, either # local embedded micro-VMs (KVM/HVF, no daemon) or a remote `boxlite serve` # pool. Same lazy-import posture as the modal/daytona extras. Pre-1.0 SDK: pin # minor. boxlite = ["boxlite>=0.9.5,<1"] # Microsandbox launcher: local libkrun microVMs with an optional embedded SDK. # Require 0.6.9 for upstream bug fixes and pin the beta SDK's minor version. microsandbox = ["microsandbox>=0.6.9,<0.7"] # CoreWeave Sandbox launcher (`omnigent sandbox --provider cwsandbox` and # server-managed `sandbox.provider: cwsandbox`). # Same lazy-import posture as above. Pre-1.0 SDK: pin minor. cwsandbox = ["cwsandbox>=0.24,<1"] # E2B sandbox launcher (`omnigent sandbox --provider e2b` and # server-managed `sandbox.provider: e2b`). # Same lazy-import posture as above. e2b = ["e2b>=2.26,<3"] # Islo sandbox launcher (`omnigent sandbox --provider islo` and # server-managed `sandbox.provider: islo`). The launcher module ships in # the base package and imports the SDK lazily, so only users of the # provider need this extra. islo = ["islo>=0.3.5,<0.4"] # NVIDIA OpenShell launcher (`omnigent sandbox --provider openshell`). # Self-hosted gRPC gateway; the SDK pulls grpcio + protobuf. Same # lazy-import posture as above. Pre-1.0 SDK: pin minor. 0.1 addresses # exec by sandbox name within a workspace. openshell = ["openshell>=0.1.0,<1"] # On-demand Kubernetes runner Pod launcher (server-managed # `sandbox.provider: kubernetes`). The launcher module ships in the base # package and imports the official client lazily, so only users of the # provider need this extra. kubernetes = ["kubernetes>=36,<38"] # Vault Transit backend for the credential store's SecretCipher — the # non-AWS alternative to the built-in KMS cipher (selected by # OMNIGENT_CREDENTIAL_VAULT_KEY). hvac is imported lazily, like boto3, so # only deployments that pick the Vault backend need this extra. vault = ["hvac>=2,<3"] # OpenTelemetry tracing is opt-in so the exporters and automatic # instrumentors do not inflate the default install. tracing = [ "opentelemetry-sdk>=1.20,<2", "opentelemetry-exporter-otlp-proto-grpc>=1.20,<2", "opentelemetry-exporter-otlp-proto-http>=1.20,<2", "opentelemetry-instrumentation-fastapi>=0,<1", "opentelemetry-instrumentation-httpx>=0,<1", "opentelemetry-instrumentation-sqlalchemy>=0,<1", ] agents-sdk = [ "openai-agents>=0.1,<1", "opentelemetry-instrumentation-openai-agents-v2>=0.1", ] # Google Antigravity SDK harness (`harness: antigravity`). Optional: the # harness module imports the SDK lazily on first turn, so only users of the # harness need this extra. Early SDK (v0.1.x) — pin minor. # google-antigravity ships proto gencode compiled against protobuf 7.x, so # the runtime must satisfy >=7 when this extra is installed; the core allows # >=6,<8, so this constraint selects the 7.x slot and prevents the # "gencode/runtime version mismatch" error at import time. antigravity = ["google-antigravity>=0.1,<1", "protobuf>=7,<8"] # GitHub Copilot SDK harness (`harness: copilot`). Optional: the harness module # imports the SDK lazily on first turn, so only users of the harness need this # extra. The wheel bundles the Copilot CLI binary it drives (~90MB), which is # why it is not part of the baseline install. Pin major. copilot = ["github-copilot-sdk>=1,<2"] # Server-side streaming dictation (`WS /v1/dictation/stream`, # designs/server-dictation.md). Local CPU speech-to-text via sherpa-onnx; # the engine module imports it lazily, so only servers offering dictation # need the extra. Models are fetched separately # (scripts/fetch-dictation-models.sh). sherpa-onnx-core (the native # onnxruntime payload) is pinned explicitly because sherpa-onnx's wheel # metadata declares it inconsistently across platforms — without the # explicit pin it is missing from uv.lock, which breaks both the hashed # OSV audit and aarch64 installs. dictation = [ "sherpa-onnx>=1.13,<2", "sherpa-onnx-core>=1.13,<2", # The engine converts PCM frames with numpy directly; declare it # rather than riding whichever extra happens to pull it in. "numpy>=1.24,<3", ] # Cursor SDK harness (`harness: cursor`). Optional like antigravity: the # harness imports the cursor-sdk lazily on first turn, so only `--harness # cursor` users need this extra (`omnigent[cursor]`). Was a baseline dependency; # now opt-in to keep the default install lean. cursor = ["cursor-sdk>=0.1.7"] # Hindsight long-term memory built-in tools (hindsight_retain / _recall / # _reflect). The tools import the client lazily, so only users who enable a # Hindsight memory tool need this extra. hindsight = ["hindsight-client>=0.4.0"] # The Nimble builtins (`nimble_research`, `nimble_extract`). nimble_research # imports the nimble-python client lazily; nimble_extract talks raw httpx and # needs no client at runtime. Installing this extra is the opt-in signal that # surfaces both tools in the onboarding assistant's builtin list. nimble = ["nimble-python>=1.2.0,<2"] # Slack integration (`omni integration slack`). The @omnigent socket-mode bot # lives in the separate `omnigent-slack` package (heavy deps — slack_bolt / # aiohttp — kept out of the baseline install). The CLI launches it as a # subprocess (`python -m omnigent_slack`), so it must be importable in the same # environment; installing `omnigent[slack]` puts it there. Local/editable # installs resolve it from `integrations/slack` via [tool.uv.sources] below; # publishing `omnigent[slack]` to an index additionally requires publishing # `omnigent-slack` there (not yet wired into the release pipeline). # Version-locked (==) with the lockstep cycle — `scripts/update_versions.py` # keeps this pin in sync with the other sibling `==` pins # (`omnigent-client`, `omnigent-ui-sdk`) and the sub-package's own # `[project].version` in `integrations/slack/pyproject.toml`. slack = [ "omnigent-slack==0.21.0.dev0", ] # PostgreSQL storage backend. The server builds a psycopg engine whenever # the database URI is Postgres (``OMNIGENT_DATABASE_URI=postgresql+psycopg://…`` # / ``--database-uri``). That is a general-purpose escape hatch — any # standalone Postgres deployment needs the driver, not only Databricks — so it # gets its own extra rather than being reachable only via `databricks`. # ``_create_engine`` raises an actionable "install omnigent[postgres]" error # when the URI is Postgres but this driver is absent. postgres = ["psycopg[binary]>=3.1,<4"] databricks = [ # 0.153 is the first SDK with streaming Sandbox command execution; the # `sandbox` extra adds the grpcio it runs over. "databricks-sdk[sandbox]>=0.153,<1", "opentelemetry-distro>=0,<1", # Databricks uses a Postgres control plane (Lakebase); reuse the single # psycopg pin from the `postgres` extra so the two never drift. "omnigent[postgres]", "pymysql>=1.1,<2", # Databricks-specific MCP servers. Not imported by core Omnigent # code, so it is opt-in here rather than a default dependency. Floored # at 0.9.0 (the release the lock already resolves): with the old >=0.1.0 # floor, a fresh install could satisfy the ai-bridge floor below by # dropping mcp to 0.1.0, which pulls no mlflow/pyarrow at all. "databricks-mcp>=0.9.0", # Floor the transitive databricks-ai-bridge: older releases pull a # databricks-vectorsearch that pins protobuf 5.x (conflicts with our # protobuf>=6), so the resolver backtracks to an ai-bridge that caps # mlflow at 3.2 and pyarrow<22 — and pyarrow 21 has no cp314 wheel, so a # fresh install on Python 3.14 tries (and fails) to build it from source. "databricks-ai-bridge>=0.19", ] # Load testing (dev/loadtest/). Locust drives the concurrency and the # openai-agents harness runs the mocked turns. Opt-in — only load-test users # need it. loadtest = [ "locust>=2.30,<3", ] # Repository-only workflows use PEP 735 dependency groups so development # tooling is not published as an installable Omnigent capability. [dependency-groups] lint = [ "ruff>=0.6", "pyrefly==1.1.1", "pre-commit>=3.5", # protoc + the well-known-type protos, for regenerating the routing API # bindings (omnigent/api/routing/v1/routing_pb2.*). Bundles its own compiler so no # system protoc is needed. Capped below 1.83 so the bundled libprotoc emits # protobuf 6.x gencode; routing_pb2 must stay at gencode 6.x because # cwsandbox/modal installs are pinned to a protobuf 6.x runtime and the # protobuf runtime cannot be older than the gencode version. A 7.x runtime # (antigravity) also loads 6.x gencode under the forward-compat guarantee, # so 6.x gencode satisfies all install forks. "grpcio-tools>=1.68,<=1.81.1", ] test = [ "pytest>=7.0", # PEP 517 frontend used by the standalone-package artifact checks. "build>=1.2", # These clients are optional product capabilities, but their tests import # and execute the real packages against mocks. "hindsight-client>=0.4.0", "nimble-python>=1.2.0,<2", "pytest-asyncio>=0.21", "pytest-xdist>=3.6", # Line coverage during the unit-test matrix. Each CI shard writes its # own data file; the code-coverage workflow combines them. See # `[tool.coverage.run]` below for the relative-files setting that makes # cross-runner combine work. "pytest-cov>=5", # Isolated pytest subprocesses use the plugin's built-in base-URL support. "pytest-playwright>=0.10", # Visual-regression snapshot plugin for the e2e_ui suite. Provides the # `assert_snapshot` fixture + `--update-snapshots` flag the UI diff gate # uses (see tests/e2e_ui/visual/ and .github/workflows/ui-snapshot.yml). "pytest-playwright-visual-snapshot>=0.5.1", # Hash-partition the test suite across N shards for the nightly # matrix. Pinned to a known-stable older release. Used by e2e.yml; # the e2e-ui suite uses its own round-robin `--splits`/`--group` # split (see tests/e2e_ui/conftest.py), so no extra dep there. "pytest-shard==0.1.2", # Per-test wallclock cap. Without this, a single hung test (e.g. # a pexpect REPL test waiting for a terminal pattern that never # arrives) blocks the whole pytest session until the workflow's # step-level timeout-minutes fires, killing the worker without # producing a junit XML or per-test traceback. With --timeout=N, # pytest-timeout fails the offending test with a clear traceback # and lets the rest of the suite continue. Pinned exactly to a # known-good release for consistency with pytest-shard above. "pytest-timeout==2.4.0", # Retry LLM-driven inner tests up to 2 extra times on codex; see # ``pytest_collection_modifyitems`` in ``tests/inner/conftest.py``. # Pinned exactly to a known-stable release for consistency with # pytest-shard / pytest-timeout above. 16.2+ is required under # pytest 9: 14.0 predates pytest 9 and corrupts fixture-finalizer # state around reruns (stale FixtureDef._finalizers / SetupState # surgery, fixed upstream in 16.2), which leaked one test's # monkeypatch of ``sessions._get_runner_client`` into later tests # on the same xdist worker and flaked the WS-tunnel suite (the # "Runner stream relay exited before becoming ready" 503s). "pytest-rerunfailures==16.3", "filelock>=3.0", # Cloud-service mocks for the artifact-store / Cloudflare integration # tests: moto is the standard S3-compatible mock (used for R2), respx is # the standard HTTPX mock (used to back the Cloudflare D1 REST API with an # in-memory SQLite), and boto3 backs the S3 artifact store under test. "moto[s3]>=5,<6", "respx>=0.21,<1", "boto3>=1.30,<2", # The Cloudflare D1 SQLAlchemy dialect, exercised by the D1 FTS integration # test (tests/db/test_d1_fts_dialect.py) via respx + sqlite3. "sqlalchemy-cloudflare-d1==0.3.10", ] dev = [ { include-group = "lint" }, { include-group = "test" }, ] [project.scripts] # Unified entry point. `omnigent` is the click-based CLI that absorbs # the legacy argparse commands from `omnigent/inner/cli.py` plus the # omnigent `ap` subcommands. See designs/UNIFICATION.md § CLI # absorption. `omni` is installed as a short alias — both resolve to # the same entry point. (No former-name aliases: only `omnigent` and # `omni` ship, internal and OSS alike.) omnigent = "omnigent.cli:main" omni = "omnigent.cli:main" [tool.uv] # Keep plain `uv sync` aligned with the published base package. Repository # workflows opt into the smallest dependency group they need. default-groups = [] # `antigravity` requires protobuf>=7 (google-antigravity ships gencode # compiled against protobuf 7.x). The following extras/groups all cap their # own transitive deps at protobuf<7, making them mutually exclusive with # `antigravity`. Declaring conflicts lets uv resolve each set in an isolated # fork rather than failing the whole lockfile or silently downgrading # shared dependencies like databricks-sdk. # cwsandbox: protobuf<7 (cwsandbox itself) # modal: protobuf<7 (modal itself) # lint group: protobuf<7 (grpcio-tools<=1.81.1 requires protobuf<7) — only # conflicts with antigravity, not with cwsandbox/modal # `databricks` needs no entry: databricks-sdk>=0.153 accepts protobuf 7. conflicts = [ [{ extra = "antigravity" }, { extra = "cwsandbox" }], [{ extra = "antigravity" }, { extra = "modal" }], [{ group = "lint" }, { extra = "antigravity" }], ] # Share the repository lockfile with standalone automation packages. [tool.uv.workspace] members = [".github/triage_v2"] # uv path-deps for the sibling SDK packages (renamed in Stage 3). [tool.uv.sources] omnigent-client = { path = "sdks/python-client", editable = true } omnigent-ui-sdk = { path = "sdks/ui", editable = true } # Local resolution for the `slack` extra. Only pulled in when `omnigent[slack]` # is requested (the extra is opt-in), so a default install never builds it. omnigent-slack = { path = "integrations/slack", editable = true } [tool.setuptools] include-package-data = true [tool.setuptools.packages.find] where = ["."] # Discovers the `omnigent` package (including `omnigent.inner.*` and # all omnigent subpackages absorbed during unification). include = ["omnigent*"] [tool.setuptools.package-data] "omnigent.db" = ["alembic.ini"] "omnigent.db.migrations" = ["script.py.mako"] "omnigent.resources.examples" = ["*.yaml", "*.sh", "debby/**/*", "polly/**/*"] "omnigent.resources.pi" = ["*.cjs"] "omnigent.resources.pi_native" = ["*.js", "*.cjs"] "omnigent.resources.pi_gateway" = ["*.js"] "omnigent.resources.scripts" = ["*.sh"] # include UI assets in build, plus the static API-only landing page served # at "/" when no web UI bundle is present "omnigent.server" = ["static/web-ui/**/*", "static/api_only_landing.html"] [tool.pytest.ini_options] # auto mode: async tests without @pytest.mark.asyncio() are auto- # collected as asyncio. Inner (omnigent-legacy) tests rely on # this. SDK tests (tests/frontends/sdk/) use explicit markers; # both work under auto. asyncio_mode = "auto" testpaths = ["tests"] # tests/e2e, tests/e2e_ui, tests/browser_ui, and tests/e2e_live require # credentials and/or extra setup (Databricks profile; npm build + Playwright # browsers for the UI suites; a deployed Databricks App URL for the live # suite). Opt in via explicit pytest invocation. # structlog-config's pytest plugin clobbers unittest's private item._excinfo. # Disable it until the plugin stops writing to that attribute. addopts = "-p no:structlog_config --ignore=tests/e2e --ignore=tests/e2e_ui --ignore=tests/browser_ui --ignore=tests/e2e_live --ignore=tests/integration" # Default per-test wallclock cap. Catches infinite hangs in any # suite (unit, inner, integration, e2e) without bothering tests # that actually finish. The longest legitimate integration tests # are well under 5 min; anything longer than that is almost # certainly a hang and should surface a traceback rather than # stall the worker. CLI overrides still work: e2e workflows pass # a stricter --timeout=180; individual tests can opt out via # ``@pytest.mark.timeout(N)``. ``thread`` method is more reliable # than the default ``signal`` method when tests fork subprocesses # (SIGALRM doesn't reach blocked-on-pty children) — see # pytest-timeout README. timeout = 300 timeout_method = "thread" # SDK tests under ``tests/frontends/sdk/`` import the # ``omnigent_client`` and ``omnigent_ui_sdk`` packages that # live in ``sdks/python-client/`` and ``sdks/ui/`` respectively. # Without this pythonpath the imports fail at collection time # with ``ModuleNotFoundError: No module named 'omnigent_client'`` # unless the SDKs are installed editable into the venv. Listing # both relative paths here keeps the test suite self-contained. pythonpath = [".", "sdks/python-client", "sdks/ui"] markers = [ "live: end-to-end tests requiring real LLM API key (run with --llm-api-key)", "live_app: end-to-end tests requiring a deployed Databricks App server", "databricks: requires the `databricks` extra (psycopg / databricks-sdk). Deselected on the standard lanes via -m 'not databricks'; runs only in the Pytest (databricks) lane, which installs the extra. Use for tests that build a postgresql+psycopg engine or call the Databricks SDK.", "model(name, ...): one arg pins this test to that model overriding the suite-wide --model CLI default; multiple args fan the test out across all listed models via indirect parametrize.", "model_pinned: opt out of tests/_model_pools.py spreading and retry rotation; literal model strings are used as written.", "flaky(reruns=2, reruns_delay=0): rerun a nondeterministic test on failure (pytest-rerunfailures). Use for tests flaky due to timing/scheduling races (e.g. PTY drain timing), NOT real-LLM nondeterminism — those use llm_flaky, which rotates models per attempt.", "llm_flaky(reruns=2, reruns_delay=1): rerun a nondeterministic real-LLM test on failure, rotating to a different model each attempt (tests/_model_pools.py). Never apply to heavy e2e tests that can hit the CI --timeout=180 cap (rerun + loadscope can crash the shard).", "in_process_sessions: run a server integration test with the legacy in-process sessions path instead of sessions-native runner dispatch.", "nightly: runs only in the scheduled/dispatch pass of its suite; PR and push runs exclude it via -m 'not nightly'. Remove the marker to promote a burned-in test to the PR gate.", "compat_smoke: core server↔runner and UI↔server protocol boundary test. Must use mock LLM and exercise a cross-component interaction. Selected by the backwards-compat PR gate (-m compat_smoke) for a fast multi-test signal before the full overnight matrix.", "min_server_version(version): skip this test when the live server under test is older than `version` (PEP 440 release-tuple comparison, so a `.devN` of X satisfies X). Used by the server-version backwards-compat CI (docs/SERVER_VERSION_COMPAT_CI.md); inert in normal runs where the server is current.", "min_runner_version(version): skip this test when the pinned runner/host under test is older than `version` (PEP 440 release-tuple comparison, so a `.devN` of X satisfies X). Used by the runner/host backwards-compat CI (Config 2); inert in normal runs (no OMNIGENT_COMPAT_RUNNER_VERSION set).", "mock_only: tests/integration test that only works in mock-LLM mode (no --llm-api-key). Skipped by tests/integration/conftest.py when a real --llm-api-key is supplied (the real-LLM Integration jobs). Use for tests whose mock LLM is scripted with a fixed tool-call sequence — a real LLM cannot reproduce the scripted markers.", "visual: UI diff visual-regression snapshot (pytest-playwright-visual-snapshot). Runs only in the pinned-runner gate (.github/workflows/ui-snapshot.yml); the main e2e_ui suite excludes it via -m 'not visual' since it runs on the standard runner image.", "workspace_panel_product_default: UI test that must exercise the real collapsed-by-default Workspace panel state instead of the open-panel E2E baseline.", "smart_routing: end-to-end Smart Routing CUJ (tests/e2e/routing). Opt-in via OMNIGENT_E2E_SMART_ROUTING=1: launches a real omnigent host plus real claude/codex TUIs against a gateway. The routing service itself is an in-test mock, so no AI-Gateway task_v1 deployment is needed.", "live_model_flows: end-to-end model-flow CUJs (tests/e2e/omnigent/test_model_flows_live.py). Opt-in via OMNIGENT_E2E_MODEL_FLOWS=1: boots a real omnigent server + host from a checkout (OMNIGENT_E2E_MODEL_FLOWS_REPO overrides which one, enabling the red-on-main matrix) with real claude/codex logins, drives the real SPA in a browser, and asserts pane truth via tmux. Never runs in CI by accident; each landing PR runs it locally per docs/model-flows test plan.", "posix_only: test relies on POSIX-only behaviour (fork, PTY, tmux, Unix sockets, signals); auto-skipped on Windows by tests/conftest.py.", "windows_only: test relies on Windows-only behaviour (Job Objects, cmd.exe); auto-skipped on POSIX by tests/conftest.py.", ] # Visual snapshot baselines are committed here, keyed per-OS by the plugin # (e.g. test_empty_landing_matches_baseline[linux].png). Failure artifacts # (actual/expected/diff PNGs) land in the gitignored sibling dir. Both are # relative to the repo root, which is where pytest is always invoked. playwright_visual_snapshots_path = "tests/e2e_ui/visual/snapshots" playwright_visual_snapshot_failures_path = "tests/e2e_ui/visual/snapshot_failures" [tool.coverage.run] # Measure the shipped package only; tests live under tests/. source = ["omnigent"] # Each CI shard sets COVERAGE_FILE=artifacts/.coverage. and writes # one data file (pytest-cov already combines the xdist workers within a # shard). `relative_files` stores paths relative to the repo root so the # code-coverage workflow can `coverage combine` data files produced on # separate sharded runners with differing absolute checkout paths. relative_files = true [tool.coverage.report] show_missing = true # Report-only for now: no `fail_under`, so coverage never fails a build. # The code-coverage workflow reports against a plain `git checkout` that # has no generated files (e.g. omnigent/_build_info.py, written during # `uv sync` in the CI shards). Without this, `coverage report`/`xml` exit # 1 on "No source for code" and fail the step under `set -e`; ignore those # unreadable-source files instead. ignore_errors = true [tool.ruff] line-length = 99 target-version = "py312" # Vendored upstream code — not ours to restyle. # - ``omnigent/inner/databricks_mcps/google``: Google MCP server source. # - ``assistant-ui/``: assistant-ui.com React/Python suite imported as a # vendored snapshot; tracked upstream, no in-tree edits. extend-exclude = [ "omnigent/inner/databricks_mcps/google", "assistant-ui", # Protobuf-generated code (DO NOT EDIT); not ours to restyle. "omnigent/api/**/*_pb2.py", "omnigent/api/**/*_pb2.pyi", ] [tool.ruff.format] docstring-code-format = true [tool.ruff.lint] # ``E``/``F`` — pycodestyle + pyflakes baseline # ``I`` — import sorting # ``UP`` — pyupgrade (modernize syntax) # ``ARG`` — unused function arguments # ``BLE`` — blind ``except Exception`` # ``B`` — flake8-bugbear (latent bugs, e.g. mutable defaults, # unbound loop variables in closures, raise-from) # ``SIM`` — flake8-simplify (reduce unnecessary complexity) # ``RET`` — flake8-return (consistent return statements) # ``C4`` — flake8-comprehensions (cleaner list/dict/set comprehensions) # ``PIE`` — flake8-pie (misc correctness + style) # ``DTZ`` — flake8-datetimez (explicit timezone-aware datetimes) # ``LOG`` — flake8-logging (correct exception and logger usage) # ``RUF`` — ruff-specific (sorted ``__all__``, dangling asyncio # tasks, unused noqa, ...) select = ["E", "F", "I", "UP", "ARG", "BLE", "B", "SIM", "RET", "C4", "PIE", "DTZ", "LOG", "RUF"] ignore = [ # Preserve runtime type-alias, enum, and generic semantics. These rewrites # need compatibility review rather than automatic syntax modernization. "UP040", "UP042", "UP046", "UP047", # ARG003 (unused class-method args): too noisy on ABC-subclass signatures # where the arg is required by the interface contract. "ARG003", # RUF001/002/003 ambiguous-unicode: the repo uses ``—`` / ``–`` / ``×`` / # ``╱`` in docstrings and CLI UI strings intentionally. The rule was # designed for source strings that could be confused with ASCII and # throws off on legitimate typographic characters in prose. "RUF001", "RUF002", "RUF003", # SIM102/103/108: style-only transforms (collapse nested if / return # condition directly / rewrite if-else as ternary). Often make code # harder to read when the conditions are non-trivial. Kept available # as manual refactoring hints but not enforced. "SIM102", "SIM103", "SIM108", ] [tool.ruff.lint.isort] # Classify first-party imports by NAME, not by file existence. Ruff's # default detection resolves each module against the filesystem, so an # import of an optional submodule absent from a given checkout (e.g. # omnigent.onboarding.internal_beta) would be reclassified as third-party # there, demanding a different import order per checkout. Pinning the # package name keeps one canonical order everywhere. known-first-party = ["omnigent"] [tool.ruff.lint.per-file-ignores] # Tests legitimately accept parameters they don't use (pytest fixtures, # mock callback signatures) and intercept broad exceptions to assert on # error paths. Cleaning these up is a separate cleanup pass. # # RUF012 (mutable default class attribute): test fake classes and # Textual/pytest harnesses routinely declare class-level # ``BINDINGS = [...]`` / ``inputSchema: dict = {...}`` that are never # mutated — the rule's production-shared-state concern doesn't apply. # # B007 (loop var unused): occasional ``for i, x in enumerate(...)`` in # tests where only ``x`` matters; style not worth policing. # # B008 (function call in default): pytest / factory_boy idioms. # # SIM117 (combined with): already-legible nested ``with`` in test # fixtures that would be harder to read if merged. "tests/**/*.py" = [ "ARG001", "ARG002", "ARG004", "ARG005", "BLE001", "RUF012", "B007", "B008", "SIM117", ] # Example scripts predate the rule and are exempted until they graduate. "tests/resources/examples/**/*.py" = [ "ARG001", "ARG002", "ARG004", "ARG005", "BLE001", "RUF012", "B007", "B008", "SIM117", ] # Graduation backlog for code migrated from omnigent during the # unification. Pre-unification Omnigent' ruff config only selected # ``E, F, I, UP`` — none of the ARG/BLE/B/SIM/... rules — so these # files have a backlog of unused-arg, blind-except, and bugbear issues # that were invisible before. Graduate each subpath out of this list # as it's cleaned up. "omnigent/llms/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] "omnigent/onboarding/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] "omnigent/runtime/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] "omnigent/tools/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] # The native Antigravity (agy) transcript forwarder / supervisor is a # long-lived boundary loop that must restart on ANY non-cancellation # Exception (a crash there silently stops the web transcript mirror), so its # top-level catch is a deliberate, documented boundary catch — BLE001 is # waived for these top-level antigravity_native modules (see the inline # comments at each catch site). "omnigent/harnesses/antigravity_native/*.py" = ["BLE001"] # Telemetry modules intentionally catch all exceptions at every boundary — # telemetry must never disrupt the application under any circumstances. "omnigent/telemetry/*.py" = ["BLE001", "SIM105"] "sdks/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] # slack_bolt dispatches to handlers by parameter NAME (``body``, ``event``, # ``client``, ``ack``, ``view``), so unused handler args can't be renamed to # ``_`` without breaking injection. The bot also has deliberate boundary # catches (best-effort acks, background login tasks) that must never abort a # turn, hence BLE001. "integrations/slack/**/*.py" = [ "ARG001", "ARG002", "BLE001", "B008", "RUF012", ] # The sessions route is a facade over an implementation package. Its explicit # imports intentionally re-export implementation names and load route modules # for registration, so F401 remains expected in this package. "omnigent/server/routes/sessions/__init__.py" = ["ARG001", "ARG002", "BLE001", "E501", "F401"] "omnigent/server/routes/sessions/*.py" = ["ARG001", "ARG002", "BLE001", "E501", "F401"]