From 31b7bda620f53bc6026b5eb3fac34d50859fa1df Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:52:25 +0000 Subject: [PATCH 1/5] feat: Add video_memory to request the 4GiB VRAM GPU browser tier Stainless-Generated-From: f581fece2ccb216a10620562f96439429da85527 --- api.md | 1 + src/kernel/resources/browsers/browsers.py | 14 ++++++++++++++ src/kernel/types/__init__.py | 1 + src/kernel/types/browser_create_params.py | 8 ++++++++ src/kernel/types/browser_create_response.py | 4 ++++ src/kernel/types/browser_list_response.py | 4 ++++ src/kernel/types/browser_memory.py | 2 +- src/kernel/types/browser_pool_acquire_response.py | 4 ++++ src/kernel/types/browser_retrieve_response.py | 4 ++++ src/kernel/types/browser_update_response.py | 4 ++++ src/kernel/types/browser_video_memory.py | 7 +++++++ .../types/invocation_list_browsers_response.py | 4 ++++ tests/api_resources/test_browsers.py | 2 ++ 13 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/kernel/types/browser_video_memory.py diff --git a/api.md b/api.md index 113f03a0..1b71010c 100644 --- a/api.md +++ b/api.md @@ -131,6 +131,7 @@ from kernel.types import ( BrowserReplResult, BrowserReplTextContent, BrowserUsage, + BrowserVideoMemory, Profile, Tags, VaultReference, diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py index 38089c20..ec5f0f42 100644 --- a/src/kernel/resources/browsers/browsers.py +++ b/src/kernel/resources/browsers/browsers.py @@ -25,6 +25,7 @@ AsyncFsResourceWithStreamingResponse, ) from ...types import ( + BrowserVideoMemory, BrowserMemoryRequest, browser_curl_params, browser_list_params, @@ -97,6 +98,7 @@ AsyncPlaywrightResourceWithStreamingResponse, ) from ...types.browser_repl_result import BrowserReplResult +from ...types.browser_video_memory import BrowserVideoMemory from ...types.browser_curl_response import BrowserCurlResponse from ...types.browser_list_response import BrowserListResponse from ...types.vault_reference_param import VaultReferenceParam @@ -206,6 +208,7 @@ def create( telemetry: Optional[browser_create_params.Telemetry] | Omit = omit, timeout_seconds: int | Omit = omit, vaults: Iterable[VaultReferenceParam] | Omit = omit, + video_memory: BrowserVideoMemory | Omit = omit, viewport: BrowserViewport | 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. @@ -293,6 +296,10 @@ def create( vaults: Project-scoped vaults to link to the browser session. Links are immutable after creation. + video_memory: Video memory (VRAM) for a GPU browser session. Requires gpu=true. Defaults to + 2GiB, which comes with 4 vCPU and 6GiB memory. 4GiB comes with 8 vCPU and 12GiB + memory. + viewport: Initial browser window size in pixels with optional refresh rate. If omitted, image defaults apply (1920x1080@25). For GPU images, the default is 1920x1080@60. Arbitrary viewport dimensions and refresh rates are accepted. @@ -337,6 +344,7 @@ def create( "telemetry": telemetry, "timeout_seconds": timeout_seconds, "vaults": vaults, + "video_memory": video_memory, "viewport": viewport, }, browser_create_params.BrowserCreateParams, @@ -915,6 +923,7 @@ async def create( telemetry: Optional[browser_create_params.Telemetry] | Omit = omit, timeout_seconds: int | Omit = omit, vaults: Iterable[VaultReferenceParam] | Omit = omit, + video_memory: BrowserVideoMemory | Omit = omit, viewport: BrowserViewport | 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. @@ -1002,6 +1011,10 @@ async def create( vaults: Project-scoped vaults to link to the browser session. Links are immutable after creation. + video_memory: Video memory (VRAM) for a GPU browser session. Requires gpu=true. Defaults to + 2GiB, which comes with 4 vCPU and 6GiB memory. 4GiB comes with 8 vCPU and 12GiB + memory. + viewport: Initial browser window size in pixels with optional refresh rate. If omitted, image defaults apply (1920x1080@25). For GPU images, the default is 1920x1080@60. Arbitrary viewport dimensions and refresh rates are accepted. @@ -1046,6 +1059,7 @@ async def create( "telemetry": telemetry, "timeout_seconds": timeout_seconds, "vaults": vaults, + "video_memory": video_memory, "viewport": viewport, }, browser_create_params.BrowserCreateParams, diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index 8d605df8..a3eac5b8 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -65,6 +65,7 @@ from .vault_upsert_params import VaultUpsertParams as VaultUpsertParams from .browser_proxy_config import BrowserProxyConfig as BrowserProxyConfig from .browser_repl_content import BrowserReplContent as BrowserReplContent +from .browser_video_memory import BrowserVideoMemory as BrowserVideoMemory from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse from .search_create_params import SearchCreateParams as SearchCreateParams from .vault_encryption_key import VaultEncryptionKey as VaultEncryptionKey diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py index 2e456a80..3fe435d5 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 Literal, TypedDict from .tags_param import TagsParam +from .browser_video_memory import BrowserVideoMemory from .vault_reference_param import VaultReferenceParam from .browser_memory_request import BrowserMemoryRequest from .browser_proxy_config_param import BrowserProxyConfigParam @@ -158,6 +159,13 @@ class BrowserCreateParams(TypedDict, total=False): Links are immutable after creation. """ + video_memory: BrowserVideoMemory + """Video memory (VRAM) for a GPU browser session. + + Requires gpu=true. Defaults to 2GiB, which comes with 4 vCPU and 6GiB memory. + 4GiB comes with 8 vCPU and 12GiB memory. + """ + viewport: BrowserViewport """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py index 09c88f9a..25e82277 100644 --- a/src/kernel/types/browser_create_response.py +++ b/src/kernel/types/browser_create_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class BrowserCreateResponse(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py index 6f1aad11..6527b9c2 100644 --- a/src/kernel/types/browser_list_response.py +++ b/src/kernel/types/browser_list_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class BrowserListResponse(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_memory.py b/src/kernel/types/browser_memory.py index b86a12aa..31f362ee 100644 --- a/src/kernel/types/browser_memory.py +++ b/src/kernel/types/browser_memory.py @@ -4,4 +4,4 @@ __all__ = ["BrowserMemory"] -BrowserMemory: TypeAlias = Literal["1GiB", "2GiB", "6GiB", "8GiB", "16GiB"] +BrowserMemory: TypeAlias = Literal["1GiB", "2GiB", "6GiB", "8GiB", "12GiB", "16GiB"] diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py index a0617cb6..4cbda2d3 100644 --- a/src/kernel/types/browser_pool_acquire_response.py +++ b/src/kernel/types/browser_pool_acquire_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class BrowserPoolAcquireResponse(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py index 1642e55e..735c9ba4 100644 --- a/src/kernel/types/browser_retrieve_response.py +++ b/src/kernel/types/browser_retrieve_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class BrowserRetrieveResponse(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py index 98484751..b53d2ac3 100644 --- a/src/kernel/types/browser_update_response.py +++ b/src/kernel/types/browser_update_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class BrowserUpdateResponse(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/src/kernel/types/browser_video_memory.py b/src/kernel/types/browser_video_memory.py new file mode 100644 index 00000000..28c3569e --- /dev/null +++ b/src/kernel/types/browser_video_memory.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__ = ["BrowserVideoMemory"] + +BrowserVideoMemory: TypeAlias = Literal["2GiB", "4GiB"] diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py index 6589ad92..dc99f0dc 100644 --- a/src/kernel/types/invocation_list_browsers_response.py +++ b/src/kernel/types/invocation_list_browsers_response.py @@ -12,6 +12,7 @@ from .browser_memory import BrowserMemory from .vault_reference import VaultReference from .browser_pool_ref import BrowserPoolRef +from .browser_video_memory import BrowserVideoMemory from .browser_network_config import BrowserNetworkConfig from .shared.browser_viewport import BrowserViewport from .browsers.browser_telemetry_config import BrowserTelemetryConfig @@ -135,6 +136,9 @@ class Browser(BaseModel): vaults: Optional[List[VaultReference]] = None """Vaults linked when the browser session was created.""" + video_memory: Optional[BrowserVideoMemory] = None + """Video memory (VRAM) of the GPU browser session. Only present when gpu is true.""" + viewport: Optional[BrowserViewport] = None """ Initial browser window size in pixels with optional refresh rate. If omitted, diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py index 6aff8010..d12c67b2 100644 --- a/tests/api_resources/test_browsers.py +++ b/tests/api_resources/test_browsers.py @@ -114,6 +114,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: "name": "x", } ], + video_memory="2GiB", viewport={ "height": 800, "width": 1280, @@ -660,6 +661,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> "name": "x", } ], + video_memory="2GiB", viewport={ "height": 800, "width": 1280, From d387aab4847329eb4405318a20ee93a372399f70 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:16:11 +0000 Subject: [PATCH 2/5] feat: Fix 1Password fill classification for repeat fills and unconfirmed submits Stainless-Generated-From: 399a7000e4351b576352ac5a8772b7dc28a15682 --- .../credential_vault_item_request_param.py | 3 ++- ...password_fill_vault_item_operation_result.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/kernel/types/vaults/credential_vault_item_request_param.py b/src/kernel/types/vaults/credential_vault_item_request_param.py index d316f7bd..4034d9c5 100644 --- a/src/kernel/types/vaults/credential_vault_item_request_param.py +++ b/src/kernel/types/vaults/credential_vault_item_request_param.py @@ -15,7 +15,8 @@ class CredentialVaultItemRequestParam(TypedDict, total=False): If they choose 1Password, connect their account and request access to a login in their own non-shared vault; passkeys are not supported. If they decline or that path fails, collect a Kernel-hosted credential item instead. Never - automatically retry an uncertain 1Password request or fill. + automatically retry an uncertain 1Password request. After an uncertain fill, + inspect the page to see the result of the fill. Do not use credential items for credit card data. Use wallet and card item types instead. Kernel credentials declare fields and may enter pending_collection. 1Password credentials either reference a connected credential_account or diff --git a/src/kernel/types/vaults/one_password_fill_vault_item_operation_result.py b/src/kernel/types/vaults/one_password_fill_vault_item_operation_result.py index b1586f5b..9fa64aeb 100644 --- a/src/kernel/types/vaults/one_password_fill_vault_item_operation_result.py +++ b/src/kernel/types/vaults/one_password_fill_vault_item_operation_result.py @@ -18,10 +18,10 @@ class OnePasswordFillVaultItemOperationResult(BaseModel): """Kernel's outcome of the extension call. fill_submitted means the extension reported submission, not website - authentication. fill_failed means the extension returned a known failure and may - include error_code. fill_unknown means submission may have happened without a - conclusive response; it has no error_code and must not be retried in the same - browser. + authentication. fill_failed means the extension returned a known failure and + includes error_code. fill_unknown means the form may have been filled or + submitted without a conclusive result; inspect the page to see the result of the + fill. """ type: Literal["1pw_fill"] @@ -29,8 +29,9 @@ class OnePasswordFillVaultItemOperationResult(BaseModel): error_code: Optional[Literal["fillFailed", "autosubmitFailed", "noExistingCredentials", "authenticationFailed"]] = ( None ) - """Present only for a conclusive fill_failed response. - - These are allowlisted 1Password extension codes, never raw errors, secrets, or - page content. + """ + Present for fill_failed, and for fill_unknown when the extension reported + autosubmitFailed (it filled and submitted the form but could not confirm the + fill finished). These are allowlisted 1Password extension codes, never raw + errors, secrets, or page content. """ From 541fb3fc033b8b9542838a116684578cab18fba0 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:14:19 +0000 Subject: [PATCH 3/5] feat: Support egress allowlists on browser pools Stainless-Generated-From: 5799c8ad59001401cd999d6117d0a5733f3686a8 --- src/kernel/resources/browser_pools.py | 52 ++++++++++++------- src/kernel/types/browser_network_config.py | 6 ++- .../types/browser_network_config_param.py | 6 ++- .../types/browser_network_update_param.py | 6 ++- .../types/browser_pool_create_params.py | 4 +- .../types/browser_pool_update_params.py | 20 ++++--- 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/kernel/resources/browser_pools.py b/src/kernel/resources/browser_pools.py index a4257b83..5fc7147a 100644 --- a/src/kernel/resources/browser_pools.py +++ b/src/kernel/resources/browser_pools.py @@ -123,9 +123,9 @@ def create( name: Optional name for the browser pool. Must be unique within the project. - network: Network configuration applied to browsers in this pool. Proxy routes require - proxy-v3; the pool will not provision browsers through Envoy if proxy-v3 is - unavailable. + network: Network configuration applied to browsers in this pool. Egress allowlists and + proxy routes require proxy-v3; the pool will not provision browsers through + Envoy if proxy-v3 is unavailable. profile: Profile configuration for browsers in a pool. Provide either id or name. Profiles must be created beforehand. Unlike single browser sessions, pools load @@ -296,9 +296,11 @@ def update( discard_all_idle: Whether to discard all idle browsers and rebuild them immediately with the new configuration. Defaults to false. Only browsers that are idle when the update runs are rebuilt. A browser that is in use during the update keeps its original - configuration, and if it is later released with `reuse: true` it returns to the - pool with that stale configuration until it is discarded (by this flag on a - later update, or by flushing the pool). + configuration, except that its allowlist is reset to the current pool baseline + on release or the browser is replaced. For other configuration fields, if it is + later released with `reuse: true` it returns to the pool with that stale + configuration until it is discarded (by this flag on a later update, or by + flushing the pool). extensions: If provided, replaces the extension list. Empty array clears all previously-selected extensions. Omit this field to leave extensions unchanged. @@ -321,10 +323,14 @@ def update( network: If provided, replaces the pool's network configuration. Omit to leave the existing configuration unchanged; an empty object ({}) removes it, while network: {private_hosts: []} or network: {proxy_routes: []} sets an explicit - empty list. Proxy routes require proxy-v3; the pool will not provision browsers - through Envoy if proxy-v3 is unavailable. Only applied to browsers created in - the pool after the update; browsers already in the pool keep their configuration - until discarded (see discard_all_idle). + empty list; allowed_hosts must be non-empty when supplied. Egress allowlists and + proxy routes require proxy-v3; the pool will not provision browsers through + Envoy if proxy-v3 is unavailable. Changing allowed_hosts automatically replaces + idle browsers. Leased browsers retain their allowlist until release, when it is + reset to the current pool baseline or the browser is replaced if it cannot + safely receive that baseline. Stale browsers cannot be acquired. Other network + fields only apply to browsers created after the update; existing browsers keep + those fields until discarded (see discard_all_idle). profile: Profile configuration for browsers in a pool. Provide either id or name. Profiles must be created beforehand. Unlike single browser sessions, pools load @@ -774,9 +780,9 @@ async def create( name: Optional name for the browser pool. Must be unique within the project. - network: Network configuration applied to browsers in this pool. Proxy routes require - proxy-v3; the pool will not provision browsers through Envoy if proxy-v3 is - unavailable. + network: Network configuration applied to browsers in this pool. Egress allowlists and + proxy routes require proxy-v3; the pool will not provision browsers through + Envoy if proxy-v3 is unavailable. profile: Profile configuration for browsers in a pool. Provide either id or name. Profiles must be created beforehand. Unlike single browser sessions, pools load @@ -947,9 +953,11 @@ async def update( discard_all_idle: Whether to discard all idle browsers and rebuild them immediately with the new configuration. Defaults to false. Only browsers that are idle when the update runs are rebuilt. A browser that is in use during the update keeps its original - configuration, and if it is later released with `reuse: true` it returns to the - pool with that stale configuration until it is discarded (by this flag on a - later update, or by flushing the pool). + configuration, except that its allowlist is reset to the current pool baseline + on release or the browser is replaced. For other configuration fields, if it is + later released with `reuse: true` it returns to the pool with that stale + configuration until it is discarded (by this flag on a later update, or by + flushing the pool). extensions: If provided, replaces the extension list. Empty array clears all previously-selected extensions. Omit this field to leave extensions unchanged. @@ -972,10 +980,14 @@ async def update( network: If provided, replaces the pool's network configuration. Omit to leave the existing configuration unchanged; an empty object ({}) removes it, while network: {private_hosts: []} or network: {proxy_routes: []} sets an explicit - empty list. Proxy routes require proxy-v3; the pool will not provision browsers - through Envoy if proxy-v3 is unavailable. Only applied to browsers created in - the pool after the update; browsers already in the pool keep their configuration - until discarded (see discard_all_idle). + empty list; allowed_hosts must be non-empty when supplied. Egress allowlists and + proxy routes require proxy-v3; the pool will not provision browsers through + Envoy if proxy-v3 is unavailable. Changing allowed_hosts automatically replaces + idle browsers. Leased browsers retain their allowlist until release, when it is + reset to the current pool baseline or the browser is replaced if it cannot + safely receive that baseline. Stale browsers cannot be acquired. Other network + fields only apply to browsers created after the update; existing browsers keep + those fields until discarded (see discard_all_idle). profile: Profile configuration for browsers in a pool. Provide either id or name. Profiles must be created beforehand. Unlike single browser sessions, pools load diff --git a/src/kernel/types/browser_network_config.py b/src/kernel/types/browser_network_config.py index 19de4d72..cd205e65 100644 --- a/src/kernel/types/browser_network_config.py +++ b/src/kernel/types/browser_network_config.py @@ -52,8 +52,10 @@ class BrowserNetworkConfig(BaseModel): processes in the browser VM that do not use the browser's proxy, are not filtered, and Kernel's own control traffic is always allowed. Can be replaced or removed while the session runs with PATCH /browsers/{id_or_name}, but not added - to a browser created without one. Requires proxy v3. Not supported on browser - pools. + to a browser created without one. Supported on browser pools and their leased + browsers. Per-lease changes are reset to the pool's allowlist on release. If a + lessee removes the allowlist and the pool still requires one, the browser is + replaced on release rather than given an allowlist again. Requires proxy v3. """ private_hosts: Optional[List[str]] = None diff --git a/src/kernel/types/browser_network_config_param.py b/src/kernel/types/browser_network_config_param.py index 94877452..723fd6ee 100644 --- a/src/kernel/types/browser_network_config_param.py +++ b/src/kernel/types/browser_network_config_param.py @@ -55,8 +55,10 @@ class BrowserNetworkConfigParam(TypedDict, total=False): processes in the browser VM that do not use the browser's proxy, are not filtered, and Kernel's own control traffic is always allowed. Can be replaced or removed while the session runs with PATCH /browsers/{id_or_name}, but not added - to a browser created without one. Requires proxy v3. Not supported on browser - pools. + to a browser created without one. Supported on browser pools and their leased + browsers. Per-lease changes are reset to the pool's allowlist on release. If a + lessee removes the allowlist and the pool still requires one, the browser is + replaced on release rather than given an allowlist again. Requires proxy v3. """ private_hosts: SequenceNotStr[str] diff --git a/src/kernel/types/browser_network_update_param.py b/src/kernel/types/browser_network_update_param.py index 62163e13..31158d46 100644 --- a/src/kernel/types/browser_network_update_param.py +++ b/src/kernel/types/browser_network_update_param.py @@ -29,6 +29,8 @@ class BrowserNetworkUpdateParam(TypedDict, total=False): minutes during a Kernel deploy. Connections to destinations it still allows, such as WebSockets, stay open. A start_url in the same request must be allowed by the updated list, and is loaded only after the list takes effect. Requires a - browser created with proxy v3, and not supported on pooled browsers. If the - request fails, retry it: the new list may already apply to some requests. + browser created with proxy v3. Supported on leased pooled browsers; the pool's + allowlist is restored before reuse, or the browser is destroyed if it cannot be + safely restored. If the request fails, retry it: the new list may already apply + to some requests. """ diff --git a/src/kernel/types/browser_pool_create_params.py b/src/kernel/types/browser_pool_create_params.py index f078c393..6850502a 100644 --- a/src/kernel/types/browser_pool_create_params.py +++ b/src/kernel/types/browser_pool_create_params.py @@ -70,8 +70,8 @@ class BrowserPoolCreateParams(TypedDict, total=False): network: BrowserNetworkConfigParam """Network configuration applied to browsers in this pool. - Proxy routes require proxy-v3; the pool will not provision browsers through - Envoy if proxy-v3 is unavailable. + Egress allowlists and proxy routes require proxy-v3; the pool will not provision + browsers through Envoy if proxy-v3 is unavailable. """ profile: Profile diff --git a/src/kernel/types/browser_pool_update_params.py b/src/kernel/types/browser_pool_update_params.py index 2731b6ea..8374568e 100644 --- a/src/kernel/types/browser_pool_update_params.py +++ b/src/kernel/types/browser_pool_update_params.py @@ -37,9 +37,11 @@ class BrowserPoolUpdateParams(TypedDict, total=False): Whether to discard all idle browsers and rebuild them immediately with the new configuration. Defaults to false. Only browsers that are idle when the update runs are rebuilt. A browser that is in use during the update keeps its original - configuration, and if it is later released with `reuse: true` it returns to the - pool with that stale configuration until it is discarded (by this flag on a - later update, or by flushing the pool). + configuration, except that its allowlist is reset to the current pool baseline + on release or the browser is replaced. For other configuration fields, if it is + later released with `reuse: true` it returns to the pool with that stale + configuration until it is discarded (by this flag on a later update, or by + flushing the pool). """ extensions: Iterable[BrowserExtension] @@ -81,10 +83,14 @@ class BrowserPoolUpdateParams(TypedDict, total=False): Omit to leave the existing configuration unchanged; an empty object ({}) removes it, while network: {private_hosts: []} or network: {proxy_routes: []} sets an - explicit empty list. Proxy routes require proxy-v3; the pool will not provision - browsers through Envoy if proxy-v3 is unavailable. Only applied to browsers - created in the pool after the update; browsers already in the pool keep their - configuration until discarded (see discard_all_idle). + explicit empty list; allowed_hosts must be non-empty when supplied. Egress + allowlists and proxy routes require proxy-v3; the pool will not provision + browsers through Envoy if proxy-v3 is unavailable. Changing allowed_hosts + automatically replaces idle browsers. Leased browsers retain their allowlist + until release, when it is reset to the current pool baseline or the browser is + replaced if it cannot safely receive that baseline. Stale browsers cannot be + acquired. Other network fields only apply to browsers created after the update; + existing browsers keep those fields until discarded (see discard_all_idle). """ profile: Profile From f7daa043513d08f6cc6aefec7b058d5bd57f2590 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:06:25 +0000 Subject: [PATCH 4/5] feat: Filter the telemetry SSE stream by event type Stainless-Generated-From: d9acb7a1d1280429f6a722334df6386f763b66e1 --- src/kernel/resources/browsers/telemetry.py | 44 +++++++++++++++++-- .../types/browsers/telemetry_stream_params.py | 12 +++++ .../api_resources/browsers/test_telemetry.py | 2 + 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/kernel/resources/browsers/telemetry.py b/src/kernel/resources/browsers/telemetry.py index 45cc3516..c3e889b3 100644 --- a/src/kernel/resources/browsers/telemetry.py +++ b/src/kernel/resources/browsers/telemetry.py @@ -157,6 +157,7 @@ def stream( id_or_name: str, *, replay: str | Omit = omit, + type: SequenceNotStr[str] | Omit = omit, last_event_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. @@ -171,7 +172,10 @@ def stream( stream closes when the browser session terminates. Each event frame includes an id: field containing a monotonically increasing sequence number; pass it as Last-Event-ID on reconnect to resume without gaps. The event: field is never - set; all frames carry JSON in the data: field. A keepalive comment frame is sent + set; event frames carry JSON in the data: field. A frame with an id: field and + no data: field moves Last-Event-ID past events that were not delivered, because + a type filter excluded them or they could not be decoded or exceeded the size + limit; SSE clients dispatch no event for it. A keepalive comment frame is sent every 15 seconds when no events arrive. Returns 404 if the browser session does not exist. If telemetry was not enabled on the session, the stream opens but no events are delivered. Fresh connections only see new events; pass replay=all to @@ -182,6 +186,14 @@ def stream( any other value is treated as from-now. The buffer is bounded, so the first event id may be greater than 1 if older events were evicted. + type: Deliver only these event types, such as captcha_solve_started or + captcha_challenge_result. Repeat the parameter or pass comma-separated values + for multiple types. Keepalive frames are always delivered. Filtered-out events + are not sent. An id-only frame carrying the latest skipped id is sent about + every 15 seconds while skipped events keep arriving, and otherwise with the next + keepalive or when the stream ends, so the connection stays active and + Last-Event-ID moves past them; a reconnect resumes after the skipped events. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -201,7 +213,13 @@ def stream( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"replay": replay}, telemetry_stream_params.TelemetryStreamParams), + query=maybe_transform( + { + "replay": replay, + "type": type, + }, + telemetry_stream_params.TelemetryStreamParams, + ), ), cast_to=TelemetryStreamResponse, stream=True, @@ -339,6 +357,7 @@ async def stream( id_or_name: str, *, replay: str | Omit = omit, + type: SequenceNotStr[str] | Omit = omit, last_event_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. @@ -353,7 +372,10 @@ async def stream( stream closes when the browser session terminates. Each event frame includes an id: field containing a monotonically increasing sequence number; pass it as Last-Event-ID on reconnect to resume without gaps. The event: field is never - set; all frames carry JSON in the data: field. A keepalive comment frame is sent + set; event frames carry JSON in the data: field. A frame with an id: field and + no data: field moves Last-Event-ID past events that were not delivered, because + a type filter excluded them or they could not be decoded or exceeded the size + limit; SSE clients dispatch no event for it. A keepalive comment frame is sent every 15 seconds when no events arrive. Returns 404 if the browser session does not exist. If telemetry was not enabled on the session, the stream opens but no events are delivered. Fresh connections only see new events; pass replay=all to @@ -364,6 +386,14 @@ async def stream( any other value is treated as from-now. The buffer is bounded, so the first event id may be greater than 1 if older events were evicted. + type: Deliver only these event types, such as captcha_solve_started or + captcha_challenge_result. Repeat the parameter or pass comma-separated values + for multiple types. Keepalive frames are always delivered. Filtered-out events + are not sent. An id-only frame carrying the latest skipped id is sent about + every 15 seconds while skipped events keep arriving, and otherwise with the next + keepalive or when the stream ends, so the connection stays active and + Last-Event-ID moves past them; a reconnect resumes after the skipped events. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -383,7 +413,13 @@ async def stream( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"replay": replay}, telemetry_stream_params.TelemetryStreamParams), + query=await async_maybe_transform( + { + "replay": replay, + "type": type, + }, + telemetry_stream_params.TelemetryStreamParams, + ), ), cast_to=TelemetryStreamResponse, stream=True, diff --git a/src/kernel/types/browsers/telemetry_stream_params.py b/src/kernel/types/browsers/telemetry_stream_params.py index d9703bc7..3e99dd5e 100644 --- a/src/kernel/types/browsers/telemetry_stream_params.py +++ b/src/kernel/types/browsers/telemetry_stream_params.py @@ -4,6 +4,7 @@ from typing_extensions import Annotated, TypedDict +from ..._types import SequenceNotStr from ..._utils import PropertyInfo __all__ = ["TelemetryStreamParams"] @@ -17,4 +18,15 @@ class TelemetryStreamParams(TypedDict, total=False): event id may be greater than 1 if older events were evicted. """ + type: SequenceNotStr[str] + """ + Deliver only these event types, such as captcha_solve_started or + captcha_challenge_result. Repeat the parameter or pass comma-separated values + for multiple types. Keepalive frames are always delivered. Filtered-out events + are not sent. An id-only frame carrying the latest skipped id is sent about + every 15 seconds while skipped events keep arriving, and otherwise with the next + keepalive or when the stream ends, so the connection stays active and + Last-Event-ID moves past them; a reconnect resumes after the skipped events. + """ + last_event_id: Annotated[str, PropertyInfo(alias="Last-Event-ID")] diff --git a/tests/api_resources/browsers/test_telemetry.py b/tests/api_resources/browsers/test_telemetry.py index 6bf65cc9..62742f45 100644 --- a/tests/api_resources/browsers/test_telemetry.py +++ b/tests/api_resources/browsers/test_telemetry.py @@ -89,6 +89,7 @@ def test_method_stream_with_all_params(self, client: Kernel) -> None: telemetry_stream = client.browsers.telemetry.stream( id_or_name="htzv5orfit78e1m2biiifpbv", replay="replay", + type=["string"], last_event_id="Last-Event-ID", ) telemetry_stream.response.close() @@ -203,6 +204,7 @@ async def test_method_stream_with_all_params(self, async_client: AsyncKernel) -> telemetry_stream = await async_client.browsers.telemetry.stream( id_or_name="htzv5orfit78e1m2biiifpbv", replay="replay", + type=["string"], last_event_id="Last-Event-ID", ) await telemetry_stream.response.aclose() From 14c0301dec47634fa3239b33053a066d6c772359 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:11:36 +0000 Subject: [PATCH 5/5] release: 0.123.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ pyproject.toml | 2 +- src/kernel/_version.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bd115b20..58ebf7f2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.122.0" + ".": "0.123.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f7520574..7fdca44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.123.0](https://github.com/kernel/kernel-python-sdk/compare/v0.122.0...v0.123.0) (2026-10-09) + + +### Features + +* Add video_memory to request the 4GiB VRAM GPU browser tier ([31b7bda](https://github.com/kernel/kernel-python-sdk/commit/31b7bda620f53bc6026b5eb3fac34d50859fa1df)) +* Filter the telemetry SSE stream by event type ([f7daa04](https://github.com/kernel/kernel-python-sdk/commit/f7daa043513d08f6cc6aefec7b058d5bd57f2590)) +* Fix 1Password fill classification for repeat fills and unconfirmed submits ([d387aab](https://github.com/kernel/kernel-python-sdk/commit/d387aab4847329eb4405318a20ee93a372399f70)) +* Support egress allowlists on browser pools ([541fb3f](https://github.com/kernel/kernel-python-sdk/commit/541fb3fc033b8b9542838a116684578cab18fba0)) + ## [0.122.0](https://github.com/kernel/kernel-python-sdk/compare/v0.121.0...v0.122.0) (2026-10-09) diff --git a/pyproject.toml b/pyproject.toml index 1f59c57a..6e2be1a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.122.0" +version = "0.123.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 72628b36..f0320775 100644 --- a/src/kernel/_version.py +++ b/src/kernel/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "kernel" -__version__ = "0.122.0" # x-release-please-version +__version__ = "0.123.0" # x-release-please-version