From c1f457c36f2949bc3cb723025c4975638e8e1ce0 Mon Sep 17 00:00:00 2001 From: API Engineering Date: Tue, 13 Oct 2026 06:58:26 +0000 Subject: [PATCH] [bot] Updated client based on openapi-0dd8470/clientgen --- DO_OPENAPI_COMMIT_SHA.txt | 2 +- src/pydo/_client.py | 24 + src/pydo/aio/_client.py | 24 + src/pydo/aio/operations/__init__.py | 8 + src/pydo/aio/operations/_operations.py | 121628 ++++++++++------- src/pydo/operations/__init__.py | 8 + src/pydo/operations/_operations.py | 152157 +++++++++++++--------- 7 files changed, 161754 insertions(+), 112097 deletions(-) diff --git a/DO_OPENAPI_COMMIT_SHA.txt b/DO_OPENAPI_COMMIT_SHA.txt index 67d44682..1a19225d 100644 --- a/DO_OPENAPI_COMMIT_SHA.txt +++ b/DO_OPENAPI_COMMIT_SHA.txt @@ -1 +1 @@ -97dca99 +0dd8470 diff --git a/src/pydo/_client.py b/src/pydo/_client.py index c59a0d6d..3555b29d 100644 --- a/src/pydo/_client.py +++ b/src/pydo/_client.py @@ -37,6 +37,7 @@ FunctionsAccessKeyOperations, FunctionsOperations, GenaiOperations, + HarnessOperations, ImageActionsOperations, ImagesOperations, InferenceOperations, @@ -45,6 +46,8 @@ KubernetesOperations, LoadBalancersOperations, McpServersOperations, + MicrovmCheckpointsOperations, + MicrovmsOperations, MonitoringOperations, NfsOperations, OneClicksOperations, @@ -63,6 +66,7 @@ ReservedIPv6Operations, SecurityOperations, SessionsOperations, + SignalsOperations, SizesOperations, SnapshotsOperations, SpacesKeyOperations, @@ -118,6 +122,8 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype actions: pydo.operations.ActionsOperations :ivar addons: AddonsOperations operations :vartype addons: pydo.operations.AddonsOperations + :ivar harness: HarnessOperations operations + :vartype harness: pydo.operations.HarnessOperations :ivar apps: AppsOperations operations :vartype apps: pydo.operations.AppsOperations :ivar cdn: CdnOperations operations @@ -160,10 +166,16 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype image_actions: pydo.operations.ImageActionsOperations :ivar insights: InsightsOperations operations :vartype insights: pydo.operations.InsightsOperations + :ivar signals: SignalsOperations operations + :vartype signals: pydo.operations.SignalsOperations :ivar kubernetes: KubernetesOperations operations :vartype kubernetes: pydo.operations.KubernetesOperations :ivar load_balancers: LoadBalancersOperations operations :vartype load_balancers: pydo.operations.LoadBalancersOperations + :ivar microvms: MicrovmsOperations operations + :vartype microvms: pydo.operations.MicrovmsOperations + :ivar microvm_checkpoints: MicrovmCheckpointsOperations operations + :vartype microvm_checkpoints: pydo.operations.MicrovmCheckpointsOperations :ivar monitoring: MonitoringOperations operations :vartype monitoring: pydo.operations.MonitoringOperations :ivar nfs: NfsOperations operations @@ -309,6 +321,9 @@ def __init__( self.addons = AddonsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.harness = HarnessOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.apps = AppsOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -372,12 +387,21 @@ def __init__( self.insights = InsightsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.signals = SignalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.kubernetes = KubernetesOperations( self._client, self._config, self._serialize, self._deserialize ) self.load_balancers = LoadBalancersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.microvms = MicrovmsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.microvm_checkpoints = MicrovmCheckpointsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.monitoring = MonitoringOperations( self._client, self._config, self._serialize, self._deserialize ) diff --git a/src/pydo/aio/_client.py b/src/pydo/aio/_client.py index cb653108..1b7f4ae5 100644 --- a/src/pydo/aio/_client.py +++ b/src/pydo/aio/_client.py @@ -37,6 +37,7 @@ FunctionsAccessKeyOperations, FunctionsOperations, GenaiOperations, + HarnessOperations, ImageActionsOperations, ImagesOperations, InferenceOperations, @@ -45,6 +46,8 @@ KubernetesOperations, LoadBalancersOperations, McpServersOperations, + MicrovmCheckpointsOperations, + MicrovmsOperations, MonitoringOperations, NfsOperations, OneClicksOperations, @@ -63,6 +66,7 @@ ReservedIPv6Operations, SecurityOperations, SessionsOperations, + SignalsOperations, SizesOperations, SnapshotsOperations, SpacesKeyOperations, @@ -118,6 +122,8 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype actions: pydo.aio.operations.ActionsOperations :ivar addons: AddonsOperations operations :vartype addons: pydo.aio.operations.AddonsOperations + :ivar harness: HarnessOperations operations + :vartype harness: pydo.aio.operations.HarnessOperations :ivar apps: AppsOperations operations :vartype apps: pydo.aio.operations.AppsOperations :ivar cdn: CdnOperations operations @@ -160,10 +166,16 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype image_actions: pydo.aio.operations.ImageActionsOperations :ivar insights: InsightsOperations operations :vartype insights: pydo.aio.operations.InsightsOperations + :ivar signals: SignalsOperations operations + :vartype signals: pydo.aio.operations.SignalsOperations :ivar kubernetes: KubernetesOperations operations :vartype kubernetes: pydo.aio.operations.KubernetesOperations :ivar load_balancers: LoadBalancersOperations operations :vartype load_balancers: pydo.aio.operations.LoadBalancersOperations + :ivar microvms: MicrovmsOperations operations + :vartype microvms: pydo.aio.operations.MicrovmsOperations + :ivar microvm_checkpoints: MicrovmCheckpointsOperations operations + :vartype microvm_checkpoints: pydo.aio.operations.MicrovmCheckpointsOperations :ivar monitoring: MonitoringOperations operations :vartype monitoring: pydo.aio.operations.MonitoringOperations :ivar nfs: NfsOperations operations @@ -309,6 +321,9 @@ def __init__( self.addons = AddonsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.harness = HarnessOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.apps = AppsOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -372,12 +387,21 @@ def __init__( self.insights = InsightsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.signals = SignalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.kubernetes = KubernetesOperations( self._client, self._config, self._serialize, self._deserialize ) self.load_balancers = LoadBalancersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.microvms = MicrovmsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.microvm_checkpoints = MicrovmCheckpointsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.monitoring = MonitoringOperations( self._client, self._config, self._serialize, self._deserialize ) diff --git a/src/pydo/aio/operations/__init__.py b/src/pydo/aio/operations/__init__.py index 163af5e7..7a4d3dd1 100644 --- a/src/pydo/aio/operations/__init__.py +++ b/src/pydo/aio/operations/__init__.py @@ -17,6 +17,7 @@ from ._operations import SshKeysOperations from ._operations import ActionsOperations from ._operations import AddonsOperations +from ._operations import HarnessOperations from ._operations import AppsOperations from ._operations import CdnOperations from ._operations import CertificatesOperations @@ -38,8 +39,11 @@ from ._operations import ImagesOperations from ._operations import ImageActionsOperations from ._operations import InsightsOperations +from ._operations import SignalsOperations from ._operations import KubernetesOperations from ._operations import LoadBalancersOperations +from ._operations import MicrovmsOperations +from ._operations import MicrovmCheckpointsOperations from ._operations import MonitoringOperations from ._operations import NfsOperations from ._operations import OrganizationsOperations @@ -91,6 +95,7 @@ "SshKeysOperations", "ActionsOperations", "AddonsOperations", + "HarnessOperations", "AppsOperations", "CdnOperations", "CertificatesOperations", @@ -112,8 +117,11 @@ "ImagesOperations", "ImageActionsOperations", "InsightsOperations", + "SignalsOperations", "KubernetesOperations", "LoadBalancersOperations", + "MicrovmsOperations", + "MicrovmCheckpointsOperations", "MonitoringOperations", "NfsOperations", "OrganizationsOperations", diff --git a/src/pydo/aio/operations/_operations.py b/src/pydo/aio/operations/_operations.py index 3273a2ff..52ab8593 100644 --- a/src/pydo/aio/operations/_operations.py +++ b/src/pydo/aio/operations/_operations.py @@ -337,9 +337,9 @@ build_genai_get_scenario_set_download_url_request, build_genai_get_scenario_set_request, build_genai_get_scheduled_indexing_request, - build_genai_get_simulation_journey_request, - build_genai_get_simulation_journey_trajectory_request, - build_genai_get_simulation_journey_trajectory_url_request, + build_genai_get_simulation_episode_request, + build_genai_get_simulation_episode_trajectory_request, + build_genai_get_simulation_episode_trajectory_url_request, build_genai_get_simulation_run_request, build_genai_get_workspace_request, build_genai_import_custom_model_request, @@ -376,7 +376,7 @@ build_genai_list_scenario_library_scenarios_request, build_genai_list_scenario_sets_request, build_genai_list_scenarios_request, - build_genai_list_simulation_journeys_request, + build_genai_list_simulation_episodes_request, build_genai_list_simulation_runs_request, build_genai_list_workspaces_request, build_genai_patch_cancel_simulation_run_request, @@ -403,6 +403,60 @@ build_genai_update_scenario_set_request, build_genai_update_simulation_run_request, build_genai_update_workspace_request, + build_harness_cancel_session_workspace_transfer_request, + build_harness_cancel_trigger_execution_request, + build_harness_commit_session_workspace_transfer_request, + build_harness_create_config_clone_request, + build_harness_create_config_request, + build_harness_create_session_checkpoint_request, + build_harness_create_session_fork_request, + build_harness_create_session_input_request, + build_harness_create_session_request, + build_harness_create_session_workspace_part_upload_urls_request, + build_harness_create_session_workspace_transfer_request, + build_harness_create_session_workspace_upload_request, + build_harness_create_template_request, + build_harness_create_trigger_request, + build_harness_delete_config_request, + build_harness_delete_session_checkpoint_request, + build_harness_delete_session_request, + build_harness_delete_template_request, + build_harness_delete_trigger_request, + build_harness_get_config_request, + build_harness_get_session_checkpoint_request, + build_harness_get_session_events_request, + build_harness_get_session_ingress_request, + build_harness_get_session_port_forward_request, + build_harness_get_session_request, + build_harness_get_session_workspace_download_request, + build_harness_get_session_workspace_transfer_request, + build_harness_get_template_build_request, + build_harness_get_template_request, + build_harness_get_trigger_by_session_request, + build_harness_get_trigger_execution_request, + build_harness_get_trigger_request, + build_harness_list_config_sessions_request, + build_harness_list_configs_request, + build_harness_list_reusable_sessions_request, + build_harness_list_session_checkpoints_request, + build_harness_list_session_sandbox_sizes_request, + build_harness_list_sessions_request, + build_harness_list_template_builds_request, + build_harness_list_templates_request, + build_harness_list_trigger_executions_request, + build_harness_list_triggers_request, + build_harness_list_webhook_providers_request, + build_harness_post_session_hitl_request, + build_harness_post_session_pause_request, + build_harness_post_session_resume_request, + build_harness_post_session_sandbox_exec_request, + build_harness_post_trigger_rotate_secret_request, + build_harness_post_trigger_webhook_request, + build_harness_revert_session_checkpoint_request, + build_harness_update_session_request, + build_harness_update_template_request, + build_harness_update_trigger_request, + build_harness_validate_session_policy_request, build_image_actions_get_request, build_image_actions_list_request, build_image_actions_post_request, @@ -442,10 +496,13 @@ build_insights_get_prom_query_range_request, build_insights_get_prom_query_request, build_insights_get_prom_series_request, + build_insights_get_trace_request, build_insights_list_alert_instances_request, build_insights_list_alert_rules_request, build_insights_list_notification_channels_request, build_insights_post_logs_search_request, + build_insights_post_spans_search_request, + build_insights_post_traces_search_request, build_insights_update_alert_rule_request, build_insights_update_notification_channel_request, build_invoices_get_by_uuid_request, @@ -499,6 +556,19 @@ build_mcp_servers_patch_request, build_mcp_servers_run_resync_request, build_mcp_servers_update_tools_request, + build_microvm_checkpoints_create_request, + build_microvm_checkpoints_delete_request, + build_microvm_checkpoints_get_request, + build_microvm_checkpoints_list_request, + build_microvms_create_request, + build_microvms_delete_request, + build_microvms_get_console_request, + build_microvms_get_options_request, + build_microvms_get_request, + build_microvms_list_request, + build_microvms_post_pause_request, + build_microvms_post_resume_request, + build_microvms_run_command_request, build_monitoring_create_alert_policy_request, build_monitoring_create_destination_request, build_monitoring_create_sink_request, @@ -680,6 +750,20 @@ build_sessions_delete_request, build_sessions_list_request, build_sessions_list_search_request, + build_signals_create_deletion_request, + build_signals_create_export_request, + build_signals_get_consent_request, + build_signals_get_deletion_request, + build_signals_get_export_download_request, + build_signals_get_export_options_request, + build_signals_get_export_request, + build_signals_list_agent_sessions_request, + build_signals_list_consents_request, + build_signals_list_deletions_request, + build_signals_list_exports_request, + build_signals_list_session_dialogues_request, + build_signals_update_consent_request, + build_signals_update_team_consent_request, build_sizes_list_request, build_snapshots_delete_request, build_snapshots_get_request, @@ -14281,14 +14365,14 @@ async def patch_plan( return cast(JSON, deserialized) # type: ignore -class AppsOperations: # pylint: disable=too-many-public-methods +class HarnessOperations: # pylint: disable=too-many-public-methods """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~pydo.aio.GeneratedClient`'s - :attr:`apps` attribute. + :attr:`harness` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -14301,27 +14385,28 @@ def __init__(self, *args, **kwargs) -> None: ) @distributed_trace_async - async def list( + async def list_configs( self, *, - page: int = 1, - per_page: int = 20, - with_projects: Optional[bool] = None, + page_size: int = 50, + page_token: Optional[str] = None, + search: Optional[str] = None, **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """List All Apps. + """List active Environment Configs for the caller's team. - List all apps on your account. Information about the current active deployment as well as any - in progress ones will also be included for each app. + Returns lightweight team-scoped config metadata without loading or returning environment-spec + JSON. Soft-deleted configs are excluded. - :keyword page: Which 'page' of paginated results to return. Default value is 1. - :paramtype page: int - :keyword per_page: Number of items returned per page. Default value is 20. - :paramtype per_page: int - :keyword with_projects: Whether the project_id of listed apps should be fetched and included. - Default value is None. - :paramtype with_projects: bool + :keyword page_size: Page size. Defaults to 50 when omitted. Values above 200 are rejected. + Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Default value is None. + :paramtype page_token: str + :keyword search: Optional case-insensitive substring filter on config name (contains). Empty or + omitted returns the unfiltered page. At most 64 characters. Default value is None. + :paramtype search: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -14331,3119 +14416,19844 @@ async def list( # response body for status code(s): 200 response == { - "meta": { - "total": 0 # Optional. Number of objects returned by the request. - }, - "apps": [ + "configs": [ { + "agentspec_schema_version": "str", # The manifest contract + the server accepted at create time: the manifest's own apiVersion for + legacy envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that + created the config. Required. + "id": "str", # Required. + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00" # Required. + } + ], + "next_page_token": "str" # Optional. + } + # response body for status code(s): 400 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_configs_request( + page_size=page_size, + page_token=page_token, + search=search, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_config( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "manifest_yaml": "str", # Customer environment-spec YAML text. The HTTP JSON + body is capped at 1 MiB and this field is capped at 512 KiB. Required. + "name": "str", # Config name, case-insensitively unique among the team's + active configs. Required. + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent to + Insights. + "metrics": bool, # Optional. Accepted and stored for parity with the + Insights schema, but metrics are always collected: unregistered metrics are + dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are sent to + Insights. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, "spec": { - "name": "str", # The name of the app. Must be unique - across all apps in the same account. Required. - "databases": [ - { - "name": "str", # The database's - name. The name must be unique across all components within - the same app and cannot use capital letters. Required. - "cluster_name": "str", # Optional. - The name of the underlying DigitalOcean DBaaS cluster. This - is required for production databases. For dev databases, if - cluster_name is not set, a new cluster will be provisioned. - "db_name": "str", # Optional. The - name of the MySQL or PostgreSQL database to configure. - "db_user": "str", # Optional. The - name of the MySQL or PostgreSQL user to configure. - "engine": "UNSET", # Optional. - Default value is "UNSET". * MYSQL: MySQL * PG: PostgreSQL * - REDIS: Caching * MONGODB: MongoDB * KAFKA: Kafka * - OPENSEARCH: OpenSearch * VALKEY: ValKey. Known values are: - "UNSET", "MYSQL", "PG", "REDIS", "MONGODB", "KAFKA", - "OPENSEARCH", and "VALKEY". - "production": bool, # Optional. - Whether this is a production or dev database. - "version": "str" # Optional. The - version of the database engine. + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] } - ], - "disable_edge_cache": False, # Optional. Default - value is False. .. role:: raw-html-m2r(raw) :format: html If set - to ``true``"" , the app will **not** be cached at the edge (CDN). - Enable this option if you want to manage CDN configuration - yourself"u2014whether by using an external CDN provider or by - handling static content and caching within your app. This setting is - also recommended for apps that require real-time data or serve - dynamic content, such as those using Server-Sent Events (SSE) over - GET, or hosting an MCP (Model Context Protocol) Server that utilizes - SSE."" :raw-html-m2r:`
` **Note:** This feature is not available - for static site components."" :raw-html-m2r:`
` For more - information, see `Disable CDN Cache - `_. - "disable_email_obfuscation": False, # Optional. - Default value is False. If set to ``true``"" , email addresses in the - app will not be obfuscated. This is useful for apps that require - email addresses to be visible (in the HTML markup). - "domains": [ - { - "domain": "str", # The hostname for - the domain. Required. - "minimum_tls_version": "str", # - Optional. The minimum version of TLS a client application can - use to access resources for the domain. Must be one of the - following values wrapped within quotations: ``"1.2"`` or - ``"1.3"``. Known values are: "1.2" and "1.3". - "type": "UNSPECIFIED", # Optional. - Default value is "UNSPECIFIED". * DEFAULT: The default - ``.ondigitalocean.app`` domain assigned to this app * - PRIMARY: The primary domain for this app that is displayed as - the default in the control panel, used in bindable - environment variables, and any other places that reference an - app's live URL. Only one domain may be set as primary. * - ALIAS: A non-primary domain. Known values are: "UNSPECIFIED", - "DEFAULT", "PRIMARY", and "ALIAS". - "wildcard": bool, # Optional. - Indicates whether the domain includes all sub-domains, in - addition to the given domain. - "zone": "str" # Optional. Optional. - If the domain uses DigitalOcean DNS and you would like App - Platform to automatically manage it for you, set this to the - name of the domain on your account. For example, If the - domain you are adding is ``app.domain.com``"" , the zone - could be ``domain.com``. + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". } - ], - "egress": { - "type": "AUTOASSIGN" # Optional. Default - value is "AUTOASSIGN". The app egress type. Known values are: - "AUTOASSIGN" and "DEDICATED_IP". }, - "enhanced_threat_control_enabled": False, # - Optional. Default value is False. If set to ``true``"" , suspicious - requests will go through additional security checks to help mitigate - layer 7 DDoS attacks. - "functions": [ - { - "name": "str", # The name. Must be - unique across all components within the same app. Required. - "alerts": [ + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ { - "disabled": bool, # - Optional. Is the alert disabled?. - "operator": - "UNSPECIFIED_OPERATOR", # Optional. Default value is - "UNSPECIFIED_OPERATOR". Known values are: - "UNSPECIFIED_OPERATOR", "GREATER_THAN", and - "LESS_THAN". - "rule": - "UNSPECIFIED_RULE", # Optional. Default value is - "UNSPECIFIED_RULE". Known values are: - "UNSPECIFIED_RULE", "CPU_UTILIZATION", - "MEM_UTILIZATION", "RESTART_COUNT", - "DEPLOYMENT_FAILED", "DEPLOYMENT_LIVE", - "DOMAIN_FAILED", "DOMAIN_LIVE", "AUTOSCALE_FAILED", - "AUTOSCALE_SUCCEEDED", "FUNCTIONS_ACTIVATION_COUNT", - "FUNCTIONS_AVERAGE_DURATION_MS", - "FUNCTIONS_ERROR_RATE_PER_MINUTE", - "FUNCTIONS_AVERAGE_WAIT_TIME_MS", - "FUNCTIONS_ERROR_COUNT", - "FUNCTIONS_GB_RATE_PER_SECOND", - "REQUESTS_PER_SECOND", and "REQUEST_DURATION_P95_MS". - "value": 0.0, # - Optional. Threshold value for alert. - "window": - "UNSPECIFIED_WINDOW" # Optional. Default value is - "UNSPECIFIED_WINDOW". Known values are: - "UNSPECIFIED_WINDOW", "FIVE_MINUTES", "TEN_MINUTES", - "THIRTY_MINUTES", and "ONE_HOUR". + "host": "str" # + Required. } ], - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "cors": { - "allow_credentials": bool, # - Optional. Whether browsers should expose the response to - the client-side JavaScript code when the request"u2019s - credentials mode is include. This configures the - ``Access-Control-Allow-Credentials`` header. - "allow_headers": [ - "str" # Optional. - The set of allowed HTTP request headers. This - configures the ``Access-Control-Allow-Headers`` - header. - ], - "allow_methods": [ + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ "str" # Optional. - The set of allowed HTTP methods. This configures the - ``Access-Control-Allow-Methods`` header. - ], - "allow_origins": [ - { - "exact": - "str", # Optional. Exact string match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. An empty string is a valid value and will be - serialized explicitly rather than being omitted - from the payload. - "prefix": - "str", # Optional. Prefix-based match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. - "regex": - "str" # Optional. RE2 style regex-based match. - Only 1 of ``exact``"" , ``prefix``"" , or - ``regex`` must be set. For more information about - RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. ], - "expose_headers": [ + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ "str" # Optional. - The set of HTTP response headers that browsers are - allowed to access. This configures the - ``Access-Control-Expose-Headers`` header. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. ], - "max_age": "str" # Optional. - An optional duration specifying how long browsers can - cache the results of a preflight request. This configures - the ``Access-Control-Max-Age`` header. - }, - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_config( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "log_destinations": [ + } + ] + }, + "sandbox": { + "egress": { + "allow": [ { - "name": "str", # + "host": "str" # Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. - } - } - ], - "routes": [ - { - "path": "str", # - Optional. (Deprecated - Use Ingress Rules instead). - An HTTP path prefix. Paths must start with / and must - be unique across all components within an app. - "preserve_path_prefix": bool # Optional. An optional - flag to preserve the path that is forwarded to the - backend service. By default, the HTTP request path - will be trimmed from the left when forwarded to the - component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. } ], - "source_dir": "str" # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. } ], - "ingress": { - "custom_error_page_url": "str", # Optional. - Optional HTTPS URL of a custom error page to display when the app - is unreachable. The page is shown in a full-viewport iframe. The - target must allow framing: avoid ``X-Frame-Options: DENY`` and a - restrictive ``Content-Security-Policy`` ``frame-ancestors`` that - blocks the platform. If omitted, the default platform error page - is used. - "rules": [ + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ { - "component": { - "name": "str", # The - name of the component to route to. Required. - "preserve_path_prefix": "str", # Optional. An - optional flag to preserve the path that is forwarded - to the backend service. By default, the HTTP request - path will be trimmed from the left when forwarded to - the component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. Note: this is not - applicable for Functions Components and is mutually - exclusive with ``rewrite``. - "rewrite": "str" # - Optional. An optional field that will rewrite the - path of the component to be what is specified here. - By default, the HTTP request path will be trimmed - from the left when forwarded to the component. For - example, a component with ``path=/api`` will have - requests to ``/api/list`` trimmed to ``/list``. If - you specified the rewrite to be ``/v1/``"" , requests - to ``/api/list`` would be rewritten to ``/v1/list``. - Note: this is mutually exclusive with - ``preserve_path_prefix``. - }, - "cors": { - "allow_credentials": - bool, # Optional. Whether browsers should expose the - response to the client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the ``Access-Control-Allow-Credentials`` - header. - "allow_headers": [ - "str" # - Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" # - Optional. The set of allowed HTTP methods. This - configures the ``Access-Control-Allow-Methods`` - header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. An empty string is - a valid value and will be serialized - explicitly rather than being omitted from the - payload. - "prefix": "str", # Optional. Prefix-based - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. For - more information about RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # - Optional. The set of HTTP response headers that - browsers are allowed to access. This configures - the ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # - Optional. An optional duration specifying how long - browsers can cache the results of a preflight - request. This configures the - ``Access-Control-Max-Age`` header. + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_config(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "manifest_yaml": "str", # Customer environment-spec YAML text. The HTTP JSON + body is capped at 1 MiB and this field is capped at 512 KiB. Required. + "name": "str", # Config name, case-insensitively unique among the team's + active configs. Required. + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent to + Insights. + "metrics": bool, # Optional. Accepted and stored for parity with the + Insights schema, but metrics are always collected: unregistered metrics are + dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are sent to + Insights. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". "match": { - "authority": { - "exact": - "str" # Exact string match. An empty string is a - valid value and will be serialized explicitly - rather than being omitted from the payload. - Required. - }, - "path": { - "prefix": - "str" # Prefix-based match. For example, - ``/api`` will match ``/api``"" , ``/api/``"" , - and any nested paths such as - ``/api/v1/endpoint``. Required. - } - }, - "redirect": { - "authority": "str", - # Optional. The authority/host to redirect to. This - can be a hostname or IP address. Note: use ``port`` - to set the port. - "port": 0, # - Optional. The port to redirect to. - "redirect_code": 0, - # Optional. The redirect code to use. Defaults to - ``302``. Supported values are 300, 301, 302, 303, - 304, 307, 308. - "scheme": "str", # - Optional. The scheme to redirect to. Supported values - are ``http`` or ``https``. Default: ``https``. - "uri": "str" # - Optional. An optional URI path to redirect to. Note: - if this is specified the whole URI of the original - request will be overwritten to this value, - irrespective of the original request URI being - matched. + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. } } ] }, - "jobs": [ - { - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "build_command": "str", # Optional. - An optional build command to run while building this - component from source. - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root of - the repo. If set, it will be used to build this component. - Otherwise, App Platform will attempt to build it using - buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. - } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": bool # - Optional. Whether to automatically deploy new images. - Can only be used for images hosted in DOCR and can - only be used with an image tag, not a specific - digest. - }, - "digest": "str", # Optional. - The image digest. Cannot be specified if tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for the - ``DOCR`` registry type. - "registry_credentials": - "str", # Optional. The credentials to be able to pull - the image. The value will be encrypted on first - submission. On following submissions, the encrypted value - should be used. * "$username:$access_token" for - registries of type ``DOCKER_HUB``. * - "$username:$access_token" for registries of type - ``GHCR``. - "registry_type": "str", # - Optional. * DOCKER_HUB: The DockerHub container registry - type. * DOCR: The DigitalOcean container registry type. * - GHCR: The Github container registry type. Known values - are: "DOCKER_HUB", "DOCR", and "GHCR". - "repository": "str", # - Optional. The repository name. - "tag": "latest" # Optional. - Default value is "latest". The repository tag. Defaults - to ``latest`` if not provided and no digest is provided. - Cannot be specified if digest is provided. - }, - "instance_count": 1, # Optional. - Default value is 1. The amount of instances that this - component should be scaled to. Default: 1. Must not be set if - autoscaling is used. - "instance_size_slug": {}, - "kind": "UNSPECIFIED", # Optional. - Default value is "UNSPECIFIED". * UNSPECIFIED: Default job - type, will auto-complete to POST_DEPLOY kind. * PRE_DEPLOY: - Indicates a job that runs before an app deployment. * - POST_DEPLOY: Indicates a job that runs after an app - deployment. * FAILED_DEPLOY: Indicates a job that runs after - a component fails to deploy. Known values are: "UNSPECIFIED", - "PRE_DEPLOY", "POST_DEPLOY", and "FAILED_DEPLOY". - "log_destinations": [ + "sandbox": { + "egress": { + "allow": [ { - "name": "str", # + "host": "str" # Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. - } } ], - "name": "str", # Optional. The name. - Must be unique across all components within the same app. - "run_command": "str", # Optional. An - optional run command to override the component's default. - "source_dir": "str", # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. - "termination": { - "grace_period_seconds": 0 # - Optional. The number of seconds to wait between sending a - TERM signal to a container and issuing a KILL which - causes immediate shutdown. (Default 120). + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. } ], - "maintenance": { - "archive": bool, # Optional. Indicates - whether the app should be archived. Setting this to true implies - that enabled is set to true. - "enabled": bool, # Optional. Indicates - whether maintenance mode should be enabled for the app. - "offline_page_url": "str" # Optional. A - custom offline page to display when maintenance mode is enabled - or the app is archived. + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). }, - "region": "str", # Optional. The slug form of the - geographical origin of the app. Default: ``nearest available``. Known - values are: "atl", "nyc", "sfo", "tor", "ams", "fra", "lon", "blr", - "sgp", and "syd". - "services": [ + "skills": [ { - "autoscaling": { - "max_instance_count": 0, # - Optional. The maximum amount of instances for this - component. Maximum 250. Consider using a larger instance - size if your application requires more than 250 - instances. - "metrics": { - "request_duration": { - "p95_milliseconds": 0 # Optional. The p95 target - request duration in milliseconds for the - component. - }, - "requests_per_second": { - "per_instance": 0 # Optional. The target number - of requests per second per instance for the - component. - } + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. }, - "min_instance_count": 0 # - Optional. The minimum amount of instances for this - component. - }, - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "build_command": "str", # Optional. - An optional build command to run while building this - component from source. - "cors": { - "allow_credentials": bool, # - Optional. Whether browsers should expose the response to - the client-side JavaScript code when the request"u2019s - credentials mode is include. This configures the - ``Access-Control-Allow-Credentials`` header. - "allow_headers": [ + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ "str" # Optional. - The set of allowed HTTP request headers. This - configures the ``Access-Control-Allow-Headers`` - header. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. ], - "allow_methods": [ + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ "str" # Optional. - The set of allowed HTTP methods. This configures the - ``Access-Control-Allow-Methods`` header. - ], - "allow_origins": [ - { - "exact": - "str", # Optional. Exact string match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. An empty string is a valid value and will be - serialized explicitly rather than being omitted - from the payload. - "prefix": - "str", # Optional. Prefix-based match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. - "regex": - "str" # Optional. RE2 style regex-based match. - Only 1 of ``exact``"" , ``prefix``"" , or - ``regex`` must be set. For more information about - RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. ], - "expose_headers": [ - "str" # Optional. - The set of HTTP response headers that browsers are - allowed to access. This configures the - ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # Optional. - An optional duration specifying how long browsers can - cache the results of a preflight request. This configures - the ``Access-Control-Max-Age`` header. - }, - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root of - the repo. If set, it will be used to build this component. - Otherwise, App Platform will attempt to build it using - buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_config_request( + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 400, 409, 413, 415]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 201: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_config(self, config_id: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Get an active Environment Config and its environment spec. + + Returns one team-scoped Environment Config including the sanitized environment spec and + redacted credential slot metadata. Secret values are never returned. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "health_check": { - "failure_threshold": 0, # - Optional. The number of failed health checks before - considered unhealthy. - "http_path": "str", # - Optional. The route path used for the HTTP health check - ping. If not set, the HTTP health check will be disabled - and a TCP health check used instead. - "initial_delay_seconds": 0, - # Optional. The number of seconds to wait before - beginning health checks. - "period_seconds": 0, # - Optional. The number of seconds to wait between health - checks. - "port": 0, # Optional. The - port on which the health check will be performed. If not - set, the health check will be performed on the - component's http_port. - "success_threshold": 0, # - Optional. The number of successful health checks before - considered healthy. - "timeout_seconds": 0 # - Optional. The number of seconds after which the check - times out. - }, - "http_port": 0, # Optional. The - internal port on which this service's run command will - listen. Default: 8080 If there is not an environment variable - with the name ``PORT``"" , one will be automatically added - with its value set to the value of this field. - "image": { - "deploy_on_push": { - "enabled": bool # - Optional. Whether to automatically deploy new images. - Can only be used for images hosted in DOCR and can - only be used with an image tag, not a specific - digest. - }, - "digest": "str", # Optional. - The image digest. Cannot be specified if tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for the - ``DOCR`` registry type. - "registry_credentials": - "str", # Optional. The credentials to be able to pull - the image. The value will be encrypted on first - submission. On following submissions, the encrypted value - should be used. * "$username:$access_token" for - registries of type ``DOCKER_HUB``. * - "$username:$access_token" for registries of type - ``GHCR``. - "registry_type": "str", # - Optional. * DOCKER_HUB: The DockerHub container registry - type. * DOCR: The DigitalOcean container registry type. * - GHCR: The Github container registry type. Known values - are: "DOCKER_HUB", "DOCR", and "GHCR". - "repository": "str", # - Optional. The repository name. - "tag": "latest" # Optional. - Default value is "latest". The repository tag. Defaults - to ``latest`` if not provided and no digest is provided. - Cannot be specified if digest is provided. - }, - "instance_count": 1, # Optional. - Default value is 1. The amount of instances that this - component should be scaled to. Default: 1. Must not be set if - autoscaling is used. - "instance_size_slug": {}, - "internal_ports": [ - 0 # Optional. The ports on - which this service will listen for internal traffic. - ], - "liveness_health_check": { - "failure_threshold": 0, # - Optional. The number of failed health checks before - considered unhealthy. - "http_path": "str", # - Optional. The route path used for the HTTP health check - ping. If not set, the HTTP health check will be disabled - and a TCP health check used instead. - "initial_delay_seconds": 0, - # Optional. The number of seconds to wait before - beginning health checks. - "period_seconds": 0, # - Optional. The number of seconds to wait between health - checks. - "port": 0, # Optional. The - port on which the health check will be performed. - "success_threshold": 0, # - Optional. The number of successful health checks before - considered healthy. - "timeout_seconds": 0 # - Optional. The number of seconds after which the check - times out. - }, - "log_destinations": [ + } + ] + }, + "sandbox": { + "egress": { + "allow": [ { - "name": "str", # + "host": "str" # Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. - } - } - ], - "name": "str", # Optional. The name. - Must be unique across all components within the same app. - "protocol": "str", # Optional. The - protocol which the service uses to serve traffic on the - http_port. * ``HTTP``"" : The app is serving the HTTP - protocol. Default. * ``HTTP2``"" : The app is serving the - HTTP/2 protocol. Currently, this needs to be implemented in - the service by serving HTTP/2 cleartext (h2c). Known values - are: "HTTP" and "HTTP2". - "routes": [ - { - "path": "str", # - Optional. (Deprecated - Use Ingress Rules instead). - An HTTP path prefix. Paths must start with / and must - be unique across all components within an app. - "preserve_path_prefix": bool # Optional. An optional - flag to preserve the path that is forwarded to the - backend service. By default, the HTTP request path - will be trimmed from the left when forwarded to the - component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. } ], - "run_command": "str", # Optional. An - optional run command to override the component's default. - "source_dir": "str", # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. - "termination": { - "drain_seconds": 0, # - Optional. The number of seconds to wait between selecting - a container instance for termination and issuing the TERM - signal. Selecting a container instance for termination - begins an asynchronous drain of new requests on upstream - load-balancers. (Default 15). - "grace_period_seconds": 0 # - Optional. The number of seconds to wait between sending a - TERM signal to a container and issuing a KILL which - causes immediate shutdown. (Default 120). + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. } ], - "static_sites": [ + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ { - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "build_command": "str", # Optional. - An optional build command to run while building this - component from source. - "catchall_document": "str", # - Optional. The name of the document to use as the fallback for - any requests to documents that are not found when serving - this static site. Only 1 of ``catchall_document`` or - ``error_document`` can be set. - "cors": { - "allow_credentials": bool, # - Optional. Whether browsers should expose the response to - the client-side JavaScript code when the request"u2019s - credentials mode is include. This configures the - ``Access-Control-Allow-Credentials`` header. - "allow_headers": [ - "str" # Optional. - The set of allowed HTTP request headers. This - configures the ``Access-Control-Allow-Headers`` - header. - ], - "allow_methods": [ + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ "str" # Optional. - The set of allowed HTTP methods. This configures the - ``Access-Control-Allow-Methods`` header. - ], - "allow_origins": [ - { - "exact": - "str", # Optional. Exact string match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. An empty string is a valid value and will be - serialized explicitly rather than being omitted - from the payload. - "prefix": - "str", # Optional. Prefix-based match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. - "regex": - "str" # Optional. RE2 style regex-based match. - Only 1 of ``exact``"" , ``prefix``"" , or - ``regex`` must be set. For more information about - RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. ], - "expose_headers": [ + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ "str" # Optional. - The set of HTTP response headers that browsers are - allowed to access. This configures the - ``Access-Control-Expose-Headers`` header. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. ], - "max_age": "str" # Optional. - An optional duration specifying how long browsers can - cache the results of a preflight request. This configures - the ``Access-Control-Max-Age`` header. - }, - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root of - the repo. If set, it will be used to build this component. - Otherwise, App Platform will attempt to build it using - buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_config_request( + config_id=config_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_config(self, config_id: str, **kwargs: Any) -> Optional[JSON]: + # pylint: disable=line-too-long + """Soft-delete an Environment Config. + + Releases the active team-scoped name; the config row is retained (soft delete), so sessions + created from it keep a resolvable config_id. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 400, 404, 409 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_delete_config_request( + config_id=config_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 400, 404, 409]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_config_clone( + self, + config_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str", # New config name, unique among the team's active configs. + Required. + "oauth_assignments": { + "str": "str" # Optional. Write-only map from oauth logical slot to + an OAuth assignment UUID. Required when the source declares those slots; + never copied from the source. + }, + "secrets": { + "str": "str" # Optional. Write-only map from tenantSecret logical + slot to plaintext. Required when the source declares those slots; never + copied from the source. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. } - ], - "error_document": "404.html", # - Optional. Default value is "404.html". The name of the error - document to use when serving this static site. Default: - 404.html. If no such file exists within the built assets, App - Platform will supply one. - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": bool # - Optional. Whether to automatically deploy new images. - Can only be used for images hosted in DOCR and can - only be used with an image tag, not a specific - digest. - }, - "digest": "str", # Optional. - The image digest. Cannot be specified if tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for the - ``DOCR`` registry type. - "registry_credentials": - "str", # Optional. The credentials to be able to pull - the image. The value will be encrypted on first - submission. On following submissions, the encrypted value - should be used. * "$username:$access_token" for - registries of type ``DOCKER_HUB``. * - "$username:$access_token" for registries of type - ``GHCR``. - "registry_type": "str", # - Optional. * DOCKER_HUB: The DockerHub container registry - type. * DOCR: The DigitalOcean container registry type. * - GHCR: The Github container registry type. Known values - are: "DOCKER_HUB", "DOCR", and "GHCR". - "repository": "str", # - Optional. The repository name. - "tag": "latest" # Optional. - Default value is "latest". The repository tag. Defaults - to ``latest`` if not provided and no digest is provided. - Cannot be specified if digest is provided. - }, - "index_document": "index.html", # - Optional. Default value is "index.html". The name of the - index document to use when serving this static site. Default: - index.html. - "log_destinations": [ + } + ] + }, + "sandbox": { + "egress": { + "allow": [ { - "name": "str", # + "host": "str" # Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. - } - } - ], - "name": "str", # Optional. The name. - Must be unique across all components within the same app. - "output_dir": "str", # Optional. An - optional path to where the built assets will be located, - relative to the build context. If not set, App Platform will - automatically scan for these directory names: ``_static``"" , - ``dist``"" , ``public``"" , ``build``. - "routes": [ - { - "path": "str", # - Optional. (Deprecated - Use Ingress Rules instead). - An HTTP path prefix. Paths must start with / and must - be unique across all components within an app. - "preserve_path_prefix": bool # Optional. An optional - flag to preserve the path that is forwarded to the - backend service. By default, the HTTP request path - will be trimmed from the left when forwarded to the - component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. } ], - "run_command": "str", # Optional. An - optional run command to override the component's default. - "source_dir": "str" # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. - } - ], - "vpc": { - "egress_ips": [ + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ { - "ip": "str" # Optional. The - egress ips associated with the VPC. + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. } ], - "id": "str" # Optional. The ID of the VPC. + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. }, - "workers": [ + "secrets": [ { - "autoscaling": { - "max_instance_count": 0, # - Optional. The maximum amount of instances for this - component. Maximum 250. Consider using a larger instance - size if your application requires more than 250 - instances. - "metrics": { - "cpu": { - "percent": 80 - # Optional. Default value is 80. The average - target CPU utilization for the component. - } - }, - "min_instance_count": 0 # - Optional. The minimum amount of instances for this - component. - }, - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "build_command": "str", # Optional. - An optional build command to run while building this - component from source. - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root of - the repo. If set, it will be used to build this component. - Otherwise, App Platform will attempt to build it using - buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. - } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": bool # - Optional. Whether to automatically deploy new images. - Can only be used for images hosted in DOCR and can - only be used with an image tag, not a specific - digest. + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. }, - "digest": "str", # Optional. - The image digest. Cannot be specified if tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for the - ``DOCR`` registry type. - "registry_credentials": - "str", # Optional. The credentials to be able to pull - the image. The value will be encrypted on first - submission. On following submissions, the encrypted value - should be used. * "$username:$access_token" for - registries of type ``DOCKER_HUB``. * - "$username:$access_token" for registries of type - ``GHCR``. - "registry_type": "str", # - Optional. * DOCKER_HUB: The DockerHub container registry - type. * DOCR: The DigitalOcean container registry type. * - GHCR: The Github container registry type. Known values - are: "DOCKER_HUB", "DOCR", and "GHCR". - "repository": "str", # - Optional. The repository name. - "tag": "latest" # Optional. - Default value is "latest". The repository tag. Defaults - to ``latest`` if not provided and no digest is provided. - Cannot be specified if digest is provided. - }, - "instance_count": 1, # Optional. - Default value is 1. The amount of instances that this - component should be scaled to. Default: 1. Must not be set if - autoscaling is used. - "instance_size_slug": {}, - "liveness_health_check": { - "failure_threshold": 0, # - Optional. The number of failed health checks before - considered unhealthy. - "http_path": "str", # - Optional. The route path used for the HTTP health check - ping. If not set, the HTTP health check will be disabled - and a TCP health check used instead. - "initial_delay_seconds": 0, - # Optional. The number of seconds to wait before - beginning health checks. - "period_seconds": 0, # - Optional. The number of seconds to wait between health - checks. - "port": 0, # Optional. The - port on which the health check will be performed. - "success_threshold": 0, # - Optional. The number of successful health checks before - considered healthy. - "timeout_seconds": 0 # - Optional. The number of seconds after which the check - times out. - }, - "log_destinations": [ + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_config_clone( + self, + config_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ { - "name": "str", # + "host": "str" # Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. - } } ], - "name": "str", # Optional. The name. - Must be unique across all components within the same app. - "run_command": "str", # Optional. An - optional run command to override the component's default. - "source_dir": "str", # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. - "termination": { - "grace_period_seconds": 0 # - Optional. The number of seconds to wait between sending a - TERM signal to a container and issuing a KILL which - causes immediate shutdown. (Default 120). + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. } - } - ] - }, - "active_deployment": { - "cause": "str", # Optional. What caused this - deployment to be created. - "cloned_from": "str", # Optional. The ID of a - previous deployment that this deployment was cloned from. - "created_at": "2020-02-20 00:00:00", # Optional. The - creation time of the deployment. - "functions": [ + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ { - "name": "str", # Optional. The name - of this functions component. - "namespace": "str", # Optional. The - namespace where the functions are deployed. - "source_commit_hash": "str" # - Optional. The commit hash of the repository that was used to - build this functions component. + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. } ], - "id": "str", # Optional. The ID of the deployment. - "jobs": [ + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ { - "name": "str", # Optional. The name - of this job. - "source_commit_hash": "str" # - Optional. The commit hash of the repository that was used to - build this job. + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } } ], - "phase": "UNKNOWN", # Optional. Default value is - "UNKNOWN". Known values are: "UNKNOWN", "PENDING_BUILD", "BUILDING", - "PENDING_DEPLOY", "DEPLOYING", "ACTIVE", "SUPERSEDED", "ERROR", and - "CANCELED". - "phase_last_updated_at": "2020-02-20 00:00:00", # - Optional. When the deployment phase was last updated. - "progress": { - "error_steps": 0, # Optional. Number of - unsuccessful steps. - "pending_steps": 0, # Optional. Number of - pending steps. - "running_steps": 0, # Optional. Number of - currently running steps. - "steps": [ + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ { - "component_name": "str", # - Optional. The component name that this step is associated - with. - "ended_at": "2020-02-20 - 00:00:00", # Optional. The end time of this step. - "message_base": "str", # - Optional. The base of a human-readable description of the - step intended to be combined with the component name for - presentation. For example: ``message_base`` = "Building - service" ``component_name`` = "api". - "name": "str", # Optional. - The name of this step. - "reason": { - "code": "str", # - Optional. The error code. - "message": "str" # - Optional. The error message. + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. }, - "started_at": "2020-02-20 - 00:00:00", # Optional. The start time of this step. - "status": "UNKNOWN", # - Optional. Default value is "UNKNOWN". Known values are: - "UNKNOWN", "PENDING", "RUNNING", "ERROR", and "SUCCESS". - "steps": [ - {} # Optional. Child - steps of this step. - ] - } - ], - "success_steps": 0, # Optional. Number of - successful steps. - "summary_steps": [ - { - "component_name": "str", # - Optional. The component name that this step is associated - with. - "ended_at": "2020-02-20 - 00:00:00", # Optional. The end time of this step. - "message_base": "str", # - Optional. The base of a human-readable description of the - step intended to be combined with the component name for - presentation. For example: ``message_base`` = "Building - service" ``component_name`` = "api". "name": "str", # Optional. - The name of this step. - "reason": { - "code": "str", # - Optional. The error code. - "message": "str" # - Optional. The error message. - }, - "started_at": "2020-02-20 - 00:00:00", # Optional. The start time of this step. - "status": "UNKNOWN", # - Optional. Default value is "UNKNOWN". Known values are: - "UNKNOWN", "PENDING", "RUNNING", "ERROR", and "SUCCESS". - "steps": [ - {} # Optional. Child - steps of this step. - ] + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. } - ], - "total_steps": 0 # Optional. Total number of - steps. + ] }, - "services": [ - { - "name": "str", # Optional. The name - of this service. - "source_commit_hash": "str" # - Optional. The commit hash of the repository that was used to - build this service. + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_config_clone( + self, config_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str", # New config name, unique among the team's active configs. + Required. + "oauth_assignments": { + "str": "str" # Optional. Write-only map from oauth logical slot to + an OAuth assignment UUID. Required when the source declares those slots; + never copied from the source. + }, + "secrets": { + "str": "str" # Optional. Write-only map from tenantSecret logical + slot to plaintext. Required when the source declares those slots; never + copied from the source. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] } - ], - "spec": { - "name": "str", # The name of the app. Must - be unique across all apps in the same account. Required. - "databases": [ - { - "name": "str", # The - database's name. The name must be unique across all - components within the same app and cannot use capital - letters. Required. - "cluster_name": "str", # - Optional. The name of the underlying DigitalOcean DBaaS - cluster. This is required for production databases. For - dev databases, if cluster_name is not set, a new cluster - will be provisioned. - "db_name": "str", # - Optional. The name of the MySQL or PostgreSQL database to - configure. - "db_user": "str", # - Optional. The name of the MySQL or PostgreSQL user to - configure. - "engine": "UNSET", # - Optional. Default value is "UNSET". * MYSQL: MySQL * PG: - PostgreSQL * REDIS: Caching * MONGODB: MongoDB * KAFKA: - Kafka * OPENSEARCH: OpenSearch * VALKEY: ValKey. Known - values are: "UNSET", "MYSQL", "PG", "REDIS", "MONGODB", - "KAFKA", "OPENSEARCH", and "VALKEY". - "production": bool, # - Optional. Whether this is a production or dev database. - "version": "str" # Optional. - The version of the database engine. - } - ], - "disable_edge_cache": False, # Optional. - Default value is False. .. role:: raw-html-m2r(raw) :format: - html If set to ``true``"" , the app will **not** be cached at - the edge (CDN). Enable this option if you want to manage CDN - configuration yourself"u2014whether by using an external CDN - provider or by handling static content and caching within your - app. This setting is also recommended for apps that require - real-time data or serve dynamic content, such as those using - Server-Sent Events (SSE) over GET, or hosting an MCP (Model - Context Protocol) Server that utilizes SSE."" - :raw-html-m2r:`
` **Note:** This feature is not available for - static site components."" :raw-html-m2r:`
` For more - information, see `Disable CDN Cache - `_. - "disable_email_obfuscation": False, # - Optional. Default value is False. If set to ``true``"" , email - addresses in the app will not be obfuscated. This is useful for - apps that require email addresses to be visible (in the HTML - markup). - "domains": [ + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ { - "domain": "str", # The - hostname for the domain. Required. - "minimum_tls_version": "str", - # Optional. The minimum version of TLS a client - application can use to access resources for the domain. - Must be one of the following values wrapped within - quotations: ``"1.2"`` or ``"1.3"``. Known values are: - "1.2" and "1.3". - "type": "UNSPECIFIED", # - Optional. Default value is "UNSPECIFIED". * DEFAULT: The - default ``.ondigitalocean.app`` domain assigned to this - app * PRIMARY: The primary domain for this app that is - displayed as the default in the control panel, used in - bindable environment variables, and any other places that - reference an app's live URL. Only one domain may be set - as primary. * ALIAS: A non-primary domain. Known values - are: "UNSPECIFIED", "DEFAULT", "PRIMARY", and "ALIAS". - "wildcard": bool, # - Optional. Indicates whether the domain includes all - sub-domains, in addition to the given domain. - "zone": "str" # Optional. - Optional. If the domain uses DigitalOcean DNS and you - would like App Platform to automatically manage it for - you, set this to the name of the domain on your account. - For example, If the domain you are adding is - ``app.domain.com``"" , the zone could be ``domain.com``. + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } } - ], + ] + }, + "sandbox": { "egress": { - "type": "AUTOASSIGN" # Optional. - Default value is "AUTOASSIGN". The app egress type. Known - values are: "AUTOASSIGN" and "DEDICATED_IP". - }, - "enhanced_threat_control_enabled": False, # - Optional. Default value is False. If set to ``true``"" , - suspicious requests will go through additional security checks to - help mitigate layer 7 DDoS attacks. - "functions": [ + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ { - "name": "str", # The name. - Must be unique across all components within the same app. + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. Required. - "alerts": [ - { - "disabled": - bool, # Optional. Is the alert disabled?. - "operator": - "UNSPECIFIED_OPERATOR", # Optional. Default - value is "UNSPECIFIED_OPERATOR". Known values - are: "UNSPECIFIED_OPERATOR", "GREATER_THAN", and - "LESS_THAN". - "rule": - "UNSPECIFIED_RULE", # Optional. Default value is - "UNSPECIFIED_RULE". Known values are: - "UNSPECIFIED_RULE", "CPU_UTILIZATION", - "MEM_UTILIZATION", "RESTART_COUNT", - "DEPLOYMENT_FAILED", "DEPLOYMENT_LIVE", - "DOMAIN_FAILED", "DOMAIN_LIVE", - "AUTOSCALE_FAILED", "AUTOSCALE_SUCCEEDED", - "FUNCTIONS_ACTIVATION_COUNT", - "FUNCTIONS_AVERAGE_DURATION_MS", - "FUNCTIONS_ERROR_RATE_PER_MINUTE", - "FUNCTIONS_AVERAGE_WAIT_TIME_MS", - "FUNCTIONS_ERROR_COUNT", - "FUNCTIONS_GB_RATE_PER_SECOND", - "REQUESTS_PER_SECOND", and - "REQUEST_DURATION_P95_MS". - "value": 0.0, - # Optional. Threshold value for alert. - "window": - "UNSPECIFIED_WINDOW" # Optional. Default value - is "UNSPECIFIED_WINDOW". Known values are: - "UNSPECIFIED_WINDOW", "FIVE_MINUTES", - "TEN_MINUTES", "THIRTY_MINUTES", and "ONE_HOUR". - } - ], - "bitbucket": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "cors": { - "allow_credentials": - bool, # Optional. Whether browsers should expose the - response to the client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the ``Access-Control-Allow-Credentials`` - header. - "allow_headers": [ - "str" # - Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" # - Optional. The set of allowed HTTP methods. This - configures the ``Access-Control-Allow-Methods`` - header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. An empty string is - a valid value and will be serialized - explicitly rather than being omitted from the - payload. - "prefix": "str", # Optional. Prefix-based - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. For - more information about RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # - Optional. The set of HTTP response headers that - browsers are allowed to access. This configures - the ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # - Optional. An optional duration specifying how long - browsers can cache the results of a preflight - request. This configures the - ``Access-Control-Max-Age`` header. - }, - "envs": [ - { - "key": "str", - # The variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value - is "RUN_AND_BUILD_TIME". * RUN_TIME: Made - available only at run-time * BUILD_TIME: Made - available only at build-time * - RUN_AND_BUILD_TIME: Made available at both build - and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and - "RUN_AND_BUILD_TIME". - "type": - "GENERAL", # Optional. Default value is - "GENERAL". * GENERAL: A plain-text environment - variable * SECRET: A secret encrypted environment - variable. Known values are: "GENERAL" and - "SECRET". - "value": - "str" # Optional. The value. If the type is - ``SECRET``"" , the value will be encrypted on - first submission. On following submissions, the - encrypted value should be used. - } - ], - "git": { - "branch": "str", # - Optional. The name of the branch to use. - "repo_clone_url": - "str" # Optional. The clone URL of the repo. - Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "log_destinations": [ - { - "name": - "str", # Required. - "datadog": { - "api_key": "str", # Datadog API key. - Required. - "endpoint": "str" # Optional. Datadog HTTP - log intake endpoint. - }, - "logtail": { - "token": "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": { - "password": "str", # Optional. Password - for user defined in User. Is required - when ``endpoint`` is set. Cannot be set - if using a DigitalOcean DBaaS OpenSearch - cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to - ``doadmin`` when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name - of a DigitalOcean DBaaS OpenSearch cluster to - use as a log forwarding destination. Cannot - be specified if ``endpoint`` is also - specified. - "endpoint": "str", # Optional. OpenSearch - API Endpoint. Only HTTPS is supported. - Format: - https://:code:``::code:``. Cannot - be specified if ``cluster_name`` is also - specified. - "index_name": "logs" # Optional. Default - value is "logs". The index name to use for - the logs. If not set, the default index name - is "logs". - }, - "papertrail": - { - "endpoint": "str" # Papertrail syslog - endpoint. Required. - } - } - ], - "routes": [ - { - "path": - "str", # Optional. (Deprecated - Use Ingress - Rules instead). An HTTP path prefix. Paths must - start with / and must be unique across all - components within an app. - "preserve_path_prefix": bool # Optional. An - optional flag to preserve the path that is - forwarded to the backend service. By default, the - HTTP request path will be trimmed from the left - when forwarded to the component. For example, a - component with ``path=/api`` will have requests - to ``/api/list`` trimmed to ``/list``. If this - value is ``true``"" , the path will remain - ``/api/list``. - } - ], - "source_dir": "str" # - Optional. An optional path to the working directory to - use for the build. For Dockerfile builds, this will be - used as the build context. Must be relative to the root - of the repo. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. } ], - "ingress": { - "custom_error_page_url": "str", # - Optional. Optional HTTPS URL of a custom error page to - display when the app is unreachable. The page is shown in a - full-viewport iframe. The target must allow framing: avoid - ``X-Frame-Options: DENY`` and a restrictive - ``Content-Security-Policy`` ``frame-ancestors`` that blocks - the platform. If omitted, the default platform error page is - used. - "rules": [ - { - "component": { - "name": - "str", # The name of the component to route to. - Required. - "preserve_path_prefix": "str", # Optional. An - optional flag to preserve the path that is - forwarded to the backend service. By default, the - HTTP request path will be trimmed from the left - when forwarded to the component. For example, a - component with ``path=/api`` will have requests - to ``/api/list`` trimmed to ``/list``. If this - value is ``true``"" , the path will remain - ``/api/list``. Note: this is not applicable for - Functions Components and is mutually exclusive - with ``rewrite``. - "rewrite": - "str" # Optional. An optional field that will - rewrite the path of the component to be what is - specified here. By default, the HTTP request path - will be trimmed from the left when forwarded to - the component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If you specified the - rewrite to be ``/v1/``"" , requests to - ``/api/list`` would be rewritten to ``/v1/list``. - Note: this is mutually exclusive with - ``preserve_path_prefix``. - }, - "cors": { - "allow_credentials": bool, # Optional. Whether - browsers should expose the response to the - client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the - ``Access-Control-Allow-Credentials`` header. - "allow_headers": [ - "str" - # Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" - # Optional. The set of allowed HTTP methods. - This configures the - ``Access-Control-Allow-Methods`` header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. - An empty string is a valid value and will - be serialized explicitly rather than - being omitted from the payload. - "prefix": "str", # Optional. - Prefix-based match. Only 1 of ``exact``"" - , ``prefix``"" , or ``regex`` must be - set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" - , ``prefix``"" , or ``regex`` must be - set. For more information about RE2 - syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" - # Optional. The set of HTTP response headers - that browsers are allowed to access. This - configures the - ``Access-Control-Expose-Headers`` header. - ], - "max_age": - "str" # Optional. An optional duration - specifying how long browsers can cache the - results of a preflight request. This configures - the ``Access-Control-Max-Age`` header. - }, - "match": { - "authority": - { - "exact": "str" # Exact string match. An - empty string is a valid value and will be - serialized explicitly rather than being - omitted from the payload. Required. - }, - "path": { - "prefix": "str" # Prefix-based match. For - example, ``/api`` will match ``/api``"" , - ``/api/``"" , and any nested paths such as - ``/api/v1/endpoint``. Required. - } - }, - "redirect": { - "authority": - "str", # Optional. The authority/host to - redirect to. This can be a hostname or IP - address. Note: use ``port`` to set the port. - "port": 0, # - Optional. The port to redirect to. - "redirect_code": 0, # Optional. The redirect - code to use. Defaults to ``302``. Supported - values are 300, 301, 302, 303, 304, 307, 308. - "scheme": - "str", # Optional. The scheme to redirect to. - Supported values are ``http`` or ``https``. - Default: ``https``. - "uri": "str" - # Optional. An optional URI path to redirect to. - Note: if this is specified the whole URI of the - original request will be overwritten to this - value, irrespective of the original request URI - being matched. - } - } - ] - }, - "jobs": [ + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ { - "bitbucket": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. }, - "build_command": "str", # - Optional. An optional build command to run while building - this component from source. - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root - of the repo. If set, it will be used to build this - component. Otherwise, App Platform will attempt to build - it using buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", - # The variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value - is "RUN_AND_BUILD_TIME". * RUN_TIME: Made - available only at run-time * BUILD_TIME: Made - available only at build-time * - RUN_AND_BUILD_TIME: Made available at both build - and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and - "RUN_AND_BUILD_TIME". - "type": - "GENERAL", # Optional. Default value is - "GENERAL". * GENERAL: A plain-text environment - variable * SECRET: A secret encrypted environment - variable. Known values are: "GENERAL" and - "SECRET". - "value": - "str" # Optional. The value. If the type is - ``SECRET``"" , the value will be encrypted on - first submission. On following submissions, the - encrypted value should be used. - } + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. ], - "git": { - "branch": "str", # - Optional. The name of the branch to use. - "repo_clone_url": - "str" # Optional. The clone URL of the repo. - Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": - bool # Optional. Whether to automatically deploy - new images. Can only be used for images hosted in - DOCR and can only be used with an image tag, not - a specific digest. - }, - "digest": "str", # - Optional. The image digest. Cannot be specified if - tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for - the ``DOCR`` registry type. - "registry_credentials": "str", # Optional. The - credentials to be able to pull the image. The value - will be encrypted on first submission. On following - submissions, the encrypted value should be used. * - "$username:$access_token" for registries of type - ``DOCKER_HUB``. * "$username:$access_token" for - registries of type ``GHCR``. - "registry_type": - "str", # Optional. * DOCKER_HUB: The DockerHub - container registry type. * DOCR: The DigitalOcean - container registry type. * GHCR: The Github container - registry type. Known values are: "DOCKER_HUB", - "DOCR", and "GHCR". - "repository": "str", - # Optional. The repository name. - "tag": "latest" # - Optional. Default value is "latest". The repository - tag. Defaults to ``latest`` if not provided and no - digest is provided. Cannot be specified if digest is - provided. - }, - "instance_count": 1, # - Optional. Default value is 1. The amount of instances - that this component should be scaled to. Default: 1. Must - not be set if autoscaling is used. - "instance_size_slug": {}, - "kind": "UNSPECIFIED", # - Optional. Default value is "UNSPECIFIED". * UNSPECIFIED: - Default job type, will auto-complete to POST_DEPLOY kind. - * PRE_DEPLOY: Indicates a job that runs before an app - deployment. * POST_DEPLOY: Indicates a job that runs - after an app deployment. * FAILED_DEPLOY: Indicates a job - that runs after a component fails to deploy. Known values - are: "UNSPECIFIED", "PRE_DEPLOY", "POST_DEPLOY", and - "FAILED_DEPLOY". - "log_destinations": [ - { - "name": - "str", # Required. - "datadog": { - "api_key": "str", # Datadog API key. - Required. - "endpoint": "str" # Optional. Datadog HTTP - log intake endpoint. - }, - "logtail": { - "token": "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": { - "password": "str", # Optional. Password - for user defined in User. Is required - when ``endpoint`` is set. Cannot be set - if using a DigitalOcean DBaaS OpenSearch - cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to - ``doadmin`` when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name - of a DigitalOcean DBaaS OpenSearch cluster to - use as a log forwarding destination. Cannot - be specified if ``endpoint`` is also - specified. - "endpoint": "str", # Optional. OpenSearch - API Endpoint. Only HTTPS is supported. - Format: - https://:code:``::code:``. Cannot - be specified if ``cluster_name`` is also - specified. - "index_name": "logs" # Optional. Default - value is "logs". The index name to use for - the logs. If not set, the default index name - is "logs". - }, - "papertrail": - { - "endpoint": "str" # Papertrail syslog - endpoint. Required. - } - } - ], - "name": "str", # Optional. - The name. Must be unique across all components within the - same app. - "run_command": "str", # - Optional. An optional run command to override the - component's default. - "source_dir": "str", # - Optional. An optional path to the working directory to - use for the build. For Dockerfile builds, this will be - used as the build context. Must be relative to the root - of the repo. - "termination": { - "grace_period_seconds": 0 # Optional. The number of - seconds to wait between sending a TERM signal to a - container and issuing a KILL which causes immediate - shutdown. (Default 120). - } - } - ], - "maintenance": { - "archive": bool, # Optional. - Indicates whether the app should be archived. Setting this to - true implies that enabled is set to true. - "enabled": bool, # Optional. - Indicates whether maintenance mode should be enabled for the - app. - "offline_page_url": "str" # - Optional. A custom offline page to display when maintenance - mode is enabled or the app is archived. - }, - "region": "str", # Optional. The slug form - of the geographical origin of the app. Default: ``nearest - available``. Known values are: "atl", "nyc", "sfo", "tor", "ams", - "fra", "lon", "blr", "sgp", and "syd". - "services": [ - { - "autoscaling": { - "max_instance_count": - 0, # Optional. The maximum amount of instances for - this component. Maximum 250. Consider using a larger - instance size if your application requires more than - 250 instances. - "metrics": { - "request_duration": { - "p95_milliseconds": 0 # Optional. The p95 - target request duration in milliseconds for - the component. - }, - "requests_per_second": { - "per_instance": 0 # Optional. The target - number of requests per second per instance - for the component. - } - }, - "min_instance_count": - 0 # Optional. The minimum amount of instances for - this component. - }, - "bitbucket": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "build_command": "str", # - Optional. An optional build command to run while building - this component from source. - "cors": { - "allow_credentials": - bool, # Optional. Whether browsers should expose the - response to the client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the ``Access-Control-Allow-Credentials`` - header. - "allow_headers": [ - "str" # - Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" # - Optional. The set of allowed HTTP methods. This - configures the ``Access-Control-Allow-Methods`` - header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. An empty string is - a valid value and will be serialized - explicitly rather than being omitted from the - payload. - "prefix": "str", # Optional. Prefix-based - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. For - more information about RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # - Optional. The set of HTTP response headers that - browsers are allowed to access. This configures - the ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # - Optional. An optional duration specifying how long - browsers can cache the results of a preflight - request. This configures the - ``Access-Control-Max-Age`` header. - }, - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root - of the repo. If set, it will be used to build this - component. Otherwise, App Platform will attempt to build - it using buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", - # The variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value - is "RUN_AND_BUILD_TIME". * RUN_TIME: Made - available only at run-time * BUILD_TIME: Made - available only at build-time * - RUN_AND_BUILD_TIME: Made available at both build - and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and - "RUN_AND_BUILD_TIME". - "type": - "GENERAL", # Optional. Default value is - "GENERAL". * GENERAL: A plain-text environment - variable * SECRET: A secret encrypted environment - variable. Known values are: "GENERAL" and - "SECRET". - "value": - "str" # Optional. The value. If the type is - ``SECRET``"" , the value will be encrypted on - first submission. On following submissions, the - encrypted value should be used. - } - ], - "git": { - "branch": "str", # - Optional. The name of the branch to use. - "repo_clone_url": - "str" # Optional. The clone URL of the repo. - Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "health_check": { - "failure_threshold": - 0, # Optional. The number of failed health checks - before considered unhealthy. - "http_path": "str", - # Optional. The route path used for the HTTP health - check ping. If not set, the HTTP health check will be - disabled and a TCP health check used instead. - "initial_delay_seconds": 0, # Optional. The number - of seconds to wait before beginning health checks. - "period_seconds": 0, - # Optional. The number of seconds to wait between - health checks. - "port": 0, # - Optional. The port on which the health check will be - performed. If not set, the health check will be - performed on the component's http_port. - "success_threshold": - 0, # Optional. The number of successful health - checks before considered healthy. - "timeout_seconds": 0 - # Optional. The number of seconds after which the - check times out. - }, - "http_port": 0, # Optional. - The internal port on which this service's run command - will listen. Default: 8080 If there is not an environment - variable with the name ``PORT``"" , one will be - automatically added with its value set to the value of - this field. - "image": { - "deploy_on_push": { - "enabled": - bool # Optional. Whether to automatically deploy - new images. Can only be used for images hosted in - DOCR and can only be used with an image tag, not - a specific digest. - }, - "digest": "str", # - Optional. The image digest. Cannot be specified if - tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for - the ``DOCR`` registry type. - "registry_credentials": "str", # Optional. The - credentials to be able to pull the image. The value - will be encrypted on first submission. On following - submissions, the encrypted value should be used. * - "$username:$access_token" for registries of type - ``DOCKER_HUB``. * "$username:$access_token" for - registries of type ``GHCR``. - "registry_type": - "str", # Optional. * DOCKER_HUB: The DockerHub - container registry type. * DOCR: The DigitalOcean - container registry type. * GHCR: The Github container - registry type. Known values are: "DOCKER_HUB", - "DOCR", and "GHCR". - "repository": "str", - # Optional. The repository name. - "tag": "latest" # - Optional. Default value is "latest". The repository - tag. Defaults to ``latest`` if not provided and no - digest is provided. Cannot be specified if digest is - provided. - }, - "instance_count": 1, # - Optional. Default value is 1. The amount of instances - that this component should be scaled to. Default: 1. Must - not be set if autoscaling is used. - "instance_size_slug": {}, - "internal_ports": [ - 0 # Optional. The - ports on which this service will listen for internal - traffic. - ], - "liveness_health_check": { - "failure_threshold": - 0, # Optional. The number of failed health checks - before considered unhealthy. - "http_path": "str", - # Optional. The route path used for the HTTP health - check ping. If not set, the HTTP health check will be - disabled and a TCP health check used instead. - "initial_delay_seconds": 0, # Optional. The number - of seconds to wait before beginning health checks. - "period_seconds": 0, - # Optional. The number of seconds to wait between - health checks. - "port": 0, # - Optional. The port on which the health check will be - performed. - "success_threshold": - 0, # Optional. The number of successful health - checks before considered healthy. - "timeout_seconds": 0 - # Optional. The number of seconds after which the - check times out. - }, - "log_destinations": [ - { - "name": - "str", # Required. - "datadog": { - "api_key": "str", # Datadog API key. - Required. - "endpoint": "str" # Optional. Datadog HTTP - log intake endpoint. - }, - "logtail": { - "token": "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": { - "password": "str", # Optional. Password - for user defined in User. Is required - when ``endpoint`` is set. Cannot be set - if using a DigitalOcean DBaaS OpenSearch - cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to - ``doadmin`` when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name - of a DigitalOcean DBaaS OpenSearch cluster to - use as a log forwarding destination. Cannot - be specified if ``endpoint`` is also - specified. - "endpoint": "str", # Optional. OpenSearch - API Endpoint. Only HTTPS is supported. - Format: - https://:code:``::code:``. Cannot - be specified if ``cluster_name`` is also - specified. - "index_name": "logs" # Optional. Default - value is "logs". The index name to use for - the logs. If not set, the default index name - is "logs". - }, - "papertrail": - { - "endpoint": "str" # Papertrail syslog - endpoint. Required. - } - } - ], - "name": "str", # Optional. - The name. Must be unique across all components within the - same app. - "protocol": "str", # - Optional. The protocol which the service uses to serve - traffic on the http_port. * ``HTTP``"" : The app is - serving the HTTP protocol. Default. * ``HTTP2``"" : The - app is serving the HTTP/2 protocol. Currently, this needs - to be implemented in the service by serving HTTP/2 - cleartext (h2c). Known values are: "HTTP" and "HTTP2". - "routes": [ - { - "path": - "str", # Optional. (Deprecated - Use Ingress - Rules instead). An HTTP path prefix. Paths must - start with / and must be unique across all - components within an app. - "preserve_path_prefix": bool # Optional. An - optional flag to preserve the path that is - forwarded to the backend service. By default, the - HTTP request path will be trimmed from the left - when forwarded to the component. For example, a - component with ``path=/api`` will have requests - to ``/api/list`` trimmed to ``/list``. If this - value is ``true``"" , the path will remain - ``/api/list``. - } - ], - "run_command": "str", # - Optional. An optional run command to override the - component's default. - "source_dir": "str", # - Optional. An optional path to the working directory to - use for the build. For Dockerfile builds, this will be - used as the build context. Must be relative to the root - of the repo. - "termination": { - "drain_seconds": 0, - # Optional. The number of seconds to wait between - selecting a container instance for termination and - issuing the TERM signal. Selecting a container - instance for termination begins an asynchronous drain - of new requests on upstream load-balancers. (Default - 15). - "grace_period_seconds": 0 # Optional. The number of - seconds to wait between sending a TERM signal to a - container and issuing a KILL which causes immediate - shutdown. (Default 120). - } - } - ], - "static_sites": [ - { - "bitbucket": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "build_command": "str", # - Optional. An optional build command to run while building - this component from source. - "catchall_document": "str", - # Optional. The name of the document to use as the - fallback for any requests to documents that are not found - when serving this static site. Only 1 of - ``catchall_document`` or ``error_document`` can be set. - "cors": { - "allow_credentials": - bool, # Optional. Whether browsers should expose the - response to the client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the ``Access-Control-Allow-Credentials`` - header. - "allow_headers": [ - "str" # - Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" # - Optional. The set of allowed HTTP methods. This - configures the ``Access-Control-Allow-Methods`` - header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. An empty string is - a valid value and will be serialized - explicitly rather than being omitted from the - payload. - "prefix": "str", # Optional. Prefix-based - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. For - more information about RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # - Optional. The set of HTTP response headers that - browsers are allowed to access. This configures - the ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # - Optional. An optional duration specifying how long - browsers can cache the results of a preflight - request. This configures the - ``Access-Control-Max-Age`` header. - }, - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root - of the repo. If set, it will be used to build this - component. Otherwise, App Platform will attempt to build - it using buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", - # The variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value - is "RUN_AND_BUILD_TIME". * RUN_TIME: Made - available only at run-time * BUILD_TIME: Made - available only at build-time * - RUN_AND_BUILD_TIME: Made available at both build - and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and - "RUN_AND_BUILD_TIME". - "type": - "GENERAL", # Optional. Default value is - "GENERAL". * GENERAL: A plain-text environment - variable * SECRET: A secret encrypted environment - variable. Known values are: "GENERAL" and - "SECRET". - "value": - "str" # Optional. The value. If the type is - ``SECRET``"" , the value will be encrypted on - first submission. On following submissions, the - encrypted value should be used. - } - ], - "error_document": "404.html", - # Optional. Default value is "404.html". The name of the - error document to use when serving this static site. - Default: 404.html. If no such file exists within the - built assets, App Platform will supply one. - "git": { - "branch": "str", # - Optional. The name of the branch to use. - "repo_clone_url": - "str" # Optional. The clone URL of the repo. - Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": - bool # Optional. Whether to automatically deploy - new images. Can only be used for images hosted in - DOCR and can only be used with an image tag, not - a specific digest. - }, - "digest": "str", # - Optional. The image digest. Cannot be specified if - tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for - the ``DOCR`` registry type. - "registry_credentials": "str", # Optional. The - credentials to be able to pull the image. The value - will be encrypted on first submission. On following - submissions, the encrypted value should be used. * - "$username:$access_token" for registries of type - ``DOCKER_HUB``. * "$username:$access_token" for - registries of type ``GHCR``. - "registry_type": - "str", # Optional. * DOCKER_HUB: The DockerHub - container registry type. * DOCR: The DigitalOcean - container registry type. * GHCR: The Github container - registry type. Known values are: "DOCKER_HUB", - "DOCR", and "GHCR". - "repository": "str", - # Optional. The repository name. - "tag": "latest" # - Optional. Default value is "latest". The repository - tag. Defaults to ``latest`` if not provided and no - digest is provided. Cannot be specified if digest is - provided. - }, - "index_document": - "index.html", # Optional. Default value is "index.html". - The name of the index document to use when serving this - static site. Default: index.html. - "log_destinations": [ - { - "name": - "str", # Required. - "datadog": { - "api_key": "str", # Datadog API key. - Required. - "endpoint": "str" # Optional. Datadog HTTP - log intake endpoint. - }, - "logtail": { - "token": "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": { - "password": "str", # Optional. Password - for user defined in User. Is required - when ``endpoint`` is set. Cannot be set - if using a DigitalOcean DBaaS OpenSearch - cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to - ``doadmin`` when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name - of a DigitalOcean DBaaS OpenSearch cluster to - use as a log forwarding destination. Cannot - be specified if ``endpoint`` is also - specified. - "endpoint": "str", # Optional. OpenSearch - API Endpoint. Only HTTPS is supported. - Format: - https://:code:``::code:``. Cannot - be specified if ``cluster_name`` is also - specified. - "index_name": "logs" # Optional. Default - value is "logs". The index name to use for - the logs. If not set, the default index name - is "logs". - }, - "papertrail": - { - "endpoint": "str" # Papertrail syslog - endpoint. Required. - } - } - ], - "name": "str", # Optional. - The name. Must be unique across all components within the - same app. - "output_dir": "str", # - Optional. An optional path to where the built assets will - be located, relative to the build context. If not set, - App Platform will automatically scan for these directory - names: ``_static``"" , ``dist``"" , ``public``"" , - ``build``. - "routes": [ - { - "path": - "str", # Optional. (Deprecated - Use Ingress - Rules instead). An HTTP path prefix. Paths must - start with / and must be unique across all - components within an app. - "preserve_path_prefix": bool # Optional. An - optional flag to preserve the path that is - forwarded to the backend service. By default, the - HTTP request path will be trimmed from the left - when forwarded to the component. For example, a - component with ``path=/api`` will have requests - to ``/api/list`` trimmed to ``/list``. If this - value is ``true``"" , the path will remain - ``/api/list``. - } - ], - "run_command": "str", # - Optional. An optional run command to override the - component's default. - "source_dir": "str" # - Optional. An optional path to the working directory to - use for the build. For Dockerfile builds, this will be - used as the build context. Must be relative to the root - of the repo. - } - ], - "vpc": { - "egress_ips": [ - { - "ip": "str" # - Optional. The egress ips associated with the VPC. - } - ], - "id": "str" # Optional. The ID of - the VPC. - }, - "workers": [ - { - "autoscaling": { - "max_instance_count": - 0, # Optional. The maximum amount of instances for - this component. Maximum 250. Consider using a larger - instance size if your application requires more than - 250 instances. - "metrics": { - "cpu": { - "percent": 80 # Optional. Default value is - 80. The average target CPU utilization for - the component. - } - }, - "min_instance_count": - 0 # Optional. The minimum amount of instances for - this component. - }, - "bitbucket": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "build_command": "str", # - Optional. An optional build command to run while building - this component from source. - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root - of the repo. If set, it will be used to build this - component. Otherwise, App Platform will attempt to build - it using buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", - # The variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value - is "RUN_AND_BUILD_TIME". * RUN_TIME: Made - available only at run-time * BUILD_TIME: Made - available only at build-time * - RUN_AND_BUILD_TIME: Made available at both build - and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and - "RUN_AND_BUILD_TIME". - "type": - "GENERAL", # Optional. Default value is - "GENERAL". * GENERAL: A plain-text environment - variable * SECRET: A secret encrypted environment - variable. Known values are: "GENERAL" and - "SECRET". - "value": - "str" # Optional. The value. If the type is - ``SECRET``"" , the value will be encrypted on - first submission. On following submissions, the - encrypted value should be used. - } - ], - "git": { - "branch": "str", # - Optional. The name of the branch to use. - "repo_clone_url": - "str" # Optional. The clone URL of the repo. - Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # - Optional. The name of the branch to use. - "deploy_on_push": - bool, # Optional. Whether to automatically deploy - new commits made to the repo. - "repo": "str" # - Optional. The name of the repo in the format - owner/repo. Example: ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": - bool # Optional. Whether to automatically deploy - new images. Can only be used for images hosted in - DOCR and can only be used with an image tag, not - a specific digest. - }, - "digest": "str", # - Optional. The image digest. Cannot be specified if - tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for - the ``DOCR`` registry type. - "registry_credentials": "str", # Optional. The - credentials to be able to pull the image. The value - will be encrypted on first submission. On following - submissions, the encrypted value should be used. * - "$username:$access_token" for registries of type - ``DOCKER_HUB``. * "$username:$access_token" for - registries of type ``GHCR``. - "registry_type": - "str", # Optional. * DOCKER_HUB: The DockerHub - container registry type. * DOCR: The DigitalOcean - container registry type. * GHCR: The Github container - registry type. Known values are: "DOCKER_HUB", - "DOCR", and "GHCR". - "repository": "str", - # Optional. The repository name. - "tag": "latest" # - Optional. Default value is "latest". The repository - tag. Defaults to ``latest`` if not provided and no - digest is provided. Cannot be specified if digest is - provided. - }, - "instance_count": 1, # - Optional. Default value is 1. The amount of instances - that this component should be scaled to. Default: 1. Must - not be set if autoscaling is used. - "instance_size_slug": {}, - "liveness_health_check": { - "failure_threshold": - 0, # Optional. The number of failed health checks - before considered unhealthy. - "http_path": "str", - # Optional. The route path used for the HTTP health - check ping. If not set, the HTTP health check will be - disabled and a TCP health check used instead. - "initial_delay_seconds": 0, # Optional. The number - of seconds to wait before beginning health checks. - "period_seconds": 0, - # Optional. The number of seconds to wait between - health checks. - "port": 0, # - Optional. The port on which the health check will be - performed. - "success_threshold": - 0, # Optional. The number of successful health - checks before considered healthy. - "timeout_seconds": 0 - # Optional. The number of seconds after which the - check times out. - }, - "log_destinations": [ - { - "name": - "str", # Required. - "datadog": { - "api_key": "str", # Datadog API key. - Required. - "endpoint": "str" # Optional. Datadog HTTP - log intake endpoint. - }, - "logtail": { - "token": "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": { - "password": "str", # Optional. Password - for user defined in User. Is required - when ``endpoint`` is set. Cannot be set - if using a DigitalOcean DBaaS OpenSearch - cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to - ``doadmin`` when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name - of a DigitalOcean DBaaS OpenSearch cluster to - use as a log forwarding destination. Cannot - be specified if ``endpoint`` is also - specified. - "endpoint": "str", # Optional. OpenSearch - API Endpoint. Only HTTPS is supported. - Format: - https://:code:``::code:``. Cannot - be specified if ``cluster_name`` is also - specified. - "index_name": "logs" # Optional. Default - value is "logs". The index name to use for - the logs. If not set, the default index name - is "logs". - }, - "papertrail": - { - "endpoint": "str" # Papertrail syslog - endpoint. Required. - } - } + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. ], - "name": "str", # Optional. - The name. Must be unique across all components within the - same app. - "run_command": "str", # - Optional. An optional run command to override the - component's default. - "source_dir": "str", # - Optional. An optional path to the working directory to - use for the build. For Dockerfile builds, this will be - used as the build context. Must be relative to the root - of the repo. - "termination": { - "grace_period_seconds": 0 # Optional. The number of - seconds to wait between sending a TERM signal to a - container and issuing a KILL which causes immediate - shutdown. (Default 120). - } + "url": "str" # Optional. + Inline server endpoint; https:// only. } ] }, - "static_sites": [ - { - "name": "str", # Optional. The name - of this static site. - "source_commit_hash": "str" # - Optional. The commit hash of the repository that was used to - build this static site. - } - ], - "tier_slug": "str", # Optional. The current pricing - tier slug of the deployment. - "updated_at": "2020-02-20 00:00:00", # Optional. - When the deployment was last updated. - "workers": [ - { - "name": "str", # Optional. The name - of this worker. - "source_commit_hash": "str" # - Optional. The commit hash of the repository that was used to - build this worker. - } - ] - }, - "created_at": "2020-02-20 00:00:00", # Optional. The - creation time of the app. - "dedicated_ips": [ - { - "id": "str", # Optional. The ID of the - dedicated egress IP. - "ip": "str", # Optional. The IP address of - the dedicated egress IP. - "status": "UNKNOWN" # Optional. Default - value is "UNKNOWN". The status of the dedicated egress IP. Known - values are: "UNKNOWN", "ASSIGNING", "ASSIGNED", and "REMOVED". + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] } - ], - "default_ingress": "str", # Optional. The default hostname - on which the app is accessible. - "domains": [ - { - "certificate_expires_at": "2020-02-20 - 00:00:00", # Optional. Current SSL certificate expiration time. - "id": "str", # Optional. The ID of the - domain. - "phase": "UNKNOWN", # Optional. Default - value is "UNKNOWN". Known values are: "UNKNOWN", "PENDING", - "CONFIGURING", "ACTIVE", and "ERROR". - "progress": { - "steps": [ - {} # Optional. The steps of - the domain's progress. - ] - }, - "rotate_validation_records": bool, # - Optional. Validation values have changed and require manual - intervention. - "spec": { - "domain": "str", # The hostname for - the domain. Required. - "minimum_tls_version": "str", # - Optional. The minimum version of TLS a client application can - use to access resources for the domain. Must be one of the - following values wrapped within quotations: ``"1.2"`` or - ``"1.3"``. Known values are: "1.2" and "1.3". - "type": "UNSPECIFIED", # Optional. - Default value is "UNSPECIFIED". * DEFAULT: The default + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_config_clone_request( + config_id=config_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 400, 404, 409, 413, 415]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 201: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_config_sessions( + self, + config_id: str, + *, + page_size: int = 50, + page_token: Optional[str] = None, + status: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """List the sessions created from one Environment Config. + + Enumerates the caller's team sessions whose session was created from this durable Environment + Config. Keyset-paginated on an opaque cursor, newest first, same shape and status semantics as + GET /v2/agents/sessions. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive; values above 200 + are clamped to 200. Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :keyword status: Optional filter on session status. Empty / SESSION_STATUS_UNSPECIFIED still + hides destroyed and failed sessions by default; SESSION_STATUS_ALL opts out and returns every + session regardless of status. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", + "SESSION_STATUS_PAUSED", and "SESSION_STATUS_ALL". Default value is None. + :paramtype status: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "next_page_token": "str", # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_config_sessions_request( + config_id=config_id, + page_size=page_size, + page_token=page_token, + status=status, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_sessions( + self, + *, + page_size: int = 50, + page_token: Optional[str] = None, + status: str = "SESSION_STATUS_UNSPECIFIED", + name: Optional[str] = None, + search: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ListSessions enumerates sessions for the caller's tenant. + + Keyset-paginated on an opaque cursor. Results are scoped to the authenticated team. Destroyed + and failed sessions are omitted by default (matching the Cloud Console); pass + ``status=SESSION_STATUS_ALL`` to list every session regardless of status, or an explicit status + such as ``status=SESSION_STATUS_FAILED`` or ``status=SESSION_STATUS_DESTROYED`` to list only + those. + + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive; values above 200 + are clamped to 200 (not rejected). Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :keyword status: Optional filter on session status. Empty / SESSION_STATUS_UNSPECIFIED means no + explicit filter, which still hides destroyed and failed sessions by default; SESSION_STATUS_ALL + opts out and returns every session regardless of status (no session is ever actually in this + status; it only means something as a filter value here); any other explicit status overrides + the default. Known values are: "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". Default value is + "SESSION_STATUS_UNSPECIFIED". + :paramtype status: str + :keyword name: Optional exact-name filter (case-insensitive). Returns the single live + (non-terminal) session that currently holds this name for the caller's team, or an empty list + if none does. Matches the live holder only, so historical terminal rows and reused names never + make it ambiguous (per-team live-name uniqueness bounds it to at most one). This is how clients + (doctl / Console) resolve a session name to its id; the {session_id} path stays UUID-only. + Default value is None. + :paramtype name: str + :keyword search: Optional case-insensitive substring filter on session name (contains), for + callers that don't know the exact name (e.g. a UI search box). Unlike ``name``\\ , this may + return zero, one, or many sessions, including terminal ones. Independent of ``name``\\ ; at + most 64 characters. Default value is None. + :paramtype search: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "next_page_token": "str", # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_sessions_request( + page_size=page_size, + page_token=page_token, + status=status, + name=name, + search=search, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session( + self, + body: JSON, + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Required. + :type body: JSON + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "config_id": "str", # Required. + "name": "str", # Session name, independent of the config name. Must satisfy + the existing environment-spec metadata.name rules and must not be UUID-shaped. + Required. + "resume_on_topoff": bool # Optional. Opt the new session in to automatic + resumption when the team's prepayment balance is restored. Defaults to false. + Session-scoped, not config-scoped: the referenced config never confers this, so + one shared config cannot enrol every session created from it. Omit to leave the + default (or to let the ?resume_on_topoff= query parameter supply it). + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session( + self, + body: IO[bytes], + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Required. + :type body: IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session( + self, + body: Union[JSON, IO[bytes]], + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Is either a JSON type or a + IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "config_id": "str", # Required. + "name": "str", # Session name, independent of the config name. Must satisfy + the existing environment-spec metadata.name rules and must not be UUID-shaped. + Required. + "resume_on_topoff": bool # Optional. Opt the new session in to automatic + resumption when the team's prepayment balance is restored. Defaults to false. + Session-scoped, not config-scoped: the referenced config never confers this, so + one shared config cannot enrol every session created from it. Omit to leave the + default (or to let the ?resume_on_topoff= query parameter supply it). + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_session_request( + x_device_uuid=x_device_uuid, + openai_session_id=openai_session_id, + resume_on_topoff=resume_on_topoff, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [ + 200, + 400, + 402, + 403, + 404, + 409, + 413, + 415, + 501, + 503, + ]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 503: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def validate_session_policy(self, data: IO[bytes], **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """ValidatePolicy checks a manifest's tool-permission policy against the agent's capabilities + without creating a session. + + Stateless preflight: parses the same environment spec as CreateSession and runs the identical + resolve + validate pipeline against the capability descriptor stamped on the selected + template's image (resolved per-request via the sandbox, not a separate registry). Returns + per-rule verdicts. Nothing is provisioned or persisted. + + Each rule is validated against the descriptor for the (agent, version) the template resolves to + and gets a verdict: ``exact``\\ , ``degraded``\\ , ``unsupported``\\ , or ``unverified``. + ``ok`` is false when any strict rule is unsupported or unverified — i.e. CreateSession would + reject the policy for that agent. An empty ``spec.permissions`` returns ``ok: true`` with no + verdicts. Because it shares the create path's descriptor lookup, the verdicts match what + CreateSession would compute. + + :param data: The same environment spec you would submit to CreateSession (YAML). Only + agent/template selection + and the permissions block are used. The live API also accepts ``application/yaml``\\ , + ``text/yaml``\\ , and + ``text/x-yaml``\\ ; OpenAPI ``content`` lists a single media type so generated clients can + emit this + operation. Required. + :type data: IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "agent": "str", # Optional. + "defaultAction": "str", # Optional. Catch-all disposition AS SHIPPED (after + unverified allow"u2192ask downgrade). When defaultActionVerdict is degraded, this + remains the shipped value; see defaultActionRendered for what the agent will + actually enforce. Known values are: "allow", "ask", and "deny". + "defaultActionReason": "str", # Optional. Human-readable explanation when + defaultActionVerdict is degraded. + "defaultActionRendered": "str", # Optional. Disposition the agent will + actually enforce as the catch-all: equals defaultAction when exact; the + more-restrictive degrade target (usually ask) when degraded. Known values are: + "allow", "ask", and "deny". + "defaultActionVerdict": "str", # Optional. exact "u2014 agent can enforce + the catch-all as written; degraded "u2014 agent cannot enforce the requested + defaultAction (warn-only; shipped value unchanged); omitted when no descriptor + was available to classify against. Known values are: "exact", "degraded", + "unsupported", and "unverified". + "ok": bool, # Optional. False when any strict rule is unsupported or + unverified "u2014 i.e. CreateSession would reject this policy for this (agent, + version). + "verdicts": [ + { + "reason": "str", # Optional. Human-readable explanation of + the verdict. + "rendered": "str", # Optional. The disposition the agent + will actually enforce: the requested action when exact/unverified, the + more-restrictive target when degraded, omitted when unsupported (cannot + render). Known values are: "allow", "ask", and "deny". + "rule": { + "action": "str", # Optional. Known values are: + "allow", "ask", and "deny". + "enforcement": "str", # Optional. Known values are: + "strict" and "best-effort". + "id": "str", # Optional. Stable rule id assigned + during resolution; correlates with the PolicyDenied event's matched + rule. + "match": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "tool": "str" # Optional. A rule after resolution + ("u2229 tenant "u2229 platform), carrying a stable id for audit + correlation. + }, + "suggestion": "str", # Optional. Optional actionable hint + (e.g. "set enforcement: best-effort, or target a capable agent"). + "verdict": "str" # Optional. exact "u2014 enforced precisely + as written; degraded "u2014 renderable only in a more-restrictive form + (deny"u2192ask), warned; unsupported "u2014 cannot be enforced (strict + "u21d2 session reject); unverified "u2014 no descriptor available, + rendered conservatively but unproven. Known values are: "exact", + "degraded", "unsupported", and "unverified". + } + ], + "version": "str" # Optional. + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + content_type: str = kwargs.pop("content_type") + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _content = data + if not content_type: + raise TypeError( + "Missing required keyword-only argument: content_type. Known values are:" + + "'application/x-yaml'" + ) + + _request = build_harness_validate_session_policy_request( + content_type=content_type, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_session_sandbox_sizes(self, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """ListSandboxSizes returns the customer-selectable sandbox (microVM) size catalog. + + Read-only catalog the UI (and other clients) render as the size picker, instead of hardcoding + the list. The set is the single source of truth shared with create-time validation and the + default-size fallback, so every ``slug`` returned here is accepted by CreateSession as + spec.sandbox.sizeSlug. Sizes are returned smallest-to-largest. Static and safe to cache; + independent of team/session; no pagination. + + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "sizes": [ + { + "memory_mb": 0, # Optional. Memory in MB (for display). + "price_per_hour_usd": 0.0, # Optional. Hourly on-demand + price in USD (for display). + "price_per_month_usd": 0.0, # Optional. Monthly on-demand + price in USD, assuming continuous usage (for display). + "slug": "str", # Optional. DO size slug; submit as + spec.sandbox.sizeSlug on CreateSession. e.g. "mars-2vcpu-4gb". + "vcpus": 0 # Optional. vCPU count (for display). + } + ] + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_session_sandbox_sizes_request( + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_session( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """DestroySession tears down the sandbox and marks the session DESTROYED. + + Persistent workspace contents are retained per tenant policy. Returns 204 No Content on + success. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the session + row as the device that performed the destroy. Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_delete_session_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_session(self, session_id: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """GetSession returns the current snapshot of a session. + + GetSession returns the current snapshot of a session. + + :param session_id: The session UUID. Required. + :type session_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_session_request( + session_id=session_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def update_session( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "resume_on_topoff": bool # Optional. When present, sets whether this session + opts in to automatic resumption when the team's prepayment balance is restored + after a low-balance pause. Session-scoped consent, not config-scoped. Omitted + properties are not modified. + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def update_session( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def update_session( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "resume_on_topoff": bool # Optional. When present, sets whether this session + opts in to automatic resumption when the team's prepayment balance is restored + after a low-balance pause. Session-scoped consent, not config-scoped. Omitted + properties are not modified. + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_update_session_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_session_checkpoints( + self, + session_id: str, + *, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ListCheckpoints enumerates a session's checkpoints, newest first. + + Keyset-paginated on an opaque cursor, like ListSessions. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive. Default value is + None. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoints": [ + { + "checkpoint_id": "str", # Optional. Public checkpoint id, + stable for the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Newest + first. + "error_message": "str", # Optional. Client-safe failure + detail; set only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of + the last event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted + automatically by the platform (on run completion, or when ForkSession is + called without from_checkpoint_id). Known values are: "explicit" and + "implicit". + "label": "str", # Optional. Optional user-supplied label + from CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine + state. 0 until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> + READY | FAILED. Only READY checkpoints are forkable/restorable. + CreateCheckpoint blocks until terminal, so PENDING is only ever + observable via list/get while a capture is in flight. Known values are: + "PENDING", "READY", and "FAILED". + } + ], + "next_page_token": "str" # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + } + # response body for status code(s): 400, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_session_checkpoints_request( + session_id=session_id, + page_size=page_size, + page_token=page_token, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[JSON] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Default value + is None. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "label": "str" # Optional. Optional human-readable label stored on the + checkpoint; "" if omitted. + } + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[IO[bytes]] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Default value + is None. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[Union[JSON, IO[bytes]]] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Is either a + JSON type or a IO[bytes] type. Default value is None. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "label": "str" # Optional. Optional human-readable label stored on the + checkpoint; "" if omitted. + } + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + if body is not None: + _json = body + else: + _json = None + + _request = build_harness_create_session_checkpoint_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """GetCheckpoint returns the current snapshot of a checkpoint. + + GetCheckpoint returns the current snapshot of a checkpoint. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """DeleteCheckpoint removes a checkpoint and its underlying machine-state capture. + + Idempotent: deleting an already-gone checkpoint still returns 200. Removes the DB row and the + substrate checkpoint. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint_id": "str", # Optional. Echo of the requested checkpoint id. + "deleted": bool # Optional. Always true on 200 (delete is idempotent). + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_delete_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def revert_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """RollbackToCheckpoint rewinds a session to a checkpoint. + + In-place restore: boots a fresh sandbox from the checkpoint and atomically swaps the session's + sandbox pointer to it, so the session keeps its same session_id; the old sandbox is then torn + down. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_revert_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session_fork( + self, + session_id: str, + body: Optional[JSON] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Default value is None. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "count": 1, # Optional. Default value is 1. Number of child sessions to + create, 1-4. Defaults to 1 when omitted; a value outside 1-4 is rejected with + 400. + "from_checkpoint_id": "str" # Optional. Checkpoint to fork from; must be + READY. Omitted/empty means capture an implicit checkpoint of the session "now" + first and fork from that. + } + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_fork( + self, + session_id: str, + body: Optional[IO[bytes]] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Default value is None. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_fork( + self, + session_id: str, + body: Optional[Union[JSON, IO[bytes]]] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Is either a JSON type or a IO[bytes] type. Default value is None. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "count": 1, # Optional. Default value is 1. Number of child sessions to + create, 1-4. Defaults to 1 when omitted; a value outside 1-4 is rejected with + 400. + "from_checkpoint_id": "str" # Optional. Checkpoint to fork from; must be + READY. Omitted/empty means capture an implicit checkpoint of the session "now" + first and fork from that. + } + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + if body is not None: + _json = body + else: + _json = None + + _request = build_harness_create_session_fork_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: JSON, + *, + x_device_uuid: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Required. + :type body: JSON + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "outcome": "HITL_OUTCOME_UNSPECIFIED", # Default value is + "HITL_OUTCOME_UNSPECIFIED". HITLOutcome is the user's decision on a HITL request. + DEFER backs off this attempt without treating it as a rejection. Known values + are: "HITL_OUTCOME_UNSPECIFIED", "HITL_OUTCOME_APPROVE", "HITL_OUTCOME_REJECT", + and "HITL_OUTCOME_DEFER". + "reason": "str", # Optional. Optional free-text reason. Captured in the + audit trail; not interpreted. + "source": "RESOLUTION_SOURCE_UNSPECIFIED" # Optional. Default value is + "RESOLUTION_SOURCE_UNSPECIFIED". How a HITL resolution was triggered. Defaults to + RESOLUTION_SOURCE_OUT_OF_BAND when omitted. Known values are: + "RESOLUTION_SOURCE_UNSPECIFIED", "RESOLUTION_SOURCE_INLINE_KEYSTROKE", and + "RESOLUTION_SOURCE_OUT_OF_BAND". + } + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: IO[bytes], + *, + x_device_uuid: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Required. + :type body: IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: Union[JSON, IO[bytes]], + *, + x_device_uuid: Optional[str] = None, + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "outcome": "HITL_OUTCOME_UNSPECIFIED", # Default value is + "HITL_OUTCOME_UNSPECIFIED". HITLOutcome is the user's decision on a HITL request. + DEFER backs off this attempt without treating it as a rejection. Known values + are: "HITL_OUTCOME_UNSPECIFIED", "HITL_OUTCOME_APPROVE", "HITL_OUTCOME_REJECT", + and "HITL_OUTCOME_DEFER". + "reason": "str", # Optional. Optional free-text reason. Captured in the + audit trail; not interpreted. + "source": "RESOLUTION_SOURCE_UNSPECIFIED" # Optional. Default value is + "RESOLUTION_SOURCE_UNSPECIFIED". How a HITL resolution was triggered. Defaults to + RESOLUTION_SOURCE_OUT_OF_BAND when omitted. Known values are: + "RESOLUTION_SOURCE_UNSPECIFIED", "RESOLUTION_SOURCE_INLINE_KEYSTROKE", and + "RESOLUTION_SOURCE_OUT_OF_BAND". + } + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_post_session_hitl_request( + session_id=session_id, + request_id_parameter=request_id_parameter, + x_device_uuid=x_device_uuid, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 400, 402, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_session_input( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "text": "str" # The user message to deliver into the session. Required and + non-empty. Required. + } + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_input( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_input( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "text": "str" # The user message to deliver into the session. Required and + non-empty. Required. + } + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_session_input_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 402, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def post_session_pause( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """PauseSession freezes the session's microVM sandbox. + + Supported on microvm-substrate sessions only; dewdrop/docker sessions return 501 Not + Implemented. Paused sessions count against quota. Clients learn state via + GetSession/ListSessions; no live session.updated event is emitted. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded for audit. + Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_post_session_pause_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def post_session_resume( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResumeSession explicitly thaws a PAUSED session's microVM sandbox. + + Only valid for microvm-substrate sessions; dewdrop/docker sessions return 501 Not Implemented. + Implicit resume also occurs when SendInput or ResolveHITL is called on a paused session. + Returns synchronously once the sandbox is ready. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded for audit. + Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_post_session_resume_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def post_session_sandbox_exec( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "argv": [ + "str" # Program and arguments. Required and non-empty; run directly, + with no shell interpretation. Required. + ], + "stdin": bytes("bytes", encoding="utf-8"), # Optional. Optional base64 stdin + payload, delivered in full and then closed so the process sees EOF. One-shot only + "u2014 there is no streaming stdin on this endpoint. + "timeout_seconds": 0, # Optional. Optional timeout. Clamped server-side + (default 60, maximum 240) regardless of the value sent. + "workdir": "str" # Optional. Working directory inside the sandbox. Empty + uses the sandbox default (/workspace); not restricted to the workspace root. + } + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def post_session_sandbox_exec( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def post_session_sandbox_exec( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Is either a JSON type or a IO[bytes] type. + Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "argv": [ + "str" # Program and arguments. Required and non-empty; run directly, + with no shell interpretation. Required. + ], + "stdin": bytes("bytes", encoding="utf-8"), # Optional. Optional base64 stdin + payload, delivered in full and then closed so the process sees EOF. One-shot only + "u2014 there is no streaming stdin on this endpoint. + "timeout_seconds": 0, # Optional. Optional timeout. Clamped server-side + (default 60, maximum 240) regardless of the value sent. + "workdir": "str" # Optional. Working directory inside the sandbox. Empty + uses the sandbox default (/workspace); not restricted to the workspace root. + } + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_post_session_sandbox_exec_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 402, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_session_workspace_download( + self, + session_id: str, + *, + path: str, + as_archive: Optional[bool] = None, + **kwargs: Any + ) -> Union[AsyncIterator[bytes], JSON]: + # pylint: disable=line-too-long + """Download a file or tar archive from the session's sandbox workspace. + + Streams the workspace payload back as ``application/octet-stream`` using chunked transfer + encoding, followed by a fixed integrity footer. + + Response body layout: ```` + ``