From 57af2e181b716145ff3e11a0d74c04dd332f9e35 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 5 Dec 2025 22:00:22 +0000
Subject: [PATCH 1/8] refactor(browser): remove persistence option UI
---
.stats.yml | 6 +-
README.md | 18 +++---
src/kernel/resources/browsers/browsers.py | 61 +++++++++++-------
src/kernel/types/browser_create_params.py | 11 ++--
src/kernel/types/browser_create_response.py | 2 +-
src/kernel/types/browser_list_response.py | 2 +-
src/kernel/types/browser_persistence.py | 2 +-
src/kernel/types/browser_persistence_param.py | 2 +-
.../types/browser_pool_acquire_response.py | 2 +-
src/kernel/types/browser_retrieve_response.py | 2 +-
tests/api_resources/test_browsers.py | 64 +++++++++++--------
11 files changed, 96 insertions(+), 76 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 7792bb0..a0095f1 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 74
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-003e9afa15f0765009d2c7d34e8eb62268d818e628e3c84361b21138e30cc423.yml
-openapi_spec_hash: c1b8309f60385bf2b02d245363ca47c1
-config_hash: a4124701ae0a474e580d7416adbcfb00
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-cbef7e4fef29ad40af5c767aceb762ee68811c2287f255c05d2ee44a9a9247a2.yml
+openapi_spec_hash: 467e61e072773ec9f2d49c7dd3de8c2f
+config_hash: 6dbe88d2ba9df1ec46cedbfdb7d00000
diff --git a/README.md b/README.md
index d5905ea..6d1dd19 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ client = Kernel(
)
browser = client.browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
print(browser.session_id)
```
@@ -63,7 +63,7 @@ client = AsyncKernel(
async def main() -> None:
browser = await client.browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
print(browser.session_id)
@@ -99,7 +99,7 @@ async def main() -> None:
http_client=DefaultAioHttpClient(),
) as client:
browser = await client.browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
print(browser.session_id)
@@ -242,7 +242,7 @@ client = Kernel()
try:
client.browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
except kernel.APIConnectionError as e:
print("The server could not be reached")
@@ -287,7 +287,7 @@ client = Kernel(
# Or, configure per-request:
client.with_options(max_retries=5).browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
```
@@ -312,7 +312,7 @@ client = Kernel(
# Override per-request:
client.with_options(timeout=5.0).browsers.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
)
```
@@ -355,9 +355,7 @@ from kernel import Kernel
client = Kernel()
response = client.browsers.with_raw_response.create(
- persistence={
- "id": "browser-for-user-1234"
- },
+ stealth=True,
)
print(response.headers.get('X-My-Header'))
@@ -377,7 +375,7 @@ To stream the response body, use `.with_streaming_response` instead, which requi
```python
with client.browsers.with_streaming_response.create(
- persistence={"id": "browser-for-user-1234"},
+ stealth=True,
) as response:
print(response.headers.get("X-My-Header"))
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index f41b46a..30888da 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import typing_extensions
from typing import Mapping, Iterable, cast
import httpx
@@ -161,7 +162,7 @@ def create(
kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
view.
- persistence: Optional persistence configuration for the browser session.
+ persistence: DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead.
profile: Profile selection for the browser session. Provide either id or name. If
specified, the matching profile will be loaded into the browser session.
@@ -174,11 +175,10 @@ def create(
mechanisms.
timeout_seconds: The number of seconds of inactivity before the browser session is terminated.
- Only applicable to non-persistent browsers. Activity includes CDP connections
- and live view connections. Defaults to 60 seconds. Minimum allowed is 10
- seconds. Maximum allowed is 259200 (72 hours). We check for inactivity every 5
- seconds, so the actual timeout behavior you will see is +/- 5 seconds around the
- specified value.
+ Activity includes CDP connections and live view connections. Defaults to 60
+ seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We
+ check for inactivity every 5 seconds, so the actual timeout behavior you will
+ see is +/- 5 seconds around the specified value.
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
image defaults apply (commonly 1024x768@60). Only specific viewport
@@ -307,6 +307,7 @@ def list(
model=BrowserListResponse,
)
+ @typing_extensions.deprecated("deprecated")
def delete(
self,
*,
@@ -318,8 +319,10 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Delete a persistent browser session by its persistent_id.
+ """DEPRECATED: Use DELETE /browsers/{id} instead.
+
+ Delete a persistent browser
+ session by its persistent_id.
Args:
persistent_id: Persistent browser identifier
@@ -504,7 +507,7 @@ async def create(
kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
view.
- persistence: Optional persistence configuration for the browser session.
+ persistence: DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead.
profile: Profile selection for the browser session. Provide either id or name. If
specified, the matching profile will be loaded into the browser session.
@@ -517,11 +520,10 @@ async def create(
mechanisms.
timeout_seconds: The number of seconds of inactivity before the browser session is terminated.
- Only applicable to non-persistent browsers. Activity includes CDP connections
- and live view connections. Defaults to 60 seconds. Minimum allowed is 10
- seconds. Maximum allowed is 259200 (72 hours). We check for inactivity every 5
- seconds, so the actual timeout behavior you will see is +/- 5 seconds around the
- specified value.
+ Activity includes CDP connections and live view connections. Defaults to 60
+ seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We
+ check for inactivity every 5 seconds, so the actual timeout behavior you will
+ see is +/- 5 seconds around the specified value.
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
image defaults apply (commonly 1024x768@60). Only specific viewport
@@ -650,6 +652,7 @@ def list(
model=BrowserListResponse,
)
+ @typing_extensions.deprecated("deprecated")
async def delete(
self,
*,
@@ -661,8 +664,10 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Delete a persistent browser session by its persistent_id.
+ """DEPRECATED: Use DELETE /browsers/{id} instead.
+
+ Delete a persistent browser
+ session by its persistent_id.
Args:
persistent_id: Persistent browser identifier
@@ -784,8 +789,10 @@ def __init__(self, browsers: BrowsersResource) -> None:
self.list = to_raw_response_wrapper(
browsers.list,
)
- self.delete = to_raw_response_wrapper(
- browsers.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ to_raw_response_wrapper(
+ browsers.delete, # pyright: ignore[reportDeprecated],
+ )
)
self.delete_by_id = to_raw_response_wrapper(
browsers.delete_by_id,
@@ -832,8 +839,10 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
self.list = async_to_raw_response_wrapper(
browsers.list,
)
- self.delete = async_to_raw_response_wrapper(
- browsers.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ async_to_raw_response_wrapper(
+ browsers.delete, # pyright: ignore[reportDeprecated],
+ )
)
self.delete_by_id = async_to_raw_response_wrapper(
browsers.delete_by_id,
@@ -880,8 +889,10 @@ def __init__(self, browsers: BrowsersResource) -> None:
self.list = to_streamed_response_wrapper(
browsers.list,
)
- self.delete = to_streamed_response_wrapper(
- browsers.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ browsers.delete, # pyright: ignore[reportDeprecated],
+ )
)
self.delete_by_id = to_streamed_response_wrapper(
browsers.delete_by_id,
@@ -928,8 +939,10 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
self.list = async_to_streamed_response_wrapper(
browsers.list,
)
- self.delete = async_to_streamed_response_wrapper(
- browsers.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ browsers.delete, # pyright: ignore[reportDeprecated],
+ )
)
self.delete_by_id = async_to_streamed_response_wrapper(
browsers.delete_by_id,
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index d1ff790..4a5f479 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -36,7 +36,7 @@ class BrowserCreateParams(TypedDict, total=False):
"""
persistence: BrowserPersistenceParam
- """Optional persistence configuration for the browser session."""
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
profile: BrowserProfile
"""Profile selection for the browser session.
@@ -60,11 +60,10 @@ class BrowserCreateParams(TypedDict, total=False):
timeout_seconds: int
"""The number of seconds of inactivity before the browser session is terminated.
- Only applicable to non-persistent browsers. Activity includes CDP connections
- and live view connections. Defaults to 60 seconds. Minimum allowed is 10
- seconds. Maximum allowed is 259200 (72 hours). We check for inactivity every 5
- seconds, so the actual timeout behavior you will see is +/- 5 seconds around the
- specified value.
+ Activity includes CDP connections and live view connections. Defaults to 60
+ seconds. Minimum allowed is 10 seconds. Maximum allowed is 259200 (72 hours). We
+ check for inactivity every 5 seconds, so the actual timeout behavior you will
+ see is +/- 5 seconds around the specified value.
"""
viewport: BrowserViewport
diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py
index 0a5f33d..344549e 100644
--- a/src/kernel/types/browser_create_response.py
+++ b/src/kernel/types/browser_create_response.py
@@ -43,7 +43,7 @@ class BrowserCreateResponse(BaseModel):
"""Whether the browser session is running in kiosk mode."""
persistence: Optional[BrowserPersistence] = None
- """Optional persistence configuration for the browser session."""
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
profile: Optional[Profile] = None
"""Browser profile metadata."""
diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py
index d639729..cfa2ce5 100644
--- a/src/kernel/types/browser_list_response.py
+++ b/src/kernel/types/browser_list_response.py
@@ -43,7 +43,7 @@ class BrowserListResponse(BaseModel):
"""Whether the browser session is running in kiosk mode."""
persistence: Optional[BrowserPersistence] = None
- """Optional persistence configuration for the browser session."""
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
profile: Optional[Profile] = None
"""Browser profile metadata."""
diff --git a/src/kernel/types/browser_persistence.py b/src/kernel/types/browser_persistence.py
index 9c6bfc7..5c362ee 100644
--- a/src/kernel/types/browser_persistence.py
+++ b/src/kernel/types/browser_persistence.py
@@ -7,4 +7,4 @@
class BrowserPersistence(BaseModel):
id: str
- """Unique identifier for the persistent browser session."""
+ """DEPRECATED: Unique identifier for the persistent browser session."""
diff --git a/src/kernel/types/browser_persistence_param.py b/src/kernel/types/browser_persistence_param.py
index b483291..bbd9e48 100644
--- a/src/kernel/types/browser_persistence_param.py
+++ b/src/kernel/types/browser_persistence_param.py
@@ -9,4 +9,4 @@
class BrowserPersistenceParam(TypedDict, total=False):
id: Required[str]
- """Unique identifier for the persistent browser session."""
+ """DEPRECATED: Unique identifier for the persistent browser session."""
diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py
index 76ad037..1bb69e8 100644
--- a/src/kernel/types/browser_pool_acquire_response.py
+++ b/src/kernel/types/browser_pool_acquire_response.py
@@ -43,7 +43,7 @@ class BrowserPoolAcquireResponse(BaseModel):
"""Whether the browser session is running in kiosk mode."""
persistence: Optional[BrowserPersistence] = None
- """Optional persistence configuration for the browser session."""
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
profile: Optional[Profile] = None
"""Browser profile metadata."""
diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py
index 111149b..2b49d65 100644
--- a/src/kernel/types/browser_retrieve_response.py
+++ b/src/kernel/types/browser_retrieve_response.py
@@ -43,7 +43,7 @@ class BrowserRetrieveResponse(BaseModel):
"""Whether the browser session is running in kiosk mode."""
persistence: Optional[BrowserPersistence] = None
- """Optional persistence configuration for the browser session."""
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
profile: Optional[Profile] = None
"""Browser profile metadata."""
diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py
index c87fc3d..a766656 100644
--- a/tests/api_resources/test_browsers.py
+++ b/tests/api_resources/test_browsers.py
@@ -16,6 +16,8 @@
)
from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination
+# pyright: reportDeprecated=false
+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -163,17 +165,20 @@ def test_streaming_response_list(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_delete(self, client: Kernel) -> None:
- browser = client.browsers.delete(
- persistent_id="persistent_id",
- )
+ with pytest.warns(DeprecationWarning):
+ browser = client.browsers.delete(
+ persistent_id="persistent_id",
+ )
+
assert browser is None
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_delete(self, client: Kernel) -> None:
- response = client.browsers.with_raw_response.delete(
- persistent_id="persistent_id",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.browsers.with_raw_response.delete(
+ persistent_id="persistent_id",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -183,14 +188,15 @@ def test_raw_response_delete(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_streaming_response_delete(self, client: Kernel) -> None:
- with client.browsers.with_streaming_response.delete(
- persistent_id="persistent_id",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.browsers.with_streaming_response.delete(
+ persistent_id="persistent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- browser = response.parse()
- assert browser is None
+ browser = response.parse()
+ assert browser is None
assert cast(Any, response.is_closed) is True
@@ -449,17 +455,20 @@ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_delete(self, async_client: AsyncKernel) -> None:
- browser = await async_client.browsers.delete(
- persistent_id="persistent_id",
- )
+ with pytest.warns(DeprecationWarning):
+ browser = await async_client.browsers.delete(
+ persistent_id="persistent_id",
+ )
+
assert browser is None
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
- response = await async_client.browsers.with_raw_response.delete(
- persistent_id="persistent_id",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.browsers.with_raw_response.delete(
+ persistent_id="persistent_id",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -469,14 +478,15 @@ async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncKernel) -> None:
- async with async_client.browsers.with_streaming_response.delete(
- persistent_id="persistent_id",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- browser = await response.parse()
- assert browser is None
+ with pytest.warns(DeprecationWarning):
+ async with async_client.browsers.with_streaming_response.delete(
+ persistent_id="persistent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser = await response.parse()
+ assert browser is None
assert cast(Any, response.is_closed) is True
From 2ba9e5740d0b12605de3b48af06658b8db47ba11 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 5 Dec 2025 22:56:08 +0000
Subject: [PATCH 2/8] feat: [wip] Browser pools polish pass
---
.stats.yml | 4 +-
src/kernel/resources/browser_pools.py | 44 +++++++++----------
src/kernel/resources/browsers/browsers.py | 20 ++++-----
src/kernel/types/browser_create_params.py | 2 +-
src/kernel/types/browser_create_response.py | 2 +-
src/kernel/types/browser_list_response.py | 2 +-
.../types/browser_pool_acquire_response.py | 2 +-
.../types/browser_pool_create_params.py | 2 +-
src/kernel/types/browser_pool_request.py | 2 +-
.../types/browser_pool_update_params.py | 4 +-
src/kernel/types/browser_retrieve_response.py | 2 +-
11 files changed, 43 insertions(+), 43 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index a0095f1..44c807a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 74
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-cbef7e4fef29ad40af5c767aceb762ee68811c2287f255c05d2ee44a9a9247a2.yml
-openapi_spec_hash: 467e61e072773ec9f2d49c7dd3de8c2f
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3a68acd8c46e121c66be5b4c30bb4e962967840ca0f31070905baa39635fbc2d.yml
+openapi_spec_hash: 9453963fbb01de3e0afb462b16cdf115
config_hash: 6dbe88d2ba9df1ec46cedbfdb7d00000
diff --git a/src/kernel/resources/browser_pools.py b/src/kernel/resources/browser_pools.py
index d085d51..8c480ed 100644
--- a/src/kernel/resources/browser_pools.py
+++ b/src/kernel/resources/browser_pools.py
@@ -106,11 +106,11 @@ def create(
are destroyed. Defaults to 600 seconds if not specified
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
@@ -209,7 +209,7 @@ def update(
size: Number of browsers to create in the pool
discard_all_idle: Whether to discard all idle browsers and rebuild the pool immediately. Defaults
- to true.
+ to false.
extensions: List of browser extensions to load into the session. Provide each by id or name.
@@ -236,11 +236,11 @@ def update(
are destroyed. Defaults to 600 seconds if not specified
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
@@ -540,11 +540,11 @@ async def create(
are destroyed. Defaults to 600 seconds if not specified
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
@@ -643,7 +643,7 @@ async def update(
size: Number of browsers to create in the pool
discard_all_idle: Whether to discard all idle browsers and rebuild the pool immediately. Defaults
- to true.
+ to false.
extensions: List of browser extensions to load into the session. Provide each by id or name.
@@ -670,11 +670,11 @@ async def update(
are destroyed. Defaults to 600 seconds if not specified
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 30888da..cbd1773 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -181,11 +181,11 @@ def create(
see is +/- 5 seconds around the specified value.
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
@@ -526,11 +526,11 @@ async def create(
see is +/- 5 seconds around the specified value.
viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
- image defaults apply (commonly 1024x768@60). Only specific viewport
- configurations are supported. The server will reject unsupported combinations.
- Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
- 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
- be automatically determined from the width and height if they match a supported
+ image defaults apply (1920x1080@25). Only specific viewport configurations are
+ supported. The server will reject unsupported combinations. Supported
+ resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will be
+ automatically determined from the width and height if they match a supported
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index 4a5f479..0818760 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -69,7 +69,7 @@ class BrowserCreateParams(TypedDict, total=False):
viewport: BrowserViewport
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py
index 344549e..efff854 100644
--- a/src/kernel/types/browser_create_response.py
+++ b/src/kernel/types/browser_create_response.py
@@ -54,7 +54,7 @@ class BrowserCreateResponse(BaseModel):
viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py
index cfa2ce5..3ce2648 100644
--- a/src/kernel/types/browser_list_response.py
+++ b/src/kernel/types/browser_list_response.py
@@ -54,7 +54,7 @@ class BrowserListResponse(BaseModel):
viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py
index 1bb69e8..4b70a87 100644
--- a/src/kernel/types/browser_pool_acquire_response.py
+++ b/src/kernel/types/browser_pool_acquire_response.py
@@ -54,7 +54,7 @@ class BrowserPoolAcquireResponse(BaseModel):
viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_pool_create_params.py b/src/kernel/types/browser_pool_create_params.py
index c7f87c6..6c8e815 100644
--- a/src/kernel/types/browser_pool_create_params.py
+++ b/src/kernel/types/browser_pool_create_params.py
@@ -65,7 +65,7 @@ class BrowserPoolCreateParams(TypedDict, total=False):
viewport: BrowserViewport
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_pool_request.py b/src/kernel/types/browser_pool_request.py
index c25b3a5..c54fad4 100644
--- a/src/kernel/types/browser_pool_request.py
+++ b/src/kernel/types/browser_pool_request.py
@@ -63,7 +63,7 @@ class BrowserPoolRequest(BaseModel):
viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_pool_update_params.py b/src/kernel/types/browser_pool_update_params.py
index ed9a7e8..2cd3be7 100644
--- a/src/kernel/types/browser_pool_update_params.py
+++ b/src/kernel/types/browser_pool_update_params.py
@@ -19,7 +19,7 @@ class BrowserPoolUpdateParams(TypedDict, total=False):
discard_all_idle: bool
"""Whether to discard all idle browsers and rebuild the pool immediately.
- Defaults to true.
+ Defaults to false.
"""
extensions: Iterable[BrowserExtension]
@@ -71,7 +71,7 @@ class BrowserPoolUpdateParams(TypedDict, total=False):
viewport: BrowserViewport
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py
index 2b49d65..12f58a5 100644
--- a/src/kernel/types/browser_retrieve_response.py
+++ b/src/kernel/types/browser_retrieve_response.py
@@ -54,7 +54,7 @@ class BrowserRetrieveResponse(BaseModel):
viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
- If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ If omitted, image defaults apply (1920x1080@25). Only specific viewport
configurations are supported. The server will reject unsupported combinations.
Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
From d7bd8a22d40243c043e14b899dd2006fc0d51a19 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 6 Dec 2025 02:01:10 +0000
Subject: [PATCH 3/8] =?UTF-8?q?feat:=20Enhance=20agent=20authentication=20?=
=?UTF-8?q?with=20optional=20login=20page=20URL=20and=20auth=20ch=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.stats.yml | 8 +-
api.md | 37 ++
src/kernel/_client.py | 9 +
src/kernel/resources/__init__.py | 14 +
src/kernel/resources/agents/__init__.py | 33 ++
src/kernel/resources/agents/agents.py | 102 ++++
src/kernel/resources/agents/auth/__init__.py | 33 ++
src/kernel/resources/agents/auth/auth.py | 332 +++++++++++++
.../resources/agents/auth/invocations.py | 448 ++++++++++++++++++
src/kernel/types/agents/__init__.py | 11 +
.../agents/agent_auth_discover_response.py | 28 ++
.../agents/agent_auth_invocation_response.py | 22 +
.../types/agents/agent_auth_start_response.py | 24 +
.../agents/agent_auth_submit_response.py | 34 ++
src/kernel/types/agents/auth/__init__.py | 8 +
.../agents/auth/invocation_discover_params.py | 16 +
.../agents/auth/invocation_exchange_params.py | 12 +
.../auth/invocation_exchange_response.py | 13 +
.../agents/auth/invocation_submit_params.py | 13 +
src/kernel/types/agents/auth_agent.py | 21 +
src/kernel/types/agents/auth_start_params.py | 33 ++
src/kernel/types/agents/discovered_field.py | 28 ++
tests/api_resources/agents/__init__.py | 1 +
tests/api_resources/agents/auth/__init__.py | 1 +
.../agents/auth/test_invocations.py | 421 ++++++++++++++++
tests/api_resources/agents/test_auth.py | 206 ++++++++
26 files changed, 1904 insertions(+), 4 deletions(-)
create mode 100644 src/kernel/resources/agents/__init__.py
create mode 100644 src/kernel/resources/agents/agents.py
create mode 100644 src/kernel/resources/agents/auth/__init__.py
create mode 100644 src/kernel/resources/agents/auth/auth.py
create mode 100644 src/kernel/resources/agents/auth/invocations.py
create mode 100644 src/kernel/types/agents/__init__.py
create mode 100644 src/kernel/types/agents/agent_auth_discover_response.py
create mode 100644 src/kernel/types/agents/agent_auth_invocation_response.py
create mode 100644 src/kernel/types/agents/agent_auth_start_response.py
create mode 100644 src/kernel/types/agents/agent_auth_submit_response.py
create mode 100644 src/kernel/types/agents/auth/__init__.py
create mode 100644 src/kernel/types/agents/auth/invocation_discover_params.py
create mode 100644 src/kernel/types/agents/auth/invocation_exchange_params.py
create mode 100644 src/kernel/types/agents/auth/invocation_exchange_response.py
create mode 100644 src/kernel/types/agents/auth/invocation_submit_params.py
create mode 100644 src/kernel/types/agents/auth_agent.py
create mode 100644 src/kernel/types/agents/auth_start_params.py
create mode 100644 src/kernel/types/agents/discovered_field.py
create mode 100644 tests/api_resources/agents/__init__.py
create mode 100644 tests/api_resources/agents/auth/__init__.py
create mode 100644 tests/api_resources/agents/auth/test_invocations.py
create mode 100644 tests/api_resources/agents/test_auth.py
diff --git a/.stats.yml b/.stats.yml
index 44c807a..0c474bb 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 74
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3a68acd8c46e121c66be5b4c30bb4e962967840ca0f31070905baa39635fbc2d.yml
-openapi_spec_hash: 9453963fbb01de3e0afb462b16cdf115
-config_hash: 6dbe88d2ba9df1ec46cedbfdb7d00000
+configured_endpoints: 80
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-8a37652fa586b8932466d16285359a89988505f850787f8257d0c4c7053da173.yml
+openapi_spec_hash: 042765a113f6d08109e8146b302323ec
+config_hash: 113f1e5bc3567628a5d51c70bc00969d
diff --git a/api.md b/api.md
index fe6b45c..d6384dc 100644
--- a/api.md
+++ b/api.md
@@ -280,3 +280,40 @@ Methods:
- client.browser_pools.acquire(id_or_name, \*\*params) -> BrowserPoolAcquireResponse
- client.browser_pools.flush(id_or_name) -> None
- client.browser_pools.release(id_or_name, \*\*params) -> None
+
+# Agents
+
+## Auth
+
+Types:
+
+```python
+from kernel.types.agents import (
+ AgentAuthDiscoverResponse,
+ AgentAuthInvocationResponse,
+ AgentAuthStartResponse,
+ AgentAuthSubmitResponse,
+ AuthAgent,
+ DiscoveredField,
+)
+```
+
+Methods:
+
+- client.agents.auth.retrieve(id) -> AuthAgent
+- client.agents.auth.start(\*\*params) -> AgentAuthStartResponse
+
+### Invocations
+
+Types:
+
+```python
+from kernel.types.agents.auth import InvocationExchangeResponse
+```
+
+Methods:
+
+- client.agents.auth.invocations.retrieve(invocation_id) -> AgentAuthInvocationResponse
+- client.agents.auth.invocations.discover(invocation_id, \*\*params) -> AgentAuthDiscoverResponse
+- client.agents.auth.invocations.exchange(invocation_id, \*\*params) -> InvocationExchangeResponse
+- client.agents.auth.invocations.submit(invocation_id, \*\*params) -> AgentAuthSubmitResponse
diff --git a/src/kernel/_client.py b/src/kernel/_client.py
index 37ba489..c941be7 100644
--- a/src/kernel/_client.py
+++ b/src/kernel/_client.py
@@ -29,6 +29,7 @@
SyncAPIClient,
AsyncAPIClient,
)
+from .resources.agents import agents
from .resources.browsers import browsers
__all__ = [
@@ -58,6 +59,7 @@ class Kernel(SyncAPIClient):
proxies: proxies.ProxiesResource
extensions: extensions.ExtensionsResource
browser_pools: browser_pools.BrowserPoolsResource
+ agents: agents.AgentsResource
with_raw_response: KernelWithRawResponse
with_streaming_response: KernelWithStreamedResponse
@@ -147,6 +149,7 @@ def __init__(
self.proxies = proxies.ProxiesResource(self)
self.extensions = extensions.ExtensionsResource(self)
self.browser_pools = browser_pools.BrowserPoolsResource(self)
+ self.agents = agents.AgentsResource(self)
self.with_raw_response = KernelWithRawResponse(self)
self.with_streaming_response = KernelWithStreamedResponse(self)
@@ -266,6 +269,7 @@ class AsyncKernel(AsyncAPIClient):
proxies: proxies.AsyncProxiesResource
extensions: extensions.AsyncExtensionsResource
browser_pools: browser_pools.AsyncBrowserPoolsResource
+ agents: agents.AsyncAgentsResource
with_raw_response: AsyncKernelWithRawResponse
with_streaming_response: AsyncKernelWithStreamedResponse
@@ -355,6 +359,7 @@ def __init__(
self.proxies = proxies.AsyncProxiesResource(self)
self.extensions = extensions.AsyncExtensionsResource(self)
self.browser_pools = browser_pools.AsyncBrowserPoolsResource(self)
+ self.agents = agents.AsyncAgentsResource(self)
self.with_raw_response = AsyncKernelWithRawResponse(self)
self.with_streaming_response = AsyncKernelWithStreamedResponse(self)
@@ -475,6 +480,7 @@ def __init__(self, client: Kernel) -> None:
self.proxies = proxies.ProxiesResourceWithRawResponse(client.proxies)
self.extensions = extensions.ExtensionsResourceWithRawResponse(client.extensions)
self.browser_pools = browser_pools.BrowserPoolsResourceWithRawResponse(client.browser_pools)
+ self.agents = agents.AgentsResourceWithRawResponse(client.agents)
class AsyncKernelWithRawResponse:
@@ -487,6 +493,7 @@ def __init__(self, client: AsyncKernel) -> None:
self.proxies = proxies.AsyncProxiesResourceWithRawResponse(client.proxies)
self.extensions = extensions.AsyncExtensionsResourceWithRawResponse(client.extensions)
self.browser_pools = browser_pools.AsyncBrowserPoolsResourceWithRawResponse(client.browser_pools)
+ self.agents = agents.AsyncAgentsResourceWithRawResponse(client.agents)
class KernelWithStreamedResponse:
@@ -499,6 +506,7 @@ def __init__(self, client: Kernel) -> None:
self.proxies = proxies.ProxiesResourceWithStreamingResponse(client.proxies)
self.extensions = extensions.ExtensionsResourceWithStreamingResponse(client.extensions)
self.browser_pools = browser_pools.BrowserPoolsResourceWithStreamingResponse(client.browser_pools)
+ self.agents = agents.AgentsResourceWithStreamingResponse(client.agents)
class AsyncKernelWithStreamedResponse:
@@ -511,6 +519,7 @@ def __init__(self, client: AsyncKernel) -> None:
self.proxies = proxies.AsyncProxiesResourceWithStreamingResponse(client.proxies)
self.extensions = extensions.AsyncExtensionsResourceWithStreamingResponse(client.extensions)
self.browser_pools = browser_pools.AsyncBrowserPoolsResourceWithStreamingResponse(client.browser_pools)
+ self.agents = agents.AsyncAgentsResourceWithStreamingResponse(client.agents)
Client = Kernel
diff --git a/src/kernel/resources/__init__.py b/src/kernel/resources/__init__.py
index cf08046..5de2a85 100644
--- a/src/kernel/resources/__init__.py
+++ b/src/kernel/resources/__init__.py
@@ -8,6 +8,14 @@
AppsResourceWithStreamingResponse,
AsyncAppsResourceWithStreamingResponse,
)
+from .agents import (
+ AgentsResource,
+ AsyncAgentsResource,
+ AgentsResourceWithRawResponse,
+ AsyncAgentsResourceWithRawResponse,
+ AgentsResourceWithStreamingResponse,
+ AsyncAgentsResourceWithStreamingResponse,
+)
from .proxies import (
ProxiesResource,
AsyncProxiesResource,
@@ -114,4 +122,10 @@
"AsyncBrowserPoolsResourceWithRawResponse",
"BrowserPoolsResourceWithStreamingResponse",
"AsyncBrowserPoolsResourceWithStreamingResponse",
+ "AgentsResource",
+ "AsyncAgentsResource",
+ "AgentsResourceWithRawResponse",
+ "AsyncAgentsResourceWithRawResponse",
+ "AgentsResourceWithStreamingResponse",
+ "AsyncAgentsResourceWithStreamingResponse",
]
diff --git a/src/kernel/resources/agents/__init__.py b/src/kernel/resources/agents/__init__.py
new file mode 100644
index 0000000..cb159eb
--- /dev/null
+++ b/src/kernel/resources/agents/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .auth import (
+ AuthResource,
+ AsyncAuthResource,
+ AuthResourceWithRawResponse,
+ AsyncAuthResourceWithRawResponse,
+ AuthResourceWithStreamingResponse,
+ AsyncAuthResourceWithStreamingResponse,
+)
+from .agents import (
+ AgentsResource,
+ AsyncAgentsResource,
+ AgentsResourceWithRawResponse,
+ AsyncAgentsResourceWithRawResponse,
+ AgentsResourceWithStreamingResponse,
+ AsyncAgentsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "AuthResource",
+ "AsyncAuthResource",
+ "AuthResourceWithRawResponse",
+ "AsyncAuthResourceWithRawResponse",
+ "AuthResourceWithStreamingResponse",
+ "AsyncAuthResourceWithStreamingResponse",
+ "AgentsResource",
+ "AsyncAgentsResource",
+ "AgentsResourceWithRawResponse",
+ "AsyncAgentsResourceWithRawResponse",
+ "AgentsResourceWithStreamingResponse",
+ "AsyncAgentsResourceWithStreamingResponse",
+]
diff --git a/src/kernel/resources/agents/agents.py b/src/kernel/resources/agents/agents.py
new file mode 100644
index 0000000..b7bb580
--- /dev/null
+++ b/src/kernel/resources/agents/agents.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from ..._compat import cached_property
+from .auth.auth import (
+ AuthResource,
+ AsyncAuthResource,
+ AuthResourceWithRawResponse,
+ AsyncAuthResourceWithRawResponse,
+ AuthResourceWithStreamingResponse,
+ AsyncAuthResourceWithStreamingResponse,
+)
+from ..._resource import SyncAPIResource, AsyncAPIResource
+
+__all__ = ["AgentsResource", "AsyncAgentsResource"]
+
+
+class AgentsResource(SyncAPIResource):
+ @cached_property
+ def auth(self) -> AuthResource:
+ return AuthResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AgentsResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AgentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AgentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AgentsResourceWithStreamingResponse(self)
+
+
+class AsyncAgentsResource(AsyncAPIResource):
+ @cached_property
+ def auth(self) -> AsyncAuthResource:
+ return AsyncAuthResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAgentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncAgentsResourceWithStreamingResponse(self)
+
+
+class AgentsResourceWithRawResponse:
+ def __init__(self, agents: AgentsResource) -> None:
+ self._agents = agents
+
+ @cached_property
+ def auth(self) -> AuthResourceWithRawResponse:
+ return AuthResourceWithRawResponse(self._agents.auth)
+
+
+class AsyncAgentsResourceWithRawResponse:
+ def __init__(self, agents: AsyncAgentsResource) -> None:
+ self._agents = agents
+
+ @cached_property
+ def auth(self) -> AsyncAuthResourceWithRawResponse:
+ return AsyncAuthResourceWithRawResponse(self._agents.auth)
+
+
+class AgentsResourceWithStreamingResponse:
+ def __init__(self, agents: AgentsResource) -> None:
+ self._agents = agents
+
+ @cached_property
+ def auth(self) -> AuthResourceWithStreamingResponse:
+ return AuthResourceWithStreamingResponse(self._agents.auth)
+
+
+class AsyncAgentsResourceWithStreamingResponse:
+ def __init__(self, agents: AsyncAgentsResource) -> None:
+ self._agents = agents
+
+ @cached_property
+ def auth(self) -> AsyncAuthResourceWithStreamingResponse:
+ return AsyncAuthResourceWithStreamingResponse(self._agents.auth)
diff --git a/src/kernel/resources/agents/auth/__init__.py b/src/kernel/resources/agents/auth/__init__.py
new file mode 100644
index 0000000..6130549
--- /dev/null
+++ b/src/kernel/resources/agents/auth/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .auth import (
+ AuthResource,
+ AsyncAuthResource,
+ AuthResourceWithRawResponse,
+ AsyncAuthResourceWithRawResponse,
+ AuthResourceWithStreamingResponse,
+ AsyncAuthResourceWithStreamingResponse,
+)
+from .invocations import (
+ InvocationsResource,
+ AsyncInvocationsResource,
+ InvocationsResourceWithRawResponse,
+ AsyncInvocationsResourceWithRawResponse,
+ InvocationsResourceWithStreamingResponse,
+ AsyncInvocationsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "InvocationsResource",
+ "AsyncInvocationsResource",
+ "InvocationsResourceWithRawResponse",
+ "AsyncInvocationsResourceWithRawResponse",
+ "InvocationsResourceWithStreamingResponse",
+ "AsyncInvocationsResourceWithStreamingResponse",
+ "AuthResource",
+ "AsyncAuthResource",
+ "AuthResourceWithRawResponse",
+ "AsyncAuthResourceWithRawResponse",
+ "AuthResourceWithStreamingResponse",
+ "AsyncAuthResourceWithStreamingResponse",
+]
diff --git a/src/kernel/resources/agents/auth/auth.py b/src/kernel/resources/agents/auth/auth.py
new file mode 100644
index 0000000..daa8221
--- /dev/null
+++ b/src/kernel/resources/agents/auth/auth.py
@@ -0,0 +1,332 @@
+# 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 maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from .invocations import (
+ InvocationsResource,
+ AsyncInvocationsResource,
+ InvocationsResourceWithRawResponse,
+ AsyncInvocationsResourceWithRawResponse,
+ InvocationsResourceWithStreamingResponse,
+ AsyncInvocationsResourceWithStreamingResponse,
+)
+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 ...._base_client import make_request_options
+from ....types.agents import auth_start_params
+from ....types.agents.auth_agent import AuthAgent
+from ....types.agents.agent_auth_start_response import AgentAuthStartResponse
+
+__all__ = ["AuthResource", "AsyncAuthResource"]
+
+
+class AuthResource(SyncAPIResource):
+ @cached_property
+ def invocations(self) -> InvocationsResource:
+ return InvocationsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AuthResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AuthResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AuthResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AuthResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuthAgent:
+ """Retrieve an auth agent by its ID.
+
+ Returns the current authentication status of
+ the managed profile.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/agents/auth/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgent,
+ )
+
+ def start(
+ self,
+ *,
+ profile_name: str,
+ target_domain: str,
+ app_logo_url: str | Omit = omit,
+ login_url: str | Omit = omit,
+ proxy: auth_start_params.Proxy | 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,
+ ) -> AgentAuthStartResponse:
+ """Creates a browser session and returns a handoff code for the hosted flow.
+
+ Uses
+ standard API key or JWT authentication (not the JWT returned by the exchange
+ endpoint).
+
+ Args:
+ profile_name: Name of the profile to use for this flow
+
+ target_domain: Target domain for authentication
+
+ app_logo_url: Optional logo URL for the application
+
+ login_url: Optional login page URL. If provided, will be stored on the agent and used to
+ skip Phase 1 discovery in future invocations.
+
+ proxy: Optional proxy configuration
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/agents/auth/start",
+ body=maybe_transform(
+ {
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ "app_logo_url": app_logo_url,
+ "login_url": login_url,
+ "proxy": proxy,
+ },
+ auth_start_params.AuthStartParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthStartResponse,
+ )
+
+
+class AsyncAuthResource(AsyncAPIResource):
+ @cached_property
+ def invocations(self) -> AsyncInvocationsResource:
+ return AsyncInvocationsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAuthResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAuthResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAuthResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncAuthResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuthAgent:
+ """Retrieve an auth agent by its ID.
+
+ Returns the current authentication status of
+ the managed profile.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/agents/auth/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgent,
+ )
+
+ async def start(
+ self,
+ *,
+ profile_name: str,
+ target_domain: str,
+ app_logo_url: str | Omit = omit,
+ login_url: str | Omit = omit,
+ proxy: auth_start_params.Proxy | 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,
+ ) -> AgentAuthStartResponse:
+ """Creates a browser session and returns a handoff code for the hosted flow.
+
+ Uses
+ standard API key or JWT authentication (not the JWT returned by the exchange
+ endpoint).
+
+ Args:
+ profile_name: Name of the profile to use for this flow
+
+ target_domain: Target domain for authentication
+
+ app_logo_url: Optional logo URL for the application
+
+ login_url: Optional login page URL. If provided, will be stored on the agent and used to
+ skip Phase 1 discovery in future invocations.
+
+ proxy: Optional proxy configuration
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/agents/auth/start",
+ body=await async_maybe_transform(
+ {
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ "app_logo_url": app_logo_url,
+ "login_url": login_url,
+ "proxy": proxy,
+ },
+ auth_start_params.AuthStartParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthStartResponse,
+ )
+
+
+class AuthResourceWithRawResponse:
+ def __init__(self, auth: AuthResource) -> None:
+ self._auth = auth
+
+ self.retrieve = to_raw_response_wrapper(
+ auth.retrieve,
+ )
+ self.start = to_raw_response_wrapper(
+ auth.start,
+ )
+
+ @cached_property
+ def invocations(self) -> InvocationsResourceWithRawResponse:
+ return InvocationsResourceWithRawResponse(self._auth.invocations)
+
+
+class AsyncAuthResourceWithRawResponse:
+ def __init__(self, auth: AsyncAuthResource) -> None:
+ self._auth = auth
+
+ self.retrieve = async_to_raw_response_wrapper(
+ auth.retrieve,
+ )
+ self.start = async_to_raw_response_wrapper(
+ auth.start,
+ )
+
+ @cached_property
+ def invocations(self) -> AsyncInvocationsResourceWithRawResponse:
+ return AsyncInvocationsResourceWithRawResponse(self._auth.invocations)
+
+
+class AuthResourceWithStreamingResponse:
+ def __init__(self, auth: AuthResource) -> None:
+ self._auth = auth
+
+ self.retrieve = to_streamed_response_wrapper(
+ auth.retrieve,
+ )
+ self.start = to_streamed_response_wrapper(
+ auth.start,
+ )
+
+ @cached_property
+ def invocations(self) -> InvocationsResourceWithStreamingResponse:
+ return InvocationsResourceWithStreamingResponse(self._auth.invocations)
+
+
+class AsyncAuthResourceWithStreamingResponse:
+ def __init__(self, auth: AsyncAuthResource) -> None:
+ self._auth = auth
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ auth.retrieve,
+ )
+ self.start = async_to_streamed_response_wrapper(
+ auth.start,
+ )
+
+ @cached_property
+ def invocations(self) -> AsyncInvocationsResourceWithStreamingResponse:
+ return AsyncInvocationsResourceWithStreamingResponse(self._auth.invocations)
diff --git a/src/kernel/resources/agents/auth/invocations.py b/src/kernel/resources/agents/auth/invocations.py
new file mode 100644
index 0000000..15729ed
--- /dev/null
+++ b/src/kernel/resources/agents/auth/invocations.py
@@ -0,0 +1,448 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.agents.auth import invocation_submit_params, invocation_discover_params, invocation_exchange_params
+from ....types.agents.agent_auth_submit_response import AgentAuthSubmitResponse
+from ....types.agents.agent_auth_discover_response import AgentAuthDiscoverResponse
+from ....types.agents.agent_auth_invocation_response import AgentAuthInvocationResponse
+from ....types.agents.auth.invocation_exchange_response import InvocationExchangeResponse
+
+__all__ = ["InvocationsResource", "AsyncInvocationsResource"]
+
+
+class InvocationsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> InvocationsResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return InvocationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> InvocationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return InvocationsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ invocation_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentAuthInvocationResponse:
+ """Returns invocation details including app_name and target_domain.
+
+ Uses the JWT
+ returned by the exchange endpoint, or standard API key or JWT authentication.
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return self._get(
+ f"/agents/auth/invocations/{invocation_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthInvocationResponse,
+ )
+
+ def discover(
+ self,
+ invocation_id: str,
+ *,
+ login_url: 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,
+ ) -> AgentAuthDiscoverResponse:
+ """
+ Inspects the target site to detect logged-in state or discover required fields.
+ Returns 200 with success: true when fields are found, or 4xx/5xx for failures.
+ Requires the JWT returned by the exchange endpoint.
+
+ Args:
+ login_url: Optional login page URL. If provided, will override the stored login URL for
+ this discovery invocation and skip Phase 1 discovery.
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return self._post(
+ f"/agents/auth/invocations/{invocation_id}/discover",
+ body=maybe_transform({"login_url": login_url}, invocation_discover_params.InvocationDiscoverParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthDiscoverResponse,
+ )
+
+ def exchange(
+ self,
+ invocation_id: str,
+ *,
+ code: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> InvocationExchangeResponse:
+ """Validates the handoff code and returns a JWT token for subsequent requests.
+
+ No
+ authentication required (the handoff code serves as the credential).
+
+ Args:
+ code: Handoff code from start endpoint
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return self._post(
+ f"/agents/auth/invocations/{invocation_id}/exchange",
+ body=maybe_transform({"code": code}, invocation_exchange_params.InvocationExchangeParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=InvocationExchangeResponse,
+ )
+
+ def submit(
+ self,
+ invocation_id: str,
+ *,
+ field_values: Dict[str, str],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentAuthSubmitResponse:
+ """
+ Submits field values for the discovered login form and may return additional
+ auth fields or success. Requires the JWT returned by the exchange endpoint.
+
+ Args:
+ field_values: Values for the discovered login fields
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return self._post(
+ f"/agents/auth/invocations/{invocation_id}/submit",
+ body=maybe_transform({"field_values": field_values}, invocation_submit_params.InvocationSubmitParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthSubmitResponse,
+ )
+
+
+class AsyncInvocationsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncInvocationsResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncInvocationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncInvocationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncInvocationsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ invocation_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentAuthInvocationResponse:
+ """Returns invocation details including app_name and target_domain.
+
+ Uses the JWT
+ returned by the exchange endpoint, or standard API key or JWT authentication.
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return await self._get(
+ f"/agents/auth/invocations/{invocation_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthInvocationResponse,
+ )
+
+ async def discover(
+ self,
+ invocation_id: str,
+ *,
+ login_url: 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,
+ ) -> AgentAuthDiscoverResponse:
+ """
+ Inspects the target site to detect logged-in state or discover required fields.
+ Returns 200 with success: true when fields are found, or 4xx/5xx for failures.
+ Requires the JWT returned by the exchange endpoint.
+
+ Args:
+ login_url: Optional login page URL. If provided, will override the stored login URL for
+ this discovery invocation and skip Phase 1 discovery.
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return await self._post(
+ f"/agents/auth/invocations/{invocation_id}/discover",
+ body=await async_maybe_transform(
+ {"login_url": login_url}, invocation_discover_params.InvocationDiscoverParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthDiscoverResponse,
+ )
+
+ async def exchange(
+ self,
+ invocation_id: str,
+ *,
+ code: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> InvocationExchangeResponse:
+ """Validates the handoff code and returns a JWT token for subsequent requests.
+
+ No
+ authentication required (the handoff code serves as the credential).
+
+ Args:
+ code: Handoff code from start endpoint
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return await self._post(
+ f"/agents/auth/invocations/{invocation_id}/exchange",
+ body=await async_maybe_transform({"code": code}, invocation_exchange_params.InvocationExchangeParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=InvocationExchangeResponse,
+ )
+
+ async def submit(
+ self,
+ invocation_id: str,
+ *,
+ field_values: Dict[str, str],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentAuthSubmitResponse:
+ """
+ Submits field values for the discovered login form and may return additional
+ auth fields or success. Requires the JWT returned by the exchange endpoint.
+
+ Args:
+ field_values: Values for the discovered login fields
+
+ 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 invocation_id:
+ raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}")
+ return await self._post(
+ f"/agents/auth/invocations/{invocation_id}/submit",
+ body=await async_maybe_transform(
+ {"field_values": field_values}, invocation_submit_params.InvocationSubmitParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentAuthSubmitResponse,
+ )
+
+
+class InvocationsResourceWithRawResponse:
+ def __init__(self, invocations: InvocationsResource) -> None:
+ self._invocations = invocations
+
+ self.retrieve = to_raw_response_wrapper(
+ invocations.retrieve,
+ )
+ self.discover = to_raw_response_wrapper(
+ invocations.discover,
+ )
+ self.exchange = to_raw_response_wrapper(
+ invocations.exchange,
+ )
+ self.submit = to_raw_response_wrapper(
+ invocations.submit,
+ )
+
+
+class AsyncInvocationsResourceWithRawResponse:
+ def __init__(self, invocations: AsyncInvocationsResource) -> None:
+ self._invocations = invocations
+
+ self.retrieve = async_to_raw_response_wrapper(
+ invocations.retrieve,
+ )
+ self.discover = async_to_raw_response_wrapper(
+ invocations.discover,
+ )
+ self.exchange = async_to_raw_response_wrapper(
+ invocations.exchange,
+ )
+ self.submit = async_to_raw_response_wrapper(
+ invocations.submit,
+ )
+
+
+class InvocationsResourceWithStreamingResponse:
+ def __init__(self, invocations: InvocationsResource) -> None:
+ self._invocations = invocations
+
+ self.retrieve = to_streamed_response_wrapper(
+ invocations.retrieve,
+ )
+ self.discover = to_streamed_response_wrapper(
+ invocations.discover,
+ )
+ self.exchange = to_streamed_response_wrapper(
+ invocations.exchange,
+ )
+ self.submit = to_streamed_response_wrapper(
+ invocations.submit,
+ )
+
+
+class AsyncInvocationsResourceWithStreamingResponse:
+ def __init__(self, invocations: AsyncInvocationsResource) -> None:
+ self._invocations = invocations
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ invocations.retrieve,
+ )
+ self.discover = async_to_streamed_response_wrapper(
+ invocations.discover,
+ )
+ self.exchange = async_to_streamed_response_wrapper(
+ invocations.exchange,
+ )
+ self.submit = async_to_streamed_response_wrapper(
+ invocations.submit,
+ )
diff --git a/src/kernel/types/agents/__init__.py b/src/kernel/types/agents/__init__.py
new file mode 100644
index 0000000..1fdcc09
--- /dev/null
+++ b/src/kernel/types/agents/__init__.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .auth_agent import AuthAgent as AuthAgent
+from .discovered_field import DiscoveredField as DiscoveredField
+from .auth_start_params import AuthStartParams as AuthStartParams
+from .agent_auth_start_response import AgentAuthStartResponse as AgentAuthStartResponse
+from .agent_auth_submit_response import AgentAuthSubmitResponse as AgentAuthSubmitResponse
+from .agent_auth_discover_response import AgentAuthDiscoverResponse as AgentAuthDiscoverResponse
+from .agent_auth_invocation_response import AgentAuthInvocationResponse as AgentAuthInvocationResponse
diff --git a/src/kernel/types/agents/agent_auth_discover_response.py b/src/kernel/types/agents/agent_auth_discover_response.py
new file mode 100644
index 0000000..000bdec
--- /dev/null
+++ b/src/kernel/types/agents/agent_auth_discover_response.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .discovered_field import DiscoveredField
+
+__all__ = ["AgentAuthDiscoverResponse"]
+
+
+class AgentAuthDiscoverResponse(BaseModel):
+ success: bool
+ """Whether discovery succeeded"""
+
+ error_message: Optional[str] = None
+ """Error message if discovery failed"""
+
+ fields: Optional[List[DiscoveredField]] = None
+ """Discovered form fields (present when success is true)"""
+
+ logged_in: Optional[bool] = None
+ """Whether user is already logged in"""
+
+ login_url: Optional[str] = None
+ """URL of the discovered login page"""
+
+ page_title: Optional[str] = None
+ """Title of the login page"""
diff --git a/src/kernel/types/agents/agent_auth_invocation_response.py b/src/kernel/types/agents/agent_auth_invocation_response.py
new file mode 100644
index 0000000..82b5f80
--- /dev/null
+++ b/src/kernel/types/agents/agent_auth_invocation_response.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentAuthInvocationResponse"]
+
+
+class AgentAuthInvocationResponse(BaseModel):
+ app_name: str
+ """App name (org name at time of invocation creation)"""
+
+ expires_at: datetime
+ """When the handoff code expires"""
+
+ status: Literal["IN_PROGRESS", "SUCCESS", "EXPIRED", "CANCELED"]
+ """Invocation status"""
+
+ target_domain: str
+ """Target domain for authentication"""
diff --git a/src/kernel/types/agents/agent_auth_start_response.py b/src/kernel/types/agents/agent_auth_start_response.py
new file mode 100644
index 0000000..3287ba0
--- /dev/null
+++ b/src/kernel/types/agents/agent_auth_start_response.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = ["AgentAuthStartResponse"]
+
+
+class AgentAuthStartResponse(BaseModel):
+ auth_agent_id: str
+ """Unique identifier for the auth agent managing this domain/profile"""
+
+ expires_at: datetime
+ """When the handoff code expires"""
+
+ handoff_code: str
+ """One-time code for handoff"""
+
+ hosted_url: str
+ """URL to redirect user to"""
+
+ invocation_id: str
+ """Unique identifier for the invocation"""
diff --git a/src/kernel/types/agents/agent_auth_submit_response.py b/src/kernel/types/agents/agent_auth_submit_response.py
new file mode 100644
index 0000000..c57002f
--- /dev/null
+++ b/src/kernel/types/agents/agent_auth_submit_response.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .discovered_field import DiscoveredField
+
+__all__ = ["AgentAuthSubmitResponse"]
+
+
+class AgentAuthSubmitResponse(BaseModel):
+ success: bool
+ """Whether submission succeeded"""
+
+ additional_fields: Optional[List[DiscoveredField]] = None
+ """
+ Additional fields needed (e.g., OTP) - present when needs_additional_auth is
+ true
+ """
+
+ app_name: Optional[str] = None
+ """App name (only present when logged_in is true)"""
+
+ error_message: Optional[str] = None
+ """Error message if submission failed"""
+
+ logged_in: Optional[bool] = None
+ """Whether user is now logged in"""
+
+ needs_additional_auth: Optional[bool] = None
+ """Whether additional authentication fields are needed"""
+
+ target_domain: Optional[str] = None
+ """Target domain (only present when logged_in is true)"""
diff --git a/src/kernel/types/agents/auth/__init__.py b/src/kernel/types/agents/auth/__init__.py
new file mode 100644
index 0000000..bfbd280
--- /dev/null
+++ b/src/kernel/types/agents/auth/__init__.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .invocation_submit_params import InvocationSubmitParams as InvocationSubmitParams
+from .invocation_discover_params import InvocationDiscoverParams as InvocationDiscoverParams
+from .invocation_exchange_params import InvocationExchangeParams as InvocationExchangeParams
+from .invocation_exchange_response import InvocationExchangeResponse as InvocationExchangeResponse
diff --git a/src/kernel/types/agents/auth/invocation_discover_params.py b/src/kernel/types/agents/auth/invocation_discover_params.py
new file mode 100644
index 0000000..aa03f0c
--- /dev/null
+++ b/src/kernel/types/agents/auth/invocation_discover_params.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
+
+__all__ = ["InvocationDiscoverParams"]
+
+
+class InvocationDiscoverParams(TypedDict, total=False):
+ login_url: str
+ """Optional login page URL.
+
+ If provided, will override the stored login URL for this discovery invocation
+ and skip Phase 1 discovery.
+ """
diff --git a/src/kernel/types/agents/auth/invocation_exchange_params.py b/src/kernel/types/agents/auth/invocation_exchange_params.py
new file mode 100644
index 0000000..71e4d18
--- /dev/null
+++ b/src/kernel/types/agents/auth/invocation_exchange_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["InvocationExchangeParams"]
+
+
+class InvocationExchangeParams(TypedDict, total=False):
+ code: Required[str]
+ """Handoff code from start endpoint"""
diff --git a/src/kernel/types/agents/auth/invocation_exchange_response.py b/src/kernel/types/agents/auth/invocation_exchange_response.py
new file mode 100644
index 0000000..91b74ce
--- /dev/null
+++ b/src/kernel/types/agents/auth/invocation_exchange_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ...._models import BaseModel
+
+__all__ = ["InvocationExchangeResponse"]
+
+
+class InvocationExchangeResponse(BaseModel):
+ invocation_id: str
+ """Invocation ID"""
+
+ jwt: str
+ """JWT token with invocation_id claim (30 minute TTL)"""
diff --git a/src/kernel/types/agents/auth/invocation_submit_params.py b/src/kernel/types/agents/auth/invocation_submit_params.py
new file mode 100644
index 0000000..be92e7d
--- /dev/null
+++ b/src/kernel/types/agents/auth/invocation_submit_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Required, TypedDict
+
+__all__ = ["InvocationSubmitParams"]
+
+
+class InvocationSubmitParams(TypedDict, total=False):
+ field_values: Required[Dict[str, str]]
+ """Values for the discovered login fields"""
diff --git a/src/kernel/types/agents/auth_agent.py b/src/kernel/types/agents/auth_agent.py
new file mode 100644
index 0000000..8671f97
--- /dev/null
+++ b/src/kernel/types/agents/auth_agent.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AuthAgent"]
+
+
+class AuthAgent(BaseModel):
+ id: str
+ """Unique identifier for the auth agent"""
+
+ domain: str
+ """Target domain for authentication"""
+
+ profile_name: str
+ """Name of the profile associated with this auth agent"""
+
+ status: Literal["AUTHENTICATED", "NEEDS_AUTH"]
+ """Current authentication status of the managed profile"""
diff --git a/src/kernel/types/agents/auth_start_params.py b/src/kernel/types/agents/auth_start_params.py
new file mode 100644
index 0000000..9c9fb35
--- /dev/null
+++ b/src/kernel/types/agents/auth_start_params.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 Required, TypedDict
+
+__all__ = ["AuthStartParams", "Proxy"]
+
+
+class AuthStartParams(TypedDict, total=False):
+ profile_name: Required[str]
+ """Name of the profile to use for this flow"""
+
+ target_domain: Required[str]
+ """Target domain for authentication"""
+
+ app_logo_url: str
+ """Optional logo URL for the application"""
+
+ login_url: str
+ """Optional login page URL.
+
+ If provided, will be stored on the agent and used to skip Phase 1 discovery in
+ future invocations.
+ """
+
+ proxy: Proxy
+ """Optional proxy configuration"""
+
+
+class Proxy(TypedDict, total=False):
+ proxy_id: str
+ """ID of the proxy to use"""
diff --git a/src/kernel/types/agents/discovered_field.py b/src/kernel/types/agents/discovered_field.py
new file mode 100644
index 0000000..d1b9dc9
--- /dev/null
+++ b/src/kernel/types/agents/discovered_field.py
@@ -0,0 +1,28 @@
+# 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__ = ["DiscoveredField"]
+
+
+class DiscoveredField(BaseModel):
+ label: str
+ """Field label"""
+
+ name: str
+ """Field name"""
+
+ selector: str
+ """CSS selector for the field"""
+
+ type: Literal["text", "email", "password", "tel", "number", "url", "code"]
+ """Field type"""
+
+ placeholder: Optional[str] = None
+ """Field placeholder"""
+
+ required: Optional[bool] = None
+ """Whether field is required"""
diff --git a/tests/api_resources/agents/__init__.py b/tests/api_resources/agents/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/agents/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/agents/auth/__init__.py b/tests/api_resources/agents/auth/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/agents/auth/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/agents/auth/test_invocations.py b/tests/api_resources/agents/auth/test_invocations.py
new file mode 100644
index 0000000..e9c3d8f
--- /dev/null
+++ b/tests/api_resources/agents/auth/test_invocations.py
@@ -0,0 +1,421 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel.types.agents import AgentAuthSubmitResponse, AgentAuthDiscoverResponse, AgentAuthInvocationResponse
+from kernel.types.agents.auth import (
+ InvocationExchangeResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestInvocations:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.retrieve(
+ "invocation_id",
+ )
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Kernel) -> None:
+ response = client.agents.auth.invocations.with_raw_response.retrieve(
+ "invocation_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = response.parse()
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Kernel) -> None:
+ with client.agents.auth.invocations.with_streaming_response.retrieve(
+ "invocation_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = response.parse()
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ client.agents.auth.invocations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_discover(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.discover(
+ invocation_id="invocation_id",
+ )
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_discover_with_all_params(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.discover(
+ invocation_id="invocation_id",
+ login_url="https://doordash.com/account/login",
+ )
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_discover(self, client: Kernel) -> None:
+ response = client.agents.auth.invocations.with_raw_response.discover(
+ invocation_id="invocation_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = response.parse()
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_discover(self, client: Kernel) -> None:
+ with client.agents.auth.invocations.with_streaming_response.discover(
+ invocation_id="invocation_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = response.parse()
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_discover(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ client.agents.auth.invocations.with_raw_response.discover(
+ invocation_id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_exchange(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ )
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_exchange(self, client: Kernel) -> None:
+ response = client.agents.auth.invocations.with_raw_response.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = response.parse()
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_exchange(self, client: Kernel) -> None:
+ with client.agents.auth.invocations.with_streaming_response.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = response.parse()
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_exchange(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ client.agents.auth.invocations.with_raw_response.exchange(
+ invocation_id="",
+ code="abc123xyz",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_submit(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_submit(self, client: Kernel) -> None:
+ response = client.agents.auth.invocations.with_raw_response.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = response.parse()
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_submit(self, client: Kernel) -> None:
+ with client.agents.auth.invocations.with_streaming_response.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = response.parse()
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_submit(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ client.agents.auth.invocations.with_raw_response.submit(
+ invocation_id="",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
+
+
+class TestAsyncInvocations:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.retrieve(
+ "invocation_id",
+ )
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.invocations.with_raw_response.retrieve(
+ "invocation_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.invocations.with_streaming_response.retrieve(
+ "invocation_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthInvocationResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ await async_client.agents.auth.invocations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_discover(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.discover(
+ invocation_id="invocation_id",
+ )
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_discover_with_all_params(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.discover(
+ invocation_id="invocation_id",
+ login_url="https://doordash.com/account/login",
+ )
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_discover(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.invocations.with_raw_response.discover(
+ invocation_id="invocation_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_discover(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.invocations.with_streaming_response.discover(
+ invocation_id="invocation_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthDiscoverResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_discover(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ await async_client.agents.auth.invocations.with_raw_response.discover(
+ invocation_id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_exchange(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ )
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_exchange(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.invocations.with_raw_response.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = await response.parse()
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_exchange(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.invocations.with_streaming_response.exchange(
+ invocation_id="invocation_id",
+ code="abc123xyz",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = await response.parse()
+ assert_matches_type(InvocationExchangeResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_exchange(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ await async_client.agents.auth.invocations.with_raw_response.exchange(
+ invocation_id="",
+ code="abc123xyz",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_submit(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_submit(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.invocations.with_raw_response.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_submit(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.invocations.with_streaming_response.submit(
+ invocation_id="invocation_id",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = await response.parse()
+ assert_matches_type(AgentAuthSubmitResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_submit(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"):
+ await async_client.agents.auth.invocations.with_raw_response.submit(
+ invocation_id="",
+ field_values={
+ "email": "user@example.com",
+ "password": "********",
+ },
+ )
diff --git a/tests/api_resources/agents/test_auth.py b/tests/api_resources/agents/test_auth.py
new file mode 100644
index 0000000..0dc190d
--- /dev/null
+++ b/tests/api_resources/agents/test_auth.py
@@ -0,0 +1,206 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel.types.agents import AuthAgent, AgentAuthStartResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAuth:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Kernel) -> None:
+ auth = client.agents.auth.retrieve(
+ "id",
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Kernel) -> None:
+ response = client.agents.auth.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Kernel) -> None:
+ with client.agents.auth.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.agents.auth.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_start(self, client: Kernel) -> None:
+ auth = client.agents.auth.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ )
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_start_with_all_params(self, client: Kernel) -> None:
+ auth = client.agents.auth.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ app_logo_url="https://example.com/logo.png",
+ login_url="https://doordash.com/account/login",
+ proxy={"proxy_id": "proxy_id"},
+ )
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_start(self, client: Kernel) -> None:
+ response = client.agents.auth.with_raw_response.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = response.parse()
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_start(self, client: Kernel) -> None:
+ with client.agents.auth.with_streaming_response.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = response.parse()
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncAuth:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.retrieve(
+ "id",
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = await response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = await response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.agents.auth.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_start(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ )
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_start_with_all_params(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ app_logo_url="https://example.com/logo.png",
+ login_url="https://doordash.com/account/login",
+ proxy={"proxy_id": "proxy_id"},
+ )
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_start(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.with_raw_response.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = await response.parse()
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_start(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.with_streaming_response.start(
+ profile_name="auth-abc123",
+ target_domain="doordash.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = await response.parse()
+ assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
From 84a794c35f5a454b047d53b37867586f85e2536a Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 8 Dec 2025 06:55:42 +0000
Subject: [PATCH 4/8] =?UTF-8?q?feat:=20enhance=20agent=20authentication=20?=
=?UTF-8?q?API=20with=20new=20endpoints=20and=20request=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.stats.yml | 8 +-
api.md | 8 +-
src/kernel/resources/agents/auth/auth.py | 268 +++++++++++++-----
.../resources/agents/auth/invocations.py | 96 ++++++-
src/kernel/types/agents/__init__.py | 7 +-
src/kernel/types/agents/auth/__init__.py | 1 +
.../agents/auth/invocation_create_params.py | 12 +
... auth_agent_invocation_create_response.py} | 7 +-
..._start_params.py => auth_create_params.py} | 13 +-
src/kernel/types/agents/auth_list_params.py | 21 ++
.../agents/auth/test_invocations.py | 75 ++++-
tests/api_resources/agents/test_auth.py | 183 ++++++++----
12 files changed, 546 insertions(+), 153 deletions(-)
create mode 100644 src/kernel/types/agents/auth/invocation_create_params.py
rename src/kernel/types/agents/{agent_auth_start_response.py => auth_agent_invocation_create_response.py} (69%)
rename src/kernel/types/agents/{auth_start_params.py => auth_create_params.py} (61%)
create mode 100644 src/kernel/types/agents/auth_list_params.py
diff --git a/.stats.yml b/.stats.yml
index 0c474bb..4bf353a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 80
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-8a37652fa586b8932466d16285359a89988505f850787f8257d0c4c7053da173.yml
-openapi_spec_hash: 042765a113f6d08109e8146b302323ec
-config_hash: 113f1e5bc3567628a5d51c70bc00969d
+configured_endpoints: 82
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-b6957db438b01d979b62de21d4e674601b37d55b850b95a6e2b4c771aad5e840.yml
+openapi_spec_hash: 1c8aac8322bc9df8f1b82a7e7a0c692b
+config_hash: a4b4d14bdf6af723b235a6981977627c
diff --git a/api.md b/api.md
index d6384dc..2876b33 100644
--- a/api.md
+++ b/api.md
@@ -291,17 +291,20 @@ Types:
from kernel.types.agents import (
AgentAuthDiscoverResponse,
AgentAuthInvocationResponse,
- AgentAuthStartResponse,
AgentAuthSubmitResponse,
AuthAgent,
+ AuthAgentCreateRequest,
+ AuthAgentInvocationCreateRequest,
+ AuthAgentInvocationCreateResponse,
DiscoveredField,
)
```
Methods:
+- client.agents.auth.create(\*\*params) -> AuthAgent
- client.agents.auth.retrieve(id) -> AuthAgent
-- client.agents.auth.start(\*\*params) -> AgentAuthStartResponse
+- client.agents.auth.list(\*\*params) -> SyncOffsetPagination[AuthAgent]
### Invocations
@@ -313,6 +316,7 @@ from kernel.types.agents.auth import InvocationExchangeResponse
Methods:
+- client.agents.auth.invocations.create(\*\*params) -> AuthAgentInvocationCreateResponse
- client.agents.auth.invocations.retrieve(invocation_id) -> AgentAuthInvocationResponse
- client.agents.auth.invocations.discover(invocation_id, \*\*params) -> AgentAuthDiscoverResponse
- client.agents.auth.invocations.exchange(invocation_id, \*\*params) -> InvocationExchangeResponse
diff --git a/src/kernel/resources/agents/auth/auth.py b/src/kernel/resources/agents/auth/auth.py
index daa8221..b4fc758 100644
--- a/src/kernel/resources/agents/auth/auth.py
+++ b/src/kernel/resources/agents/auth/auth.py
@@ -22,10 +22,10 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ...._base_client import make_request_options
-from ....types.agents import auth_start_params
+from ....pagination import SyncOffsetPagination, AsyncOffsetPagination
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.agents import auth_list_params, auth_create_params
from ....types.agents.auth_agent import AuthAgent
-from ....types.agents.agent_auth_start_response import AgentAuthStartResponse
__all__ = ["AuthResource", "AsyncAuthResource"]
@@ -54,6 +54,61 @@ def with_streaming_response(self) -> AuthResourceWithStreamingResponse:
"""
return AuthResourceWithStreamingResponse(self)
+ def create(
+ self,
+ *,
+ profile_name: str,
+ target_domain: str,
+ login_url: str | Omit = omit,
+ proxy: auth_create_params.Proxy | 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,
+ ) -> AuthAgent:
+ """
+ Creates a new auth agent for the specified domain and profile combination, or
+ returns an existing one if it already exists. This is idempotent - calling with
+ the same domain and profile will return the same agent. Does NOT start an
+ invocation - use POST /agents/auth/invocations to start an auth flow.
+
+ Args:
+ profile_name: Name of the profile to use for this auth agent
+
+ target_domain: Target domain for authentication
+
+ login_url: Optional login page URL. If provided, will be stored on the agent and used to
+ skip discovery in future invocations.
+
+ proxy: Optional proxy configuration
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/agents/auth",
+ body=maybe_transform(
+ {
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ "login_url": login_url,
+ "proxy": proxy,
+ },
+ auth_create_params.AuthCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgent,
+ )
+
def retrieve(
self,
id: str,
@@ -89,38 +144,31 @@ def retrieve(
cast_to=AuthAgent,
)
- def start(
+ def list(
self,
*,
- profile_name: str,
- target_domain: str,
- app_logo_url: str | Omit = omit,
- login_url: str | Omit = omit,
- proxy: auth_start_params.Proxy | Omit = omit,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ profile_name: str | Omit = omit,
+ target_domain: 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,
- ) -> AgentAuthStartResponse:
- """Creates a browser session and returns a handoff code for the hosted flow.
-
- Uses
- standard API key or JWT authentication (not the JWT returned by the exchange
- endpoint).
+ ) -> SyncOffsetPagination[AuthAgent]:
+ """
+ List auth agents with optional filters for profile_name and target_domain.
Args:
- profile_name: Name of the profile to use for this flow
+ limit: Maximum number of results to return
- target_domain: Target domain for authentication
-
- app_logo_url: Optional logo URL for the application
+ offset: Number of results to skip
- login_url: Optional login page URL. If provided, will be stored on the agent and used to
- skip Phase 1 discovery in future invocations.
+ profile_name: Filter by profile name
- proxy: Optional proxy configuration
+ target_domain: Filter by target domain
extra_headers: Send extra headers
@@ -130,22 +178,25 @@ def start(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return self._post(
- "/agents/auth/start",
- body=maybe_transform(
- {
- "profile_name": profile_name,
- "target_domain": target_domain,
- "app_logo_url": app_logo_url,
- "login_url": login_url,
- "proxy": proxy,
- },
- auth_start_params.AuthStartParams,
- ),
+ return self._get_api_list(
+ "/agents/auth",
+ page=SyncOffsetPagination[AuthAgent],
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ },
+ auth_list_params.AuthListParams,
+ ),
),
- cast_to=AgentAuthStartResponse,
+ model=AuthAgent,
)
@@ -173,6 +224,61 @@ def with_streaming_response(self) -> AsyncAuthResourceWithStreamingResponse:
"""
return AsyncAuthResourceWithStreamingResponse(self)
+ async def create(
+ self,
+ *,
+ profile_name: str,
+ target_domain: str,
+ login_url: str | Omit = omit,
+ proxy: auth_create_params.Proxy | 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,
+ ) -> AuthAgent:
+ """
+ Creates a new auth agent for the specified domain and profile combination, or
+ returns an existing one if it already exists. This is idempotent - calling with
+ the same domain and profile will return the same agent. Does NOT start an
+ invocation - use POST /agents/auth/invocations to start an auth flow.
+
+ Args:
+ profile_name: Name of the profile to use for this auth agent
+
+ target_domain: Target domain for authentication
+
+ login_url: Optional login page URL. If provided, will be stored on the agent and used to
+ skip discovery in future invocations.
+
+ proxy: Optional proxy configuration
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/agents/auth",
+ body=await async_maybe_transform(
+ {
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ "login_url": login_url,
+ "proxy": proxy,
+ },
+ auth_create_params.AuthCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgent,
+ )
+
async def retrieve(
self,
id: str,
@@ -208,38 +314,31 @@ async def retrieve(
cast_to=AuthAgent,
)
- async def start(
+ def list(
self,
*,
- profile_name: str,
- target_domain: str,
- app_logo_url: str | Omit = omit,
- login_url: str | Omit = omit,
- proxy: auth_start_params.Proxy | Omit = omit,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ profile_name: str | Omit = omit,
+ target_domain: 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,
- ) -> AgentAuthStartResponse:
- """Creates a browser session and returns a handoff code for the hosted flow.
-
- Uses
- standard API key or JWT authentication (not the JWT returned by the exchange
- endpoint).
+ ) -> AsyncPaginator[AuthAgent, AsyncOffsetPagination[AuthAgent]]:
+ """
+ List auth agents with optional filters for profile_name and target_domain.
Args:
- profile_name: Name of the profile to use for this flow
+ limit: Maximum number of results to return
- target_domain: Target domain for authentication
+ offset: Number of results to skip
- app_logo_url: Optional logo URL for the application
+ profile_name: Filter by profile name
- login_url: Optional login page URL. If provided, will be stored on the agent and used to
- skip Phase 1 discovery in future invocations.
-
- proxy: Optional proxy configuration
+ target_domain: Filter by target domain
extra_headers: Send extra headers
@@ -249,22 +348,25 @@ async def start(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return await self._post(
- "/agents/auth/start",
- body=await async_maybe_transform(
- {
- "profile_name": profile_name,
- "target_domain": target_domain,
- "app_logo_url": app_logo_url,
- "login_url": login_url,
- "proxy": proxy,
- },
- auth_start_params.AuthStartParams,
- ),
+ return self._get_api_list(
+ "/agents/auth",
+ page=AsyncOffsetPagination[AuthAgent],
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ "profile_name": profile_name,
+ "target_domain": target_domain,
+ },
+ auth_list_params.AuthListParams,
+ ),
),
- cast_to=AgentAuthStartResponse,
+ model=AuthAgent,
)
@@ -272,11 +374,14 @@ class AuthResourceWithRawResponse:
def __init__(self, auth: AuthResource) -> None:
self._auth = auth
+ self.create = to_raw_response_wrapper(
+ auth.create,
+ )
self.retrieve = to_raw_response_wrapper(
auth.retrieve,
)
- self.start = to_raw_response_wrapper(
- auth.start,
+ self.list = to_raw_response_wrapper(
+ auth.list,
)
@cached_property
@@ -288,11 +393,14 @@ class AsyncAuthResourceWithRawResponse:
def __init__(self, auth: AsyncAuthResource) -> None:
self._auth = auth
+ self.create = async_to_raw_response_wrapper(
+ auth.create,
+ )
self.retrieve = async_to_raw_response_wrapper(
auth.retrieve,
)
- self.start = async_to_raw_response_wrapper(
- auth.start,
+ self.list = async_to_raw_response_wrapper(
+ auth.list,
)
@cached_property
@@ -304,11 +412,14 @@ class AuthResourceWithStreamingResponse:
def __init__(self, auth: AuthResource) -> None:
self._auth = auth
+ self.create = to_streamed_response_wrapper(
+ auth.create,
+ )
self.retrieve = to_streamed_response_wrapper(
auth.retrieve,
)
- self.start = to_streamed_response_wrapper(
- auth.start,
+ self.list = to_streamed_response_wrapper(
+ auth.list,
)
@cached_property
@@ -320,11 +431,14 @@ class AsyncAuthResourceWithStreamingResponse:
def __init__(self, auth: AsyncAuthResource) -> None:
self._auth = auth
+ self.create = async_to_streamed_response_wrapper(
+ auth.create,
+ )
self.retrieve = async_to_streamed_response_wrapper(
auth.retrieve,
)
- self.start = async_to_streamed_response_wrapper(
- auth.start,
+ self.list = async_to_streamed_response_wrapper(
+ auth.list,
)
@cached_property
diff --git a/src/kernel/resources/agents/auth/invocations.py b/src/kernel/resources/agents/auth/invocations.py
index 15729ed..361f424 100644
--- a/src/kernel/resources/agents/auth/invocations.py
+++ b/src/kernel/resources/agents/auth/invocations.py
@@ -17,11 +17,17 @@
async_to_streamed_response_wrapper,
)
from ...._base_client import make_request_options
-from ....types.agents.auth import invocation_submit_params, invocation_discover_params, invocation_exchange_params
+from ....types.agents.auth import (
+ invocation_create_params,
+ invocation_submit_params,
+ invocation_discover_params,
+ invocation_exchange_params,
+)
from ....types.agents.agent_auth_submit_response import AgentAuthSubmitResponse
from ....types.agents.agent_auth_discover_response import AgentAuthDiscoverResponse
from ....types.agents.agent_auth_invocation_response import AgentAuthInvocationResponse
from ....types.agents.auth.invocation_exchange_response import InvocationExchangeResponse
+from ....types.agents.auth_agent_invocation_create_response import AuthAgentInvocationCreateResponse
__all__ = ["InvocationsResource", "AsyncInvocationsResource"]
@@ -46,6 +52,43 @@ def with_streaming_response(self) -> InvocationsResourceWithStreamingResponse:
"""
return InvocationsResourceWithStreamingResponse(self)
+ def create(
+ self,
+ *,
+ auth_agent_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuthAgentInvocationCreateResponse:
+ """Creates a new authentication invocation for the specified auth agent.
+
+ This
+ starts the auth flow and returns a hosted URL for the user to complete
+ authentication.
+
+ Args:
+ auth_agent_id: ID of the auth agent to create an invocation for
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/agents/auth/invocations",
+ body=maybe_transform({"auth_agent_id": auth_agent_id}, invocation_create_params.InvocationCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgentInvocationCreateResponse,
+ )
+
def retrieve(
self,
invocation_id: str,
@@ -219,6 +262,45 @@ def with_streaming_response(self) -> AsyncInvocationsResourceWithStreamingRespon
"""
return AsyncInvocationsResourceWithStreamingResponse(self)
+ async def create(
+ self,
+ *,
+ auth_agent_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AuthAgentInvocationCreateResponse:
+ """Creates a new authentication invocation for the specified auth agent.
+
+ This
+ starts the auth flow and returns a hosted URL for the user to complete
+ authentication.
+
+ Args:
+ auth_agent_id: ID of the auth agent to create an invocation for
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/agents/auth/invocations",
+ body=await async_maybe_transform(
+ {"auth_agent_id": auth_agent_id}, invocation_create_params.InvocationCreateParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AuthAgentInvocationCreateResponse,
+ )
+
async def retrieve(
self,
invocation_id: str,
@@ -380,6 +462,9 @@ class InvocationsResourceWithRawResponse:
def __init__(self, invocations: InvocationsResource) -> None:
self._invocations = invocations
+ self.create = to_raw_response_wrapper(
+ invocations.create,
+ )
self.retrieve = to_raw_response_wrapper(
invocations.retrieve,
)
@@ -398,6 +483,9 @@ class AsyncInvocationsResourceWithRawResponse:
def __init__(self, invocations: AsyncInvocationsResource) -> None:
self._invocations = invocations
+ self.create = async_to_raw_response_wrapper(
+ invocations.create,
+ )
self.retrieve = async_to_raw_response_wrapper(
invocations.retrieve,
)
@@ -416,6 +504,9 @@ class InvocationsResourceWithStreamingResponse:
def __init__(self, invocations: InvocationsResource) -> None:
self._invocations = invocations
+ self.create = to_streamed_response_wrapper(
+ invocations.create,
+ )
self.retrieve = to_streamed_response_wrapper(
invocations.retrieve,
)
@@ -434,6 +525,9 @@ class AsyncInvocationsResourceWithStreamingResponse:
def __init__(self, invocations: AsyncInvocationsResource) -> None:
self._invocations = invocations
+ self.create = async_to_streamed_response_wrapper(
+ invocations.create,
+ )
self.retrieve = async_to_streamed_response_wrapper(
invocations.retrieve,
)
diff --git a/src/kernel/types/agents/__init__.py b/src/kernel/types/agents/__init__.py
index 1fdcc09..686e805 100644
--- a/src/kernel/types/agents/__init__.py
+++ b/src/kernel/types/agents/__init__.py
@@ -3,9 +3,12 @@
from __future__ import annotations
from .auth_agent import AuthAgent as AuthAgent
+from .auth_list_params import AuthListParams as AuthListParams
from .discovered_field import DiscoveredField as DiscoveredField
-from .auth_start_params import AuthStartParams as AuthStartParams
-from .agent_auth_start_response import AgentAuthStartResponse as AgentAuthStartResponse
+from .auth_create_params import AuthCreateParams as AuthCreateParams
from .agent_auth_submit_response import AgentAuthSubmitResponse as AgentAuthSubmitResponse
from .agent_auth_discover_response import AgentAuthDiscoverResponse as AgentAuthDiscoverResponse
from .agent_auth_invocation_response import AgentAuthInvocationResponse as AgentAuthInvocationResponse
+from .auth_agent_invocation_create_response import (
+ AuthAgentInvocationCreateResponse as AuthAgentInvocationCreateResponse,
+)
diff --git a/src/kernel/types/agents/auth/__init__.py b/src/kernel/types/agents/auth/__init__.py
index bfbd280..0296883 100644
--- a/src/kernel/types/agents/auth/__init__.py
+++ b/src/kernel/types/agents/auth/__init__.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from .invocation_create_params import InvocationCreateParams as InvocationCreateParams
from .invocation_submit_params import InvocationSubmitParams as InvocationSubmitParams
from .invocation_discover_params import InvocationDiscoverParams as InvocationDiscoverParams
from .invocation_exchange_params import InvocationExchangeParams as InvocationExchangeParams
diff --git a/src/kernel/types/agents/auth/invocation_create_params.py b/src/kernel/types/agents/auth/invocation_create_params.py
new file mode 100644
index 0000000..b3de645
--- /dev/null
+++ b/src/kernel/types/agents/auth/invocation_create_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["InvocationCreateParams"]
+
+
+class InvocationCreateParams(TypedDict, total=False):
+ auth_agent_id: Required[str]
+ """ID of the auth agent to create an invocation for"""
diff --git a/src/kernel/types/agents/agent_auth_start_response.py b/src/kernel/types/agents/auth_agent_invocation_create_response.py
similarity index 69%
rename from src/kernel/types/agents/agent_auth_start_response.py
rename to src/kernel/types/agents/auth_agent_invocation_create_response.py
index 3287ba0..0283c35 100644
--- a/src/kernel/types/agents/agent_auth_start_response.py
+++ b/src/kernel/types/agents/auth_agent_invocation_create_response.py
@@ -4,13 +4,10 @@
from ..._models import BaseModel
-__all__ = ["AgentAuthStartResponse"]
+__all__ = ["AuthAgentInvocationCreateResponse"]
-class AgentAuthStartResponse(BaseModel):
- auth_agent_id: str
- """Unique identifier for the auth agent managing this domain/profile"""
-
+class AuthAgentInvocationCreateResponse(BaseModel):
expires_at: datetime
"""When the handoff code expires"""
diff --git a/src/kernel/types/agents/auth_start_params.py b/src/kernel/types/agents/auth_create_params.py
similarity index 61%
rename from src/kernel/types/agents/auth_start_params.py
rename to src/kernel/types/agents/auth_create_params.py
index 9c9fb35..fe57730 100644
--- a/src/kernel/types/agents/auth_start_params.py
+++ b/src/kernel/types/agents/auth_create_params.py
@@ -4,24 +4,21 @@
from typing_extensions import Required, TypedDict
-__all__ = ["AuthStartParams", "Proxy"]
+__all__ = ["AuthCreateParams", "Proxy"]
-class AuthStartParams(TypedDict, total=False):
+class AuthCreateParams(TypedDict, total=False):
profile_name: Required[str]
- """Name of the profile to use for this flow"""
+ """Name of the profile to use for this auth agent"""
target_domain: Required[str]
"""Target domain for authentication"""
- app_logo_url: str
- """Optional logo URL for the application"""
-
login_url: str
"""Optional login page URL.
- If provided, will be stored on the agent and used to skip Phase 1 discovery in
- future invocations.
+ If provided, will be stored on the agent and used to skip discovery in future
+ invocations.
"""
proxy: Proxy
diff --git a/src/kernel/types/agents/auth_list_params.py b/src/kernel/types/agents/auth_list_params.py
new file mode 100644
index 0000000..a4b2ffc
--- /dev/null
+++ b/src/kernel/types/agents/auth_list_params.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["AuthListParams"]
+
+
+class AuthListParams(TypedDict, total=False):
+ limit: int
+ """Maximum number of results to return"""
+
+ offset: int
+ """Number of results to skip"""
+
+ profile_name: str
+ """Filter by profile name"""
+
+ target_domain: str
+ """Filter by target domain"""
diff --git a/tests/api_resources/agents/auth/test_invocations.py b/tests/api_resources/agents/auth/test_invocations.py
index e9c3d8f..7957caf 100644
--- a/tests/api_resources/agents/auth/test_invocations.py
+++ b/tests/api_resources/agents/auth/test_invocations.py
@@ -9,7 +9,12 @@
from kernel import Kernel, AsyncKernel
from tests.utils import assert_matches_type
-from kernel.types.agents import AgentAuthSubmitResponse, AgentAuthDiscoverResponse, AgentAuthInvocationResponse
+from kernel.types.agents import (
+ AgentAuthSubmitResponse,
+ AgentAuthDiscoverResponse,
+ AgentAuthInvocationResponse,
+ AuthAgentInvocationCreateResponse,
+)
from kernel.types.agents.auth import (
InvocationExchangeResponse,
)
@@ -20,6 +25,40 @@
class TestInvocations:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ invocation = client.agents.auth.invocations.create(
+ auth_agent_id="abc123xyz",
+ )
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.agents.auth.invocations.with_raw_response.create(
+ auth_agent_id="abc123xyz",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = response.parse()
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.agents.auth.invocations.with_streaming_response.create(
+ auth_agent_id="abc123xyz",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = response.parse()
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_retrieve(self, client: Kernel) -> None:
@@ -223,6 +262,40 @@ class TestAsyncInvocations:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ invocation = await async_client.agents.auth.invocations.create(
+ auth_agent_id="abc123xyz",
+ )
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.invocations.with_raw_response.create(
+ auth_agent_id="abc123xyz",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ invocation = await response.parse()
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.invocations.with_streaming_response.create(
+ auth_agent_id="abc123xyz",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ invocation = await response.parse()
+ assert_matches_type(AuthAgentInvocationCreateResponse, invocation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
diff --git a/tests/api_resources/agents/test_auth.py b/tests/api_resources/agents/test_auth.py
index 0dc190d..5115f90 100644
--- a/tests/api_resources/agents/test_auth.py
+++ b/tests/api_resources/agents/test_auth.py
@@ -9,7 +9,8 @@
from kernel import Kernel, AsyncKernel
from tests.utils import assert_matches_type
-from kernel.types.agents import AuthAgent, AgentAuthStartResponse
+from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination
+from kernel.types.agents import AuthAgent
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +18,54 @@
class TestAuth:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ auth = client.agents.auth.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Kernel) -> None:
+ auth = client.agents.auth.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ login_url="https://netflix.com/login",
+ proxy={"proxy_id": "proxy_id"},
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.agents.auth.with_raw_response.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.agents.auth.with_streaming_response.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_retrieve(self, client: Kernel) -> None:
@@ -61,50 +110,40 @@ def test_path_params_retrieve(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_start(self, client: Kernel) -> None:
- auth = client.agents.auth.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- )
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ def test_method_list(self, client: Kernel) -> None:
+ auth = client.agents.auth.list()
+ assert_matches_type(SyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_start_with_all_params(self, client: Kernel) -> None:
- auth = client.agents.auth.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- app_logo_url="https://example.com/logo.png",
- login_url="https://doordash.com/account/login",
- proxy={"proxy_id": "proxy_id"},
+ def test_method_list_with_all_params(self, client: Kernel) -> None:
+ auth = client.agents.auth.list(
+ limit=100,
+ offset=0,
+ profile_name="profile_name",
+ target_domain="target_domain",
)
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(SyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_start(self, client: Kernel) -> None:
- response = client.agents.auth.with_raw_response.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- )
+ def test_raw_response_list(self, client: Kernel) -> None:
+ response = client.agents.auth.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
auth = response.parse()
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(SyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_start(self, client: Kernel) -> None:
- with client.agents.auth.with_streaming_response.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- ) as response:
+ def test_streaming_response_list(self, client: Kernel) -> None:
+ with client.agents.auth.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
auth = response.parse()
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(SyncOffsetPagination[AuthAgent], auth, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -114,6 +153,54 @@ class TestAsyncAuth:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ login_url="https://netflix.com/login",
+ proxy={"proxy_id": "proxy_id"},
+ )
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.with_raw_response.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ auth = await response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.with_streaming_response.create(
+ profile_name="user-123",
+ target_domain="netflix.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ auth = await response.parse()
+ assert_matches_type(AuthAgent, auth, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
@@ -158,49 +245,39 @@ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_start(self, async_client: AsyncKernel) -> None:
- auth = await async_client.agents.auth.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- )
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ async def test_method_list(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.list()
+ assert_matches_type(AsyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_start_with_all_params(self, async_client: AsyncKernel) -> None:
- auth = await async_client.agents.auth.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- app_logo_url="https://example.com/logo.png",
- login_url="https://doordash.com/account/login",
- proxy={"proxy_id": "proxy_id"},
+ async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None:
+ auth = await async_client.agents.auth.list(
+ limit=100,
+ offset=0,
+ profile_name="profile_name",
+ target_domain="target_domain",
)
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(AsyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_start(self, async_client: AsyncKernel) -> None:
- response = await async_client.agents.auth.with_raw_response.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- )
+ async def test_raw_response_list(self, async_client: AsyncKernel) -> None:
+ response = await async_client.agents.auth.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
auth = await response.parse()
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(AsyncOffsetPagination[AuthAgent], auth, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_start(self, async_client: AsyncKernel) -> None:
- async with async_client.agents.auth.with_streaming_response.start(
- profile_name="auth-abc123",
- target_domain="doordash.com",
- ) as response:
+ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
+ async with async_client.agents.auth.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
auth = await response.parse()
- assert_matches_type(AgentAuthStartResponse, auth, path=["response"])
+ assert_matches_type(AsyncOffsetPagination[AuthAgent], auth, path=["response"])
assert cast(Any, response.is_closed) is True
From c8d571c785a4488549701aad525357a9fabec69f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 9 Dec 2025 03:42:24 +0000
Subject: [PATCH 5/8] fix(types): allow pyright to infer TypedDict types within
SequenceNotStr
---
src/kernel/_types.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/kernel/_types.py b/src/kernel/_types.py
index 2c1d83b..275ffbb 100644
--- a/src/kernel/_types.py
+++ b/src/kernel/_types.py
@@ -243,6 +243,9 @@ class HttpxSendArgs(TypedDict, total=False):
if TYPE_CHECKING:
# This works because str.__contains__ does not accept object (either in typeshed or at runtime)
# https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285
+ #
+ # Note: index() and count() methods are intentionally omitted to allow pyright to properly
+ # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr.
class SequenceNotStr(Protocol[_T_co]):
@overload
def __getitem__(self, index: SupportsIndex, /) -> _T_co: ...
@@ -251,8 +254,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ...
def __contains__(self, value: object, /) -> bool: ...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[_T_co]: ...
- def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ...
- def count(self, value: Any, /) -> int: ...
def __reversed__(self) -> Iterator[_T_co]: ...
else:
# just point this to a normal `Sequence` at runtime to avoid having to special case
From 75fa7cb7b954f8a68cc5eb74ad3196b350e8f9dd Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 9 Dec 2025 03:44:08 +0000
Subject: [PATCH 6/8] chore: add missing docstrings
---
.../types/agents/agent_auth_discover_response.py | 2 ++
.../types/agents/agent_auth_invocation_response.py | 2 ++
.../types/agents/agent_auth_submit_response.py | 2 ++
.../agents/auth/invocation_exchange_response.py | 2 ++
src/kernel/types/agents/auth_agent.py | 4 ++++
.../agents/auth_agent_invocation_create_response.py | 2 ++
src/kernel/types/agents/auth_create_params.py | 2 ++
src/kernel/types/agents/discovered_field.py | 2 ++
src/kernel/types/app_list_response.py | 2 ++
src/kernel/types/browser_persistence.py | 2 ++
src/kernel/types/browser_persistence_param.py | 2 ++
src/kernel/types/browser_pool.py | 2 ++
src/kernel/types/browser_pool_request.py | 5 +++++
.../computer_set_cursor_visibility_response.py | 2 ++
.../types/browsers/fs/watch_events_response.py | 2 ++
.../types/browsers/playwright_execute_response.py | 2 ++
src/kernel/types/browsers/process_exec_response.py | 2 ++
src/kernel/types/browsers/process_kill_response.py | 2 ++
src/kernel/types/browsers/process_spawn_response.py | 2 ++
src/kernel/types/browsers/process_status_response.py | 2 ++
src/kernel/types/browsers/process_stdin_response.py | 2 ++
.../types/browsers/process_stdout_stream_response.py | 2 ++
src/kernel/types/browsers/replay_list_response.py | 2 ++
src/kernel/types/browsers/replay_start_response.py | 2 ++
src/kernel/types/deployment_create_params.py | 4 ++++
src/kernel/types/deployment_create_response.py | 2 ++
src/kernel/types/deployment_follow_response.py | 2 ++
src/kernel/types/deployment_list_response.py | 2 ++
src/kernel/types/deployment_retrieve_response.py | 2 ++
src/kernel/types/deployment_state_event.py | 4 ++++
src/kernel/types/extension_list_response.py | 2 ++
src/kernel/types/extension_upload_response.py | 2 ++
src/kernel/types/invocation_state_event.py | 2 ++
src/kernel/types/profile.py | 2 ++
src/kernel/types/proxy_create_params.py | 10 ++++++++++
src/kernel/types/proxy_create_response.py | 12 ++++++++++++
src/kernel/types/proxy_list_response.py | 12 ++++++++++++
src/kernel/types/proxy_retrieve_response.py | 12 ++++++++++++
src/kernel/types/shared/app_action.py | 2 ++
src/kernel/types/shared/browser_extension.py | 5 +++++
src/kernel/types/shared/browser_profile.py | 6 ++++++
src/kernel/types/shared/browser_viewport.py | 9 +++++++++
src/kernel/types/shared/error_event.py | 2 ++
src/kernel/types/shared/heartbeat_event.py | 2 ++
src/kernel/types/shared/log_event.py | 2 ++
src/kernel/types/shared_params/browser_extension.py | 5 +++++
src/kernel/types/shared_params/browser_profile.py | 6 ++++++
src/kernel/types/shared_params/browser_viewport.py | 9 +++++++++
48 files changed, 171 insertions(+)
diff --git a/src/kernel/types/agents/agent_auth_discover_response.py b/src/kernel/types/agents/agent_auth_discover_response.py
index 000bdec..5e411dc 100644
--- a/src/kernel/types/agents/agent_auth_discover_response.py
+++ b/src/kernel/types/agents/agent_auth_discover_response.py
@@ -9,6 +9,8 @@
class AgentAuthDiscoverResponse(BaseModel):
+ """Response from discover endpoint matching AuthBlueprint schema"""
+
success: bool
"""Whether discovery succeeded"""
diff --git a/src/kernel/types/agents/agent_auth_invocation_response.py b/src/kernel/types/agents/agent_auth_invocation_response.py
index 82b5f80..02b5ecf 100644
--- a/src/kernel/types/agents/agent_auth_invocation_response.py
+++ b/src/kernel/types/agents/agent_auth_invocation_response.py
@@ -9,6 +9,8 @@
class AgentAuthInvocationResponse(BaseModel):
+ """Response from get invocation endpoint"""
+
app_name: str
"""App name (org name at time of invocation creation)"""
diff --git a/src/kernel/types/agents/agent_auth_submit_response.py b/src/kernel/types/agents/agent_auth_submit_response.py
index c57002f..5ca9578 100644
--- a/src/kernel/types/agents/agent_auth_submit_response.py
+++ b/src/kernel/types/agents/agent_auth_submit_response.py
@@ -9,6 +9,8 @@
class AgentAuthSubmitResponse(BaseModel):
+ """Response from submit endpoint matching SubmitResult schema"""
+
success: bool
"""Whether submission succeeded"""
diff --git a/src/kernel/types/agents/auth/invocation_exchange_response.py b/src/kernel/types/agents/auth/invocation_exchange_response.py
index 91b74ce..710d9c3 100644
--- a/src/kernel/types/agents/auth/invocation_exchange_response.py
+++ b/src/kernel/types/agents/auth/invocation_exchange_response.py
@@ -6,6 +6,8 @@
class InvocationExchangeResponse(BaseModel):
+ """Response from exchange endpoint"""
+
invocation_id: str
"""Invocation ID"""
diff --git a/src/kernel/types/agents/auth_agent.py b/src/kernel/types/agents/auth_agent.py
index 8671f97..ff9f5e9 100644
--- a/src/kernel/types/agents/auth_agent.py
+++ b/src/kernel/types/agents/auth_agent.py
@@ -8,6 +8,10 @@
class AuthAgent(BaseModel):
+ """
+ An auth agent that manages authentication for a specific domain and profile combination
+ """
+
id: str
"""Unique identifier for the auth agent"""
diff --git a/src/kernel/types/agents/auth_agent_invocation_create_response.py b/src/kernel/types/agents/auth_agent_invocation_create_response.py
index 0283c35..baa80e2 100644
--- a/src/kernel/types/agents/auth_agent_invocation_create_response.py
+++ b/src/kernel/types/agents/auth_agent_invocation_create_response.py
@@ -8,6 +8,8 @@
class AuthAgentInvocationCreateResponse(BaseModel):
+ """Response from creating an auth agent invocation"""
+
expires_at: datetime
"""When the handoff code expires"""
diff --git a/src/kernel/types/agents/auth_create_params.py b/src/kernel/types/agents/auth_create_params.py
index fe57730..7869925 100644
--- a/src/kernel/types/agents/auth_create_params.py
+++ b/src/kernel/types/agents/auth_create_params.py
@@ -26,5 +26,7 @@ class AuthCreateParams(TypedDict, total=False):
class Proxy(TypedDict, total=False):
+ """Optional proxy configuration"""
+
proxy_id: str
"""ID of the proxy to use"""
diff --git a/src/kernel/types/agents/discovered_field.py b/src/kernel/types/agents/discovered_field.py
index d1b9dc9..0c6715c 100644
--- a/src/kernel/types/agents/discovered_field.py
+++ b/src/kernel/types/agents/discovered_field.py
@@ -9,6 +9,8 @@
class DiscoveredField(BaseModel):
+ """A discovered form field"""
+
label: str
"""Field label"""
diff --git a/src/kernel/types/app_list_response.py b/src/kernel/types/app_list_response.py
index 56a2d4b..338f506 100644
--- a/src/kernel/types/app_list_response.py
+++ b/src/kernel/types/app_list_response.py
@@ -10,6 +10,8 @@
class AppListResponse(BaseModel):
+ """Summary of an application version."""
+
id: str
"""Unique identifier for the app version"""
diff --git a/src/kernel/types/browser_persistence.py b/src/kernel/types/browser_persistence.py
index 5c362ee..381d630 100644
--- a/src/kernel/types/browser_persistence.py
+++ b/src/kernel/types/browser_persistence.py
@@ -6,5 +6,7 @@
class BrowserPersistence(BaseModel):
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
+
id: str
"""DEPRECATED: Unique identifier for the persistent browser session."""
diff --git a/src/kernel/types/browser_persistence_param.py b/src/kernel/types/browser_persistence_param.py
index bbd9e48..6109abf 100644
--- a/src/kernel/types/browser_persistence_param.py
+++ b/src/kernel/types/browser_persistence_param.py
@@ -8,5 +8,7 @@
class BrowserPersistenceParam(TypedDict, total=False):
+ """DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles instead."""
+
id: Required[str]
"""DEPRECATED: Unique identifier for the persistent browser session."""
diff --git a/src/kernel/types/browser_pool.py b/src/kernel/types/browser_pool.py
index 5fd30dc..ddd3d9f 100644
--- a/src/kernel/types/browser_pool.py
+++ b/src/kernel/types/browser_pool.py
@@ -10,6 +10,8 @@
class BrowserPool(BaseModel):
+ """A browser pool containing multiple identically configured browsers."""
+
id: str
"""Unique identifier for the browser pool"""
diff --git a/src/kernel/types/browser_pool_request.py b/src/kernel/types/browser_pool_request.py
index c54fad4..a392b3f 100644
--- a/src/kernel/types/browser_pool_request.py
+++ b/src/kernel/types/browser_pool_request.py
@@ -11,6 +11,11 @@
class BrowserPoolRequest(BaseModel):
+ """Parameters for creating a browser pool.
+
+ All browsers in the pool will be created with the same configuration.
+ """
+
size: int
"""Number of browsers to create in the pool"""
diff --git a/src/kernel/types/browsers/computer_set_cursor_visibility_response.py b/src/kernel/types/browsers/computer_set_cursor_visibility_response.py
index c82302e..0e07023 100644
--- a/src/kernel/types/browsers/computer_set_cursor_visibility_response.py
+++ b/src/kernel/types/browsers/computer_set_cursor_visibility_response.py
@@ -6,5 +6,7 @@
class ComputerSetCursorVisibilityResponse(BaseModel):
+ """Generic OK response."""
+
ok: bool
"""Indicates success."""
diff --git a/src/kernel/types/browsers/fs/watch_events_response.py b/src/kernel/types/browsers/fs/watch_events_response.py
index 8df2f50..5778a30 100644
--- a/src/kernel/types/browsers/fs/watch_events_response.py
+++ b/src/kernel/types/browsers/fs/watch_events_response.py
@@ -9,6 +9,8 @@
class WatchEventsResponse(BaseModel):
+ """Filesystem change event."""
+
path: str
"""Absolute path of the file or directory."""
diff --git a/src/kernel/types/browsers/playwright_execute_response.py b/src/kernel/types/browsers/playwright_execute_response.py
index a805ba8..d53080d 100644
--- a/src/kernel/types/browsers/playwright_execute_response.py
+++ b/src/kernel/types/browsers/playwright_execute_response.py
@@ -8,6 +8,8 @@
class PlaywrightExecuteResponse(BaseModel):
+ """Result of Playwright code execution"""
+
success: bool
"""Whether the code executed successfully"""
diff --git a/src/kernel/types/browsers/process_exec_response.py b/src/kernel/types/browsers/process_exec_response.py
index 02588de..a5e4b77 100644
--- a/src/kernel/types/browsers/process_exec_response.py
+++ b/src/kernel/types/browsers/process_exec_response.py
@@ -8,6 +8,8 @@
class ProcessExecResponse(BaseModel):
+ """Result of a synchronous command execution."""
+
duration_ms: Optional[int] = None
"""Execution duration in milliseconds."""
diff --git a/src/kernel/types/browsers/process_kill_response.py b/src/kernel/types/browsers/process_kill_response.py
index ed128a7..6706e88 100644
--- a/src/kernel/types/browsers/process_kill_response.py
+++ b/src/kernel/types/browsers/process_kill_response.py
@@ -6,5 +6,7 @@
class ProcessKillResponse(BaseModel):
+ """Generic OK response."""
+
ok: bool
"""Indicates success."""
diff --git a/src/kernel/types/browsers/process_spawn_response.py b/src/kernel/types/browsers/process_spawn_response.py
index 23444da..0cda64d 100644
--- a/src/kernel/types/browsers/process_spawn_response.py
+++ b/src/kernel/types/browsers/process_spawn_response.py
@@ -9,6 +9,8 @@
class ProcessSpawnResponse(BaseModel):
+ """Information about a spawned process."""
+
pid: Optional[int] = None
"""OS process ID."""
diff --git a/src/kernel/types/browsers/process_status_response.py b/src/kernel/types/browsers/process_status_response.py
index 67626fe..91c7724 100644
--- a/src/kernel/types/browsers/process_status_response.py
+++ b/src/kernel/types/browsers/process_status_response.py
@@ -9,6 +9,8 @@
class ProcessStatusResponse(BaseModel):
+ """Current status of a process."""
+
cpu_pct: Optional[float] = None
"""Estimated CPU usage percentage."""
diff --git a/src/kernel/types/browsers/process_stdin_response.py b/src/kernel/types/browsers/process_stdin_response.py
index d137a96..be3c798 100644
--- a/src/kernel/types/browsers/process_stdin_response.py
+++ b/src/kernel/types/browsers/process_stdin_response.py
@@ -8,5 +8,7 @@
class ProcessStdinResponse(BaseModel):
+ """Result of writing to stdin."""
+
written_bytes: Optional[int] = None
"""Number of bytes written."""
diff --git a/src/kernel/types/browsers/process_stdout_stream_response.py b/src/kernel/types/browsers/process_stdout_stream_response.py
index 0b1d0a8..6e911f5 100644
--- a/src/kernel/types/browsers/process_stdout_stream_response.py
+++ b/src/kernel/types/browsers/process_stdout_stream_response.py
@@ -9,6 +9,8 @@
class ProcessStdoutStreamResponse(BaseModel):
+ """SSE payload representing process output or lifecycle events."""
+
data_b64: Optional[str] = None
"""Base64-encoded data from the process stream."""
diff --git a/src/kernel/types/browsers/replay_list_response.py b/src/kernel/types/browsers/replay_list_response.py
index f53dd4d..8cf9d54 100644
--- a/src/kernel/types/browsers/replay_list_response.py
+++ b/src/kernel/types/browsers/replay_list_response.py
@@ -10,6 +10,8 @@
class ReplayListResponseItem(BaseModel):
+ """Information about a browser replay recording."""
+
replay_id: str
"""Unique identifier for the replay recording."""
diff --git a/src/kernel/types/browsers/replay_start_response.py b/src/kernel/types/browsers/replay_start_response.py
index dd837d5..ac4130b 100644
--- a/src/kernel/types/browsers/replay_start_response.py
+++ b/src/kernel/types/browsers/replay_start_response.py
@@ -9,6 +9,8 @@
class ReplayStartResponse(BaseModel):
+ """Information about a browser replay recording."""
+
replay_id: str
"""Unique identifier for the replay recording."""
diff --git a/src/kernel/types/deployment_create_params.py b/src/kernel/types/deployment_create_params.py
index 16eb570..84d3d87 100644
--- a/src/kernel/types/deployment_create_params.py
+++ b/src/kernel/types/deployment_create_params.py
@@ -37,6 +37,8 @@ class DeploymentCreateParams(TypedDict, total=False):
class SourceAuth(TypedDict, total=False):
+ """Authentication for private repositories."""
+
token: Required[str]
"""GitHub PAT or installation access token"""
@@ -45,6 +47,8 @@ class SourceAuth(TypedDict, total=False):
class Source(TypedDict, total=False):
+ """Source from which to fetch application code."""
+
entrypoint: Required[str]
"""Relative path to the application entrypoint within the selected path."""
diff --git a/src/kernel/types/deployment_create_response.py b/src/kernel/types/deployment_create_response.py
index c14bf27..5746c97 100644
--- a/src/kernel/types/deployment_create_response.py
+++ b/src/kernel/types/deployment_create_response.py
@@ -10,6 +10,8 @@
class DeploymentCreateResponse(BaseModel):
+ """Deployment record information."""
+
id: str
"""Unique identifier for the deployment"""
diff --git a/src/kernel/types/deployment_follow_response.py b/src/kernel/types/deployment_follow_response.py
index ca3c512..d6de222 100644
--- a/src/kernel/types/deployment_follow_response.py
+++ b/src/kernel/types/deployment_follow_response.py
@@ -16,6 +16,8 @@
class AppVersionSummaryEvent(BaseModel):
+ """Summary of an application version."""
+
id: str
"""Unique identifier for the app version"""
diff --git a/src/kernel/types/deployment_list_response.py b/src/kernel/types/deployment_list_response.py
index d22b007..d7719d4 100644
--- a/src/kernel/types/deployment_list_response.py
+++ b/src/kernel/types/deployment_list_response.py
@@ -10,6 +10,8 @@
class DeploymentListResponse(BaseModel):
+ """Deployment record information."""
+
id: str
"""Unique identifier for the deployment"""
diff --git a/src/kernel/types/deployment_retrieve_response.py b/src/kernel/types/deployment_retrieve_response.py
index 28c0d4b..3601c86 100644
--- a/src/kernel/types/deployment_retrieve_response.py
+++ b/src/kernel/types/deployment_retrieve_response.py
@@ -10,6 +10,8 @@
class DeploymentRetrieveResponse(BaseModel):
+ """Deployment record information."""
+
id: str
"""Unique identifier for the deployment"""
diff --git a/src/kernel/types/deployment_state_event.py b/src/kernel/types/deployment_state_event.py
index 572d51b..cc221c7 100644
--- a/src/kernel/types/deployment_state_event.py
+++ b/src/kernel/types/deployment_state_event.py
@@ -10,6 +10,8 @@
class Deployment(BaseModel):
+ """Deployment record information."""
+
id: str
"""Unique identifier for the deployment"""
@@ -36,6 +38,8 @@ class Deployment(BaseModel):
class DeploymentStateEvent(BaseModel):
+ """An event representing the current state of a deployment."""
+
deployment: Deployment
"""Deployment record information."""
diff --git a/src/kernel/types/extension_list_response.py b/src/kernel/types/extension_list_response.py
index c8c99e7..79a5c99 100644
--- a/src/kernel/types/extension_list_response.py
+++ b/src/kernel/types/extension_list_response.py
@@ -10,6 +10,8 @@
class ExtensionListResponseItem(BaseModel):
+ """A browser extension uploaded to Kernel."""
+
id: str
"""Unique identifier for the extension"""
diff --git a/src/kernel/types/extension_upload_response.py b/src/kernel/types/extension_upload_response.py
index 373e886..1b3be22 100644
--- a/src/kernel/types/extension_upload_response.py
+++ b/src/kernel/types/extension_upload_response.py
@@ -9,6 +9,8 @@
class ExtensionUploadResponse(BaseModel):
+ """A browser extension uploaded to Kernel."""
+
id: str
"""Unique identifier for the extension"""
diff --git a/src/kernel/types/invocation_state_event.py b/src/kernel/types/invocation_state_event.py
index 48a2fa3..f32bf8e 100644
--- a/src/kernel/types/invocation_state_event.py
+++ b/src/kernel/types/invocation_state_event.py
@@ -51,6 +51,8 @@ class Invocation(BaseModel):
class InvocationStateEvent(BaseModel):
+ """An event representing the current state of an invocation."""
+
event: Literal["invocation_state"]
"""Event type identifier (always "invocation_state")."""
diff --git a/src/kernel/types/profile.py b/src/kernel/types/profile.py
index 3ec5890..e141aa0 100644
--- a/src/kernel/types/profile.py
+++ b/src/kernel/types/profile.py
@@ -9,6 +9,8 @@
class Profile(BaseModel):
+ """Browser profile metadata."""
+
id: str
"""Unique identifier for the profile"""
diff --git a/src/kernel/types/proxy_create_params.py b/src/kernel/types/proxy_create_params.py
index 485df60..0a3536f 100644
--- a/src/kernel/types/proxy_create_params.py
+++ b/src/kernel/types/proxy_create_params.py
@@ -35,16 +35,22 @@ class ProxyCreateParams(TypedDict, total=False):
class ConfigDatacenterProxyConfig(TypedDict, total=False):
+ """Configuration for a datacenter proxy."""
+
country: str
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigIspProxyConfig(TypedDict, total=False):
+ """Configuration for an ISP proxy."""
+
country: str
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigResidentialProxyConfig(TypedDict, total=False):
+ """Configuration for residential proxies."""
+
asn: str
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -68,6 +74,8 @@ class ConfigResidentialProxyConfig(TypedDict, total=False):
class ConfigMobileProxyConfig(TypedDict, total=False):
+ """Configuration for mobile proxies."""
+
asn: str
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -150,6 +158,8 @@ class ConfigMobileProxyConfig(TypedDict, total=False):
class ConfigCreateCustomProxyConfig(TypedDict, total=False):
+ """Configuration for a custom proxy (e.g., private proxy server)."""
+
host: Required[str]
"""Proxy host address or IP."""
diff --git a/src/kernel/types/proxy_create_response.py b/src/kernel/types/proxy_create_response.py
index 6ec2f7f..dc474ab 100644
--- a/src/kernel/types/proxy_create_response.py
+++ b/src/kernel/types/proxy_create_response.py
@@ -18,16 +18,22 @@
class ConfigDatacenterProxyConfig(BaseModel):
+ """Configuration for a datacenter proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigIspProxyConfig(BaseModel):
+ """Configuration for an ISP proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigResidentialProxyConfig(BaseModel):
+ """Configuration for residential proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -51,6 +57,8 @@ class ConfigResidentialProxyConfig(BaseModel):
class ConfigMobileProxyConfig(BaseModel):
+ """Configuration for mobile proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -135,6 +143,8 @@ class ConfigMobileProxyConfig(BaseModel):
class ConfigCustomProxyConfig(BaseModel):
+ """Configuration for a custom proxy (e.g., private proxy server)."""
+
host: str
"""Proxy host address or IP."""
@@ -158,6 +168,8 @@ class ConfigCustomProxyConfig(BaseModel):
class ProxyCreateResponse(BaseModel):
+ """Configuration for routing traffic through a proxy."""
+
type: Literal["datacenter", "isp", "residential", "mobile", "custom"]
"""Proxy type to use.
diff --git a/src/kernel/types/proxy_list_response.py b/src/kernel/types/proxy_list_response.py
index e4abb0d..08c846f 100644
--- a/src/kernel/types/proxy_list_response.py
+++ b/src/kernel/types/proxy_list_response.py
@@ -19,16 +19,22 @@
class ProxyListResponseItemConfigDatacenterProxyConfig(BaseModel):
+ """Configuration for a datacenter proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ProxyListResponseItemConfigIspProxyConfig(BaseModel):
+ """Configuration for an ISP proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ProxyListResponseItemConfigResidentialProxyConfig(BaseModel):
+ """Configuration for residential proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -52,6 +58,8 @@ class ProxyListResponseItemConfigResidentialProxyConfig(BaseModel):
class ProxyListResponseItemConfigMobileProxyConfig(BaseModel):
+ """Configuration for mobile proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -136,6 +144,8 @@ class ProxyListResponseItemConfigMobileProxyConfig(BaseModel):
class ProxyListResponseItemConfigCustomProxyConfig(BaseModel):
+ """Configuration for a custom proxy (e.g., private proxy server)."""
+
host: str
"""Proxy host address or IP."""
@@ -159,6 +169,8 @@ class ProxyListResponseItemConfigCustomProxyConfig(BaseModel):
class ProxyListResponseItem(BaseModel):
+ """Configuration for routing traffic through a proxy."""
+
type: Literal["datacenter", "isp", "residential", "mobile", "custom"]
"""Proxy type to use.
diff --git a/src/kernel/types/proxy_retrieve_response.py b/src/kernel/types/proxy_retrieve_response.py
index 5262fc4..24c7b96 100644
--- a/src/kernel/types/proxy_retrieve_response.py
+++ b/src/kernel/types/proxy_retrieve_response.py
@@ -18,16 +18,22 @@
class ConfigDatacenterProxyConfig(BaseModel):
+ """Configuration for a datacenter proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigIspProxyConfig(BaseModel):
+ """Configuration for an ISP proxy."""
+
country: Optional[str] = None
"""ISO 3166 country code. Defaults to US if not provided."""
class ConfigResidentialProxyConfig(BaseModel):
+ """Configuration for residential proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -51,6 +57,8 @@ class ConfigResidentialProxyConfig(BaseModel):
class ConfigMobileProxyConfig(BaseModel):
+ """Configuration for mobile proxies."""
+
asn: Optional[str] = None
"""Autonomous system number. See https://bgp.potaroo.net/cidr/autnums.html"""
@@ -135,6 +143,8 @@ class ConfigMobileProxyConfig(BaseModel):
class ConfigCustomProxyConfig(BaseModel):
+ """Configuration for a custom proxy (e.g., private proxy server)."""
+
host: str
"""Proxy host address or IP."""
@@ -158,6 +168,8 @@ class ConfigCustomProxyConfig(BaseModel):
class ProxyRetrieveResponse(BaseModel):
+ """Configuration for routing traffic through a proxy."""
+
type: Literal["datacenter", "isp", "residential", "mobile", "custom"]
"""Proxy type to use.
diff --git a/src/kernel/types/shared/app_action.py b/src/kernel/types/shared/app_action.py
index 3d71136..1babce1 100644
--- a/src/kernel/types/shared/app_action.py
+++ b/src/kernel/types/shared/app_action.py
@@ -6,5 +6,7 @@
class AppAction(BaseModel):
+ """An action available on the app"""
+
name: str
"""Name of the action"""
diff --git a/src/kernel/types/shared/browser_extension.py b/src/kernel/types/shared/browser_extension.py
index 7bc1a5f..a91d2dc 100644
--- a/src/kernel/types/shared/browser_extension.py
+++ b/src/kernel/types/shared/browser_extension.py
@@ -8,6 +8,11 @@
class BrowserExtension(BaseModel):
+ """Extension selection for the browser session.
+
+ Provide either id or name of an extension uploaded to Kernel.
+ """
+
id: Optional[str] = None
"""Extension ID to load for this browser session"""
diff --git a/src/kernel/types/shared/browser_profile.py b/src/kernel/types/shared/browser_profile.py
index 5f790cc..4aadc31 100644
--- a/src/kernel/types/shared/browser_profile.py
+++ b/src/kernel/types/shared/browser_profile.py
@@ -8,6 +8,12 @@
class BrowserProfile(BaseModel):
+ """Profile selection for the browser session.
+
+ Provide either id or name. If specified, the
+ matching profile will be loaded into the browser session. Profiles must be created beforehand.
+ """
+
id: Optional[str] = None
"""Profile ID to load for this browser session"""
diff --git a/src/kernel/types/shared/browser_viewport.py b/src/kernel/types/shared/browser_viewport.py
index abffcc2..ab8f427 100644
--- a/src/kernel/types/shared/browser_viewport.py
+++ b/src/kernel/types/shared/browser_viewport.py
@@ -8,6 +8,15 @@
class BrowserViewport(BaseModel):
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (1920x1080@25).
+ Only specific viewport configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25, 1024x768@60, 1200x800@60
+ If refresh_rate is not provided, it will be automatically determined from the width and height if they match a supported configuration exactly.
+ Note: Higher resolutions may affect the responsiveness of live view browser
+ """
+
height: int
"""Browser window height in pixels."""
diff --git a/src/kernel/types/shared/error_event.py b/src/kernel/types/shared/error_event.py
index 0041b89..35175f5 100644
--- a/src/kernel/types/shared/error_event.py
+++ b/src/kernel/types/shared/error_event.py
@@ -10,6 +10,8 @@
class ErrorEvent(BaseModel):
+ """An error event from the application."""
+
error: ErrorModel
event: Literal["error"]
diff --git a/src/kernel/types/shared/heartbeat_event.py b/src/kernel/types/shared/heartbeat_event.py
index d5ca811..3745e9b 100644
--- a/src/kernel/types/shared/heartbeat_event.py
+++ b/src/kernel/types/shared/heartbeat_event.py
@@ -9,6 +9,8 @@
class HeartbeatEvent(BaseModel):
+ """Heartbeat event sent periodically to keep SSE connection alive."""
+
event: Literal["sse_heartbeat"]
"""Event type identifier (always "sse_heartbeat")."""
diff --git a/src/kernel/types/shared/log_event.py b/src/kernel/types/shared/log_event.py
index 69dbc56..078b6ec 100644
--- a/src/kernel/types/shared/log_event.py
+++ b/src/kernel/types/shared/log_event.py
@@ -9,6 +9,8 @@
class LogEvent(BaseModel):
+ """A log entry from the application."""
+
event: Literal["log"]
"""Event type identifier (always "log")."""
diff --git a/src/kernel/types/shared_params/browser_extension.py b/src/kernel/types/shared_params/browser_extension.py
index d81ac70..e6c2b8f 100644
--- a/src/kernel/types/shared_params/browser_extension.py
+++ b/src/kernel/types/shared_params/browser_extension.py
@@ -8,6 +8,11 @@
class BrowserExtension(TypedDict, total=False):
+ """Extension selection for the browser session.
+
+ Provide either id or name of an extension uploaded to Kernel.
+ """
+
id: str
"""Extension ID to load for this browser session"""
diff --git a/src/kernel/types/shared_params/browser_profile.py b/src/kernel/types/shared_params/browser_profile.py
index e1027d2..51187db 100644
--- a/src/kernel/types/shared_params/browser_profile.py
+++ b/src/kernel/types/shared_params/browser_profile.py
@@ -8,6 +8,12 @@
class BrowserProfile(TypedDict, total=False):
+ """Profile selection for the browser session.
+
+ Provide either id or name. If specified, the
+ matching profile will be loaded into the browser session. Profiles must be created beforehand.
+ """
+
id: str
"""Profile ID to load for this browser session"""
diff --git a/src/kernel/types/shared_params/browser_viewport.py b/src/kernel/types/shared_params/browser_viewport.py
index b7cb2f0..9236547 100644
--- a/src/kernel/types/shared_params/browser_viewport.py
+++ b/src/kernel/types/shared_params/browser_viewport.py
@@ -8,6 +8,15 @@
class BrowserViewport(TypedDict, total=False):
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (1920x1080@25).
+ Only specific viewport configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25, 1024x768@60, 1200x800@60
+ If refresh_rate is not provided, it will be automatically determined from the width and height if they match a supported configuration exactly.
+ Note: Higher resolutions may affect the responsiveness of live view browser
+ """
+
height: Required[int]
"""Browser window height in pixels."""
From 169539a9b861b90f554b192dc8e457ad17a851a0 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 11 Dec 2025 19:34:54 +0000
Subject: [PATCH 7/8] feat: Enhance AuthAgent model with last_auth_check_at
field
---
.stats.yml | 4 ++--
src/kernel/types/agents/auth_agent.py | 5 +++++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 4bf353a..135345a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 82
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-b6957db438b01d979b62de21d4e674601b37d55b850b95a6e2b4c771aad5e840.yml
-openapi_spec_hash: 1c8aac8322bc9df8f1b82a7e7a0c692b
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-dac11bdb857e700a8c39d183e753ddd1ebaaca69fd9fc5ee57d6b56b70b00e6e.yml
+openapi_spec_hash: 78fbc50dd0b61cdc87564fbea278ee23
config_hash: a4b4d14bdf6af723b235a6981977627c
diff --git a/src/kernel/types/agents/auth_agent.py b/src/kernel/types/agents/auth_agent.py
index ff9f5e9..423b92e 100644
--- a/src/kernel/types/agents/auth_agent.py
+++ b/src/kernel/types/agents/auth_agent.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Optional
+from datetime import datetime
from typing_extensions import Literal
from ..._models import BaseModel
@@ -23,3 +25,6 @@ class AuthAgent(BaseModel):
status: Literal["AUTHENTICATED", "NEEDS_AUTH"]
"""Current authentication status of the managed profile"""
+
+ last_auth_check_at: Optional[datetime] = None
+ """When the last authentication check was performed"""
From 08a2e176116beafee50e37a832c14df7ea4568e5 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 11 Dec 2025 19:35:14 +0000
Subject: [PATCH 8/8] release: 0.23.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 26 ++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/kernel/_version.py | 2 +-
4 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index cb9d254..7f3f5c8 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.22.0"
+ ".": "0.23.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9f3aa1..f8e2118 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
# Changelog
+## 0.23.0 (2025-12-11)
+
+Full Changelog: [v0.22.0...v0.23.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.22.0...v0.23.0)
+
+### Features
+
+* [wip] Browser pools polish pass ([2ba9e57](https://github.com/onkernel/kernel-python-sdk/commit/2ba9e5740d0b12605de3b48af06658b8db47ba11))
+* enhance agent authentication API with new endpoints and request… ([84a794c](https://github.com/onkernel/kernel-python-sdk/commit/84a794c35f5a454b047d53b37867586f85e2536a))
+* Enhance agent authentication with optional login page URL and auth ch… ([d7bd8a2](https://github.com/onkernel/kernel-python-sdk/commit/d7bd8a22d40243c043e14b899dd2006fc0d51a19))
+* Enhance AuthAgent model with last_auth_check_at field ([169539a](https://github.com/onkernel/kernel-python-sdk/commit/169539a9b861b90f554b192dc8e457ad17a851a0))
+
+
+### Bug Fixes
+
+* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([c8d571c](https://github.com/onkernel/kernel-python-sdk/commit/c8d571c785a4488549701aad525357a9fabec69f))
+
+
+### Chores
+
+* add missing docstrings ([75fa7cb](https://github.com/onkernel/kernel-python-sdk/commit/75fa7cb7b954f8a68cc5eb74ad3196b350e8f9dd))
+
+
+### Refactors
+
+* **browser:** remove persistence option UI ([57af2e1](https://github.com/onkernel/kernel-python-sdk/commit/57af2e181b716145ff3e11a0d74c04dd332f9e35))
+
## 0.22.0 (2025-12-05)
Full Changelog: [v0.21.0...v0.22.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.21.0...v0.22.0)
diff --git a/pyproject.toml b/pyproject.toml
index facf999..6e76a58 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.22.0"
+version = "0.23.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 c911504..6e51841 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.22.0" # x-release-please-version
+__version__ = "0.23.0" # x-release-please-version