diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8c7bf663c..e1206f497 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.10.3" + ".": "0.10.4" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 9035f51a3..bf0e07b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.10.4 (2026-04-30) + +Full Changelog: [v0.10.3...v0.10.4](https://github.com/scaleapi/scale-agentex-python/compare/v0.10.3...v0.10.4) + ## 0.10.3 (2026-04-30) Full Changelog: [v0.10.2...v0.10.3](https://github.com/scaleapi/scale-agentex-python/compare/v0.10.2...v0.10.3) diff --git a/pyproject.toml b/pyproject.toml index e85ca9af8..eeb562e0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agentex-sdk" -version = "0.10.3" +version = "0.10.4" description = "The official Python library for the agentex API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/agentex/_version.py b/src/agentex/_version.py index f082e645b..f9812cb77 100644 --- a/src/agentex/_version.py +++ b/src/agentex/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "agentex" -__version__ = "0.10.3" # x-release-please-version +__version__ = "0.10.4" # x-release-please-version diff --git a/src/agentex/lib/sdk/config/environment_config.py b/src/agentex/lib/sdk/config/environment_config.py index d4290903f..cad86419a 100644 --- a/src/agentex/lib/sdk/config/environment_config.py +++ b/src/agentex/lib/sdk/config/environment_config.py @@ -165,6 +165,17 @@ def get_configs_for_env(self, env_target: str) -> dict[str, AgentEnvironmentConf account_id: 6887f093600ecd59bbbd3095 helm_overrides: + The principal must contain exactly one of `user_id` or `service_account_id`. + Use `service_account_id` to register an agent under a service account + instead of a personal user identity: + dev: + kubernetes: + namespace: "sgp-000-hello-acp" + auth: + principal: + service_account_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + account_id: 6887f093600ecd59bbbd3095 + if the environment field is not explicitly set, we assume its the same as the name of the environment Args: diff --git a/src/agentex/lib/sdk/config/validation.py b/src/agentex/lib/sdk/config/validation.py index d051fa1ae..f4853de7f 100644 --- a/src/agentex/lib/sdk/config/validation.py +++ b/src/agentex/lib/sdk/config/validation.py @@ -103,11 +103,14 @@ def _validate_single_environment_config(env_name: str, env_config: AgentEnvironm # Validate auth principal principal = env_config.auth.principal - if not principal.get("user_id"): - raise ValueError("Auth principal must contain non-empty 'user_id'") + user_id = principal.get("user_id") + service_account_id = principal.get("service_account_id") + if not user_id and not service_account_id: + raise ValueError("Auth principal must contain non-empty 'user_id' or 'service_account_id'") + if user_id and service_account_id: + raise ValueError("Auth principal must contain only one of 'user_id' or 'service_account_id', not both") # Check for environment-specific user_id patterns - user_id = principal["user_id"] if isinstance(user_id, str): if not any(env_name.lower() in user_id.lower() for env_name in ["dev", "prod", "staging", env_name]): logger.warning( @@ -233,11 +236,12 @@ def generate_helpful_error_message(error: Exception, context: str = "") -> str: "1. Check file location: should be next to manifest.yaml\n" "2. Verify file permissions" ) - elif "user_id" in base_msg.lower(): + elif "user_id" in base_msg.lower() or "service_account_id" in base_msg.lower(): base_msg += ( "\n\nšŸ’” Auth Principal Tips:\n" - "- user_id should be unique per environment\n" - "- Include environment name (e.g., 'dev_my_agent')\n" + "- Set exactly one of 'user_id' or 'service_account_id'\n" + "- The id should be unique per environment\n" + "- For user_id, include environment name (e.g., 'dev_my_agent')\n" "- Use consistent naming convention across agents" ) elif "namespace" in base_msg.lower(): diff --git a/tests/lib/cli/test_validation.py b/tests/lib/cli/test_validation.py new file mode 100644 index 000000000..c921d46e3 --- /dev/null +++ b/tests/lib/cli/test_validation.py @@ -0,0 +1,76 @@ +"""Tests for the auth-principal portion of the environments.yaml validator. + +Covers the rule that an env config's principal must carry exactly one of +`user_id` or `service_account_id` — the same shape that downstream services +(agentex-auth, SGP) expect on the wire. +""" + +import pytest + +from agentex.lib.sdk.config.validation import ( + EnvironmentsValidationError, + validate_environments_config, +) +from agentex.lib.sdk.config.environment_config import ( + AgentAuthConfig, + AgentKubernetesConfig, + AgentEnvironmentConfig, + AgentEnvironmentsConfig, +) + + +def _config_with_principal(principal: dict) -> AgentEnvironmentsConfig: + return AgentEnvironmentsConfig( + schema_version="v1", + environments={ + "dev": AgentEnvironmentConfig( + kubernetes=AgentKubernetesConfig(namespace="dev-ns"), + auth=AgentAuthConfig(principal=principal), + ) + }, + ) + + +def test_user_only_principal_passes(): + """Existing user_id-only configs continue to validate (backwards compat).""" + config = _config_with_principal({"user_id": "73d0c8bd-4726-434c-9686-eb627d89f078", "account_id": "acct-1"}) + + validate_environments_config(config) + + +def test_service_account_only_principal_passes(): + """New service_account_id-only configs validate.""" + config = _config_with_principal( + {"service_account_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "account_id": "acct-1"} + ) + + validate_environments_config(config) + + +def test_principal_with_neither_id_is_rejected(): + """A principal with no identity id fails fast with a clear error.""" + config = _config_with_principal({"account_id": "acct-1"}) + + with pytest.raises(EnvironmentsValidationError) as exc_info: + validate_environments_config(config) + + msg = str(exc_info.value) + assert "user_id" in msg + assert "service_account_id" in msg + + +def test_principal_with_both_ids_is_rejected(): + """Setting both ids is a config error — the principal must commit to one identity type.""" + config = _config_with_principal( + { + "user_id": "73d0c8bd-4726-434c-9686-eb627d89f078", + "service_account_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "account_id": "acct-1", + } + ) + + with pytest.raises(EnvironmentsValidationError) as exc_info: + validate_environments_config(config) + + msg = str(exc_info.value) + assert "only one of" in msg.lower() or "not both" in msg.lower()