From 457e6c14bd10df055044c2b48704b64d90ca8aff Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Fri, 16 Oct 2026 20:06:51 +0000
Subject: [PATCH 1/5] feat: chore(stlc): seal custom-code tracking files
Stainless-Generated-From: f9646cbea73c1147aefb46dce6e811b9683125e0
---
api.md | 8 +-
src/kernel/resources/proxies.py | 86 ++++++++++---------
.../types/vaults/card_vault_item_spec.py | 7 +-
.../vaults/card_vault_item_spec_param.py | 7 +-
tests/api_resources/test_proxies.py | 76 ++++++++--------
5 files changed, 88 insertions(+), 96 deletions(-)
diff --git a/api.md b/api.md
index 33b17d60..882ac3a8 100644
--- a/api.md
+++ b/api.md
@@ -484,11 +484,11 @@ from kernel.types import (
Methods:
- client.proxies.create(\*\*params) -> ProxyCreateResponse
-- client.proxies.retrieve(id) -> ProxyRetrieveResponse
-- client.proxies.update(id, \*\*params) -> ProxyUpdateResponse
+- client.proxies.retrieve(id_or_name) -> ProxyRetrieveResponse
+- client.proxies.update(id_or_name, \*\*params) -> ProxyUpdateResponse
- client.proxies.list(\*\*params) -> SyncOffsetPagination[ProxyListResponse]
-- client.proxies.delete(id) -> None
-- client.proxies.check(id, \*\*params) -> ProxyCheckResponse
+- client.proxies.delete(id_or_name) -> None
+- client.proxies.check(id_or_name, \*\*params) -> ProxyCheckResponse
# Extensions
diff --git a/src/kernel/resources/proxies.py b/src/kernel/resources/proxies.py
index 240d18d1..4b0d3458 100644
--- a/src/kernel/resources/proxies.py
+++ b/src/kernel/resources/proxies.py
@@ -105,7 +105,7 @@ def create(
def retrieve(
self,
- id: str,
+ id_or_name: 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.
@@ -115,7 +115,10 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ProxyRetrieveResponse:
"""
- Retrieve a proxy in the resolved project by ID.
+ Retrieve a proxy in the resolved project by ID or exact name using the database
+ collation. IDs take precedence when a value matches both an ID and a name. Proxy
+ names are not unique; an ambiguous name returns a 400 response with code
+ ambiguous_proxy_name.
Args:
extra_headers: Send extra headers
@@ -126,10 +129,10 @@ def retrieve(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return self._get(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -138,7 +141,7 @@ def retrieve(
def update(
self,
- id: str,
+ id_or_name: str,
*,
name: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -150,10 +153,8 @@ def update(
) -> ProxyUpdateResponse:
"""Update a proxy's name.
- Proxy names are not unique and are not ID-or-name
- addressable on this endpoint; duplicate names are allowed. Name-based
- session-create lookups can remain ambiguous until callers resolve proxies by ID
- or the API adds a stronger uniqueness contract.
+ Proxy names are not unique and duplicate names are
+ allowed.
Args:
name: New proxy name. Proxy names are trimmed and length-checked only; duplicates are
@@ -167,10 +168,10 @@ def update(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return self._patch(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
body=maybe_transform({"name": name}, proxy_update_params.ProxyUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -238,7 +239,7 @@ def list(
def delete(
self,
- id: str,
+ id_or_name: 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.
@@ -263,11 +264,11 @@ def delete(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -276,7 +277,7 @@ def delete(
def check(
self,
- id: str,
+ id_or_name: str,
*,
url: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -317,10 +318,10 @@ def check(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return self._post(
- path_template("/proxies/{id}/check", id=id),
+ path_template("/proxies/{id_or_name}/check", id_or_name=id_or_name),
body=maybe_transform({"url": url}, proxy_check_params.ProxyCheckParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -406,7 +407,7 @@ async def create(
async def retrieve(
self,
- id: str,
+ id_or_name: 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.
@@ -416,7 +417,10 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ProxyRetrieveResponse:
"""
- Retrieve a proxy in the resolved project by ID.
+ Retrieve a proxy in the resolved project by ID or exact name using the database
+ collation. IDs take precedence when a value matches both an ID and a name. Proxy
+ names are not unique; an ambiguous name returns a 400 response with code
+ ambiguous_proxy_name.
Args:
extra_headers: Send extra headers
@@ -427,10 +431,10 @@ async def retrieve(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return await self._get(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -439,7 +443,7 @@ async def retrieve(
async def update(
self,
- id: str,
+ id_or_name: str,
*,
name: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -451,10 +455,8 @@ async def update(
) -> ProxyUpdateResponse:
"""Update a proxy's name.
- Proxy names are not unique and are not ID-or-name
- addressable on this endpoint; duplicate names are allowed. Name-based
- session-create lookups can remain ambiguous until callers resolve proxies by ID
- or the API adds a stronger uniqueness contract.
+ Proxy names are not unique and duplicate names are
+ allowed.
Args:
name: New proxy name. Proxy names are trimmed and length-checked only; duplicates are
@@ -468,10 +470,10 @@ async def update(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return await self._patch(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
body=await async_maybe_transform({"name": name}, proxy_update_params.ProxyUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -539,7 +541,7 @@ def list(
async def delete(
self,
- id: str,
+ id_or_name: 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.
@@ -564,11 +566,11 @@ async def delete(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- path_template("/proxies/{id}", id=id),
+ path_template("/proxies/{id_or_name}", id_or_name=id_or_name),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -577,7 +579,7 @@ async def delete(
async def check(
self,
- id: str,
+ id_or_name: str,
*,
url: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -618,10 +620,10 @@ async def check(
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}")
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
return await self._post(
- path_template("/proxies/{id}/check", id=id),
+ path_template("/proxies/{id_or_name}/check", id_or_name=id_or_name),
body=await async_maybe_transform({"url": url}, proxy_check_params.ProxyCheckParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
diff --git a/src/kernel/types/vaults/card_vault_item_spec.py b/src/kernel/types/vaults/card_vault_item_spec.py
index f6b471f8..749bf1d4 100644
--- a/src/kernel/types/vaults/card_vault_item_spec.py
+++ b/src/kernel/types/vaults/card_vault_item_spec.py
@@ -63,12 +63,7 @@ class LinkCardVaultItemSpec(BaseModel):
"""
amount: int
- """Integer amount in minor currency units.
-
- Link Pay Token execution permits at most 500000; virtual-card execution permits
- at most 50000. Authorization rejects a larger amount when the checkout requires
- a virtual card.
- """
+ """Integer amount in minor currency units, at most 50000."""
context: str
diff --git a/src/kernel/types/vaults/card_vault_item_spec_param.py b/src/kernel/types/vaults/card_vault_item_spec_param.py
index 6e3ad521..c888247f 100644
--- a/src/kernel/types/vaults/card_vault_item_spec_param.py
+++ b/src/kernel/types/vaults/card_vault_item_spec_param.py
@@ -63,12 +63,7 @@ class LinkCardVaultItemSpec(TypedDict, total=False):
"""
amount: Required[int]
- """Integer amount in minor currency units.
-
- Link Pay Token execution permits at most 500000; virtual-card execution permits
- at most 50000. Authorization rejects a larger amount when the checkout requires
- a virtual card.
- """
+ """Integer amount in minor currency units, at most 50000."""
context: Required[str]
diff --git a/tests/api_resources/test_proxies.py b/tests/api_resources/test_proxies.py
index 7fc6412c..a8737804 100644
--- a/tests/api_resources/test_proxies.py
+++ b/tests/api_resources/test_proxies.py
@@ -74,7 +74,7 @@ def test_streaming_response_create(self, client: Kernel) -> None:
@parametrize
def test_method_retrieve(self, client: Kernel) -> None:
proxy = client.proxies.retrieve(
- "id",
+ "id_or_name",
)
assert_matches_type(ProxyRetrieveResponse, proxy, path=["response"])
@@ -82,7 +82,7 @@ def test_method_retrieve(self, client: Kernel) -> None:
@parametrize
def test_raw_response_retrieve(self, client: Kernel) -> None:
response = client.proxies.with_raw_response.retrieve(
- "id",
+ "id_or_name",
)
assert response.is_closed is True
@@ -94,7 +94,7 @@ def test_raw_response_retrieve(self, client: Kernel) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Kernel) -> None:
with client.proxies.with_streaming_response.retrieve(
- "id",
+ "id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -107,7 +107,7 @@ def test_streaming_response_retrieve(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_path_params_retrieve(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
client.proxies.with_raw_response.retrieve(
"",
)
@@ -116,7 +116,7 @@ def test_path_params_retrieve(self, client: Kernel) -> None:
@parametrize
def test_method_update(self, client: Kernel) -> None:
proxy = client.proxies.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
)
assert_matches_type(ProxyUpdateResponse, proxy, path=["response"])
@@ -125,7 +125,7 @@ def test_method_update(self, client: Kernel) -> None:
@parametrize
def test_raw_response_update(self, client: Kernel) -> None:
response = client.proxies.with_raw_response.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
)
@@ -138,7 +138,7 @@ def test_raw_response_update(self, client: Kernel) -> None:
@parametrize
def test_streaming_response_update(self, client: Kernel) -> None:
with client.proxies.with_streaming_response.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
) as response:
assert not response.is_closed
@@ -152,9 +152,9 @@ def test_streaming_response_update(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_path_params_update(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
client.proxies.with_raw_response.update(
- id="",
+ id_or_name="",
name="my-renamed-proxy",
)
@@ -201,7 +201,7 @@ def test_streaming_response_list(self, client: Kernel) -> None:
@parametrize
def test_method_delete(self, client: Kernel) -> None:
proxy = client.proxies.delete(
- "id",
+ "id_or_name",
)
assert proxy is None
@@ -209,7 +209,7 @@ def test_method_delete(self, client: Kernel) -> None:
@parametrize
def test_raw_response_delete(self, client: Kernel) -> None:
response = client.proxies.with_raw_response.delete(
- "id",
+ "id_or_name",
)
assert response.is_closed is True
@@ -221,7 +221,7 @@ def test_raw_response_delete(self, client: Kernel) -> None:
@parametrize
def test_streaming_response_delete(self, client: Kernel) -> None:
with client.proxies.with_streaming_response.delete(
- "id",
+ "id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -234,7 +234,7 @@ def test_streaming_response_delete(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_path_params_delete(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
client.proxies.with_raw_response.delete(
"",
)
@@ -243,7 +243,7 @@ def test_path_params_delete(self, client: Kernel) -> None:
@parametrize
def test_method_check(self, client: Kernel) -> None:
proxy = client.proxies.check(
- id="id",
+ id_or_name="id_or_name",
)
assert_matches_type(ProxyCheckResponse, proxy, path=["response"])
@@ -251,7 +251,7 @@ def test_method_check(self, client: Kernel) -> None:
@parametrize
def test_method_check_with_all_params(self, client: Kernel) -> None:
proxy = client.proxies.check(
- id="id",
+ id_or_name="id_or_name",
url="https://example.com",
)
assert_matches_type(ProxyCheckResponse, proxy, path=["response"])
@@ -260,7 +260,7 @@ def test_method_check_with_all_params(self, client: Kernel) -> None:
@parametrize
def test_raw_response_check(self, client: Kernel) -> None:
response = client.proxies.with_raw_response.check(
- id="id",
+ id_or_name="id_or_name",
)
assert response.is_closed is True
@@ -272,7 +272,7 @@ def test_raw_response_check(self, client: Kernel) -> None:
@parametrize
def test_streaming_response_check(self, client: Kernel) -> None:
with client.proxies.with_streaming_response.check(
- id="id",
+ id_or_name="id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -285,9 +285,9 @@ def test_streaming_response_check(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_path_params_check(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
client.proxies.with_raw_response.check(
- id="",
+ id_or_name="",
)
@@ -346,7 +346,7 @@ async def test_streaming_response_create(self, async_client: AsyncKernel) -> Non
@parametrize
async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
proxy = await async_client.proxies.retrieve(
- "id",
+ "id_or_name",
)
assert_matches_type(ProxyRetrieveResponse, proxy, path=["response"])
@@ -354,7 +354,7 @@ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
response = await async_client.proxies.with_raw_response.retrieve(
- "id",
+ "id_or_name",
)
assert response.is_closed is True
@@ -366,7 +366,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
async with async_client.proxies.with_streaming_response.retrieve(
- "id",
+ "id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -379,7 +379,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> N
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
await async_client.proxies.with_raw_response.retrieve(
"",
)
@@ -388,7 +388,7 @@ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_method_update(self, async_client: AsyncKernel) -> None:
proxy = await async_client.proxies.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
)
assert_matches_type(ProxyUpdateResponse, proxy, path=["response"])
@@ -397,7 +397,7 @@ async def test_method_update(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_raw_response_update(self, async_client: AsyncKernel) -> None:
response = await async_client.proxies.with_raw_response.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
)
@@ -410,7 +410,7 @@ async def test_raw_response_update(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_streaming_response_update(self, async_client: AsyncKernel) -> None:
async with async_client.proxies.with_streaming_response.update(
- id="id",
+ id_or_name="id_or_name",
name="my-renamed-proxy",
) as response:
assert not response.is_closed
@@ -424,9 +424,9 @@ async def test_streaming_response_update(self, async_client: AsyncKernel) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_path_params_update(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
await async_client.proxies.with_raw_response.update(
- id="",
+ id_or_name="",
name="my-renamed-proxy",
)
@@ -473,7 +473,7 @@ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_method_delete(self, async_client: AsyncKernel) -> None:
proxy = await async_client.proxies.delete(
- "id",
+ "id_or_name",
)
assert proxy is None
@@ -481,7 +481,7 @@ async def test_method_delete(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
response = await async_client.proxies.with_raw_response.delete(
- "id",
+ "id_or_name",
)
assert response.is_closed is True
@@ -493,7 +493,7 @@ async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncKernel) -> None:
async with async_client.proxies.with_streaming_response.delete(
- "id",
+ "id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -506,7 +506,7 @@ async def test_streaming_response_delete(self, async_client: AsyncKernel) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
await async_client.proxies.with_raw_response.delete(
"",
)
@@ -515,7 +515,7 @@ async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_method_check(self, async_client: AsyncKernel) -> None:
proxy = await async_client.proxies.check(
- id="id",
+ id_or_name="id_or_name",
)
assert_matches_type(ProxyCheckResponse, proxy, path=["response"])
@@ -523,7 +523,7 @@ async def test_method_check(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_method_check_with_all_params(self, async_client: AsyncKernel) -> None:
proxy = await async_client.proxies.check(
- id="id",
+ id_or_name="id_or_name",
url="https://example.com",
)
assert_matches_type(ProxyCheckResponse, proxy, path=["response"])
@@ -532,7 +532,7 @@ async def test_method_check_with_all_params(self, async_client: AsyncKernel) ->
@parametrize
async def test_raw_response_check(self, async_client: AsyncKernel) -> None:
response = await async_client.proxies.with_raw_response.check(
- id="id",
+ id_or_name="id_or_name",
)
assert response.is_closed is True
@@ -544,7 +544,7 @@ async def test_raw_response_check(self, async_client: AsyncKernel) -> None:
@parametrize
async def test_streaming_response_check(self, async_client: AsyncKernel) -> None:
async with async_client.proxies.with_streaming_response.check(
- id="id",
+ id_or_name="id_or_name",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -557,7 +557,7 @@ async def test_streaming_response_check(self, async_client: AsyncKernel) -> None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_path_params_check(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
await async_client.proxies.with_raw_response.check(
- id="",
+ id_or_name="",
)
From c1a6765c789f5ed7585a55780f83435cc8c5d4ee Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Mon, 19 Oct 2026 16:51:08 +0000
Subject: [PATCH 2/5] feat: Support existing-user AgentCard wallet imports
Stainless-Generated-From: 207cf9bd5926ddf327a2b6db9df5a97300e62a59
---
src/kernel/resources/vaults/items.py | 24 +++++++++++++++----
src/kernel/types/vaults/item_upsert_params.py | 14 ++++++++---
src/kernel/types/vaults/vault_item.py | 12 ++++++++--
.../vaults/vault_item_operation_response.py | 12 ++++++++--
.../types/vaults/wallet_vault_item_spec.py | 2 +-
5 files changed, 52 insertions(+), 12 deletions(-)
diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py
index 209c9e86..9829d498 100644
--- a/src/kernel/resources/vaults/items.py
+++ b/src/kernel/resources/vaults/items.py
@@ -1236,8 +1236,16 @@ def upsert(
select a customer-owned configuration. Mode (sandbox vs live) is determined by
the selected credential; there is no per-item test flag. Without user_id,
creation returns a hosted enrollment action and Kernel polls until the user
- connects. user_id may only reference a user already enrolled by a wallet in this
- organization under the same configuration.
+ connects. With a customer-owned provider_config, user_id imports an existing
+ connection after verifying access with the selected credentials in their sandbox
+ or live mode; no hosted enrollment is needed, even if the connected user has no
+ stored cards. With Kernel-managed credentials, user_id may only reference a user
+ already enrolled by a wallet in this organization under the same configuration.
+ The user and configuration bindings cannot be changed after creation. Import
+ validation is read-only and runs before creation: a failed validation leaves no
+ wallet or reserved key, so the user_id can be corrected and retried. Concurrent
+ imports of the same key may both validate, but create at most one wallet and
+ connected event.
extra_headers: Send extra headers
@@ -2609,8 +2617,16 @@ async def upsert(
select a customer-owned configuration. Mode (sandbox vs live) is determined by
the selected credential; there is no per-item test flag. Without user_id,
creation returns a hosted enrollment action and Kernel polls until the user
- connects. user_id may only reference a user already enrolled by a wallet in this
- organization under the same configuration.
+ connects. With a customer-owned provider_config, user_id imports an existing
+ connection after verifying access with the selected credentials in their sandbox
+ or live mode; no hosted enrollment is needed, even if the connected user has no
+ stored cards. With Kernel-managed credentials, user_id may only reference a user
+ already enrolled by a wallet in this organization under the same configuration.
+ The user and configuration bindings cannot be changed after creation. Import
+ validation is read-only and runs before creation: a failed validation leaves no
+ wallet or reserved key, so the user_id can be corrected and retried. Concurrent
+ imports of the same key may both validate, but create at most one wallet and
+ connected event.
extra_headers: Send extra headers
diff --git a/src/kernel/types/vaults/item_upsert_params.py b/src/kernel/types/vaults/item_upsert_params.py
index 3fa6c509..04bd3f11 100644
--- a/src/kernel/types/vaults/item_upsert_params.py
+++ b/src/kernel/types/vaults/item_upsert_params.py
@@ -40,8 +40,16 @@ class WalletVaultItemRequest(TypedDict, total=False):
customer-owned configuration. Mode (sandbox vs live) is determined by the
selected credential; there is no per-item test flag. Without user_id, creation
returns a hosted enrollment action and Kernel polls until the user connects.
- user_id may only reference a user already enrolled by a wallet in this
- organization under the same configuration.
+ With a customer-owned provider_config, user_id imports an existing connection
+ after verifying access with the selected credentials in their sandbox or live
+ mode; no hosted enrollment is needed, even if the connected user has no stored
+ cards. With Kernel-managed credentials, user_id may only reference a user
+ already enrolled by a wallet in this organization under the same configuration.
+ The user and configuration bindings cannot be changed after creation. Import
+ validation is read-only and runs before creation: a failed validation leaves no
+ wallet or reserved key, so the user_id can be corrected and retried. Concurrent
+ imports of the same key may both validate, but create at most one wallet and
+ connected event.
"""
type: Required[Literal["wallet"]]
@@ -162,7 +170,7 @@ class WalletVaultItemRequestSpecAgentCardWalletVaultItemSpecProviderConfig(Typed
class WalletVaultItemRequestSpecAgentCardWalletVaultItemSpec(TypedDict, total=False):
"""AgentCard wallet.
- Omit provider_config to use Kernel-managed credentials, or select a customer-owned configuration. Mode (sandbox vs live) is determined by the selected credential; there is no per-item test flag. Without user_id, creation returns a hosted enrollment action and Kernel polls until the user connects. user_id may only reference a user already enrolled by a wallet in this organization under the same configuration.
+ Omit provider_config to use Kernel-managed credentials, or select a customer-owned configuration. Mode (sandbox vs live) is determined by the selected credential; there is no per-item test flag. Without user_id, creation returns a hosted enrollment action and Kernel polls until the user connects. With a customer-owned provider_config, user_id imports an existing connection after verifying access with the selected credentials in their sandbox or live mode; no hosted enrollment is needed, even if the connected user has no stored cards. With Kernel-managed credentials, user_id may only reference a user already enrolled by a wallet in this organization under the same configuration. The user and configuration bindings cannot be changed after creation. Import validation is read-only and runs before creation: a failed validation leaves no wallet or reserved key, so the user_id can be corrected and retried. Concurrent imports of the same key may both validate, but create at most one wallet and connected event.
"""
provider: Required[Literal["agentcard"]]
diff --git a/src/kernel/types/vaults/vault_item.py b/src/kernel/types/vaults/vault_item.py
index 1b72d59c..45d2f021 100644
--- a/src/kernel/types/vaults/vault_item.py
+++ b/src/kernel/types/vaults/vault_item.py
@@ -86,8 +86,16 @@ class WalletVaultItem(BaseModel):
customer-owned configuration. Mode (sandbox vs live) is determined by the
selected credential; there is no per-item test flag. Without user_id, creation
returns a hosted enrollment action and Kernel polls until the user connects.
- user_id may only reference a user already enrolled by a wallet in this
- organization under the same configuration.
+ With a customer-owned provider_config, user_id imports an existing connection
+ after verifying access with the selected credentials in their sandbox or live
+ mode; no hosted enrollment is needed, even if the connected user has no stored
+ cards. With Kernel-managed credentials, user_id may only reference a user
+ already enrolled by a wallet in this organization under the same configuration.
+ The user and configuration bindings cannot be changed after creation. Import
+ validation is read-only and runs before creation: a failed validation leaves no
+ wallet or reserved key, so the user_id can be corrected and retried. Concurrent
+ imports of the same key may both validate, but create at most one wallet and
+ connected event.
"""
state: WalletVaultItemState
diff --git a/src/kernel/types/vaults/vault_item_operation_response.py b/src/kernel/types/vaults/vault_item_operation_response.py
index 4faf7487..a7a31733 100644
--- a/src/kernel/types/vaults/vault_item_operation_response.py
+++ b/src/kernel/types/vaults/vault_item_operation_response.py
@@ -88,8 +88,16 @@ class WalletVaultItem(BaseModel):
customer-owned configuration. Mode (sandbox vs live) is determined by the
selected credential; there is no per-item test flag. Without user_id, creation
returns a hosted enrollment action and Kernel polls until the user connects.
- user_id may only reference a user already enrolled by a wallet in this
- organization under the same configuration.
+ With a customer-owned provider_config, user_id imports an existing connection
+ after verifying access with the selected credentials in their sandbox or live
+ mode; no hosted enrollment is needed, even if the connected user has no stored
+ cards. With Kernel-managed credentials, user_id may only reference a user
+ already enrolled by a wallet in this organization under the same configuration.
+ The user and configuration bindings cannot be changed after creation. Import
+ validation is read-only and runs before creation: a failed validation leaves no
+ wallet or reserved key, so the user_id can be corrected and retried. Concurrent
+ imports of the same key may both validate, but create at most one wallet and
+ connected event.
"""
state: WalletVaultItemState
diff --git a/src/kernel/types/vaults/wallet_vault_item_spec.py b/src/kernel/types/vaults/wallet_vault_item_spec.py
index 288fd7e3..6c4d9950 100644
--- a/src/kernel/types/vaults/wallet_vault_item_spec.py
+++ b/src/kernel/types/vaults/wallet_vault_item_spec.py
@@ -81,7 +81,7 @@ class AgentCardWalletVaultItemSpecProviderConfig(BaseModel):
class AgentCardWalletVaultItemSpec(BaseModel):
"""AgentCard wallet.
- Omit provider_config to use Kernel-managed credentials, or select a customer-owned configuration. Mode (sandbox vs live) is determined by the selected credential; there is no per-item test flag. Without user_id, creation returns a hosted enrollment action and Kernel polls until the user connects. user_id may only reference a user already enrolled by a wallet in this organization under the same configuration.
+ Omit provider_config to use Kernel-managed credentials, or select a customer-owned configuration. Mode (sandbox vs live) is determined by the selected credential; there is no per-item test flag. Without user_id, creation returns a hosted enrollment action and Kernel polls until the user connects. With a customer-owned provider_config, user_id imports an existing connection after verifying access with the selected credentials in their sandbox or live mode; no hosted enrollment is needed, even if the connected user has no stored cards. With Kernel-managed credentials, user_id may only reference a user already enrolled by a wallet in this organization under the same configuration. The user and configuration bindings cannot be changed after creation. Import validation is read-only and runs before creation: a failed validation leaves no wallet or reserved key, so the user_id can be corrected and retried. Concurrent imports of the same key may both validate, but create at most one wallet and connected event.
"""
provider: Literal["agentcard"]
From a9ab0d3955c3535458f99f117fae744e34c66930 Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Mon, 19 Oct 2026 17:21:53 +0000
Subject: [PATCH 3/5] feat: Fence browser allowlist updates with revision
checks
Stainless-Generated-From: 98e3c1efbf1a574ca1fd596e616e5a0dd857b703
---
api.md | 1 +
src/kernel/types/__init__.py | 2 ++
src/kernel/types/browser_allowlist_state.py | 21 +++++++++++++++++++
.../types/browser_allowlist_state_param.py | 21 +++++++++++++++++++
src/kernel/types/browser_network_config.py | 9 ++++++++
.../types/browser_network_update_param.py | 19 +++++++++++++++++
tests/api_resources/test_browsers.py | 12 +++++++++--
7 files changed, 83 insertions(+), 2 deletions(-)
create mode 100644 src/kernel/types/browser_allowlist_state.py
create mode 100644 src/kernel/types/browser_allowlist_state_param.py
diff --git a/api.md b/api.md
index 882ac3a8..9822c59d 100644
--- a/api.md
+++ b/api.md
@@ -117,6 +117,7 @@ Types:
```python
from kernel.types import (
+ BrowserAllowlistState,
BrowserMemory,
BrowserMemoryRequest,
BrowserNetworkConfig,
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index a3eac5b8..656272ec 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -98,6 +98,7 @@
from .invocation_state_event import InvocationStateEvent as InvocationStateEvent
from .recommendation_summary import RecommendationSummary as RecommendationSummary
from .api_key_retrieve_params import APIKeyRetrieveParams as APIKeyRetrieveParams
+from .browser_allowlist_state import BrowserAllowlistState as BrowserAllowlistState
from .browser_create_response import BrowserCreateResponse as BrowserCreateResponse
from .browser_retrieve_params import BrowserRetrieveParams as BrowserRetrieveParams
from .browser_update_response import BrowserUpdateResponse as BrowserUpdateResponse
@@ -138,6 +139,7 @@
from .deployment_retrieve_response import DeploymentRetrieveResponse as DeploymentRetrieveResponse
from .invocation_retrieve_response import InvocationRetrieveResponse as InvocationRetrieveResponse
from .audit_log_export_chunk_params import AuditLogExportChunkParams as AuditLogExportChunkParams
+from .browser_allowlist_state_param import BrowserAllowlistStateParam as BrowserAllowlistStateParam
from .browser_pool_acquire_response import BrowserPoolAcquireResponse as BrowserPoolAcquireResponse
from .config_registry_lookup_params import ConfigRegistryLookupParams as ConfigRegistryLookupParams
from .credential_totp_code_response import CredentialTotpCodeResponse as CredentialTotpCodeResponse
diff --git a/src/kernel/types/browser_allowlist_state.py b/src/kernel/types/browser_allowlist_state.py
new file mode 100644
index 00000000..21ee5cfb
--- /dev/null
+++ b/src/kernel/types/browser_allowlist_state.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["BrowserAllowlistState"]
+
+
+class BrowserAllowlistState(BaseModel):
+ revision: str
+ """Opaque revision to pass as network.expected_allowlist_revision on an update."""
+
+ base_revision: Optional[str] = None
+ """
+ Revision against which that mutation was accepted; reuse it when retrying the
+ mutation.
+ """
+
+ mutation_id: Optional[str] = None
+ """ID of the last conditional allowlist update saved on this browser."""
diff --git a/src/kernel/types/browser_allowlist_state_param.py b/src/kernel/types/browser_allowlist_state_param.py
new file mode 100644
index 00000000..72e0a3ef
--- /dev/null
+++ b/src/kernel/types/browser_allowlist_state_param.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 Required, TypedDict
+
+__all__ = ["BrowserAllowlistStateParam"]
+
+
+class BrowserAllowlistStateParam(TypedDict, total=False):
+ revision: Required[str]
+ """Opaque revision to pass as network.expected_allowlist_revision on an update."""
+
+ base_revision: str
+ """
+ Revision against which that mutation was accepted; reuse it when retrying the
+ mutation.
+ """
+
+ mutation_id: str
+ """ID of the last conditional allowlist update saved on this browser."""
diff --git a/src/kernel/types/browser_network_config.py b/src/kernel/types/browser_network_config.py
index cd205e65..d326c6bf 100644
--- a/src/kernel/types/browser_network_config.py
+++ b/src/kernel/types/browser_network_config.py
@@ -3,6 +3,7 @@
from typing import List, Optional
from .._models import BaseModel
+from .browser_allowlist_state import BrowserAllowlistState
__all__ = ["BrowserNetworkConfig", "ProxyRoute", "ProxyRouteProxy"]
@@ -58,6 +59,14 @@ class BrowserNetworkConfig(BaseModel):
replaced on release rather than given an allowlist again. Requires proxy v3.
"""
+ allowlist_state: Optional[BrowserAllowlistState] = None
+ """Concurrency metadata for a browser's saved allowlist configuration.
+
+ Browser pools omit it. This remains present after removal so an uncertain
+ removal can be retried. It does not prove proxy or VM application completed. Do
+ not include it in creation or pool updates.
+ """
+
private_hosts: Optional[List[str]] = None
"""
Destinations the browser reaches directly through the session's own network
diff --git a/src/kernel/types/browser_network_update_param.py b/src/kernel/types/browser_network_update_param.py
index 31158d46..f329552d 100644
--- a/src/kernel/types/browser_network_update_param.py
+++ b/src/kernel/types/browser_network_update_param.py
@@ -34,3 +34,22 @@ class BrowserNetworkUpdateParam(TypedDict, total=False):
safely restored. If the request fails, retry it: the new list may already apply
to some requests.
"""
+
+ expected_allowlist_revision: str
+ """The network.allowlist_state.revision from the browser's last saved snapshot.
+
+ Supply together with mutation_id to opt into conditional allowlist updates. The
+ revision is checked atomically with the configuration write. A stale request
+ returns 409 stale_network without changing the allowlist. A GET alone does not
+ fence an earlier unfinished PATCH.
+ """
+
+ mutation_id: str
+ """
+ A unique ID for this allowlist update, supplied together with
+ expected_allowlist_revision. Reuse both values and the identical allowed_hosts
+ payload when retrying an uncertain update. If it is still the current mutation,
+ the retry finishes proxy and VM application without creating another revision. A
+ superseded mutation returns 409 stale_network. These fields guard only
+ network.allowed_hosts, not other browser update fields.
+ """
diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py
index d12c67b2..c1475b95 100644
--- a/tests/api_resources/test_browsers.py
+++ b/tests/api_resources/test_browsers.py
@@ -211,7 +211,11 @@ def test_method_update_with_all_params(self, client: Kernel) -> None:
id_or_name="htzv5orfit78e1m2biiifpbv",
disable_default_proxy=True,
name="checkout-flow-1",
- network={"allowed_hosts": ["example.com", "*.example.com"]},
+ network={
+ "allowed_hosts": ["example.com", "*.example.com"],
+ "expected_allowlist_revision": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "mutation_id": "mutation_id",
+ },
profile={
"id": "id",
"name": "name",
@@ -758,7 +762,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) ->
id_or_name="htzv5orfit78e1m2biiifpbv",
disable_default_proxy=True,
name="checkout-flow-1",
- network={"allowed_hosts": ["example.com", "*.example.com"]},
+ network={
+ "allowed_hosts": ["example.com", "*.example.com"],
+ "expected_allowlist_revision": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "mutation_id": "mutation_id",
+ },
profile={
"id": "id",
"name": "name",
From 48fd52646ea0dafcecd637d2cae6c71a19318f7a Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Mon, 19 Oct 2026 19:09:28 +0000
Subject: [PATCH 4/5] feat: Add browser location session policy
Stainless-Generated-From: a55a0720ed50df5df1001ae2613977041468284b
---
README.md | 4 +--
api.md | 4 +++
src/kernel/resources/browsers/browsers.py | 32 +++++++++++++++++++
src/kernel/types/__init__.py | 6 ++++
src/kernel/types/browser_create_params.py | 8 +++++
src/kernel/types/browser_create_response.py | 4 +++
src/kernel/types/browser_list_response.py | 4 +++
src/kernel/types/browser_location.py | 28 ++++++++++++++++
.../types/browser_location_request_param.py | 21 ++++++++++++
src/kernel/types/browser_location_source.py | 7 ++++
.../browser_location_update_request_param.py | 26 +++++++++++++++
.../types/browser_pool_acquire_response.py | 4 +++
src/kernel/types/browser_retrieve_response.py | 4 +++
src/kernel/types/browser_update_params.py | 12 +++++++
src/kernel/types/browser_update_response.py | 4 +++
.../invocation_list_browsers_response.py | 4 +++
tests/api_resources/test_browsers.py | 16 ++++++++++
17 files changed, 186 insertions(+), 2 deletions(-)
create mode 100644 src/kernel/types/browser_location.py
create mode 100644 src/kernel/types/browser_location_request_param.py
create mode 100644 src/kernel/types/browser_location_source.py
create mode 100644 src/kernel/types/browser_location_update_request_param.py
diff --git a/README.md b/README.md
index 35a3bc41..a8c9fc1c 100644
--- a/README.md
+++ b/README.md
@@ -203,9 +203,9 @@ from kernel import Kernel
client = Kernel()
browser = client.browsers.create(
- network={},
+ location={},
)
-print(browser.network)
+print(browser.location)
```
## File uploads
diff --git a/api.md b/api.md
index 9822c59d..5463b586 100644
--- a/api.md
+++ b/api.md
@@ -118,6 +118,10 @@ Types:
```python
from kernel.types import (
BrowserAllowlistState,
+ BrowserLocation,
+ BrowserLocationRequest,
+ BrowserLocationSource,
+ BrowserLocationUpdateRequest,
BrowserMemory,
BrowserMemoryRequest,
BrowserNetworkConfig,
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 0333bfdd..885c7709 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -116,8 +116,10 @@
from ...types.browser_network_config_param import BrowserNetworkConfigParam
from ...types.browser_network_update_param import BrowserNetworkUpdateParam
from ...types.shared_params.browser_profile import BrowserProfile
+from ...types.browser_location_request_param import BrowserLocationRequestParam
from ...types.shared_params.browser_viewport import BrowserViewport
from ...types.shared_params.browser_extension import BrowserExtension
+from ...types.browser_location_update_request_param import BrowserLocationUpdateRequestParam
__all__ = ["BrowsersResource", "AsyncBrowsersResource"]
@@ -195,6 +197,7 @@ def create(
headless: bool | Omit = omit,
invocation_id: str | Omit = omit,
kiosk_mode: bool | Omit = omit,
+ location: BrowserLocationRequestParam | Omit = omit,
memory: BrowserMemoryRequest | Omit = omit,
name: str | Omit = omit,
network: BrowserNetworkConfigParam | Omit = omit,
@@ -239,6 +242,9 @@ def create(
kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
view.
+ location: Optional session-only timezone and locale overrides. Each omitted field uses the
+ default for the browser's region. start_url opens once the location applies.
+
memory:
Memory for a non-GPU browser. Headless: 1GiB (default), 2GiB, or 4GiB. Headful:
8GiB (default) or 16GiB.
@@ -333,6 +339,7 @@ def create(
"headless": headless,
"invocation_id": invocation_id,
"kiosk_mode": kiosk_mode,
+ "location": location,
"memory": memory,
"name": name,
"network": network,
@@ -404,6 +411,7 @@ def update(
id_or_name: str,
*,
disable_default_proxy: bool | Omit = omit,
+ location: BrowserLocationUpdateRequestParam | Omit = omit,
name: Optional[str] | Omit = omit,
network: BrowserNetworkUpdateParam | Omit = omit,
profile: BrowserProfile | Omit = omit,
@@ -427,6 +435,14 @@ def update(
disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth
proxy. Deprecated in favor of proxy.mode.
+ location: Session-only timezone and locale overrides. Omit the object or a field to leave
+ it unchanged; set a string to override; set null to clear that field and use the
+ browser region's default. A browser that cannot change location refuses the
+ request before anything changes. The location applies after the request's other
+ changes, and start_url opens once it has applied. If applying it fails, the
+ other changes and the saved override remain and start_url does not open; retry
+ the request to apply both.
+
name: Human-readable name for the browser session. Omit to leave unchanged, set to an
empty string to clear the name. When set, must be unique among active sessions
within the project.
@@ -478,6 +494,7 @@ def update(
body=maybe_transform(
{
"disable_default_proxy": disable_default_proxy,
+ "location": location,
"name": name,
"network": network,
"profile": profile,
@@ -912,6 +929,7 @@ async def create(
headless: bool | Omit = omit,
invocation_id: str | Omit = omit,
kiosk_mode: bool | Omit = omit,
+ location: BrowserLocationRequestParam | Omit = omit,
memory: BrowserMemoryRequest | Omit = omit,
name: str | Omit = omit,
network: BrowserNetworkConfigParam | Omit = omit,
@@ -956,6 +974,9 @@ async def create(
kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
view.
+ location: Optional session-only timezone and locale overrides. Each omitted field uses the
+ default for the browser's region. start_url opens once the location applies.
+
memory:
Memory for a non-GPU browser. Headless: 1GiB (default), 2GiB, or 4GiB. Headful:
8GiB (default) or 16GiB.
@@ -1050,6 +1071,7 @@ async def create(
"headless": headless,
"invocation_id": invocation_id,
"kiosk_mode": kiosk_mode,
+ "location": location,
"memory": memory,
"name": name,
"network": network,
@@ -1121,6 +1143,7 @@ async def update(
id_or_name: str,
*,
disable_default_proxy: bool | Omit = omit,
+ location: BrowserLocationUpdateRequestParam | Omit = omit,
name: Optional[str] | Omit = omit,
network: BrowserNetworkUpdateParam | Omit = omit,
profile: BrowserProfile | Omit = omit,
@@ -1144,6 +1167,14 @@ async def update(
disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth
proxy. Deprecated in favor of proxy.mode.
+ location: Session-only timezone and locale overrides. Omit the object or a field to leave
+ it unchanged; set a string to override; set null to clear that field and use the
+ browser region's default. A browser that cannot change location refuses the
+ request before anything changes. The location applies after the request's other
+ changes, and start_url opens once it has applied. If applying it fails, the
+ other changes and the saved override remain and start_url does not open; retry
+ the request to apply both.
+
name: Human-readable name for the browser session. Omit to leave unchanged, set to an
empty string to clear the name. When set, must be unique among active sessions
within the project.
@@ -1195,6 +1226,7 @@ async def update(
body=await async_maybe_transform(
{
"disable_default_proxy": disable_default_proxy,
+ "location": location,
"name": name,
"network": network,
"profile": profile,
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index 656272ec..a7735052 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -44,6 +44,7 @@
from .lookup_response import LookupResponse as LookupResponse
from .vault_reference import VaultReference as VaultReference
from .analysis_summary import AnalysisSummary as AnalysisSummary
+from .browser_location import BrowserLocation as BrowserLocation
from .browser_pool_ref import BrowserPoolRef as BrowserPoolRef
from .app_list_response import AppListResponse as AppListResponse
from .no_recommendation import NoRecommendation as NoRecommendation
@@ -100,6 +101,7 @@
from .api_key_retrieve_params import APIKeyRetrieveParams as APIKeyRetrieveParams
from .browser_allowlist_state import BrowserAllowlistState as BrowserAllowlistState
from .browser_create_response import BrowserCreateResponse as BrowserCreateResponse
+from .browser_location_source import BrowserLocationSource as BrowserLocationSource
from .browser_retrieve_params import BrowserRetrieveParams as BrowserRetrieveParams
from .browser_update_response import BrowserUpdateResponse as BrowserUpdateResponse
from .extension_list_response import ExtensionListResponse as ExtensionListResponse
@@ -144,6 +146,7 @@
from .config_registry_lookup_params import ConfigRegistryLookupParams as ConfigRegistryLookupParams
from .credential_totp_code_response import CredentialTotpCodeResponse as CredentialTotpCodeResponse
from .browser_load_extensions_params import BrowserLoadExtensionsParams as BrowserLoadExtensionsParams
+from .browser_location_request_param import BrowserLocationRequestParam as BrowserLocationRequestParam
from .config_registry_resolve_params import ConfigRegistryResolveParams as ConfigRegistryResolveParams
from .credential_provider_list_params import CredentialProviderListParams as CredentialProviderListParams
from .credential_provider_test_result import CredentialProviderTestResult as CredentialProviderTestResult
@@ -153,6 +156,9 @@
from .vault_provider_config_list_params import VaultProviderConfigListParams as VaultProviderConfigListParams
from .vault_provider_config_create_params import VaultProviderConfigCreateParams as VaultProviderConfigCreateParams
from .vault_provider_config_update_params import VaultProviderConfigUpdateParams as VaultProviderConfigUpdateParams
+from .browser_location_update_request_param import (
+ BrowserLocationUpdateRequestParam as BrowserLocationUpdateRequestParam,
+)
from .credential_provider_list_items_response import (
CredentialProviderListItemsResponse as CredentialProviderListItemsResponse,
)
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index d773f8cc..8e319668 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -12,6 +12,7 @@
from .browser_proxy_config_param import BrowserProxyConfigParam
from .browser_network_config_param import BrowserNetworkConfigParam
from .shared_params.browser_profile import BrowserProfile
+from .browser_location_request_param import BrowserLocationRequestParam
from .shared_params.browser_viewport import BrowserViewport
from .shared_params.browser_extension import BrowserExtension
from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
@@ -62,6 +63,13 @@ class BrowserCreateParams(TypedDict, total=False):
view.
"""
+ location: BrowserLocationRequestParam
+ """Optional session-only timezone and locale overrides.
+
+ Each omitted field uses the default for the browser's region. start_url opens
+ once the location applies.
+ """
+
memory: BrowserMemoryRequest
"""Memory for a non-GPU browser.
diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py
index 25e82277..53ae17de 100644
--- a/src/kernel/types/browser_create_response.py
+++ b/src/kernel/types/browser_create_response.py
@@ -11,6 +11,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -76,6 +77,9 @@ class BrowserCreateResponse(BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py
index 6527b9c2..fa6c01ba 100644
--- a/src/kernel/types/browser_list_response.py
+++ b/src/kernel/types/browser_list_response.py
@@ -11,6 +11,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -76,6 +77,9 @@ class BrowserListResponse(BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/src/kernel/types/browser_location.py b/src/kernel/types/browser_location.py
new file mode 100644
index 00000000..1ae33af7
--- /dev/null
+++ b/src/kernel/types/browser_location.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+from .browser_location_source import BrowserLocationSource
+
+__all__ = ["BrowserLocation"]
+
+
+class BrowserLocation(BaseModel):
+ locale: str
+ """Effective canonical BCP 47 formatting locale selected for the browser session."""
+
+ locale_source: BrowserLocationSource
+ """Where one effective browser location field came from.
+
+ `override` is set explicitly on the session; `metro` is the default for the
+ browser's region.
+ """
+
+ timezone: str
+ """Effective IANA timezone selected for the browser session."""
+
+ timezone_source: BrowserLocationSource
+ """Where one effective browser location field came from.
+
+ `override` is set explicitly on the session; `metro` is the default for the
+ browser's region.
+ """
diff --git a/src/kernel/types/browser_location_request_param.py b/src/kernel/types/browser_location_request_param.py
new file mode 100644
index 00000000..55b73014
--- /dev/null
+++ b/src/kernel/types/browser_location_request_param.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__ = ["BrowserLocationRequestParam"]
+
+
+class BrowserLocationRequestParam(TypedDict, total=False):
+ locale: str
+ """Session-only supported canonical BCP 47 locale override.
+
+ Omit to use the default for the browser's region.
+ """
+
+ timezone: str
+ """Session-only IANA timezone override.
+
+ Omit to use the default for the browser's region.
+ """
diff --git a/src/kernel/types/browser_location_source.py b/src/kernel/types/browser_location_source.py
new file mode 100644
index 00000000..b067e5ef
--- /dev/null
+++ b/src/kernel/types/browser_location_source.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BrowserLocationSource"]
+
+BrowserLocationSource: TypeAlias = Literal["override", "metro"]
diff --git a/src/kernel/types/browser_location_update_request_param.py b/src/kernel/types/browser_location_update_request_param.py
new file mode 100644
index 00000000..a0742845
--- /dev/null
+++ b/src/kernel/types/browser_location_update_request_param.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["BrowserLocationUpdateRequestParam"]
+
+
+class BrowserLocationUpdateRequestParam(TypedDict, total=False):
+ """
+ Omit a field to leave it unchanged, set a string override, or set null to clear the override and use the default for the browser's region.
+ """
+
+ locale: Optional[str]
+ """Session-only supported canonical BCP 47 locale override.
+
+ Null clears the override and uses the browser region's default.
+ """
+
+ timezone: Optional[str]
+ """Session-only IANA timezone override.
+
+ Null clears the override and uses the browser region's default.
+ """
diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py
index 8791bb7b..cceb9892 100644
--- a/src/kernel/types/browser_pool_acquire_response.py
+++ b/src/kernel/types/browser_pool_acquire_response.py
@@ -12,6 +12,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -77,6 +78,9 @@ class BrowserPoolAcquireResponse(NoContentAsNone, BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py
index 735c9ba4..c537589f 100644
--- a/src/kernel/types/browser_retrieve_response.py
+++ b/src/kernel/types/browser_retrieve_response.py
@@ -11,6 +11,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -76,6 +77,9 @@ class BrowserRetrieveResponse(BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/src/kernel/types/browser_update_params.py b/src/kernel/types/browser_update_params.py
index 475010bd..3be13135 100644
--- a/src/kernel/types/browser_update_params.py
+++ b/src/kernel/types/browser_update_params.py
@@ -10,6 +10,7 @@
from .browser_network_update_param import BrowserNetworkUpdateParam
from .shared_params.browser_profile import BrowserProfile
from .shared_params.browser_viewport import BrowserViewport
+from .browser_location_update_request_param import BrowserLocationUpdateRequestParam
from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
__all__ = [
@@ -30,6 +31,17 @@ class BrowserUpdateParams(TypedDict, total=False):
proxy. Deprecated in favor of proxy.mode.
"""
+ location: BrowserLocationUpdateRequestParam
+ """Session-only timezone and locale overrides.
+
+ Omit the object or a field to leave it unchanged; set a string to override; set
+ null to clear that field and use the browser region's default. A browser that
+ cannot change location refuses the request before anything changes. The location
+ applies after the request's other changes, and start_url opens once it has
+ applied. If applying it fails, the other changes and the saved override remain
+ and start_url does not open; retry the request to apply both.
+ """
+
name: Optional[str]
"""Human-readable name for the browser session.
diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py
index b53d2ac3..5b9f8ae3 100644
--- a/src/kernel/types/browser_update_response.py
+++ b/src/kernel/types/browser_update_response.py
@@ -11,6 +11,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -76,6 +77,9 @@ class BrowserUpdateResponse(BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py
index dc99f0dc..2df9877c 100644
--- a/src/kernel/types/invocation_list_browsers_response.py
+++ b/src/kernel/types/invocation_list_browsers_response.py
@@ -11,6 +11,7 @@
from .browser_usage import BrowserUsage
from .browser_memory import BrowserMemory
from .vault_reference import VaultReference
+from .browser_location import BrowserLocation
from .browser_pool_ref import BrowserPoolRef
from .browser_video_memory import BrowserVideoMemory
from .browser_network_config import BrowserNetworkConfig
@@ -76,6 +77,9 @@ class Browser(BaseModel):
kiosk_mode: Optional[bool] = None
"""Whether the browser session is running in kiosk mode."""
+ location: Optional[BrowserLocation] = None
+ """Effective browser timezone and locale with per-field provenance."""
+
name: Optional[str] = None
"""Human-readable name of the browser session, if one was set at creation."""
diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py
index c1475b95..6c5e9696 100644
--- a/tests/api_resources/test_browsers.py
+++ b/tests/api_resources/test_browsers.py
@@ -46,6 +46,10 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
headless=False,
invocation_id="rr33xuugxj9h0bkf1rdt2bet",
kiosk_mode=True,
+ location={
+ "locale": "en-US",
+ "timezone": "America/Los_Angeles",
+ },
memory="8GiB",
name="checkout-flow-1",
network={
@@ -210,6 +214,10 @@ def test_method_update_with_all_params(self, client: Kernel) -> None:
browser = client.browsers.update(
id_or_name="htzv5orfit78e1m2biiifpbv",
disable_default_proxy=True,
+ location={
+ "locale": "en-US",
+ "timezone": "America/Los_Angeles",
+ },
name="checkout-flow-1",
network={
"allowed_hosts": ["example.com", "*.example.com"],
@@ -597,6 +605,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
headless=False,
invocation_id="rr33xuugxj9h0bkf1rdt2bet",
kiosk_mode=True,
+ location={
+ "locale": "en-US",
+ "timezone": "America/Los_Angeles",
+ },
memory="8GiB",
name="checkout-flow-1",
network={
@@ -761,6 +773,10 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) ->
browser = await async_client.browsers.update(
id_or_name="htzv5orfit78e1m2biiifpbv",
disable_default_proxy=True,
+ location={
+ "locale": "en-US",
+ "timezone": "America/Los_Angeles",
+ },
name="checkout-flow-1",
network={
"allowed_hosts": ["example.com", "*.example.com"],
From a32f2ad7ea01c1fb7cbeeed8909a96c8100dd6af Mon Sep 17 00:00:00 2001
From: "kernel-internal[bot]"
<260533166+kernel-internal[bot]@users.noreply.github.com>
Date: Mon, 19 Oct 2026 19:15:00 +0000
Subject: [PATCH 5/5] release: 0.128.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 10 ++++++++++
pyproject.toml | 2 +-
src/kernel/_version.py | 2 +-
4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 47775e91..f118d043 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.127.0"
+ ".": "0.128.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dcd39316..4193ab9c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## [0.128.0](https://github.com/kernel/kernel-python-sdk/compare/v0.127.0...v0.128.0) (2026-10-19)
+
+
+### Features
+
+* Add browser location session policy ([48fd526](https://github.com/kernel/kernel-python-sdk/commit/48fd52646ea0dafcecd637d2cae6c71a19318f7a))
+* chore(stlc): seal custom-code tracking files ([457e6c1](https://github.com/kernel/kernel-python-sdk/commit/457e6c14bd10df055044c2b48704b64d90ca8aff))
+* Fence browser allowlist updates with revision checks ([a9ab0d3](https://github.com/kernel/kernel-python-sdk/commit/a9ab0d3955c3535458f99f117fae744e34c66930))
+* Support existing-user AgentCard wallet imports ([c1a6765](https://github.com/kernel/kernel-python-sdk/commit/c1a6765c789f5ed7585a55780f83435cc8c5d4ee))
+
## [0.127.0](https://github.com/kernel/kernel-python-sdk/compare/v0.126.0...v0.127.0) (2026-10-16)
diff --git a/pyproject.toml b/pyproject.toml
index d2de2e3b..eca42025 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.127.0"
+version = "0.128.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 d351e0a2..ba7941bc 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.127.0" # x-release-please-version
+__version__ = "0.128.0" # x-release-please-version