diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 87d3d84c..2afb750c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.56.0" + ".": "0.57.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index e0a033ee..3dfe6c63 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 112 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-982efd191c23737c9e1cdbcbf9237fa2231b9f74e0a25db2870293bdf9951c21.yml -openapi_spec_hash: eeb27952a4cc939316c24fc0ce2c9e3a -config_hash: 08d55086449943a8fec212b870061a3f +configured_endpoints: 113 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-a9c7e806132001dbfbd4e8ae6c7d0935e503f457d63385fc800c862e3d064375.yml +openapi_spec_hash: b048dcb0c5401bc0a301c3d30cb8ecba +config_hash: 37661d89120558d34b6cc184292632f2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a3eba14..661d0529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.57.0 (2026-05-21) + +Full Changelog: [v0.56.0...v0.57.0](https://github.com/kernel/kernel-python-sdk/compare/v0.56.0...v0.57.0) + +### Features + +* [kernel-1116] browser events api integration ([bfd4043](https://github.com/kernel/kernel-python-sdk/commit/bfd404368adf8a4305aabe2fad448e7eeba40322)) +* browsers: accept chrome_policy on POST /browsers (KERNEL-1216) ([b1807a2](https://github.com/kernel/kernel-python-sdk/commit/b1807a2d690f206e7482f5862acde36e692d07aa)) + ## 0.56.0 (2026-05-18) Full Changelog: [v0.55.0...v0.56.0](https://github.com/kernel/kernel-python-sdk/compare/v0.55.0...v0.56.0) diff --git a/api.md b/api.md index 3dea16a3..d0b1d720 100644 --- a/api.md +++ b/api.md @@ -103,6 +103,50 @@ Methods: - client.browsers.delete_by_id(id) -> None - client.browsers.load_extensions(id, \*\*params) -> None +## Telemetry + +Types: + +```python +from kernel.types.browsers import ( + BrowserCallStack, + BrowserConsoleErrorEvent, + BrowserConsoleLogEvent, + BrowserEventContext, + BrowserEventSource, + BrowserHTTPHeaders, + BrowserInteractionClickEvent, + BrowserInteractionKeyEvent, + BrowserInteractionScrollSettledEvent, + BrowserMonitorDisconnectedEvent, + BrowserMonitorInitFailedEvent, + BrowserMonitorReconnectFailedEvent, + BrowserMonitorReconnectedEvent, + BrowserMonitorScreenshotEvent, + BrowserNetworkIdleEvent, + BrowserNetworkLoadingFailedEvent, + BrowserNetworkRequestEvent, + BrowserNetworkResponseEvent, + BrowserPageDomContentLoadedEvent, + BrowserPageLayoutSettledEvent, + BrowserPageLayoutShiftEvent, + BrowserPageLcpEvent, + BrowserPageLoadEvent, + BrowserPageNavigationEvent, + BrowserPageNavigationSettledEvent, + BrowserPageTabOpenedEvent, + BrowserTelemetryCategoriesConfig, + BrowserTelemetryCategoryConfig, + BrowserTelemetryConfig, + BrowserTelemetryEvent, + TelemetryStreamResponse, +) +``` + +Methods: + +- client.browsers.telemetry.stream(id) -> TelemetryStreamResponse + ## Replays Types: diff --git a/pyproject.toml b/pyproject.toml index c98902d7..6956b281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.56.0" +version = "0.57.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 a4d2a572..fb252c57 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.56.0" # x-release-please-version +__version__ = "0.57.0" # x-release-please-version diff --git a/src/kernel/resources/browser_pools.py b/src/kernel/resources/browser_pools.py index 682b2936..db8ebbeb 100644 --- a/src/kernel/resources/browser_pools.py +++ b/src/kernel/resources/browser_pools.py @@ -110,9 +110,11 @@ def create( proxy_id: Optional proxy to associate to the browser session. Must reference a proxy belonging to the caller's org. - start_url: Optional URL to open when a browser is created for the pool. Navigation is - best-effort, so navigation failures do not prevent the pool from filling. Reused - browsers keep the page left by the previous lease. + start_url: Optional URL to navigate to when a new browser is warmed into the pool. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. @@ -259,9 +261,11 @@ def update( proxy_id: Optional proxy to associate to the browser session. Must reference a proxy belonging to the caller's org. - start_url: Optional URL to open when a browser is created for the pool. Navigation is - best-effort, so navigation failures do not prevent the pool from filling. Reused - browsers keep the page left by the previous lease. + start_url: Optional URL to navigate to when a new browser is warmed into the pool. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. @@ -584,9 +588,11 @@ async def create( proxy_id: Optional proxy to associate to the browser session. Must reference a proxy belonging to the caller's org. - start_url: Optional URL to open when a browser is created for the pool. Navigation is - best-effort, so navigation failures do not prevent the pool from filling. Reused - browsers keep the page left by the previous lease. + start_url: Optional URL to navigate to when a new browser is warmed into the pool. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. @@ -733,9 +739,11 @@ async def update( proxy_id: Optional proxy to associate to the browser session. Must reference a proxy belonging to the caller's org. - start_url: Optional URL to open when a browser is created for the pool. Navigation is - best-effort, so navigation failures do not prevent the pool from filling. Reused - browsers keep the page left by the previous lease. + start_url: Optional URL to navigate to when a new browser is warmed into the pool. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. diff --git a/src/kernel/resources/browsers/__init__.py b/src/kernel/resources/browsers/__init__.py index a1acee20..928d4553 100644 --- a/src/kernel/resources/browsers/__init__.py +++ b/src/kernel/resources/browsers/__init__.py @@ -48,6 +48,14 @@ ComputerResourceWithStreamingResponse, AsyncComputerResourceWithStreamingResponse, ) +from .telemetry import ( + TelemetryResource, + AsyncTelemetryResource, + TelemetryResourceWithRawResponse, + AsyncTelemetryResourceWithRawResponse, + TelemetryResourceWithStreamingResponse, + AsyncTelemetryResourceWithStreamingResponse, +) from .playwright import ( PlaywrightResource, AsyncPlaywrightResource, @@ -58,6 +66,12 @@ ) __all__ = [ + "TelemetryResource", + "AsyncTelemetryResource", + "TelemetryResourceWithRawResponse", + "AsyncTelemetryResourceWithRawResponse", + "TelemetryResourceWithStreamingResponse", + "AsyncTelemetryResourceWithStreamingResponse", "ReplaysResource", "AsyncReplaysResource", "ReplaysResourceWithRawResponse", diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py index 82ee8b85..f7136a1a 100644 --- a/src/kernel/resources/browsers/browsers.py +++ b/src/kernel/resources/browsers/browsers.py @@ -62,6 +62,14 @@ AsyncComputerResourceWithStreamingResponse, ) from ..._compat import cached_property +from .telemetry import ( + TelemetryResource, + AsyncTelemetryResource, + TelemetryResourceWithRawResponse, + AsyncTelemetryResourceWithRawResponse, + TelemetryResourceWithStreamingResponse, + AsyncTelemetryResourceWithStreamingResponse, +) from .playwright import ( PlaywrightResource, AsyncPlaywrightResource, @@ -94,6 +102,7 @@ from ...types.shared_params.browser_profile import BrowserProfile from ...types.shared_params.browser_viewport import BrowserViewport from ...types.shared_params.browser_extension import BrowserExtension +from ...types.browsers.browser_telemetry_config_param import BrowserTelemetryConfigParam __all__ = ["BrowsersResource", "AsyncBrowsersResource"] @@ -101,6 +110,11 @@ class BrowsersResource(SyncAPIResource): """Create and manage browser sessions.""" + @cached_property + def telemetry(self) -> TelemetryResource: + """Stream live telemetry events from a browser session.""" + return TelemetryResource(self._client) + @cached_property def replays(self) -> ReplaysResource: """Record and manage browser session video replays.""" @@ -152,6 +166,7 @@ def with_streaming_response(self) -> BrowsersResourceWithStreamingResponse: def create( self, *, + chrome_policy: Dict[str, object] | Omit = omit, extensions: Iterable[BrowserExtension] | Omit = omit, gpu: bool | Omit = omit, headless: bool | Omit = omit, @@ -162,6 +177,7 @@ def create( proxy_id: str | Omit = omit, start_url: str | Omit = omit, stealth: bool | Omit = omit, + telemetry: Optional[BrowserTelemetryConfigParam] | Omit = omit, timeout_seconds: int | 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. @@ -175,6 +191,12 @@ def create( Create a new browser session from within an action. Args: + chrome_policy: Custom Chrome enterprise policy overrides applied to this browser session. Keys + are Chrome enterprise policy names; values must match their expected types. + Blocked: kernel-managed policies (extensions, proxy, CDP/automation). Ignored + when reusing an existing persistent session. See + https://chromeenterprise.google/policies/ + extensions: List of browser extensions to load into the session. Provide each by id or name. gpu: If true, enables GPU acceleration for the browser session. Requires Start-Up or @@ -204,6 +226,10 @@ def create( stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. + telemetry: Telemetry configuration for the browser session. If provided, telemetry capture + starts with the specified category filter when the session is created. If + omitted, no telemetry capture is started. + timeout_seconds: The number of seconds of inactivity before the browser session is terminated. Activity includes CDP connections and live view connections. Defaults to 60 seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We @@ -235,6 +261,7 @@ def create( "/browsers", body=maybe_transform( { + "chrome_policy": chrome_policy, "extensions": extensions, "gpu": gpu, "headless": headless, @@ -245,6 +272,7 @@ def create( "proxy_id": proxy_id, "start_url": start_url, "stealth": stealth, + "telemetry": telemetry, "timeout_seconds": timeout_seconds, "viewport": viewport, }, @@ -305,6 +333,7 @@ def update( disable_default_proxy: bool | Omit = omit, profile: BrowserProfile | Omit = omit, proxy_id: Optional[str] | Omit = omit, + telemetry: Optional[BrowserTelemetryConfigParam] | Omit = omit, viewport: browser_update_params.Viewport | 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. @@ -326,6 +355,11 @@ def update( proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove proxy. + telemetry: Telemetry configuration. Omit, set to null, or set to an empty object ({}) to + leave the existing configuration unchanged (no-op). To enable capture for all + categories using VM defaults, set browser to an empty object ({"browser": {}}). + To stop capture, set every category's enabled to false. + viewport: Viewport configuration to apply to the browser session. extra_headers: Send extra headers @@ -345,6 +379,7 @@ def update( "disable_default_proxy": disable_default_proxy, "profile": profile, "proxy_id": proxy_id, + "telemetry": telemetry, "viewport": viewport, }, browser_update_params.BrowserUpdateParams, @@ -665,6 +700,11 @@ def load_extensions( class AsyncBrowsersResource(AsyncAPIResource): """Create and manage browser sessions.""" + @cached_property + def telemetry(self) -> AsyncTelemetryResource: + """Stream live telemetry events from a browser session.""" + return AsyncTelemetryResource(self._client) + @cached_property def replays(self) -> AsyncReplaysResource: """Record and manage browser session video replays.""" @@ -716,6 +756,7 @@ def with_streaming_response(self) -> AsyncBrowsersResourceWithStreamingResponse: async def create( self, *, + chrome_policy: Dict[str, object] | Omit = omit, extensions: Iterable[BrowserExtension] | Omit = omit, gpu: bool | Omit = omit, headless: bool | Omit = omit, @@ -726,6 +767,7 @@ async def create( proxy_id: str | Omit = omit, start_url: str | Omit = omit, stealth: bool | Omit = omit, + telemetry: Optional[BrowserTelemetryConfigParam] | Omit = omit, timeout_seconds: int | 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. @@ -739,6 +781,12 @@ async def create( Create a new browser session from within an action. Args: + chrome_policy: Custom Chrome enterprise policy overrides applied to this browser session. Keys + are Chrome enterprise policy names; values must match their expected types. + Blocked: kernel-managed policies (extensions, proxy, CDP/automation). Ignored + when reusing an existing persistent session. See + https://chromeenterprise.google/policies/ + extensions: List of browser extensions to load into the session. Provide each by id or name. gpu: If true, enables GPU acceleration for the browser session. Requires Start-Up or @@ -768,6 +816,10 @@ async def create( stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot mechanisms. + telemetry: Telemetry configuration for the browser session. If provided, telemetry capture + starts with the specified category filter when the session is created. If + omitted, no telemetry capture is started. + timeout_seconds: The number of seconds of inactivity before the browser session is terminated. Activity includes CDP connections and live view connections. Defaults to 60 seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We @@ -799,6 +851,7 @@ async def create( "/browsers", body=await async_maybe_transform( { + "chrome_policy": chrome_policy, "extensions": extensions, "gpu": gpu, "headless": headless, @@ -809,6 +862,7 @@ async def create( "proxy_id": proxy_id, "start_url": start_url, "stealth": stealth, + "telemetry": telemetry, "timeout_seconds": timeout_seconds, "viewport": viewport, }, @@ -869,6 +923,7 @@ async def update( disable_default_proxy: bool | Omit = omit, profile: BrowserProfile | Omit = omit, proxy_id: Optional[str] | Omit = omit, + telemetry: Optional[BrowserTelemetryConfigParam] | Omit = omit, viewport: browser_update_params.Viewport | 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. @@ -890,6 +945,11 @@ async def update( proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove proxy. + telemetry: Telemetry configuration. Omit, set to null, or set to an empty object ({}) to + leave the existing configuration unchanged (no-op). To enable capture for all + categories using VM defaults, set browser to an empty object ({"browser": {}}). + To stop capture, set every category's enabled to false. + viewport: Viewport configuration to apply to the browser session. extra_headers: Send extra headers @@ -909,6 +969,7 @@ async def update( "disable_default_proxy": disable_default_proxy, "profile": profile, "proxy_id": proxy_id, + "telemetry": telemetry, "viewport": viewport, }, browser_update_params.BrowserUpdateParams, @@ -1259,6 +1320,11 @@ def __init__(self, browsers: BrowsersResource) -> None: browsers.load_extensions, ) + @cached_property + def telemetry(self) -> TelemetryResourceWithRawResponse: + """Stream live telemetry events from a browser session.""" + return TelemetryResourceWithRawResponse(self._browsers.telemetry) + @cached_property def replays(self) -> ReplaysResourceWithRawResponse: """Record and manage browser session video replays.""" @@ -1320,6 +1386,11 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None: browsers.load_extensions, ) + @cached_property + def telemetry(self) -> AsyncTelemetryResourceWithRawResponse: + """Stream live telemetry events from a browser session.""" + return AsyncTelemetryResourceWithRawResponse(self._browsers.telemetry) + @cached_property def replays(self) -> AsyncReplaysResourceWithRawResponse: """Record and manage browser session video replays.""" @@ -1381,6 +1452,11 @@ def __init__(self, browsers: BrowsersResource) -> None: browsers.load_extensions, ) + @cached_property + def telemetry(self) -> TelemetryResourceWithStreamingResponse: + """Stream live telemetry events from a browser session.""" + return TelemetryResourceWithStreamingResponse(self._browsers.telemetry) + @cached_property def replays(self) -> ReplaysResourceWithStreamingResponse: """Record and manage browser session video replays.""" @@ -1442,6 +1518,11 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None: browsers.load_extensions, ) + @cached_property + def telemetry(self) -> AsyncTelemetryResourceWithStreamingResponse: + """Stream live telemetry events from a browser session.""" + return AsyncTelemetryResourceWithStreamingResponse(self._browsers.telemetry) + @cached_property def replays(self) -> AsyncReplaysResourceWithStreamingResponse: """Record and manage browser session video replays.""" diff --git a/src/kernel/resources/browsers/telemetry.py b/src/kernel/resources/browsers/telemetry.py new file mode 100644 index 00000000..cb95b2d1 --- /dev/null +++ b/src/kernel/resources/browsers/telemetry.py @@ -0,0 +1,195 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, strip_not_given +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 ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.browsers.telemetry_stream_response import TelemetryStreamResponse + +__all__ = ["TelemetryResource", "AsyncTelemetryResource"] + + +class TelemetryResource(SyncAPIResource): + """Stream live telemetry events from a browser session.""" + + @cached_property + def with_raw_response(self) -> TelemetryResourceWithRawResponse: + """ + 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 TelemetryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TelemetryResourceWithStreamingResponse: + """ + 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 TelemetryResourceWithStreamingResponse(self) + + def stream( + self, + id: str, + *, + 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. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[TelemetryStreamResponse]: + """Streams browser telemetry events as a server-sent events (SSE) stream. + + The + 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 + 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. + + 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": "text/event-stream", **(extra_headers or {})} + extra_headers = {**strip_not_given({"Last-Event-ID": last_event_id}), **(extra_headers or {})} + return self._get( + path_template("/browsers/{id}/telemetry", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TelemetryStreamResponse, + stream=True, + stream_cls=Stream[TelemetryStreamResponse], + ) + + +class AsyncTelemetryResource(AsyncAPIResource): + """Stream live telemetry events from a browser session.""" + + @cached_property + def with_raw_response(self) -> AsyncTelemetryResourceWithRawResponse: + """ + 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 AsyncTelemetryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTelemetryResourceWithStreamingResponse: + """ + 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 AsyncTelemetryResourceWithStreamingResponse(self) + + async def stream( + self, + id: str, + *, + 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. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[TelemetryStreamResponse]: + """Streams browser telemetry events as a server-sent events (SSE) stream. + + The + 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 + 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. + + 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": "text/event-stream", **(extra_headers or {})} + extra_headers = {**strip_not_given({"Last-Event-ID": last_event_id}), **(extra_headers or {})} + return await self._get( + path_template("/browsers/{id}/telemetry", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TelemetryStreamResponse, + stream=True, + stream_cls=AsyncStream[TelemetryStreamResponse], + ) + + +class TelemetryResourceWithRawResponse: + def __init__(self, telemetry: TelemetryResource) -> None: + self._telemetry = telemetry + + self.stream = to_raw_response_wrapper( + telemetry.stream, + ) + + +class AsyncTelemetryResourceWithRawResponse: + def __init__(self, telemetry: AsyncTelemetryResource) -> None: + self._telemetry = telemetry + + self.stream = async_to_raw_response_wrapper( + telemetry.stream, + ) + + +class TelemetryResourceWithStreamingResponse: + def __init__(self, telemetry: TelemetryResource) -> None: + self._telemetry = telemetry + + self.stream = to_streamed_response_wrapper( + telemetry.stream, + ) + + +class AsyncTelemetryResourceWithStreamingResponse: + def __init__(self, telemetry: AsyncTelemetryResource) -> None: + self._telemetry = telemetry + + self.stream = async_to_streamed_response_wrapper( + telemetry.stream, + ) diff --git a/src/kernel/resources/proxies.py b/src/kernel/resources/proxies.py index bacdd570..501a2e88 100644 --- a/src/kernel/resources/proxies.py +++ b/src/kernel/resources/proxies.py @@ -222,7 +222,9 @@ def check( between requests, so a successful check validates proxy configuration but does not guarantee that a subsequent browser session will use the same exit IP or reach the same site — it is useful for verifying credentials and connectivity, - not for predicting site-specific behavior. + not for predicting site-specific behavior. When provided, the check result does + not update the proxy's health status, since a failure may indicate a problem + with the target site rather than the proxy itself. extra_headers: Send extra headers @@ -440,7 +442,9 @@ async def check( between requests, so a successful check validates proxy configuration but does not guarantee that a subsequent browser session will use the same exit IP or reach the same site — it is useful for verifying credentials and connectivity, - not for predicting site-specific behavior. + not for predicting site-specific behavior. When provided, the check result does + not update the proxy's health status, since a failure may indicate a problem + with the target site rather than the proxy itself. extra_headers: Send extra headers diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index 3838047e..1a7d19c3 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations +from . import browsers +from .. import _compat from .shared import ( LogEvent as LogEvent, AppAction as AppAction, @@ -91,3 +93,18 @@ from .extension_download_from_chrome_store_params import ( ExtensionDownloadFromChromeStoreParams as ExtensionDownloadFromChromeStoreParams, ) + +# Rebuild cyclical models only after all modules are imported. +# This ensures that, when building the deferred (due to cyclical references) model schema, +# Pydantic can resolve the necessary references. +# See: https://github.com/pydantic/pydantic/issues/11250 for more context. +if _compat.PYDANTIC_V1: + browsers.browser_call_stack.BrowserCallStack.update_forward_refs() # type: ignore + browsers.browser_console_error_event.BrowserConsoleErrorEvent.update_forward_refs() # type: ignore + browsers.browser_console_log_event.BrowserConsoleLogEvent.update_forward_refs() # type: ignore + browsers.telemetry_stream_response.TelemetryStreamResponse.update_forward_refs() # type: ignore +else: + browsers.browser_call_stack.BrowserCallStack.model_rebuild(_parent_namespace_depth=0) + browsers.browser_console_error_event.BrowserConsoleErrorEvent.model_rebuild(_parent_namespace_depth=0) + browsers.browser_console_log_event.BrowserConsoleLogEvent.model_rebuild(_parent_namespace_depth=0) + browsers.telemetry_stream_response.TelemetryStreamResponse.model_rebuild(_parent_namespace_depth=0) diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py index 399ded4c..ca184fc7 100644 --- a/src/kernel/types/browser_create_params.py +++ b/src/kernel/types/browser_create_params.py @@ -2,18 +2,28 @@ from __future__ import annotations -from typing import Iterable +from typing import Dict, Iterable, Optional from typing_extensions import TypedDict from .browser_persistence_param import BrowserPersistenceParam from .shared_params.browser_profile import BrowserProfile from .shared_params.browser_viewport import BrowserViewport from .shared_params.browser_extension import BrowserExtension +from .browsers.browser_telemetry_config_param import BrowserTelemetryConfigParam __all__ = ["BrowserCreateParams"] class BrowserCreateParams(TypedDict, total=False): + chrome_policy: Dict[str, object] + """Custom Chrome enterprise policy overrides applied to this browser session. + + Keys are Chrome enterprise policy names; values must match their expected types. + Blocked: kernel-managed policies (extensions, proxy, CDP/automation). Ignored + when reusing an existing persistent session. See + https://chromeenterprise.google/policies/ + """ + extensions: Iterable[BrowserExtension] """List of browser extensions to load into the session. @@ -70,6 +80,13 @@ class BrowserCreateParams(TypedDict, total=False): mechanisms. """ + telemetry: Optional[BrowserTelemetryConfigParam] + """Telemetry configuration for the browser session. + + If provided, telemetry capture starts with the specified category filter when + the session is created. If omitted, no telemetry capture is started. + """ + timeout_seconds: int """The number of seconds of inactivity before the browser session is terminated. diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py index 1c30ee63..bbb2287e 100644 --- a/src/kernel/types/browser_create_response.py +++ b/src/kernel/types/browser_create_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["BrowserCreateResponse"] @@ -44,6 +45,13 @@ class BrowserCreateResponse(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class BrowserCreateResponse(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py index 2a172671..ed14251a 100644 --- a/src/kernel/types/browser_list_response.py +++ b/src/kernel/types/browser_list_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["BrowserListResponse"] @@ -44,6 +45,13 @@ class BrowserListResponse(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class BrowserListResponse(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/browser_pool.py b/src/kernel/types/browser_pool.py index e456fca2..8ead6807 100644 --- a/src/kernel/types/browser_pool.py +++ b/src/kernel/types/browser_pool.py @@ -64,10 +64,12 @@ class BrowserPoolConfig(BaseModel): """ start_url: Optional[str] = None - """Optional URL to open when a browser is created for the pool. + """Optional URL to navigate to when a new browser is warmed into the pool. - Navigation is best-effort, so navigation failures do not prevent the pool from - filling. Reused browsers keep the page left by the previous lease. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. """ stealth: Optional[bool] = None diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py index 91ea02e7..20ab1fc5 100644 --- a/src/kernel/types/browser_pool_acquire_response.py +++ b/src/kernel/types/browser_pool_acquire_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["BrowserPoolAcquireResponse"] @@ -44,6 +45,13 @@ class BrowserPoolAcquireResponse(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class BrowserPoolAcquireResponse(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/browser_pool_create_params.py b/src/kernel/types/browser_pool_create_params.py index bc08c3f2..1ffeeb75 100644 --- a/src/kernel/types/browser_pool_create_params.py +++ b/src/kernel/types/browser_pool_create_params.py @@ -63,10 +63,12 @@ class BrowserPoolCreateParams(TypedDict, total=False): """ start_url: str - """Optional URL to open when a browser is created for the pool. + """Optional URL to navigate to when a new browser is warmed into the pool. - Navigation is best-effort, so navigation failures do not prevent the pool from - filling. Reused browsers keep the page left by the previous lease. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. """ stealth: bool diff --git a/src/kernel/types/browser_pool_update_params.py b/src/kernel/types/browser_pool_update_params.py index 043eaf85..4efc9ced 100644 --- a/src/kernel/types/browser_pool_update_params.py +++ b/src/kernel/types/browser_pool_update_params.py @@ -69,10 +69,12 @@ class BrowserPoolUpdateParams(TypedDict, total=False): """ start_url: str - """Optional URL to open when a browser is created for the pool. + """Optional URL to navigate to when a new browser is warmed into the pool. - Navigation is best-effort, so navigation failures do not prevent the pool from - filling. Reused browsers keep the page left by the previous lease. + Best-effort: failures to navigate do not fail pool fill. Only applied to + newly-warmed browsers; browsers reused via release/acquire keep whatever URL the + previous lease left them on. Accepts any URL Chromium can resolve, including + chrome:// pages. """ stealth: bool diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py index 63f4da9d..6c79b519 100644 --- a/src/kernel/types/browser_retrieve_response.py +++ b/src/kernel/types/browser_retrieve_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["BrowserRetrieveResponse"] @@ -44,6 +45,13 @@ class BrowserRetrieveResponse(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class BrowserRetrieveResponse(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/browser_update_params.py b/src/kernel/types/browser_update_params.py index e0f7588e..297d0388 100644 --- a/src/kernel/types/browser_update_params.py +++ b/src/kernel/types/browser_update_params.py @@ -7,6 +7,7 @@ from .shared_params.browser_profile import BrowserProfile from .shared_params.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config_param import BrowserTelemetryConfigParam __all__ = ["BrowserUpdateParams", "Viewport"] @@ -30,6 +31,15 @@ class BrowserUpdateParams(TypedDict, total=False): Omit to leave unchanged, set to empty string to remove proxy. """ + telemetry: Optional[BrowserTelemetryConfigParam] + """Telemetry configuration. + + Omit, set to null, or set to an empty object ({}) to leave the existing + configuration unchanged (no-op). To enable capture for all categories using VM + defaults, set browser to an empty object ({"browser": {}}). To stop capture, set + every category's enabled to false. + """ + viewport: Viewport """Viewport configuration to apply to the browser session.""" diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py index 2e30f49d..4f237cf5 100644 --- a/src/kernel/types/browser_update_response.py +++ b/src/kernel/types/browser_update_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["BrowserUpdateResponse"] @@ -44,6 +45,13 @@ class BrowserUpdateResponse(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class BrowserUpdateResponse(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/browsers/__init__.py b/src/kernel/types/browsers/__init__.py index 1e47205d..a3bca412 100644 --- a/src/kernel/types/browsers/__init__.py +++ b/src/kernel/types/browsers/__init__.py @@ -5,6 +5,7 @@ from .f_move_params import FMoveParams as FMoveParams from .f_upload_params import FUploadParams as FUploadParams from .log_stream_params import LogStreamParams as LogStreamParams +from .browser_call_stack import BrowserCallStack as BrowserCallStack from .f_file_info_params import FFileInfoParams as FFileInfoParams from .f_read_file_params import FReadFileParams as FReadFileParams from .f_list_files_params import FListFilesParams as FListFilesParams @@ -13,41 +14,84 @@ from .process_exec_params import ProcessExecParams as ProcessExecParams from .process_kill_params import ProcessKillParams as ProcessKillParams from .replay_start_params import ReplayStartParams as ReplayStartParams +from .browser_event_source import BrowserEventSource as BrowserEventSource +from .browser_http_headers import BrowserHTTPHeaders as BrowserHTTPHeaders from .f_delete_file_params import FDeleteFileParams as FDeleteFileParams from .f_file_info_response import FFileInfoResponse as FFileInfoResponse from .process_spawn_params import ProcessSpawnParams as ProcessSpawnParams from .process_stdin_params import ProcessStdinParams as ProcessStdinParams from .replay_list_response import ReplayListResponse as ReplayListResponse +from .browser_event_context import BrowserEventContext as BrowserEventContext from .computer_batch_params import ComputerBatchParams as ComputerBatchParams from .f_list_files_response import FListFilesResponse as FListFilesResponse from .process_exec_response import ProcessExecResponse as ProcessExecResponse from .process_kill_response import ProcessKillResponse as ProcessKillResponse from .process_resize_params import ProcessResizeParams as ProcessResizeParams from .replay_start_response import ReplayStartResponse as ReplayStartResponse +from .browser_page_lcp_event import BrowserPageLcpEvent as BrowserPageLcpEvent from .computer_scroll_params import ComputerScrollParams as ComputerScrollParams from .process_spawn_response import ProcessSpawnResponse as ProcessSpawnResponse from .process_stdin_response import ProcessStdinResponse as ProcessStdinResponse +from .browser_page_load_event import BrowserPageLoadEvent as BrowserPageLoadEvent +from .browser_telemetry_event import BrowserTelemetryEvent as BrowserTelemetryEvent from .process_resize_response import ProcessResizeResponse as ProcessResizeResponse from .process_status_response import ProcessStatusResponse as ProcessStatusResponse +from .browser_telemetry_config import BrowserTelemetryConfig as BrowserTelemetryConfig +from .browser_console_log_event import BrowserConsoleLogEvent as BrowserConsoleLogEvent from .computer_press_key_params import ComputerPressKeyParams as ComputerPressKeyParams from .computer_type_text_params import ComputerTypeTextParams as ComputerTypeTextParams from .f_create_directory_params import FCreateDirectoryParams as FCreateDirectoryParams from .f_delete_directory_params import FDeleteDirectoryParams as FDeleteDirectoryParams from .f_download_dir_zip_params import FDownloadDirZipParams as FDownloadDirZipParams from .playwright_execute_params import PlaywrightExecuteParams as PlaywrightExecuteParams +from .telemetry_stream_response import TelemetryStreamResponse as TelemetryStreamResponse +from .browser_network_idle_event import BrowserNetworkIdleEvent as BrowserNetworkIdleEvent from .computer_drag_mouse_params import ComputerDragMouseParams as ComputerDragMouseParams from .computer_move_mouse_params import ComputerMoveMouseParams as ComputerMoveMouseParams +from .browser_console_error_event import BrowserConsoleErrorEvent as BrowserConsoleErrorEvent from .computer_click_mouse_params import ComputerClickMouseParams as ComputerClickMouseParams from .playwright_execute_response import PlaywrightExecuteResponse as PlaywrightExecuteResponse +from .browser_interaction_key_event import BrowserInteractionKeyEvent as BrowserInteractionKeyEvent +from .browser_network_request_event import BrowserNetworkRequestEvent as BrowserNetworkRequestEvent +from .browser_page_navigation_event import BrowserPageNavigationEvent as BrowserPageNavigationEvent +from .browser_page_tab_opened_event import BrowserPageTabOpenedEvent as BrowserPageTabOpenedEvent from .f_set_file_permissions_params import FSetFilePermissionsParams as FSetFilePermissionsParams +from .browser_network_response_event import BrowserNetworkResponseEvent as BrowserNetworkResponseEvent +from .browser_telemetry_config_param import BrowserTelemetryConfigParam as BrowserTelemetryConfigParam from .process_stdout_stream_response import ProcessStdoutStreamResponse as ProcessStdoutStreamResponse +from .browser_interaction_click_event import BrowserInteractionClickEvent as BrowserInteractionClickEvent +from .browser_page_layout_shift_event import BrowserPageLayoutShiftEvent as BrowserPageLayoutShiftEvent from .computer_write_clipboard_params import ComputerWriteClipboardParams as ComputerWriteClipboardParams +from .browser_monitor_screenshot_event import BrowserMonitorScreenshotEvent as BrowserMonitorScreenshotEvent from .computer_read_clipboard_response import ComputerReadClipboardResponse as ComputerReadClipboardResponse +from .browser_monitor_init_failed_event import BrowserMonitorInitFailedEvent as BrowserMonitorInitFailedEvent +from .browser_monitor_reconnected_event import BrowserMonitorReconnectedEvent as BrowserMonitorReconnectedEvent +from .browser_page_layout_settled_event import BrowserPageLayoutSettledEvent as BrowserPageLayoutSettledEvent +from .browser_telemetry_category_config import BrowserTelemetryCategoryConfig as BrowserTelemetryCategoryConfig +from .browser_monitor_disconnected_event import BrowserMonitorDisconnectedEvent as BrowserMonitorDisconnectedEvent from .computer_capture_screenshot_params import ComputerCaptureScreenshotParams as ComputerCaptureScreenshotParams +from .browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig as BrowserTelemetryCategoriesConfig +from .browser_network_loading_failed_event import BrowserNetworkLoadingFailedEvent as BrowserNetworkLoadingFailedEvent from .computer_get_mouse_position_response import ComputerGetMousePositionResponse as ComputerGetMousePositionResponse +from .browser_page_dom_content_loaded_event import BrowserPageDomContentLoadedEvent as BrowserPageDomContentLoadedEvent +from .browser_page_navigation_settled_event import ( + BrowserPageNavigationSettledEvent as BrowserPageNavigationSettledEvent, +) from .computer_set_cursor_visibility_params import ( ComputerSetCursorVisibilityParams as ComputerSetCursorVisibilityParams, ) +from .browser_monitor_reconnect_failed_event import ( + BrowserMonitorReconnectFailedEvent as BrowserMonitorReconnectFailedEvent, +) +from .browser_telemetry_category_config_param import ( + BrowserTelemetryCategoryConfigParam as BrowserTelemetryCategoryConfigParam, +) from .computer_set_cursor_visibility_response import ( ComputerSetCursorVisibilityResponse as ComputerSetCursorVisibilityResponse, ) +from .browser_interaction_scroll_settled_event import ( + BrowserInteractionScrollSettledEvent as BrowserInteractionScrollSettledEvent, +) +from .browser_telemetry_categories_config_param import ( + BrowserTelemetryCategoriesConfigParam as BrowserTelemetryCategoriesConfigParam, +) diff --git a/src/kernel/types/browsers/browser_call_stack.py b/src/kernel/types/browsers/browser_call_stack.py new file mode 100644 index 00000000..d9e97ffd --- /dev/null +++ b/src/kernel/types/browsers/browser_call_stack.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["BrowserCallStack", "CallFrame"] + + +class CallFrame(BaseModel): + column_number: int = FieldInfo(alias="columnNumber") + """Zero-based column number within the line.""" + + function_name: str = FieldInfo(alias="functionName") + """JavaScript function name, or empty string for anonymous functions.""" + + line_number: int = FieldInfo(alias="lineNumber") + """Zero-based line number within the script.""" + + script_id: str = FieldInfo(alias="scriptId") + """CDP script identifier.""" + + url: str + """URL or name of the script file.""" + + +class BrowserCallStack(BaseModel): + """ + CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. + """ + + call_frames: List[CallFrame] = FieldInfo(alias="callFrames") + """Ordered list of call frames, outermost first.""" + + description: Optional[str] = None + """Optional label for the stack trace (e.g. async cause).""" + + parent: Optional["BrowserCallStack"] = None + """Parent stack trace for async stacks.""" diff --git a/src/kernel/types/browsers/browser_console_error_event.py b/src/kernel/types/browsers/browser_console_error_event.py new file mode 100644 index 00000000..a152f944 --- /dev/null +++ b/src/kernel/types/browsers/browser_console_error_event.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserConsoleErrorEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + text: str + """Error message text. Present in both source paths.""" + + args: Optional[List[str]] = None + """All console arguments coerced to strings. + + Present only when sourced from Runtime.consoleAPICalled. + """ + + column: Optional[int] = None + """Column number in the script where the exception was thrown. + + Present only when sourced from Runtime.exceptionThrown. + """ + + level: Optional[str] = None + """CDP console type value, always "error". + + Present only when sourced from Runtime.consoleAPICalled. + """ + + line: Optional[int] = None + """Line number in the script where the exception was thrown. + + Present only when sourced from Runtime.exceptionThrown. + """ + + source_url: Optional[str] = None + """URL of the script file that threw the exception. + + Present only when sourced from Runtime.exceptionThrown. + """ + + stack_trace: Optional["BrowserCallStack"] = None + """ + CDP Runtime.StackTrace representing the JavaScript call stack at the time of an + event. Fields use CDP naming conventions rather than snake_case to match the + Chrome DevTools Protocol wire format. + """ + + +class BrowserConsoleErrorEvent(BaseModel): + """A browser console error or uncaught JavaScript exception event. + + Emitted from two distinct CDP sources with different data shapes. Runtime.consoleAPICalled (console.error calls) produces level, text, args, and stack_trace. Runtime.exceptionThrown (uncaught exceptions) produces text, line, column, source_url, and stack_trace. Fields not applicable to the source are absent. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["console_error"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" + + +from .browser_call_stack import BrowserCallStack diff --git a/src/kernel/types/browsers/browser_console_log_event.py b/src/kernel/types/browsers/browser_console_log_event.py new file mode 100644 index 00000000..5994c0be --- /dev/null +++ b/src/kernel/types/browsers/browser_console_log_event.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserConsoleLogEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + args: Optional[List[str]] = None + """All console arguments coerced to strings.""" + + level: Optional[str] = None + """CDP Runtime.consoleAPICalled type, passed through unfiltered from Chrome. + + error is routed to console_error events instead; all other CDP console types + appear here. See CDP spec for the full enum. + """ + + stack_trace: Optional["BrowserCallStack"] = None + """ + CDP Runtime.StackTrace representing the JavaScript call stack at the time of an + event. Fields use CDP naming conventions rather than snake_case to match the + Chrome DevTools Protocol wire format. + """ + + text: Optional[str] = None + """First console argument coerced to string.""" + + +class BrowserConsoleLogEvent(BaseModel): + """A browser console log event (console.log, console.info, console.warn, etc.).""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["console_log"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" + + +from .browser_call_stack import BrowserCallStack diff --git a/src/kernel/types/browsers/browser_event_context.py b/src/kernel/types/browsers/browser_event_context.py new file mode 100644 index 00000000..6f2e9549 --- /dev/null +++ b/src/kernel/types/browsers/browser_event_context.py @@ -0,0 +1,42 @@ +# 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__ = ["BrowserEventContext"] + + +class BrowserEventContext(BaseModel): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + frame_id: Optional[str] = None + """CDP frame identifier within the target.""" + + loader_id: Optional[str] = None + """CDP document loader identifier, reset on each navigation.""" + + nav_seq: Optional[int] = None + """ + Monotonically increasing navigation sequence number, incremented on each + top-level navigation within the target. + """ + + session_id: Optional[str] = None + """CDP session identifier for the target connection.""" + + target_id: Optional[str] = None + """Browser target identifier (stable across navigations within a tab).""" + + target_type: Optional[Literal["page", "background_page", "service_worker", "shared_worker", "other"]] = None + """CDP target type of the page that produced the event.""" + + url: Optional[str] = None + """ + URL relevant to this event — page URL for navigation and page events, request + URL for network events. + """ diff --git a/src/kernel/types/browsers/browser_event_source.py b/src/kernel/types/browsers/browser_event_source.py new file mode 100644 index 00000000..40125b6b --- /dev/null +++ b/src/kernel/types/browsers/browser_event_source.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BrowserEventSource"] + + +class BrowserEventSource(BaseModel): + """Provenance metadata identifying which producer emitted the event.""" + + kind: Literal["cdp", "kernel_api", "extension", "local_process"] + """Event producer. + + cdp: Chrome DevTools Protocol events from the browser. kernel_api: Kernel API + server. extension: injected Chrome extension. local_process: system process + running alongside the browser. + """ + + event: Optional[str] = None + """Producer-specific event name (e.g. + + Runtime.consoleAPICalled for CDP-sourced console events, Runtime.exceptionThrown + for uncaught exceptions). + """ + + metadata: Optional[Dict[str, str]] = None + """Producer-specific context (e.g. CDP target/session/frame IDs).""" diff --git a/src/kernel/types/browsers/browser_http_headers.py b/src/kernel/types/browsers/browser_http_headers.py new file mode 100644 index 00000000..b55ae855 --- /dev/null +++ b/src/kernel/types/browsers/browser_http_headers.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["BrowserHTTPHeaders"] + +BrowserHTTPHeaders: TypeAlias = Dict[str, object] diff --git a/src/kernel/types/browsers/browser_interaction_click_event.py b/src/kernel/types/browsers/browser_interaction_click_event.py new file mode 100644 index 00000000..efc76b34 --- /dev/null +++ b/src/kernel/types/browsers/browser_interaction_click_event.py @@ -0,0 +1,54 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserInteractionClickEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + selector: Optional[str] = None + """CSS selector path to the clicked element.""" + + tag: Optional[str] = None + """HTML tag name of the clicked element in uppercase (e.g. BUTTON, A, DIV).""" + + text: Optional[str] = None + """Visible text content of the clicked element, trimmed.""" + + x: Optional[int] = None + """Viewport x-coordinate of the click in CSS pixels.""" + + y: Optional[int] = None + """Viewport y-coordinate of the click in CSS pixels.""" + + +class BrowserInteractionClickEvent(BaseModel): + """A browser user click event captured via injected page script.""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["interaction_click"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_interaction_key_event.py b/src/kernel/types/browsers/browser_interaction_key_event.py new file mode 100644 index 00000000..e8860330 --- /dev/null +++ b/src/kernel/types/browsers/browser_interaction_key_event.py @@ -0,0 +1,48 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserInteractionKeyEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + key: Optional[str] = None + """Key value from the KeyboardEvent (e.g. Enter, Backspace, a).""" + + selector: Optional[str] = None + """CSS selector path to the element that had focus when the key was pressed.""" + + tag: Optional[str] = None + """HTML tag name of the focused element in uppercase (e.g. INPUT, TEXTAREA, DIV).""" + + +class BrowserInteractionKeyEvent(BaseModel): + """A browser keyboard event captured via injected page script.""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["interaction_key"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_interaction_scroll_settled_event.py b/src/kernel/types/browsers/browser_interaction_scroll_settled_event.py new file mode 100644 index 00000000..16b7536b --- /dev/null +++ b/src/kernel/types/browsers/browser_interaction_scroll_settled_event.py @@ -0,0 +1,56 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserInteractionScrollSettledEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + from_x: Optional[int] = None + """Scroll x-position at the start of the scroll gesture in CSS pixels.""" + + from_y: Optional[int] = None + """Scroll y-position at the start of the scroll gesture in CSS pixels.""" + + target_selector: Optional[str] = None + """CSS selector path to the scrolled element.""" + + to_x: Optional[int] = None + """Final scroll x-position after the gesture settled in CSS pixels.""" + + to_y: Optional[int] = None + """Final scroll y-position after the gesture settled in CSS pixels.""" + + +class BrowserInteractionScrollSettledEvent(BaseModel): + """ + A browser scroll settled event emitted after scroll position stops changing, captured via injected page script. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["interaction_scroll_settled"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_monitor_disconnected_event.py b/src/kernel/types/browsers/browser_monitor_disconnected_event.py new file mode 100644 index 00000000..b329ab13 --- /dev/null +++ b/src/kernel/types/browsers/browser_monitor_disconnected_event.py @@ -0,0 +1,34 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserMonitorDisconnectedEvent", "Data"] + + +class Data(BaseModel): + reason: Optional[Literal["chrome_restarted"]] = None + """Reason for the disconnection. chrome_restarted: Chrome process restarted.""" + + +class BrowserMonitorDisconnectedEvent(BaseModel): + """The CDP connection to Chrome was lost. + + Telemetry events may be dropped until monitor_reconnected arrives. Treat any in-progress computed state (network_idle, page_layout_settled) as unreliable until then. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["monitor_disconnected"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_monitor_init_failed_event.py b/src/kernel/types/browsers/browser_monitor_init_failed_event.py new file mode 100644 index 00000000..a745fb8d --- /dev/null +++ b/src/kernel/types/browsers/browser_monitor_init_failed_event.py @@ -0,0 +1,31 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserMonitorInitFailedEvent", "Data"] + + +class Data(BaseModel): + step: Optional[str] = None + """The CDP method or initialization step that failed (e.g. Target.setAutoAttach).""" + + +class BrowserMonitorInitFailedEvent(BaseModel): + """The CDP session could not be initialized.""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["monitor_init_failed"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_monitor_reconnect_failed_event.py b/src/kernel/types/browsers/browser_monitor_reconnect_failed_event.py new file mode 100644 index 00000000..79308d2e --- /dev/null +++ b/src/kernel/types/browsers/browser_monitor_reconnect_failed_event.py @@ -0,0 +1,37 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserMonitorReconnectFailedEvent", "Data"] + + +class Data(BaseModel): + reason: Optional[Literal["reconnect_exhausted"]] = None + """Reason for the reconnection failure. + + reconnect_exhausted: all retry attempts were used up without successfully + restoring the CDP connection. + """ + + +class BrowserMonitorReconnectFailedEvent(BaseModel): + """ + The CDP connection to Chrome could not be re-established after exhausting all reconnection attempts. No further telemetry events will arrive on this session. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["monitor_reconnect_failed"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_monitor_reconnected_event.py b/src/kernel/types/browsers/browser_monitor_reconnected_event.py new file mode 100644 index 00000000..a49ad351 --- /dev/null +++ b/src/kernel/types/browsers/browser_monitor_reconnected_event.py @@ -0,0 +1,33 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserMonitorReconnectedEvent", "Data"] + + +class Data(BaseModel): + reconnect_duration_ms: Optional[int] = None + """Wall-clock time in milliseconds taken to reconnect after the disconnection.""" + + +class BrowserMonitorReconnectedEvent(BaseModel): + """ + The CDP connection to Chrome was successfully re-established after a disconnection. Events emitted during the gap are lost. Computed state is reset, so navigation and network tracking restart fresh from this point. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["monitor_reconnected"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_monitor_screenshot_event.py b/src/kernel/types/browsers/browser_monitor_screenshot_event.py new file mode 100644 index 00000000..9b446fa9 --- /dev/null +++ b/src/kernel/types/browsers/browser_monitor_screenshot_event.py @@ -0,0 +1,31 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserMonitorScreenshotEvent", "Data"] + + +class Data(BaseModel): + png: Optional[str] = None + """Base64-encoded PNG screenshot of the browser viewport.""" + + +class BrowserMonitorScreenshotEvent(BaseModel): + """A periodic screenshot of the browser viewport.""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["monitor_screenshot"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_network_idle_event.py b/src/kernel/types/browsers/browser_network_idle_event.py new file mode 100644 index 00000000..4b4fae6b --- /dev/null +++ b/src/kernel/types/browsers/browser_network_idle_event.py @@ -0,0 +1,34 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserNetworkIdleEvent"] + + +class BrowserNetworkIdleEvent(BaseModel): + """ + A browser network idle event emitted after a 500ms quiet period with no in-flight HTTP requests. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["network_idle"] + + data: Optional[BrowserEventContext] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_network_loading_failed_event.py b/src/kernel/types/browsers/browser_network_loading_failed_event.py new file mode 100644 index 00000000..bf566a6b --- /dev/null +++ b/src/kernel/types/browsers/browser_network_loading_failed_event.py @@ -0,0 +1,58 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserNetworkLoadingFailedEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + canceled: Optional[bool] = None + """True if the request was canceled by the browser or page script.""" + + error_text: Optional[str] = None + """Network error description (e.g. net::ERR_CONNECTION_REFUSED).""" + + request_id: Optional[str] = None + """CDP request identifier matching the originating network_request event.""" + + resource_type: Optional[str] = None + """CDP Network.ResourceType for the request, passed through as-is from Chrome. + + Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, + Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + """ + + +class BrowserNetworkLoadingFailedEvent(BaseModel): + """A browser network loading failed event. + + If the request was already in flight when CDP attached (no prior network_request was emitted for it), url, frame_id, loader_id, and resource_type are absent; BrowserEventContext is partially populated in that case. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["network_loading_failed"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_network_request_event.py b/src/kernel/types/browsers/browser_network_request_event.py new file mode 100644 index 00000000..e1bd9428 --- /dev/null +++ b/src/kernel/types/browsers/browser_network_request_event.py @@ -0,0 +1,74 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_http_headers import BrowserHTTPHeaders +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserNetworkRequestEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + document_url: Optional[str] = None + """URL of the document that initiated the request.""" + + headers: Optional[BrowserHTTPHeaders] = None + """Request headers.""" + + initiator_type: Optional[str] = None + """ + CDP Initiator.type indicating what caused the request, passed through as-is from + Chrome. Known values include script, parser, preload, and other. + """ + + is_redirect: Optional[bool] = None + """True if this request is the result of a redirect.""" + + method: Optional[str] = None + """HTTP method as sent on the wire (e.g. GET, POST).""" + + post_data: Optional[str] = None + """Request body for POST/PUT requests, if available.""" + + redirect_url: Optional[str] = None + """Original URL before the redirect, present when is_redirect is true.""" + + request_id: Optional[str] = None + """CDP request identifier, unique within the session.""" + + resource_type: Optional[str] = None + """CDP Network.ResourceType for the request, passed through as-is from Chrome. + + Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, + Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + """ + + +class BrowserNetworkRequestEvent(BaseModel): + """A browser network request sent event.""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["network_request"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_network_response_event.py b/src/kernel/types/browsers/browser_network_response_event.py new file mode 100644 index 00000000..8a71d24a --- /dev/null +++ b/src/kernel/types/browsers/browser_network_response_event.py @@ -0,0 +1,71 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_http_headers import BrowserHTTPHeaders +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserNetworkResponseEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + body: Optional[str] = None + """Truncated response body, present only for text MIME types.""" + + headers: Optional[BrowserHTTPHeaders] = None + """Response headers.""" + + method: Optional[str] = None + """HTTP method of the original request.""" + + mime_type: Optional[str] = None + """MIME type of the response (e.g. text/html, application/json).""" + + request_id: Optional[str] = None + """CDP request identifier matching the originating network_request event.""" + + resource_type: Optional[str] = None + """CDP Network.ResourceType for the request, passed through as-is from Chrome. + + Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, + Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + """ + + status: Optional[int] = None + """HTTP response status code.""" + + status_text: Optional[str] = None + """HTTP response status text (e.g. OK, Not Found).""" + + +class BrowserNetworkResponseEvent(BaseModel): + """A browser network response received event. + + Fired after the response body is fully received, not when headers arrive. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["network_response"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_dom_content_loaded_event.py b/src/kernel/types/browsers/browser_page_dom_content_loaded_event.py new file mode 100644 index 00000000..e917ca32 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_dom_content_loaded_event.py @@ -0,0 +1,45 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageDomContentLoadedEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + cdp_timestamp: Optional[float] = None + """ + Chrome monotonic clock value in seconds at which DOMContentLoaded fired, + relative to browser process start (not Unix epoch). Use ts for wall-clock time. + """ + + +class BrowserPageDomContentLoadedEvent(BaseModel): + """A browser DOMContentLoaded event (CDP Page.domContentEventFired).""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_dom_content_loaded"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_layout_settled_event.py b/src/kernel/types/browsers/browser_page_layout_settled_event.py new file mode 100644 index 00000000..289b53a5 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_layout_settled_event.py @@ -0,0 +1,34 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageLayoutSettledEvent"] + + +class BrowserPageLayoutSettledEvent(BaseModel): + """ + A browser layout settled event emitted 1 second after page load with no intervening layout shifts, indicating visual stability. Each layout shift resets the 1-second timer. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_layout_settled"] + + data: Optional[BrowserEventContext] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_layout_shift_event.py b/src/kernel/types/browsers/browser_page_layout_shift_event.py new file mode 100644 index 00000000..336bfce2 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_layout_shift_event.py @@ -0,0 +1,69 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageLayoutShiftEvent", "Data", "DataLayoutShiftDetails"] + + +class DataLayoutShiftDetails(BaseModel): + """PerformanceLayoutShift attributes from the Performance Timeline entry.""" + + had_recent_input: Optional[bool] = None + """ + True if the layout shift was preceded by user input within 500ms, excluding it + from CLS. + """ + + value: Optional[float] = None + """Layout shift score for this entry (contribution to CLS).""" + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + duration: Optional[float] = None + """ + Duration of the layout shift entry in milliseconds (always 0 for layout shifts + per spec). + """ + + layout_shift_details: Optional[DataLayoutShiftDetails] = None + """PerformanceLayoutShift attributes from the Performance Timeline entry.""" + + source_frame_id: Optional[str] = None + """CDP frame identifier of the frame where the layout shift occurred.""" + + time: Optional[float] = None + """Performance Timeline timestamp of the layout shift in milliseconds.""" + + +class BrowserPageLayoutShiftEvent(BaseModel): + """ + A browser cumulative layout shift (CLS) event from the Performance Timeline API. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_layout_shift"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_lcp_event.py b/src/kernel/types/browsers/browser_page_lcp_event.py new file mode 100644 index 00000000..13b9c270 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_lcp_event.py @@ -0,0 +1,75 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageLcpEvent", "Data", "DataLcpDetails"] + + +class DataLcpDetails(BaseModel): + """LargestContentfulPaint attributes from the Performance Timeline entry.""" + + element_id: Optional[str] = None + """id attribute of the LCP element, if present.""" + + load_time: Optional[float] = None + """Load time of the LCP element in milliseconds.""" + + node_id: Optional[int] = None + """CDP DOM node identifier of the LCP element.""" + + render_time: Optional[float] = None + """ + Render time of the LCP element in milliseconds; 0 for cross-origin images + without Timing-Allow-Origin. + """ + + size: Optional[float] = None + """Visible area of the LCP element in pixels squared.""" + + url: Optional[str] = None + """URL of the LCP element for image or video elements.""" + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + lcp_details: Optional[DataLcpDetails] = None + """LargestContentfulPaint attributes from the Performance Timeline entry.""" + + source_frame_id: Optional[str] = None + """CDP frame identifier of the frame where the LCP element was rendered.""" + + time: Optional[float] = None + """Performance Timeline timestamp of the LCP entry in milliseconds.""" + + +class BrowserPageLcpEvent(BaseModel): + """ + A browser Largest Contentful Paint (LCP) event from the Performance Timeline API. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_lcp"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_load_event.py b/src/kernel/types/browsers/browser_page_load_event.py new file mode 100644 index 00000000..b4d85c8f --- /dev/null +++ b/src/kernel/types/browsers/browser_page_load_event.py @@ -0,0 +1,45 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageLoadEvent", "Data"] + + +class Data(BrowserEventContext): + """Browser event context stamped by the browser monitor onto all CDP-sourced events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + cdp_timestamp: Optional[float] = None + """ + Chrome monotonic clock value in seconds at which the load event fired, relative + to browser process start (not Unix epoch). Use ts for wall-clock time. + """ + + +class BrowserPageLoadEvent(BaseModel): + """A browser page load event (CDP Page.loadEventFired).""" + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_load"] + + data: Optional[Data] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_navigation_event.py b/src/kernel/types/browsers/browser_page_navigation_event.py new file mode 100644 index 00000000..f857398c --- /dev/null +++ b/src/kernel/types/browsers/browser_page_navigation_event.py @@ -0,0 +1,55 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserPageNavigationEvent", "Data"] + + +class Data(BaseModel): + frame_id: Optional[str] = None + """CDP frame identifier of the navigated frame.""" + + loader_id: Optional[str] = None + """New CDP document loader identifier assigned for this navigation.""" + + parent_frame_id: Optional[str] = None + """ + Parent frame identifier for subframe navigations; absent for top-level + navigations. + """ + + session_id: Optional[str] = None + """CDP session identifier.""" + + target_id: Optional[str] = None + """Browser target identifier.""" + + target_type: Optional[Literal["page", "background_page", "service_worker", "shared_worker", "other"]] = None + """CDP target type of the page that produced the event.""" + + url: Optional[str] = None + """URL navigated to.""" + + +class BrowserPageNavigationEvent(BaseModel): + """A browser page navigation started event (CDP Page.frameNavigated). + + Carries nav context fields inline but not nav_seq, as this event resets the navigation epoch. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_navigation"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_navigation_settled_event.py b/src/kernel/types/browsers/browser_page_navigation_settled_event.py new file mode 100644 index 00000000..b5227ff4 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_navigation_settled_event.py @@ -0,0 +1,34 @@ +# 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 +from .browser_event_source import BrowserEventSource +from .browser_event_context import BrowserEventContext + +__all__ = ["BrowserPageNavigationSettledEvent"] + + +class BrowserPageNavigationSettledEvent(BaseModel): + """ + Emitted when page_dom_content_loaded and page_layout_settled have both fired for the same navigation, indicating the page is loaded and visually stable. Independent of network_idle; a single pending request does not block it. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_navigation_settled"] + + data: Optional[BrowserEventContext] = None + """Browser event context stamped by the browser monitor onto all CDP-sourced + events. + + Identifies the target, frame, and navigation epoch in which the event occurred. + """ + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_page_tab_opened_event.py b/src/kernel/types/browsers/browser_page_tab_opened_event.py new file mode 100644 index 00000000..9831aee1 --- /dev/null +++ b/src/kernel/types/browsers/browser_page_tab_opened_event.py @@ -0,0 +1,45 @@ +# 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 +from .browser_event_source import BrowserEventSource + +__all__ = ["BrowserPageTabOpenedEvent", "Data"] + + +class Data(BaseModel): + opener_id: Optional[str] = None + """Target identifier of the tab that opened this one, if any.""" + + target_id: Optional[str] = None + """CDP target identifier for the newly opened tab.""" + + target_type: Optional[Literal["page", "background_page", "service_worker", "shared_worker", "other"]] = None + """CDP target type of the page that produced the event.""" + + title: Optional[str] = None + """Initial page title of the new tab.""" + + url: Optional[str] = None + """Initial URL of the new tab.""" + + +class BrowserPageTabOpenedEvent(BaseModel): + """ + A new browser tab or target was opened (CDP Target.attachedToTarget for page targets). Fires before a CDP session is attached to the new target, so session_id, frame_id, loader_id, and nav_seq are absent; this event does not compose BrowserEventContext. Consumers reading context fields generically should treat it as a special case. + """ + + source: BrowserEventSource + """Provenance metadata identifying which producer emitted the event.""" + + ts: int + """Event timestamp in Unix microseconds.""" + + type: Literal["page_tab_opened"] + + data: Optional[Data] = None + + truncated: Optional[bool] = None + """True if the data field was truncated due to size limits.""" diff --git a/src/kernel/types/browsers/browser_telemetry_categories_config.py b/src/kernel/types/browsers/browser_telemetry_categories_config.py new file mode 100644 index 00000000..82cdb1db --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_categories_config.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .browser_telemetry_category_config import BrowserTelemetryCategoryConfig + +__all__ = ["BrowserTelemetryCategoriesConfig"] + + +class BrowserTelemetryCategoriesConfig(BaseModel): + """Per-category telemetry capture settings.""" + + console: Optional[BrowserTelemetryCategoryConfig] = None + """Console output (log, warn, error) and uncaught exceptions.""" + + interaction: Optional[BrowserTelemetryCategoryConfig] = None + """User interaction events including clicks, keydowns, and scroll-settled events.""" + + network: Optional[BrowserTelemetryCategoryConfig] = None + """ + HTTP request and response metadata including URL, method, status code, and + timing. Request post data is forwarded as-is from CDP. Text response bodies are + truncated at 8 KB for structured types (JSON, XML, form data) and 4 KB for other + text types. Binary responses (images, fonts, media) are excluded. + """ + + page: Optional[BrowserTelemetryCategoryConfig] = None + """ + Page lifecycle events including navigation, DOMContentLoaded, load, layout + shifts, and LCP. + """ diff --git a/src/kernel/types/browsers/browser_telemetry_categories_config_param.py b/src/kernel/types/browsers/browser_telemetry_categories_config_param.py new file mode 100644 index 00000000..75a3a797 --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_categories_config_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .browser_telemetry_category_config_param import BrowserTelemetryCategoryConfigParam + +__all__ = ["BrowserTelemetryCategoriesConfigParam"] + + +class BrowserTelemetryCategoriesConfigParam(TypedDict, total=False): + """Per-category telemetry capture settings.""" + + console: BrowserTelemetryCategoryConfigParam + """Console output (log, warn, error) and uncaught exceptions.""" + + interaction: BrowserTelemetryCategoryConfigParam + """User interaction events including clicks, keydowns, and scroll-settled events.""" + + network: BrowserTelemetryCategoryConfigParam + """ + HTTP request and response metadata including URL, method, status code, and + timing. Request post data is forwarded as-is from CDP. Text response bodies are + truncated at 8 KB for structured types (JSON, XML, form data) and 4 KB for other + text types. Binary responses (images, fonts, media) are excluded. + """ + + page: BrowserTelemetryCategoryConfigParam + """ + Page lifecycle events including navigation, DOMContentLoaded, load, layout + shifts, and LCP. + """ diff --git a/src/kernel/types/browsers/browser_telemetry_category_config.py b/src/kernel/types/browsers/browser_telemetry_category_config.py new file mode 100644 index 00000000..4a2da2cb --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_category_config.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["BrowserTelemetryCategoryConfig"] + + +class BrowserTelemetryCategoryConfig(BaseModel): + """Per-category telemetry configuration.""" + + enabled: Optional[bool] = None + """Whether this category is captured. Defaults to true if omitted.""" diff --git a/src/kernel/types/browsers/browser_telemetry_category_config_param.py b/src/kernel/types/browsers/browser_telemetry_category_config_param.py new file mode 100644 index 00000000..3824b4c8 --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_category_config_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BrowserTelemetryCategoryConfigParam"] + + +class BrowserTelemetryCategoryConfigParam(TypedDict, total=False): + """Per-category telemetry configuration.""" + + enabled: bool + """Whether this category is captured. Defaults to true if omitted.""" diff --git a/src/kernel/types/browsers/browser_telemetry_config.py b/src/kernel/types/browsers/browser_telemetry_config.py new file mode 100644 index 00000000..d4365eeb --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_config.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig + +__all__ = ["BrowserTelemetryConfig"] + + +class BrowserTelemetryConfig(BaseModel): + """Telemetry configuration for a browser session.""" + + browser: Optional[BrowserTelemetryCategoriesConfig] = None + """Per-category enable/disable flags. If omitted, all categories are captured.""" diff --git a/src/kernel/types/browsers/browser_telemetry_config_param.py b/src/kernel/types/browsers/browser_telemetry_config_param.py new file mode 100644 index 00000000..33464340 --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_config_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam + +__all__ = ["BrowserTelemetryConfigParam"] + + +class BrowserTelemetryConfigParam(TypedDict, total=False): + """Telemetry configuration for a browser session.""" + + browser: BrowserTelemetryCategoriesConfigParam + """Per-category enable/disable flags. If omitted, all categories are captured.""" diff --git a/src/kernel/types/browsers/browser_telemetry_event.py b/src/kernel/types/browsers/browser_telemetry_event.py new file mode 100644 index 00000000..c27297bc --- /dev/null +++ b/src/kernel/types/browsers/browser_telemetry_event.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .browser_page_lcp_event import BrowserPageLcpEvent +from .browser_page_load_event import BrowserPageLoadEvent +from .browser_network_idle_event import BrowserNetworkIdleEvent +from .browser_interaction_key_event import BrowserInteractionKeyEvent +from .browser_network_request_event import BrowserNetworkRequestEvent +from .browser_page_navigation_event import BrowserPageNavigationEvent +from .browser_page_tab_opened_event import BrowserPageTabOpenedEvent +from .browser_network_response_event import BrowserNetworkResponseEvent +from .browser_interaction_click_event import BrowserInteractionClickEvent +from .browser_page_layout_shift_event import BrowserPageLayoutShiftEvent +from .browser_monitor_screenshot_event import BrowserMonitorScreenshotEvent +from .browser_monitor_init_failed_event import BrowserMonitorInitFailedEvent +from .browser_monitor_reconnected_event import BrowserMonitorReconnectedEvent +from .browser_page_layout_settled_event import BrowserPageLayoutSettledEvent +from .browser_monitor_disconnected_event import BrowserMonitorDisconnectedEvent +from .browser_network_loading_failed_event import BrowserNetworkLoadingFailedEvent +from .browser_page_dom_content_loaded_event import BrowserPageDomContentLoadedEvent +from .browser_page_navigation_settled_event import BrowserPageNavigationSettledEvent +from .browser_monitor_reconnect_failed_event import BrowserMonitorReconnectFailedEvent +from .browser_interaction_scroll_settled_event import BrowserInteractionScrollSettledEvent + +__all__ = ["BrowserTelemetryEvent"] + +BrowserTelemetryEvent: TypeAlias = Annotated[ + Union[ + "BrowserConsoleLogEvent", + "BrowserConsoleErrorEvent", + BrowserNetworkRequestEvent, + BrowserNetworkResponseEvent, + BrowserNetworkLoadingFailedEvent, + BrowserNetworkIdleEvent, + BrowserPageNavigationEvent, + BrowserPageDomContentLoadedEvent, + BrowserPageLoadEvent, + BrowserPageTabOpenedEvent, + BrowserPageLayoutShiftEvent, + BrowserPageLcpEvent, + BrowserPageLayoutSettledEvent, + BrowserPageNavigationSettledEvent, + BrowserInteractionClickEvent, + BrowserInteractionKeyEvent, + BrowserInteractionScrollSettledEvent, + BrowserMonitorScreenshotEvent, + BrowserMonitorDisconnectedEvent, + BrowserMonitorReconnectedEvent, + BrowserMonitorReconnectFailedEvent, + BrowserMonitorInitFailedEvent, + ], + PropertyInfo(discriminator="type"), +] + +from .browser_console_log_event import BrowserConsoleLogEvent +from .browser_console_error_event import BrowserConsoleErrorEvent diff --git a/src/kernel/types/browsers/telemetry_stream_response.py b/src/kernel/types/browsers/telemetry_stream_response.py new file mode 100644 index 00000000..fef04b3f --- /dev/null +++ b/src/kernel/types/browsers/telemetry_stream_response.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._models import BaseModel + +__all__ = ["TelemetryStreamResponse"] + + +class TelemetryStreamResponse(BaseModel): + """Envelope wrapping a browser telemetry event with its monotonic sequence number. + + Each SSE data: frame carries one envelope as JSON. The seq value is also emitted as the SSE id: field so clients can pass it as Last-Event-ID on reconnect. + """ + + event: "BrowserTelemetryEvent" + """Union type representing any browser telemetry event. + + Discriminated on `type`. Events with a `monitor_` prefix (monitor_screenshot, + monitor_disconnected, monitor_reconnected, monitor_reconnect_failed, + monitor_init_failed) are always emitted regardless of the category configuration + in BrowserTelemetryConfig. All other event types are controlled by the + per-category enable/disable flags. + """ + + seq: int + """Process-monotonic sequence number assigned by the browser VM. + + Pass as Last-Event-ID on reconnect to resume without gaps. Gaps in received seq + values indicate dropped events. + """ + + +from .browser_telemetry_event import BrowserTelemetryEvent diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py index c7b9e394..7c60ae22 100644 --- a/src/kernel/types/invocation_list_browsers_response.py +++ b/src/kernel/types/invocation_list_browsers_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Dict, List, Optional from datetime import datetime from .profile import Profile @@ -9,6 +9,7 @@ from .browser_pool_ref import BrowserPoolRef from .browser_persistence import BrowserPersistence from .shared.browser_viewport import BrowserViewport +from .browsers.browser_telemetry_config import BrowserTelemetryConfig __all__ = ["InvocationListBrowsersResponse", "Browser"] @@ -44,6 +45,13 @@ class Browser(BaseModel): Only available for non-headless browsers. """ + chrome_policy: Optional[Dict[str, object]] = None + """ + Custom Chrome enterprise policy overrides that were applied to this browser + session, if any. Echoed back for verification. Keys are Chrome enterprise policy + names. + """ + deleted_at: Optional[datetime] = None """When the browser session was soft-deleted. Only present for deleted sessions.""" @@ -69,7 +77,16 @@ class Browser(BaseModel): """ID of the proxy associated with this browser session, if any.""" start_url: Optional[str] = None - """Start URL requested for the session, if provided.""" + """URL the session was asked to navigate to on creation, if any. + + Recorded for debugging. Navigation is fire-and-forget — the URL is dispatched to + the browser without waiting for it to load, and any errors (DNS failure, bad + status, timeout) are silently dropped. Captures what was requested, not what the + browser actually loaded. + """ + + telemetry: Optional[BrowserTelemetryConfig] = None + """Active telemetry configuration for the session, if any.""" usage: Optional[BrowserUsage] = None """Session usage metrics.""" diff --git a/src/kernel/types/proxy_check_params.py b/src/kernel/types/proxy_check_params.py index de3a8c3d..47f99f04 100644 --- a/src/kernel/types/proxy_check_params.py +++ b/src/kernel/types/proxy_check_params.py @@ -19,5 +19,8 @@ class ProxyCheckParams(TypedDict, total=False): proxies, the exit node changes between requests, so a successful check validates proxy configuration but does not guarantee that a subsequent browser session will use the same exit IP or reach the same site — it is useful for verifying - credentials and connectivity, not for predicting site-specific behavior. + credentials and connectivity, not for predicting site-specific behavior. When + provided, the check result does not update the proxy's health status, since a + failure may indicate a problem with the target site rather than the proxy + itself. """ diff --git a/tests/api_resources/browsers/test_telemetry.py b/tests/api_resources/browsers/test_telemetry.py new file mode 100644 index 00000000..0fcd2715 --- /dev/null +++ b/tests/api_resources/browsers/test_telemetry.py @@ -0,0 +1,122 @@ +# 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 + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTelemetry: + 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_stream(self, client: Kernel) -> None: + telemetry_stream = client.browsers.telemetry.stream( + id="id", + ) + telemetry_stream.response.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_stream_with_all_params(self, client: Kernel) -> None: + telemetry_stream = client.browsers.telemetry.stream( + id="id", + last_event_id="Last-Event-ID", + ) + telemetry_stream.response.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_stream(self, client: Kernel) -> None: + response = client.browsers.telemetry.with_raw_response.stream( + id="id", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_stream(self, client: Kernel) -> None: + with client.browsers.telemetry.with_streaming_response.stream( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_stream(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.browsers.telemetry.with_raw_response.stream( + id="", + ) + + +class TestAsyncTelemetry: + 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_stream(self, async_client: AsyncKernel) -> None: + telemetry_stream = await async_client.browsers.telemetry.stream( + id="id", + ) + await telemetry_stream.response.aclose() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncKernel) -> None: + telemetry_stream = await async_client.browsers.telemetry.stream( + id="id", + last_event_id="Last-Event-ID", + ) + await telemetry_stream.response.aclose() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncKernel) -> None: + response = await async_client.browsers.telemetry.with_raw_response.stream( + id="id", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncKernel) -> None: + async with async_client.browsers.telemetry.with_streaming_response.stream( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_stream(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.browsers.telemetry.with_raw_response.stream( + id="", + ) diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py index 94c52654..dcd20977 100644 --- a/tests/api_resources/test_browsers.py +++ b/tests/api_resources/test_browsers.py @@ -36,6 +36,7 @@ def test_method_create(self, client: Kernel) -> None: @parametrize def test_method_create_with_all_params(self, client: Kernel) -> None: browser = client.browsers.create( + chrome_policy={"foo": "bar"}, extensions=[ { "id": "id", @@ -55,6 +56,14 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: proxy_id="proxy_id", start_url="https://example.com", stealth=True, + telemetry={ + "browser": { + "console": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + } + }, timeout_seconds=10, viewport={ "height": 800, @@ -157,6 +166,14 @@ def test_method_update_with_all_params(self, client: Kernel) -> None: "save_changes": True, }, proxy_id="proxy_id", + telemetry={ + "browser": { + "console": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + } + }, viewport={ "height": 800, "width": 1280, @@ -462,6 +479,7 @@ async def test_method_create(self, async_client: AsyncKernel) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None: browser = await async_client.browsers.create( + chrome_policy={"foo": "bar"}, extensions=[ { "id": "id", @@ -481,6 +499,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> proxy_id="proxy_id", start_url="https://example.com", stealth=True, + telemetry={ + "browser": { + "console": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + } + }, timeout_seconds=10, viewport={ "height": 800, @@ -583,6 +609,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> "save_changes": True, }, proxy_id="proxy_id", + telemetry={ + "browser": { + "console": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + } + }, viewport={ "height": 800, "width": 1280,