From 47ba0547e248280eca96a0acf762f357b3393afe Mon Sep 17 00:00:00 2001
From: sjmiller609 <7516283+sjmiller609@users.noreply.github.com>
Date: Fri, 7 Aug 2026 18:42:47 +0000
Subject: [PATCH 1/4] Build SDK
Stainless-Generated-From: 752706f5daca4d21f33a833f0267a2666d93f153
---
.stats.yml | 2 +-
api.md | 26 +-
src/kernel/_client.py | 2 +-
src/kernel/resources/audit_logs/__init__.py | 33 +
.../resources/{ => audit_logs}/audit_logs.py | 58 +-
.../audit_logs/export_destinations.py | 691 ++++++++++++++++++
src/kernel/types/audit_logs/__init__.py | 11 +
.../audit_log_export_destination.py | 79 ++
...udit_log_export_destination_test_result.py | 22 +
.../export_destination_create_params.py | 23 +
.../export_destination_list_params.py | 15 +
.../export_destination_update_params.py | 26 +
tests/api_resources/audit_logs/__init__.py | 1 +
.../audit_logs/test_export_destinations.py | 592 +++++++++++++++
14 files changed, 1567 insertions(+), 14 deletions(-)
create mode 100644 src/kernel/resources/audit_logs/__init__.py
rename src/kernel/resources/{ => audit_logs}/audit_logs.py (89%)
create mode 100644 src/kernel/resources/audit_logs/export_destinations.py
create mode 100644 src/kernel/types/audit_logs/__init__.py
create mode 100644 src/kernel/types/audit_logs/audit_log_export_destination.py
create mode 100644 src/kernel/types/audit_logs/audit_log_export_destination_test_result.py
create mode 100644 src/kernel/types/audit_logs/export_destination_create_params.py
create mode 100644 src/kernel/types/audit_logs/export_destination_list_params.py
create mode 100644 src/kernel/types/audit_logs/export_destination_update_params.py
create mode 100644 tests/api_resources/audit_logs/__init__.py
create mode 100644 tests/api_resources/audit_logs/test_export_destinations.py
diff --git a/.stats.yml b/.stats.yml
index 71c320fc..4bb27f8b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1 +1 @@
-configured_endpoints: 127
+configured_endpoints: 133
diff --git a/api.md b/api.md
index 2685a9d5..bf8b28ae 100644
--- a/api.md
+++ b/api.md
@@ -458,8 +458,30 @@ from kernel.types import AuditLogEntry
Methods:
-- client.audit_logs.list(\*\*params) -> SyncPageTokenPagination[AuditLogEntry]
-- client.audit_logs.export_chunk(\*\*params) -> BinaryAPIResponse
+- client.audit_logs.list(\*\*params) -> SyncPageTokenPagination[AuditLogEntry]
+- client.audit_logs.export_chunk(\*\*params) -> BinaryAPIResponse
+
+## ExportDestinations
+
+Types:
+
+```python
+from kernel.types.audit_logs import (
+ AuditLogExportDestination,
+ AuditLogExportDestinationTestResult,
+ CreateAuditLogExportDestinationRequest,
+ UpdateAuditLogExportDestinationRequest,
+)
+```
+
+Methods:
+
+- client.audit_logs.export_destinations.create(\*\*params) -> AuditLogExportDestination
+- client.audit_logs.export_destinations.retrieve(id) -> AuditLogExportDestination
+- client.audit_logs.export_destinations.update(id, \*\*params) -> AuditLogExportDestination
+- client.audit_logs.export_destinations.list(\*\*params) -> SyncOffsetPagination[AuditLogExportDestination]
+- client.audit_logs.export_destinations.delete(id) -> None
+- client.audit_logs.export_destinations.test(id) -> AuditLogExportDestinationTestResult
# APIKeys
diff --git a/src/kernel/_client.py b/src/kernel/_client.py
index 64c7136b..62bd2014 100644
--- a/src/kernel/_client.py
+++ b/src/kernel/_client.py
@@ -68,7 +68,6 @@
from .resources.api_keys import APIKeysResource, AsyncAPIKeysResource
from .resources.profiles import ProfilesResource, AsyncProfilesResource
from .resources.auth.auth import AuthResource, AsyncAuthResource
- from .resources.audit_logs import AuditLogsResource, AsyncAuditLogsResource
from .resources.extensions import ExtensionsResource, AsyncExtensionsResource
from .resources.credentials import CredentialsResource, AsyncCredentialsResource
from .resources.deployments import DeploymentsResource, AsyncDeploymentsResource
@@ -77,6 +76,7 @@
from .resources.browsers.browsers import BrowsersResource, AsyncBrowsersResource
from .resources.projects.projects import ProjectsResource, AsyncProjectsResource
from .resources.credential_providers import CredentialProvidersResource, AsyncCredentialProvidersResource
+ from .resources.audit_logs.audit_logs import AuditLogsResource, AsyncAuditLogsResource
from .resources.organization.organization import OrganizationResource, AsyncOrganizationResource
__all__ = [
diff --git a/src/kernel/resources/audit_logs/__init__.py b/src/kernel/resources/audit_logs/__init__.py
new file mode 100644
index 00000000..5127038d
--- /dev/null
+++ b/src/kernel/resources/audit_logs/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .audit_logs import (
+ AuditLogsResource,
+ AsyncAuditLogsResource,
+ AuditLogsResourceWithRawResponse,
+ AsyncAuditLogsResourceWithRawResponse,
+ AuditLogsResourceWithStreamingResponse,
+ AsyncAuditLogsResourceWithStreamingResponse,
+)
+from .export_destinations import (
+ ExportDestinationsResource,
+ AsyncExportDestinationsResource,
+ ExportDestinationsResourceWithRawResponse,
+ AsyncExportDestinationsResourceWithRawResponse,
+ ExportDestinationsResourceWithStreamingResponse,
+ AsyncExportDestinationsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ExportDestinationsResource",
+ "AsyncExportDestinationsResource",
+ "ExportDestinationsResourceWithRawResponse",
+ "AsyncExportDestinationsResourceWithRawResponse",
+ "ExportDestinationsResourceWithStreamingResponse",
+ "AsyncExportDestinationsResourceWithStreamingResponse",
+ "AuditLogsResource",
+ "AsyncAuditLogsResource",
+ "AuditLogsResourceWithRawResponse",
+ "AsyncAuditLogsResourceWithRawResponse",
+ "AuditLogsResourceWithStreamingResponse",
+ "AsyncAuditLogsResourceWithStreamingResponse",
+]
diff --git a/src/kernel/resources/audit_logs.py b/src/kernel/resources/audit_logs/audit_logs.py
similarity index 89%
rename from src/kernel/resources/audit_logs.py
rename to src/kernel/resources/audit_logs/audit_logs.py
index aac5d71c..d6aa80a6 100644
--- a/src/kernel/resources/audit_logs.py
+++ b/src/kernel/resources/audit_logs/audit_logs.py
@@ -8,12 +8,12 @@
import httpx
-from ..types import audit_log_list_params, audit_log_export_chunk_params
-from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
+from ...types import audit_log_list_params, audit_log_export_chunk_params
+from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
BinaryAPIResponse,
AsyncBinaryAPIResponse,
StreamedBinaryAPIResponse,
@@ -27,10 +27,18 @@
async_to_custom_raw_response_wrapper,
async_to_custom_streamed_response_wrapper,
)
-from ..pagination import SyncPageTokenPagination, AsyncPageTokenPagination
-from .._base_client import AsyncPaginator, make_request_options
-from ..types.audit_log_entry import AuditLogEntry
-from ..lib.audit_log_download import (
+from ...pagination import SyncPageTokenPagination, AsyncPageTokenPagination
+from ..._base_client import AsyncPaginator, make_request_options
+from .export_destinations import (
+ ExportDestinationsResource,
+ AsyncExportDestinationsResource,
+ ExportDestinationsResourceWithRawResponse,
+ AsyncExportDestinationsResourceWithRawResponse,
+ ExportDestinationsResourceWithStreamingResponse,
+ AsyncExportDestinationsResourceWithStreamingResponse,
+)
+from ...types.audit_log_entry import AuditLogEntry
+from ...lib.audit_log_download import (
ProgressCallback,
AsyncProgressCallback,
AuditLogDownloadResult,
@@ -44,6 +52,11 @@
class AuditLogsResource(SyncAPIResource):
"""Read audit log records for the authenticated organization."""
+ @cached_property
+ def export_destinations(self) -> ExportDestinationsResource:
+ """Read audit log records for the authenticated organization."""
+ return ExportDestinationsResource(self._client)
+
@cached_property
def with_raw_response(self) -> AuditLogsResourceWithRawResponse:
"""
@@ -287,6 +300,11 @@ def fetch_chunk(cursor: str | None) -> ContextManager[StreamedBinaryAPIResponse]
class AsyncAuditLogsResource(AsyncAPIResource):
"""Read audit log records for the authenticated organization."""
+ @cached_property
+ def export_destinations(self) -> AsyncExportDestinationsResource:
+ """Read audit log records for the authenticated organization."""
+ return AsyncExportDestinationsResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncAuditLogsResourceWithRawResponse:
"""
@@ -539,6 +557,11 @@ def __init__(self, audit_logs: AuditLogsResource) -> None:
BinaryAPIResponse,
)
+ @cached_property
+ def export_destinations(self) -> ExportDestinationsResourceWithRawResponse:
+ """Read audit log records for the authenticated organization."""
+ return ExportDestinationsResourceWithRawResponse(self._audit_logs.export_destinations)
+
class AsyncAuditLogsResourceWithRawResponse:
def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
@@ -552,6 +575,11 @@ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
AsyncBinaryAPIResponse,
)
+ @cached_property
+ def export_destinations(self) -> AsyncExportDestinationsResourceWithRawResponse:
+ """Read audit log records for the authenticated organization."""
+ return AsyncExportDestinationsResourceWithRawResponse(self._audit_logs.export_destinations)
+
class AuditLogsResourceWithStreamingResponse:
def __init__(self, audit_logs: AuditLogsResource) -> None:
@@ -565,6 +593,11 @@ def __init__(self, audit_logs: AuditLogsResource) -> None:
StreamedBinaryAPIResponse,
)
+ @cached_property
+ def export_destinations(self) -> ExportDestinationsResourceWithStreamingResponse:
+ """Read audit log records for the authenticated organization."""
+ return ExportDestinationsResourceWithStreamingResponse(self._audit_logs.export_destinations)
+
class AsyncAuditLogsResourceWithStreamingResponse:
def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
@@ -577,3 +610,8 @@ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
audit_logs.export_chunk,
AsyncStreamedBinaryAPIResponse,
)
+
+ @cached_property
+ def export_destinations(self) -> AsyncExportDestinationsResourceWithStreamingResponse:
+ """Read audit log records for the authenticated organization."""
+ return AsyncExportDestinationsResourceWithStreamingResponse(self._audit_logs.export_destinations)
diff --git a/src/kernel/resources/audit_logs/export_destinations.py b/src/kernel/resources/audit_logs/export_destinations.py
new file mode 100644
index 00000000..c5d79624
--- /dev/null
+++ b/src/kernel/resources/audit_logs/export_destinations.py
@@ -0,0 +1,691 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncOffsetPagination, AsyncOffsetPagination
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.audit_logs import (
+ export_destination_list_params,
+ export_destination_create_params,
+ export_destination_update_params,
+)
+from ...types.audit_logs.audit_log_export_destination import AuditLogExportDestination
+from ...types.audit_logs.audit_log_export_destination_test_result import AuditLogExportDestinationTestResult
+
+__all__ = ["ExportDestinationsResource", "AsyncExportDestinationsResource"]
+
+
+class ExportDestinationsResource(SyncAPIResource):
+ """Read audit log records for the authenticated organization."""
+
+ @cached_property
+ def with_raw_response(self) -> ExportDestinationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return ExportDestinationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ExportDestinationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#with_streaming_response
+ """
+ return ExportDestinationsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ bucket: str,
+ format: Literal["jsonl.gz"],
+ prefix: str,
+ region: str,
+ role_arn: str,
+ type: Literal["s3"],
+ kms_key_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """Create a paused destination.
+
+ Activate it with a status update once the
+ destination test passes. Requires an active Enterprise plan.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/audit-logs/export/destinations",
+ body=maybe_transform(
+ {
+ "bucket": bucket,
+ "format": format,
+ "prefix": prefix,
+ "region": region,
+ "role_arn": role_arn,
+ "type": type,
+ "kms_key_id": kms_key_id,
+ },
+ export_destination_create_params.ExportDestinationCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """
+ Retrieve details for a single audit log export destination by its ID.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ def update(
+ self,
+ id: str,
+ *,
+ bucket: str | Omit = omit,
+ kms_key_id: str | Omit = omit,
+ prefix: str | Omit = omit,
+ region: str | Omit = omit,
+ role_arn: str | Omit = omit,
+ status: Literal["active", "paused"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """Apply a partial update to a destination.
+
+ Requires an active Enterprise plan.
+ Returns 409 when the destination was changed concurrently, because the merged
+ configuration this request validated is no longer the one that would be stored;
+ retry against fresh state. Pausing prevents new delivery attempts, but an S3
+ upload already in progress may complete after the response.
+
+ Args:
+ kms_key_id: KMS key ID, alias, or ARN. Set to an empty string to remove the configured KMS
+ key; omit or send null to leave unchanged.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._patch(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ body=maybe_transform(
+ {
+ "bucket": bucket,
+ "kms_key_id": kms_key_id,
+ "prefix": prefix,
+ "region": region,
+ "role_arn": role_arn,
+ "status": status,
+ },
+ export_destination_update_params.ExportDestinationUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SyncOffsetPagination[AuditLogExportDestination]:
+ """
+ List audit log export destinations for the organization with pagination support.
+
+ Args:
+ limit: Limit the number of destinations to return.
+
+ offset: Offset the number of destinations to return.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/audit-logs/export/destinations",
+ page=SyncOffsetPagination[AuditLogExportDestination],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ export_destination_list_params.ExportDestinationListParams,
+ ),
+ ),
+ model=AuditLogExportDestination,
+ )
+
+ def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Soft delete the destination and prevent new delivery attempts.
+
+ An S3 upload
+ already in progress may complete after the response.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def test(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestinationTestResult:
+ """
+ Verify the destination is writable by assuming the configured role and uploading
+ a temporary probe object with the same request metadata as a real delivery.
+ Requires an active Enterprise plan.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ path_template("/audit-logs/export/destinations/{id}/test", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestinationTestResult,
+ )
+
+
+class AsyncExportDestinationsResource(AsyncAPIResource):
+ """Read audit log records for the authenticated organization."""
+
+ @cached_property
+ def with_raw_response(self) -> AsyncExportDestinationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncExportDestinationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncExportDestinationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncExportDestinationsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ bucket: str,
+ format: Literal["jsonl.gz"],
+ prefix: str,
+ region: str,
+ role_arn: str,
+ type: Literal["s3"],
+ kms_key_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """Create a paused destination.
+
+ Activate it with a status update once the
+ destination test passes. Requires an active Enterprise plan.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/audit-logs/export/destinations",
+ body=await async_maybe_transform(
+ {
+ "bucket": bucket,
+ "format": format,
+ "prefix": prefix,
+ "region": region,
+ "role_arn": role_arn,
+ "type": type,
+ "kms_key_id": kms_key_id,
+ },
+ export_destination_create_params.ExportDestinationCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """
+ Retrieve details for a single audit log export destination by its ID.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ async def update(
+ self,
+ id: str,
+ *,
+ bucket: str | Omit = omit,
+ kms_key_id: str | Omit = omit,
+ prefix: str | Omit = omit,
+ region: str | Omit = omit,
+ role_arn: str | Omit = omit,
+ status: Literal["active", "paused"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestination:
+ """Apply a partial update to a destination.
+
+ Requires an active Enterprise plan.
+ Returns 409 when the destination was changed concurrently, because the merged
+ configuration this request validated is no longer the one that would be stored;
+ retry against fresh state. Pausing prevents new delivery attempts, but an S3
+ upload already in progress may complete after the response.
+
+ Args:
+ kms_key_id: KMS key ID, alias, or ARN. Set to an empty string to remove the configured KMS
+ key; omit or send null to leave unchanged.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._patch(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ body=await async_maybe_transform(
+ {
+ "bucket": bucket,
+ "kms_key_id": kms_key_id,
+ "prefix": prefix,
+ "region": region,
+ "role_arn": role_arn,
+ "status": status,
+ },
+ export_destination_update_params.ExportDestinationUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestination,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[AuditLogExportDestination, AsyncOffsetPagination[AuditLogExportDestination]]:
+ """
+ List audit log export destinations for the organization with pagination support.
+
+ Args:
+ limit: Limit the number of destinations to return.
+
+ offset: Offset the number of destinations to return.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/audit-logs/export/destinations",
+ page=AsyncOffsetPagination[AuditLogExportDestination],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ export_destination_list_params.ExportDestinationListParams,
+ ),
+ ),
+ model=AuditLogExportDestination,
+ )
+
+ async def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Soft delete the destination and prevent new delivery attempts.
+
+ An S3 upload
+ already in progress may complete after the response.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/audit-logs/export/destinations/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def test(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuditLogExportDestinationTestResult:
+ """
+ Verify the destination is writable by assuming the configured role and uploading
+ a temporary probe object with the same request metadata as a real delivery.
+ Requires an active Enterprise plan.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ path_template("/audit-logs/export/destinations/{id}/test", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuditLogExportDestinationTestResult,
+ )
+
+
+class ExportDestinationsResourceWithRawResponse:
+ def __init__(self, export_destinations: ExportDestinationsResource) -> None:
+ self._export_destinations = export_destinations
+
+ self.create = to_raw_response_wrapper(
+ export_destinations.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ export_destinations.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ export_destinations.update,
+ )
+ self.list = to_raw_response_wrapper(
+ export_destinations.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ export_destinations.delete,
+ )
+ self.test = to_raw_response_wrapper(
+ export_destinations.test,
+ )
+
+
+class AsyncExportDestinationsResourceWithRawResponse:
+ def __init__(self, export_destinations: AsyncExportDestinationsResource) -> None:
+ self._export_destinations = export_destinations
+
+ self.create = async_to_raw_response_wrapper(
+ export_destinations.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ export_destinations.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ export_destinations.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ export_destinations.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ export_destinations.delete,
+ )
+ self.test = async_to_raw_response_wrapper(
+ export_destinations.test,
+ )
+
+
+class ExportDestinationsResourceWithStreamingResponse:
+ def __init__(self, export_destinations: ExportDestinationsResource) -> None:
+ self._export_destinations = export_destinations
+
+ self.create = to_streamed_response_wrapper(
+ export_destinations.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ export_destinations.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ export_destinations.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ export_destinations.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ export_destinations.delete,
+ )
+ self.test = to_streamed_response_wrapper(
+ export_destinations.test,
+ )
+
+
+class AsyncExportDestinationsResourceWithStreamingResponse:
+ def __init__(self, export_destinations: AsyncExportDestinationsResource) -> None:
+ self._export_destinations = export_destinations
+
+ self.create = async_to_streamed_response_wrapper(
+ export_destinations.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ export_destinations.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ export_destinations.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ export_destinations.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ export_destinations.delete,
+ )
+ self.test = async_to_streamed_response_wrapper(
+ export_destinations.test,
+ )
diff --git a/src/kernel/types/audit_logs/__init__.py b/src/kernel/types/audit_logs/__init__.py
new file mode 100644
index 00000000..0a6f41ef
--- /dev/null
+++ b/src/kernel/types/audit_logs/__init__.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .audit_log_export_destination import AuditLogExportDestination as AuditLogExportDestination
+from .export_destination_list_params import ExportDestinationListParams as ExportDestinationListParams
+from .export_destination_create_params import ExportDestinationCreateParams as ExportDestinationCreateParams
+from .export_destination_update_params import ExportDestinationUpdateParams as ExportDestinationUpdateParams
+from .audit_log_export_destination_test_result import (
+ AuditLogExportDestinationTestResult as AuditLogExportDestinationTestResult,
+)
diff --git a/src/kernel/types/audit_logs/audit_log_export_destination.py b/src/kernel/types/audit_logs/audit_log_export_destination.py
new file mode 100644
index 00000000..0897cdf7
--- /dev/null
+++ b/src/kernel/types/audit_logs/audit_log_export_destination.py
@@ -0,0 +1,79 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AuditLogExportDestination"]
+
+
+class AuditLogExportDestination(BaseModel):
+ """An organization-scoped audit log export destination.
+
+ Delivery is at-least-once for rows visible when their window is committed: a delivery that is retried rewrites the same object, and the same `event_id` can appear in more than one object, so consumers must deduplicate on `event_id`. Each event-time window is held for ten minutes before it commits; a row that becomes visible after its window is committed may not be delivered.
+
+ Objects are written as `/destination_id=/org_id=/date=/hour=/-.jsonl.gz`, where `date` and `hour` are the UTC calendar hour that fully contains every row in the object, so the layout is safe to register as a Hive-partitioned table. The object name is derived from the rows it holds, so a retried delivery rewrites its own object.
+ """
+
+ id: str
+
+ bucket: str
+
+ consecutive_failures: int
+
+ created_at: datetime
+
+ external_id: str
+
+ format: Literal["jsonl.gz"]
+
+ kernel_role_arn: str
+ """The Kernel role that assumes `role_arn` in your account to deliver logs.
+
+ Allow this role as the principal in your role's trust policy, and require
+ `external_id` as the `sts:ExternalId` condition.
+
+ Recreating a destination issues a new `external_id`, which the trust policy has
+ to be updated to match.
+ """
+
+ prefix: str
+
+ region: str
+
+ role_arn: str
+
+ status: Literal["active", "paused"]
+ """Pausing prevents new delivery attempts.
+
+ An S3 upload already in progress may complete after the pause response; its rows
+ can appear again after the destination is resumed.
+ """
+
+ type: Literal["s3"]
+
+ updated_at: datetime
+
+ kms_key_id: Optional[str] = None
+
+ last_error: Optional[str] = None
+ """Sanitized description of the most recent delivery failure."""
+
+ last_error_at: Optional[datetime] = None
+
+ last_exported_cursor: Optional[str] = None
+ """Opaque, versioned checkpoint for forward-only continuous export.
+
+ This value is not compatible with audit-log list page tokens.
+
+ Delivery starts at the moment the destination is activated, so events recorded
+ before that are not delivered. Pausing stops delivery and resuming starts again
+ from the time of the resume: events recorded while a destination was paused are
+ never exported, and pausing is not a way to defer delivery.
+ """
+
+ last_success_at: Optional[datetime] = None
+
+ next_attempt_at: Optional[datetime] = None
diff --git a/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py b/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py
new file mode 100644
index 00000000..ca1eda6f
--- /dev/null
+++ b/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AuditLogExportDestinationTestResult", "Error"]
+
+
+class Error(BaseModel):
+ code: Literal["assume_role_failed", "put_object_failed"]
+
+ message: str
+
+
+class AuditLogExportDestinationTestResult(BaseModel):
+ stage: Literal["assume_role", "put_object", "complete"]
+
+ success: bool
+
+ error: Optional[Error] = None
diff --git a/src/kernel/types/audit_logs/export_destination_create_params.py b/src/kernel/types/audit_logs/export_destination_create_params.py
new file mode 100644
index 00000000..b01cdee0
--- /dev/null
+++ b/src/kernel/types/audit_logs/export_destination_create_params.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ExportDestinationCreateParams"]
+
+
+class ExportDestinationCreateParams(TypedDict, total=False):
+ bucket: Required[str]
+
+ format: Required[Literal["jsonl.gz"]]
+
+ prefix: Required[str]
+
+ region: Required[str]
+
+ role_arn: Required[str]
+
+ type: Required[Literal["s3"]]
+
+ kms_key_id: str
diff --git a/src/kernel/types/audit_logs/export_destination_list_params.py b/src/kernel/types/audit_logs/export_destination_list_params.py
new file mode 100644
index 00000000..2026a4b1
--- /dev/null
+++ b/src/kernel/types/audit_logs/export_destination_list_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ExportDestinationListParams"]
+
+
+class ExportDestinationListParams(TypedDict, total=False):
+ limit: int
+ """Limit the number of destinations to return."""
+
+ offset: int
+ """Offset the number of destinations to return."""
diff --git a/src/kernel/types/audit_logs/export_destination_update_params.py b/src/kernel/types/audit_logs/export_destination_update_params.py
new file mode 100644
index 00000000..e21538f0
--- /dev/null
+++ b/src/kernel/types/audit_logs/export_destination_update_params.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ExportDestinationUpdateParams"]
+
+
+class ExportDestinationUpdateParams(TypedDict, total=False):
+ bucket: str
+
+ kms_key_id: str
+ """KMS key ID, alias, or ARN.
+
+ Set to an empty string to remove the configured KMS key; omit or send null to
+ leave unchanged.
+ """
+
+ prefix: str
+
+ region: str
+
+ role_arn: str
+
+ status: Literal["active", "paused"]
diff --git a/tests/api_resources/audit_logs/__init__.py b/tests/api_resources/audit_logs/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/audit_logs/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/audit_logs/test_export_destinations.py b/tests/api_resources/audit_logs/test_export_destinations.py
new file mode 100644
index 00000000..34b4fde8
--- /dev/null
+++ b/tests/api_resources/audit_logs/test_export_destinations.py
@@ -0,0 +1,592 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination
+from kernel.types.audit_logs import (
+ AuditLogExportDestination,
+ AuditLogExportDestinationTestResult,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestExportDestinations:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ kms_key_id="kms_key_id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.retrieve(
+ "id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.audit_logs.export_destinations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.update(
+ id="id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update_with_all_params(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.update(
+ id="id",
+ bucket="xxx",
+ kms_key_id="kms_key_id",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ status="active",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.audit_logs.export_destinations.with_raw_response.update(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.list()
+ assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.list(
+ limit=1,
+ offset=0,
+ )
+ assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.delete(
+ "id",
+ )
+ assert export_destination is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert export_destination is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert export_destination is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.audit_logs.export_destinations.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_test(self, client: Kernel) -> None:
+ export_destination = client.audit_logs.export_destinations.test(
+ "id",
+ )
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_test(self, client: Kernel) -> None:
+ response = client.audit_logs.export_destinations.with_raw_response.test(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_test(self, client: Kernel) -> None:
+ with client.audit_logs.export_destinations.with_streaming_response.test(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = response.parse()
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_test(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.audit_logs.export_destinations.with_raw_response.test(
+ "",
+ )
+
+
+class TestAsyncExportDestinations:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ kms_key_id="kms_key_id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.create(
+ bucket="xxx",
+ format="jsonl.gz",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ type="s3",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.retrieve(
+ "id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.audit_logs.export_destinations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.update(
+ id="id",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.update(
+ id="id",
+ bucket="xxx",
+ kms_key_id="kms_key_id",
+ prefix="prefix",
+ region="x",
+ role_arn="x",
+ status="active",
+ )
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestination, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.audit_logs.export_destinations.with_raw_response.update(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.list()
+ assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.list(
+ limit=1,
+ offset=0,
+ )
+ assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.delete(
+ "id",
+ )
+ assert export_destination is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert export_destination is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert export_destination is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.audit_logs.export_destinations.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_test(self, async_client: AsyncKernel) -> None:
+ export_destination = await async_client.audit_logs.export_destinations.test(
+ "id",
+ )
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_test(self, async_client: AsyncKernel) -> None:
+ response = await async_client.audit_logs.export_destinations.with_raw_response.test(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_test(self, async_client: AsyncKernel) -> None:
+ async with async_client.audit_logs.export_destinations.with_streaming_response.test(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ export_destination = await response.parse()
+ assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_test(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.audit_logs.export_destinations.with_raw_response.test(
+ "",
+ )
From 5ae6c3e0b6a54a884b9fc6d3d4658d970a7aa459 Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Sat, 8 Aug 2026 19:56:33 +0000
Subject: [PATCH 2/4] feat: Forward egress deploy owner attribution
Stainless-Generated-From: 00e953c7c4e06216815d4e9f8e53ab7596262ca7
---
api.md | 4 +
src/kernel/resources/auth/connections.py | 91 +++++----
src/kernel/resources/browsers/browsers.py | 65 +++++-
src/kernel/types/__init__.py | 4 +
src/kernel/types/auth/__init__.py | 2 +
.../types/auth/connection_create_params.py | 33 ++--
.../types/auth/connection_login_params.py | 31 ++-
.../types/auth/connection_update_params.py | 33 ++--
src/kernel/types/auth/managed_auth.py | 24 ++-
.../types/auth/managed_auth_browser_config.py | 118 +++++++++++
.../auth/managed_auth_browser_config_param.py | 120 +++++++++++
src/kernel/types/browser_create_params.py | 25 ++-
src/kernel/types/browser_create_response.py | 9 +-
src/kernel/types/browser_list_response.py | 9 +-
.../types/browser_pool_acquire_response.py | 9 +-
src/kernel/types/browser_proxy.py | 30 +++
src/kernel/types/browser_proxy_config.py | 34 ++++
.../types/browser_proxy_config_param.py | 35 ++++
src/kernel/types/browser_proxy_mode.py | 7 +
src/kernel/types/browser_retrieve_response.py | 9 +-
src/kernel/types/browser_update_params.py | 17 +-
src/kernel/types/browser_update_response.py | 9 +-
.../invocation_list_browsers_response.py | 9 +-
tests/api_resources/auth/test_connections.py | 186 ++++++++++++++++++
tests/api_resources/test_browsers.py | 20 ++
25 files changed, 808 insertions(+), 125 deletions(-)
create mode 100644 src/kernel/types/auth/managed_auth_browser_config.py
create mode 100644 src/kernel/types/auth/managed_auth_browser_config_param.py
create mode 100644 src/kernel/types/browser_proxy.py
create mode 100644 src/kernel/types/browser_proxy_config.py
create mode 100644 src/kernel/types/browser_proxy_config_param.py
create mode 100644 src/kernel/types/browser_proxy_mode.py
diff --git a/api.md b/api.md
index 7de716a0..65ac4c06 100644
--- a/api.md
+++ b/api.md
@@ -81,6 +81,9 @@ Types:
```python
from kernel.types import (
BrowserPoolRef,
+ BrowserProxy,
+ BrowserProxyConfig,
+ BrowserProxyMode,
BrowserUsage,
Profile,
Tags,
@@ -314,6 +317,7 @@ Types:
from kernel.types.auth import (
LoginResponse,
ManagedAuth,
+ ManagedAuthBrowserConfig,
ManagedAuthCreateRequest,
ManagedAuthTimelineEvent,
ManagedAuthUpdateRequest,
diff --git a/src/kernel/resources/auth/connections.py b/src/kernel/resources/auth/connections.py
index f5f4529a..a84d42af 100644
--- a/src/kernel/resources/auth/connections.py
+++ b/src/kernel/resources/auth/connections.py
@@ -33,6 +33,7 @@
from ...types.auth.submit_fields_response import SubmitFieldsResponse
from ...types.auth.connection_follow_response import ConnectionFollowResponse
from ...types.auth.managed_auth_timeline_event import ManagedAuthTimelineEvent
+from ...types.auth.managed_auth_browser_config_param import ManagedAuthBrowserConfigParam
__all__ = ["ConnectionsResource", "AsyncConnectionsResource"]
@@ -66,6 +67,7 @@ def create(
profile_name: str,
allowed_domains: SequenceNotStr[str] | Omit = omit,
auto_reauth: bool | Omit = omit,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_create_params.BrowserTelemetry] | Omit = omit,
credential: connection_create_params.Credential | Omit = omit,
health_check_interval: int | Omit = omit,
@@ -120,9 +122,11 @@ def create(
false, expired sessions are marked as `NEEDS_AUTH` instead of attempting
re-auth. Defaults to true.
- browser_telemetry: Browser telemetry configuration used by this connection's browser sessions by
- default. Uses the exact create-browser configuration. Can be overridden
- per-login.
+ browser: Default browser configuration for login, reauthentication, and health-check
+ sessions.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
credential:
Reference to credentials for the auth connection. Use one of:
@@ -144,10 +148,7 @@ def create(
login_url: Optional login page URL to skip discovery
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Whether to record browser sessions for this connection by default. Useful for
debugging. Can be overridden per-login. Defaults to false.
@@ -171,6 +172,7 @@ def create(
"profile_name": profile_name,
"allowed_domains": allowed_domains,
"auto_reauth": auto_reauth,
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"credential": credential,
"health_check_interval": health_check_interval,
@@ -229,6 +231,7 @@ def update(
*,
allowed_domains: SequenceNotStr[str] | Omit = omit,
auto_reauth: bool | Omit = omit,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_update_params.BrowserTelemetry] | Omit = omit,
credential: connection_update_params.Credential | Omit = omit,
health_check_interval: int | Omit = omit,
@@ -260,9 +263,11 @@ def update(
when `health_checks` is false. When false, expired sessions detected by a health
check are marked as `NEEDS_AUTH` instead of attempting re-auth.
- browser_telemetry: Browser telemetry configuration used by future browser sessions for this
- connection. Uses the exact create-browser configuration. Set enabled to false to
- disable telemetry.
+ browser: Browser configuration updates for future login, reauthentication, and
+ health-check sessions. Omitted properties remain unchanged.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
credential:
Reference to credentials for the auth connection. Use one of:
@@ -280,10 +285,7 @@ def update(
login_url: Login page URL. Set to empty string to clear.
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Whether to record browser sessions for this connection by default
@@ -305,6 +307,7 @@ def update(
{
"allowed_domains": allowed_domains,
"auto_reauth": auto_reauth,
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"credential": credential,
"health_check_interval": health_check_interval,
@@ -464,6 +467,7 @@ def login(
self,
id: str,
*,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_login_params.BrowserTelemetry] | Omit = omit,
proxy: connection_login_params.Proxy | Omit = omit,
record_session: bool | Omit = omit,
@@ -481,14 +485,13 @@ def login(
credentials are stored.
Args:
- browser_telemetry: Override the connection's default browser telemetry configuration for this
- login. When omitted, the connection's browser_telemetry default is used. Uses
- the exact create-browser configuration.
+ browser: Browser configuration override for this login. Omitted properties inherit the
+ connection defaults.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Override the connection's default for recording this login's browser session.
When omitted, the connection's record_session default is used.
@@ -507,6 +510,7 @@ def login(
path_template("/auth/connections/{id}/login", id=id),
body=maybe_transform(
{
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"proxy": proxy,
"record_session": record_session,
@@ -675,6 +679,7 @@ async def create(
profile_name: str,
allowed_domains: SequenceNotStr[str] | Omit = omit,
auto_reauth: bool | Omit = omit,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_create_params.BrowserTelemetry] | Omit = omit,
credential: connection_create_params.Credential | Omit = omit,
health_check_interval: int | Omit = omit,
@@ -729,9 +734,11 @@ async def create(
false, expired sessions are marked as `NEEDS_AUTH` instead of attempting
re-auth. Defaults to true.
- browser_telemetry: Browser telemetry configuration used by this connection's browser sessions by
- default. Uses the exact create-browser configuration. Can be overridden
- per-login.
+ browser: Default browser configuration for login, reauthentication, and health-check
+ sessions.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
credential:
Reference to credentials for the auth connection. Use one of:
@@ -753,10 +760,7 @@ async def create(
login_url: Optional login page URL to skip discovery
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Whether to record browser sessions for this connection by default. Useful for
debugging. Can be overridden per-login. Defaults to false.
@@ -780,6 +784,7 @@ async def create(
"profile_name": profile_name,
"allowed_domains": allowed_domains,
"auto_reauth": auto_reauth,
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"credential": credential,
"health_check_interval": health_check_interval,
@@ -838,6 +843,7 @@ async def update(
*,
allowed_domains: SequenceNotStr[str] | Omit = omit,
auto_reauth: bool | Omit = omit,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_update_params.BrowserTelemetry] | Omit = omit,
credential: connection_update_params.Credential | Omit = omit,
health_check_interval: int | Omit = omit,
@@ -869,9 +875,11 @@ async def update(
when `health_checks` is false. When false, expired sessions detected by a health
check are marked as `NEEDS_AUTH` instead of attempting re-auth.
- browser_telemetry: Browser telemetry configuration used by future browser sessions for this
- connection. Uses the exact create-browser configuration. Set enabled to false to
- disable telemetry.
+ browser: Browser configuration updates for future login, reauthentication, and
+ health-check sessions. Omitted properties remain unchanged.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
credential:
Reference to credentials for the auth connection. Use one of:
@@ -889,10 +897,7 @@ async def update(
login_url: Login page URL. Set to empty string to clear.
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Whether to record browser sessions for this connection by default
@@ -914,6 +919,7 @@ async def update(
{
"allowed_domains": allowed_domains,
"auto_reauth": auto_reauth,
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"credential": credential,
"health_check_interval": health_check_interval,
@@ -1073,6 +1079,7 @@ async def login(
self,
id: str,
*,
+ browser: ManagedAuthBrowserConfigParam | Omit = omit,
browser_telemetry: Optional[connection_login_params.BrowserTelemetry] | Omit = omit,
proxy: connection_login_params.Proxy | Omit = omit,
record_session: bool | Omit = omit,
@@ -1090,14 +1097,13 @@ async def login(
credentials are stored.
Args:
- browser_telemetry: Override the connection's default browser telemetry configuration for this
- login. When omitted, the connection's browser_telemetry default is used. Uses
- the exact create-browser configuration.
+ browser: Browser configuration override for this login. Omitted properties inherit the
+ connection defaults.
+
+ browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing
+ clients.
- proxy: Proxy selection. Provide either id or name. The proxy must be in the same
- project as the resource referencing it. When selecting by name, the name must
- match exactly one active proxy in the project. Ambiguous names return a 400; use
- id for stable references.
+ proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
record_session: Override the connection's default for recording this login's browser session.
When omitted, the connection's record_session default is used.
@@ -1116,6 +1122,7 @@ async def login(
path_template("/auth/connections/{id}/login", id=id),
body=await async_maybe_transform(
{
+ "browser": browser,
"browser_telemetry": browser_telemetry,
"proxy": proxy,
"record_session": record_session,
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 553a98dd..19f48988 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -97,6 +97,7 @@
from ...types.browser_create_response import BrowserCreateResponse
from ...types.browser_update_response import BrowserUpdateResponse
from ...types.browser_retrieve_response import BrowserRetrieveResponse
+from ...types.browser_proxy_config_param import BrowserProxyConfigParam
from ...types.shared_params.browser_profile import BrowserProfile
from ...types.shared_params.browser_viewport import BrowserViewport
from ...types.shared_params.browser_extension import BrowserExtension
@@ -172,6 +173,7 @@ def create(
kiosk_mode: bool | Omit = omit,
name: str | Omit = omit,
profile: BrowserProfile | Omit = omit,
+ proxy: BrowserProxyConfigParam | Omit = omit,
proxy_id: str | Omit = omit,
start_url: str | Omit = omit,
stealth: bool | Omit = omit,
@@ -216,15 +218,26 @@ def create(
specified, the matching profile will be loaded into the browser session.
Profiles must be created beforehand.
+ proxy: Proxy configuration for the browser session. Cannot be combined with proxy_id.
+ Omit to use the browser default: stealth browsers use Kernel's default stealth
+ proxy, while non-stealth browsers use direct egress. Set mode to direct to force
+ direct egress regardless of stealth. Set mode to default to explicitly use the
+ browser default: Kernel's default stealth proxy when stealth=true, or direct
+ egress when stealth=false. Select id or name to use that proxy regardless of
+ stealth. Proxy selection does not change stealth or CAPTCHA solver behavior.
+
proxy_id: Optional proxy to associate to the browser session. Must reference a proxy in
- the same project as the browser session.
+ the same project as the browser session. Deprecated in favor of proxy.
start_url: Optional URL to open when the browser session is created. Navigation is
best-effort, so navigation failures do not prevent the session from being
created.
- stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
- mechanisms.
+ stealth: If true, launches the browser in stealth mode and enables the CAPTCHA solver.
+ Defaults to false. When proxy is omitted, stealth browsers use Kernel's default
+ stealth proxy and non-stealth browsers use direct egress. An explicit proxy
+ configuration changes only egress; it does not enable or disable stealth or the
+ CAPTCHA solver.
tags: Optional user-defined key-value tags for the browser session, used to find and
group sessions later. Can be changed later via PATCH /browsers/{id_or_name}. Up
@@ -275,6 +288,7 @@ def create(
"kiosk_mode": kiosk_mode,
"name": name,
"profile": profile,
+ "proxy": proxy,
"proxy_id": proxy_id,
"start_url": start_url,
"stealth": stealth,
@@ -340,6 +354,7 @@ def update(
disable_default_proxy: bool | Omit = omit,
name: Optional[str] | Omit = omit,
profile: BrowserProfile | Omit = omit,
+ proxy: BrowserProxyConfigParam | Omit = omit,
proxy_id: Optional[str] | Omit = omit,
tags: Optional[TagsParam] | Omit = omit,
telemetry: Optional[browser_update_params.Telemetry] | Omit = omit,
@@ -356,7 +371,7 @@ def update(
Args:
disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth
- proxy.
+ proxy. Deprecated in favor of proxy.mode.
name: Human-readable name for the browser session. Omit to leave unchanged, set to an
empty string to clear the name. When set, must be unique among active sessions
@@ -365,8 +380,15 @@ def update(
profile: Profile to load into the browser session. Only allowed if the session does not
already have a profile loaded.
+ proxy: Proxy configuration to apply. Omit to leave the current configuration unchanged.
+ Cannot be combined with proxy_id or disable_default_proxy. Set mode to direct to
+ switch to direct egress regardless of stealth. Set mode to default to restore
+ the browser default after using a selected proxy: Kernel's default stealth proxy
+ for a stealth browser, or direct egress for a non-stealth browser. Updating
+ proxy does not change stealth or CAPTCHA solver behavior.
+
proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove
- proxy.
+ proxy. Deprecated in favor of proxy.
tags: User-defined key-value tags for the browser session. Omit to leave unchanged.
Provide a map to replace the entire tag set (full replace, not a merge). Set to
@@ -397,6 +419,7 @@ def update(
"disable_default_proxy": disable_default_proxy,
"name": name,
"profile": profile,
+ "proxy": proxy,
"proxy_id": proxy_id,
"tags": tags,
"telemetry": telemetry,
@@ -750,6 +773,7 @@ async def create(
kiosk_mode: bool | Omit = omit,
name: str | Omit = omit,
profile: BrowserProfile | Omit = omit,
+ proxy: BrowserProxyConfigParam | Omit = omit,
proxy_id: str | Omit = omit,
start_url: str | Omit = omit,
stealth: bool | Omit = omit,
@@ -794,15 +818,26 @@ async def create(
specified, the matching profile will be loaded into the browser session.
Profiles must be created beforehand.
+ proxy: Proxy configuration for the browser session. Cannot be combined with proxy_id.
+ Omit to use the browser default: stealth browsers use Kernel's default stealth
+ proxy, while non-stealth browsers use direct egress. Set mode to direct to force
+ direct egress regardless of stealth. Set mode to default to explicitly use the
+ browser default: Kernel's default stealth proxy when stealth=true, or direct
+ egress when stealth=false. Select id or name to use that proxy regardless of
+ stealth. Proxy selection does not change stealth or CAPTCHA solver behavior.
+
proxy_id: Optional proxy to associate to the browser session. Must reference a proxy in
- the same project as the browser session.
+ the same project as the browser session. Deprecated in favor of proxy.
start_url: Optional URL to open when the browser session is created. Navigation is
best-effort, so navigation failures do not prevent the session from being
created.
- stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
- mechanisms.
+ stealth: If true, launches the browser in stealth mode and enables the CAPTCHA solver.
+ Defaults to false. When proxy is omitted, stealth browsers use Kernel's default
+ stealth proxy and non-stealth browsers use direct egress. An explicit proxy
+ configuration changes only egress; it does not enable or disable stealth or the
+ CAPTCHA solver.
tags: Optional user-defined key-value tags for the browser session, used to find and
group sessions later. Can be changed later via PATCH /browsers/{id_or_name}. Up
@@ -853,6 +888,7 @@ async def create(
"kiosk_mode": kiosk_mode,
"name": name,
"profile": profile,
+ "proxy": proxy,
"proxy_id": proxy_id,
"start_url": start_url,
"stealth": stealth,
@@ -918,6 +954,7 @@ async def update(
disable_default_proxy: bool | Omit = omit,
name: Optional[str] | Omit = omit,
profile: BrowserProfile | Omit = omit,
+ proxy: BrowserProxyConfigParam | Omit = omit,
proxy_id: Optional[str] | Omit = omit,
tags: Optional[TagsParam] | Omit = omit,
telemetry: Optional[browser_update_params.Telemetry] | Omit = omit,
@@ -934,7 +971,7 @@ async def update(
Args:
disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth
- proxy.
+ proxy. Deprecated in favor of proxy.mode.
name: Human-readable name for the browser session. Omit to leave unchanged, set to an
empty string to clear the name. When set, must be unique among active sessions
@@ -943,8 +980,15 @@ async def update(
profile: Profile to load into the browser session. Only allowed if the session does not
already have a profile loaded.
+ proxy: Proxy configuration to apply. Omit to leave the current configuration unchanged.
+ Cannot be combined with proxy_id or disable_default_proxy. Set mode to direct to
+ switch to direct egress regardless of stealth. Set mode to default to restore
+ the browser default after using a selected proxy: Kernel's default stealth proxy
+ for a stealth browser, or direct egress for a non-stealth browser. Updating
+ proxy does not change stealth or CAPTCHA solver behavior.
+
proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove
- proxy.
+ proxy. Deprecated in favor of proxy.
tags: User-defined key-value tags for the browser session. Omit to leave unchanged.
Provide a map to replace the entire tag set (full replace, not a merge). Set to
@@ -975,6 +1019,7 @@ async def update(
"disable_default_proxy": disable_default_proxy,
"name": name,
"profile": profile,
+ "proxy": proxy,
"proxy_id": proxy_id,
"tags": tags,
"telemetry": telemetry,
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index ab58edbc..e86ac9c4 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -22,6 +22,7 @@
from .credential import Credential as Credential
from .tags_param import TagsParam as TagsParam
from .browser_pool import BrowserPool as BrowserPool
+from .browser_proxy import BrowserProxy as BrowserProxy
from .browser_usage import BrowserUsage as BrowserUsage
from .app_list_params import AppListParams as AppListParams
from .audit_log_entry import AuditLogEntry as AuditLogEntry
@@ -29,6 +30,7 @@
from .browser_pool_ref import BrowserPoolRef as BrowserPoolRef
from .app_list_response import AppListResponse as AppListResponse
from .proxy_list_params import ProxyListParams as ProxyListParams
+from .browser_proxy_mode import BrowserProxyMode as BrowserProxyMode
from .proxy_check_params import ProxyCheckParams as ProxyCheckParams
from .api_key_list_params import APIKeyListParams as APIKeyListParams
from .browser_curl_params import BrowserCurlParams as BrowserCurlParams
@@ -39,6 +41,7 @@
from .proxy_create_params import ProxyCreateParams as ProxyCreateParams
from .proxy_list_response import ProxyListResponse as ProxyListResponse
from .proxy_update_params import ProxyUpdateParams as ProxyUpdateParams
+from .browser_proxy_config import BrowserProxyConfig as BrowserProxyConfig
from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse
from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams
from .api_key_rotate_params import APIKeyRotateParams as APIKeyRotateParams
@@ -85,6 +88,7 @@
from .browser_pool_create_params import BrowserPoolCreateParams as BrowserPoolCreateParams
from .browser_pool_delete_params import BrowserPoolDeleteParams as BrowserPoolDeleteParams
from .browser_pool_update_params import BrowserPoolUpdateParams as BrowserPoolUpdateParams
+from .browser_proxy_config_param import BrowserProxyConfigParam as BrowserProxyConfigParam
from .deployment_create_response import DeploymentCreateResponse as DeploymentCreateResponse
from .deployment_follow_response import DeploymentFollowResponse as DeploymentFollowResponse
from .invocation_create_response import InvocationCreateResponse as InvocationCreateResponse
diff --git a/src/kernel/types/auth/__init__.py b/src/kernel/types/auth/__init__.py
index 2d0cf1b0..ced593b0 100644
--- a/src/kernel/types/auth/__init__.py
+++ b/src/kernel/types/auth/__init__.py
@@ -13,4 +13,6 @@
from .connection_update_params import ConnectionUpdateParams as ConnectionUpdateParams
from .connection_follow_response import ConnectionFollowResponse as ConnectionFollowResponse
from .connection_timeline_params import ConnectionTimelineParams as ConnectionTimelineParams
+from .managed_auth_browser_config import ManagedAuthBrowserConfig as ManagedAuthBrowserConfig
from .managed_auth_timeline_event import ManagedAuthTimelineEvent as ManagedAuthTimelineEvent
+from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam as ManagedAuthBrowserConfigParam
diff --git a/src/kernel/types/auth/connection_create_params.py b/src/kernel/types/auth/connection_create_params.py
index 407857be..23d53cb2 100644
--- a/src/kernel/types/auth/connection_create_params.py
+++ b/src/kernel/types/auth/connection_create_params.py
@@ -6,6 +6,7 @@
from typing_extensions import Required, TypedDict
from ..._types import SequenceNotStr
+from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam
from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
__all__ = [
@@ -62,11 +63,16 @@ class ConnectionCreateParams(TypedDict, total=False):
re-auth. Defaults to true.
"""
- browser_telemetry: Optional[BrowserTelemetry]
+ browser: ManagedAuthBrowserConfigParam
+ """
+ Default browser configuration for login, reauthentication, and health-check
+ sessions.
"""
- Browser telemetry configuration used by this connection's browser sessions by
- default. Uses the exact create-browser configuration. Can be overridden
- per-login.
+
+ browser_telemetry: Optional[BrowserTelemetry]
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
credential: Credential
@@ -98,13 +104,7 @@ class ConnectionCreateParams(TypedDict, total=False):
"""Optional login page URL to skip discovery"""
proxy: Proxy
- """Proxy selection.
-
- Provide either id or name. The proxy must be in the same project as the resource
- referencing it. When selecting by name, the name must match exactly one active
- proxy in the project. Ambiguous names return a 400; use id for stable
- references.
- """
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
record_session: bool
"""Whether to record browser sessions for this connection by default.
@@ -164,8 +164,9 @@ class BrowserTelemetryExport(TypedDict, total=False):
class BrowserTelemetry(TypedDict, total=False):
- """
- Browser telemetry configuration used by this connection's browser sessions by default. Uses the exact create-browser configuration. Can be overridden per-login.
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
browser: BrowserTelemetryCategoriesConfigParam
@@ -225,11 +226,7 @@ class Credential(TypedDict, total=False):
class Proxy(TypedDict, total=False):
- """Proxy selection.
-
- Provide either id or name. The proxy must be in the same project as the resource referencing it.
- When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references.
- """
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
id: str
"""Proxy ID"""
diff --git a/src/kernel/types/auth/connection_login_params.py b/src/kernel/types/auth/connection_login_params.py
index 117d3c7d..c26b020f 100644
--- a/src/kernel/types/auth/connection_login_params.py
+++ b/src/kernel/types/auth/connection_login_params.py
@@ -5,6 +5,7 @@
from typing import Optional
from typing_extensions import TypedDict
+from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam
from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
__all__ = [
@@ -18,23 +19,21 @@
class ConnectionLoginParams(TypedDict, total=False):
- browser_telemetry: Optional[BrowserTelemetry]
- """Override the connection's default browser telemetry configuration for this
- login.
+ browser: ManagedAuthBrowserConfigParam
+ """Browser configuration override for this login.
- When omitted, the connection's browser_telemetry default is used. Uses the exact
- create-browser configuration.
+ Omitted properties inherit the connection defaults.
"""
- proxy: Proxy
- """Proxy selection.
+ browser_telemetry: Optional[BrowserTelemetry]
+ """Deprecated.
- Provide either id or name. The proxy must be in the same project as the resource
- referencing it. When selecting by name, the name must match exactly one active
- proxy in the project. Ambiguous names return a 400; use id for stable
- references.
+ Use browser.telemetry. Retained during migration for existing clients.
"""
+ proxy: Proxy
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
+
record_session: bool
"""Override the connection's default for recording this login's browser session.
@@ -87,9 +86,9 @@ class BrowserTelemetryExport(TypedDict, total=False):
class BrowserTelemetry(TypedDict, total=False):
- """Override the connection's default browser telemetry configuration for this login.
+ """Deprecated.
- When omitted, the connection's browser_telemetry default is used. Uses the exact create-browser configuration.
+ Use browser.telemetry. Retained during migration for existing clients.
"""
browser: BrowserTelemetryCategoriesConfigParam
@@ -127,11 +126,7 @@ class BrowserTelemetry(TypedDict, total=False):
class Proxy(TypedDict, total=False):
- """Proxy selection.
-
- Provide either id or name. The proxy must be in the same project as the resource referencing it.
- When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references.
- """
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
id: str
"""Proxy ID"""
diff --git a/src/kernel/types/auth/connection_update_params.py b/src/kernel/types/auth/connection_update_params.py
index f92a40c7..ad3fbe44 100644
--- a/src/kernel/types/auth/connection_update_params.py
+++ b/src/kernel/types/auth/connection_update_params.py
@@ -6,6 +6,7 @@
from typing_extensions import TypedDict
from ..._types import SequenceNotStr
+from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam
from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
__all__ = [
@@ -35,11 +36,16 @@ class ConnectionUpdateParams(TypedDict, total=False):
re-auth.
"""
- browser_telemetry: Optional[BrowserTelemetry]
+ browser: ManagedAuthBrowserConfigParam
+ """
+ Browser configuration updates for future login, reauthentication, and
+ health-check sessions. Omitted properties remain unchanged.
"""
- Browser telemetry configuration used by future browser sessions for this
- connection. Uses the exact create-browser configuration. Set enabled to false to
- disable telemetry.
+
+ browser_telemetry: Optional[BrowserTelemetry]
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
credential: Credential
@@ -65,13 +71,7 @@ class ConnectionUpdateParams(TypedDict, total=False):
"""Login page URL. Set to empty string to clear."""
proxy: Proxy
- """Proxy selection.
-
- Provide either id or name. The proxy must be in the same project as the resource
- referencing it. When selecting by name, the name must match exactly one active
- proxy in the project. Ambiguous names return a 400; use id for stable
- references.
- """
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
record_session: bool
"""Whether to record browser sessions for this connection by default"""
@@ -125,8 +125,9 @@ class BrowserTelemetryExport(TypedDict, total=False):
class BrowserTelemetry(TypedDict, total=False):
- """
- Browser telemetry configuration used by future browser sessions for this connection. Uses the exact create-browser configuration. Set enabled to false to disable telemetry.
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
browser: BrowserTelemetryCategoriesConfigParam
@@ -186,11 +187,7 @@ class Credential(TypedDict, total=False):
class Proxy(TypedDict, total=False):
- """Proxy selection.
-
- Provide either id or name. The proxy must be in the same project as the resource referencing it.
- When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references.
- """
+ """Deprecated. Use browser.proxy. Retained during migration for existing clients."""
id: str
"""Proxy ID"""
diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py
index e3bf7b9c..f1bb2198 100644
--- a/src/kernel/types/auth/managed_auth.py
+++ b/src/kernel/types/auth/managed_auth.py
@@ -5,6 +5,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from .managed_auth_browser_config import ManagedAuthBrowserConfig
from ..browsers.browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig
__all__ = [
@@ -68,8 +69,9 @@ class BrowserTelemetryExport(BaseModel):
class BrowserTelemetry(BaseModel):
- """
- Browser telemetry configuration used by this connection's browser sessions by default. The exact create-browser configuration is preserved and can be overridden per-login.
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
browser: Optional[BrowserTelemetryCategoriesConfig] = None
@@ -324,6 +326,12 @@ class ManagedAuth(BaseModel):
re-auth.
"""
+ browser: Optional[ManagedAuthBrowserConfig] = None
+ """
+ Default browser configuration for login, reauthentication, and health-check
+ sessions.
+ """
+
browser_session_id: Optional[str] = None
"""
ID of the underlying browser session driving the current flow (present when flow
@@ -332,10 +340,9 @@ class ManagedAuth(BaseModel):
"""
browser_telemetry: Optional[BrowserTelemetry] = None
- """
- Browser telemetry configuration used by this connection's browser sessions by
- default. The exact create-browser configuration is preserved and can be
- overridden per-login.
+ """Deprecated.
+
+ Use browser.telemetry. Retained during migration for existing clients.
"""
can_reauth: Optional[bool] = None
@@ -513,7 +520,10 @@ class ManagedAuth(BaseModel):
"""URL where the browser landed after successful login"""
proxy_id: Optional[str] = None
- """ID of the proxy associated with this connection, if any."""
+ """Deprecated.
+
+ Read browser.proxy instead. Retained during migration for existing clients.
+ """
sign_in_options: Optional[List[SignInOption]] = None
"""
diff --git a/src/kernel/types/auth/managed_auth_browser_config.py b/src/kernel/types/auth/managed_auth_browser_config.py
new file mode 100644
index 00000000..a4d63de7
--- /dev/null
+++ b/src/kernel/types/auth/managed_auth_browser_config.py
@@ -0,0 +1,118 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+from ..browser_proxy_config import BrowserProxyConfig
+from ..browsers.browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig
+
+__all__ = [
+ "ManagedAuthBrowserConfig",
+ "Telemetry",
+ "TelemetryExport",
+ "TelemetryExportOtlp",
+ "TelemetryExportOtlpDestination",
+]
+
+
+class TelemetryExportOtlpDestination(BaseModel):
+ """OTLP destination to export this session's captured telemetry to.
+
+ Provide either id or name. Requires telemetry capture to be enabled.
+ """
+
+ id: Optional[str] = None
+ """OTLP destination ID"""
+
+ name: Optional[str] = None
+ """OTLP destination name"""
+
+
+class TelemetryExportOtlp(BaseModel):
+ """
+ Export captured telemetry over OTLP to one of the org's configured destinations.
+ """
+
+ destination: Optional[TelemetryExportOtlpDestination] = None
+ """OTLP destination to export this session's captured telemetry to.
+
+ Provide either id or name. Requires telemetry capture to be enabled.
+ """
+
+ enabled: Optional[bool] = None
+ """Whether to export captured telemetry over OTLP.
+
+ Setting destination implies enabled=true, so this only needs to be set
+ explicitly to disable export (enabled=false with a destination is rejected).
+ """
+
+
+class TelemetryExport(BaseModel):
+ """Where to export this session's captured telemetry.
+
+ Omit to capture without exporting.
+ """
+
+ otlp: Optional[TelemetryExportOtlp] = None
+ """
+ Export captured telemetry over OTLP to one of the org's configured destinations.
+ """
+
+
+class Telemetry(BaseModel):
+ """Browser telemetry configuration using the same semantics as browser create."""
+
+ browser: Optional[BrowserTelemetryCategoriesConfig] = None
+ """Per-category capture flags.
+
+ The operational categories (control, connection, system, captcha) are captured
+ whenever telemetry is enabled; set one to enabled=false to opt out. The CDP
+ categories (console, network, page, interaction) and screenshot are off by
+ default; set enabled=true to opt in. On create, provided categories layer onto
+ the default set. On update, provided categories merge onto the session's current
+ config; when no telemetry is active this falls back to the default set (matching
+ create). If browser is omitted or empty, the default set is used. A browser
+ config that disables every category stops capture on update and starts no
+ capture on create.
+ """
+
+ enabled: Optional[bool] = None
+ """Request shortcut for browser telemetry capture.
+
+ True enables capture; with no browser category settings it captures the default
+ set (control, connection, system, captcha), and any browser category settings
+ are layered onto that default set. On update, enabled=true resolves the config
+ fresh from the default set plus any provided categories, replacing the session's
+ current selection rather than merging onto it; omit enabled to merge categories
+ onto the current selection instead. False stops capture on update and starts no
+ capture on create. enabled=false cannot be combined with browser category
+ settings.
+ """
+
+ export: Optional[TelemetryExport] = None
+ """Where to export this session's captured telemetry.
+
+ Omit to capture without exporting.
+ """
+
+
+class ManagedAuthBrowserConfig(BaseModel):
+ """
+ Browser configuration applied to browser sessions created for a managed auth connection. Managed auth controls the profile, headless mode, timeout, start URL, kiosk mode, and viewport.
+ """
+
+ proxy: Optional[BrowserProxyConfig] = None
+ """Proxy configuration for managed auth browser sessions.
+
+ Omit on create to derive the default from stealth, or on update and login to
+ preserve or inherit the connection default.
+ """
+
+ stealth: Optional[bool] = None
+ """Whether managed auth browser sessions use stealth mode.
+
+ Defaults to true when omitted.
+ """
+
+ telemetry: Optional[Telemetry] = None
+ """Browser telemetry configuration using the same semantics as browser create."""
diff --git a/src/kernel/types/auth/managed_auth_browser_config_param.py b/src/kernel/types/auth/managed_auth_browser_config_param.py
new file mode 100644
index 00000000..7f8650e0
--- /dev/null
+++ b/src/kernel/types/auth/managed_auth_browser_config_param.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+from ..browser_proxy_config_param import BrowserProxyConfigParam
+from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
+
+__all__ = [
+ "ManagedAuthBrowserConfigParam",
+ "Telemetry",
+ "TelemetryExport",
+ "TelemetryExportOtlp",
+ "TelemetryExportOtlpDestination",
+]
+
+
+class TelemetryExportOtlpDestination(TypedDict, total=False):
+ """OTLP destination to export this session's captured telemetry to.
+
+ Provide either id or name. Requires telemetry capture to be enabled.
+ """
+
+ id: str
+ """OTLP destination ID"""
+
+ name: str
+ """OTLP destination name"""
+
+
+class TelemetryExportOtlp(TypedDict, total=False):
+ """
+ Export captured telemetry over OTLP to one of the org's configured destinations.
+ """
+
+ destination: TelemetryExportOtlpDestination
+ """OTLP destination to export this session's captured telemetry to.
+
+ Provide either id or name. Requires telemetry capture to be enabled.
+ """
+
+ enabled: bool
+ """Whether to export captured telemetry over OTLP.
+
+ Setting destination implies enabled=true, so this only needs to be set
+ explicitly to disable export (enabled=false with a destination is rejected).
+ """
+
+
+class TelemetryExport(TypedDict, total=False):
+ """Where to export this session's captured telemetry.
+
+ Omit to capture without exporting.
+ """
+
+ otlp: TelemetryExportOtlp
+ """
+ Export captured telemetry over OTLP to one of the org's configured destinations.
+ """
+
+
+class Telemetry(TypedDict, total=False):
+ """Browser telemetry configuration using the same semantics as browser create."""
+
+ browser: BrowserTelemetryCategoriesConfigParam
+ """Per-category capture flags.
+
+ The operational categories (control, connection, system, captcha) are captured
+ whenever telemetry is enabled; set one to enabled=false to opt out. The CDP
+ categories (console, network, page, interaction) and screenshot are off by
+ default; set enabled=true to opt in. On create, provided categories layer onto
+ the default set. On update, provided categories merge onto the session's current
+ config; when no telemetry is active this falls back to the default set (matching
+ create). If browser is omitted or empty, the default set is used. A browser
+ config that disables every category stops capture on update and starts no
+ capture on create.
+ """
+
+ enabled: bool
+ """Request shortcut for browser telemetry capture.
+
+ True enables capture; with no browser category settings it captures the default
+ set (control, connection, system, captcha), and any browser category settings
+ are layered onto that default set. On update, enabled=true resolves the config
+ fresh from the default set plus any provided categories, replacing the session's
+ current selection rather than merging onto it; omit enabled to merge categories
+ onto the current selection instead. False stops capture on update and starts no
+ capture on create. enabled=false cannot be combined with browser category
+ settings.
+ """
+
+ export: TelemetryExport
+ """Where to export this session's captured telemetry.
+
+ Omit to capture without exporting.
+ """
+
+
+class ManagedAuthBrowserConfigParam(TypedDict, total=False):
+ """
+ Browser configuration applied to browser sessions created for a managed auth connection. Managed auth controls the profile, headless mode, timeout, start URL, kiosk mode, and viewport.
+ """
+
+ proxy: BrowserProxyConfigParam
+ """Proxy configuration for managed auth browser sessions.
+
+ Omit on create to derive the default from stealth, or on update and login to
+ preserve or inherit the connection default.
+ """
+
+ stealth: bool
+ """Whether managed auth browser sessions use stealth mode.
+
+ Defaults to true when omitted.
+ """
+
+ telemetry: Optional[Telemetry]
+ """Browser telemetry configuration using the same semantics as browser create."""
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index f09a29cf..262383c5 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -6,6 +6,7 @@
from typing_extensions import TypedDict
from .tags_param import TagsParam
+from .browser_proxy_config_param import BrowserProxyConfigParam
from .shared_params.browser_profile import BrowserProfile
from .shared_params.browser_viewport import BrowserViewport
from .shared_params.browser_extension import BrowserExtension
@@ -70,10 +71,23 @@ class BrowserCreateParams(TypedDict, total=False):
into the browser session. Profiles must be created beforehand.
"""
+ proxy: BrowserProxyConfigParam
+ """Proxy configuration for the browser session.
+
+ Cannot be combined with proxy_id. Omit to use the browser default: stealth
+ browsers use Kernel's default stealth proxy, while non-stealth browsers use
+ direct egress. Set mode to direct to force direct egress regardless of stealth.
+ Set mode to default to explicitly use the browser default: Kernel's default
+ stealth proxy when stealth=true, or direct egress when stealth=false. Select id
+ or name to use that proxy regardless of stealth. Proxy selection does not change
+ stealth or CAPTCHA solver behavior.
+ """
+
proxy_id: str
"""Optional proxy to associate to the browser session.
- Must reference a proxy in the same project as the browser session.
+ Must reference a proxy in the same project as the browser session. Deprecated in
+ favor of proxy.
"""
start_url: str
@@ -84,9 +98,12 @@ class BrowserCreateParams(TypedDict, total=False):
"""
stealth: bool
- """
- If true, launches the browser in stealth mode to reduce detection by anti-bot
- mechanisms.
+ """If true, launches the browser in stealth mode and enables the CAPTCHA solver.
+
+ Defaults to false. When proxy is omitted, stealth browsers use Kernel's default
+ stealth proxy and non-stealth browsers use direct egress. An explicit proxy
+ configuration changes only egress; it does not enable or disable stealth or the
+ CAPTCHA solver.
"""
tags: TagsParam
diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py
index 0dbf867c..8618e7e5 100644
--- a/src/kernel/types/browser_create_response.py
+++ b/src/kernel/types/browser_create_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class BrowserCreateResponse(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py
index f3215ed5..8f4917a8 100644
--- a/src/kernel/types/browser_list_response.py
+++ b/src/kernel/types/browser_list_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class BrowserListResponse(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py
index 1dabc492..7d849958 100644
--- a/src/kernel/types/browser_pool_acquire_response.py
+++ b/src/kernel/types/browser_pool_acquire_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class BrowserPoolAcquireResponse(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/src/kernel/types/browser_proxy.py b/src/kernel/types/browser_proxy.py
new file mode 100644
index 00000000..20078429
--- /dev/null
+++ b/src/kernel/types/browser_proxy.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+from .browser_proxy_mode import BrowserProxyMode
+
+__all__ = ["BrowserProxy"]
+
+
+class BrowserProxy(BaseModel):
+ """Resolved proxy configuration for a browser session.
+
+ Selected proxies are returned by stable ID.
+ """
+
+ id: Optional[str] = None
+ """Selected proxy ID."""
+
+ mode: Optional[BrowserProxyMode] = None
+ """Proxy egress mode.
+
+ direct forces no proxy regardless of stealth. default uses the browser's
+ stealth-derived default: Kernel's default stealth proxy when stealth=true, or
+ direct egress when stealth=false. default is primarily useful on browser update
+ to restore the browser default after selected-proxy egress.
+ """
+
+ name: Optional[str] = None
+ """Selected proxy name."""
diff --git a/src/kernel/types/browser_proxy_config.py b/src/kernel/types/browser_proxy_config.py
new file mode 100644
index 00000000..6db5d5ec
--- /dev/null
+++ b/src/kernel/types/browser_proxy_config.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+from .browser_proxy_mode import BrowserProxyMode
+
+__all__ = ["BrowserProxyConfig"]
+
+
+class BrowserProxyConfig(BaseModel):
+ """Browser proxy configuration.
+
+ Provide exactly one of mode, id, or name; an empty object is invalid.
+ Set mode to direct for no proxy regardless of stealth. Set mode to default to use the browser's stealth-derived default: Kernel's default stealth proxy when stealth=true, or direct egress when stealth=false.
+ Select id or name to use that proxy regardless of stealth. The selected proxy must be in the same project as the browser. Names must match exactly one active proxy; use id for stable references.
+ Proxy configuration changes only egress and does not change stealth or CAPTCHA solver behavior. A stealth browser using mode=direct still runs in stealth mode with the CAPTCHA solver enabled.
+ When proxy is omitted on browser creation, stealth browsers use Kernel's default stealth proxy and non-stealth browsers use direct egress. When omitted on update, the current configuration is unchanged.
+ """
+
+ id: Optional[str] = None
+ """Proxy ID."""
+
+ mode: Optional[BrowserProxyMode] = None
+ """Proxy egress mode.
+
+ direct forces no proxy regardless of stealth. default uses the browser's
+ stealth-derived default: Kernel's default stealth proxy when stealth=true, or
+ direct egress when stealth=false. default is primarily useful on browser update
+ to restore the browser default after selected-proxy egress.
+ """
+
+ name: Optional[str] = None
+ """Proxy name. Must match exactly one active proxy in the project."""
diff --git a/src/kernel/types/browser_proxy_config_param.py b/src/kernel/types/browser_proxy_config_param.py
new file mode 100644
index 00000000..0fc83157
--- /dev/null
+++ b/src/kernel/types/browser_proxy_config_param.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+from .browser_proxy_mode import BrowserProxyMode
+
+__all__ = ["BrowserProxyConfigParam"]
+
+
+class BrowserProxyConfigParam(TypedDict, total=False):
+ """Browser proxy configuration.
+
+ Provide exactly one of mode, id, or name; an empty object is invalid.
+ Set mode to direct for no proxy regardless of stealth. Set mode to default to use the browser's stealth-derived default: Kernel's default stealth proxy when stealth=true, or direct egress when stealth=false.
+ Select id or name to use that proxy regardless of stealth. The selected proxy must be in the same project as the browser. Names must match exactly one active proxy; use id for stable references.
+ Proxy configuration changes only egress and does not change stealth or CAPTCHA solver behavior. A stealth browser using mode=direct still runs in stealth mode with the CAPTCHA solver enabled.
+ When proxy is omitted on browser creation, stealth browsers use Kernel's default stealth proxy and non-stealth browsers use direct egress. When omitted on update, the current configuration is unchanged.
+ """
+
+ id: str
+ """Proxy ID."""
+
+ mode: BrowserProxyMode
+ """Proxy egress mode.
+
+ direct forces no proxy regardless of stealth. default uses the browser's
+ stealth-derived default: Kernel's default stealth proxy when stealth=true, or
+ direct egress when stealth=false. default is primarily useful on browser update
+ to restore the browser default after selected-proxy egress.
+ """
+
+ name: str
+ """Proxy name. Must match exactly one active proxy in the project."""
diff --git a/src/kernel/types/browser_proxy_mode.py b/src/kernel/types/browser_proxy_mode.py
new file mode 100644
index 00000000..c8b51c1b
--- /dev/null
+++ b/src/kernel/types/browser_proxy_mode.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BrowserProxyMode"]
+
+BrowserProxyMode: TypeAlias = Literal["direct", "default"]
diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py
index 5563ab98..3afc6082 100644
--- a/src/kernel/types/browser_retrieve_response.py
+++ b/src/kernel/types/browser_retrieve_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class BrowserRetrieveResponse(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/src/kernel/types/browser_update_params.py b/src/kernel/types/browser_update_params.py
index 01b19c8a..a9ea675c 100644
--- a/src/kernel/types/browser_update_params.py
+++ b/src/kernel/types/browser_update_params.py
@@ -6,6 +6,7 @@
from typing_extensions import TypedDict
from .tags_param import TagsParam
+from .browser_proxy_config_param import BrowserProxyConfigParam
from .shared_params.browser_profile import BrowserProfile
from .shared_params.browser_viewport import BrowserViewport
from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
@@ -24,7 +25,7 @@ class BrowserUpdateParams(TypedDict, total=False):
disable_default_proxy: bool
"""
If true, stealth browsers connect directly instead of using the default stealth
- proxy.
+ proxy. Deprecated in favor of proxy.mode.
"""
name: Optional[str]
@@ -40,10 +41,22 @@ class BrowserUpdateParams(TypedDict, total=False):
Only allowed if the session does not already have a profile loaded.
"""
+ proxy: BrowserProxyConfigParam
+ """Proxy configuration to apply.
+
+ Omit to leave the current configuration unchanged. Cannot be combined with
+ proxy_id or disable_default_proxy. Set mode to direct to switch to direct egress
+ regardless of stealth. Set mode to default to restore the browser default after
+ using a selected proxy: Kernel's default stealth proxy for a stealth browser, or
+ direct egress for a non-stealth browser. Updating proxy does not change stealth
+ or CAPTCHA solver behavior.
+ """
+
proxy_id: Optional[str]
"""ID of the proxy to use.
- Omit to leave unchanged, set to empty string to remove proxy.
+ Omit to leave unchanged, set to empty string to remove proxy. Deprecated in
+ favor of proxy.
"""
tags: Optional[TagsParam]
diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py
index e4602bd5..d60fe900 100644
--- a/src/kernel/types/browser_update_response.py
+++ b/src/kernel/types/browser_update_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class BrowserUpdateResponse(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py
index 9abb0fa6..5cfe7e89 100644
--- a/src/kernel/types/invocation_list_browsers_response.py
+++ b/src/kernel/types/invocation_list_browsers_response.py
@@ -6,6 +6,7 @@
from .tags import Tags
from .profile import Profile
from .._models import BaseModel
+from .browser_proxy import BrowserProxy
from .browser_usage import BrowserUsage
from .browser_pool_ref import BrowserPoolRef
from .shared.browser_viewport import BrowserViewport
@@ -79,8 +80,14 @@ class Browser(BaseModel):
when the session ends. Omitted when no profile is attached.
"""
+ proxy: Optional[BrowserProxy] = None
+ """Resolved proxy configuration for this browser session."""
+
proxy_id: Optional[str] = None
- """ID of the proxy associated with this browser session, if any."""
+ """ID of the proxy associated with this browser session, if any.
+
+ Deprecated in favor of proxy.
+ """
start_url: Optional[str] = None
"""URL the session was asked to navigate to on creation, if any.
diff --git a/tests/api_resources/auth/test_connections.py b/tests/api_resources/auth/test_connections.py
index 321e84e4..f026a2cd 100644
--- a/tests/api_resources/auth/test_connections.py
+++ b/tests/api_resources/auth/test_connections.py
@@ -40,6 +40,37 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
profile_name="user-123",
allowed_domains=["login.netflix.com", "auth.netflix.com"],
auto_reauth=True,
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
@@ -166,6 +197,37 @@ def test_method_update_with_all_params(self, client: Kernel) -> None:
id="id",
allowed_domains=["login.netflix.com", "auth.netflix.com"],
auto_reauth=True,
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
@@ -377,6 +439,37 @@ def test_method_login(self, client: Kernel) -> None:
def test_method_login_with_all_params(self, client: Kernel) -> None:
connection = client.auth.connections.login(
id="id",
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
@@ -581,6 +674,37 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
profile_name="user-123",
allowed_domains=["login.netflix.com", "auth.netflix.com"],
auto_reauth=True,
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
@@ -707,6 +831,37 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) ->
id="id",
allowed_domains=["login.netflix.com", "auth.netflix.com"],
auto_reauth=True,
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
@@ -918,6 +1073,37 @@ async def test_method_login(self, async_client: AsyncKernel) -> None:
async def test_method_login_with_all_params(self, async_client: AsyncKernel) -> None:
connection = await async_client.auth.connections.login(
id="id",
+ browser={
+ "proxy": {
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
+ "stealth": False,
+ "telemetry": {
+ "browser": {
+ "captcha": {"enabled": True},
+ "connection": {"enabled": True},
+ "console": {"enabled": True},
+ "control": {"enabled": True},
+ "interaction": {"enabled": True},
+ "network": {"enabled": True},
+ "page": {"enabled": True},
+ "screenshot": {"enabled": True},
+ "system": {"enabled": True},
+ },
+ "enabled": True,
+ "export": {
+ "otlp": {
+ "destination": {
+ "id": "id",
+ "name": "name",
+ },
+ "enabled": True,
+ }
+ },
+ },
+ },
browser_telemetry={
"browser": {
"captcha": {"enabled": True},
diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py
index a8f93efc..8b0a6182 100644
--- a/tests/api_resources/test_browsers.py
+++ b/tests/api_resources/test_browsers.py
@@ -51,6 +51,11 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
"name": "name",
"save_changes": True,
},
+ proxy={
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
proxy_id="proxy_id",
start_url="https://example.com",
stealth=True,
@@ -183,6 +188,11 @@ def test_method_update_with_all_params(self, client: Kernel) -> None:
"name": "name",
"save_changes": True,
},
+ proxy={
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
proxy_id="proxy_id",
tags={
"team": "backend",
@@ -496,6 +506,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
"name": "name",
"save_changes": True,
},
+ proxy={
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
proxy_id="proxy_id",
start_url="https://example.com",
stealth=True,
@@ -628,6 +643,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) ->
"name": "name",
"save_changes": True,
},
+ proxy={
+ "id": "x",
+ "mode": "direct",
+ "name": "x",
+ },
proxy_id="proxy_id",
tags={
"team": "backend",
From bacb44f61f32e82367c264bab9120595f93f5cbf Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Sun, 9 Aug 2026 16:32:50 +0000
Subject: [PATCH 3/4] feat: Persist CUA-TS reauth blockers before failure
Stainless-Generated-From: 207ec8076e1bcc7c24257506b0d58a18837505bb
---
src/kernel/types/auth/managed_auth.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py
index f1bb2198..3625cbbc 100644
--- a/src/kernel/types/auth/managed_auth.py
+++ b/src/kernel/types/auth/managed_auth.py
@@ -369,6 +369,7 @@ class ManagedAuth(BaseModel):
"requires_totp_without_secret",
"requires_sms_code",
"requires_email_code",
+ "requires_customer_input",
]
] = None
"""
@@ -401,6 +402,8 @@ class ManagedAuth(BaseModel):
automatically
- `requires_email_code` — flow needs an email code that cannot be received
automatically
+ - `requires_customer_input` — flow needs another field or choice that is
+ unavailable during unattended re-authentication
"""
choices: Optional[List[Choice]] = None
From 2afe161bedb272fe458ae2f0adcd3df140cb151d Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Mon, 10 Aug 2026 16:33:45 +0000
Subject: [PATCH 4/4] feat: Managed auth: free-plan connections (cap 3),
credentials, 6h floor (packaging PR 5)
Stainless-Generated-From: 27294af8c209bd0f75875350803cd3ec10f081a9
---
src/kernel/resources/auth/connections.py | 14 ++++++++------
src/kernel/types/auth/connection_create_params.py | 5 +++--
src/kernel/types/auth/managed_auth.py | 5 +++--
3 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/kernel/resources/auth/connections.py b/src/kernel/resources/auth/connections.py
index a84d42af..56785ea2 100644
--- a/src/kernel/resources/auth/connections.py
+++ b/src/kernel/resources/auth/connections.py
@@ -137,9 +137,10 @@ def create(
health_check_interval: Interval in seconds between automatic health checks. When set, the system
periodically verifies the authentication status and triggers re-authentication
- if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour). The minimum
- depends on your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes),
- Hobbyist: 3600 (1 hour).
+ if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour) or your plan
+ minimum, whichever is larger. The minimum depends on your plan: Enterprise: 300
+ (5 minutes), Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour), Free: 21600 (6
+ hours).
health_checks: Whether to enable periodic health checks. When false, the system will not
automatically verify authentication status, and `auto_reauth` has no effect on
@@ -749,9 +750,10 @@ async def create(
health_check_interval: Interval in seconds between automatic health checks. When set, the system
periodically verifies the authentication status and triggers re-authentication
- if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour). The minimum
- depends on your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes),
- Hobbyist: 3600 (1 hour).
+ if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour) or your plan
+ minimum, whichever is larger. The minimum depends on your plan: Enterprise: 300
+ (5 minutes), Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour), Free: 21600 (6
+ hours).
health_checks: Whether to enable periodic health checks. When false, the system will not
automatically verify authentication status, and `auto_reauth` has no effect on
diff --git a/src/kernel/types/auth/connection_create_params.py b/src/kernel/types/auth/connection_create_params.py
index 23d53cb2..2fdc808b 100644
--- a/src/kernel/types/auth/connection_create_params.py
+++ b/src/kernel/types/auth/connection_create_params.py
@@ -88,8 +88,9 @@ class ConnectionCreateParams(TypedDict, total=False):
When set, the system periodically verifies the authentication status and
triggers re-authentication if needed. Maximum is 86400 (24 hours). Default is
- 3600 (1 hour). The minimum depends on your plan: Enterprise: 300 (5 minutes),
- Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour).
+ 3600 (1 hour) or your plan minimum, whichever is larger. The minimum depends on
+ your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), Hobbyist:
+ 3600 (1 hour), Free: 21600 (6 hours).
"""
health_checks: bool
diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py
index 3625cbbc..9db58514 100644
--- a/src/kernel/types/auth/managed_auth.py
+++ b/src/kernel/types/auth/managed_auth.py
@@ -469,8 +469,9 @@ class ManagedAuth(BaseModel):
When set, the system periodically verifies the authentication status and
triggers re-authentication if needed. Maximum is 86400 (24 hours). Default is
- 3600 (1 hour). The minimum depends on your plan: Enterprise: 300 (5 minutes),
- Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour).
+ 3600 (1 hour) or your plan minimum, whichever is larger. The minimum depends on
+ your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), Hobbyist:
+ 3600 (1 hour), Free: 21600 (6 hours).
"""
health_checks: Optional[bool] = None